]> granicus.if.org Git - postgresql/commitdiff
pg_dump et al: Add --if-exists option
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 3 Mar 2014 18:02:18 +0000 (15:02 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 3 Mar 2014 18:02:18 +0000 (15:02 -0300)
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
doc/src/sgml/ref/pg_dump.sgml
doc/src/sgml/ref/pg_dumpall.sgml
doc/src/sgml/ref/pg_restore.sgml
src/bin/pg_dump/pg_backup.h
src/bin/pg_dump/pg_backup_archiver.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dumpall.c
src/bin/pg_dump/pg_restore.c

index 40c69f05421ecadeb0a0540b6773e00b78921294..1f0d4ded32e36988e00b9d8ce6f8085ebad60de1 100644 (file)
@@ -145,7 +145,8 @@ PostgreSQL documentation
        <para>
         Output commands to clean (drop)
         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.)
        </para>
 
@@ -649,6 +650,17 @@ PostgreSQL documentation
       </listitem>
      </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>
       <term><option>--disable-dollar-quoting</></term>
       <listitem>
index f33793985f9569762cf40c466b6acd8228f3a3d8..fcf5f77a6dbfb047b37f389e1b0e7e23797c08ae 100644 (file)
@@ -300,6 +300,17 @@ PostgreSQL documentation
       </listitem>
      </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>
       <term><option>--inserts</option></term>
       <listitem>
index cd60b2558a4e975eac1e1c0dce70d7ce2b58977c..4bc30ce679b9c87d05c3872def83773f7b3c8416 100644 (file)
       <listitem>
        <para>
         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.)
        </para>
       </listitem>
       </listitem>
      </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>
       <term><option>--no-data-for-failed-tables</option></term>
       <listitem>
index 6927968de0a0d7e087d14f05089111c90403f341..83f7216d50e6a66d557854a9f213325d16f703e1 100644 (file)
@@ -113,6 +113,7 @@ typedef struct _restoreOptions
        char       *superuser;          /* Username to use as superuser */
        char       *use_role;           /* Issue SET ROLE to this */
        int                     dropSchema;
+       int                     if_exists;
        const char *filename;
        int                     dataOnly;
        int                     schemaOnly;
index 46699a2d1474c91ee58b4612a53be5f6c07707be..946454019bd533471275b5e556b941b0cdc11838 100644 (file)
@@ -413,8 +413,77 @@ RestoreArchive(Archive *AHX)
                                /* Select owner and schema as necessary */
                                _becomeOwner(AH, te);
                                _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);
+                                       }
+                               }
                        }
                }
 
index 770e97dce971ea26cb4fc6e0cf1d11a45ba02723..f5a6bbb723e1f3185a2644a24c65d3c34417bcd8 100644 (file)
@@ -132,6 +132,7 @@ static int  binary_upgrade = 0;
 static int     disable_dollar_quoting = 0;
 static int     dump_inserts = 0;
 static int     column_inserts = 0;
+static int     if_exists = 0;
 static int     no_security_labels = 0;
 static int     no_synchronized_snapshots = 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-triggers", no_argument, &disable_triggers, 1},
                {"exclude-table-data", required_argument, NULL, 4},
+               {"if-exists", no_argument, &if_exists, 1},
                {"inserts", no_argument, &dump_inserts, 1},
                {"lock-wait-timeout", required_argument, NULL, 2},
                {"no-tablespaces", no_argument, &outputNoTablespaces, 1},
@@ -573,6 +575,9 @@ main(int argc, char **argv)
                exit_nicely(1);
        }
 
+       if (if_exists && !outputClean)
+               exit_horribly(NULL, "option --if-exists requires -c/--clean option\n");
+
        /* Identify archive format to emit */
        archiveFormat = parseArchiveFormat(format, &archiveMode);
 
@@ -805,6 +810,7 @@ main(int argc, char **argv)
        ropt->dropSchema = outputClean;
        ropt->dataOnly = dataOnly;
        ropt->schemaOnly = schemaOnly;
+       ropt->if_exists = if_exists;
        ropt->dumpSections = dumpSections;
        ropt->aclsSkip = aclsSkip;
        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-triggers           disable triggers during data-only restore\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(_("  --no-security-labels         do not dump security label assignments\n"));
        printf(_("  --no-synchronized-snapshots  do not use synchronized snapshots in parallel jobs\n"));
index 193c1a0c389a941b2e9a240eaaac39ba859fa999..f7c610deeb7001cf9c97595e9e2401cafaa7a645 100644 (file)
@@ -73,6 +73,7 @@ static int    binary_upgrade = 0;
 static int     column_inserts = 0;
 static int     disable_dollar_quoting = 0;
 static int     disable_triggers = 0;
+static int     if_exists = 0;
 static int     inserts = 0;
 static int     no_tablespaces = 0;
 static int     use_setsessauth = 0;
@@ -119,6 +120,7 @@ main(int argc, char *argv[])
                {"column-inserts", no_argument, &column_inserts, 1},
                {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1},
                {"disable-triggers", no_argument, &disable_triggers, 1},
+               {"if-exists", no_argument, &if_exists, 1},
                {"inserts", no_argument, &inserts, 1},
                {"lock-wait-timeout", required_argument, NULL, 2},
                {"no-tablespaces", no_argument, &no_tablespaces, 1},
@@ -334,6 +336,13 @@ main(int argc, char *argv[])
                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)
        {
                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(_("  --disable-dollar-quoting     disable dollar quoting, use SQL standard quoting\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(_("  --no-security-labels         do not dump security label assignments\n"));
        printf(_("  --no-tablespaces             do not dump tablespace assignments\n"));
@@ -624,7 +634,9 @@ dropRoles(PGconn *conn)
 
                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);
@@ -994,7 +1006,9 @@ dropTablespaces(PGconn *conn)
        {
                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);
@@ -1148,7 +1162,9 @@ dropDBs(PGconn *conn)
                if (strcmp(dbname, "template1") != 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));
                }
        }
 
index df9477b69fa97a02c000d1e50a21435f1a0d0653..f7f3f51ed3550613d893d2e55e220fa4e9281009 100644 (file)
@@ -70,6 +70,7 @@ main(int argc, char **argv)
        Archive    *AH;
        char       *inputFileSpec;
        static int      disable_triggers = 0;
+       static int      if_exists = 0;
        static int      no_data_for_failed_tables = 0;
        static int      outputNoTablespaces = 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
                 */
                {"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-tablespaces", no_argument, &outputNoTablespaces, 1},
                {"role", required_argument, NULL, 2},
@@ -336,6 +338,14 @@ main(int argc, char **argv)
        opts->use_setsessauth = use_setsessauth;
        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)
        {
                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(_("  -1, --single-transaction     restore as a single transaction\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"
                         "                               created\n"));
        printf(_("  --no-security-labels         do not restore security labels\n"));