Add a --role option to pg_dump, pg_dumpall, and pg_restore. This allows
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 5 Jan 2009 16:54:37 +0000 (16:54 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 5 Jan 2009 16:54:37 +0000 (16:54 +0000)
performing dumps and restores in accordance with a security policy that
forbids logging in directly as superuser, but instead specifies that you
should log into an admin account and then SET ROLE to the superuser.

In passing, clean up some ugly and mostly-broken code for quoting shell
arguments in pg_dumpall.

Benedek László, with some help from Tom Lane

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 2e30906baf1117843ca06d4015714a7e72196ea6..9c8563018f9943bf284dd80d5042169c41c3f809 100644 (file)
@@ -698,6 +698,23 @@ PostgreSQL documentation
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><option>--role=<replaceable class="parameter">rolename</replaceable></option></term>
+      <listitem>
+       <para>
+        Specifies a role name to be used to create the dump.
+        This option causes <application>pg_dump</> to issue a
+        <command>SET ROLE</> <replaceable class="parameter">rolename</>
+        command after connecting to the database. It is useful when the
+        authenticated user (specified by <option>-U</>) lacks privileges
+        needed by <application>pg_dump</>, but can switch to a role with
+        the required rights.  Some installations have a policy against
+        logging in directly as a superuser, and use of this option allows
+        dumps to be made without violating the policy.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
  </refsect1>
index ec40890ad32b97e5ecc9994c74817ad03b14f88b..818c79efb3b7e59e4e24c2e88704c99813e4dba3 100644 (file)
@@ -129,7 +129,7 @@ PostgreSQL documentation
        </para>
       </listitem>
      </varlistentry>
+
      <varlistentry>
       <term><option>-f <replaceable class="parameter">filename</replaceable></option></term>
       <term><option>--file=<replaceable class="parameter">filename</replaceable></option></term>
@@ -183,7 +183,7 @@ PostgreSQL documentation
         Do not output commands to set
         ownership of objects to match the original database.
         By default, <application>pg_dumpall</application> issues
-        <command>ALTER OWNER</> or 
+        <command>ALTER OWNER</> or
         <command>SET SESSION AUTHORIZATION</command>
         statements to set ownership of created schema elements.
         These statements
@@ -342,8 +342,8 @@ PostgreSQL documentation
 
    <variablelist>
      <varlistentry>
-      <term>-h <replaceable>host</replaceable></term>
-      <term>--host=<replaceable>host</replaceable></term>
+      <term><option>-h <replaceable>host</replaceable></option></term>
+      <term><option>--host=<replaceable>host</replaceable></option></term>
       <listitem>
        <para>
         Specifies the host name of the machine on which the database
@@ -354,10 +354,10 @@ PostgreSQL documentation
        </para>
       </listitem>
      </varlistentry>
+
      <varlistentry>
-      <term>-l <replaceable>dbname</replaceable></term>
-      <term>--database=<replaceable>dbname</replaceable></term>
+      <term><option>-l <replaceable>dbname</replaceable></option></term>
+      <term><option>--database=<replaceable>dbname</replaceable></option></term>
       <listitem>
        <para>
          Specifies the name of the database to connect to to dump global
@@ -369,8 +369,8 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
-      <term>-p <replaceable>port</replaceable></term>
-      <term>--port=<replaceable>port</replaceable></term>
+      <term><option>-p <replaceable>port</replaceable></option></term>
+      <term><option>--port=<replaceable>port</replaceable></option></term>
       <listitem>
        <para>
         Specifies the TCP port or local Unix domain socket file
@@ -382,8 +382,8 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
-      <term>-U <replaceable>username</replaceable></term>
-      <term>--username=<replaceable>username</replaceable></term>
+      <term><option>-U <replaceable>username</replaceable></option></term>
+      <term><option>--username=<replaceable>username</replaceable></option></term>
       <listitem>
        <para>
         User name to connect as.
@@ -392,12 +392,12 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
-      <term>-W</term>
-      <term>--password</term>
+      <term><option>-W</option></term>
+      <term><option>--password</option></term>
       <listitem>
        <para>
         Force <application>pg_dumpall</application> to prompt for a
-        password before connecting to a database.  
+        password before connecting to a database.
        </para>
 
        <para>
@@ -417,6 +417,23 @@ PostgreSQL documentation
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><option>--role=<replaceable class="parameter">rolename</replaceable></option></term>
+      <listitem>
+       <para>
+        Specifies a role name to be used to create the dump.
+        This option causes <application>pg_dumpall</> to issue a
+        <command>SET ROLE</> <replaceable class="parameter">rolename</>
+        command after connecting to the database. It is useful when the
+        authenticated user (specified by <option>-U</>) lacks privileges
+        needed by <application>pg_dumpall</>, but can switch to a role with
+        the required rights.  Some installations have a policy against
+        logging in directly as a superuser, and use of this option allows
+        dumps to be made without violating the policy.
+       </para>
+      </listitem>
+     </varlistentry>
    </variablelist>
   </para>
  </refsect1>
@@ -503,6 +520,6 @@ PostgreSQL documentation
     Check <xref linkend="app-pgdump"> for details on possible
     error conditions.
   </para>
- </refsect1>   
+ </refsect1>
 
 </refentry>
index 8e0f990cce39481e85d1442af0c5c76721d3c191..aec696323e86e92c56f6648a0986f17655a20894 100644 (file)
       <listitem>
        <para>
         Exit if an error is encountered while sending SQL commands to
-        the database. The default is to continue and to display a count of 
+        the database. The default is to continue and to display a count of
         errors at the end of the restoration.
        </para>
       </listitem>
         Do not output commands to set
         ownership of objects to match the original database.
         By default, <application>pg_restore</application> issues
-        <command>ALTER OWNER</> or 
+        <command>ALTER OWNER</> or
         <command>SET SESSION AUTHORIZATION</command>
         statements to set ownership of created schema elements.
         These statements will fail unless the initial connection to the
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-1</option></term>
+      <term><option>--single-transaction</option></term>
+      <listitem>
+       <para>
+        Execute the restore as a single transaction (that is, wrap the
+        emitted commands in <command>BEGIN</>/<command>COMMIT</>).  This
+        ensures that either all the commands complete successfully, or no
+        changes are applied. This option implies
+        <option>--exit-on-error</>.
+       </para>
+      </listitem>
+     </varlistentry>
+
     </variablelist>
    </para>
 
       <listitem>
        <para>
         Force <application>pg_restore</application> to prompt for a
-        password before connecting to a database.  
+        password before connecting to a database.
        </para>
 
        <para>
      </varlistentry>
 
      <varlistentry>
-      <term><option>-1</option></term>
-      <term><option>--single-transaction</option></term>
+      <term><option>--role=<replaceable class="parameter">rolename</replaceable></option></term>
       <listitem>
        <para>
-        Execute the restore as a single transaction (that is, wrap the
-        emitted commands in <command>BEGIN</>/<command>COMMIT</>).  This
-        ensures that either all the commands complete successfully, or no
-        changes are applied. This option implies
-        <option>--exit-on-error</>.
+        Specifies a role name to be used to perform the restore.
+        This option causes <application>pg_restore</> to issue a
+        <command>SET ROLE</> <replaceable class="parameter">rolename</>
+        command after connecting to the database. It is useful when the
+        authenticated user (specified by <option>-U</>) lacks privileges
+        needed by <application>pg_restore</>, but can switch to a role with
+        the required rights.  Some installations have a policy against
+        logging in directly as a superuser, and use of this option allows
+        restores to be performed without violating the policy.
        </para>
       </listitem>
      </varlistentry>
index c57bb22d9aaa8e31c571b2b43bece37a6e01bf0f..7117337d4032745053dfaf29cde0a5730e85d846 100644 (file)
@@ -89,6 +89,7 @@ typedef struct _restoreOptions
        int                     use_setsessauth;/* Use SET SESSION AUTHORIZATION commands
                                                                 * instead of OWNER TO */
        char       *superuser;          /* Username to use as superuser */
+       char       *use_role;           /* Issue SET ROLE to this */
        int                     dataOnly;
        int                     dropSchema;
        char       *filename;
index 0bbba25d328182902f834a5781e9bce9bca2fb01..114df8818083232bcf7ee1e114b751f1598cb2f0 100644 (file)
@@ -462,9 +462,8 @@ NewRestoreOptions(void)
 
        opts = (RestoreOptions *) calloc(1, sizeof(RestoreOptions));
 
+       /* set any fields that shouldn't default to zeroes */
        opts->format = archUnknown;
-       opts->suppressDumpWarnings = false;
-       opts->exit_on_error = false;
 
        return opts;
 }
@@ -2146,6 +2145,10 @@ _doSetFixedOutputState(ArchiveHandle *AH)
        ahprintf(AH, "SET standard_conforming_strings = %s;\n",
                         AH->public.std_strings ? "on" : "off");
 
+       /* Select the role to be used during restore */
+       if (AH->ropt && AH->ropt->use_role)
+               ahprintf(AH, "SET ROLE %s;\n", fmtId(AH->ropt->use_role));
+
        /* Make sure function checking is disabled */
        ahprintf(AH, "SET check_function_bodies = false;\n");
 
index 5ab0e4b3a135ac2304d6fc1163fb85260bd2d473..07f002a5fb625aaec27d9f7602b7a2973fbc0509 100644 (file)
@@ -227,6 +227,7 @@ main(int argc, char **argv)
        bool            outputBlobs = false;
        int                     outputNoOwner = 0;
        char       *outputSuperuser = NULL;
+       char       *use_role = NULL;
        int                     my_version;
        int                     optindex;
        RestoreOptions *ropt;
@@ -274,6 +275,7 @@ main(int argc, char **argv)
                {"disable-triggers", no_argument, &disable_triggers, 1},
                {"lock-wait-timeout", required_argument, NULL, 2},
                {"no-tablespaces", no_argument, &outputNoTablespaces, 1},
+               {"role", required_argument, NULL, 3},
                {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
 
                {NULL, 0, NULL, 0}
@@ -447,11 +449,14 @@ main(int argc, char **argv)
                                /* This covers the long options equivalent to -X xxx. */
                                break;
 
-                       case 2:
-                               /* lock-wait-timeout */
+                       case 2:                         /* lock-wait-timeout */
                                lockWaitTimeout = optarg;
                                break;
 
+                       case 3:                         /* SET ROLE */
+                               use_role = optarg;
+                               break;
+
                        default:
                                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
                                exit(1);
@@ -570,6 +575,16 @@ main(int argc, char **argv)
        std_strings = PQparameterStatus(g_conn, "standard_conforming_strings");
        g_fout->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
 
+       /* Set the role if requested */
+       if (use_role && g_fout->remoteVersion >= 80100)
+       {
+               PQExpBuffer query = createPQExpBuffer();
+
+               appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
+               do_sql_command(g_conn, query->data);
+               destroyPQExpBuffer(query);
+       }
+
        /* Set the datestyle to ISO to ensure the dump's portability */
        do_sql_command(g_conn, "SET DATESTYLE = ISO");
 
@@ -807,6 +822,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(_("  --no-tablespaces            do not dump tablespace assignments\n"));
+       printf(_("  --role=ROLENAME             do SET ROLE before dump\n"));
        printf(_("  --use-set-session-authorization\n"
                         "                              use SESSION AUTHORIZATION commands instead of\n"
        "                              ALTER OWNER commands to set ownership\n"));
index 81f49dba61c7ad54a502d9abbf09446d5510cf58..51b65cb39e8a33fe33b9188220893d8f2d794ced 100644 (file)
@@ -48,6 +48,7 @@ static void makeAlterConfigCommand(PGconn *conn, const char *arrayitem,
                                           const char *type, const char *name);
 static void dumpDatabases(PGconn *conn);
 static void dumpTimestamp(char *msg);
+static void doShellQuoting(PQExpBuffer buf, const char *str);
 
 static int     runPgDump(const char *dbname);
 static PGconn *connectDatabase(const char *dbname, const char *pghost, const char *pgport,
@@ -77,6 +78,7 @@ main(int argc, char *argv[])
        char       *pgport = NULL;
        char       *pguser = NULL;
        char       *pgdb = NULL;
+       char       *use_role = NULL;
        bool            force_password = false;
        bool            data_only = false;
        bool            globals_only = false;
@@ -118,9 +120,10 @@ main(int argc, char *argv[])
                 */
                {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1},
                {"disable-triggers", no_argument, &disable_triggers, 1},
+               {"lock-wait-timeout", required_argument, NULL, 2},
                {"no-tablespaces", no_argument, &no_tablespaces, 1},
+               {"role", required_argument, NULL, 3},
                {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
-               {"lock-wait-timeout", required_argument, NULL, 2},
 
                {NULL, 0, NULL, 0}
        };
@@ -191,12 +194,8 @@ main(int argc, char *argv[])
 
                        case 'f':
                                filename = optarg;
-#ifndef WIN32
-                               appendPQExpBuffer(pgdumpopts, " -f '%s'", filename);
-#else
-                               appendPQExpBuffer(pgdumpopts, " -f \"%s\"", filename);
-#endif
-
+                               appendPQExpBuffer(pgdumpopts, " -f ");
+                               doShellQuoting(pgdumpopts, filename);
                                break;
 
                        case 'g':
@@ -205,12 +204,8 @@ main(int argc, char *argv[])
 
                        case 'h':
                                pghost = optarg;
-#ifndef WIN32
-                               appendPQExpBuffer(pgdumpopts, " -h '%s'", pghost);
-#else
-                               appendPQExpBuffer(pgdumpopts, " -h \"%s\"", pghost);
-#endif
-
+                               appendPQExpBuffer(pgdumpopts, " -h ");
+                               doShellQuoting(pgdumpopts, pghost);
                                break;
 
                        case 'i':
@@ -231,11 +226,8 @@ main(int argc, char *argv[])
 
                        case 'p':
                                pgport = optarg;
-#ifndef WIN32
-                               appendPQExpBuffer(pgdumpopts, " -p '%s'", pgport);
-#else
-                               appendPQExpBuffer(pgdumpopts, " -p \"%s\"", pgport);
-#endif
+                               appendPQExpBuffer(pgdumpopts, " -p ");
+                               doShellQuoting(pgdumpopts, pgport);
                                break;
 
                        case 'r':
@@ -248,11 +240,8 @@ main(int argc, char *argv[])
                                break;
 
                        case 'S':
-#ifndef WIN32
-                               appendPQExpBuffer(pgdumpopts, " -S '%s'", optarg);
-#else
-                               appendPQExpBuffer(pgdumpopts, " -S \"%s\"", optarg);
-#endif
+                               appendPQExpBuffer(pgdumpopts, " -S ");
+                               doShellQuoting(pgdumpopts, optarg);
                                break;
 
                        case 't':
@@ -261,11 +250,8 @@ main(int argc, char *argv[])
 
                        case 'U':
                                pguser = optarg;
-#ifndef WIN32
-                               appendPQExpBuffer(pgdumpopts, " -U '%s'", pguser);
-#else
-                               appendPQExpBuffer(pgdumpopts, " -U \"%s\"", pguser);
-#endif
+                               appendPQExpBuffer(pgdumpopts, " -U ");
+                               doShellQuoting(pgdumpopts, pguser);
                                break;
 
                        case 'v':
@@ -307,8 +293,14 @@ main(int argc, char *argv[])
                                break;
 
                        case 2:
-                               appendPQExpBuffer(pgdumpopts, " --lock-wait-timeout=");
-                               appendPQExpBuffer(pgdumpopts, "%s", optarg);
+                               appendPQExpBuffer(pgdumpopts, " --lock-wait-timeout ");
+                               doShellQuoting(pgdumpopts, optarg);
+                               break;
+
+                       case 3:
+                               use_role = optarg;
+                               appendPQExpBuffer(pgdumpopts, " --role ");
+                               doShellQuoting(pgdumpopts, use_role);
                                break;
 
                        default:
@@ -426,6 +418,16 @@ main(int argc, char *argv[])
        if (!std_strings)
                std_strings = "off";
 
+       /* Set the role if requested */
+       if (use_role && server_version >= 80100)
+       {
+               PQExpBuffer query = createPQExpBuffer();
+
+               appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
+               executeCommand(conn, query->data);
+               destroyPQExpBuffer(query);
+       }
+
        fprintf(OPF, "--\n-- PostgreSQL database cluster dump\n--\n\n");
        if (verbose)
                dumpTimestamp("Started on");
@@ -513,6 +515,7 @@ help(void)
                         "                           disable dollar quoting, use SQL standard quoting\n"));
        printf(_("  --disable-triggers       disable triggers during data-only restore\n"));
        printf(_("  --no-tablespaces         do not dump tablespace assignments\n"));
+       printf(_("  --role=ROLENAME          do SET ROLE before dump\n"));
        printf(_("  --use-set-session-authorization\n"
                         "                           use SESSION AUTHORIZATION commands instead of\n"
                         "                           OWNER TO commands\n"));
@@ -1271,56 +1274,21 @@ static int
 runPgDump(const char *dbname)
 {
        PQExpBuffer cmd = createPQExpBuffer();
-       const char *p;
        int                     ret;
 
+       appendPQExpBuffer(cmd, SYSTEMQUOTE "\"%s\" %s", pg_dump_bin,
+                                         pgdumpopts->data);
+
        /*
-        * Win32 has to use double-quotes for args, rather than single quotes.
-        * Strangely enough, this is the only place we pass a database name on the
-        * command line, except "postgres" which doesn't need quoting.
-        *
         * If we have a filename, use the undocumented plain-append pg_dump
         * format.
         */
        if (filename)
-       {
-#ifndef WIN32
-               appendPQExpBuffer(cmd, SYSTEMQUOTE"\"%s\" %s -Fa '", pg_dump_bin,
-#else
-               appendPQExpBuffer(cmd, SYSTEMQUOTE"\"%s\" %s -Fa \"", pg_dump_bin,
-#endif
-                                                 pgdumpopts->data);
-       }
+               appendPQExpBuffer(cmd, " -Fa ");
        else
-       {
-#ifndef WIN32
-               appendPQExpBuffer(cmd, SYSTEMQUOTE "\"%s\" %s -Fp '", pg_dump_bin,
-#else
-               appendPQExpBuffer(cmd, SYSTEMQUOTE "\"%s\" %s -Fp \"", pg_dump_bin,
-#endif
-                                                 pgdumpopts->data);
-       }
-
-
-       /* Shell quoting is not quite like SQL quoting, so can't use fmtId */
-       for (p = dbname; *p; p++)
-       {
-#ifndef WIN32
-               if (*p == '\'')
-                       appendPQExpBuffer(cmd, "'\"'\"'");
-#else
-               if (*p == '"')
-                       appendPQExpBuffer(cmd, "\\\"");
-#endif
-               else
-                       appendPQExpBufferChar(cmd, *p);
-       }
+               appendPQExpBuffer(cmd, " -Fp ");
 
-#ifndef WIN32
-       appendPQExpBufferChar(cmd, '\'');
-#else
-       appendPQExpBufferChar(cmd, '"');
-#endif
+       doShellQuoting(cmd, dbname);
 
        appendPQExpBuffer(cmd, "%s", SYSTEMQUOTE);
 
@@ -1338,7 +1306,6 @@ runPgDump(const char *dbname)
 }
 
 
-
 /*
  * Make a database connection with the given parameters.  An
  * interactive password prompt is automatically issued if required.
@@ -1527,3 +1494,38 @@ dumpTimestamp(char *msg)
                                 localtime(&now)) != 0)
                fprintf(OPF, "-- %s %s\n\n", msg, buf);
 }
+
+
+/*
+ * Append the given string to the shell command being built in the buffer,
+ * with suitable shell-style quoting.
+ */
+static void
+doShellQuoting(PQExpBuffer buf, const char *str)
+{
+       const char *p;
+
+#ifndef WIN32
+       appendPQExpBufferChar(buf, '\'');
+       for (p = str; *p; p++)
+       {
+               if (*p == '\'')
+                       appendPQExpBuffer(buf, "'\"'\"'");
+               else
+                       appendPQExpBufferChar(buf, *p);
+       }
+       appendPQExpBufferChar(buf, '\'');
+
+#else /* WIN32 */
+
+       appendPQExpBufferChar(buf, '"');
+       for (p = str; *p; p++)
+       {
+               if (*p == '"')
+                       appendPQExpBuffer(buf, "\\\"");
+               else
+                       appendPQExpBufferChar(buf, *p);
+       }
+       appendPQExpBufferChar(buf, '"');
+#endif /* WIN32 */
+}
index eb16aa9d4400b65424e1398c371fe326465a0ca6..88d64ae9057949d1d5db07519369ca3655238d19 100644 (file)
@@ -51,6 +51,9 @@
 
 #include "getopt_long.h"
 
+extern char *optarg;
+extern int     optind;
+
 #ifndef HAVE_INT_OPTRESET
 int                    optreset;
 #endif
@@ -72,8 +75,6 @@ main(int argc, char **argv)
        int                     exit_code;
        Archive    *AH;
        char       *inputFileSpec;
-       extern int      optind;
-       extern char *optarg;
        static int      disable_triggers = 0;
        static int      no_data_for_failed_tables = 0;
        static int  outputNoTablespaces = 0;
@@ -114,6 +115,7 @@ main(int argc, char **argv)
                {"disable-triggers", no_argument, &disable_triggers, 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},
                {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
 
                {NULL, 0, NULL, 0}
@@ -261,13 +263,17 @@ main(int argc, char **argv)
                                }
                                break;
 
+                       case '1':                       /* Restore data in a single transaction */
+                               opts->single_txn = true;
+                               opts->exit_on_error = true;
+                               break;
+
                        case 0:
                                /* This covers the long options equivalent to -X xxx. */
                                break;
 
-                       case '1':                       /* Restore data in a single transaction */
-                               opts->single_txn = true;
-                               opts->exit_on_error = true;
+                       case 2:                         /* SET ROLE */
+                               opts->use_role = optarg;
                                break;
 
                        default:
@@ -405,6 +411,7 @@ usage(const char *progname)
                         "                           do not restore data of tables that could not be\n"
                         "                           created\n"));
        printf(_("  --no-tablespaces         do not dump tablespace assignments\n"));
+       printf(_("  --role=ROLENAME          do SET ROLE before restore\n"));
        printf(_("  --use-set-session-authorization\n"
                         "                           use SESSION AUTHORIZATION commands instead of\n"
                         "                           OWNER TO commands\n"));