pg_dump et al: Add --if-exists option

This option makes pg_dump, pg_dumpall and pg_restore inject an IF EXISTS
clause to each DROP command they emit.  (In pg_dumpall, the clause is
not added to individual objects drops, but rather to the CREATE DATABASE
commands, as well as CREATE ROLE and CREATE TABLESPACE.)

This allows for a better user dump experience when using --clean in case
some objects do not already exist.  Per bug #7873 by Dave Rolsky.

Author: Pavel Stěhule
Reviewed-by: Jeevan Chalke, Álvaro Herrera, Josh Kupershmidt
This commit is contained in:
Alvaro Herrera 2014-03-03 15:02:18 -03:00
parent 34c6d9611d
commit 9067310cc5
8 changed files with 146 additions and 7 deletions

View File

@ -145,7 +145,8 @@ PostgreSQL documentation
<para> <para>
Output commands to clean (drop) Output commands to clean (drop)
database objects prior to outputting the commands for creating them. database objects prior to outputting the commands for creating them.
(Restore might generate some harmless error messages, if any objects (Unless <option>--if-exists</> is also specified,
restore might generate some harmless error messages, if any objects
were not present in the destination database.) were not present in the destination database.)
</para> </para>
@ -649,6 +650,17 @@ PostgreSQL documentation
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--if-exists</option></term>
<listitem>
<para>
Use conditional commands (i.e. add an <literal>IF EXISTS</literal>
clause) when cleaning database objects. This option is not valid
unless <option>--clean</> is also specified.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--disable-dollar-quoting</></term> <term><option>--disable-dollar-quoting</></term>
<listitem> <listitem>

View File

@ -300,6 +300,17 @@ PostgreSQL documentation
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--if-exists</option></term>
<listitem>
<para>
Use conditional commands (i.e. add an <literal>IF EXISTS</literal>
clause) to clean databases and other objects. This option is not valid
unless <option>--clean</> is also specified.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--inserts</option></term> <term><option>--inserts</option></term>
<listitem> <listitem>

View File

@ -109,7 +109,8 @@
<listitem> <listitem>
<para> <para>
Clean (drop) database objects before recreating them. Clean (drop) database objects before recreating them.
(This might generate some harmless error messages, if any objects (Unless <option>--if-exists</> is used,
this might generate some harmless error messages, if any objects
were not present in the destination database.) were not present in the destination database.)
</para> </para>
</listitem> </listitem>
@ -489,6 +490,17 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--if-exists</option></term>
<listitem>
<para>
Use conditional commands (i.e. add an <literal>IF EXISTS</literal>
clause) when cleaning database objects. This option is not valid
unless <option>--clean</> is also specified.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--no-data-for-failed-tables</option></term> <term><option>--no-data-for-failed-tables</option></term>
<listitem> <listitem>

View File

@ -113,6 +113,7 @@ typedef struct _restoreOptions
char *superuser; /* Username to use as superuser */ char *superuser; /* Username to use as superuser */
char *use_role; /* Issue SET ROLE to this */ char *use_role; /* Issue SET ROLE to this */
int dropSchema; int dropSchema;
int if_exists;
const char *filename; const char *filename;
int dataOnly; int dataOnly;
int schemaOnly; int schemaOnly;

View File

@ -413,8 +413,77 @@ RestoreArchive(Archive *AHX)
/* Select owner and schema as necessary */ /* Select owner and schema as necessary */
_becomeOwner(AH, te); _becomeOwner(AH, te);
_selectOutputSchema(AH, te->namespace); _selectOutputSchema(AH, te->namespace);
/* Drop it */
ahprintf(AH, "%s", te->dropStmt); /*
* Now emit the DROP command, if the object has one. Note we
* don't necessarily emit it verbatim; at this point we add an
* appropriate IF EXISTS clause, if the user requested it.
*/
if (*te->dropStmt != '\0')
{
if (!ropt->if_exists)
{
/* No --if-exists? Then just use the original */
ahprintf(AH, "%s", te->dropStmt);
}
else
{
char buffer[40];
char *mark;
char *dropStmt = pg_strdup(te->dropStmt);
char *dropStmtPtr = dropStmt;
PQExpBuffer ftStmt = createPQExpBuffer();
/*
* Need to inject IF EXISTS clause after ALTER TABLE
* part in ALTER TABLE .. DROP statement
*/
if (strncmp(dropStmt, "ALTER TABLE", 11) == 0)
{
appendPQExpBuffer(ftStmt,
"ALTER TABLE IF EXISTS");
dropStmt = dropStmt + 11;
}
/*
* ALTER TABLE..ALTER COLUMN..DROP DEFAULT does not
* support the IF EXISTS clause, and therefore we
* simply emit the original command for such objects.
* For other objects, we need to extract the first part
* of the DROP which includes the object type. Most of
* the time this matches te->desc, so search for that;
* however for the different kinds of CONSTRAINTs, we
* know to search for hardcoded "DROP CONSTRAINT"
* instead.
*/
if (strcmp(te->desc, "DEFAULT") == 0)
appendPQExpBuffer(ftStmt, "%s", dropStmt);
else
{
if (strcmp(te->desc, "CONSTRAINT") == 0 ||
strcmp(te->desc, "CHECK CONSTRAINT") == 0 ||
strcmp(te->desc, "FK CONSTRAINT") == 0)
strcpy(buffer, "DROP CONSTRAINT");
else
snprintf(buffer, sizeof(buffer), "DROP %s",
te->desc);
mark = strstr(dropStmt, buffer);
Assert(mark != NULL);
*mark = '\0';
appendPQExpBuffer(ftStmt, "%s%s IF EXISTS%s",
dropStmt, buffer,
mark + strlen(buffer));
}
ahprintf(AH, "%s", ftStmt->data);
destroyPQExpBuffer(ftStmt);
pg_free(dropStmtPtr);
}
}
} }
} }

View File

@ -132,6 +132,7 @@ static int binary_upgrade = 0;
static int disable_dollar_quoting = 0; static int disable_dollar_quoting = 0;
static int dump_inserts = 0; static int dump_inserts = 0;
static int column_inserts = 0; static int column_inserts = 0;
static int if_exists = 0;
static int no_security_labels = 0; static int no_security_labels = 0;
static int no_synchronized_snapshots = 0; static int no_synchronized_snapshots = 0;
static int no_unlogged_table_data = 0; static int no_unlogged_table_data = 0;
@ -345,6 +346,7 @@ main(int argc, char **argv)
{"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1}, {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1},
{"disable-triggers", no_argument, &disable_triggers, 1}, {"disable-triggers", no_argument, &disable_triggers, 1},
{"exclude-table-data", required_argument, NULL, 4}, {"exclude-table-data", required_argument, NULL, 4},
{"if-exists", no_argument, &if_exists, 1},
{"inserts", no_argument, &dump_inserts, 1}, {"inserts", no_argument, &dump_inserts, 1},
{"lock-wait-timeout", required_argument, NULL, 2}, {"lock-wait-timeout", required_argument, NULL, 2},
{"no-tablespaces", no_argument, &outputNoTablespaces, 1}, {"no-tablespaces", no_argument, &outputNoTablespaces, 1},
@ -573,6 +575,9 @@ main(int argc, char **argv)
exit_nicely(1); exit_nicely(1);
} }
if (if_exists && !outputClean)
exit_horribly(NULL, "option --if-exists requires -c/--clean option\n");
/* Identify archive format to emit */ /* Identify archive format to emit */
archiveFormat = parseArchiveFormat(format, &archiveMode); archiveFormat = parseArchiveFormat(format, &archiveMode);
@ -805,6 +810,7 @@ main(int argc, char **argv)
ropt->dropSchema = outputClean; ropt->dropSchema = outputClean;
ropt->dataOnly = dataOnly; ropt->dataOnly = dataOnly;
ropt->schemaOnly = schemaOnly; ropt->schemaOnly = schemaOnly;
ropt->if_exists = if_exists;
ropt->dumpSections = dumpSections; ropt->dumpSections = dumpSections;
ropt->aclsSkip = aclsSkip; ropt->aclsSkip = aclsSkip;
ropt->superuser = outputSuperuser; ropt->superuser = outputSuperuser;
@ -886,6 +892,7 @@ help(const char *progname)
printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n")); printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
printf(_(" --disable-triggers disable triggers during data-only restore\n")); printf(_(" --disable-triggers disable triggers during data-only restore\n"));
printf(_(" --exclude-table-data=TABLE do NOT dump data for the named table(s)\n")); printf(_(" --exclude-table-data=TABLE do NOT dump data for the named table(s)\n"));
printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
printf(_(" --inserts dump data as INSERT commands, rather than COPY\n")); printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
printf(_(" --no-security-labels do not dump security label assignments\n")); printf(_(" --no-security-labels do not dump security label assignments\n"));
printf(_(" --no-synchronized-snapshots do not use synchronized snapshots in parallel jobs\n")); printf(_(" --no-synchronized-snapshots do not use synchronized snapshots in parallel jobs\n"));

View File

@ -73,6 +73,7 @@ static int binary_upgrade = 0;
static int column_inserts = 0; static int column_inserts = 0;
static int disable_dollar_quoting = 0; static int disable_dollar_quoting = 0;
static int disable_triggers = 0; static int disable_triggers = 0;
static int if_exists = 0;
static int inserts = 0; static int inserts = 0;
static int no_tablespaces = 0; static int no_tablespaces = 0;
static int use_setsessauth = 0; static int use_setsessauth = 0;
@ -119,6 +120,7 @@ main(int argc, char *argv[])
{"column-inserts", no_argument, &column_inserts, 1}, {"column-inserts", no_argument, &column_inserts, 1},
{"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1}, {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1},
{"disable-triggers", no_argument, &disable_triggers, 1}, {"disable-triggers", no_argument, &disable_triggers, 1},
{"if-exists", no_argument, &if_exists, 1},
{"inserts", no_argument, &inserts, 1}, {"inserts", no_argument, &inserts, 1},
{"lock-wait-timeout", required_argument, NULL, 2}, {"lock-wait-timeout", required_argument, NULL, 2},
{"no-tablespaces", no_argument, &no_tablespaces, 1}, {"no-tablespaces", no_argument, &no_tablespaces, 1},
@ -334,6 +336,13 @@ main(int argc, char *argv[])
exit_nicely(1); exit_nicely(1);
} }
if (if_exists && !output_clean)
{
fprintf(stderr, _("%s: option --if-exists requires -c/--clean option\n"),
progname);
exit_nicely(1);
}
if (roles_only && tablespaces_only) if (roles_only && tablespaces_only)
{ {
fprintf(stderr, _("%s: options -r/--roles-only and -t/--tablespaces-only cannot be used together\n"), fprintf(stderr, _("%s: options -r/--roles-only and -t/--tablespaces-only cannot be used together\n"),
@ -564,6 +573,7 @@ help(void)
printf(_(" --column-inserts dump data as INSERT commands with column names\n")); printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n")); printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
printf(_(" --disable-triggers disable triggers during data-only restore\n")); printf(_(" --disable-triggers disable triggers during data-only restore\n"));
printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
printf(_(" --inserts dump data as INSERT commands, rather than COPY\n")); printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
printf(_(" --no-security-labels do not dump security label assignments\n")); printf(_(" --no-security-labels do not dump security label assignments\n"));
printf(_(" --no-tablespaces do not dump tablespace assignments\n")); printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
@ -624,7 +634,9 @@ dropRoles(PGconn *conn)
rolename = PQgetvalue(res, i, i_rolname); rolename = PQgetvalue(res, i, i_rolname);
fprintf(OPF, "DROP ROLE %s;\n", fmtId(rolename)); fprintf(OPF, "DROP ROLE %s%s;\n",
if_exists ? "IF EXISTS " : "",
fmtId(rolename));
} }
PQclear(res); PQclear(res);
@ -994,7 +1006,9 @@ dropTablespaces(PGconn *conn)
{ {
char *spcname = PQgetvalue(res, i, 0); char *spcname = PQgetvalue(res, i, 0);
fprintf(OPF, "DROP TABLESPACE %s;\n", fmtId(spcname)); fprintf(OPF, "DROP TABLESPACE %s%s;\n",
if_exists ? "IF EXISTS " : "",
fmtId(spcname));
} }
PQclear(res); PQclear(res);
@ -1148,7 +1162,9 @@ dropDBs(PGconn *conn)
if (strcmp(dbname, "template1") != 0 && if (strcmp(dbname, "template1") != 0 &&
strcmp(dbname, "postgres") != 0) strcmp(dbname, "postgres") != 0)
{ {
fprintf(OPF, "DROP DATABASE %s;\n", fmtId(dbname)); fprintf(OPF, "DROP DATABASE %s%s;\n",
if_exists ? "IF EXISTS " : "",
fmtId(dbname));
} }
} }

View File

@ -70,6 +70,7 @@ main(int argc, char **argv)
Archive *AH; Archive *AH;
char *inputFileSpec; char *inputFileSpec;
static int disable_triggers = 0; static int disable_triggers = 0;
static int if_exists = 0;
static int no_data_for_failed_tables = 0; static int no_data_for_failed_tables = 0;
static int outputNoTablespaces = 0; static int outputNoTablespaces = 0;
static int use_setsessauth = 0; static int use_setsessauth = 0;
@ -110,6 +111,7 @@ main(int argc, char **argv)
* the following options don't have an equivalent short option letter * the following options don't have an equivalent short option letter
*/ */
{"disable-triggers", no_argument, &disable_triggers, 1}, {"disable-triggers", no_argument, &disable_triggers, 1},
{"if-exists", no_argument, &if_exists, 1},
{"no-data-for-failed-tables", no_argument, &no_data_for_failed_tables, 1}, {"no-data-for-failed-tables", no_argument, &no_data_for_failed_tables, 1},
{"no-tablespaces", no_argument, &outputNoTablespaces, 1}, {"no-tablespaces", no_argument, &outputNoTablespaces, 1},
{"role", required_argument, NULL, 2}, {"role", required_argument, NULL, 2},
@ -336,6 +338,14 @@ main(int argc, char **argv)
opts->use_setsessauth = use_setsessauth; opts->use_setsessauth = use_setsessauth;
opts->no_security_labels = no_security_labels; opts->no_security_labels = no_security_labels;
if (if_exists && !opts->dropSchema)
{
fprintf(stderr, _("%s: option --if-exists requires -c/--clean option\n"),
progname);
exit_nicely(1);
}
opts->if_exists = if_exists;
if (opts->formatName) if (opts->formatName)
{ {
switch (opts->formatName[0]) switch (opts->formatName[0])
@ -450,6 +460,7 @@ usage(const char *progname)
printf(_(" -x, --no-privileges skip restoration of access privileges (grant/revoke)\n")); printf(_(" -x, --no-privileges skip restoration of access privileges (grant/revoke)\n"));
printf(_(" -1, --single-transaction restore as a single transaction\n")); printf(_(" -1, --single-transaction restore as a single transaction\n"));
printf(_(" --disable-triggers disable triggers during data-only restore\n")); printf(_(" --disable-triggers disable triggers during data-only restore\n"));
printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
printf(_(" --no-data-for-failed-tables do not restore data of tables that could not be\n" printf(_(" --no-data-for-failed-tables do not restore data of tables that could not be\n"
" created\n")); " created\n"));
printf(_(" --no-security-labels do not restore security labels\n")); printf(_(" --no-security-labels do not restore security labels\n"));