5757bool creating_extension = false;
5858Oid CurrentExtensionObject = InvalidOid ;
5959
60- /* Character that separates extension & version names in a script filename */
61- #define EXT_VERSION_SEP '-'
62-
6360/*
6461 * Internal data structure to hold the results of parsing a control file
6562 */
@@ -225,9 +222,42 @@ get_extension_schema(Oid ext_oid)
225222static void
226223check_valid_extension_name (const char * extensionname )
227224{
225+ int namelen = strlen (extensionname );
226+
228227/*
229- * No directory separators (this is sufficient to prevent ".." style
230- * attacks).
228+ * Disallow empty names (the parser rejects empty identifiers anyway,
229+ * but let's check).
230+ */
231+ if (namelen == 0 )
232+ ereport (ERROR ,
233+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
234+ errmsg ("invalid extension name: \"%s\"" ,extensionname ),
235+ errdetail ("Extension names must not be empty." )));
236+
237+ /*
238+ * No double dashes, since that would make script filenames ambiguous.
239+ */
240+ if (strstr (extensionname ,"--" ))
241+ ereport (ERROR ,
242+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
243+ errmsg ("invalid extension name: \"%s\"" ,extensionname ),
244+ errdetail ("Extension names must not contain \"--\"." )));
245+
246+ /*
247+ * No leading or trailing dash either. (We could probably allow this,
248+ * but it would require much care in filename parsing and would make
249+ * filenames visually if not formally ambiguous. Since there's no
250+ * real-world use case, let's just forbid it.)
251+ */
252+ if (extensionname [0 ]== '-' || extensionname [namelen - 1 ]== '-' )
253+ ereport (ERROR ,
254+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
255+ errmsg ("invalid extension name: \"%s\"" ,extensionname ),
256+ errdetail ("Extension names must not begin or end with \"-\"." )));
257+
258+ /*
259+ * No directory separators either (this is sufficient to prevent ".."
260+ * style attacks).
231261 */
232262if (first_dir_separator (extensionname )!= NULL )
233263ereport (ERROR ,
@@ -239,16 +269,39 @@ check_valid_extension_name(const char *extensionname)
239269static void
240270check_valid_version_name (const char * versionname )
241271{
242- /* No separators --- would risk confusion of install vs update scripts */
243- if (strchr (versionname ,EXT_VERSION_SEP ))
272+ int namelen = strlen (versionname );
273+
274+ /*
275+ * Disallow empty names (we could possibly allow this, but there seems
276+ * little point).
277+ */
278+ if (namelen == 0 )
279+ ereport (ERROR ,
280+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
281+ errmsg ("invalid extension version name: \"%s\"" ,versionname ),
282+ errdetail ("Version names must not be empty." )));
283+
284+ /*
285+ * No double dashes, since that would make script filenames ambiguous.
286+ */
287+ if (strstr (versionname ,"--" ))
288+ ereport (ERROR ,
289+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
290+ errmsg ("invalid extension version name: \"%s\"" ,versionname ),
291+ errdetail ("Version names must not contain \"--\"." )));
292+
293+ /*
294+ * No leading or trailing dash either.
295+ */
296+ if (versionname [0 ]== '-' || versionname [namelen - 1 ]== '-' )
244297ereport (ERROR ,
245298(errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
246299errmsg ("invalid extension version name: \"%s\"" ,versionname ),
247- errdetail ("Version names must notcontain the character \"%c \"." ,
248- EXT_VERSION_SEP )));
300+ errdetail ("Version names must notbegin or end with \"- \"." )));
301+
249302/*
250- * No directory separators (this is sufficient to prevent ".." style
251- * attacks).
303+ * No directory separatorseither (this is sufficient to prevent ".."
304+ *style attacks).
252305 */
253306if (first_dir_separator (versionname )!= NULL )
254307ereport (ERROR ,
@@ -336,8 +389,8 @@ get_extension_aux_control_filename(ExtensionControlFile *control,
336389scriptdir = get_extension_script_directory (control );
337390
338391result = (char * )palloc (MAXPGPATH );
339- snprintf (result ,MAXPGPATH ,"%s/%s%c %s.control" ,
340- scriptdir ,control -> name ,EXT_VERSION_SEP , version );
392+ snprintf (result ,MAXPGPATH ,"%s/%s-- %s.control" ,
393+ scriptdir ,control -> name ,version );
341394
342395pfree (scriptdir );
343396
@@ -355,12 +408,11 @@ get_extension_script_filename(ExtensionControlFile *control,
355408
356409result = (char * )palloc (MAXPGPATH );
357410if (from_version )
358- snprintf (result ,MAXPGPATH ,"%s/%s%c%s%c%s.sql" ,
359- scriptdir ,control -> name ,EXT_VERSION_SEP ,from_version ,
360- EXT_VERSION_SEP ,version );
411+ snprintf (result ,MAXPGPATH ,"%s/%s--%s--%s.sql" ,
412+ scriptdir ,control -> name ,from_version ,version );
361413else
362- snprintf (result ,MAXPGPATH ,"%s/%s%c %s.sql" ,
363- scriptdir ,control -> name ,EXT_VERSION_SEP , version );
414+ snprintf (result ,MAXPGPATH ,"%s/%s-- %s.sql" ,
415+ scriptdir ,control -> name ,version );
364416
365417pfree (scriptdir );
366418
@@ -426,7 +478,7 @@ parse_extension_control_file(ExtensionControlFile *control,
426478if (version )
427479ereport (ERROR ,
428480(errcode (ERRCODE_SYNTAX_ERROR ),
429- errmsg ("parameter \"%s\" cannot be set in aper-version extension control file" ,
481+ errmsg ("parameter \"%s\" cannot be set in asecondary extension control file" ,
430482item -> name )));
431483
432484control -> directory = pstrdup (item -> value );
@@ -436,7 +488,7 @@ parse_extension_control_file(ExtensionControlFile *control,
436488if (version )
437489ereport (ERROR ,
438490(errcode (ERRCODE_SYNTAX_ERROR ),
439- errmsg ("parameter \"%s\" cannot be set in aper-version extension control file" ,
491+ errmsg ("parameter \"%s\" cannot be set in asecondary extension control file" ,
440492item -> name )));
441493
442494control -> default_version = pstrdup (item -> value );
@@ -907,16 +959,18 @@ get_ext_ver_list(ExtensionControlFile *control)
907959
908960/* ... matching extension name followed by separator */
909961if (strncmp (de -> d_name ,control -> name ,extnamelen )!= 0 ||
910- de -> d_name [extnamelen ]!= EXT_VERSION_SEP )
962+ de -> d_name [extnamelen ]!= '-' ||
963+ de -> d_name [extnamelen + 1 ]!= '-' )
911964continue ;
912965
913- /* extract version names from 'extname-something.sql' filename */
914- vername = pstrdup (de -> d_name + extnamelen + 1 );
966+ /* extract version names from 'extname-- something.sql' filename */
967+ vername = pstrdup (de -> d_name + extnamelen + 2 );
915968* strrchr (vername ,'.' )= '\0' ;
916- vername2 = strchr (vername ,EXT_VERSION_SEP );
969+ vername2 = strstr (vername ,"--" );
917970if (!vername2 )
918971continue ;/* it's not an update script */
919- * vername2 ++ = '\0' ;
972+ * vername2 = '\0' ;/* terminate first version */
973+ vername2 += 2 ;/* and point to second */
920974
921975/* Create ExtensionVersionInfos and link them together */
922976evi = get_ext_ver_info (vername ,& evi_list );
@@ -979,6 +1033,20 @@ identify_update_path(ExtensionControlFile *control,
9791033evi2 -> distance = newdist ;
9801034evi2 -> previous = evi ;
9811035}
1036+ else if (newdist == evi2 -> distance &&
1037+ evi2 -> previous != NULL &&
1038+ strcmp (evi -> name ,evi2 -> previous -> name )< 0 )
1039+ {
1040+ /*
1041+ * Break ties in favor of the version name that comes first
1042+ * according to strcmp(). This behavior is undocumented and
1043+ * users shouldn't rely on it. We do it just to ensure that
1044+ * if there is a tie, the update path that is chosen does not
1045+ * depend on random factors like the order in which directory
1046+ * entries get visited.
1047+ */
1048+ evi2 -> previous = evi ;
1049+ }
9821050}
9831051}
9841052
@@ -1251,7 +1319,7 @@ CreateExtension(CreateExtensionStmt *stmt)
12511319requiredExtensions );
12521320
12531321/*
1254- * Apply any comment on extension
1322+ * Apply anycontrol-file comment on extension
12551323 */
12561324if (control -> comment != NULL )
12571325CreateComments (extensionOid ,ExtensionRelationId ,0 ,control -> comment );
@@ -1544,6 +1612,10 @@ pg_available_extensions(PG_FUNCTION_ARGS)
15441612extname = pstrdup (de -> d_name );
15451613* strrchr (extname ,'.' )= '\0' ;
15461614
1615+ /* ignore it if it's an auxiliary control file */
1616+ if (strstr (extname ,"--" ))
1617+ continue ;
1618+
15471619control = read_extension_control_file (extname );
15481620
15491621memset (values ,0 ,sizeof (values ));