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;