mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-24 18:55:04 +08:00
Fix pg_dumpall so that when --clean is specified, it drops roles and
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.
This commit is contained in:
parent
088ac581d4
commit
e1e17e2a0a
@ -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
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user