From: Tom Lane Date: Mon, 5 Jan 2009 16:54:37 +0000 (+0000) Subject: Add a --role option to pg_dump, pg_dumpall, and pg_restore. This allows X-Git-Tag: recoveryinfrav9~129 X-Git-Url: http://waps.l3s.uni-hannover.de/gitweb/?a=commitdiff_plain;h=e261293ac486513c9f84207e5b7f813b62e918c3;p=users%2Fsimon%2Fpostgres.git Add a --role option to pg_dump, pg_dumpall, and pg_restore. This allows 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 --- diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index 2e30906baf..9c8563018f 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -698,6 +698,23 @@ PostgreSQL documentation + + + + + + Specifies a role name to be used to create the dump. + This option causes pg_dump to issue a + SET ROLE rolename + command after connecting to the database. It is useful when the + authenticated user (specified by + + diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml index ec40890ad3..818c79efb3 100644 --- a/doc/src/sgml/ref/pg_dumpall.sgml +++ b/doc/src/sgml/ref/pg_dumpall.sgml @@ -129,7 +129,7 @@ PostgreSQL documentation - + @@ -183,7 +183,7 @@ PostgreSQL documentation Do not output commands to set ownership of objects to match the original database. By default, pg_dumpall issues - ALTER OWNER or + ALTER OWNER or SET SESSION AUTHORIZATION statements to set ownership of created schema elements. These statements @@ -342,8 +342,8 @@ PostgreSQL documentation - -h host - --host=host + + Specifies the host name of the machine on which the database @@ -354,10 +354,10 @@ PostgreSQL documentation - + - -l dbname - --database=dbname + + Specifies the name of the database to connect to to dump global @@ -369,8 +369,8 @@ PostgreSQL documentation - -p port - --port=port + + Specifies the TCP port or local Unix domain socket file @@ -382,8 +382,8 @@ PostgreSQL documentation - -U username - --username=username + + User name to connect as. @@ -392,12 +392,12 @@ PostgreSQL documentation - -W - --password + + Force pg_dumpall to prompt for a - password before connecting to a database. + password before connecting to a database. @@ -417,6 +417,23 @@ PostgreSQL documentation + + + + + + Specifies a role name to be used to create the dump. + This option causes pg_dumpall to issue a + SET ROLE rolename + command after connecting to the database. It is useful when the + authenticated user (specified by + + @@ -503,6 +520,6 @@ PostgreSQL documentation Check for details on possible error conditions. - + diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml index 8e0f990cce..aec696323e 100644 --- a/doc/src/sgml/ref/pg_restore.sgml +++ b/doc/src/sgml/ref/pg_restore.sgml @@ -135,7 +135,7 @@ 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. @@ -261,7 +261,7 @@ Do not output commands to set ownership of objects to match the original database. By default, pg_restore issues - ALTER OWNER or + ALTER OWNER or SET SESSION AUTHORIZATION statements to set ownership of created schema elements. These statements will fail unless the initial connection to the @@ -429,6 +429,20 @@ + + + + + + Execute the restore as a single transaction (that is, wrap the + emitted commands in BEGIN/COMMIT). This + ensures that either all the commands complete successfully, or no + changes are applied. This option implies + + + + @@ -480,7 +494,7 @@ Force pg_restore to prompt for a - password before connecting to a database. + password before connecting to a database. @@ -496,15 +510,18 @@ - - + - Execute the restore as a single transaction (that is, wrap the - emitted commands in BEGIN/COMMIT). This - ensures that either all the commands complete successfully, or no - changes are applied. This option implies - diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index c57bb22d9a..7117337d40 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -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; diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index 0bbba25d32..114df88180 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -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"); diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 5ab0e4b3a1..07f002a5fb 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -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")); diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 81f49dba61..51b65cb39e 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -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 */ +} diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c index eb16aa9d44..88d64ae905 100644 --- a/src/bin/pg_dump/pg_restore.c +++ b/src/bin/pg_dump/pg_restore.c @@ -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"));