Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 27d5d7a

Browse files
committed
Change the naming convention for extension files to use double dashes.
This allows us to have an unambiguous rule for deconstructing the names of script files and secondary control files, without having to forbid extension and version names from containing any dashes. We do have to forbid them from containing double dashes or leading/trailing dashes, but neither restriction is likely to bother anyone in practice. Per discussion, this seems like a better solution overall than the original design.
1 parent 5569ae5 commit 27d5d7a

File tree

2 files changed

+113
-39
lines changed

2 files changed

+113
-39
lines changed

doc/src/sgml/extend.sgml

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -368,8 +368,8 @@
368368
installation's <literal>SHAREDIR/extension</literal> directory. There
369369
must also be at least one <acronym>SQL</> script file, which follows the
370370
naming pattern
371-
<literal><replaceable>extension</>-<replaceable>version</>.sql</literal>
372-
(for example, <literal>foo-1.0.sql</> for version <literal>1.0</> of
371+
<literal><replaceable>extension</>--<replaceable>version</>.sql</literal>
372+
(for example, <literal>foo--1.0.sql</> for version <literal>1.0</> of
373373
extension <literal>foo</>). By default, the script file(s) are also
374374
placed in the <literal>SHAREDIR/extension</literal> directory; but the
375375
control file can specify a different directory for the script file(s).
@@ -378,7 +378,7 @@
378378
<para>
379379
The file format for an extension control file is the same as for the
380380
<filename>postgresql.conf</> file, namely a list of
381-
<replaceable>parameter-name</> <literal>=</> <replaceable>value</>
381+
<replaceable>parameter_name</> <literal>=</> <replaceable>value</>
382382
assignments, one per line. Blank lines and comments introduced by
383383
<literal>#</> are allowed. Be sure to quote any value that is not
384384
a single word or number.
@@ -477,7 +477,7 @@
477477
In addition to the primary control file
478478
<literal><replaceable>extension</>.control</literal>,
479479
an extension can have secondary control files named in the style
480-
<literal><replaceable>extension</>-<replaceable>version</>.control</literal>.
480+
<literal><replaceable>extension</>--<replaceable>version</>.control</literal>.
481481
If supplied, these must be located in the script file directory.
482482
Secondary control files follow the same format as the primary control
483483
file. Any parameters set in a secondary control file override the
@@ -671,15 +671,15 @@ SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entr
671671
dynamically from one version to the next, you should provide
672672
<firstterm>update scripts</> that make the necessary changes to go from
673673
one version to the next. Update scripts have names following the pattern
674-
<literal><replaceable>extension</>-<replaceable>oldversion</>-<replaceable>newversion</>.sql</literal>
675-
(for example, <literal>foo-1.0-1.1.sql</> contains the commands to modify
674+
<literal><replaceable>extension</>--<replaceable>oldversion</>--<replaceable>newversion</>.sql</literal>
675+
(for example, <literal>foo--1.0--1.1.sql</> contains the commands to modify
676676
version <literal>1.0</> of extension <literal>foo</> into version
677677
<literal>1.1</>).
678678
</para>
679679

680680
<para>
681681
Given that a suitable update script is available, the command
682-
<command>ALTER EXTENSION ... UPDATE</> will update an installed extension
682+
<command>ALTER EXTENSION UPDATE</> will update an installed extension
683683
to the specified new version. The update script is run in the same
684684
environment that <command>CREATE EXTENSION</> provides for installation
685685
scripts: in particular, <varname>search_path</> is set up in the same
@@ -712,7 +712,7 @@ SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entr
712712
class="parameter">old_version</> option, which causes it to not run the
713713
normal installation script for the target version, but instead the update
714714
script named
715-
<literal><replaceable>extension</>-<replaceable>old_version</>-<replaceable>target_version</>.sql</literal>.
715+
<literal><replaceable>extension</>--<replaceable>old_version</>--<replaceable>target_version</>.sql</literal>.
716716
The choice of the dummy version name to use as <replaceable
717717
class="parameter">old_version</> is up to the extension author, though
718718
<literal>unpackaged</> is a common convention. If you have multiple
@@ -723,7 +723,7 @@ SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entr
723723
<para>
724724
<command>ALTER EXTENSION</> is able to execute sequences of update
725725
script files to achieve a requested update. For example, if only
726-
<literal>foo-1.0-1.1.sql</> and <literal>foo-1.1-2.0.sql</> are
726+
<literal>foo--1.0--1.1.sql</> and <literal>foo--1.1--2.0.sql</> are
727727
available, <command>ALTER EXTENSION</> will apply them in sequence if an
728728
update to version <literal>2.0</> is requested when <literal>1.0</> is
729729
currently installed.
@@ -734,11 +734,13 @@ SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entr
734734
of version names: for example, it does not know whether <literal>1.1</>
735735
follows <literal>1.0</>. It just matches up the available version names
736736
and follows the path that requires applying the fewest update scripts.
737+
(A version name can actually be any string that doesn't contain
738+
<literal>--</> or leading or trailing <literal>-</>.)
737739
</para>
738740

739741
<para>
740742
Sometimes it is useful to provide <quote>downgrade</> scripts, for
741-
example <literal>foo-1.1-1.0.sql</> to allow reverting the changes
743+
example <literal>foo--1.1--1.0.sql</> to allow reverting the changes
742744
associated with version <literal>1.1</>. If you do that, be careful
743745
of the possibility that a downgrade script might unexpectedly
744746
get applied because it yields a shorter path. The risky case is where
@@ -761,7 +763,7 @@ SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entr
761763
</para>
762764

763765
<para>
764-
The script file <filename>pair-1.0.sql</> looks like this:
766+
The script file <filename>pair--1.0.sql</> looks like this:
765767

766768
<programlisting><![CDATA[
767769
CREATE TYPE pair AS ( k text, v text );
@@ -803,7 +805,7 @@ relocatable = true
803805

804806
<programlisting>
805807
EXTENSION = pair
806-
DATA = pair-1.0.sql
808+
DATA = pair--1.0.sql
807809

808810
PG_CONFIG = pg_config
809811
PGXS := $(shell $(PG_CONFIG) --pgxs)
@@ -860,7 +862,7 @@ include $(PGXS)
860862
<programlisting>
861863
MODULES = isbn_issn
862864
EXTENSION = isbn_issn
863-
DATA_built = isbn_issn-1.0.sql
865+
DATA = isbn_issn--1.0.sql
864866
DOCS = README.isbn_issn
865867

866868
PG_CONFIG = pg_config

src/backend/commands/extension.c

Lines changed: 98 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,6 @@
5757
bool creating_extension = false;
5858
Oid 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)
225222
static void
226223
check_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
*/
232262
if (first_dir_separator(extensionname) != NULL)
233263
ereport(ERROR,
@@ -239,16 +269,39 @@ check_valid_extension_name(const char *extensionname)
239269
static void
240270
check_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] == '-')
244297
ereport(ERROR,
245298
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
246299
errmsg("invalid extension version name: \"%s\"", versionname),
247-
errdetail("Version names must not contain the character \"%c\".",
248-
EXT_VERSION_SEP)));
300+
errdetail("Version names must not begin or end with \"-\".")));
301+
249302
/*
250-
* No directory separators (this is sufficient to prevent ".." style
251-
* attacks).
303+
* No directory separators either (this is sufficient to prevent ".."
304+
* style attacks).
252305
*/
253306
if (first_dir_separator(versionname) != NULL)
254307
ereport(ERROR,
@@ -336,8 +389,8 @@ get_extension_aux_control_filename(ExtensionControlFile *control,
336389
scriptdir = get_extension_script_directory(control);
337390

338391
result = (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

342395
pfree(scriptdir);
343396

@@ -355,12 +408,11 @@ get_extension_script_filename(ExtensionControlFile *control,
355408

356409
result = (char *) palloc(MAXPGPATH);
357410
if (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);
361413
else
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

365417
pfree(scriptdir);
366418

@@ -426,7 +478,7 @@ parse_extension_control_file(ExtensionControlFile *control,
426478
if (version)
427479
ereport(ERROR,
428480
(errcode(ERRCODE_SYNTAX_ERROR),
429-
errmsg("parameter \"%s\" cannot be set in a per-version extension control file",
481+
errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
430482
item->name)));
431483

432484
control->directory = pstrdup(item->value);
@@ -436,7 +488,7 @@ parse_extension_control_file(ExtensionControlFile *control,
436488
if (version)
437489
ereport(ERROR,
438490
(errcode(ERRCODE_SYNTAX_ERROR),
439-
errmsg("parameter \"%s\" cannot be set in a per-version extension control file",
491+
errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
440492
item->name)));
441493

442494
control->default_version = pstrdup(item->value);
@@ -907,16 +959,18 @@ get_ext_ver_list(ExtensionControlFile *control)
907959

908960
/* ... matching extension name followed by separator */
909961
if (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] != '-')
911964
continue;
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, "--");
917970
if (!vername2)
918971
continue; /* 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 */
922976
evi = get_ext_ver_info(vername, &evi_list);
@@ -979,6 +1033,20 @@ identify_update_path(ExtensionControlFile *control,
9791033
evi2->distance = newdist;
9801034
evi2->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)
12511319
requiredExtensions);
12521320

12531321
/*
1254-
* Apply any comment on extension
1322+
* Apply any control-file comment on extension
12551323
*/
12561324
if (control->comment != NULL)
12571325
CreateComments(extensionOid, ExtensionRelationId, 0, control->comment);
@@ -1544,6 +1612,10 @@ pg_available_extensions(PG_FUNCTION_ARGS)
15441612
extname = 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+
15471619
control = read_extension_control_file(extname);
15481620

15491621
memset(values, 0, sizeof(values));

0 commit comments

Comments
 (0)