Skip to content

Commit 6568cef

Browse files
committed
Add support for --extension in pg_dump
When specified, only extensions matching the given pattern are included in dumps. Similarly to --table and --schema, when --strict-names is used, a perfect match is required. Also, like the two other options, this new option offers no guarantee that dependent objects have been dumped, so a restore may fail on a clean database. Tests are added in test_pg_dump/, checking after a set of positive and negative cases, with or without an extension's contents added to the dump generated. Author: Guillaume Lelarge Reviewed-by: David Fetter, Tom Lane, Michael Paquier, Asif Rehman, Julien Rouhaud Discussion: https://postgr.es/m/CAECtzeXOt4cnMU5+XMZzxBPJ_wu76pNy6HZKPRBL-j7yj1E4+g@mail.gmail.com
1 parent 65158f4 commit 6568cef

File tree

3 files changed

+161
-28
lines changed

3 files changed

+161
-28
lines changed

doc/src/sgml/ref/pg_dump.sgml

+38-5
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,38 @@ PostgreSQL documentation
215215
</listitem>
216216
</varlistentry>
217217

218+
<varlistentry>
219+
<term><option>-e <replaceable class="parameter">pattern</replaceable></option></term>
220+
<term><option>--extension=<replaceable class="parameter">pattern</replaceable></option></term>
221+
<listitem>
222+
<para>
223+
Dump only extensions matching <replaceable
224+
class="parameter">pattern</replaceable>. When this option is not
225+
specified, all non-system extensions in the target database will be
226+
dumped. Multiple extensions can be selected by writing multiple
227+
<option>-e</option> switches. The <replaceable
228+
class="parameter">pattern</replaceable> parameter is interpreted as a
229+
pattern according to the same rules used by
230+
<application>psql</application>'s <literal>\d</literal> commands (see
231+
<xref linkend="app-psql-patterns"/>), so multiple extensions can also
232+
be selected by writing wildcard characters in the pattern. When using
233+
wildcards, be careful to quote the pattern if needed to prevent the
234+
shell from expanding the wildcards.
235+
</para>
236+
237+
<note>
238+
<para>
239+
When <option>-e</option> is specified,
240+
<application>pg_dump</application> makes no attempt to dump any other
241+
database objects that the selected extension(s) might depend upon.
242+
Therefore, there is no guarantee that the results of a
243+
specific-extension dump can be successfully restored by themselves
244+
into a clean database.
245+
</para>
246+
</note>
247+
</listitem>
248+
</varlistentry>
249+
218250
<varlistentry>
219251
<term><option>-E <replaceable class="parameter">encoding</replaceable></option></term>
220252
<term><option>--encoding=<replaceable class="parameter">encoding</replaceable></option></term>
@@ -1079,11 +1111,12 @@ PostgreSQL documentation
10791111
<term><option>--strict-names</option></term>
10801112
<listitem>
10811113
<para>
1082-
Require that each schema
1083-
(<option>-n</option>/<option>--schema</option>) and table
1084-
(<option>-t</option>/<option>--table</option>) qualifier match at
1085-
least one schema/table in the database to be dumped. Note that if
1086-
none of the schema/table qualifiers find
1114+
Require that each
1115+
extension (<option>-e</option>/<option>--extension</option>),
1116+
schema (<option>-n</option>/<option>--schema</option>) and
1117+
table (<option>-t</option>/<option>--table</option>) qualifier
1118+
match at least one extension/schema/table in the database to be dumped.
1119+
Note that if none of the extension/schema/table qualifiers find
10871120
matches, <application>pg_dump</application> will generate an error
10881121
even without <option>--strict-names</option>.
10891122
</para>

src/bin/pg_dump/pg_dump.c

+87-6
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
123123
static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
124124
static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
125125

126+
static SimpleStringList extension_include_patterns = {NULL, NULL};
127+
static SimpleOidList extension_include_oids = {NULL, NULL};
128+
126129
static const CatalogId nilCatalogId = {0, 0};
127130

128131
/* override for standard extra_float_digits setting */
@@ -151,6 +154,10 @@ static void expand_schema_name_patterns(Archive *fout,
151154
SimpleStringList *patterns,
152155
SimpleOidList *oids,
153156
bool strict_names);
157+
static void expand_extension_name_patterns(Archive *fout,
158+
SimpleStringList *patterns,
159+
SimpleOidList *oids,
160+
bool strict_names);
154161
static void expand_foreign_server_name_patterns(Archive *fout,
155162
SimpleStringList *patterns,
156163
SimpleOidList *oids);
@@ -335,6 +342,7 @@ main(int argc, char **argv)
335342
{"clean", no_argument, NULL, 'c'},
336343
{"create", no_argument, NULL, 'C'},
337344
{"dbname", required_argument, NULL, 'd'},
345+
{"extension", required_argument, NULL, 'e'},
338346
{"file", required_argument, NULL, 'f'},
339347
{"format", required_argument, NULL, 'F'},
340348
{"host", required_argument, NULL, 'h'},
@@ -426,7 +434,7 @@ main(int argc, char **argv)
426434

427435
InitDumpOptions(&dopt);
428436

429-
while ((c = getopt_long(argc, argv, "abBcCd:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:",
437+
while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:",
430438
long_options, &optindex)) != -1)
431439
{
432440
switch (c)
@@ -455,6 +463,11 @@ main(int argc, char **argv)
455463
dopt.cparams.dbname = pg_strdup(optarg);
456464
break;
457465

466+
case 'e': /* include extension(s) */
467+
simple_string_list_append(&extension_include_patterns, optarg);
468+
dopt.include_everything = false;
469+
break;
470+
458471
case 'E': /* Dump encoding */
459472
dumpencoding = pg_strdup(optarg);
460473
break;
@@ -834,6 +847,16 @@ main(int argc, char **argv)
834847

835848
/* non-matching exclusion patterns aren't an error */
836849

850+
/* Expand extension selection patterns into OID lists */
851+
if (extension_include_patterns.head != NULL)
852+
{
853+
expand_extension_name_patterns(fout, &extension_include_patterns,
854+
&extension_include_oids,
855+
strict_names);
856+
if (extension_include_oids.head == NULL)
857+
fatal("no matching extensions were found");
858+
}
859+
837860
/*
838861
* Dumping blobs is the default for dumps where an inclusion switch is not
839862
* used (an "include everything" dump). -B can be used to exclude blobs
@@ -1025,6 +1048,7 @@ help(const char *progname)
10251048
printf(_(" -B, --no-blobs exclude large objects in dump\n"));
10261049
printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
10271050
printf(_(" -C, --create include commands to create database in dump\n"));
1051+
printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
10281052
printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
10291053
printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
10301054
printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
@@ -1367,6 +1391,53 @@ expand_schema_name_patterns(Archive *fout,
13671391
destroyPQExpBuffer(query);
13681392
}
13691393

1394+
/*
1395+
* Find the OIDs of all extensions matching the given list of patterns,
1396+
* and append them to the given OID list.
1397+
*/
1398+
static void
1399+
expand_extension_name_patterns(Archive *fout,
1400+
SimpleStringList *patterns,
1401+
SimpleOidList *oids,
1402+
bool strict_names)
1403+
{
1404+
PQExpBuffer query;
1405+
PGresult *res;
1406+
SimpleStringListCell *cell;
1407+
int i;
1408+
1409+
if (patterns->head == NULL)
1410+
return; /* nothing to do */
1411+
1412+
query = createPQExpBuffer();
1413+
1414+
/*
1415+
* The loop below runs multiple SELECTs might sometimes result in
1416+
* duplicate entries in the OID list, but we don't care.
1417+
*/
1418+
for (cell = patterns->head; cell; cell = cell->next)
1419+
{
1420+
appendPQExpBufferStr(query,
1421+
"SELECT oid FROM pg_catalog.pg_extension e\n");
1422+
processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1423+
false, NULL, "e.extname", NULL, NULL);
1424+
1425+
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1426+
if (strict_names && PQntuples(res) == 0)
1427+
fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1428+
1429+
for (i = 0; i < PQntuples(res); i++)
1430+
{
1431+
simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1432+
}
1433+
1434+
PQclear(res);
1435+
resetPQExpBuffer(query);
1436+
}
1437+
1438+
destroyPQExpBuffer(query);
1439+
}
1440+
13701441
/*
13711442
* Find the OIDs of all foreign servers matching the given list of patterns,
13721443
* and append them to the given OID list.
@@ -1793,8 +1864,9 @@ selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
17931864
* Built-in extensions should be skipped except for checking ACLs, since we
17941865
* assume those will already be installed in the target database. We identify
17951866
* such extensions by their having OIDs in the range reserved for initdb.
1796-
* We dump all user-added extensions by default, or none of them if
1797-
* include_everything is false (i.e., a --schema or --table switch was given).
1867+
* We dump all user-added extensions by default. No extensions are dumped
1868+
* if include_everything is false (i.e., a --schema or --table switch was
1869+
* given), except if --extension specifies a list of extensions to dump.
17981870
*/
17991871
static void
18001872
selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
@@ -1807,9 +1879,18 @@ selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
18071879
if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
18081880
extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
18091881
else
1810-
extinfo->dobj.dump = extinfo->dobj.dump_contains =
1811-
dopt->include_everything ? DUMP_COMPONENT_ALL :
1812-
DUMP_COMPONENT_NONE;
1882+
{
1883+
/* check if there is a list of extensions to dump */
1884+
if (extension_include_oids.head != NULL)
1885+
extinfo->dobj.dump = extinfo->dobj.dump_contains =
1886+
simple_oid_list_member(&extension_include_oids,
1887+
extinfo->dobj.catId.oid) ?
1888+
DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1889+
else
1890+
extinfo->dobj.dump = extinfo->dobj.dump_contains =
1891+
dopt->include_everything ?
1892+
DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1893+
}
18131894
}
18141895

18151896
/*

src/test/modules/test_pg_dump/t/001_base.pl

+36-17
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,20 @@
194194
'pg_dump', '--no-sync', "--file=$tempdir/section_post_data.sql",
195195
'--section=post-data', 'postgres',
196196
],
197+
},
198+
with_extension => {
199+
dump_cmd => [
200+
'pg_dump', '--no-sync', "--file=$tempdir/with_extension.sql",
201+
'--extension=test_pg_dump', '--no-sync', 'postgres',
202+
],
203+
},
204+
205+
# plgsql in the list blocks the dump of extension test_pg_dump
206+
without_extension => {
207+
dump_cmd => [
208+
'pg_dump', '--no-sync', "--file=$tempdir/without_extension.sql",
209+
'--extension=plpgsql', '--no-sync', 'postgres',
210+
],
197211
},);
198212

199213
###############################################################
@@ -228,14 +242,16 @@
228242
# Tests which are considered 'full' dumps by pg_dump, but there
229243
# are flags used to exclude specific items (ACLs, blobs, etc).
230244
my %full_runs = (
231-
binary_upgrade => 1,
232-
clean => 1,
233-
clean_if_exists => 1,
234-
createdb => 1,
235-
defaults => 1,
236-
exclude_table => 1,
237-
no_privs => 1,
238-
no_owner => 1,);
245+
binary_upgrade => 1,
246+
clean => 1,
247+
clean_if_exists => 1,
248+
createdb => 1,
249+
defaults => 1,
250+
exclude_table => 1,
251+
no_privs => 1,
252+
no_owner => 1,
253+
with_extension => 1,
254+
without_extension => 1);
239255

240256
my %tests = (
241257
'ALTER EXTENSION test_pg_dump' => {
@@ -261,7 +277,7 @@
261277
schema_only => 1,
262278
section_pre_data => 1,
263279
},
264-
unlike => { binary_upgrade => 1, },
280+
unlike => { binary_upgrade => 1, without_extension => 1 },
265281
},
266282
267283
'CREATE ROLE regress_dump_test_role' => {
@@ -320,6 +336,7 @@
320336
section_data => 1,
321337
extension_schema => 1,
322338
},
339+
unlike => { without_extension => 1, },
323340
},
324341
325342
'CREATE TABLE regress_pg_dump_table' => {
@@ -343,8 +360,9 @@
343360
extension_schema => 1,
344361
},
345362
unlike => {
346-
binary_upgrade => 1,
347-
exclude_table => 1,
363+
binary_upgrade => 1,
364+
exclude_table => 1,
365+
without_extension => 1,
348366
},
349367
},
350368
@@ -367,7 +385,7 @@
367385
schema_only => 1,
368386
section_pre_data => 1,
369387
},
370-
unlike => { no_privs => 1, },
388+
unlike => { no_privs => 1, without_extension => 1, },
371389
},
372390
373391
'REVOKE GRANT OPTION FOR UPDATE ON SEQUENCE wgo_then_regular' => {
@@ -384,7 +402,7 @@
384402
schema_only => 1,
385403
section_pre_data => 1,
386404
},
387-
unlike => { no_privs => 1, },
405+
unlike => { no_privs => 1, without_extension => 1, },
388406
},
389407
390408
'CREATE ACCESS METHOD regress_test_am' => {
@@ -404,6 +422,7 @@
404422
schema_only => 1,
405423
section_pre_data => 1,
406424
},
425+
unlike => { without_extension => 1, },
407426
},
408427
409428
'GRANT SELECT regress_pg_dump_table_added pre-ALTER EXTENSION' => {
@@ -428,7 +447,7 @@
428447
schema_only => 1,
429448
section_pre_data => 1,
430449
},
431-
unlike => { no_privs => 1, },
450+
unlike => { no_privs => 1, without_extension => 1, },
432451
},
433452
434453
'GRANT SELECT ON TABLE regress_pg_dump_table' => {
@@ -462,7 +481,7 @@
462481
schema_only => 1,
463482
section_pre_data => 1,
464483
},
465-
unlike => { no_privs => 1, },
484+
unlike => { no_privs => 1, without_extension => 1 },
466485
},
467486
468487
'GRANT USAGE ON regress_pg_dump_table_col1_seq TO regress_dump_test_role'
@@ -478,7 +497,7 @@
478497
schema_only => 1,
479498
section_pre_data => 1,
480499
},
481-
unlike => { no_privs => 1, },
500+
unlike => { no_privs => 1, without_extension => 1, },
482501
},
483502
484503
'GRANT USAGE ON regress_pg_dump_seq TO regress_dump_test_role' => {
@@ -500,7 +519,7 @@
500519
schema_only => 1,
501520
section_pre_data => 1,
502521
},
503-
unlike => { no_privs => 1, },
522+
unlike => { no_privs => 1, without_extension => 1, },
504523
},
505524
506525
# Objects included in extension part of a schema created by this extension */

0 commit comments

Comments
 (0)