]> granicus.if.org Git - postgresql/commitdiff
Fix pg_dumpall so that when --clean is specified, it drops roles and
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 11 Apr 2009 20:23:05 +0000 (20:23 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 11 Apr 2009 20:23:05 +0000 (20:23 +0000)
tablespaces in an order that has some chance of working.
Per a complaint from Kevin Bailey.

This is a pre-existing bug, but given the lack of prior complaints I'm
not sure it's worth back-patching.  In most cases failure of the DROP
commands wouldn't be that important anyway.

In passing, fix syntax errors in dumpCreateDB()'s queries for old servers;
these were apparently introduced in recent binary_upgrade patch.

src/bin/pg_dump/pg_dumpall.c

index 0d6d1e450c6da5bf44df2cf40ffcf908c41be3a3..c43e905f087cd0b39aa7678776105c12c3d1f43a 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.123 2009/04/08 19:02:37 heikki Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.124 2009/04/11 20:23:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,10 +33,13 @@ static const char *progname;
 
 static void help(void);
 
+static void dropRoles(PGconn *conn);
 static void dumpRoles(PGconn *conn);
 static void dumpRoleMembership(PGconn *conn);
 static void dumpGroups(PGconn *conn);
+static void dropTablespaces(PGconn *conn);
 static void dumpTablespaces(PGconn *conn);
+static void dropDBs(PGconn *conn);
 static void dumpCreateDB(PGconn *conn);
 static void dumpDatabaseConfig(PGconn *conn, const char *dbname);
 static void dumpUserConfig(PGconn *conn, const char *username);
@@ -54,7 +57,6 @@ static void executeCommand(PGconn *conn, const char *query);
 
 static char pg_dump_bin[MAXPGPATH];
 static PQExpBuffer pgdumpopts;
-static bool output_clean = false;
 static bool skip_acls = false;
 static bool verbose = false;
 
@@ -82,6 +84,7 @@ main(int argc, char *argv[])
        enum trivalue prompt_password = TRI_DEFAULT;
        bool            data_only = false;
        bool            globals_only = false;
+       bool            output_clean = false;
        bool            roles_only = false;
        bool            tablespaces_only = false;
        bool            schema_only = false;
@@ -90,8 +93,9 @@ main(int argc, char *argv[])
        const char *std_strings;
        int                     c,
                                ret;
+       int                     optindex;
 
-       struct option long_options[] = {
+       static struct option long_options[] = {
                {"data-only", no_argument, NULL, 'a'},
                {"clean", no_argument, NULL, 'c'},
                {"file", required_argument, NULL, 'f'},
@@ -130,8 +134,6 @@ main(int argc, char *argv[])
                {NULL, 0, NULL, 0}
        };
 
-       int                     optindex;
-
        set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
 
        progname = get_progname(argv[0]);
@@ -442,16 +444,41 @@ main(int argc, char *argv[])
 
        fprintf(OPF, "\\connect postgres\n\n");
 
+       /* Replicate encoding and std_strings in output */
+       fprintf(OPF, "SET client_encoding = '%s';\n",
+                       pg_encoding_to_char(encoding));
+       fprintf(OPF, "SET standard_conforming_strings = %s;\n", std_strings);
+       if (strcmp(std_strings, "off") == 0)
+               fprintf(OPF, "SET escape_string_warning = off;\n");
+       fprintf(OPF, "\n");
+
        if (!data_only)
        {
-               /* Replicate encoding and std_strings in output */
-               fprintf(OPF, "SET client_encoding = '%s';\n",
-                               pg_encoding_to_char(encoding));
-               fprintf(OPF, "SET standard_conforming_strings = %s;\n", std_strings);
-               if (strcmp(std_strings, "off") == 0)
-                       fprintf(OPF, "SET escape_string_warning = 'off';\n");
-               fprintf(OPF, "\n");
+               /*
+                * If asked to --clean, do that first.  We can avoid detailed
+                * dependency analysis because databases never depend on each other,
+                * and tablespaces never depend on each other.  Roles could have
+                * grants to each other, but DROP ROLE will clean those up silently.
+                */
+               if (output_clean)
+               {
+                       if (!globals_only && !roles_only && !tablespaces_only)
+                               dropDBs(conn);
 
+                       if (!roles_only && !no_tablespaces)
+                       {
+                               if (server_version >= 80000)
+                                       dropTablespaces(conn);
+                       }
+
+                       if (!tablespaces_only)
+                               dropRoles(conn);
+               }
+
+               /*
+                * Now create objects as requested.  Be careful that option logic
+                * here is the same as for drops above.
+                */
                if (!tablespaces_only)
                {
                        /* Dump roles (users) */
@@ -492,7 +519,6 @@ main(int argc, char *argv[])
 }
 
 
-
 static void
 help(void)
 {
@@ -541,6 +567,48 @@ help(void)
 }
 
 
+/*
+ * Drop roles
+ */
+static void
+dropRoles(PGconn *conn)
+{
+       PGresult   *res;
+       int                     i_rolname;
+       int                     i;
+
+       if (server_version >= 80100)
+               res = executeQuery(conn,
+                                                  "SELECT rolname "
+                                                  "FROM pg_authid "
+                                                  "ORDER BY 1");
+       else
+               res = executeQuery(conn,
+                                                  "SELECT usename as rolname "
+                                                  "FROM pg_shadow "
+                                                  "UNION "
+                                                  "SELECT groname as rolname "
+                                                  "FROM pg_group "
+                                                  "ORDER BY 1");
+
+       i_rolname = PQfnumber(res, "rolname");
+
+       if (PQntuples(res) > 0)
+               fprintf(OPF, "--\n-- Drop roles\n--\n\n");
+
+       for (i = 0; i < PQntuples(res); i++)
+       {
+               const char *rolename;
+
+               rolename = PQgetvalue(res, i, i_rolname);
+
+               fprintf(OPF, "DROP ROLE %s;\n", fmtId(rolename));
+       }
+
+       PQclear(res);
+
+       fprintf(OPF, "\n\n");
+}
 
 /*
  * Dump roles
@@ -637,14 +705,12 @@ dumpRoles(PGconn *conn)
 
                resetPQExpBuffer(buf);
 
-               if (output_clean)
-                       appendPQExpBuffer(buf, "DROP ROLE %s;\n", fmtId(rolename));
-
                /*
                 * We dump CREATE ROLE followed by ALTER ROLE to ensure that the role
-                * will acquire the right properties even if it already exists. (The
-                * above DROP may therefore seem redundant, but it isn't really,
-                * because this technique doesn't get rid of role memberships.)
+                * will acquire the right properties even if it already exists (ie,
+                * it won't hurt for the CREATE to fail).  This is particularly
+                * important for the role we are connected as, since even with --clean
+                * we will have failed to drop it.
                 */
                appendPQExpBuffer(buf, "CREATE ROLE %s;\n", fmtId(rolename));
                appendPQExpBuffer(buf, "ALTER ROLE %s WITH", fmtId(rolename));
@@ -834,6 +900,40 @@ dumpGroups(PGconn *conn)
        fprintf(OPF, "\n\n");
 }
 
+
+/*
+ * Drop tablespaces.
+ */
+static void
+dropTablespaces(PGconn *conn)
+{
+       PGresult   *res;
+       int                     i;
+
+       /*
+        * Get all tablespaces except built-in ones (which we assume are named
+        * pg_xxx)
+        */
+       res = executeQuery(conn, "SELECT spcname "
+                                          "FROM pg_catalog.pg_tablespace "
+                                          "WHERE spcname !~ '^pg_' "
+                                          "ORDER BY 1");
+
+       if (PQntuples(res) > 0)
+               fprintf(OPF, "--\n-- Drop tablespaces\n--\n\n");
+
+       for (i = 0; i < PQntuples(res); i++)
+       {
+               char       *spcname = PQgetvalue(res, i, 0);
+
+               fprintf(OPF, "DROP TABLESPACE %s;\n", fmtId(spcname));
+       }
+
+       PQclear(res);
+
+       fprintf(OPF, "\n\n");
+}
+
 /*
  * Dump tablespaces.
  */
@@ -880,9 +980,6 @@ dumpTablespaces(PGconn *conn)
                /* needed for buildACLCommands() */
                fspcname = strdup(fmtId(spcname));
 
-               if (output_clean)
-                       appendPQExpBuffer(buf, "DROP TABLESPACE %s;\n", fspcname);
-
                appendPQExpBuffer(buf, "CREATE TABLESPACE %s", fspcname);
                appendPQExpBuffer(buf, " OWNER %s", fmtId(spcowner));
 
@@ -917,6 +1014,53 @@ dumpTablespaces(PGconn *conn)
        fprintf(OPF, "\n\n");
 }
 
+
+/*
+ * Dump commands to drop each database.
+ *
+ * This should match the set of databases targeted by dumpCreateDB().
+ */
+static void
+dropDBs(PGconn *conn)
+{
+       PGresult   *res;
+       int                     i;
+
+       if (server_version >= 70100)
+               res = executeQuery(conn,
+                                                  "SELECT datname "
+                                                  "FROM pg_database d "
+                                                  "WHERE datallowconn ORDER BY 1");
+       else
+               res = executeQuery(conn,
+                                                  "SELECT datname "
+                                                  "FROM pg_database d "
+                                                  "ORDER BY 1");
+
+       if (PQntuples(res) > 0)
+               fprintf(OPF, "--\n-- Drop databases\n--\n\n");
+
+       for (i = 0; i < PQntuples(res); i++)
+       {
+               char       *dbname = PQgetvalue(res, i, 0);
+
+               /*
+                * Skip "template1" and "postgres"; the restore script is almost
+                * certainly going to be run in one or the other, and we don't know
+                * which.  This must agree with dumpCreateDB's choices!
+                */
+               if (strcmp(dbname, "template1") != 0 &&
+                       strcmp(dbname, "postgres") != 0)
+               {
+                       fprintf(OPF, "DROP DATABASE %s;\n", fmtId(dbname));
+               }
+       }
+
+       PQclear(res);
+
+       fprintf(OPF, "\n\n");
+}
+
 /*
  * Dump commands to create each database.
  *
@@ -984,7 +1128,7 @@ dumpCreateDB(PGconn *conn)
                                        "(select usename from pg_shadow where usesysid=datdba), "
                                                   "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), "
                                                   "pg_encoding_to_char(d.encoding), "
-                                                  "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid"
+                                                  "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid"
                                                   "datistemplate, '' as datacl, -1 as datconnlimit, "
                                                   "'pg_default' AS dattablespace "
                                                   "FROM pg_database d "
@@ -999,7 +1143,7 @@ dumpCreateDB(PGconn *conn)
                                                   "SELECT datname, "
                                        "(select usename from pg_shadow where usesysid=datdba), "
                                                   "pg_encoding_to_char(d.encoding), "
-                                                  "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid"
+                                                  "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid"
                                                   "'f' as datistemplate, "
                                                   "'' as datacl, -1 as datconnlimit, "
                                                   "'pg_default' AS dattablespace "
@@ -1033,9 +1177,6 @@ dumpCreateDB(PGconn *conn)
                if (strcmp(dbname, "template1") != 0 &&
                        strcmp(dbname, "postgres") != 0)
                {
-                       if (output_clean)
-                               appendPQExpBuffer(buf, "DROP DATABASE %s;\n", fdbname);
-
                        appendPQExpBuffer(buf, "CREATE DATABASE %s", fdbname);
 
                        appendPQExpBuffer(buf, " WITH TEMPLATE = template0");
@@ -1120,7 +1261,6 @@ dumpCreateDB(PGconn *conn)
 }
 
 
-
 /*
  * Dump database-specific configuration
  */