mirror of
https://github.com/postgres/postgres.git
synced 2025-05-05 09:19:17 +03:00
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
This commit is contained in:
parent
65158f497a
commit
6568cef26e
@ -215,6 +215,38 @@ PostgreSQL documentation
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>-e <replaceable class="parameter">pattern</replaceable></option></term>
|
||||||
|
<term><option>--extension=<replaceable class="parameter">pattern</replaceable></option></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Dump only extensions matching <replaceable
|
||||||
|
class="parameter">pattern</replaceable>. When this option is not
|
||||||
|
specified, all non-system extensions in the target database will be
|
||||||
|
dumped. Multiple extensions can be selected by writing multiple
|
||||||
|
<option>-e</option> switches. The <replaceable
|
||||||
|
class="parameter">pattern</replaceable> parameter is interpreted as a
|
||||||
|
pattern according to the same rules used by
|
||||||
|
<application>psql</application>'s <literal>\d</literal> commands (see
|
||||||
|
<xref linkend="app-psql-patterns"/>), so multiple extensions can also
|
||||||
|
be selected by writing wildcard characters in the pattern. When using
|
||||||
|
wildcards, be careful to quote the pattern if needed to prevent the
|
||||||
|
shell from expanding the wildcards.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<note>
|
||||||
|
<para>
|
||||||
|
When <option>-e</option> is specified,
|
||||||
|
<application>pg_dump</application> makes no attempt to dump any other
|
||||||
|
database objects that the selected extension(s) might depend upon.
|
||||||
|
Therefore, there is no guarantee that the results of a
|
||||||
|
specific-extension dump can be successfully restored by themselves
|
||||||
|
into a clean database.
|
||||||
|
</para>
|
||||||
|
</note>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>-E <replaceable class="parameter">encoding</replaceable></option></term>
|
<term><option>-E <replaceable class="parameter">encoding</replaceable></option></term>
|
||||||
<term><option>--encoding=<replaceable class="parameter">encoding</replaceable></option></term>
|
<term><option>--encoding=<replaceable class="parameter">encoding</replaceable></option></term>
|
||||||
@ -1079,11 +1111,12 @@ PostgreSQL documentation
|
|||||||
<term><option>--strict-names</option></term>
|
<term><option>--strict-names</option></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Require that each schema
|
Require that each
|
||||||
(<option>-n</option>/<option>--schema</option>) and table
|
extension (<option>-e</option>/<option>--extension</option>),
|
||||||
(<option>-t</option>/<option>--table</option>) qualifier match at
|
schema (<option>-n</option>/<option>--schema</option>) and
|
||||||
least one schema/table in the database to be dumped. Note that if
|
table (<option>-t</option>/<option>--table</option>) qualifier
|
||||||
none of the schema/table qualifiers find
|
match at least one extension/schema/table in the database to be dumped.
|
||||||
|
Note that if none of the extension/schema/table qualifiers find
|
||||||
matches, <application>pg_dump</application> will generate an error
|
matches, <application>pg_dump</application> will generate an error
|
||||||
even without <option>--strict-names</option>.
|
even without <option>--strict-names</option>.
|
||||||
</para>
|
</para>
|
||||||
|
@ -123,6 +123,9 @@ static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
|
|||||||
static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
|
static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
|
||||||
static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
|
static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
|
||||||
|
|
||||||
|
static SimpleStringList extension_include_patterns = {NULL, NULL};
|
||||||
|
static SimpleOidList extension_include_oids = {NULL, NULL};
|
||||||
|
|
||||||
static const CatalogId nilCatalogId = {0, 0};
|
static const CatalogId nilCatalogId = {0, 0};
|
||||||
|
|
||||||
/* override for standard extra_float_digits setting */
|
/* override for standard extra_float_digits setting */
|
||||||
@ -151,6 +154,10 @@ static void expand_schema_name_patterns(Archive *fout,
|
|||||||
SimpleStringList *patterns,
|
SimpleStringList *patterns,
|
||||||
SimpleOidList *oids,
|
SimpleOidList *oids,
|
||||||
bool strict_names);
|
bool strict_names);
|
||||||
|
static void expand_extension_name_patterns(Archive *fout,
|
||||||
|
SimpleStringList *patterns,
|
||||||
|
SimpleOidList *oids,
|
||||||
|
bool strict_names);
|
||||||
static void expand_foreign_server_name_patterns(Archive *fout,
|
static void expand_foreign_server_name_patterns(Archive *fout,
|
||||||
SimpleStringList *patterns,
|
SimpleStringList *patterns,
|
||||||
SimpleOidList *oids);
|
SimpleOidList *oids);
|
||||||
@ -335,6 +342,7 @@ main(int argc, char **argv)
|
|||||||
{"clean", no_argument, NULL, 'c'},
|
{"clean", no_argument, NULL, 'c'},
|
||||||
{"create", no_argument, NULL, 'C'},
|
{"create", no_argument, NULL, 'C'},
|
||||||
{"dbname", required_argument, NULL, 'd'},
|
{"dbname", required_argument, NULL, 'd'},
|
||||||
|
{"extension", required_argument, NULL, 'e'},
|
||||||
{"file", required_argument, NULL, 'f'},
|
{"file", required_argument, NULL, 'f'},
|
||||||
{"format", required_argument, NULL, 'F'},
|
{"format", required_argument, NULL, 'F'},
|
||||||
{"host", required_argument, NULL, 'h'},
|
{"host", required_argument, NULL, 'h'},
|
||||||
@ -426,7 +434,7 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
InitDumpOptions(&dopt);
|
InitDumpOptions(&dopt);
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, "abBcCd:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:",
|
while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:",
|
||||||
long_options, &optindex)) != -1)
|
long_options, &optindex)) != -1)
|
||||||
{
|
{
|
||||||
switch (c)
|
switch (c)
|
||||||
@ -455,6 +463,11 @@ main(int argc, char **argv)
|
|||||||
dopt.cparams.dbname = pg_strdup(optarg);
|
dopt.cparams.dbname = pg_strdup(optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'e': /* include extension(s) */
|
||||||
|
simple_string_list_append(&extension_include_patterns, optarg);
|
||||||
|
dopt.include_everything = false;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'E': /* Dump encoding */
|
case 'E': /* Dump encoding */
|
||||||
dumpencoding = pg_strdup(optarg);
|
dumpencoding = pg_strdup(optarg);
|
||||||
break;
|
break;
|
||||||
@ -834,6 +847,16 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
/* non-matching exclusion patterns aren't an error */
|
/* non-matching exclusion patterns aren't an error */
|
||||||
|
|
||||||
|
/* Expand extension selection patterns into OID lists */
|
||||||
|
if (extension_include_patterns.head != NULL)
|
||||||
|
{
|
||||||
|
expand_extension_name_patterns(fout, &extension_include_patterns,
|
||||||
|
&extension_include_oids,
|
||||||
|
strict_names);
|
||||||
|
if (extension_include_oids.head == NULL)
|
||||||
|
fatal("no matching extensions were found");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dumping blobs is the default for dumps where an inclusion switch is not
|
* Dumping blobs is the default for dumps where an inclusion switch is not
|
||||||
* used (an "include everything" dump). -B can be used to exclude blobs
|
* used (an "include everything" dump). -B can be used to exclude blobs
|
||||||
@ -1025,6 +1048,7 @@ help(const char *progname)
|
|||||||
printf(_(" -B, --no-blobs exclude large objects in dump\n"));
|
printf(_(" -B, --no-blobs exclude large objects in dump\n"));
|
||||||
printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
|
printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
|
||||||
printf(_(" -C, --create include commands to create database in dump\n"));
|
printf(_(" -C, --create include commands to create database in dump\n"));
|
||||||
|
printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
|
||||||
printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
|
printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
|
||||||
printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
|
printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
|
||||||
printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
|
printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
|
||||||
@ -1367,6 +1391,53 @@ expand_schema_name_patterns(Archive *fout,
|
|||||||
destroyPQExpBuffer(query);
|
destroyPQExpBuffer(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the OIDs of all extensions matching the given list of patterns,
|
||||||
|
* and append them to the given OID list.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
expand_extension_name_patterns(Archive *fout,
|
||||||
|
SimpleStringList *patterns,
|
||||||
|
SimpleOidList *oids,
|
||||||
|
bool strict_names)
|
||||||
|
{
|
||||||
|
PQExpBuffer query;
|
||||||
|
PGresult *res;
|
||||||
|
SimpleStringListCell *cell;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (patterns->head == NULL)
|
||||||
|
return; /* nothing to do */
|
||||||
|
|
||||||
|
query = createPQExpBuffer();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The loop below runs multiple SELECTs might sometimes result in
|
||||||
|
* duplicate entries in the OID list, but we don't care.
|
||||||
|
*/
|
||||||
|
for (cell = patterns->head; cell; cell = cell->next)
|
||||||
|
{
|
||||||
|
appendPQExpBufferStr(query,
|
||||||
|
"SELECT oid FROM pg_catalog.pg_extension e\n");
|
||||||
|
processSQLNamePattern(GetConnection(fout), query, cell->val, false,
|
||||||
|
false, NULL, "e.extname", NULL, NULL);
|
||||||
|
|
||||||
|
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
|
||||||
|
if (strict_names && PQntuples(res) == 0)
|
||||||
|
fatal("no matching extensions were found for pattern \"%s\"", cell->val);
|
||||||
|
|
||||||
|
for (i = 0; i < PQntuples(res); i++)
|
||||||
|
{
|
||||||
|
simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
resetPQExpBuffer(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyPQExpBuffer(query);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the OIDs of all foreign servers matching the given list of patterns,
|
* Find the OIDs of all foreign servers matching the given list of patterns,
|
||||||
* and append them to the given OID list.
|
* and append them to the given OID list.
|
||||||
@ -1793,8 +1864,9 @@ selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
|
|||||||
* Built-in extensions should be skipped except for checking ACLs, since we
|
* Built-in extensions should be skipped except for checking ACLs, since we
|
||||||
* assume those will already be installed in the target database. We identify
|
* assume those will already be installed in the target database. We identify
|
||||||
* such extensions by their having OIDs in the range reserved for initdb.
|
* such extensions by their having OIDs in the range reserved for initdb.
|
||||||
* We dump all user-added extensions by default, or none of them if
|
* We dump all user-added extensions by default. No extensions are dumped
|
||||||
* include_everything is false (i.e., a --schema or --table switch was given).
|
* if include_everything is false (i.e., a --schema or --table switch was
|
||||||
|
* given), except if --extension specifies a list of extensions to dump.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
|
selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
|
||||||
@ -1807,9 +1879,18 @@ selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
|
|||||||
if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
|
if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
|
||||||
extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
|
extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
/* check if there is a list of extensions to dump */
|
||||||
|
if (extension_include_oids.head != NULL)
|
||||||
extinfo->dobj.dump = extinfo->dobj.dump_contains =
|
extinfo->dobj.dump = extinfo->dobj.dump_contains =
|
||||||
dopt->include_everything ? DUMP_COMPONENT_ALL :
|
simple_oid_list_member(&extension_include_oids,
|
||||||
DUMP_COMPONENT_NONE;
|
extinfo->dobj.catId.oid) ?
|
||||||
|
DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
|
||||||
|
else
|
||||||
|
extinfo->dobj.dump = extinfo->dobj.dump_contains =
|
||||||
|
dopt->include_everything ?
|
||||||
|
DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -194,6 +194,20 @@ my %pgdump_runs = (
|
|||||||
'pg_dump', '--no-sync', "--file=$tempdir/section_post_data.sql",
|
'pg_dump', '--no-sync', "--file=$tempdir/section_post_data.sql",
|
||||||
'--section=post-data', 'postgres',
|
'--section=post-data', 'postgres',
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
with_extension => {
|
||||||
|
dump_cmd => [
|
||||||
|
'pg_dump', '--no-sync', "--file=$tempdir/with_extension.sql",
|
||||||
|
'--extension=test_pg_dump', '--no-sync', 'postgres',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
# plgsql in the list blocks the dump of extension test_pg_dump
|
||||||
|
without_extension => {
|
||||||
|
dump_cmd => [
|
||||||
|
'pg_dump', '--no-sync', "--file=$tempdir/without_extension.sql",
|
||||||
|
'--extension=plpgsql', '--no-sync', 'postgres',
|
||||||
|
],
|
||||||
},);
|
},);
|
||||||
|
|
||||||
###############################################################
|
###############################################################
|
||||||
@ -235,7 +249,9 @@ my %full_runs = (
|
|||||||
defaults => 1,
|
defaults => 1,
|
||||||
exclude_table => 1,
|
exclude_table => 1,
|
||||||
no_privs => 1,
|
no_privs => 1,
|
||||||
no_owner => 1,);
|
no_owner => 1,
|
||||||
|
with_extension => 1,
|
||||||
|
without_extension => 1);
|
||||||
|
|
||||||
my %tests = (
|
my %tests = (
|
||||||
'ALTER EXTENSION test_pg_dump' => {
|
'ALTER EXTENSION test_pg_dump' => {
|
||||||
@ -261,7 +277,7 @@ my %tests = (
|
|||||||
schema_only => 1,
|
schema_only => 1,
|
||||||
section_pre_data => 1,
|
section_pre_data => 1,
|
||||||
},
|
},
|
||||||
unlike => { binary_upgrade => 1, },
|
unlike => { binary_upgrade => 1, without_extension => 1 },
|
||||||
},
|
},
|
||||||
|
|
||||||
'CREATE ROLE regress_dump_test_role' => {
|
'CREATE ROLE regress_dump_test_role' => {
|
||||||
@ -320,6 +336,7 @@ my %tests = (
|
|||||||
section_data => 1,
|
section_data => 1,
|
||||||
extension_schema => 1,
|
extension_schema => 1,
|
||||||
},
|
},
|
||||||
|
unlike => { without_extension => 1, },
|
||||||
},
|
},
|
||||||
|
|
||||||
'CREATE TABLE regress_pg_dump_table' => {
|
'CREATE TABLE regress_pg_dump_table' => {
|
||||||
@ -345,6 +362,7 @@ my %tests = (
|
|||||||
unlike => {
|
unlike => {
|
||||||
binary_upgrade => 1,
|
binary_upgrade => 1,
|
||||||
exclude_table => 1,
|
exclude_table => 1,
|
||||||
|
without_extension => 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -367,7 +385,7 @@ my %tests = (
|
|||||||
schema_only => 1,
|
schema_only => 1,
|
||||||
section_pre_data => 1,
|
section_pre_data => 1,
|
||||||
},
|
},
|
||||||
unlike => { no_privs => 1, },
|
unlike => { no_privs => 1, without_extension => 1, },
|
||||||
},
|
},
|
||||||
|
|
||||||
'REVOKE GRANT OPTION FOR UPDATE ON SEQUENCE wgo_then_regular' => {
|
'REVOKE GRANT OPTION FOR UPDATE ON SEQUENCE wgo_then_regular' => {
|
||||||
@ -384,7 +402,7 @@ my %tests = (
|
|||||||
schema_only => 1,
|
schema_only => 1,
|
||||||
section_pre_data => 1,
|
section_pre_data => 1,
|
||||||
},
|
},
|
||||||
unlike => { no_privs => 1, },
|
unlike => { no_privs => 1, without_extension => 1, },
|
||||||
},
|
},
|
||||||
|
|
||||||
'CREATE ACCESS METHOD regress_test_am' => {
|
'CREATE ACCESS METHOD regress_test_am' => {
|
||||||
@ -404,6 +422,7 @@ my %tests = (
|
|||||||
schema_only => 1,
|
schema_only => 1,
|
||||||
section_pre_data => 1,
|
section_pre_data => 1,
|
||||||
},
|
},
|
||||||
|
unlike => { without_extension => 1, },
|
||||||
},
|
},
|
||||||
|
|
||||||
'GRANT SELECT regress_pg_dump_table_added pre-ALTER EXTENSION' => {
|
'GRANT SELECT regress_pg_dump_table_added pre-ALTER EXTENSION' => {
|
||||||
@ -428,7 +447,7 @@ my %tests = (
|
|||||||
schema_only => 1,
|
schema_only => 1,
|
||||||
section_pre_data => 1,
|
section_pre_data => 1,
|
||||||
},
|
},
|
||||||
unlike => { no_privs => 1, },
|
unlike => { no_privs => 1, without_extension => 1, },
|
||||||
},
|
},
|
||||||
|
|
||||||
'GRANT SELECT ON TABLE regress_pg_dump_table' => {
|
'GRANT SELECT ON TABLE regress_pg_dump_table' => {
|
||||||
@ -462,7 +481,7 @@ my %tests = (
|
|||||||
schema_only => 1,
|
schema_only => 1,
|
||||||
section_pre_data => 1,
|
section_pre_data => 1,
|
||||||
},
|
},
|
||||||
unlike => { no_privs => 1, },
|
unlike => { no_privs => 1, without_extension => 1 },
|
||||||
},
|
},
|
||||||
|
|
||||||
'GRANT USAGE ON regress_pg_dump_table_col1_seq TO regress_dump_test_role'
|
'GRANT USAGE ON regress_pg_dump_table_col1_seq TO regress_dump_test_role'
|
||||||
@ -478,7 +497,7 @@ my %tests = (
|
|||||||
schema_only => 1,
|
schema_only => 1,
|
||||||
section_pre_data => 1,
|
section_pre_data => 1,
|
||||||
},
|
},
|
||||||
unlike => { no_privs => 1, },
|
unlike => { no_privs => 1, without_extension => 1, },
|
||||||
},
|
},
|
||||||
|
|
||||||
'GRANT USAGE ON regress_pg_dump_seq TO regress_dump_test_role' => {
|
'GRANT USAGE ON regress_pg_dump_seq TO regress_dump_test_role' => {
|
||||||
@ -500,7 +519,7 @@ my %tests = (
|
|||||||
schema_only => 1,
|
schema_only => 1,
|
||||||
section_pre_data => 1,
|
section_pre_data => 1,
|
||||||
},
|
},
|
||||||
unlike => { no_privs => 1, },
|
unlike => { no_privs => 1, without_extension => 1, },
|
||||||
},
|
},
|
||||||
|
|
||||||
# Objects included in extension part of a schema created by this extension */
|
# Objects included in extension part of a schema created by this extension */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user