aboutsummaryrefslogtreecommitdiff
path: root/src/bin/pg_dump/pg_dumpall.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/pg_dump/pg_dumpall.c')
-rw-r--r--src/bin/pg_dump/pg_dumpall.c604
1 files changed, 604 insertions, 0 deletions
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
new file mode 100644
index 00000000000..5e1e66404c8
--- /dev/null
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -0,0 +1,604 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_dumpall
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.1 2002/08/27 18:57:26 petere Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <unistd.h>
+#ifdef ENABLE_NLS
+#include <locale.h>
+#endif
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#ifndef HAVE_STRDUP
+#include "strdup.h"
+#endif
+#include <errno.h>
+
+#include "dumputils.h"
+#include "libpq-fe.h"
+#include "pg_backup.h"
+#include "pqexpbuffer.h"
+
+#define _(x) gettext((x))
+
+
+static char *progname;
+
+static void help(void);
+static void dumpUsers(PGconn *conn);
+static void dumpGroups(PGconn *conn);
+static void dumpCreateDB(PGconn *conn);
+static void dumpDatabases(PGconn *conn);
+static int runPgDump(const char *dbname);
+static PGconn *connectDatabase(const char *dbname, const char *pghost, const char *pgport,
+ const char *pguser, bool require_password);
+static PGresult *executeQuery(PGconn *conn, const char *query);
+static char *findPgDump(const char *argv0);
+
+
+char *pgdumploc;
+PQExpBuffer pgdumpopts;
+bool output_clean = false;
+bool verbose = false;
+
+
+
+int
+main(int argc, char *argv[])
+{
+ char *pghost = NULL;
+ char *pgport = NULL;
+ char *pguser = NULL;
+ bool force_password = false;
+ bool globals_only = false;
+ PGconn *conn;
+ int c;
+
+#ifdef HAVE_GETOPT_LONG
+ static struct option long_options[] = {
+ {"clean", no_argument, NULL, 'c'},
+ {"inserts", no_argument, NULL, 'd'},
+ {"attribute-inserts", no_argument, NULL, 'D'},
+ {"column-inserts", no_argument, NULL, 'D'},
+ {"host", required_argument, NULL, 'h'},
+ {"ignore-version", no_argument, NULL, 'i'},
+ {"oids", no_argument, NULL, 'o'},
+ {"port", required_argument, NULL, 'p'},
+ {"password", no_argument, NULL, 'W'},
+ {"username", required_argument, NULL, 'U'},
+ {"verbose", no_argument, NULL, 'v'},
+ {NULL, 0, NULL, 0}
+ };
+
+ int optindex;
+#endif
+
+#ifdef ENABLE_NLS
+ setlocale(LC_ALL, "");
+ bindtextdomain("pg_dump", LOCALEDIR);
+ textdomain("pg_dump");
+#endif
+
+ if (!strrchr(argv[0], '/'))
+ progname = argv[0];
+ else
+ progname = strrchr(argv[0], '/') + 1;
+
+ if (argc > 1)
+ {
+ if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
+ {
+ help();
+ exit(0);
+ }
+ if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
+ {
+ puts("pg_dumpall (PostgreSQL) " PG_VERSION);
+ exit(0);
+ }
+ }
+
+ pgdumploc = findPgDump(argv[0]);
+ pgdumpopts = createPQExpBuffer();
+
+#ifdef HAVE_GETOPT_LONG
+ while ((c = getopt_long(argc, argv, "cdDgh:iop:U:vW", long_options, &optindex)) != -1)
+#else
+ while ((c = getopt(argc, argv, "cdDgh:iop:U:vW")) != -1)
+#endif
+ {
+ switch (c)
+ {
+ case 'c':
+ output_clean = true;
+ appendPQExpBuffer(pgdumpopts, " -c");
+ break;
+
+ case 'd':
+ case 'D':
+ appendPQExpBuffer(pgdumpopts, " -%c", c);
+ break;
+
+ case 'g':
+ globals_only = true;
+ break;
+
+ case 'h':
+ pghost = optarg;
+ appendPQExpBuffer(pgdumpopts, " -h '%s'", pghost);
+ break;
+
+ case 'i':
+ case 'o':
+ appendPQExpBuffer(pgdumpopts, " -%c", c);
+ break;
+
+ case 'p':
+ pgport = optarg;
+ appendPQExpBuffer(pgdumpopts, " -p '%s'", pgport);
+ break;
+
+ case 'U':
+ pguser = optarg;
+ appendPQExpBuffer(pgdumpopts, " -U '%s'", pguser);
+ break;
+
+ case 'v':
+ verbose = true;
+ appendPQExpBuffer(pgdumpopts, " -v");
+ break;
+
+ case 'W':
+ force_password = true;
+ appendPQExpBuffer(pgdumpopts, " -W");
+ break;
+
+ default:
+ fprintf(stderr, _("Try '%s --help' for more information.\n"), progname);
+ exit(1);
+ }
+ }
+
+ if (optind < argc)
+ {
+ fprintf(stderr,
+ _("%s: too many command line options (first is '%s')\n"
+ "Try '%s --help' for more information.\n"),
+ progname, argv[optind], progname);
+ exit(1);
+ }
+
+
+ conn = connectDatabase("template1", pghost, pgport, pguser, force_password);
+
+ printf("--\n");
+ printf("-- PostgreSQL database cluster dump\n");
+ printf("--\n\n");
+ printf("\\connect \"template1\"\n\n");
+
+ dumpUsers(conn);
+ dumpGroups(conn);
+
+ if (globals_only)
+ goto end;
+
+ dumpCreateDB(conn);
+ dumpDatabases(conn);
+
+end:
+ PQfinish(conn);
+ exit(0);
+}
+
+
+
+static void
+help(void)
+{
+ printf(_("%s extracts a PostgreSQL database cluster into an SQL script file.\n\n"), progname);
+ printf(_("Usage:\n"));
+ printf(_(" %s [OPTIONS]\n\n"), progname);
+
+ printf(_("Options:\n"));
+#ifdef HAVE_GETOPT_LONG
+ printf(_(" -c, --clean clean (drop) schema prior to create\n"));
+ printf(_(" -d, --inserts dump data as INSERT, rather than COPY, commands\n"));
+ printf(_(" -D, --column-inserts dump data as INSERT commands with column names\n"));
+ printf(_(" -g, --globals-only only dump global objects, no databases\n"));
+ printf(_(" -h, --host=HOSTNAME database server host name\n"));
+ printf(_(" -i, --ignore-version proceed even when server version mismatches\n"
+ " pg_dumpall version\n"));
+ printf(_(" -o, --oids include OIDs in dump\n"));
+ printf(_(" -p, --port=PORT database server port number\n"));
+ printf(_(" -U, --username=NAME connect as specified database user\n"));
+ printf(_(" -v, --verbose verbose mode\n"));
+ printf(_(" -W, --password force password prompt (should happen automatically)\n"));
+#else /* not HAVE_GETOPT_LONG */
+ printf(_(" -c clean (drop) schema prior to create\n"));
+ printf(_(" -d dump data as INSERT, rather than COPY, commands\n"));
+ printf(_(" -D dump data as INSERT commands with column names\n"));
+ printf(_(" -g only dump global objects, no databases\n"));
+ printf(_(" -h HOSTNAME database server host name\n"));
+ printf(_(" -i proceed even when server version mismatches\n"
+ " pg_dumpall version\n"));
+ printf(_(" -o include oids in dump\n"));
+ printf(_(" -p PORT database server port number\n"));
+ printf(_(" -U NAME connect as specified database user\n"));
+ printf(_(" -v verbose mode\n"));
+ printf(_(" -W force password prompt (should happen automatically)\n"));
+#endif /* not HAVE_GETOPT_LONG */
+
+ printf(_("\nThe SQL script will be written to the standard output.\n\n"));
+ printf(_("Report bugs to <pgsql-bugs@postgresql.org>.\n"));
+}
+
+
+
+/*
+ * Dump users (but not the user created by initdb).
+ */
+static void
+dumpUsers(PGconn *conn)
+{
+ PGresult *res;
+ int i;
+
+ printf("DELETE FROM pg_shadow WHERE usesysid <> (SELECT datdba FROM pg_database WHERE datname = 'template0');\n\n");
+
+ res = executeQuery(conn,
+ "SELECT usename, usesysid, passwd, usecreatedb, usesuper, CAST(valuntil AS timestamp) "
+ "FROM pg_shadow "
+ "WHERE usesysid <> (SELECT datdba FROM pg_database WHERE datname = 'template0');");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ PQExpBuffer buf = createPQExpBuffer();
+
+ appendPQExpBuffer(buf, "CREATE USER %s WITH SYSID %s",
+ fmtId(PQgetvalue(res, i, 0)),
+ PQgetvalue(res, i, 1));
+
+ if (!PQgetisnull(res, i, 2))
+ {
+ appendPQExpBuffer(buf, " PASSWORD ");
+ appendStringLiteral(buf, PQgetvalue(res, i, 2), true);
+ }
+
+ if (strcmp(PQgetvalue(res, i, 3), "t")==0)
+ appendPQExpBuffer(buf, " CREATEDB");
+ else
+ appendPQExpBuffer(buf, " NOCREATEDB");
+
+ if (strcmp(PQgetvalue(res, i, 4), "t")==0)
+ appendPQExpBuffer(buf, " CREATEUSER");
+ else
+ appendPQExpBuffer(buf, " NOCREATEUSER");
+
+ if (!PQgetisnull(res, i, 5))
+ appendPQExpBuffer(buf, " VALID UNTIL '%s'", PQgetvalue(res, i, 5));
+
+ appendPQExpBuffer(buf, ";\n");
+
+ printf("%s", buf->data);
+ destroyPQExpBuffer(buf);
+ }
+
+ PQclear(res);
+ printf("\n\n");
+}
+
+
+
+/*
+ * Dump groups.
+ */
+static void
+dumpGroups(PGconn *conn)
+{
+ PGresult *res;
+ int i;
+
+ printf("DELETE FROM pg_group;\n\n");
+
+ res = executeQuery(conn, "SELECT groname, grosysid, grolist FROM pg_group;");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ PQExpBuffer buf = createPQExpBuffer();
+ char *val;
+ char *tok;
+
+ appendPQExpBuffer(buf, "CREATE GROUP %s WITH SYSID %s;\n",
+ fmtId(PQgetvalue(res, i, 0)),
+ PQgetvalue(res, i, 1));
+
+ val = strdup(PQgetvalue(res, i, 2));
+ tok = strtok(val, ",{}");
+ do
+ {
+ PGresult *res2;
+ PQExpBuffer buf2 = createPQExpBuffer();
+ int j;
+
+ appendPQExpBuffer(buf2, "SELECT usename FROM pg_shadow WHERE usesysid = %s;", tok);
+ res2 = executeQuery(conn, buf2->data);
+ destroyPQExpBuffer(buf2);
+
+ for (j = 0; j < PQntuples(res2); j++)
+ {
+ appendPQExpBuffer(buf, "ALTER GROUP %s ", fmtId(PQgetvalue(res, i, 0)));
+ appendPQExpBuffer(buf, "ADD USER %s;\n", fmtId(PQgetvalue(res2, j, 0)));
+ }
+
+ PQclear(res2);
+
+ tok = strtok(NULL, "{},");
+ }
+ while (tok);
+
+ printf("%s", buf->data);
+ destroyPQExpBuffer(buf);
+ }
+
+ PQclear(res);
+ printf("\n\n");
+}
+
+
+
+/*
+ * Dump commands to create each database.
+ *
+ * To minimize the number of reconnections (and possibly ensuing
+ * password prompts) required by the output script, we emit all CREATE
+ * DATABASE commands during the initial phase of the script, and then
+ * run pg_dump for each database to dump the contents of that
+ * database. We skip databases marked not datallowconn, since we'd be
+ * unable to connect to them anyway (and besides, we don't want to
+ * dump template0).
+ */
+static void
+dumpCreateDB(PGconn *conn)
+{
+ PGresult *res;
+ int i;
+
+ /* Basically this query returns: dbname, dbowner, encoding, istemplate, dbpath */
+ res = executeQuery(conn, "SELECT datname, coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), pg_encoding_to_char(d.encoding), datistemplate, datpath FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) WHERE datallowconn ORDER BY 1;");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ PQExpBuffer buf = createPQExpBuffer();
+ char *dbname = PQgetvalue(res, i, 0);
+ char *dbowner = PQgetvalue(res, i, 1);
+ char *dbencoding = PQgetvalue(res, i, 2);
+ char *dbistemplate = PQgetvalue(res, i, 3);
+ char *dbpath = PQgetvalue(res, i, 4);
+
+ if (strcmp(dbname, "template1")==0)
+ continue;
+
+ if (output_clean)
+ appendPQExpBuffer(buf, "DROP DATABASE %s\n;", fmtId(dbname));
+
+ appendPQExpBuffer(buf, "CREATE DATABASE %s", fmtId(dbname));
+ appendPQExpBuffer(buf, " WITH OWNER = %s TEMPLATE = template0", fmtId(dbowner));
+
+ if (strcmp(dbpath, "")!=0)
+ {
+ appendPQExpBuffer(buf, " LOCATION = ");
+ appendStringLiteral(buf, dbpath, true);
+ }
+
+ appendPQExpBuffer(buf, " ENCODING = ");
+ appendStringLiteral(buf, dbencoding, true);
+
+ appendPQExpBuffer(buf, ";\n");
+
+ if (strcmp(dbistemplate, "t")==0)
+ {
+ appendPQExpBuffer(buf, "UPDATE pg_database SET datistemplate = 't' WHERE datname = ");
+ appendStringLiteral(buf, dbname, true);
+ appendPQExpBuffer(buf, ";\n");
+ }
+ printf("%s", buf->data);
+ destroyPQExpBuffer(buf);
+ }
+
+ PQclear(res);
+ printf("\n\n");
+}
+
+
+
+/*
+ * Dump contents of databases.
+ */
+static void
+dumpDatabases(PGconn *conn)
+{
+ PGresult *res;
+ int i;
+
+ res = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;");
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ int ret;
+
+ char *dbname = PQgetvalue(res, i, 0);
+ if (verbose)
+ fprintf(stderr, _("%s: dumping database \"%s\"...\n"), progname, dbname);
+
+ printf("\\connect %s\n", fmtId(dbname));
+ ret = runPgDump(dbname);
+ if (ret != 0)
+ {
+ fprintf(stderr, _("%s: pg_dump failed on %s, exiting\n"), progname, dbname);
+ exit(1);
+ }
+ }
+
+ PQclear(res);
+}
+
+
+
+/*
+ * Run pg_dump on dbname.
+ */
+static int
+runPgDump(const char *dbname)
+{
+ PQExpBuffer cmd = createPQExpBuffer();
+ int ret;
+
+ appendPQExpBuffer(cmd, "%s %s -X use-set-session-authorization -Fp %s",
+ pgdumploc, pgdumpopts->data, dbname);
+ if (verbose)
+ fprintf(stderr, _("%s: running %s\n"), progname, cmd->data);
+
+ ret = system(cmd->data);
+ destroyPQExpBuffer(cmd);
+
+ return ret;
+}
+
+
+
+/*
+ * Make a database connection with the given parameters. An
+ * interactive password prompt is automatically issued if required.
+ */
+static PGconn *
+connectDatabase(const char *dbname, const char *pghost, const char *pgport,
+ const char *pguser, bool require_password)
+{
+ PGconn *conn;
+ char *password = NULL;
+ bool need_pass = false;
+
+ if (require_password)
+ password = simple_prompt("Password: ", 100, false);
+
+ /*
+ * Start the connection. Loop until we have a password if requested
+ * by backend.
+ */
+ do
+ {
+ need_pass = false;
+ conn = PQsetdbLogin(pghost, pgport, NULL, NULL, dbname, pguser, password);
+
+ if (!conn)
+ {
+ fprintf(stderr, _("%s: could not connection to database %s\n"),
+ progname, dbname);
+ exit(0);
+ }
+
+ if (PQstatus(conn) == CONNECTION_BAD &&
+ strcmp(PQerrorMessage(conn), "fe_sendauth: no password supplied\n") == 0 &&
+ !feof(stdin))
+ {
+ PQfinish(conn);
+ need_pass = true;
+ free(password);
+ password = NULL;
+ password = simple_prompt("Password: ", 100, false);
+ }
+ } while (need_pass);
+
+ if (password)
+ free(password);
+
+ /* check to see that the backend connection was successfully made */
+ if (PQstatus(conn) == CONNECTION_BAD)
+ {
+ fprintf(stderr, _("%s: could not connection to database %s: %s\n"),
+ progname, dbname, PQerrorMessage(conn));
+ exit(0);
+ }
+
+ return conn;
+}
+
+
+
+/*
+ * Run a query, return the results, exit program on failure.
+ */
+static PGresult *
+executeQuery(PGconn *conn, const char *query)
+{
+ PGresult *res;
+
+ res = PQexec(conn, query);
+ if (!res ||
+ PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ fprintf(stderr, _("%s: query failed: %s"), progname, PQerrorMessage(conn));
+ fprintf(stderr, _("%s: query was: %s"), progname, query);
+ PQfinish(conn);
+ exit(1);
+ }
+
+ return res;
+}
+
+
+
+/*
+ * Find location of pg_dump executable.
+ */
+static char *
+findPgDump(const char *argv0)
+{
+ char *last;
+ PQExpBuffer cmd;
+ static char *result = NULL;
+
+ if (result)
+ return result;
+
+ cmd = createPQExpBuffer();
+ last = strrchr(argv0, '/');
+
+ if (!last)
+ appendPQExpBuffer(cmd, "pg_dump");
+ else
+ {
+ char *dir = strdup(argv0);
+ *(dir + (last - argv0)) = '\0';
+ appendPQExpBuffer(cmd, "%s/pg_dump", dir);
+ }
+
+ result = strdup(cmd->data);
+
+ appendPQExpBuffer(cmd, " -V >/dev/null 2>&1");
+ if (system(cmd->data)==0)
+ goto end;
+
+ result = BINDIR "/pg_dump";
+ if (system(BINDIR "/pg_dump -V >/dev/null 2>&1")==0)
+ goto end;
+
+ fprintf(stderr, _("%s: could not find pg_dump\n"
+ "Make sure it is in the path or in the same directory as %s.\n"),
+ progname, progname);
+ exit(1);
+
+end:
+ destroyPQExpBuffer(cmd);
+ return result;
+}