</para>
<para>
- <application>pg_dump</application> only dumps a single database. To backup
- global objects that are common to all databases in a cluster, such as roles
- and tablespaces, use <xref linkend="app-pg-dumpall"/>.
+ <application>pg_dump</application> only dumps a single database.
+ To back up an entire cluster, or to back up global objects that are
+ common to all databases in a cluster (such as roles and tablespaces),
+ use <xref linkend="app-pg-dumpall"/>.
</para>
<para>
switch is therefore only useful to add large objects to dumps
where a specific schema or table has been requested. Note that
blobs are considered data and therefore will be included when
- --data-only is used, but not when --schema-only is.
+ <option>--data-only</option> is used, but not
+ when <option>--schema-only</option> is.
</para>
</listitem>
</varlistentry>
recreates the target database before reconnecting to it.
</para>
+ <para>
+ With <option>--create</option>, the output also includes the
+ database's comment if any, and any configuration variable settings
+ that are specific to this database, that is,
+ any <command>ALTER DATABASE ... SET ...</command>
+ and <command>ALTER ROLE ... IN DATABASE ... SET ...</command>
+ commands that mention this database.
+ Access privileges for the database itself are also dumped,
+ unless <option>--no-acl</option> is specified.
+ </para>
+
<para>
This option is only meaningful for the plain-text format. For
the archive formats, you can specify the option when you
<command>ANALYZE</command> after restoring from a dump file
to ensure optimal performance; see <xref linkend="vacuum-for-statistics"/>
and <xref linkend="autovacuum"/> for more information.
- The dump file also does not
- contain any <command>ALTER DATABASE ... SET</command> commands;
- these settings are dumped by <xref linkend="app-pg-dumpall"/>,
- along with database users and other installation-wide settings.
</para>
<para>
</screen>
</para>
+ <para>
+ To reload an archive file into the same database it was dumped from,
+ discarding the current contents of that database:
+
+<screen>
+<prompt>$</prompt> <userinput>pg_restore -d postgres --clean --create db.dump</userinput>
+</screen>
+ </para>
+
<para>
To dump a single table named <literal>mytab</literal>:
of a cluster into one script file. The script file contains
<acronym>SQL</acronym> commands that can be used as input to <xref
linkend="app-psql"/> to restore the databases. It does this by
- calling <xref linkend="app-pgdump"/> for each database in a cluster.
+ calling <xref linkend="app-pgdump"/> for each database in the cluster.
<application>pg_dumpall</application> also dumps global objects
- that are common to all databases.
+ that are common to all databases, that is, database roles and tablespaces.
(<application>pg_dump</application> does not save these objects.)
- This currently includes information about database users and
- groups, tablespaces, and properties such as access permissions
- that apply to databases as a whole.
</para>
<para>
databases you will most likely have to connect as a database
superuser in order to produce a complete dump. Also you will need
superuser privileges to execute the saved script in order to be
- allowed to add users and groups, and to create databases.
+ allowed to add roles and create databases.
</para>
<para>
<listitem>
<para>
Use conditional commands (i.e. add an <literal>IF EXISTS</literal>
- clause) to clean databases and other objects. This option is not valid
+ clause) to drop databases and other objects. This option is not valid
unless <option>--clean</option> is also specified.
</para>
</listitem>
<para>
The option is called <literal>--dbname</literal> for consistency with other
client applications, but because <application>pg_dumpall</application>
- needs to connect to many databases, database name in the connection
- string will be ignored. Use <literal>-l</literal> option to specify
- the name of the database used to dump global objects and to discover
- what other databases should be dumped.
+ needs to connect to many databases, the database name in the
+ connection string will be ignored. Use the <literal>-l</literal>
+ option to specify the name of the database used for the initial
+ connection, which will dump global objects and discover what other
+ databases should be dumped.
</para>
</listitem>
</varlistentry>
messages will refer to <application>pg_dump</application>.
</para>
+ <para>
+ The <option>--clean</option> option can be useful even when your
+ intention is to restore the dump script into a fresh cluster. Use of
+ <option>--clean</option> authorizes the script to drop and re-create the
+ built-in <literal>postgres</literal> and <literal>template1</literal>
+ databases, ensuring that those databases will retain the same properties
+ (for instance, locale and encoding) that they had in the source cluster.
+ Without the option, those databases will retain their existing
+ database-level properties, as well as any pre-existing contents.
+ </para>
+
<para>
Once restored, it is wise to run <command>ANALYZE</command> on each
database so the optimizer has useful statistics. You
databases.
</para>
+ <para>
+ The dump script should not be expected to run completely without errors.
+ In particular, because the script will issue <command>CREATE ROLE</command>
+ for every role existing in the source cluster, it is certain to get a
+ <quote>role already exists</quote> error for the bootstrap superuser,
+ unless the destination cluster was initialized with a different bootstrap
+ superuser name. This error is harmless and should be ignored. Use of
+ the <option>--clean</option> option is likely to produce additional
+ harmless error messages about non-existent objects, although you can
+ minimize those by adding <option>--if-exists</option>.
+ </para>
+
<para>
<application>pg_dumpall</application> requires all needed
tablespace directories to exist before the restore; otherwise,
<screen>
<prompt>$</prompt> <userinput>psql -f db.out postgres</userinput>
</screen>
- (It is not important to which database you connect here since the
+ It is not important to which database you connect here since the
script file created by <application>pg_dumpall</application> will
contain the appropriate commands to create and connect to the saved
- databases.)
+ databases. An exception is that if you specified <option>--clean</option>,
+ you must connect to the <literal>postgres</literal> database initially;
+ the script will attempt to drop other databases immediately, and that
+ will fail for the database you are connected to.
</para>
</refsect1>
recreate the target database before connecting to it.
</para>
+ <para>
+ With <option>--create</option>, <application>pg_restore</application>
+ also restores the database's comment if any, and any configuration
+ variable settings that are specific to this database, that is,
+ any <command>ALTER DATABASE ... SET ...</command>
+ and <command>ALTER ROLE ... IN DATABASE ... SET ...</command>
+ commands that mention this database.
+ Access privileges for the database itself are also restored,
+ unless <option>--no-acl</option> is specified.
+ </para>
+
<para>
When this option is used, the database named with <option>-d</option>
is used only to issue the initial <command>DROP DATABASE</command> and
printfPQExpBuffer(init_racl_subquery, "NULL");
}
}
+
+/*
+ * Helper function for dumping "ALTER DATABASE/ROLE SET ..." commands.
+ *
+ * Parse the contents of configitem (a "name=value" string), wrap it in
+ * a complete ALTER command, and append it to buf.
+ *
+ * type is DATABASE or ROLE, and name is the name of the database or role.
+ * If we need an "IN" clause, type2 and name2 similarly define what to put
+ * there; otherwise they should be NULL.
+ * conn is used only to determine string-literal quoting conventions.
+ */
+void
+makeAlterConfigCommand(PGconn *conn, const char *configitem,
+ const char *type, const char *name,
+ const char *type2, const char *name2,
+ PQExpBuffer buf)
+{
+ char *mine;
+ char *pos;
+
+ /* Parse the configitem. If we can't find an "=", silently do nothing. */
+ mine = pg_strdup(configitem);
+ pos = strchr(mine, '=');
+ if (pos == NULL)
+ {
+ pg_free(mine);
+ return;
+ }
+ *pos++ = '\0';
+
+ /* Build the command, with suitable quoting for everything. */
+ appendPQExpBuffer(buf, "ALTER %s %s ", type, fmtId(name));
+ if (type2 != NULL && name2 != NULL)
+ appendPQExpBuffer(buf, "IN %s %s ", type2, fmtId(name2));
+ appendPQExpBuffer(buf, "SET %s TO ", fmtId(mine));
+
+ /*
+ * Some GUC variable names are 'LIST' type and hence must not be quoted.
+ * XXX this list is incomplete ...
+ */
+ if (pg_strcasecmp(mine, "DateStyle") == 0
+ || pg_strcasecmp(mine, "search_path") == 0)
+ appendPQExpBufferStr(buf, pos);
+ else
+ appendStringLiteralConn(buf, pos, conn);
+
+ appendPQExpBufferStr(buf, ";\n");
+
+ pg_free(mine);
+}
const char *acl_column, const char *acl_owner,
const char *obj_kind, bool binary_upgrade);
+extern void makeAlterConfigCommand(PGconn *conn, const char *configitem,
+ const char *type, const char *name,
+ const char *type2, const char *name2,
+ PQExpBuffer buf);
+
#endif /* DUMPUTILS_H */
* whole. Issuing drops against anything else would be wrong,
* because at this point we're connected to the wrong database.
* Conversely, if we're not in createDB mode, we'd better not
- * issue a DROP against the database at all.
+ * issue a DROP against the database at all. (The DATABASE
+ * PROPERTIES entry, if any, works like the DATABASE entry.)
*/
if (ropt->createDB)
{
- if (strcmp(te->desc, "DATABASE") != 0)
+ if (strcmp(te->desc, "DATABASE") != 0 &&
+ strcmp(te->desc, "DATABASE PROPERTIES") != 0)
continue;
}
else
{
- if (strcmp(te->desc, "DATABASE") == 0)
+ if (strcmp(te->desc, "DATABASE") == 0 ||
+ strcmp(te->desc, "DATABASE PROPERTIES") == 0)
continue;
}
* we simply emit the original command for DEFAULT
* objects (modulo the adjustment made above).
*
+ * Likewise, don't mess with DATABASE PROPERTIES.
+ *
* If we used CREATE OR REPLACE VIEW as a means of
* quasi-dropping an ON SELECT rule, that should
* be emitted unchanged as well.
* search for hardcoded "DROP CONSTRAINT" instead.
*/
if (strcmp(te->desc, "DEFAULT") == 0 ||
+ strcmp(te->desc, "DATABASE PROPERTIES") == 0 ||
strncmp(dropStmt, "CREATE OR REPLACE VIEW", 22) == 0)
appendPQExpBufferStr(ftStmt, dropStmt);
else
reqs = te->reqs;
/*
- * Ignore DATABASE entry unless we should create it. We must check this
- * here, not in _tocEntryRequired, because the createDB option should not
- * affect emitting a DATABASE entry to an archive file.
+ * Ignore DATABASE and related entries unless createDB is specified. We
+ * must check this here, not in _tocEntryRequired, because !createDB
+ * should not prevent emitting these entries to an archive file.
*/
- if (!ropt->createDB && strcmp(te->desc, "DATABASE") == 0)
+ if (!ropt->createDB &&
+ (strcmp(te->desc, "DATABASE") == 0 ||
+ strcmp(te->desc, "DATABASE PROPERTIES") == 0 ||
+ (strcmp(te->desc, "ACL") == 0 &&
+ strncmp(te->tag, "DATABASE ", 9) == 0) ||
+ (strcmp(te->desc, "COMMENT") == 0 &&
+ strncmp(te->tag, "DATABASE ", 9) == 0) ||
+ (strcmp(te->desc, "SECURITY LABEL") == 0 &&
+ strncmp(te->tag, "DATABASE ", 9) == 0)))
reqs = 0;
/* Dump any relevant dump warnings to stderr */
* Special Case: If 'SEQUENCE SET' or anything to do with BLOBs, then
* it is considered a data entry. We don't need to check for the
* BLOBS entry or old-style BLOB COMMENTS, because they will have
- * hadDumper = true ... but we do need to check new-style BLOB
- * comments.
+ * hadDumper = true ... but we do need to check new-style BLOB ACLs,
+ * comments, etc.
*/
if (strcmp(te->desc, "SEQUENCE SET") == 0 ||
strcmp(te->desc, "BLOB") == 0 ||
else if (strcmp(te->desc, "CAST") == 0 ||
strcmp(te->desc, "CHECK CONSTRAINT") == 0 ||
strcmp(te->desc, "CONSTRAINT") == 0 ||
+ strcmp(te->desc, "DATABASE PROPERTIES") == 0 ||
strcmp(te->desc, "DEFAULT") == 0 ||
strcmp(te->desc, "FK CONSTRAINT") == 0 ||
strcmp(te->desc, "INDEX") == 0 ||
static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo);
static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo);
static void dumpDatabase(Archive *AH);
+static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
+ const char *dbname, Oid dboid);
static void dumpEncoding(Archive *AH);
static void dumpStdStrings(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
dumpEncoding(fout);
dumpStdStrings(fout);
- /* The database item is always next, unless we don't want it at all */
+ /* The database items are always next, unless we don't want them at all */
if (dopt.include_everything && !dopt.dataOnly)
dumpDatabase(fout);
i_ctype,
i_frozenxid,
i_minmxid,
+ i_datacl,
+ i_rdatacl,
+ i_datistemplate,
+ i_datconnlimit,
i_tablespace;
CatalogId dbCatId;
DumpId dbDumpId;
*encoding,
*collate,
*ctype,
+ *datacl,
+ *rdatacl,
+ *datistemplate,
+ *datconnlimit,
*tablespace;
uint32 frozenxid,
minmxid;
+ char *qdatname;
datname = PQdb(conn);
+ qdatname = pg_strdup(fmtId(datname));
if (g_verbose)
write_msg(NULL, "saving database definition\n");
/* Make sure we are in proper schema */
selectSourceSchema(fout, "pg_catalog");
- /* Get the database owner and parameters from pg_database */
- if (fout->remoteVersion >= 90300)
+ /* Fetch the database-level properties for this database */
+ if (fout->remoteVersion >= 90600)
{
appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
"(%s datdba) AS dba, "
"pg_encoding_to_char(encoding) AS encoding, "
"datcollate, datctype, datfrozenxid, datminmxid, "
+ "(SELECT array_agg(acl ORDER BY acl::text COLLATE \"C\") FROM ( "
+ " SELECT unnest(coalesce(datacl,acldefault('d',datdba))) AS acl "
+ " EXCEPT SELECT unnest(acldefault('d',datdba))) as datacls)"
+ " AS datacl, "
+ "(SELECT array_agg(acl ORDER BY acl::text COLLATE \"C\") FROM ( "
+ " SELECT unnest(acldefault('d',datdba)) AS acl "
+ " EXCEPT SELECT unnest(coalesce(datacl,acldefault('d',datdba)))) as rdatacls)"
+ " AS rdatacl, "
+ "datistemplate, datconnlimit, "
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
+ "shobj_description(oid, 'pg_database') AS description "
+
+ "FROM pg_database "
+ "WHERE datname = ",
+ username_subquery);
+ appendStringLiteralAH(dbQry, datname, fout);
+ }
+ else if (fout->remoteVersion >= 90300)
+ {
+ appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
+ "(%s datdba) AS dba, "
+ "pg_encoding_to_char(encoding) AS encoding, "
+ "datcollate, datctype, datfrozenxid, datminmxid, "
+ "datacl, '' as rdatacl, datistemplate, datconnlimit, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
"shobj_description(oid, 'pg_database') AS description "
"(%s datdba) AS dba, "
"pg_encoding_to_char(encoding) AS encoding, "
"datcollate, datctype, datfrozenxid, 0 AS datminmxid, "
+ "datacl, '' as rdatacl, datistemplate, datconnlimit, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
"shobj_description(oid, 'pg_database') AS description "
"(%s datdba) AS dba, "
"pg_encoding_to_char(encoding) AS encoding, "
"NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, "
+ "datacl, '' as rdatacl, datistemplate, datconnlimit, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
"shobj_description(oid, 'pg_database') AS description "
"(%s datdba) AS dba, "
"pg_encoding_to_char(encoding) AS encoding, "
"NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, "
+ "datacl, '' as rdatacl, datistemplate, "
+ "-1 as datconnlimit, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace "
"FROM pg_database "
"WHERE datname = ",
i_ctype = PQfnumber(res, "datctype");
i_frozenxid = PQfnumber(res, "datfrozenxid");
i_minmxid = PQfnumber(res, "datminmxid");
+ i_datacl = PQfnumber(res, "datacl");
+ i_rdatacl = PQfnumber(res, "rdatacl");
+ i_datistemplate = PQfnumber(res, "datistemplate");
+ i_datconnlimit = PQfnumber(res, "datconnlimit");
i_tablespace = PQfnumber(res, "tablespace");
dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
ctype = PQgetvalue(res, 0, i_ctype);
frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
+ datacl = PQgetvalue(res, 0, i_datacl);
+ rdatacl = PQgetvalue(res, 0, i_rdatacl);
+ datistemplate = PQgetvalue(res, 0, i_datistemplate);
+ datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
tablespace = PQgetvalue(res, 0, i_tablespace);
+ /*
+ * Prepare the CREATE DATABASE command. We must specify encoding, locale,
+ * and tablespace since those can't be altered later. Other DB properties
+ * are left to the DATABASE PROPERTIES entry, so that they can be applied
+ * after reconnecting to the target DB.
+ */
appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
- fmtId(datname));
+ qdatname);
if (strlen(encoding) > 0)
{
appendPQExpBufferStr(creaQry, " ENCODING = ");
appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
appendStringLiteralAH(creaQry, ctype, fout);
}
+
+ /*
+ * Note: looking at dopt->outputNoTablespaces here is completely the wrong
+ * thing; the decision whether to specify a tablespace should be left till
+ * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
+ * label the DATABASE entry with the tablespace and let the normal
+ * tablespace selection logic work ... but CREATE DATABASE doesn't pay
+ * attention to default_tablespace, so that won't work.
+ */
if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
!dopt->outputNoTablespaces)
appendPQExpBuffer(creaQry, " TABLESPACE = %s",
fmtId(tablespace));
appendPQExpBufferStr(creaQry, ";\n");
- if (dopt->binary_upgrade)
- {
- appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
- appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
- "SET datfrozenxid = '%u', datminmxid = '%u'\n"
- "WHERE datname = ",
- frozenxid, minmxid);
- appendStringLiteralAH(creaQry, datname, fout);
- appendPQExpBufferStr(creaQry, ";\n");
-
- }
-
appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
- fmtId(datname));
+ qdatname);
dbDumpId = createDumpId();
NULL); /* Dumper Arg */
/* Compute correct tag for comments etc */
- appendPQExpBuffer(labelq, "DATABASE %s", fmtId(datname));
+ appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
/* Dump DB comment if any */
if (fout->remoteVersion >= 80200)
* Generates warning when loaded into a differently-named
* database.
*/
- appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", fmtId(datname));
+ appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
appendStringLiteralAH(dbQry, comment, fout);
appendPQExpBufferStr(dbQry, ";\n");
PQclear(shres);
}
+ /*
+ * Dump ACL if any. Note that we do not support initial privileges
+ * (pg_init_privs) on databases.
+ */
+ dumpACL(fout, dbCatId, dbDumpId, "DATABASE",
+ qdatname, NULL, labelq->data, NULL,
+ dba, datacl, rdatacl, "", "");
+
+ /*
+ * Now construct a DATABASE PROPERTIES archive entry to restore any
+ * non-default database-level properties. We want to do this after
+ * reconnecting so that these properties won't apply during the restore
+ * session. In this way, restoring works even if there is, say, an ALTER
+ * DATABASE SET that turns on default_transaction_read_only.
+ */
+ resetPQExpBuffer(creaQry);
+ resetPQExpBuffer(delQry);
+
+ if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
+ appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
+ qdatname, datconnlimit);
+
+ if (strcmp(datistemplate, "t") == 0)
+ {
+ appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
+ qdatname);
+
+ /*
+ * The backend won't accept DROP DATABASE on a template database. We
+ * can deal with that by removing the template marking before the DROP
+ * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
+ * since no such command is currently supported, fake it with a direct
+ * UPDATE on pg_database.
+ */
+ appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
+ "SET datistemplate = false WHERE datname = ");
+ appendStringLiteralAH(delQry, datname, fout);
+ appendPQExpBufferStr(delQry, ";\n");
+ }
+
+ /* Add database-specific SET options */
+ dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
+
+ /*
+ * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
+ * entry, too. It can't go into the DATABASE entry because that would
+ * result in an implicit transaction block around the CREATE DATABASE.
+ */
+ if (dopt->binary_upgrade)
+ {
+ appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
+ appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
+ "SET datfrozenxid = '%u', datminmxid = '%u'\n"
+ "WHERE datname = ",
+ frozenxid, minmxid);
+ appendStringLiteralAH(creaQry, datname, fout);
+ appendPQExpBufferStr(creaQry, ";\n");
+ }
+
+ if (creaQry->len > 0)
+ ArchiveEntry(fout, nilCatalogId, createDumpId(),
+ datname, NULL, NULL, dba,
+ false, "DATABASE PROPERTIES", SECTION_PRE_DATA,
+ creaQry->data, delQry->data, NULL,
+ &(dbDumpId), 1,
+ NULL, NULL);
+
/*
* pg_largeobject and pg_largeobject_metadata come from the old system
* intact, so set their relfrozenxids and relminmxids.
appendPQExpBuffer(loOutQry, "UPDATE pg_catalog.pg_class\n"
"SET relfrozenxid = '%u', relminmxid = '%u'\n"
"WHERE oid = %u;\n",
- atoi(PQgetvalue(lo_res, 0, i_relfrozenxid)),
- atoi(PQgetvalue(lo_res, 0, i_relminmxid)),
+ atooid(PQgetvalue(lo_res, 0, i_relfrozenxid)),
+ atooid(PQgetvalue(lo_res, 0, i_relminmxid)),
LargeObjectRelationId);
ArchiveEntry(fout, nilCatalogId, createDumpId(),
"pg_largeobject", NULL, NULL, "",
appendPQExpBuffer(loOutQry, "UPDATE pg_catalog.pg_class\n"
"SET relfrozenxid = '%u', relminmxid = '%u'\n"
"WHERE oid = %u;\n",
- atoi(PQgetvalue(lo_res, 0, i_relfrozenxid)),
- atoi(PQgetvalue(lo_res, 0, i_relminmxid)),
+ atooid(PQgetvalue(lo_res, 0, i_relfrozenxid)),
+ atooid(PQgetvalue(lo_res, 0, i_relminmxid)),
LargeObjectMetadataRelationId);
ArchiveEntry(fout, nilCatalogId, createDumpId(),
"pg_largeobject_metadata", NULL, NULL, "",
PQclear(res);
+ free(qdatname);
destroyPQExpBuffer(dbQry);
destroyPQExpBuffer(delQry);
destroyPQExpBuffer(creaQry);
destroyPQExpBuffer(labelq);
}
+/*
+ * Collect any database-specific or role-and-database-specific SET options
+ * for this database, and append them to outbuf.
+ */
+static void
+dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
+ const char *dbname, Oid dboid)
+{
+ PGconn *conn = GetConnection(AH);
+ PQExpBuffer buf = createPQExpBuffer();
+ PGresult *res;
+ int count = 1;
+
+ /*
+ * First collect database-specific options. Pre-8.4 server versions lack
+ * unnest(), so we do this the hard way by querying once per subscript.
+ */
+ for (;;)
+ {
+ if (AH->remoteVersion >= 90000)
+ printfPQExpBuffer(buf, "SELECT setconfig[%d] FROM pg_db_role_setting "
+ "WHERE setrole = 0 AND setdatabase = '%u'::oid",
+ count, dboid);
+ else
+ printfPQExpBuffer(buf, "SELECT datconfig[%d] FROM pg_database WHERE oid = '%u'::oid", count, dboid);
+
+ res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
+
+ if (PQntuples(res) == 1 &&
+ !PQgetisnull(res, 0, 0))
+ {
+ makeAlterConfigCommand(conn, PQgetvalue(res, 0, 0),
+ "DATABASE", dbname, NULL, NULL,
+ outbuf);
+ PQclear(res);
+ count++;
+ }
+ else
+ {
+ PQclear(res);
+ break;
+ }
+ }
+
+ /* Now look for role-and-database-specific options */
+ if (AH->remoteVersion >= 90000)
+ {
+ /* Here we can assume we have unnest() */
+ printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
+ "FROM pg_db_role_setting s, pg_roles r "
+ "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
+ dboid);
+
+ res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
+
+ if (PQntuples(res) > 0)
+ {
+ int i;
+
+ for (i = 0; i < PQntuples(res); i++)
+ makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
+ "ROLE", PQgetvalue(res, i, 0),
+ "DATABASE", dbname,
+ outbuf);
+ }
+
+ PQclear(res);
+ }
+
+ destroyPQExpBuffer(buf);
+}
+
/*
* dumpEncoding: put the correct encoding into the archive
*/
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);
-static void dumpDbRoleConfig(PGconn *conn);
-static void makeAlterConfigCommand(PGconn *conn, const char *arrayitem,
- const char *type, const char *name, const char *type2,
- const char *name2);
static void dumpDatabases(PGconn *conn);
static void dumpTimestamp(const char *msg);
-
-static int runPgDump(const char *dbname);
+static int runPgDump(const char *dbname, const char *create_opts);
static void buildShSecLabels(PGconn *conn, const char *catalog_name,
uint32 objectId, PQExpBuffer buffer,
const char *target, const char *objname);
static const char *progname;
static PQExpBuffer pgdumpopts;
static char *connstr = "";
+static bool output_clean = false;
static bool skip_acls = false;
static bool verbose = false;
static bool dosync = true;
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;
PGconn *conn;
/* Dump tablespaces */
if (!roles_only && !no_tablespaces)
dumpTablespaces(conn);
-
- /* Dump CREATE DATABASE commands */
- if (binary_upgrade || (!globals_only && !roles_only && !tablespaces_only))
- dumpCreateDB(conn);
-
- /* Dump role/database settings */
- if (!tablespaces_only && !roles_only)
- {
- if (server_version >= 90000)
- dumpDbRoleConfig(conn);
- }
}
if (!globals_only && !roles_only && !tablespaces_only)
/*
* 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;
+ /*
+ * Skip databases marked not datallowconn, since we'd be unable to connect
+ * to them anyway. This must agree with dumpDatabases().
+ */
res = executeQuery(conn,
"SELECT datname "
"FROM pg_database d "
- "WHERE datallowconn ORDER BY 1");
+ "WHERE datallowconn "
+ "ORDER BY datname");
if (PQntuples(res) > 0)
- fprintf(OPF, "--\n-- Drop databases\n--\n\n");
+ fprintf(OPF, "--\n-- Drop databases (except postgres and template1)\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!
+ * Skip "postgres" and "template1"; dumpDatabases() will deal with
+ * them specially. Also, be sure to skip "template0", even if for
+ * some reason it's not marked !datallowconn.
*/
if (strcmp(dbname, "template1") != 0 &&
+ strcmp(dbname, "template0") != 0 &&
strcmp(dbname, "postgres") != 0)
{
fprintf(OPF, "DROP DATABASE %s%s;\n",
fprintf(OPF, "\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)
-{
- PQExpBuffer buf = createPQExpBuffer();
- char *default_encoding = NULL;
- char *default_collate = NULL;
- char *default_ctype = NULL;
- PGresult *res;
- int i;
-
- fprintf(OPF, "--\n-- Database creation\n--\n\n");
-
- /*
- * First, get the installation's default encoding and locale information.
- * We will dump encoding and locale specifications in the CREATE DATABASE
- * commands for just those databases with values different from defaults.
- *
- * We consider template0's encoding and locale to define the installation
- * default. Pre-8.4 installations do not have per-database locale
- * settings; for them, every database must necessarily be using the
- * installation default, so there's no need to do anything.
- */
- if (server_version >= 80400)
- res = executeQuery(conn,
- "SELECT pg_encoding_to_char(encoding), "
- "datcollate, datctype "
- "FROM pg_database "
- "WHERE datname = 'template0'");
- else
- res = executeQuery(conn,
- "SELECT pg_encoding_to_char(encoding), "
- "null::text AS datcollate, null::text AS datctype "
- "FROM pg_database "
- "WHERE datname = 'template0'");
-
- /* If for some reason the template DB isn't there, treat as unknown */
- if (PQntuples(res) > 0)
- {
- if (!PQgetisnull(res, 0, 0))
- default_encoding = pg_strdup(PQgetvalue(res, 0, 0));
- if (!PQgetisnull(res, 0, 1))
- default_collate = pg_strdup(PQgetvalue(res, 0, 1));
- if (!PQgetisnull(res, 0, 2))
- default_ctype = pg_strdup(PQgetvalue(res, 0, 2));
- }
-
- PQclear(res);
-
-
- /*
- * Now collect all the information about databases to dump.
- *
- * For the database ACLs, as of 9.6, we extract both the positive (as
- * datacl) and negative (as rdatacl) ACLs, relative to the default ACL for
- * databases, which are then passed to buildACLCommands() below.
- *
- * See buildACLQueries() and buildACLCommands().
- *
- * Note that we do not support initial privileges (pg_init_privs) on
- * databases.
- */
- if (server_version >= 90600)
- printfPQExpBuffer(buf,
- "SELECT datname, "
- "coalesce(rolname, (select rolname from %s where oid=(select datdba from pg_database where datname='template0'))), "
- "pg_encoding_to_char(d.encoding), "
- "datcollate, datctype, datfrozenxid, datminmxid, "
- "datistemplate, "
- "(SELECT pg_catalog.array_agg(acl ORDER BY acl::text COLLATE \"C\") FROM ( "
- " SELECT pg_catalog.unnest(coalesce(datacl,pg_catalog.acldefault('d',datdba))) AS acl "
- " EXCEPT SELECT pg_catalog.unnest(pg_catalog.acldefault('d',datdba))) as datacls)"
- "AS datacl, "
- "(SELECT pg_catalog.array_agg(acl ORDER BY acl::text COLLATE \"C\") FROM ( "
- " SELECT pg_catalog.unnest(pg_catalog.acldefault('d',datdba)) AS acl "
- " EXCEPT SELECT pg_catalog.unnest(coalesce(datacl,pg_catalog.acldefault('d',datdba)))) as rdatacls)"
- "AS rdatacl, "
- "datconnlimit, "
- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace "
- "FROM pg_database d LEFT JOIN %s u ON (datdba = u.oid) "
- "WHERE datallowconn ORDER BY 1", role_catalog, role_catalog);
- else if (server_version >= 90300)
- printfPQExpBuffer(buf,
- "SELECT datname, "
- "coalesce(rolname, (select rolname from %s where oid=(select datdba from pg_database where datname='template0'))), "
- "pg_encoding_to_char(d.encoding), "
- "datcollate, datctype, datfrozenxid, datminmxid, "
- "datistemplate, datacl, '' as rdatacl, "
- "datconnlimit, "
- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace "
- "FROM pg_database d LEFT JOIN %s u ON (datdba = u.oid) "
- "WHERE datallowconn ORDER BY 1", role_catalog, role_catalog);
- else if (server_version >= 80400)
- printfPQExpBuffer(buf,
- "SELECT datname, "
- "coalesce(rolname, (select rolname from %s where oid=(select datdba from pg_database where datname='template0'))), "
- "pg_encoding_to_char(d.encoding), "
- "datcollate, datctype, datfrozenxid, 0 AS datminmxid, "
- "datistemplate, datacl, '' as rdatacl, "
- "datconnlimit, "
- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace "
- "FROM pg_database d LEFT JOIN %s u ON (datdba = u.oid) "
- "WHERE datallowconn ORDER BY 1", role_catalog, role_catalog);
- else if (server_version >= 80100)
- printfPQExpBuffer(buf,
- "SELECT datname, "
- "coalesce(rolname, (select rolname from %s where oid=(select datdba from pg_database where datname='template0'))), "
- "pg_encoding_to_char(d.encoding), "
- "null::text AS datcollate, null::text AS datctype, datfrozenxid, 0 AS datminmxid, "
- "datistemplate, datacl, '' as rdatacl, "
- "datconnlimit, "
- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace "
- "FROM pg_database d LEFT JOIN %s u ON (datdba = u.oid) "
- "WHERE datallowconn ORDER BY 1", role_catalog, role_catalog);
- else
- printfPQExpBuffer(buf,
- "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), "
- "null::text AS datcollate, null::text AS datctype, datfrozenxid, 0 AS datminmxid, "
- "datistemplate, datacl, '' as rdatacl, "
- "-1 as datconnlimit, "
- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace "
- "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) "
- "WHERE datallowconn ORDER BY 1");
-
- res = executeQuery(conn, buf->data);
-
- for (i = 0; i < PQntuples(res); i++)
- {
- char *dbname = PQgetvalue(res, i, 0);
- char *dbowner = PQgetvalue(res, i, 1);
- char *dbencoding = PQgetvalue(res, i, 2);
- char *dbcollate = PQgetvalue(res, i, 3);
- char *dbctype = PQgetvalue(res, i, 4);
- uint32 dbfrozenxid = atooid(PQgetvalue(res, i, 5));
- uint32 dbminmxid = atooid(PQgetvalue(res, i, 6));
- char *dbistemplate = PQgetvalue(res, i, 7);
- char *dbacl = PQgetvalue(res, i, 8);
- char *rdbacl = PQgetvalue(res, i, 9);
- char *dbconnlimit = PQgetvalue(res, i, 10);
- char *dbtablespace = PQgetvalue(res, i, 11);
- char *fdbname;
-
- fdbname = pg_strdup(fmtId(dbname));
-
- resetPQExpBuffer(buf);
-
- /*
- * Skip the CREATE DATABASE commands for "template1" and "postgres",
- * since they are presumably already there in the destination cluster.
- * We do want to emit their ACLs and config options if any, however.
- */
- if (strcmp(dbname, "template1") != 0 &&
- strcmp(dbname, "postgres") != 0)
- {
- appendPQExpBuffer(buf, "CREATE DATABASE %s", fdbname);
-
- appendPQExpBufferStr(buf, " WITH TEMPLATE = template0");
-
- if (strlen(dbowner) != 0)
- appendPQExpBuffer(buf, " OWNER = %s", fmtId(dbowner));
-
- if (default_encoding && strcmp(dbencoding, default_encoding) != 0)
- {
- appendPQExpBufferStr(buf, " ENCODING = ");
- appendStringLiteralConn(buf, dbencoding, conn);
- }
-
- if (default_collate && strcmp(dbcollate, default_collate) != 0)
- {
- appendPQExpBufferStr(buf, " LC_COLLATE = ");
- appendStringLiteralConn(buf, dbcollate, conn);
- }
-
- if (default_ctype && strcmp(dbctype, default_ctype) != 0)
- {
- appendPQExpBufferStr(buf, " LC_CTYPE = ");
- appendStringLiteralConn(buf, dbctype, conn);
- }
-
- /*
- * Output tablespace if it isn't the default. For default, it
- * uses the default from the template database. If tablespace is
- * specified and tablespace creation failed earlier, (e.g. no such
- * directory), the database creation will fail too. One solution
- * would be to use 'SET default_tablespace' like we do in pg_dump
- * for setting non-default database locations.
- */
- if (strcmp(dbtablespace, "pg_default") != 0 && !no_tablespaces)
- appendPQExpBuffer(buf, " TABLESPACE = %s",
- fmtId(dbtablespace));
-
- if (strcmp(dbistemplate, "t") == 0)
- appendPQExpBuffer(buf, " IS_TEMPLATE = true");
-
- if (strcmp(dbconnlimit, "-1") != 0)
- appendPQExpBuffer(buf, " CONNECTION LIMIT = %s",
- dbconnlimit);
-
- appendPQExpBufferStr(buf, ";\n");
- }
- else if (strcmp(dbtablespace, "pg_default") != 0 && !no_tablespaces)
- {
- /*
- * Cannot change tablespace of the database we're connected to, so
- * to move "postgres" to another tablespace, we connect to
- * "template1", and vice versa.
- */
- if (strcmp(dbname, "postgres") == 0)
- appendPQExpBuffer(buf, "\\connect template1\n");
- else
- appendPQExpBuffer(buf, "\\connect postgres\n");
-
- appendPQExpBuffer(buf, "ALTER DATABASE %s SET TABLESPACE %s;\n",
- fdbname, fmtId(dbtablespace));
-
- /* connect to original database */
- appendPsqlMetaConnect(buf, dbname);
- }
-
- if (binary_upgrade)
- {
- appendPQExpBufferStr(buf, "-- For binary upgrade, set datfrozenxid and datminmxid.\n");
- appendPQExpBuffer(buf, "UPDATE pg_catalog.pg_database "
- "SET datfrozenxid = '%u', datminmxid = '%u' "
- "WHERE datname = ",
- dbfrozenxid, dbminmxid);
- appendStringLiteralConn(buf, dbname, conn);
- appendPQExpBufferStr(buf, ";\n");
- }
-
- if (!skip_acls &&
- !buildACLCommands(fdbname, NULL, "DATABASE",
- dbacl, rdbacl, dbowner,
- "", server_version, buf))
- {
- fprintf(stderr, _("%s: could not parse ACL list (%s) for database \"%s\"\n"),
- progname, dbacl, fdbname);
- PQfinish(conn);
- exit_nicely(1);
- }
-
- fprintf(OPF, "%s", buf->data);
-
- dumpDatabaseConfig(conn, dbname);
-
- free(fdbname);
- }
-
- if (default_encoding)
- free(default_encoding);
- if (default_collate)
- free(default_collate);
- if (default_ctype)
- free(default_ctype);
-
- PQclear(res);
- destroyPQExpBuffer(buf);
-
- fprintf(OPF, "\n\n");
-}
-
-
-/*
- * Dump database-specific configuration
- */
-static void
-dumpDatabaseConfig(PGconn *conn, const char *dbname)
-{
- PQExpBuffer buf = createPQExpBuffer();
- int count = 1;
-
- for (;;)
- {
- PGresult *res;
-
- if (server_version >= 90000)
- printfPQExpBuffer(buf, "SELECT setconfig[%d] FROM pg_db_role_setting WHERE "
- "setrole = 0 AND setdatabase = (SELECT oid FROM pg_database WHERE datname = ", count);
- else
- printfPQExpBuffer(buf, "SELECT datconfig[%d] FROM pg_database WHERE datname = ", count);
- appendStringLiteralConn(buf, dbname, conn);
-
- if (server_version >= 90000)
- appendPQExpBufferChar(buf, ')');
-
- res = executeQuery(conn, buf->data);
- if (PQntuples(res) == 1 &&
- !PQgetisnull(res, 0, 0))
- {
- makeAlterConfigCommand(conn, PQgetvalue(res, 0, 0),
- "DATABASE", dbname, NULL, NULL);
- PQclear(res);
- count++;
- }
- else
- {
- PQclear(res);
- break;
- }
- }
-
- destroyPQExpBuffer(buf);
-}
-
-
/*
* Dump user-specific configuration
if (PQntuples(res) == 1 &&
!PQgetisnull(res, 0, 0))
{
+ resetPQExpBuffer(buf);
makeAlterConfigCommand(conn, PQgetvalue(res, 0, 0),
- "ROLE", username, NULL, NULL);
+ "ROLE", username, NULL, NULL,
+ buf);
+ fprintf(OPF, "%s", buf->data);
PQclear(res);
count++;
}
}
-/*
- * Dump user-and-database-specific configuration
- */
-static void
-dumpDbRoleConfig(PGconn *conn)
-{
- PQExpBuffer buf = createPQExpBuffer();
- PGresult *res;
- int i;
-
- printfPQExpBuffer(buf, "SELECT rolname, datname, unnest(setconfig) "
- "FROM pg_db_role_setting, %s u, pg_database "
- "WHERE setrole = u.oid AND setdatabase = pg_database.oid", role_catalog);
- res = executeQuery(conn, buf->data);
-
- if (PQntuples(res) > 0)
- {
- fprintf(OPF, "--\n-- Per-Database Role Settings \n--\n\n");
-
- for (i = 0; i < PQntuples(res); i++)
- {
- makeAlterConfigCommand(conn, PQgetvalue(res, i, 2),
- "ROLE", PQgetvalue(res, i, 0),
- "DATABASE", PQgetvalue(res, i, 1));
- }
-
- fprintf(OPF, "\n\n");
- }
-
- PQclear(res);
- destroyPQExpBuffer(buf);
-}
-
-
-/*
- * Helper function for dumpXXXConfig().
- */
-static void
-makeAlterConfigCommand(PGconn *conn, const char *arrayitem,
- const char *type, const char *name,
- const char *type2, const char *name2)
-{
- char *pos;
- char *mine;
- PQExpBuffer buf;
-
- mine = pg_strdup(arrayitem);
- pos = strchr(mine, '=');
- if (pos == NULL)
- {
- free(mine);
- return;
- }
-
- buf = createPQExpBuffer();
-
- *pos = 0;
- appendPQExpBuffer(buf, "ALTER %s %s ", type, fmtId(name));
- if (type2 != NULL && name2 != NULL)
- appendPQExpBuffer(buf, "IN %s %s ", type2, fmtId(name2));
- appendPQExpBuffer(buf, "SET %s TO ", fmtId(mine));
-
- /*
- * Some GUC variable names are 'LIST' type and hence must not be quoted.
- */
- if (pg_strcasecmp(mine, "DateStyle") == 0
- || pg_strcasecmp(mine, "search_path") == 0)
- appendPQExpBufferStr(buf, pos + 1);
- else
- appendStringLiteralConn(buf, pos + 1, conn);
- appendPQExpBufferStr(buf, ";\n");
-
- fprintf(OPF, "%s", buf->data);
- destroyPQExpBuffer(buf);
- free(mine);
-}
-
-
-
/*
* Dump contents of databases.
*/
PGresult *res;
int i;
- res = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1");
+ /*
+ * Skip databases marked not datallowconn, since we'd be unable to connect
+ * to them anyway. This must agree with dropDBs().
+ *
+ * We arrange for template1 to be processed first, then we process other
+ * DBs in alphabetical order. If we just did them all alphabetically, we
+ * might find ourselves trying to drop the "postgres" database while still
+ * connected to it. This makes trying to run the restore script while
+ * connected to "template1" a bad idea, but there's no fixed order that
+ * doesn't have some failure mode with --clean.
+ */
+ res = executeQuery(conn,
+ "SELECT datname "
+ "FROM pg_database d "
+ "WHERE datallowconn "
+ "ORDER BY (datname <> 'template1'), datname");
for (i = 0; i < PQntuples(res); i++)
{
+ char *dbname = PQgetvalue(res, i, 0);
+ const char *create_opts;
int ret;
- char *dbname = PQgetvalue(res, i, 0);
- PQExpBufferData connectbuf;
+ /* Skip template0, even if it's not marked !datallowconn. */
+ if (strcmp(dbname, "template0") == 0)
+ continue;
if (verbose)
fprintf(stderr, _("%s: dumping database \"%s\"...\n"), progname, dbname);
- initPQExpBuffer(&connectbuf);
- appendPsqlMetaConnect(&connectbuf, dbname);
- fprintf(OPF, "%s\n", connectbuf.data);
- termPQExpBuffer(&connectbuf);
-
/*
- * Restore will need to write to the target cluster. This connection
- * setting is emitted for pg_dumpall rather than in the code also used
- * by pg_dump, so that a cluster with databases or users which have
- * this flag turned on can still be replicated through pg_dumpall
- * without editing the file or stream. With pg_dump there are many
- * other ways to allow the file to be used, and leaving it out allows
- * users to protect databases from being accidental restore targets.
+ * We assume that "template1" and "postgres" already exist in the
+ * target installation. dropDBs() won't have removed them, for fear
+ * of removing the DB the restore script is initially connected to. If
+ * --clean was specified, tell pg_dump to drop and recreate them;
+ * otherwise we'll merely restore their contents. Other databases
+ * should simply be created.
*/
- fprintf(OPF, "SET default_transaction_read_only = off;\n\n");
+ if (strcmp(dbname, "template1") == 0 || strcmp(dbname, "postgres") == 0)
+ {
+ if (output_clean)
+ create_opts = "--clean --create";
+ else
+ {
+ create_opts = "";
+ /* Since pg_dump won't emit a \connect command, we must */
+ fprintf(OPF, "\\connect %s\n\n", dbname);
+ }
+ }
+ else
+ create_opts = "--create";
if (filename)
fclose(OPF);
- ret = runPgDump(dbname);
+ ret = runPgDump(dbname, create_opts);
if (ret != 0)
{
fprintf(stderr, _("%s: pg_dump failed on database \"%s\", exiting\n"), progname, dbname);
/*
- * Run pg_dump on dbname.
+ * Run pg_dump on dbname, with specified options.
*/
static int
-runPgDump(const char *dbname)
+runPgDump(const char *dbname, const char *create_opts)
{
PQExpBuffer connstrbuf = createPQExpBuffer();
PQExpBuffer cmd = createPQExpBuffer();
int ret;
- appendPQExpBuffer(cmd, "\"%s\" %s", pg_dump_bin,
- pgdumpopts->data);
+ appendPQExpBuffer(cmd, "\"%s\" %s %s", pg_dump_bin,
+ pgdumpopts->data, create_opts);
/*
* If we have a filename, use the undocumented plain-append pg_dump
all_runs => 1,
catch_all => 'COMMENT commands',
regexp => qr/^COMMENT ON DATABASE postgres IS .*;/m,
- like => {
+ # Should appear in the same tests as "CREATE DATABASE postgres"
+ like => { createdb => 1, },
+ unlike => {
binary_upgrade => 1,
clean => 1,
clean_if_exists => 1,
- createdb => 1,
+ column_inserts => 1,
+ data_only => 1,
defaults => 1,
exclude_dump_test_schema => 1,
exclude_test_table => 1,
no_blobs => 1,
no_privs => 1,
no_owner => 1,
+ only_dump_test_schema => 1,
+ only_dump_test_table => 1,
pg_dumpall_dbprivs => 1,
+ pg_dumpall_globals => 1,
+ pg_dumpall_globals_clean => 1,
+ role => 1,
schema_only => 1,
section_pre_data => 1,
- with_oids => 1, },
- unlike => {
- column_inserts => 1,
- data_only => 1,
- only_dump_test_schema => 1,
- only_dump_test_table => 1,
- role => 1,
- section_post_data => 1,
- test_schema_plus_blobs => 1, }, },
+ section_data => 1,
+ section_post_data => 1,
+ test_schema_plus_blobs => 1,
+ with_oids => 1, }, },
'COMMENT ON EXTENSION plpgsql' => {
all_runs => 1,
#endif
static void prepare_new_cluster(void);
-static void prepare_new_databases(void);
+static void prepare_new_globals(void);
static void create_new_objects(void);
static void copy_xact_xlog_xid(void);
static void set_frozenxids(bool minmxid_only);
/* -- NEW -- */
start_postmaster(&new_cluster, true);
- prepare_new_databases();
+ prepare_new_globals();
create_new_objects();
static void
-prepare_new_databases(void)
+prepare_new_globals(void)
{
/*
* We set autovacuum_freeze_max_age to its maximum value so autovacuum
prep_status("Restoring global objects in the new cluster");
- /*
- * We have to create the databases first so we can install support
- * functions in all the other databases. Ideally we could create the
- * support functions in template1 but pg_dumpall creates database using
- * the template0 template.
- */
exec_prog(UTILITY_LOG_FILE, NULL, true, true,
"\"%s/psql\" " EXEC_PSQL_ARGS " %s -f \"%s\"",
new_cluster.bindir, cluster_conn_opts(&new_cluster),
GLOBALS_DUMP_FILE);
check_ok();
-
- /* we load this to get a current list of databases */
- get_db_and_rel_infos(&new_cluster);
}
char sql_file_name[MAXPGPATH],
log_file_name[MAXPGPATH];
DbInfo *old_db = &old_cluster.dbarr.dbs[dbnum];
- PQExpBufferData connstr,
- escaped_connstr;
-
- initPQExpBuffer(&connstr);
- appendPQExpBuffer(&connstr, "dbname=");
- appendConnStrVal(&connstr, old_db->db_name);
- initPQExpBuffer(&escaped_connstr);
- appendShellString(&escaped_connstr, connstr.data);
- termPQExpBuffer(&connstr);
+ const char *create_opts;
+ const char *starting_db;
pg_log(PG_STATUS, "%s", old_db->db_name);
snprintf(sql_file_name, sizeof(sql_file_name), DB_DUMP_FILE_MASK, old_db->db_oid);
snprintf(log_file_name, sizeof(log_file_name), DB_DUMP_LOG_FILE_MASK, old_db->db_oid);
/*
- * pg_dump only produces its output at the end, so there is little
- * parallelism if using the pipe.
+ * template1 and postgres databases will already exist in the target
+ * installation, so tell pg_restore to drop and recreate them;
+ * otherwise we would fail to propagate their database-level
+ * properties.
*/
+ if (strcmp(old_db->db_name, "template1") == 0 ||
+ strcmp(old_db->db_name, "postgres") == 0)
+ create_opts = "--clean --create";
+ else
+ create_opts = "--create";
+
+ /* When processing template1, we can't connect there to start with */
+ if (strcmp(old_db->db_name, "template1") == 0)
+ starting_db = "postgres";
+ else
+ starting_db = "template1";
+
parallel_exec_prog(log_file_name,
NULL,
- "\"%s/pg_restore\" %s --exit-on-error --verbose --dbname %s \"%s\"",
+ "\"%s/pg_restore\" %s %s --exit-on-error --verbose "
+ "--dbname %s \"%s\"",
new_cluster.bindir,
cluster_conn_opts(&new_cluster),
- escaped_connstr.data,
+ create_opts,
+ starting_db,
sql_file_name);
-
- termPQExpBuffer(&escaped_connstr);
}
/* reap all children */
if (GET_MAJOR_VERSION(old_cluster.major_version) < 903)
set_frozenxids(true);
- /* regenerate now that we have objects in the databases */
+ /* update new_cluster info now that we have objects in the databases */
get_db_and_rel_infos(&new_cluster);
}