From 23f34fa4ba358671adab16773e79c17c92cbc870 Mon Sep 17 00:00:00 2001 From: Stephen Frost Date: Wed, 6 Apr 2016 21:45:32 -0400 Subject: [PATCH] In pg_dump, include pg_catalog and extension ACLs, if changed Now that all of the infrastructure exists, add in the ability to dump out the ACLs of the objects inside of pg_catalog or the ACLs for objects which are members of extensions, but only if they have been changed from their original values. The original values are tracked in pg_init_privs. When pg_dump'ing 9.6-and-above databases, we will dump out the ACLs for all objects in pg_catalog and the ACLs for all extension members, where the ACL has been changed from the original value which was set during either initdb or CREATE EXTENSION. This should not change dumps against pre-9.6 databases. Reviews by Alexander Korotkov, Jose Luis Tallon --- doc/src/sgml/extend.sgml | 21 + src/backend/catalog/aclchk.c | 17 +- src/backend/utils/adt/pg_upgrade_support.c | 12 + src/bin/initdb/initdb.c | 6 +- src/bin/pg_dump/dumputils.c | 274 ++++- src/bin/pg_dump/dumputils.h | 9 +- src/bin/pg_dump/pg_dump.c | 1079 ++++++++++++++++---- src/bin/pg_dump/pg_dump.h | 24 + src/bin/pg_dump/pg_dumpall.c | 6 +- src/include/catalog/binary_upgrade.h | 2 + src/include/catalog/pg_proc.h | 2 + src/test/regress/expected/init_privs.out | 13 + src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/init_privs.sql | 11 + 15 files changed, 1269 insertions(+), 210 deletions(-) create mode 100644 src/test/regress/expected/init_privs.out create mode 100644 src/test/regress/sql/init_privs.sql diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml index 4a883814d6..df3b9d9cb2 100644 --- a/doc/src/sgml/extend.sgml +++ b/doc/src/sgml/extend.sgml @@ -338,6 +338,27 @@ data; see below.) + + The extension script may set privileges on objects which are part of the + extension via GRANT and REVOKE + statements. The final set of privileges for each object (if any are set) + will be stored in the + pg_init_privs + system catalog. When pg_dump is used, the + CREATE EXTENSION command will be included in the dump, followed + by the set of GRANT and REVOKE + statements necessary to set the privileges on the objects to what they were + at the time the dump was taken. + + + + PostgreSQL does not currently support extension scripts + issuing CREATE POLICY or SECURITY LABEL + statements. These are expected to be set after the extension has been + created. All RLS policies and security labels on extension objects will be + included in dumps created by pg_dump. + + The extension mechanism also has provisions for packaging modification scripts that adjust the definitions of the SQL objects contained in an diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index ffb6678c6a..975fe13fcf 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -22,6 +22,7 @@ #include "access/htup_details.h" #include "access/sysattr.h" #include "access/xact.h" +#include "catalog/binary_upgrade.h" #include "catalog/catalog.h" #include "catalog/dependency.h" #include "catalog/indexing.h" @@ -85,6 +86,12 @@ typedef struct DropBehavior behavior; } InternalDefaultACL; +/* + * When performing a binary-upgrade, pg_dump will call a function to set + * this variable to let us know that we need to populate the pg_init_privs + * table for the GRANT/REVOKE commands while this variable is set to true. + */ +bool binary_upgrade_record_init_privs = false; static void ExecGrantStmt_oids(InternalGrant *istmt); static void ExecGrant_Relation(InternalGrant *grantStmt); @@ -5237,7 +5244,15 @@ recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl) HeapTuple tuple; HeapTuple oldtuple; - if (!creating_extension) + /* + * Generally, we only record the initial privileges when an extension is + * being created, but because we don't actually use CREATE EXTENSION + * during binary upgrades with pg_upgrade, there is a variable to let us + * know that the GRANT and REVOKE statements being issued, while this + * variable is true, are for the initial privileges of the extension + * object and therefore we need to record them. + */ + if (!creating_extension && !binary_upgrade_record_init_privs) return; relation = heap_open(InitPrivsRelationId, RowExclusiveLock); diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c index 912eadaf36..6ff035ace3 100644 --- a/src/backend/utils/adt/pg_upgrade_support.c +++ b/src/backend/utils/adt/pg_upgrade_support.c @@ -29,6 +29,7 @@ Datum binary_upgrade_set_next_toast_pg_class_oid(PG_FUNCTION_ARGS); Datum binary_upgrade_set_next_pg_enum_oid(PG_FUNCTION_ARGS); Datum binary_upgrade_set_next_pg_authid_oid(PG_FUNCTION_ARGS); Datum binary_upgrade_create_empty_extension(PG_FUNCTION_ARGS); +Datum binary_upgrade_set_record_init_privs(PG_FUNCTION_ARGS); #define CHECK_IS_BINARY_UPGRADE \ @@ -193,3 +194,14 @@ binary_upgrade_create_empty_extension(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } + +Datum +binary_upgrade_set_record_init_privs(PG_FUNCTION_ARGS) +{ + bool record_init_privs = PG_GETARG_BOOL(0); + + CHECK_IS_BINARY_UPGRADE; + binary_upgrade_record_init_privs = record_init_privs; + + PG_RETURN_VOID(); +} diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 858667b394..18a3826b00 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2002,7 +2002,11 @@ setup_privileges(FILE *cmdfd) char **priv_lines; static char *privileges_setup[] = { "UPDATE pg_class " - " SET relacl = E'{\"=r/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' " + " SET relacl = (SELECT array_agg(a.acl) FROM " + " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " + " UNION SELECT unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", "GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n\n", diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c index 5301d3fa54..c55a2fa14e 100644 --- a/src/bin/pg_dump/dumputils.c +++ b/src/bin/pg_dump/dumputils.c @@ -38,6 +38,7 @@ static void AddAcl(PQExpBuffer aclbuf, const char *keyword, * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE, * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT) * acls: the ACL string fetched from the database + * racls: the ACL string of any initial-but-now-revoked privileges * owner: username of object owner (will be passed through fmtId); can be * NULL or empty string to indicate "no owner known" * prefix: string to prefix to each generated command; typically empty @@ -54,13 +55,15 @@ static void AddAcl(PQExpBuffer aclbuf, const char *keyword, */ bool buildACLCommands(const char *name, const char *subname, - const char *type, const char *acls, const char *owner, - const char *prefix, int remoteVersion, + const char *type, const char *acls, const char *racls, + const char *owner, const char *prefix, int remoteVersion, PQExpBuffer sql) { bool ok = true; - char **aclitems; - int naclitems; + char **aclitems = NULL; + char **raclitems = NULL; + int naclitems = 0; + int nraclitems = 0; int i; PQExpBuffer grantee, grantor, @@ -70,18 +73,31 @@ buildACLCommands(const char *name, const char *subname, secondsql; bool found_owner_privs = false; - if (strlen(acls) == 0) + if (strlen(acls) == 0 && strlen(racls) == 0) return true; /* object has default permissions */ /* treat empty-string owner same as NULL */ if (owner && *owner == '\0') owner = NULL; - if (!parsePGArray(acls, &aclitems, &naclitems)) + if (strlen(acls) != 0) { - if (aclitems) - free(aclitems); - return false; + if (!parsePGArray(acls, &aclitems, &naclitems)) + { + if (aclitems) + free(aclitems); + return false; + } + } + + if (strlen(racls) != 0) + { + if (!parsePGArray(racls, &raclitems, &nraclitems)) + { + if (raclitems) + free(raclitems); + return false; + } } grantee = createPQExpBuffer(); @@ -90,24 +106,101 @@ buildACLCommands(const char *name, const char *subname, privswgo = createPQExpBuffer(); /* - * At the end, these two will be pasted together to form the result. But - * the owner privileges need to go before the other ones to keep the - * dependencies valid. In recent versions this is normally the case, but - * in old versions they come after the PUBLIC privileges and that results - * in problems if we need to run REVOKE on the owner privileges. + * At the end, these two will be pasted together to form the result. + * + * For older systems we use these to ensure that the owner privileges go + * before the other ones, as a GRANT could create the default entry for + * the object, which generally includes all rights for the owner. In more + * recent versions we normally handle this because the owner rights come + * first in the ACLs, but older versions might have them after the PUBLIC + * privileges. + * + * For 9.6 and later systems, much of this changes. With 9.6, we check + * the default privileges for the objects at dump time and create two sets + * of ACLs- "racls" which are the ACLs to REVOKE from the object (as the + * object may have initial privileges on it, along with any default ACLs + * which are not part of the current set of privileges), and regular + * "acls", which are the ACLs to GRANT to the object. We handle the + * REVOKEs first, followed by the GRANTs. */ firstsql = createPQExpBuffer(); secondsql = createPQExpBuffer(); /* - * Always start with REVOKE ALL FROM PUBLIC, so that we don't have to - * wire-in knowledge about the default public privileges for different - * kinds of objects. + * For pre-9.6 systems, we always start with REVOKE ALL FROM PUBLIC, as we + * don't wish to make any assumptions about what the default ACLs are, and + * we do not collect them during the dump phase (and racls will always be + * the empty set, see above). + * + * For 9.6 and later, if any revoke ACLs have been provided, then include + * them in 'firstsql'. + * + * Revoke ACLs happen when an object starts out life with a set of + * privileges (eg: GRANT SELECT ON pg_class TO PUBLIC;) and the user has + * decided to revoke those rights. Since those objects come into being + * with those default privileges, we have to revoke them to match what the + * current state of affairs is. Note that we only started explicitly + * tracking such initial rights in 9.6, and prior to that all initial + * rights are actually handled by the simple 'REVOKE ALL .. FROM PUBLIC' + * case, for initdb-created objects. Prior to 9.6, we didn't handle + * extensions correctly, but we do now by tracking their initial + * privileges, in the same way we track initdb initial privileges, see + * pg_init_privs. */ - appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix); - if (subname) - appendPQExpBuffer(firstsql, "(%s)", subname); - appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name); + if (remoteVersion < 90600) + { + Assert(nraclitems == 0); + + appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix); + if (subname) + appendPQExpBuffer(firstsql, "(%s)", subname); + appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name); + } + else + { + /* Scan individual REVOKE ACL items */ + for (i = 0; i < nraclitems; i++) + { + if (!parseAclItem(raclitems[i], type, name, subname, remoteVersion, + grantee, grantor, privs, privswgo)) + { + ok = false; + break; + } + + if (privs->len > 0 || privswgo->len > 0) + { + if (privs->len > 0) + { + appendPQExpBuffer(firstsql, "%sREVOKE %s ON %s %s FROM ", + prefix, privs->data, type, name); + if (grantee->len == 0) + appendPQExpBufferStr(firstsql, "PUBLIC;\n"); + else if (strncmp(grantee->data, "group ", + strlen("group ")) == 0) + appendPQExpBuffer(firstsql, "GROUP %s;\n", + fmtId(grantee->data + strlen("group "))); + else + appendPQExpBuffer(firstsql, "%s;\n", + fmtId(grantee->data)); + } + if (privswgo->len > 0) + { + appendPQExpBuffer(firstsql, + "%sREVOKE GRANT OPTION FOR %s ON %s %s FROM ", + prefix, privswgo->data, type, name); + if (grantee->len == 0) + appendPQExpBufferStr(firstsql, "PUBLIC"); + else if (strncmp(grantee->data, "group ", + strlen("group ")) == 0) + appendPQExpBuffer(firstsql, "GROUP %s", + fmtId(grantee->data + strlen("group "))); + else + appendPQExpBufferStr(firstsql, fmtId(grantee->data)); + } + } + } + } /* * We still need some hacking though to cover the case where new default @@ -138,7 +231,14 @@ buildACLCommands(const char *name, const char *subname, if (privs->len > 0 || privswgo->len > 0) { - if (owner + /* + * Prior to 9.6, we had to handle owner privileges in a special + * manner by first REVOKE'ing the rights and then GRANT'ing them + * after. With 9.6 and above, what we need to REVOKE and what we + * need to GRANT is figured out when we dump and stashed into + * "racls" and "acls", respectivly. See above. + */ + if (remoteVersion < 90600 && owner && strcmp(grantee->data, owner) == 0 && strcmp(grantor->data, owner) == 0) { @@ -172,7 +272,14 @@ buildACLCommands(const char *name, const char *subname, else { /* - * Otherwise can assume we are starting from no privs. + * For systems prior to 9.6, we can assume we are starting + * from no privs at this point. + * + * For 9.6 and above, at this point we have issued REVOKE + * statements for all initial and default privileges which are + * no longer present on the object (as they were passed in as + * 'racls') and we can simply GRANT the rights which are in + * 'acls'. */ if (grantor->len > 0 && (!owner || strcmp(owner, grantor->data) != 0)) @@ -215,9 +322,12 @@ buildACLCommands(const char *name, const char *subname, } /* - * If we didn't find any owner privs, the owner must have revoked 'em all + * For systems prior to 9.6, if we didn't find any owner privs, the owner + * must have revoked 'em all. + * + * For 9.6 and above, we handle this through the 'racls'. See above. */ - if (!found_owner_privs && owner) + if (remoteVersion < 90600 && !found_owner_privs && owner) { appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix); if (subname) @@ -235,7 +345,11 @@ buildACLCommands(const char *name, const char *subname, destroyPQExpBuffer(firstsql); destroyPQExpBuffer(secondsql); - free(aclitems); + if (aclitems) + free(aclitems); + + if (raclitems) + free(raclitems); return ok; } @@ -275,7 +389,7 @@ buildDefaultACLCommands(const char *type, const char *nspname, appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname)); result = buildACLCommands("", NULL, - type, acls, owner, + type, acls, "", owner, prefix->data, remoteVersion, sql); @@ -555,3 +669,109 @@ emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, appendPQExpBufferStr(buffer, ";\n"); } } + +/* + * buildACLQueries + * + * Build the subqueries to extract out the correct set of ACLs to be + * GRANT'd and REVOKE'd for the specific kind of object, accounting for any + * initial privileges (from pg_init_privs) and based on if we are in binary + * upgrade mode or not. + * + * Also builds subqueries to extract out the set of ACLs to go from the object + * default privileges to the privileges in pg_init_privs, if we are in binary + * upgrade mode, so that those privileges can be set up and recorded in the new + * cluster before the regular privileges are added on top of those. + */ +void +buildACLQueries(PQExpBuffer acl_subquery, PQExpBuffer racl_subquery, + PQExpBuffer init_acl_subquery, PQExpBuffer init_racl_subquery, + const char *acl_column, const char *acl_owner, + const char *obj_kind, bool binary_upgrade) +{ + /* + * To get the delta from what the permissions were at creation time + * (either initdb or CREATE EXTENSION) vs. what they are now, we have to + * look at two things: + * + * What privileges have been added, which we calculate by extracting all + * the current privileges (using the set of default privileges for the + * object type if current privileges are NULL) and then removing those + * which existed at creation time (again, using the set of default + * privileges for the object type if there were no creation time + * privileges). + * + * What privileges have been removed, which we calculate by extracting the + * privileges as they were at creation time (or the default privileges, as + * above), and then removing the current privileges (or the default + * privileges, if current privileges are NULL). + * + * As a good cross-check, both directions of these checks should result in + * the empty set if both the current ACL and the initial privs are NULL + * (meaning, in practice, that the default ACLs were there at init time + * and is what the current privileges are). + * + * We always perform this delta on all ACLs and expect that by the time + * these are run the initial privileges will be in place, even in a + * binary upgrade situation (see below). + */ + printfPQExpBuffer(acl_subquery, "(SELECT array_agg(acl) FROM " + "(SELECT unnest(coalesce(%s,acldefault(%s,%s))) AS acl " + "EXCEPT " + "SELECT unnest(coalesce(pip.initprivs,acldefault(%s,%s)))) as foo)", + acl_column, + obj_kind, + acl_owner, + obj_kind, + acl_owner); + + printfPQExpBuffer(racl_subquery, "(SELECT array_agg(acl) FROM " + "(SELECT unnest(coalesce(pip.initprivs,acldefault(%s,%s))) AS acl " + "EXCEPT " + "SELECT unnest(coalesce(%s,acldefault(%s,%s)))) as foo)", + obj_kind, + acl_owner, + acl_column, + obj_kind, + acl_owner); + + /* + * In binary upgrade mode we don't run the extension script but instead + * dump out the objects independently and then recreate them. To preserve + * the initial privileges which were set on extension objects, we need to + * grab the set of GRANT and REVOKE commands necessary to get from the + * default privileges of an object to the initial privileges as recorded + * in pg_init_privs. + * + * These will then be run ahead of the regular ACL commands, which were + * calculated using the queries above, inside of a block which sets a flag + * to indicate that the backend should record the results of these GRANT + * and REVOKE statements into pg_init_privs. This is how we preserve the + * contents of that catalog across binary upgrades. + */ + if (binary_upgrade) + { + printfPQExpBuffer(init_acl_subquery, + "CASE WHEN privtype = 'e' THEN " + "(SELECT array_agg(acl) FROM " + "(SELECT unnest(pip.initprivs) AS acl " + "EXCEPT " + "SELECT unnest(acldefault(%s,%s))) as foo) END", + obj_kind, + acl_owner); + + printfPQExpBuffer(init_racl_subquery, + "CASE WHEN privtype = 'e' THEN " + "(SELECT array_agg(acl) FROM " + "(SELECT unnest(acldefault(%s,%s)) AS acl " + "EXCEPT " + "SELECT unnest(pip.initprivs)) as foo) END", + obj_kind, + acl_owner); + } + else + { + printfPQExpBuffer(init_acl_subquery, "NULL"); + printfPQExpBuffer(init_racl_subquery, "NULL"); + } +} diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h index 4b404be99a..b2fd7d37d0 100644 --- a/src/bin/pg_dump/dumputils.h +++ b/src/bin/pg_dump/dumputils.h @@ -37,8 +37,8 @@ extern bool buildACLCommands(const char *name, const char *subname, - const char *type, const char *acls, const char *owner, - const char *prefix, int remoteVersion, + const char *type, const char *acls, const char *racls, + const char *owner, const char *prefix, int remoteVersion, PQExpBuffer sql); extern bool buildDefaultACLCommands(const char *type, const char *nspname, const char *acls, const char *owner, @@ -49,4 +49,9 @@ extern void buildShSecLabelQuery(PGconn *conn, const char *catalog_name, extern void emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, const char *target, const char *objname); +extern void buildACLQueries(PQExpBuffer acl_subquery, PQExpBuffer racl_subquery, + PQExpBuffer init_acl_subquery, PQExpBuffer init_racl_subquery, + const char *acl_column, const char *acl_owner, + const char *obj_kind, bool binary_upgrade); + #endif /* DUMPUTILS_H */ diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index e3360bc4d5..33cd6651d1 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -206,7 +206,8 @@ static void dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo); static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, const char *type, const char *name, const char *subname, const char *tag, const char *nspname, const char *owner, - const char *acls); + const char *acls, const char *racls, + const char *initacls, const char *initracls); static void getDependencies(Archive *fout); static void BuildArchiveDependencies(Archive *fout); @@ -1268,7 +1269,7 @@ expand_table_name_patterns(Archive *fout, * Returns true if object is an extension member, else false. */ static bool -checkExtensionMembership(DumpableObject *dobj, DumpOptions *dopt) +checkExtensionMembership(DumpableObject *dobj, Archive *fout) { ExtensionInfo *ext = findOwningExtension(dobj->catId); @@ -1281,12 +1282,26 @@ checkExtensionMembership(DumpableObject *dobj, DumpOptions *dopt) addObjectDependency(dobj, ext->dobj.dumpId); /* - * Normally, mark the member object as not to be dumped. But in binary - * upgrades, we still dump the members individually, since the idea is to - * exactly reproduce the database contents rather than replace the - * extension contents with something different. + * In 9.6 and above, mark the member object to have any non-initial ACL, + * policies, and security lables dumped. + * + * Note that any initial ACLs (see pg_init_privs) will be removed when we + * extract the information about the object. We don't provide support for + * initial policies and security labels and it seems unlikely for those to + * ever exist, but we may have to revisit this later. + * + * Prior to 9.6, we do not include any extension member components. + * + * In binary upgrades, we still dump all components of the members + * individually, since the idea is to exactly reproduce the database + * contents rather than replace the extension contents with something + * different. */ - if (!dopt->binary_upgrade) + if (!fout->dopt->binary_upgrade && fout->remoteVersion >= 90600) + dobj->dump = DUMP_COMPONENT_ACL | + DUMP_COMPONENT_SECLABEL | + DUMP_COMPONENT_POLICY; + else if (!fout->dopt->binary_upgrade) dobj->dump = DUMP_COMPONENT_NONE; else dobj->dump = ext->dobj.dump; @@ -1299,9 +1314,9 @@ checkExtensionMembership(DumpableObject *dobj, DumpOptions *dopt) * Mark a namespace as to be dumped or not */ static void -selectDumpableNamespace(NamespaceInfo *nsinfo, DumpOptions *dopt) +selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout) { - if (checkExtensionMembership(&nsinfo->dobj, dopt)) + if (checkExtensionMembership(&nsinfo->dobj, fout)) return; /* extension membership overrides all else */ /* @@ -1317,6 +1332,16 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, DumpOptions *dopt) simple_oid_list_member(&schema_include_oids, nsinfo->dobj.catId.oid) ? DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE; + else if (fout->remoteVersion >= 90600 && + strncmp(nsinfo->dobj.name, "pg_catalog", + strlen("pg_catalog")) == 0) + + /* + * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if + * they are interesting (and not the original ACLs which were set at + * initdb time, see pg_init_privs). + */ + nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL; else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 || strcmp(nsinfo->dobj.name, "information_schema") == 0) nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE; @@ -1337,9 +1362,9 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, DumpOptions *dopt) * Mark a table as to be dumped or not */ static void -selectDumpableTable(TableInfo *tbinfo, DumpOptions *dopt) +selectDumpableTable(TableInfo *tbinfo, Archive *fout) { - if (checkExtensionMembership(&tbinfo->dobj, dopt)) + if (checkExtensionMembership(&tbinfo->dobj, fout)) return; /* extension membership overrides all else */ /* @@ -1376,7 +1401,7 @@ selectDumpableTable(TableInfo *tbinfo, DumpOptions *dopt) * object (the table or base type). */ static void -selectDumpableType(TypeInfo *tyinfo, DumpOptions *dopt) +selectDumpableType(TypeInfo *tyinfo, Archive *fout) { /* skip complex types, except for standalone composite types */ if (OidIsValid(tyinfo->typrelid) && @@ -1405,7 +1430,7 @@ selectDumpableType(TypeInfo *tyinfo, DumpOptions *dopt) */ } - if (checkExtensionMembership(&tyinfo->dobj, dopt)) + if (checkExtensionMembership(&tyinfo->dobj, fout)) return; /* extension membership overrides all else */ /* Dump based on if the contents of the namespace are being dumped */ @@ -1443,15 +1468,15 @@ selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt) * OID is in the range reserved for initdb. */ static void -selectDumpableCast(CastInfo *cast, DumpOptions *dopt) +selectDumpableCast(CastInfo *cast, Archive *fout) { - if (checkExtensionMembership(&cast->dobj, dopt)) + if (checkExtensionMembership(&cast->dobj, fout)) return; /* extension membership overrides all else */ if (cast->dobj.catId.oid < (Oid) FirstNormalObjectId) cast->dobj.dump = DUMP_COMPONENT_NONE; else - cast->dobj.dump = dopt->include_everything ? + cast->dobj.dump = fout->dopt->include_everything ? DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE; } @@ -1464,15 +1489,15 @@ selectDumpableCast(CastInfo *cast, DumpOptions *dopt) * language's OID is in the range reserved for initdb. */ static void -selectDumpableProcLang(ProcLangInfo *plang, DumpOptions *dopt) +selectDumpableProcLang(ProcLangInfo *plang, Archive *fout) { - if (checkExtensionMembership(&plang->dobj, dopt)) + if (checkExtensionMembership(&plang->dobj, fout)) return; /* extension membership overrides all else */ if (plang->dobj.catId.oid < (Oid) FirstNormalObjectId) plang->dobj.dump = DUMP_COMPONENT_NONE; else - plang->dobj.dump = dopt->include_everything ? + plang->dobj.dump = fout->dopt->include_everything ? DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE; } @@ -1485,15 +1510,16 @@ selectDumpableProcLang(ProcLangInfo *plang, DumpOptions *dopt) * method's OID is in the range reserved for initdb. */ static void -selectDumpableAccessMethod(AccessMethodInfo *method, DumpOptions *dopt) +selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout) { - if (checkExtensionMembership(&method->dobj, dopt)) + if (checkExtensionMembership(&method->dobj, fout)) return; /* extension membership overrides all else */ if (method->dobj.catId.oid < (Oid) FirstNormalObjectId) - method->dobj.dump = false; + method->dobj.dump = DUMP_COMPONENT_NONE; else - method->dobj.dump = dopt->include_everything; + method->dobj.dump = fout->dopt->include_everything ? + DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE; } /* @@ -1523,9 +1549,9 @@ selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt) * Use this only for object types without a special-case routine above. */ static void -selectDumpableObject(DumpableObject *dobj, DumpOptions *dopt) +selectDumpableObject(DumpableObject *dobj, Archive *fout) { - if (checkExtensionMembership(dobj, dopt)) + if (checkExtensionMembership(dobj, fout)) return; /* extension membership overrides all else */ /* @@ -1535,7 +1561,7 @@ selectDumpableObject(DumpableObject *dobj, DumpOptions *dopt) if (dobj->namespace) dobj->dump = dobj->namespace->dobj.dump_contains; else - dobj->dump = dopt->include_everything ? + dobj->dump = fout->dopt->include_everything ? DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE; } @@ -2729,12 +2755,19 @@ dumpStdStrings(Archive *AH) static void getBlobs(Archive *fout) { + DumpOptions *dopt = fout->dopt; PQExpBuffer blobQry = createPQExpBuffer(); BlobInfo *binfo; DumpableObject *bdata; PGresult *res; int ntups; int i; + int i_oid; + int i_lomowner; + int i_lomacl; + int i_rlomacl; + int i_initlomacl; + int i_initrlomacl; /* Verbose message */ if (g_verbose) @@ -2744,52 +2777,102 @@ getBlobs(Archive *fout) selectSourceSchema(fout, "pg_catalog"); /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ - if (fout->remoteVersion >= 90000) + if (fout->remoteVersion >= 90600) + { + PQExpBuffer acl_subquery = createPQExpBuffer(); + PQExpBuffer racl_subquery = createPQExpBuffer(); + PQExpBuffer init_acl_subquery = createPQExpBuffer(); + PQExpBuffer init_racl_subquery = createPQExpBuffer(); + + buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery, + init_racl_subquery, "l.lomacl", "l.lomowner", "'L'", + dopt->binary_upgrade); + appendPQExpBuffer(blobQry, - "SELECT oid, (%s lomowner) AS rolname, lomacl" + "SELECT l.oid, (%s l.lomowner) AS rolname, " + "%s AS lomacl, " + "%s AS rlomacl, " + "%s AS initlomacl, " + "%s AS initrlomacl " + "FROM pg_largeobject_metadata l " + "LEFT JOIN pg_init_privs pip ON " + "(l.oid = pip.objoid AND pip.classoid = " + "(SELECT oid FROM pg_class WHERE relname = 'pg_largeobject')" + "AND pip.objsubid = 0) ", + username_subquery, + acl_subquery->data, + racl_subquery->data, + init_acl_subquery->data, + init_racl_subquery->data); + + destroyPQExpBuffer(acl_subquery); + destroyPQExpBuffer(racl_subquery); + destroyPQExpBuffer(init_acl_subquery); + destroyPQExpBuffer(init_racl_subquery); + } + else if (fout->remoteVersion >= 90000) + appendPQExpBuffer(blobQry, + "SELECT oid, (%s lomowner) AS rolname, lomacl, " + "NULL AS rlomacl, NULL as initlomacl, " + "NULL as initrlomacl " " FROM pg_largeobject_metadata", username_subquery); else if (fout->remoteVersion >= 70100) appendPQExpBufferStr(blobQry, - "SELECT DISTINCT loid, NULL::oid, NULL::oid" + "SELECT DISTINCT loid, NULL::oid, NULL, " + "NULL AS rlomacl, NULL AS initlomacl, " + "NULL AS initrlomacl " " FROM pg_largeobject"); else appendPQExpBufferStr(blobQry, - "SELECT oid, NULL::oid, NULL::oid" + "SELECT oid, NULL::oid, NULL, " + "NULL AS rlomacl, NULL AS initlomacl, " + "NULL AS initrlomacl " " FROM pg_class WHERE relkind = 'l'"); res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); + ntups = PQntuples(res); + + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + + } + + /* + * If we have any large objects, a "BLOBS" archive entry is needed. This + * is just a placeholder for sorting; it carries no data now. + */ if (ntups > 0) { - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, 0)); - AssignDumpId(&binfo[i].dobj); - - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, 0)); - if (!PQgetisnull(res, i, 1)) - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, 1)); - else - binfo[i].rolname = ""; - if (!PQgetisnull(res, i, 2)) - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, 2)); - else - binfo[i].blobacl = NULL; - } - - /* - * If we have any large objects, a "BLOBS" archive entry is needed. - * This is just a placeholder for sorting; it carries no data now. - */ bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; bdata->catId = nilCatalogId; @@ -2850,7 +2933,8 @@ dumpBlob(Archive *fout, BlobInfo *binfo) if (binfo->blobacl && (binfo->dobj.dump & DUMP_COMPONENT_ACL)) dumpACL(fout, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT", binfo->dobj.name, NULL, cquery->data, - NULL, binfo->rolname, binfo->blobacl); + NULL, binfo->rolname, binfo->blobacl, binfo->rblobacl, + binfo->initblobacl, binfo->initrblobacl); destroyPQExpBuffer(cquery); destroyPQExpBuffer(dquery); @@ -3385,6 +3469,9 @@ getNamespaces(Archive *fout, int *numNamespaces) int i_nspname; int i_rolname; int i_nspacl; + int i_rnspacl; + int i_initnspacl; + int i_initrnspacl; /* * Before 7.3, there are no real namespaces; create two dummy entries, one @@ -3401,8 +3488,11 @@ getNamespaces(Archive *fout, int *numNamespaces) nsinfo[0].dobj.name = pg_strdup("public"); nsinfo[0].rolname = pg_strdup(""); nsinfo[0].nspacl = pg_strdup(""); + nsinfo[0].rnspacl = pg_strdup(""); + nsinfo[0].initnspacl = pg_strdup(""); + nsinfo[0].initrnspacl = pg_strdup(""); - selectDumpableNamespace(&nsinfo[0], dopt); + selectDumpableNamespace(&nsinfo[0], fout); nsinfo[1].dobj.objType = DO_NAMESPACE; nsinfo[1].dobj.catId.tableoid = 0; @@ -3411,8 +3501,11 @@ getNamespaces(Archive *fout, int *numNamespaces) nsinfo[1].dobj.name = pg_strdup("pg_catalog"); nsinfo[1].rolname = pg_strdup(""); nsinfo[1].nspacl = pg_strdup(""); + nsinfo[1].rnspacl = pg_strdup(""); + nsinfo[1].initnspacl = pg_strdup(""); + nsinfo[1].initrnspacl = pg_strdup(""); - selectDumpableNamespace(&nsinfo[1], dopt); + selectDumpableNamespace(&nsinfo[1], fout); *numNamespaces = 2; @@ -3428,10 +3521,46 @@ getNamespaces(Archive *fout, int *numNamespaces) * we fetch all namespaces including system ones, so that every object we * read in can be linked to a containing namespace. */ - appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, " - "(%s nspowner) AS rolname, " - "nspacl FROM pg_namespace", - username_subquery); + if (fout->remoteVersion >= 90600) + { + PQExpBuffer acl_subquery = createPQExpBuffer(); + PQExpBuffer racl_subquery = createPQExpBuffer(); + PQExpBuffer init_acl_subquery = createPQExpBuffer(); + PQExpBuffer init_racl_subquery = createPQExpBuffer(); + + buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery, + init_racl_subquery, "n.nspacl", "n.nspowner", "'n'", + dopt->binary_upgrade); + + appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, " + "(%s nspowner) AS rolname, " + "%s as nspacl, " + "%s as rnspacl, " + "%s as initnspacl, " + "%s as initrnspacl " + "FROM pg_namespace n " + "LEFT JOIN pg_init_privs pip " + "ON (n.oid = pip.objoid AND pip.classoid = " + "(SELECT oid FROM pg_class WHERE relname = 'pg_namespace') " + "AND pip.objsubid = 0) ", + username_subquery, + acl_subquery->data, + racl_subquery->data, + init_acl_subquery->data, + init_racl_subquery->data); + + destroyPQExpBuffer(acl_subquery); + destroyPQExpBuffer(racl_subquery); + destroyPQExpBuffer(init_acl_subquery); + destroyPQExpBuffer(init_racl_subquery); + } + else + appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, " + "(%s nspowner) AS rolname, " + "nspacl, NULL as rnspacl, " + "NULL AS initnspacl, NULL as initrnspacl " + "FROM pg_namespace", + username_subquery); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -3444,6 +3573,9 @@ getNamespaces(Archive *fout, int *numNamespaces) i_nspname = PQfnumber(res, "nspname"); i_rolname = PQfnumber(res, "rolname"); i_nspacl = PQfnumber(res, "nspacl"); + i_rnspacl = PQfnumber(res, "rnspacl"); + i_initnspacl = PQfnumber(res, "initnspacl"); + i_initrnspacl = PQfnumber(res, "initrnspacl"); for (i = 0; i < ntups; i++) { @@ -3454,9 +3586,25 @@ getNamespaces(Archive *fout, int *numNamespaces) nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname)); nsinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); nsinfo[i].nspacl = pg_strdup(PQgetvalue(res, i, i_nspacl)); + nsinfo[i].rnspacl = pg_strdup(PQgetvalue(res, i, i_rnspacl)); + nsinfo[i].initnspacl = pg_strdup(PQgetvalue(res, i, i_initnspacl)); + nsinfo[i].initrnspacl = pg_strdup(PQgetvalue(res, i, i_initrnspacl)); /* Decide whether to dump this namespace */ - selectDumpableNamespace(&nsinfo[i], dopt); + selectDumpableNamespace(&nsinfo[i], fout); + + /* + * Do not try to dump ACL if the ACL is empty or the default. + * + * This is useful because, for some schemas/objects, the only + * component we are going to try and dump is the ACL and if we can + * remove that then 'dump' goes to zero/false and we don't consider + * this object for dumping at all later on. + */ + if (PQgetisnull(res, i, i_nspacl) && PQgetisnull(res, i, i_rnspacl) && + PQgetisnull(res, i, i_initnspacl) && + PQgetisnull(res, i, i_initrnspacl)) + nsinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; if (strlen(nsinfo[i].rolname) == 0) write_msg(NULL, "WARNING: owner of schema \"%s\" appears to be invalid\n", @@ -3616,6 +3764,9 @@ getTypes(Archive *fout, int *numTypes) int i_typname; int i_typnamespace; int i_typacl; + int i_rtypacl; + int i_inittypacl; + int i_initrtypacl; int i_rolname; int i_typinput; int i_typoutput; @@ -3645,10 +3796,52 @@ getTypes(Archive *fout, int *numTypes) /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); - if (fout->remoteVersion >= 90200) + if (fout->remoteVersion >= 90600) + { + PQExpBuffer acl_subquery = createPQExpBuffer(); + PQExpBuffer racl_subquery = createPQExpBuffer(); + PQExpBuffer initacl_subquery = createPQExpBuffer(); + PQExpBuffer initracl_subquery = createPQExpBuffer(); + + buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, + initracl_subquery, "t.typacl", "t.typowner", "'T'", + dopt->binary_upgrade); + + appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, " + "t.typnamespace, " + "%s AS typacl, " + "%s AS rtypacl, " + "%s AS inittypacl, " + "%s AS initrtypacl, " + "(%s t.typowner) AS rolname, " + "t.typinput::oid AS typinput, " + "t.typoutput::oid AS typoutput, t.typelem, t.typrelid, " + "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" " + "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, " + "t.typtype, t.typisdefined, " + "t.typname[0] = '_' AND t.typelem != 0 AND " + "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray " + "FROM pg_type t " + "LEFT JOIN pg_init_privs pip " + "ON (t.oid = pip.objoid AND pip.classoid = " + "(SELECT oid FROM pg_class WHERE relname = 'pg_type') " + "AND pip.objsubid = 0) ", + acl_subquery->data, + racl_subquery->data, + initacl_subquery->data, + initracl_subquery->data, + username_subquery); + + destroyPQExpBuffer(acl_subquery); + destroyPQExpBuffer(racl_subquery); + destroyPQExpBuffer(initacl_subquery); + destroyPQExpBuffer(initracl_subquery); + } + else if (fout->remoteVersion >= 90200) { appendPQExpBuffer(query, "SELECT tableoid, oid, typname, " - "typnamespace, typacl, " + "typnamespace, typacl, NULL as rtypacl, " + "NULL AS inittypacl, NULL AS initrtypacl, " "(%s typowner) AS rolname, " "typinput::oid AS typinput, " "typoutput::oid AS typoutput, typelem, typrelid, " @@ -3663,7 +3856,8 @@ getTypes(Archive *fout, int *numTypes) else if (fout->remoteVersion >= 80300) { appendPQExpBuffer(query, "SELECT tableoid, oid, typname, " - "typnamespace, NULL AS typacl, " + "typnamespace, NULL AS typacl, NULL as rtypacl, " + "NULL AS inittypacl, NULL AS initrtypacl, " "(%s typowner) AS rolname, " "typinput::oid AS typinput, " "typoutput::oid AS typoutput, typelem, typrelid, " @@ -3678,7 +3872,8 @@ getTypes(Archive *fout, int *numTypes) else if (fout->remoteVersion >= 70300) { appendPQExpBuffer(query, "SELECT tableoid, oid, typname, " - "typnamespace, NULL AS typacl, " + "typnamespace, NULL AS typacl, NULL as rtypacl, " + "NULL AS inittypacl, NULL AS initrtypacl, " "(%s typowner) AS rolname, " "typinput::oid AS typinput, " "typoutput::oid AS typoutput, typelem, typrelid, " @@ -3692,7 +3887,8 @@ getTypes(Archive *fout, int *numTypes) else if (fout->remoteVersion >= 70100) { appendPQExpBuffer(query, "SELECT tableoid, oid, typname, " - "0::oid AS typnamespace, NULL AS typacl, " + "0::oid AS typnamespace, NULL AS typacl, NULL as rtypacl, " + "NULL AS inittypacl, NULL AS initrtypacl, " "(%s typowner) AS rolname, " "typinput::oid AS typinput, " "typoutput::oid AS typoutput, typelem, typrelid, " @@ -3708,7 +3904,8 @@ getTypes(Archive *fout, int *numTypes) appendPQExpBuffer(query, "SELECT " "(SELECT oid FROM pg_class WHERE relname = 'pg_type') AS tableoid, " "oid, typname, " - "0::oid AS typnamespace, NULL AS typacl, " + "0::oid AS typnamespace, NULL AS typacl, NULL as rtypacl, " + "NULL AS inittypacl, NULL AS initrtypacl, " "(%s typowner) AS rolname, " "typinput::oid AS typinput, " "typoutput::oid AS typoutput, typelem, typrelid, " @@ -3731,6 +3928,9 @@ getTypes(Archive *fout, int *numTypes) i_typname = PQfnumber(res, "typname"); i_typnamespace = PQfnumber(res, "typnamespace"); i_typacl = PQfnumber(res, "typacl"); + i_rtypacl = PQfnumber(res, "rtypacl"); + i_inittypacl = PQfnumber(res, "inittypacl"); + i_initrtypacl = PQfnumber(res, "initrtypacl"); i_rolname = PQfnumber(res, "rolname"); i_typinput = PQfnumber(res, "typinput"); i_typoutput = PQfnumber(res, "typoutput"); @@ -3754,6 +3954,9 @@ getTypes(Archive *fout, int *numTypes) tyinfo[i].dobj.catId.oid); tyinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); tyinfo[i].typacl = pg_strdup(PQgetvalue(res, i, i_typacl)); + tyinfo[i].rtypacl = pg_strdup(PQgetvalue(res, i, i_rtypacl)); + tyinfo[i].inittypacl = pg_strdup(PQgetvalue(res, i, i_inittypacl)); + tyinfo[i].initrtypacl = pg_strdup(PQgetvalue(res, i, i_initrtypacl)); tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem)); tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid)); tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind); @@ -3771,7 +3974,13 @@ getTypes(Archive *fout, int *numTypes) tyinfo[i].isArray = false; /* Decide whether we want to dump it */ - selectDumpableType(&tyinfo[i], dopt); + selectDumpableType(&tyinfo[i], fout); + + /* Do not try to dump ACL if no ACL exists. */ + if (PQgetisnull(res, i, i_typacl) && PQgetisnull(res, i, i_rtypacl) && + PQgetisnull(res, i, i_inittypacl) && + PQgetisnull(res, i, i_initrtypacl)) + tyinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; /* * If it's a domain, fetch info about its constraints, if any @@ -3879,7 +4088,6 @@ getTypes(Archive *fout, int *numTypes) OprInfo * getOperators(Archive *fout, int *numOprs) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -3965,7 +4173,7 @@ getOperators(Archive *fout, int *numOprs) oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode)); /* Decide whether we want to dump it */ - selectDumpableObject(&(oprinfo[i].dobj), dopt); + selectDumpableObject(&(oprinfo[i].dobj), fout); if (strlen(oprinfo[i].rolname) == 0) write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n", @@ -3989,7 +4197,6 @@ getOperators(Archive *fout, int *numOprs) CollInfo * getCollations(Archive *fout, int *numCollations) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -4051,7 +4258,7 @@ getCollations(Archive *fout, int *numCollations) collinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); /* Decide whether we want to dump it */ - selectDumpableObject(&(collinfo[i].dobj), dopt); + selectDumpableObject(&(collinfo[i].dobj), fout); } PQclear(res); @@ -4071,7 +4278,6 @@ getCollations(Archive *fout, int *numCollations) ConvInfo * getConversions(Archive *fout, int *numConversions) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -4133,7 +4339,7 @@ getConversions(Archive *fout, int *numConversions) convinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); /* Decide whether we want to dump it */ - selectDumpableObject(&(convinfo[i].dobj), dopt); + selectDumpableObject(&(convinfo[i].dobj), fout); } PQclear(res); @@ -4153,7 +4359,6 @@ getConversions(Archive *fout, int *numConversions) AccessMethodInfo * getAccessMethods(Archive *fout, int *numAccessMethods) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -4207,7 +4412,7 @@ getAccessMethods(Archive *fout, int *numAccessMethods) aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype)); /* Decide whether we want to dump it */ - selectDumpableAccessMethod(&(aminfo[i]), dopt); + selectDumpableAccessMethod(&(aminfo[i]), fout); } PQclear(res); @@ -4228,7 +4433,6 @@ getAccessMethods(Archive *fout, int *numAccessMethods) OpclassInfo * getOpclasses(Archive *fout, int *numOpclasses) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -4300,7 +4504,7 @@ getOpclasses(Archive *fout, int *numOpclasses) opcinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); /* Decide whether we want to dump it */ - selectDumpableObject(&(opcinfo[i].dobj), dopt); + selectDumpableObject(&(opcinfo[i].dobj), fout); if (fout->remoteVersion >= 70300) { @@ -4327,7 +4531,6 @@ getOpclasses(Archive *fout, int *numOpclasses) OpfamilyInfo * getOpfamilies(Archive *fout, int *numOpfamilies) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -4389,7 +4592,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies) opfinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); /* Decide whether we want to dump it */ - selectDumpableObject(&(opfinfo[i].dobj), dopt); + selectDumpableObject(&(opfinfo[i].dobj), fout); if (fout->remoteVersion >= 70300) { @@ -4430,22 +4633,79 @@ getAggregates(Archive *fout, int *numAggs) int i_proargtypes; int i_rolname; int i_aggacl; + int i_raggacl; + int i_initaggacl; + int i_initraggacl; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); /* - * Find all user-defined aggregates. See comment in getFuncs() for the + * Find all interesting aggregates. See comment in getFuncs() for the * rationale behind the filtering logic. */ + if (fout->remoteVersion >= 90600) + { + PQExpBuffer acl_subquery = createPQExpBuffer(); + PQExpBuffer racl_subquery = createPQExpBuffer(); + PQExpBuffer initacl_subquery = createPQExpBuffer(); + PQExpBuffer initracl_subquery = createPQExpBuffer(); - if (fout->remoteVersion >= 80200) + buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, + initracl_subquery, "p.proacl", "p.proowner", "'f'", + dopt->binary_upgrade); + + appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, " + "p.proname AS aggname, " + "p.pronamespace AS aggnamespace, " + "p.pronargs, p.proargtypes, " + "(%s p.proowner) AS rolname, " + "%s AS aggacl, " + "%s AS raggacl, " + "%s AS initaggacl, " + "%s AS initraggacl " + "FROM pg_proc p " + "LEFT JOIN pg_init_privs pip ON " + "(p.oid = pip.objoid AND pip.classoid = " + "(SELECT oid FROM pg_class WHERE relname = 'pg_proc') " + "AND pip.objsubid = 0) " + "WHERE p.proisagg AND (" + "p.pronamespace != " + "(SELECT oid FROM pg_namespace " + "WHERE nspname = 'pg_catalog') OR " + "EXISTS (SELECT * FROM pg_init_privs pip " + "WHERE p.oid = pip.objoid AND pip.classoid = " + "(SELECT oid FROM pg_class " + "WHERE relname = 'pg_proc') " + "AND p.proacl IS DISTINCT FROM pip.initprivs)", + username_subquery, + acl_subquery->data, + racl_subquery->data, + initacl_subquery->data, + initracl_subquery->data); + if (dopt->binary_upgrade) + appendPQExpBufferStr(query, + " OR EXISTS(SELECT 1 FROM pg_depend WHERE " + "classid = 'pg_proc'::regclass AND " + "objid = p.oid AND " + "refclassid = 'pg_extension'::regclass AND " + "deptype = 'e')"); + appendPQExpBufferChar(query, ')'); + + destroyPQExpBuffer(acl_subquery); + destroyPQExpBuffer(racl_subquery); + destroyPQExpBuffer(initacl_subquery); + destroyPQExpBuffer(initracl_subquery); + } + else if (fout->remoteVersion >= 80200) { appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, " "pronamespace AS aggnamespace, " "pronargs, proargtypes, " "(%s proowner) AS rolname, " - "proacl AS aggacl " + "proacl AS aggacl, " + "NULL AS raggacl, " + "NULL AS initaggacl, NULL AS initraggacl " "FROM pg_proc p " "WHERE proisagg AND (" "pronamespace != " @@ -4468,7 +4728,9 @@ getAggregates(Archive *fout, int *numAggs) "CASE WHEN proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype THEN 0 ELSE 1 END AS pronargs, " "proargtypes, " "(%s proowner) AS rolname, " - "proacl AS aggacl " + "proacl AS aggacl, " + "NULL AS raggacl, " + "NULL AS initaggacl, NULL AS initraggacl " "FROM pg_proc " "WHERE proisagg " "AND pronamespace != " @@ -4482,7 +4744,9 @@ getAggregates(Archive *fout, int *numAggs) "CASE WHEN aggbasetype = 0 THEN 0 ELSE 1 END AS pronargs, " "aggbasetype AS proargtypes, " "(%s aggowner) AS rolname, " - "NULL AS aggacl " + "NULL AS aggacl, " + "NULL AS raggacl, " + "NULL AS initaggacl, NULL AS initraggacl " "FROM pg_aggregate " "where oid > '%u'::oid", username_subquery, @@ -4497,7 +4761,9 @@ getAggregates(Archive *fout, int *numAggs) "CASE WHEN aggbasetype = 0 THEN 0 ELSE 1 END AS pronargs, " "aggbasetype AS proargtypes, " "(%s aggowner) AS rolname, " - "NULL AS aggacl " + "NULL AS aggacl, " + "NULL AS raggacl, " + "NULL AS initaggacl, NULL AS initraggacl " "FROM pg_aggregate " "where oid > '%u'::oid", username_subquery, @@ -4519,6 +4785,9 @@ getAggregates(Archive *fout, int *numAggs) i_proargtypes = PQfnumber(res, "proargtypes"); i_rolname = PQfnumber(res, "rolname"); i_aggacl = PQfnumber(res, "aggacl"); + i_raggacl = PQfnumber(res, "raggacl"); + i_initaggacl = PQfnumber(res, "initaggacl"); + i_initraggacl = PQfnumber(res, "initraggacl"); for (i = 0; i < ntups; i++) { @@ -4538,6 +4807,9 @@ getAggregates(Archive *fout, int *numAggs) agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */ agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */ agginfo[i].aggfn.proacl = pg_strdup(PQgetvalue(res, i, i_aggacl)); + agginfo[i].aggfn.rproacl = pg_strdup(PQgetvalue(res, i, i_raggacl)); + agginfo[i].aggfn.initproacl = pg_strdup(PQgetvalue(res, i, i_initaggacl)); + agginfo[i].aggfn.initrproacl = pg_strdup(PQgetvalue(res, i, i_initraggacl)); agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs)); if (agginfo[i].aggfn.nargs == 0) agginfo[i].aggfn.argtypes = NULL; @@ -4554,7 +4826,13 @@ getAggregates(Archive *fout, int *numAggs) } /* Decide whether we want to dump it */ - selectDumpableObject(&(agginfo[i].aggfn.dobj), dopt); + selectDumpableObject(&(agginfo[i].aggfn.dobj), fout); + + /* Do not try to dump ACL if no ACL exists. */ + if (PQgetisnull(res, i, i_aggacl) && PQgetisnull(res, i, i_raggacl) && + PQgetisnull(res, i, i_initaggacl) && + PQgetisnull(res, i, i_initraggacl)) + agginfo[i].aggfn.dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -4590,17 +4868,21 @@ getFuncs(Archive *fout, int *numFuncs) int i_proargtypes; int i_prorettype; int i_proacl; + int i_rproacl; + int i_initproacl; + int i_initrproacl; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); /* - * Find all user-defined functions. Normally we can exclude functions in - * pg_catalog, which is worth doing since there are several thousand of - * 'em. However, there are some extensions that create functions in - * pg_catalog. In normal dumps we can still ignore those --- but in - * binary-upgrade mode, we must dump the member objects of the extension, - * so be sure to fetch any such functions. + * Find all interesting functions. We include functions in pg_catalog, if + * they have an ACL different from what we set at initdb time (which is + * saved in pg_init_privs for us to perform this check). There may also + * be functions which are members of extensions which we must dump if we + * are in binary upgrade mode (we'll mark those functions as to-be-dumped + * when we check if the extension is to-be-dumped and we're in binary + * upgrade mode). * * Also, in 9.2 and up, exclude functions that are internally dependent on * something else, since presumably those will be created as a result of @@ -4609,12 +4891,69 @@ getFuncs(Archive *fout, int *numFuncs) * because the constructors don't have any dependencies the range type * doesn't have; otherwise we might not get creation ordering correct. */ + if (fout->remoteVersion >= 90600) + { + PQExpBuffer acl_subquery = createPQExpBuffer(); + PQExpBuffer racl_subquery = createPQExpBuffer(); + PQExpBuffer initacl_subquery = createPQExpBuffer(); + PQExpBuffer initracl_subquery = createPQExpBuffer(); - if (fout->remoteVersion >= 70300) + buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, + initracl_subquery, "p.proacl", "p.proowner", "'f'", + dopt->binary_upgrade); + + appendPQExpBuffer(query, + "SELECT p.tableoid, p.oid, p.proname, p.prolang, " + "p.pronargs, p.proargtypes, p.prorettype, " + "%s AS proacl, " + "%s AS rproacl, " + "%s AS initproacl, " + "%s AS initrproacl, " + "p.pronamespace, " + "(%s p.proowner) AS rolname " + "FROM pg_proc p " + "LEFT JOIN pg_init_privs pip ON " + "(p.oid = pip.objoid AND pip.classoid = " + "(SELECT oid FROM pg_class WHERE relname = 'pg_proc') " + "AND pip.objsubid = 0) " + "WHERE NOT proisagg " + "AND NOT EXISTS (SELECT 1 FROM pg_depend " + "WHERE classid = 'pg_proc'::regclass AND " + "objid = p.oid AND deptype = 'i') AND (" + "pronamespace != " + "(SELECT oid FROM pg_namespace " + "WHERE nspname = 'pg_catalog') OR " + "EXISTS (SELECT * FROM pg_init_privs pip " + "WHERE p.oid = pip.objoid AND pip.classoid = " + "(SELECT oid FROM pg_class " + "WHERE relname = 'pg_proc') " + "AND p.proacl IS DISTINCT FROM pip.initprivs)", + acl_subquery->data, + racl_subquery->data, + initacl_subquery->data, + initracl_subquery->data, + username_subquery); + if (dopt->binary_upgrade) + appendPQExpBufferStr(query, + "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE " + "classid = 'pg_proc'::regclass AND " + "objid = p.oid AND " + "refclassid = 'pg_extension'::regclass AND " + "deptype = 'e')"); + appendPQExpBufferChar(query, ')'); + + destroyPQExpBuffer(acl_subquery); + destroyPQExpBuffer(racl_subquery); + destroyPQExpBuffer(initacl_subquery); + destroyPQExpBuffer(initracl_subquery); + } + else if (fout->remoteVersion >= 70300) { appendPQExpBuffer(query, "SELECT tableoid, oid, proname, prolang, " "pronargs, proargtypes, prorettype, proacl, " + "NULL as rproacl, " + "NULL as initproacl, NULL AS initrproacl, " "pronamespace, " "(%s proowner) AS rolname " "FROM pg_proc p " @@ -4643,6 +4982,8 @@ getFuncs(Archive *fout, int *numFuncs) "SELECT tableoid, oid, proname, prolang, " "pronargs, proargtypes, prorettype, " "NULL AS proacl, " + "NULL AS rproacl, " + "NULL as initproacl, NULL AS initrproacl, " "0::oid AS pronamespace, " "(%s proowner) AS rolname " "FROM pg_proc " @@ -4659,6 +5000,8 @@ getFuncs(Archive *fout, int *numFuncs) "oid, proname, prolang, " "pronargs, proargtypes, prorettype, " "NULL AS proacl, " + "NULL AS rproacl, " + "NULL as initproacl, NULL AS initrproacl, " "0::oid AS pronamespace, " "(%s proowner) AS rolname " "FROM pg_proc " @@ -4685,6 +5028,9 @@ getFuncs(Archive *fout, int *numFuncs) i_proargtypes = PQfnumber(res, "proargtypes"); i_prorettype = PQfnumber(res, "prorettype"); i_proacl = PQfnumber(res, "proacl"); + i_rproacl = PQfnumber(res, "rproacl"); + i_initproacl = PQfnumber(res, "initproacl"); + i_initrproacl = PQfnumber(res, "initrproacl"); for (i = 0; i < ntups; i++) { @@ -4701,6 +5047,9 @@ getFuncs(Archive *fout, int *numFuncs) finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang)); finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype)); finfo[i].proacl = pg_strdup(PQgetvalue(res, i, i_proacl)); + finfo[i].rproacl = pg_strdup(PQgetvalue(res, i, i_rproacl)); + finfo[i].initproacl = pg_strdup(PQgetvalue(res, i, i_initproacl)); + finfo[i].initrproacl = pg_strdup(PQgetvalue(res, i, i_initrproacl)); finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs)); if (finfo[i].nargs == 0) finfo[i].argtypes = NULL; @@ -4712,7 +5061,13 @@ getFuncs(Archive *fout, int *numFuncs) } /* Decide whether we want to dump it */ - selectDumpableObject(&(finfo[i].dobj), dopt); + selectDumpableObject(&(finfo[i].dobj), fout); + + /* Do not try to dump ACL if no ACL exists. */ + if (PQgetisnull(res, i, i_proacl) && PQgetisnull(res, i, i_rproacl) && + PQgetisnull(res, i, i_initproacl) && + PQgetisnull(res, i, i_initrproacl)) + finfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; if (strlen(finfo[i].rolname) == 0) write_msg(NULL, @@ -4729,7 +5084,7 @@ getFuncs(Archive *fout, int *numFuncs) /* * getTables - * read all the user-defined tables (no indexes, no catalogs) + * read all the tables (no indexes) * in the system catalogs return them in the TableInfo* structure * * numTables is set to the number of tables read in @@ -4749,6 +5104,9 @@ getTables(Archive *fout, int *numTables) int i_relnamespace; int i_relkind; int i_relacl; + int i_rrelacl; + int i_initrelacl; + int i_initrrelacl; int i_rolname; int i_relchecks; int i_relhastriggers; @@ -4797,7 +5155,77 @@ getTables(Archive *fout, int *numTables) * we cannot correctly identify inherited columns, owned sequences, etc. */ - if (fout->remoteVersion >= 90500) + if (fout->remoteVersion >= 90600) + { + PQExpBuffer acl_subquery = createPQExpBuffer(); + PQExpBuffer racl_subquery = createPQExpBuffer(); + PQExpBuffer initacl_subquery = createPQExpBuffer(); + PQExpBuffer initracl_subquery = createPQExpBuffer(); + + /* + * Left join to pick up dependency info linking sequences to their + * owning column, if any (note this dependency is AUTO as of 8.2) + * + * Left join to detect if any privileges are still as-set-at-init, in + * which case we won't dump out ACL commands for those. + */ + + buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, + initracl_subquery, "c.relacl", "c.relowner", + "CASE WHEN c.relkind = 'S' THEN 's' ELSE 'r' END::\"char\"", + dopt->binary_upgrade); + + appendPQExpBuffer(query, + "SELECT c.tableoid, c.oid, c.relname, " + "%s AS relacl, %s as rrelacl, " + "%s AS initrelacl, %s as initrrelacl, " + "c.relkind, c.relnamespace, " + "(%s c.relowner) AS rolname, " + "c.relchecks, c.relhastriggers, " + "c.relhasindex, c.relhasrules, c.relhasoids, " + "c.relrowsecurity, c.relforcerowsecurity, " + "c.relfrozenxid, c.relminmxid, tc.oid AS toid, " + "tc.relfrozenxid AS tfrozenxid, " + "tc.relminmxid AS tminmxid, " + "c.relpersistence, c.relispopulated, " + "c.relreplident, c.relpages, " + "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " + "d.refobjid AS owning_tab, " + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, " + "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text " + "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, " + "tc.reloptions AS toast_reloptions " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " + "d.classid = c.tableoid AND d.objid = c.oid AND " + "d.objsubid = 0 AND " + "d.refclassid = c.tableoid AND d.deptype = 'a') " + "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) " + "LEFT JOIN pg_init_privs pip ON " + "(c.oid = pip.objoid AND pip.classoid = " + "(SELECT oid FROM pg_class " + "WHERE relname = 'pg_class') AND pip.objsubid = 0) " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "ORDER BY c.oid", + acl_subquery->data, + racl_subquery->data, + initacl_subquery->data, + initracl_subquery->data, + username_subquery, + RELKIND_SEQUENCE, + RELKIND_RELATION, RELKIND_SEQUENCE, + RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + + destroyPQExpBuffer(acl_subquery); + destroyPQExpBuffer(racl_subquery); + destroyPQExpBuffer(initacl_subquery); + destroyPQExpBuffer(initracl_subquery); + } + else if (fout->remoteVersion >= 90500) { /* * Left join to pick up dependency info linking sequences to their @@ -4805,7 +5233,10 @@ getTables(Archive *fout, int *numTables) */ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.relname, " - "c.relacl, c.relkind, c.relnamespace, " + "c.relacl, NULL as rrelacl, " + "NULL AS initrelacl, NULL AS initrrelacl, " + "c.relkind, " + "c.relnamespace, " "(%s c.relowner) AS rolname, " "c.relchecks, c.relhastriggers, " "c.relhasindex, c.relhasrules, c.relhasoids, " @@ -4846,7 +5277,10 @@ getTables(Archive *fout, int *numTables) */ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.relname, " - "c.relacl, c.relkind, c.relnamespace, " + "c.relacl, NULL as rrelacl, " + "NULL AS initrelacl, NULL AS initrrelacl, " + "c.relkind, " + "c.relnamespace, " "(%s c.relowner) AS rolname, " "c.relchecks, c.relhastriggers, " "c.relhasindex, c.relhasrules, c.relhasoids, " @@ -4888,7 +5322,10 @@ getTables(Archive *fout, int *numTables) */ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.relname, " - "c.relacl, c.relkind, c.relnamespace, " + "c.relacl, NULL as rrelacl, " + "NULL AS initrelacl, NULL AS initrrelacl, " + "c.relkind, " + "c.relnamespace, " "(%s c.relowner) AS rolname, " "c.relchecks, c.relhastriggers, " "c.relhasindex, c.relhasrules, c.relhasoids, " @@ -4930,7 +5367,10 @@ getTables(Archive *fout, int *numTables) */ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.relname, " - "c.relacl, c.relkind, c.relnamespace, " + "c.relacl, NULL as rrelacl, " + "NULL AS initrelacl, NULL AS initrrelacl, " + "c.relkind, " + "c.relnamespace, " "(%s c.relowner) AS rolname, " "c.relchecks, c.relhastriggers, " "c.relhasindex, c.relhasrules, c.relhasoids, " @@ -4970,7 +5410,10 @@ getTables(Archive *fout, int *numTables) */ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.relname, " - "c.relacl, c.relkind, c.relnamespace, " + "c.relacl, NULL as rrelacl, " + "NULL AS initrelacl, NULL AS initrrelacl, " + "c.relkind, " + "c.relnamespace, " "(%s c.relowner) AS rolname, " "c.relchecks, c.relhastriggers, " "c.relhasindex, c.relhasrules, c.relhasoids, " @@ -5009,7 +5452,10 @@ getTables(Archive *fout, int *numTables) */ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.relname, " - "c.relacl, c.relkind, c.relnamespace, " + "c.relacl, NULL as rrelacl, " + "NULL AS initrelacl, NULL AS initrrelacl, " + "c.relkind, " + "c.relnamespace, " "(%s c.relowner) AS rolname, " "c.relchecks, c.relhastriggers, " "c.relhasindex, c.relhasrules, c.relhasoids, " @@ -5048,7 +5494,10 @@ getTables(Archive *fout, int *numTables) */ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.relname, " - "c.relacl, c.relkind, c.relnamespace, " + "c.relacl, NULL as rrelacl, " + "NULL AS initrelacl, NULL AS initrrelacl, " + "c.relkind, " + "c.relnamespace, " "(%s c.relowner) AS rolname, " "c.relchecks, (c.reltriggers <> 0) AS relhastriggers, " "c.relhasindex, c.relhasrules, c.relhasoids, " @@ -5087,7 +5536,9 @@ getTables(Archive *fout, int *numTables) */ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, relname, " - "relacl, relkind, relnamespace, " + "relacl, NULL as rrelacl, " + "NULL AS initrelacl, NULL AS initrrelacl, " + "relkind, relnamespace, " "(%s relowner) AS rolname, " "relchecks, (reltriggers <> 0) AS relhastriggers, " "relhasindex, relhasrules, relhasoids, " @@ -5125,7 +5576,9 @@ getTables(Archive *fout, int *numTables) */ appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, relname, " - "relacl, relkind, relnamespace, " + "relacl, NULL as rrelacl, " + "NULL AS initrelacl, NULL AS initrrelacl, " + "relkind, relnamespace, " "(%s relowner) AS rolname, " "relchecks, (reltriggers <> 0) AS relhastriggers, " "relhasindex, relhasrules, relhasoids, " @@ -5158,7 +5611,10 @@ getTables(Archive *fout, int *numTables) else if (fout->remoteVersion >= 70200) { appendPQExpBuffer(query, - "SELECT tableoid, oid, relname, relacl, relkind, " + "SELECT tableoid, oid, relname, relacl, " + "NULL as rrelacl, " + "NULL AS initrelacl, NULL AS initrrelacl, " + "relkind, " "0::oid AS relnamespace, " "(%s relowner) AS rolname, " "relchecks, (reltriggers <> 0) AS relhastriggers, " @@ -5186,7 +5642,10 @@ getTables(Archive *fout, int *numTables) { /* all tables have oids in 7.1 */ appendPQExpBuffer(query, - "SELECT tableoid, oid, relname, relacl, relkind, " + "SELECT tableoid, oid, relname, relacl, " + "NULL as rrelacl, " + "NULL AS initrelacl, NULL AS initrrelacl, " + "relkind, " "0::oid AS relnamespace, " "(%s relowner) AS rolname, " "relchecks, (reltriggers <> 0) AS relhastriggers, " @@ -5220,7 +5679,8 @@ getTables(Archive *fout, int *numTables) appendPQExpBuffer(query, "SELECT " "(SELECT oid FROM pg_class WHERE relname = 'pg_class') AS tableoid, " - "oid, relname, relacl, " + "oid, relname, relacl, NULL as rrelacl, " + "NULL AS initrelacl, NULL AS initrrelacl, " "CASE WHEN relhasrules and relkind = 'r' " " and EXISTS(SELECT rulename FROM pg_rewrite r WHERE " " r.ev_class = c.oid AND r.ev_type = '1') " @@ -5274,6 +5734,9 @@ getTables(Archive *fout, int *numTables) i_relname = PQfnumber(res, "relname"); i_relnamespace = PQfnumber(res, "relnamespace"); i_relacl = PQfnumber(res, "relacl"); + i_rrelacl = PQfnumber(res, "rrelacl"); + i_initrelacl = PQfnumber(res, "initrelacl"); + i_initrrelacl = PQfnumber(res, "initrrelacl"); i_relkind = PQfnumber(res, "relkind"); i_rolname = PQfnumber(res, "rolname"); i_relchecks = PQfnumber(res, "relchecks"); @@ -5328,6 +5791,9 @@ getTables(Archive *fout, int *numTables) tblinfo[i].dobj.catId.oid); tblinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); tblinfo[i].relacl = pg_strdup(PQgetvalue(res, i, i_relacl)); + tblinfo[i].rrelacl = pg_strdup(PQgetvalue(res, i, i_rrelacl)); + tblinfo[i].initrelacl = pg_strdup(PQgetvalue(res, i, i_initrelacl)); + tblinfo[i].initrrelacl = pg_strdup(PQgetvalue(res, i, i_initrrelacl)); tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind)); tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence)); tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0); @@ -5375,7 +5841,7 @@ getTables(Archive *fout, int *numTables) if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE) tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE; else - selectDumpableTable(&tblinfo[i], dopt); + selectDumpableTable(&tblinfo[i], fout); tblinfo[i].interesting = tblinfo[i].dobj.dump ? true : false; @@ -6461,7 +6927,6 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables) EventTriggerInfo * getEventTriggers(Archive *fout, int *numEventTriggers) { - DumpOptions *dopt = fout->dopt; int i; PQExpBuffer query; PGresult *res; @@ -6531,7 +6996,7 @@ getEventTriggers(Archive *fout, int *numEventTriggers) evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled)); /* Decide whether we want to dump it */ - selectDumpableObject(&(evtinfo[i].dobj), dopt); + selectDumpableObject(&(evtinfo[i].dobj), fout); } PQclear(res); @@ -6567,17 +7032,59 @@ getProcLangs(Archive *fout, int *numProcLangs) int i_laninline; int i_lanvalidator; int i_lanacl; + int i_rlanacl; + int i_initlanacl; + int i_initrlanacl; int i_lanowner; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); - if (fout->remoteVersion >= 90000) + if (fout->remoteVersion >= 90600) + { + PQExpBuffer acl_subquery = createPQExpBuffer(); + PQExpBuffer racl_subquery = createPQExpBuffer(); + PQExpBuffer initacl_subquery = createPQExpBuffer(); + PQExpBuffer initracl_subquery = createPQExpBuffer(); + + buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, + initracl_subquery, "l.lanacl", "l.lanowner", "'l'", + dopt->binary_upgrade); + + /* pg_language has a laninline column */ + appendPQExpBuffer(query, "SELECT l.tableoid, l.oid, " + "l.lanname, l.lanpltrusted, l.lanplcallfoid, " + "l.laninline, l.lanvalidator, " + "%s AS lanacl, " + "%s AS rlanacl, " + "%s AS initlanacl, " + "%s AS initrlanacl, " + "(%s l.lanowner) AS lanowner " + "FROM pg_language l " + "LEFT JOIN pg_init_privs pip " + "ON (l.oid = pip.objoid AND pip.classoid = " + "(SELECT oid FROM pg_class WHERE relname = 'pg_type') " + "AND pip.objsubid = 0) " + "WHERE l.lanispl " + "ORDER BY l.oid", + acl_subquery->data, + racl_subquery->data, + initacl_subquery->data, + initracl_subquery->data, + username_subquery); + + destroyPQExpBuffer(acl_subquery); + destroyPQExpBuffer(racl_subquery); + destroyPQExpBuffer(initacl_subquery); + destroyPQExpBuffer(initracl_subquery); + } + else if (fout->remoteVersion >= 90000) { /* pg_language has a laninline column */ appendPQExpBuffer(query, "SELECT tableoid, oid, " "lanname, lanpltrusted, lanplcallfoid, " - "laninline, lanvalidator, lanacl, " + "laninline, lanvalidator, lanacl, NULL AS rlanacl, " + "NULL AS initlanacl, NULL AS initrlanacl, " "(%s lanowner) AS lanowner " "FROM pg_language " "WHERE lanispl " @@ -6590,6 +7097,8 @@ getProcLangs(Archive *fout, int *numProcLangs) appendPQExpBuffer(query, "SELECT tableoid, oid, " "lanname, lanpltrusted, lanplcallfoid, " "0 AS laninline, lanvalidator, lanacl, " + "NULL AS rlanacl, " + "NULL AS initlanacl, NULL AS initrlanacl, " "(%s lanowner) AS lanowner " "FROM pg_language " "WHERE lanispl " @@ -6602,6 +7111,8 @@ getProcLangs(Archive *fout, int *numProcLangs) appendPQExpBuffer(query, "SELECT tableoid, oid, " "lanname, lanpltrusted, lanplcallfoid, " "0 AS laninline, lanvalidator, lanacl, " + "NULL AS rlanacl, " + "NULL AS initlanacl, NULL AS initrlanacl, " "(%s '10') AS lanowner " "FROM pg_language " "WHERE lanispl " @@ -6614,6 +7125,8 @@ getProcLangs(Archive *fout, int *numProcLangs) appendPQExpBuffer(query, "SELECT tableoid, oid, " "lanname, lanpltrusted, lanplcallfoid, " "0 AS laninline, lanvalidator, lanacl, " + "NULL AS rlanacl, " + "NULL AS initlanacl, NULL AS initrlanacl, " "(%s '1') AS lanowner " "FROM pg_language " "WHERE lanispl " @@ -6626,6 +7139,8 @@ getProcLangs(Archive *fout, int *numProcLangs) appendPQExpBuffer(query, "SELECT tableoid, oid, " "lanname, lanpltrusted, lanplcallfoid, " "0 AS laninline, lanvalidator, lanacl, " + "NULL AS rlanacl, " + "NULL AS initlanacl, NULL AS initrlanacl, " "NULL AS lanowner " "FROM pg_language " "WHERE lanispl " @@ -6636,6 +7151,8 @@ getProcLangs(Archive *fout, int *numProcLangs) appendPQExpBuffer(query, "SELECT tableoid, oid, " "lanname, lanpltrusted, lanplcallfoid, " "0 AS laninline, 0 AS lanvalidator, NULL AS lanacl, " + "NULL AS rlanacl, " + "NULL AS initlanacl, NULL AS initrlanacl, " "NULL AS lanowner " "FROM pg_language " "WHERE lanispl " @@ -6648,6 +7165,8 @@ getProcLangs(Archive *fout, int *numProcLangs) "oid, " "lanname, lanpltrusted, lanplcallfoid, " "0 AS laninline, 0 AS lanvalidator, NULL AS lanacl, " + "NULL AS rlanacl, " + "NULL AS initlanacl, NULL AS initrlanacl, " "NULL AS lanowner " "FROM pg_language " "WHERE lanispl " @@ -6670,6 +7189,9 @@ getProcLangs(Archive *fout, int *numProcLangs) i_laninline = PQfnumber(res, "laninline"); i_lanvalidator = PQfnumber(res, "lanvalidator"); i_lanacl = PQfnumber(res, "lanacl"); + i_rlanacl = PQfnumber(res, "rlanacl"); + i_initlanacl = PQfnumber(res, "initlanacl"); + i_initrlanacl = PQfnumber(res, "initrlanacl"); i_lanowner = PQfnumber(res, "lanowner"); for (i = 0; i < ntups; i++) @@ -6685,10 +7207,19 @@ getProcLangs(Archive *fout, int *numProcLangs) planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline)); planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator)); planginfo[i].lanacl = pg_strdup(PQgetvalue(res, i, i_lanacl)); + planginfo[i].rlanacl = pg_strdup(PQgetvalue(res, i, i_rlanacl)); + planginfo[i].initlanacl = pg_strdup(PQgetvalue(res, i, i_initlanacl)); + planginfo[i].initrlanacl = pg_strdup(PQgetvalue(res, i, i_initrlanacl)); planginfo[i].lanowner = pg_strdup(PQgetvalue(res, i, i_lanowner)); /* Decide whether we want to dump it */ - selectDumpableProcLang(&(planginfo[i]), dopt); + selectDumpableProcLang(&(planginfo[i]), fout); + + /* Do not try to dump ACL if no ACL exists. */ + if (PQgetisnull(res, i, i_lanacl) && PQgetisnull(res, i, i_rlanacl) && + PQgetisnull(res, i, i_initlanacl) && + PQgetisnull(res, i, i_initrlanacl)) + planginfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; if (fout->remoteVersion < 70300) { @@ -6721,7 +7252,6 @@ getProcLangs(Archive *fout, int *numProcLangs) CastInfo * getCasts(Archive *fout, int *numCasts) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -6827,7 +7357,7 @@ getCasts(Archive *fout, int *numCasts) } /* Decide whether we want to dump it */ - selectDumpableCast(&(castinfo[i]), dopt); + selectDumpableCast(&(castinfo[i]), fout); } PQclear(res); @@ -6863,7 +7393,6 @@ get_language_name(Archive *fout, Oid langid) TransformInfo * getTransforms(Archive *fout, int *numTransforms) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -6938,7 +7467,7 @@ getTransforms(Archive *fout, int *numTransforms) free(lanname); /* Decide whether we want to dump it */ - selectDumpableObject(&(transforminfo[i].dobj), dopt); + selectDumpableObject(&(transforminfo[i].dobj), fout); } PQclear(res); @@ -7549,7 +8078,6 @@ shouldPrintColumn(DumpOptions *dopt, TableInfo *tbinfo, int colno) TSParserInfo * getTSParsers(Archive *fout, int *numTSParsers) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -7622,7 +8150,7 @@ getTSParsers(Archive *fout, int *numTSParsers) prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype)); /* Decide whether we want to dump it */ - selectDumpableObject(&(prsinfo[i].dobj), dopt); + selectDumpableObject(&(prsinfo[i].dobj), fout); } PQclear(res); @@ -7642,7 +8170,6 @@ getTSParsers(Archive *fout, int *numTSParsers) TSDictInfo * getTSDictionaries(Archive *fout, int *numTSDicts) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -7708,7 +8235,7 @@ getTSDictionaries(Archive *fout, int *numTSDicts) dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption)); /* Decide whether we want to dump it */ - selectDumpableObject(&(dictinfo[i].dobj), dopt); + selectDumpableObject(&(dictinfo[i].dobj), fout); } PQclear(res); @@ -7728,7 +8255,6 @@ getTSDictionaries(Archive *fout, int *numTSDicts) TSTemplateInfo * getTSTemplates(Archive *fout, int *numTSTemplates) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -7786,7 +8312,7 @@ getTSTemplates(Archive *fout, int *numTSTemplates) tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize)); /* Decide whether we want to dump it */ - selectDumpableObject(&(tmplinfo[i].dobj), dopt); + selectDumpableObject(&(tmplinfo[i].dobj), fout); } PQclear(res); @@ -7806,7 +8332,6 @@ getTSTemplates(Archive *fout, int *numTSTemplates) TSConfigInfo * getTSConfigurations(Archive *fout, int *numTSConfigs) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -7865,7 +8390,7 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser)); /* Decide whether we want to dump it */ - selectDumpableObject(&(cfginfo[i].dobj), dopt); + selectDumpableObject(&(cfginfo[i].dobj), fout); } PQclear(res); @@ -7898,6 +8423,9 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) int i_fdwhandler; int i_fdwvalidator; int i_fdwacl; + int i_rfdwacl; + int i_initfdwacl; + int i_initrfdwacl; int i_fdwoptions; /* Before 8.4, there are no foreign-data wrappers */ @@ -7912,12 +8440,55 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); - if (fout->remoteVersion >= 90100) + if (fout->remoteVersion >= 90600) + { + PQExpBuffer acl_subquery = createPQExpBuffer(); + PQExpBuffer racl_subquery = createPQExpBuffer(); + PQExpBuffer initacl_subquery = createPQExpBuffer(); + PQExpBuffer initracl_subquery = createPQExpBuffer(); + + buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, + initracl_subquery, "f.fdwacl", "f.fdwowner", "'F'", + dopt->binary_upgrade); + + appendPQExpBuffer(query, "SELECT f.tableoid, f.oid, f.fdwname, " + "(%s f.fdwowner) AS rolname, " + "f.fdwhandler::pg_catalog.regproc, " + "f.fdwvalidator::pg_catalog.regproc, " + "%s AS fdwacl, " + "%s AS rfdwacl, " + "%s AS initfdwacl, " + "%s AS initrfdwacl, " + "array_to_string(ARRAY(" + "SELECT quote_ident(option_name) || ' ' || " + "quote_literal(option_value) " + "FROM pg_options_to_table(f.fdwoptions) " + "ORDER BY option_name" + "), E',\n ') AS fdwoptions " + "FROM pg_foreign_data_wrapper f " + "LEFT JOIN pg_init_privs pip " + "ON (f.oid = pip.objoid AND pip.classoid = " + "(SELECT oid FROM pg_class WHERE relname = 'pg_foreign_data_wrapper') " + "AND pip.objsubid = 0) ", + username_subquery, + acl_subquery->data, + racl_subquery->data, + initacl_subquery->data, + initracl_subquery->data); + + destroyPQExpBuffer(acl_subquery); + destroyPQExpBuffer(racl_subquery); + destroyPQExpBuffer(initacl_subquery); + destroyPQExpBuffer(initracl_subquery); + } + else if (fout->remoteVersion >= 90100) { appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, " "(%s fdwowner) AS rolname, " "fdwhandler::pg_catalog.regproc, " "fdwvalidator::pg_catalog.regproc, fdwacl, " + "NULL as rfdwacl, " + "NULL as initfdwacl, NULL AS initrfdwacl, " "array_to_string(ARRAY(" "SELECT quote_ident(option_name) || ' ' || " "quote_literal(option_value) " @@ -7933,6 +8504,8 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) "(%s fdwowner) AS rolname, " "'-' AS fdwhandler, " "fdwvalidator::pg_catalog.regproc, fdwacl, " + "NULL as rfdwacl, " + "NULL as initfdwacl, NULL AS initrfdwacl, " "array_to_string(ARRAY(" "SELECT quote_ident(option_name) || ' ' || " "quote_literal(option_value) " @@ -7957,6 +8530,9 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) i_fdwhandler = PQfnumber(res, "fdwhandler"); i_fdwvalidator = PQfnumber(res, "fdwvalidator"); i_fdwacl = PQfnumber(res, "fdwacl"); + i_rfdwacl = PQfnumber(res, "rfdwacl"); + i_initfdwacl = PQfnumber(res, "initfdwacl"); + i_initrfdwacl = PQfnumber(res, "initrfdwacl"); i_fdwoptions = PQfnumber(res, "fdwoptions"); for (i = 0; i < ntups; i++) @@ -7972,9 +8548,18 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator)); fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions)); fdwinfo[i].fdwacl = pg_strdup(PQgetvalue(res, i, i_fdwacl)); + fdwinfo[i].rfdwacl = pg_strdup(PQgetvalue(res, i, i_rfdwacl)); + fdwinfo[i].initfdwacl = pg_strdup(PQgetvalue(res, i, i_initfdwacl)); + fdwinfo[i].initrfdwacl = pg_strdup(PQgetvalue(res, i, i_initrfdwacl)); /* Decide whether we want to dump it */ - selectDumpableObject(&(fdwinfo[i].dobj), dopt); + selectDumpableObject(&(fdwinfo[i].dobj), fout); + + /* Do not try to dump ACL if no ACL exists. */ + if (PQgetisnull(res, i, i_fdwacl) && PQgetisnull(res, i, i_rfdwacl) && + PQgetisnull(res, i, i_initfdwacl) && + PQgetisnull(res, i, i_initrfdwacl)) + fdwinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -8008,6 +8593,9 @@ getForeignServers(Archive *fout, int *numForeignServers) int i_srvtype; int i_srvversion; int i_srvacl; + int i_rsrvacl; + int i_initsrvacl; + int i_initrsrvacl; int i_srvoptions; /* Before 8.4, there are no foreign servers */ @@ -8022,17 +8610,62 @@ getForeignServers(Archive *fout, int *numForeignServers) /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); - appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, " - "(%s srvowner) AS rolname, " - "srvfdw, srvtype, srvversion, srvacl," - "array_to_string(ARRAY(" - "SELECT quote_ident(option_name) || ' ' || " - "quote_literal(option_value) " - "FROM pg_options_to_table(srvoptions) " - "ORDER BY option_name" - "), E',\n ') AS srvoptions " - "FROM pg_foreign_server", - username_subquery); + if (fout->remoteVersion >= 90600) + { + PQExpBuffer acl_subquery = createPQExpBuffer(); + PQExpBuffer racl_subquery = createPQExpBuffer(); + PQExpBuffer initacl_subquery = createPQExpBuffer(); + PQExpBuffer initracl_subquery = createPQExpBuffer(); + + buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, + initracl_subquery, "f.srvacl", "f.srvowner", "'S'", + dopt->binary_upgrade); + + appendPQExpBuffer(query, "SELECT f.tableoid, f.oid, f.srvname, " + "(%s f.srvowner) AS rolname, " + "f.srvfdw, f.srvtype, f.srvversion, " + "%s AS srvacl, " + "%s AS rsrvacl, " + "%s AS initsrvacl, " + "%s AS initrsrvacl, " + "array_to_string(ARRAY(" + "SELECT quote_ident(option_name) || ' ' || " + "quote_literal(option_value) " + "FROM pg_options_to_table(f.srvoptions) " + "ORDER BY option_name" + "), E',\n ') AS srvoptions " + "FROM pg_foreign_server f " + "LEFT JOIN pg_init_privs pip " + "ON (f.oid = pip.objoid AND pip.classoid = " + "(SELECT oid FROM pg_class WHERE relname = 'pg_foreign_server') " + "AND pip.objsubid = 0) ", + username_subquery, + acl_subquery->data, + racl_subquery->data, + initacl_subquery->data, + initracl_subquery->data); + + destroyPQExpBuffer(acl_subquery); + destroyPQExpBuffer(racl_subquery); + destroyPQExpBuffer(initacl_subquery); + destroyPQExpBuffer(initracl_subquery); + } + else + { + appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, " + "(%s srvowner) AS rolname, " + "srvfdw, srvtype, srvversion, srvacl, " + "NULL AS rsrvacl, " + "NULL AS initsrvacl, NULL AS initrsrvacl, " + "array_to_string(ARRAY(" + "SELECT quote_ident(option_name) || ' ' || " + "quote_literal(option_value) " + "FROM pg_options_to_table(srvoptions) " + "ORDER BY option_name" + "), E',\n ') AS srvoptions " + "FROM pg_foreign_server", + username_subquery); + } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -8049,6 +8682,9 @@ getForeignServers(Archive *fout, int *numForeignServers) i_srvtype = PQfnumber(res, "srvtype"); i_srvversion = PQfnumber(res, "srvversion"); i_srvacl = PQfnumber(res, "srvacl"); + i_rsrvacl = PQfnumber(res, "rsrvacl"); + i_initsrvacl = PQfnumber(res, "initsrvacl"); + i_initrsrvacl = PQfnumber(res, "initrsrvacl"); i_srvoptions = PQfnumber(res, "srvoptions"); for (i = 0; i < ntups; i++) @@ -8065,9 +8701,18 @@ getForeignServers(Archive *fout, int *numForeignServers) srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion)); srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions)); srvinfo[i].srvacl = pg_strdup(PQgetvalue(res, i, i_srvacl)); + srvinfo[i].rsrvacl = pg_strdup(PQgetvalue(res, i, i_rsrvacl)); + srvinfo[i].initsrvacl = pg_strdup(PQgetvalue(res, i, i_initsrvacl)); + srvinfo[i].initrsrvacl = pg_strdup(PQgetvalue(res, i, i_initrsrvacl)); /* Decide whether we want to dump it */ - selectDumpableObject(&(srvinfo[i].dobj), dopt); + selectDumpableObject(&(srvinfo[i].dobj), fout); + + /* Do not try to dump ACL if no ACL exists. */ + if (PQgetisnull(res, i, i_srvacl) && PQgetisnull(res, i, i_rsrvacl) && + PQgetisnull(res, i, i_initsrvacl) && + PQgetisnull(res, i, i_initrsrvacl)) + srvinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -8692,7 +9337,8 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL) dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA", qnspname, NULL, nspinfo->dobj.name, NULL, - nspinfo->rolname, nspinfo->nspacl); + nspinfo->rolname, nspinfo->nspacl, nspinfo->rnspacl, + nspinfo->initnspacl, nspinfo->initrnspacl); free(qnspname); @@ -8987,7 +9633,8 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE", qtypname, NULL, tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, - tyinfo->rolname, tyinfo->typacl); + tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, + tyinfo->inittypacl, tyinfo->initrtypacl); PQclear(res); destroyPQExpBuffer(q); @@ -9125,7 +9772,8 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE", qtypname, NULL, tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, - tyinfo->rolname, tyinfo->typacl); + tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, + tyinfo->inittypacl, tyinfo->initrtypacl); PQclear(res); destroyPQExpBuffer(q); @@ -9200,7 +9848,8 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo) dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE", qtypname, NULL, tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, - tyinfo->rolname, tyinfo->typacl); + tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, + tyinfo->inittypacl, tyinfo->initrtypacl); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); @@ -9594,7 +10243,8 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE", qtypname, NULL, tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, - tyinfo->rolname, tyinfo->typacl); + tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, + tyinfo->inittypacl, tyinfo->initrtypacl); PQclear(res); destroyPQExpBuffer(q); @@ -9762,7 +10412,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE", qtypname, NULL, tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, - tyinfo->rolname, tyinfo->typacl); + tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, + tyinfo->inittypacl, tyinfo->initrtypacl); /* Dump any per-constraint comments */ for (i = 0; i < tyinfo->nDomChecks; i++) @@ -9997,7 +10648,8 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE", qtypname, NULL, tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, - tyinfo->rolname, tyinfo->typacl); + tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, + tyinfo->inittypacl, tyinfo->initrtypacl); PQclear(res); destroyPQExpBuffer(q); @@ -10315,7 +10967,8 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE", qlanname, NULL, plang->dobj.name, lanschema, - plang->lanowner, plang->lanacl); + plang->lanowner, plang->lanacl, plang->rlanacl, + plang->initlanacl, plang->initrlanacl); free(qlanname); @@ -10986,7 +11639,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION", funcsig, NULL, funcsig_tag, finfo->dobj.namespace->dobj.name, - finfo->rolname, finfo->proacl); + finfo->rolname, finfo->proacl, finfo->rproacl, + finfo->initproacl, finfo->initrproacl); PQclear(res); @@ -13069,7 +13723,9 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "FUNCTION", aggsig, NULL, aggsig_tag, agginfo->aggfn.dobj.namespace->dobj.name, - agginfo->aggfn.rolname, agginfo->aggfn.proacl); + agginfo->aggfn.rolname, agginfo->aggfn.proacl, + agginfo->aggfn.rproacl, + agginfo->aggfn.initproacl, agginfo->aggfn.initrproacl); free(aggsig); if (aggfullsig) @@ -13513,7 +14169,8 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) "FOREIGN DATA WRAPPER", qfdwname, NULL, fdwinfo->dobj.name, NULL, fdwinfo->rolname, - fdwinfo->fdwacl); + fdwinfo->fdwacl, fdwinfo->rfdwacl, + fdwinfo->initfdwacl, fdwinfo->initrfdwacl); /* Dump Foreign Data Wrapper Comments */ if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT) @@ -13609,7 +14266,8 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) "FOREIGN SERVER", qsrvname, NULL, srvinfo->dobj.name, NULL, srvinfo->rolname, - srvinfo->srvacl); + srvinfo->srvacl, srvinfo->rsrvacl, + srvinfo->initsrvacl, srvinfo->initrsrvacl); /* Dump user mappings */ if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP) @@ -13788,7 +14446,7 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL) ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId, tag->data, - daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL, + daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL, NULL, daclinfo->defaclrole, false, "DEFAULT ACL", SECTION_POST_DATA, @@ -13815,13 +14473,16 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) * 'owner' is the owner, NULL if there is no owner (for languages). * 'acls' is the string read out of the fooacl system catalog field; * it will be parsed here. + * 'racls' contains any initial ACLs that the object had which have now been + * revoked by the user, it will also be parsed here. *---------- */ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, const char *type, const char *name, const char *subname, const char *tag, const char *nspname, const char *owner, - const char *acls) + const char *acls, const char *racls, + const char *initacls, const char *initracls) { DumpOptions *dopt = fout->dopt; PQExpBuffer sql; @@ -13836,11 +14497,30 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, sql = createPQExpBuffer(); - if (!buildACLCommands(name, subname, type, acls, owner, + /* + * Check to see if this object has had any initial ACLs included for it. + * If so, we are in binary upgrade mode and these are the ACLs to turn + * into GRANT and REVOKE statements to set and record the initial + * privileges for an extension object. Let the backend know that these + * are to be recorded by calling binary_upgrade_set_record_init_privs() + * before and after. + */ + if (strlen(initacls) != 0 || strlen(initracls) != 0) + { + appendPQExpBuffer(sql, "SELECT binary_upgrade_set_record_init_privs(true);\n"); + if (!buildACLCommands(name, subname, type, initacls, initracls, owner, + "", fout->remoteVersion, sql)) + exit_horribly(NULL, + "could not parse initial GRANT ACL list (%s) or initial REVOKE ACL list (%s) for object \"%s\" (%s)\n", + initacls, initracls, name, type); + appendPQExpBuffer(sql, "SELECT binary_upgrade_set_record_init_privs(false);\n"); + } + + if (!buildACLCommands(name, subname, type, acls, racls, owner, "", fout->remoteVersion, sql)) exit_horribly(NULL, - "could not parse ACL list (%s) for object \"%s\" (%s)\n", - acls, name, type); + "could not parse GRANT ACL list (%s) or REVOKE ACL list (%s) for object \"%s\" (%s)\n", + acls, racls, name, type); if (sql->len > 0) ArchiveEntry(fout, nilCatalogId, createDumpId(), @@ -14163,7 +14843,8 @@ collectSecLabels(Archive *fout, SecLabelItem **items) static void dumpTable(Archive *fout, TableInfo *tbinfo) { - char *namecopy; + DumpOptions *dopt = fout->dopt; + char *namecopy; if (tbinfo->relkind == RELKIND_SEQUENCE) dumpSequence(fout, tbinfo); @@ -14178,12 +14859,13 @@ dumpTable(Archive *fout, TableInfo *tbinfo) "TABLE", namecopy, NULL, tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, - tbinfo->relacl); + tbinfo->relacl, tbinfo->rrelacl, + tbinfo->initrelacl, tbinfo->initrrelacl); /* - * Handle column ACLs, if any. Note: we pull these with a separate - * query rather than trying to fetch them during getTableAttrs, so - * that we won't miss ACLs on system columns. + * Handle column ACLs, if any. Note: we pull these with a separate query + * rather than trying to fetch them during getTableAttrs, so that we won't + * miss ACLs on system columns. */ if (fout->remoteVersion >= 80400 && tbinfo->dobj.dump & DUMP_COMPONENT_ACL) { @@ -14191,18 +14873,65 @@ dumpTable(Archive *fout, TableInfo *tbinfo) PGresult *res; int i; - appendPQExpBuffer(query, - "SELECT attname, attacl FROM pg_catalog.pg_attribute " - "WHERE attrelid = '%u' AND NOT attisdropped " - "AND attacl IS NOT NULL " - "ORDER BY attnum", - tbinfo->dobj.catId.oid); + if (fout->remoteVersion >= 90600) + { + PQExpBuffer acl_subquery = createPQExpBuffer(); + PQExpBuffer racl_subquery = createPQExpBuffer(); + PQExpBuffer initacl_subquery = createPQExpBuffer(); + PQExpBuffer initracl_subquery = createPQExpBuffer(); + + buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, + initracl_subquery, "at.attacl", "c.relowner", "'c'", + dopt->binary_upgrade); + + appendPQExpBuffer(query, + "SELECT at.attname, " + "%s AS attacl, " + "%s AS rattacl, " + "%s AS initattacl, " + "%s AS initrattacl " + "FROM pg_catalog.pg_attribute at " + "JOIN pg_catalog.pg_class c ON (at.attrelid = c.oid) " + "LEFT JOIN pg_init_privs pip ON " + "(pip.classoid = " + "(SELECT oid FROM pg_class WHERE relname = 'pg_class') AND " + " at.attrelid = pip.objoid AND at.attnum = pip.objsubid) " + "WHERE at.attrelid = '%u' AND " + "NOT at.attisdropped " + "AND at.attacl IS NOT NULL " + "ORDER BY at.attnum", + acl_subquery->data, + racl_subquery->data, + initacl_subquery->data, + initracl_subquery->data, + tbinfo->dobj.catId.oid); + + destroyPQExpBuffer(acl_subquery); + destroyPQExpBuffer(racl_subquery); + destroyPQExpBuffer(initacl_subquery); + destroyPQExpBuffer(initracl_subquery); + } + else + { + appendPQExpBuffer(query, + "SELECT attname, attacl, NULL as rattacl, " + "NULL AS initattacl, NULL AS initrattacl " + "FROM pg_catalog.pg_attribute " + "WHERE attrelid = '%u' AND NOT attisdropped " + "AND attacl IS NOT NULL " + "ORDER BY attnum", + tbinfo->dobj.catId.oid); + } + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); for (i = 0; i < PQntuples(res); i++) { char *attname = PQgetvalue(res, i, 0); char *attacl = PQgetvalue(res, i, 1); + char *rattacl = PQgetvalue(res, i, 2); + char *initattacl = PQgetvalue(res, i, 3); + char *initrattacl = PQgetvalue(res, i, 4); char *attnamecopy; char *acltag; @@ -14212,7 +14941,7 @@ dumpTable(Archive *fout, TableInfo *tbinfo) dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, "TABLE", namecopy, attnamecopy, acltag, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, - attacl); + attacl, rattacl, initattacl, initrattacl); free(attnamecopy); free(acltag); } @@ -14909,7 +15638,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false, reltypename, tbinfo->postponed_def ? - SECTION_POST_DATA : SECTION_PRE_DATA, + SECTION_POST_DATA : SECTION_PRE_DATA, q->data, delq->data, NULL, NULL, 0, NULL, NULL); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 85f9f480ba..7314cbeec8 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -113,6 +113,9 @@ typedef struct _namespaceInfo DumpableObject dobj; char *rolname; /* name of owner, or empty string */ char *nspacl; + char *rnspacl; + char *initnspacl; + char *initrnspacl; } NamespaceInfo; typedef struct _extensionInfo @@ -135,6 +138,9 @@ typedef struct _typeInfo */ char *rolname; /* name of owner, or empty string */ char *typacl; + char *rtypacl; + char *inittypacl; + char *initrtypacl; Oid typelem; Oid typrelid; char typrelkind; /* 'r', 'v', 'c', etc */ @@ -164,6 +170,9 @@ typedef struct _funcInfo Oid *argtypes; Oid prorettype; char *proacl; + char *rproacl; + char *initproacl; + char *initrproacl; } FuncInfo; /* AggInfo is a superset of FuncInfo */ @@ -220,6 +229,9 @@ typedef struct _tableInfo DumpableObject dobj; char *rolname; /* name of owner, or empty string */ char *relacl; + char *rrelacl; + char *initrelacl; + char *initrrelacl; char relkind; char relpersistence; /* relation persistence */ bool relispopulated; /* relation is populated */ @@ -388,6 +400,9 @@ typedef struct _procLangInfo Oid laninline; Oid lanvalidator; char *lanacl; + char *rlanacl; + char *initlanacl; + char *initrlanacl; char *lanowner; /* name of owner, or empty string */ } ProcLangInfo; @@ -457,6 +472,9 @@ typedef struct _fdwInfo char *fdwvalidator; char *fdwoptions; char *fdwacl; + char *rfdwacl; + char *initfdwacl; + char *initrfdwacl; } FdwInfo; typedef struct _foreignServerInfo @@ -467,6 +485,9 @@ typedef struct _foreignServerInfo char *srvtype; char *srvversion; char *srvacl; + char *rsrvacl; + char *initsrvacl; + char *initrsrvacl; char *srvoptions; } ForeignServerInfo; @@ -483,6 +504,9 @@ typedef struct _blobInfo DumpableObject dobj; char *rolname; char *blobacl; + char *rblobacl; + char *initblobacl; + char *initrblobacl; } BlobInfo; /* diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 530d3f4d2c..a59493710b 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -1113,8 +1113,8 @@ dumpTablespaces(PGconn *conn) fspcname, spcoptions); if (!skip_acls && - !buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner, - "", server_version, buf)) + !buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, "", + spcowner, "", server_version, buf)) { fprintf(stderr, _("%s: could not parse ACL list (%s) for tablespace \"%s\"\n"), progname, spcacl, fspcname); @@ -1444,7 +1444,7 @@ dumpCreateDB(PGconn *conn) } if (!skip_acls && - !buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner, + !buildACLCommands(fdbname, NULL, "DATABASE", dbacl, "", dbowner, "", server_version, buf)) { fprintf(stderr, _("%s: could not parse ACL list (%s) for database \"%s\"\n"), diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h index e7da413871..709373c8f8 100644 --- a/src/include/catalog/binary_upgrade.h +++ b/src/include/catalog/binary_upgrade.h @@ -30,4 +30,6 @@ extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_class_oid; extern PGDLLIMPORT Oid binary_upgrade_next_pg_enum_oid; extern PGDLLIMPORT Oid binary_upgrade_next_pg_authid_oid; +extern PGDLLIMPORT bool binary_upgrade_record_init_privs; + #endif /* BINARY_UPGRADE_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 36635f8130..d7dbc73928 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -5245,6 +5245,8 @@ DATA(insert OID = 3590 ( binary_upgrade_set_next_pg_authid_oid PGNSP PGUID 12 1 DESCR("for use by pg_upgrade"); DATA(insert OID = 3591 ( binary_upgrade_create_empty_extension PGNSP PGUID 12 1 0 0 0 f f f f f f v r 7 0 2278 "25 25 16 25 1028 1009 1009" _null_ _null_ _null_ _null_ _null_ binary_upgrade_create_empty_extension _null_ _null_ _null_ )); DESCR("for use by pg_upgrade"); +DATA(insert OID = 4083 ( binary_upgrade_set_record_init_privs PGNSP PGUID 12 1 0 0 0 f f f f t f v r 1 0 2278 "16" _null_ _null_ _null_ _null_ _null_ binary_upgrade_set_record_init_privs _null_ _null_ _null_ )); +DESCR("for use by pg_upgrade"); /* replication/origin.h */ DATA(insert OID = 6003 ( pg_replication_origin_create PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 26 "25" _null_ _null_ _null_ _null_ _null_ pg_replication_origin_create _null_ _null_ _null_ )); diff --git a/src/test/regress/expected/init_privs.out b/src/test/regress/expected/init_privs.out new file mode 100644 index 0000000000..980940fa67 --- /dev/null +++ b/src/test/regress/expected/init_privs.out @@ -0,0 +1,13 @@ +-- Test iniital privileges +-- There should always be some initial privileges, set up by initdb +SELECT count(*) > 0 FROM pg_init_privs; + ?column? +---------- + t +(1 row) + +CREATE ROLE init_privs_test_role1; +CREATE ROLE init_privs_test_role2; +-- Intentionally include some non-initial privs for pg_dump to dump out +GRANT SELECT ON pg_proc TO init_privs_test_role1; +GRANT SELECT (prosrc) ON pg_proc TO init_privs_test_role2; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 7c7b58d43d..6c1f21bb62 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -84,7 +84,7 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- # Another group of parallel tests # ---------- -test: brin gin gist spgist privileges security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator +test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 1b66516a13..8269c524dc 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -105,6 +105,7 @@ test: gin test: gist test: spgist test: privileges +test: init_privs test: security_label test: collate test: matview diff --git a/src/test/regress/sql/init_privs.sql b/src/test/regress/sql/init_privs.sql new file mode 100644 index 0000000000..38d4a88538 --- /dev/null +++ b/src/test/regress/sql/init_privs.sql @@ -0,0 +1,11 @@ +-- Test iniital privileges + +-- There should always be some initial privileges, set up by initdb +SELECT count(*) > 0 FROM pg_init_privs; + +CREATE ROLE init_privs_test_role1; +CREATE ROLE init_privs_test_role2; + +-- Intentionally include some non-initial privs for pg_dump to dump out +GRANT SELECT ON pg_proc TO init_privs_test_role1; +GRANT SELECT (prosrc) ON pg_proc TO init_privs_test_role2;