diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 10700d6fd4a..9b70906a4a9 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -707,6 +707,17 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft_empty ORDER BY c1; Remote SQL: SELECT c1, c2 FROM public.loct_empty ORDER BY c1 ASC NULLS LAST (3 rows) +-- test restriction on non-system foreign tables. +SET restrict_nonsystem_relation_kind TO 'foreign-table'; +SELECT * from ft1 where c1 < 1; -- ERROR +ERROR: access to non-system foreign table is restricted +INSERT INTO ft1 (c1) VALUES (1); -- ERROR +ERROR: access to non-system foreign table is restricted +DELETE FROM ft1 WHERE c1 = 1; -- ERROR +ERROR: access to non-system foreign table is restricted +TRUNCATE ft1; -- ERROR +ERROR: access to non-system foreign table is restricted +RESET restrict_nonsystem_relation_kind; -- =================================================================== -- WHERE with remotely-executable conditions -- =================================================================== diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index 793dd64811d..ac290d3ba30 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -321,6 +321,14 @@ DELETE FROM loct_empty; ANALYZE ft_empty; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft_empty ORDER BY c1; +-- test restriction on non-system foreign tables. +SET restrict_nonsystem_relation_kind TO 'foreign-table'; +SELECT * from ft1 where c1 < 1; -- ERROR +INSERT INTO ft1 (c1) VALUES (1); -- ERROR +DELETE FROM ft1 WHERE c1 = 1; -- ERROR +TRUNCATE ft1; -- ERROR +RESET restrict_nonsystem_relation_kind; + -- =================================================================== -- WHERE with remotely-executable conditions -- =================================================================== diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 23f60cad528..9b03793f74a 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -9027,6 +9027,23 @@ SET XML OPTION { DOCUMENT | CONTENT }; + + restrict_nonsystem_relation_kind (string) + + restrict_nonsystem_relation_kind + configuration parameter + + + + + This variable specifies relation kind to which access is restricted. + It contains a comma-separated list of relation kind. Currently, the + supported relation kinds are view and + foreign-table. + + + + diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index d3113d76a07..6a2ad26a17d 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -772,6 +772,19 @@ PostgreSQL documentation + + + + + This is the same as + the / option, + except that it also excludes any partitions or inheritance child + tables of the table(s) matching the + pattern. + + + + @@ -790,6 +803,18 @@ PostgreSQL documentation + + + + + This is the same as the option, + except that it also excludes data of any partitions or inheritance + child tables of the table(s) matching the + pattern. + + + + @@ -831,6 +856,14 @@ PostgreSQL documentation The only exception is that an empty pattern is disallowed. + + + Using wildcards in may result + in access to unexpected foreign servers. Also, to use this option securely, + make sure that the named server must have a trusted owner. + + + When is specified, @@ -1161,6 +1194,19 @@ PostgreSQL documentation + + + + + This is the same as + the / option, + except that it also includes any partitions or inheritance child + tables of the table(s) matching the + pattern. + + + + diff --git a/gpMgmt/test/behave/mgmt_utils/environment.py b/gpMgmt/test/behave/mgmt_utils/environment.py index d79f9c18acc..217583c9c4c 100644 --- a/gpMgmt/test/behave/mgmt_utils/environment.py +++ b/gpMgmt/test/behave/mgmt_utils/environment.py @@ -68,6 +68,7 @@ def before_feature(context, feature): dbconn.execSQL(context.conn, 'insert into t1 values(1, 2)') dbconn.execSQL(context.conn, 'insert into t2 values(1, 3)') dbconn.execSQL(context.conn, 'insert into t3 values(1, 4)') + dbconn.execSQL(context.conn, 'create or replace function select_one() returns integer as $$ select 1 $$ language sql') context.conn.commit() if 'gppkg' in feature.tags: diff --git a/gpMgmt/test/behave/mgmt_utils/minirepro.feature b/gpMgmt/test/behave/mgmt_utils/minirepro.feature index 15e9c666a51..03f098821ba 100644 --- a/gpMgmt/test/behave/mgmt_utils/minirepro.feature +++ b/gpMgmt/test/behave/mgmt_utils/minirepro.feature @@ -263,3 +263,15 @@ Feature: Dump minimum database objects that is related to the query And the output file "/tmp/out.sql" should contain "Table: t3, Attribute: f" And the output file "/tmp/out.sql" should be loaded to database "minidb_tmp" without error And the file "/tmp/in.sql" should be executed in database "minidb_tmp" without error + + @minirepro_core + Scenario: Dump database objects of only functions + Given the file "/tmp/in.sql" exists and contains "SELECT select_one()" + And the file "/tmp/out.sql" does not exist + When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" + Then the output file "/tmp/out.sql" should exist + And the output file "/tmp/out.sql" should contain "CREATE FUNCTION public.select_one() RETURNS integer" + And the output file "/tmp/out.sql" should contain "LANGUAGE sql" + And the output file "/tmp/out.sql" should contain "AS $$ select 1 $$;" + And the output file "/tmp/out.sql" should be loaded to database "minidb_tmp" without error + And the file "/tmp/in.sql" should be executed in database "minidb_tmp" without error diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index 2d60eff9459..7a00c5f06db 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -33,6 +33,7 @@ #include "optimizer/planmain.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -590,6 +591,15 @@ GetFdwRoutine(Oid fdwhandler) Datum datum; FdwRoutine *routine; + /* Check if the access to foreign tables is restricted */ + if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0)) + { + /* there must not be built-in FDW handler */ + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("access to non-system foreign table is restricted"))); + } + datum = OidFunctionCall0(fdwhandler); routine = (FdwRoutine *) DatumGetPointer(datum); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 53c1bf7d338..20c1587b062 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -47,6 +47,7 @@ #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" +#include "tcop/tcopprot.h" #include "utils/lsyscache.h" #include "utils/uri.h" @@ -8482,7 +8483,19 @@ make_modifytable(PlannerInfo *root, Plan *subplan, Assert(rte->rtekind == RTE_RELATION); if (rte->relkind == RELKIND_FOREIGN_TABLE) + { + /* Check if the access to foreign tables is restricted */ + if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0)) + { + /* there must not be built-in foreign tables */ + Assert(rte->relid >= FirstNormalObjectId); + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("access to non-system foreign table is restricted"))); + } + fdwroutine = GetFdwRoutineByRelId(rte->relid); + } else fdwroutine = NULL; } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 46d85f3c324..8971c42fe77 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -49,6 +49,7 @@ #include "rewrite/rewriteManip.h" #include "statistics/statistics.h" #include "storage/bufmgr.h" +#include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/partcache.h" @@ -484,6 +485,17 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, /* Grab foreign-table info using the relcache, while we have it */ if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) { + /* Check if the access to foreign tables is restricted */ + if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0)) + { + /* there must not be built-in foreign tables */ + Assert(RelationGetRelid(relation) >= FirstNormalObjectId); + + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("access to non-system foreign table is restricted"))); + } + rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation)); rel->segSeverids = GetForeignServerSegsByRelId(RelationGetRelid(relation)); rel->fdwroutine = GetFdwRoutineForRelation(relation, true); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 23c528b60f9..9da36260c77 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -43,6 +43,7 @@ #include "rewrite/rewriteManip.h" #include "rewrite/rewriteSearchCycle.h" #include "rewrite/rowsecurity.h" +#include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/rel.h" @@ -1815,6 +1816,14 @@ ApplyRetrieveRule(Query *parsetree, if (rule->qual != NULL) elog(ERROR, "cannot handle qualified ON SELECT rule"); + /* Check if the expansion of non-system views are restricted */ + if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_VIEW) != 0 && + RelationGetRelid(relation) >= FirstNormalObjectId)) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("access to non-system view \"%s\" is restricted", + RelationGetRelationName(relation)))); + if (rt_index == parsetree->resultRelation) { /* @@ -3261,6 +3270,14 @@ rewriteTargetView(Query *parsetree, Relation view) } } + /* Check if the expansion of non-system views are restricted */ + if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_VIEW) != 0 && + RelationGetRelid(view) >= FirstNormalObjectId)) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("access to non-system view \"%s\" is restricted", + RelationGetRelationName(view)))); + /* * For INSERT/UPDATE the modified columns must all be updatable. Note that * we get the modified columns from the query's targetlist, not from the diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 37cdcfba46a..6ae0202b396 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -89,6 +89,7 @@ #include "utils/snapmgr.h" #include "utils/timeout.h" #include "utils/timestamp.h" +#include "utils/varlena.h" #include "cdb/cdbutil.h" #include "cdb/cdbvars.h" @@ -151,6 +152,8 @@ cancel_pending_hook_type cancel_pending_hook = NULL; * Hook for query execution. */ exec_simple_query_hook_type exec_simple_query_hook = NULL; +/* flags for non-system relation kinds to restrict use */ +int restrict_nonsystem_relation_kind; /* ---------------- * private typedefs etc @@ -4558,6 +4561,66 @@ assign_max_stack_depth(int newval, void *extra) max_stack_depth_bytes = newval_bytes; } +/* + * GUC check_hook for restrict_nonsystem_relation_kind + */ +bool +check_restrict_nonsystem_relation_kind(char **newval, void **extra, GucSource source) +{ + char *rawstring; + List *elemlist; + ListCell *l; + int flags = 0; + + /* Need a modifiable copy of string */ + rawstring = pstrdup(*newval); + + if (!SplitIdentifierString(rawstring, ',', &elemlist)) + { + /* syntax error in list */ + GUC_check_errdetail("List syntax is invalid."); + pfree(rawstring); + list_free(elemlist); + return false; + } + + foreach(l, elemlist) + { + char *tok = (char *) lfirst(l); + + if (pg_strcasecmp(tok, "view") == 0) + flags |= RESTRICT_RELKIND_VIEW; + else if (pg_strcasecmp(tok, "foreign-table") == 0) + flags |= RESTRICT_RELKIND_FOREIGN_TABLE; + else + { + GUC_check_errdetail("Unrecognized key word: \"%s\".", tok); + pfree(rawstring); + list_free(elemlist); + return false; + } + } + + pfree(rawstring); + list_free(elemlist); + + /* Save the flags in *extra, for use by the assign function */ + *extra = malloc(sizeof(int)); + *((int *) *extra) = flags; + + return true; +} + +/* + * GUC assign_hook for restrict_nonsystem_relation_kind + */ +void +assign_restrict_nonsystem_relation_kind(const char *newval, void *extra) +{ + int *flags = (int *) extra; + + restrict_nonsystem_relation_kind = *flags; +} /* * set_debug_options --- apply "-d N" command line option diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 168f6113ce2..bc3e76d0314 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -686,6 +686,8 @@ static char *recovery_target_xid_string; static char *recovery_target_name_string; static char *recovery_target_lsn_string; static char *file_encryption_method_str; +static char *restrict_nonsystem_relation_kind_string; + /* should be static, but commands/variable.c needs to get at this */ char *role_string; @@ -4761,7 +4763,18 @@ static struct config_string ConfigureNamesString[] = "", NULL, NULL, NULL }, - + + { + {"restrict_nonsystem_relation_kind", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets relation kinds of non-system relation to restrict use"), + NULL, + GUC_LIST_INPUT | GUC_NOT_IN_SAMPLE + }, + &restrict_nonsystem_relation_kind_string, + "", + check_restrict_nonsystem_relation_kind, assign_restrict_nonsystem_relation_kind, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 661e19605f6..e180479d6a7 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -161,6 +161,8 @@ getSchemaData(Archive *fout, int *numTablesPtr) pg_log_info("reading user-defined tables"); tblinfo = getTables(fout, &numTables); + getPartitionDefs(fout, tblinfo, numTables); + getOwnedSeqs(fout, tblinfo, numTables); pg_log_info("reading user-defined functions"); @@ -606,6 +608,8 @@ AssignDumpId(DumpableObject *dobj) dobj->namespace = NULL; /* may be set later */ dobj->dump = DUMP_COMPONENT_ALL; /* default assumption */ dobj->dump_contains = DUMP_COMPONENT_ALL; /* default assumption */ + /* All objects have definitions; we may set more components bits later */ + dobj->components = DUMP_COMPONENT_DEFINITION; dobj->ext_member = false; /* default assumption */ dobj->depends_on_ext = false; /* default assumption */ dobj->dependencies = NULL; diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c index ebb3654b725..2f40f0838ea 100644 --- a/src/bin/pg_dump/dumputils.c +++ b/src/bin/pg_dump/dumputils.c @@ -25,7 +25,7 @@ static bool parseAclItem(const char *item, const char *type, const char *name, const char *subname, int remoteVersion, PQExpBuffer grantee, PQExpBuffer grantor, PQExpBuffer privs, PQExpBuffer privswgo); -static char *copyAclUserName(PQExpBuffer output, char *input); +static char *dequoteAclUserName(PQExpBuffer output, char *input); static void AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname); @@ -77,7 +77,8 @@ sanitize_line(const char *str, bool want_hyphen) * TABLE, SEQUENCE, FUNCTION, PROCEDURE, 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 + * baseacls: the initial ACL string for this object; can be + * NULL or empty string to indicate "not available from server" * 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 @@ -86,6 +87,12 @@ sanitize_line(const char *str, bool want_hyphen) * Returns true if okay, false if could not parse the acl string. * The resulting commands (if any) are appended to the contents of 'sql'. * + * baseacls is typically the result of acldefault() for the object's type + * and owner. However, if there is a pg_init_privs entry for the object, + * it should instead be the initprivs ACLs. When acls is itself a + * pg_init_privs entry, baseacls is what to dump that relative to; then + * it can be either an acldefault() value or an empty ACL "{}". + * * Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES " * or something similar, and name is an empty string. * @@ -94,15 +101,19 @@ sanitize_line(const char *str, bool want_hyphen) */ bool buildACLCommands(const char *name, const char *subname, const char *nspname, - const char *type, const char *acls, const char *racls, + const char *type, const char *acls, const char *baseacls, const char *owner, const char *prefix, int remoteVersion, PQExpBuffer sql) { bool ok = true; char **aclitems = NULL; - char **raclitems = NULL; + char **baseitems = NULL; + char **grantitems = NULL; + char **revokeitems = NULL; int naclitems = 0; - int nraclitems = 0; + int nbaseitems = 0; + int ngrantitems = 0; + int nrevokeitems = 0; int i; PQExpBuffer grantee, grantor, @@ -110,37 +121,88 @@ buildACLCommands(const char *name, const char *subname, const char *nspname, privswgo; PQExpBuffer firstsql, secondsql; - bool found_owner_privs = false; - if (strlen(acls) == 0 && strlen(racls) == 0) + /* + * If the acl was NULL (initial default state), we need do nothing. Note + * that this is distinguishable from all-privileges-revoked, which will + * look like an empty array ("{}"). + */ + if (acls == NULL || *acls == '\0') return true; /* object has default permissions */ /* treat empty-string owner same as NULL */ if (owner && *owner == '\0') owner = NULL; - if (strlen(acls) != 0) + /* Parse the acls array */ + if (!parsePGArray(acls, &aclitems, &naclitems)) + { + if (aclitems) + free(aclitems); + return false; + } + + /* Parse the baseacls, if provided */ + if (baseacls && *baseacls != '\0') { - if (!parsePGArray(acls, &aclitems, &naclitems)) + if (!parsePGArray(baseacls, &baseitems, &nbaseitems)) { if (aclitems) free(aclitems); + if (baseitems) + free(baseitems); return false; } } - if (strlen(racls) != 0) + /* + * Compare the actual ACL with the base ACL, extracting the privileges + * that need to be granted (i.e., are in the actual ACL but not the base + * ACL) and the ones that need to be revoked (the reverse). We use plain + * string comparisons to check for matches. In principle that could be + * fooled by extraneous issues such as whitespace, but since all these + * strings are the work of aclitemout(), it should be OK in practice. + * Besides, a false mismatch will just cause the output to be a little + * more verbose than it really needed to be. + * + * (If we weren't given a base ACL, this stanza winds up with all the + * ACL's items in grantitems and nothing in revokeitems. It's not worth + * special-casing that.) + */ + grantitems = (char **) pg_malloc(naclitems * sizeof(char *)); + for (i = 0; i < naclitems; i++) { - if (!parsePGArray(racls, &raclitems, &nraclitems)) + bool found = false; + + for (int j = 0; j < nbaseitems; j++) { - if (aclitems) - free(aclitems); - if (raclitems) - free(raclitems); - return false; + if (strcmp(aclitems[i], baseitems[j]) == 0) + { + found = true; + break; + } } + if (!found) + grantitems[ngrantitems++] = aclitems[i]; } + revokeitems = (char **) pg_malloc(nbaseitems * sizeof(char *)); + for (i = 0; i < nbaseitems; i++) + { + bool found = false; + for (int j = 0; j < naclitems; j++) + { + if (strcmp(baseitems[i], aclitems[j]) == 0) + { + found = true; + break; + } + } + if (!found) + revokeitems[nrevokeitems++] = baseitems[i]; + } + + /* Prepare working buffers */ grantee = createPQExpBuffer(); grantor = createPQExpBuffer(); privs = createPQExpBuffer(); @@ -148,50 +210,21 @@ buildACLCommands(const char *name, const char *subname, const char *nspname, /* * 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(); /* - * 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'. + * If we weren't given baseacls information, we just revoke everything and + * then grant what's listed in the ACL. This avoids having to embed + * detailed knowledge about what the defaults are/were, and it's not very + * expensive since servers lacking acldefault() are now rare. * - * 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. + * Otherwise, we need only revoke what's listed in revokeitems. */ - if (remoteVersion < 90600) + if (baseacls == NULL || *baseacls == '\0') { - Assert(nraclitems == 0); - + /* We assume the old defaults only involved the owner and PUBLIC */ appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix); if (subname) appendPQExpBuffer(firstsql, "(%s)", subname); @@ -199,13 +232,24 @@ buildACLCommands(const char *name, const char *subname, const char *nspname, if (nspname && *nspname) appendPQExpBuffer(firstsql, "%s.", fmtId(nspname)); appendPQExpBuffer(firstsql, "%s FROM PUBLIC;\n", name); + if (owner) + { + appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix); + if (subname) + appendPQExpBuffer(firstsql, "(%s)", subname); + appendPQExpBuffer(firstsql, " ON %s ", type); + if (nspname && *nspname) + appendPQExpBuffer(firstsql, "%s.", fmtId(nspname)); + appendPQExpBuffer(firstsql, "%s FROM %s;\n", name, fmtId(owner)); + } } else { /* Scan individual REVOKE ACL items */ - for (i = 0; i < nraclitems; i++) + for (i = 0; i < nrevokeitems; i++) { - if (!parseAclItem(raclitems[i], type, name, subname, remoteVersion, + if (!parseAclItem(revokeitems[i], + type, name, subname, remoteVersion, grantee, grantor, privs, NULL)) { ok = false; @@ -232,146 +276,92 @@ buildACLCommands(const char *name, const char *subname, const char *nspname, } } - /* Scan individual ACL items */ - for (i = 0; i < naclitems; i++) + /* + * Scan individual ACL items to be granted. + * + * The order in which privileges appear in the ACL string (the order they + * have been GRANT'd in, which the backend maintains) must be preserved to + * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on + * those are dumped in the correct order. However, some old server + * versions will show grants to PUBLIC before the owner's own grants; for + * consistency's sake, force the owner's grants to be output first. + */ + for (i = 0; i < ngrantitems; i++) { - if (!parseAclItem(aclitems[i], type, name, subname, remoteVersion, - grantee, grantor, privs, privswgo)) - { - ok = false; - break; - } - - if (grantor->len == 0 && owner) - printfPQExpBuffer(grantor, "%s", owner); - - if (privs->len > 0 || privswgo->len > 0) + if (parseAclItem(grantitems[i], type, name, subname, remoteVersion, + grantee, grantor, privs, privswgo)) { /* - * 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", respectively. See above. + * If the grantor isn't the owner, we'll need to use SET SESSION + * AUTHORIZATION to become the grantor. Issue the SET/RESET only + * if there's something useful to do. */ - if (remoteVersion < 90600 && owner - && strcmp(grantee->data, owner) == 0 - && strcmp(grantor->data, owner) == 0) + if (privs->len > 0 || privswgo->len > 0) { - found_owner_privs = true; + PQExpBuffer thissql; + + /* Set owner as grantor if that's not explicit in the ACL */ + if (grantor->len == 0 && owner) + printfPQExpBuffer(grantor, "%s", owner); + + /* Make sure owner's own grants are output before others */ + if (owner && + strcmp(grantee->data, owner) == 0 && + strcmp(grantor->data, owner) == 0) + thissql = firstsql; + else + thissql = secondsql; - /* - * For the owner, the default privilege level is ALL WITH - * GRANT OPTION. - */ - if (strcmp(privswgo->data, "ALL") != 0) - { - appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix); - if (subname) - appendPQExpBuffer(firstsql, "(%s)", subname); - appendPQExpBuffer(firstsql, " ON %s ", type); - if (nspname && *nspname) - appendPQExpBuffer(firstsql, "%s.", fmtId(nspname)); - appendPQExpBuffer(firstsql, "%s FROM %s;\n", - name, fmtId(grantee->data)); - if (privs->len > 0) - { - appendPQExpBuffer(firstsql, - "%sGRANT %s ON %s ", - prefix, privs->data, type); - if (nspname && *nspname) - appendPQExpBuffer(firstsql, "%s.", fmtId(nspname)); - appendPQExpBuffer(firstsql, - "%s TO %s;\n", - name, fmtId(grantee->data)); - } - if (privswgo->len > 0) - { - appendPQExpBuffer(firstsql, - "%sGRANT %s ON %s ", - prefix, privswgo->data, type); - if (nspname && *nspname) - appendPQExpBuffer(firstsql, "%s.", fmtId(nspname)); - appendPQExpBuffer(firstsql, - "%s TO %s WITH GRANT OPTION;\n", - name, fmtId(grantee->data)); - } - } - } - else - { - /* - * 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)) - appendPQExpBuffer(secondsql, "SET SESSION AUTHORIZATION %s;\n", + appendPQExpBuffer(thissql, "SET SESSION AUTHORIZATION %s;\n", fmtId(grantor->data)); if (privs->len > 0) { - appendPQExpBuffer(secondsql, "%sGRANT %s ON %s ", + appendPQExpBuffer(thissql, "%sGRANT %s ON %s ", prefix, privs->data, type); if (nspname && *nspname) - appendPQExpBuffer(secondsql, "%s.", fmtId(nspname)); - appendPQExpBuffer(secondsql, "%s TO ", name); + appendPQExpBuffer(thissql, "%s.", fmtId(nspname)); + appendPQExpBuffer(thissql, "%s TO ", name); if (grantee->len == 0) - appendPQExpBufferStr(secondsql, "PUBLIC;\n"); + appendPQExpBufferStr(thissql, "PUBLIC;\n"); else if (strncmp(grantee->data, "group ", strlen("group ")) == 0) - appendPQExpBuffer(secondsql, "GROUP %s;\n", + appendPQExpBuffer(thissql, "GROUP %s;\n", fmtId(grantee->data + strlen("group "))); else - appendPQExpBuffer(secondsql, "%s;\n", fmtId(grantee->data)); + appendPQExpBuffer(thissql, "%s;\n", fmtId(grantee->data)); } if (privswgo->len > 0) { - appendPQExpBuffer(secondsql, "%sGRANT %s ON %s ", + appendPQExpBuffer(thissql, "%sGRANT %s ON %s ", prefix, privswgo->data, type); if (nspname && *nspname) - appendPQExpBuffer(secondsql, "%s.", fmtId(nspname)); - appendPQExpBuffer(secondsql, "%s TO ", name); + appendPQExpBuffer(thissql, "%s.", fmtId(nspname)); + appendPQExpBuffer(thissql, "%s TO ", name); if (grantee->len == 0) - appendPQExpBufferStr(secondsql, "PUBLIC"); + appendPQExpBufferStr(thissql, "PUBLIC"); else if (strncmp(grantee->data, "group ", strlen("group ")) == 0) - appendPQExpBuffer(secondsql, "GROUP %s", + appendPQExpBuffer(thissql, "GROUP %s", fmtId(grantee->data + strlen("group "))); else - appendPQExpBufferStr(secondsql, fmtId(grantee->data)); - appendPQExpBufferStr(secondsql, " WITH GRANT OPTION;\n"); + appendPQExpBufferStr(thissql, fmtId(grantee->data)); + appendPQExpBufferStr(thissql, " WITH GRANT OPTION;\n"); } if (grantor->len > 0 && (!owner || strcmp(owner, grantor->data) != 0)) - appendPQExpBufferStr(secondsql, "RESET SESSION AUTHORIZATION;\n"); + appendPQExpBufferStr(thissql, "RESET SESSION AUTHORIZATION;\n"); } } - } - - /* - * 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 (remoteVersion < 90600 && !found_owner_privs && owner) - { - appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix); - if (subname) - appendPQExpBuffer(firstsql, "(%s)", subname); - appendPQExpBuffer(firstsql, " ON %s ", type); - if (nspname && *nspname) - appendPQExpBuffer(firstsql, "%s.", fmtId(nspname)); - appendPQExpBuffer(firstsql, "%s FROM %s;\n", - name, fmtId(owner)); + else + { + /* parseAclItem failed, give up */ + ok = false; + break; + } } destroyPQExpBuffer(grantee); @@ -385,19 +375,23 @@ buildACLCommands(const char *name, const char *subname, const char *nspname, if (aclitems) free(aclitems); - - if (raclitems) - free(raclitems); + if (baseitems) + free(baseitems); + if (grantitems) + free(grantitems); + if (revokeitems) + free(revokeitems); return ok; } /* - * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry. + * Build ALTER DEFAULT PRIVILEGES command(s) for a single pg_default_acl entry. * * type: the object type (TABLES, FUNCTIONS, etc) * nspname: schema name, or NULL for global default privileges * acls: the ACL string fetched from the database + * acldefault: the appropriate default ACL for the object type and owner * owner: username of privileges owner (will be passed through fmtId) * remoteVersion: version of database * @@ -406,8 +400,7 @@ buildACLCommands(const char *name, const char *subname, const char *nspname, */ bool buildDefaultACLCommands(const char *type, const char *nspname, - const char *acls, const char *racls, - const char *initacls, const char *initracls, + const char *acls, const char *acldefault, const char *owner, int remoteVersion, PQExpBuffer sql) @@ -427,21 +420,12 @@ buildDefaultACLCommands(const char *type, const char *nspname, if (nspname) appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname)); - if (strlen(initacls) != 0 || strlen(initracls) != 0) - { - appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n"); - if (!buildACLCommands("", NULL, NULL, type, - initacls, initracls, owner, - prefix->data, remoteVersion, sql)) - { - destroyPQExpBuffer(prefix); - return false; - } - appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n"); - } - + /* + * There's no such thing as initprivs for a default ACL, so the base ACL + * is always just the object-type-specific default. + */ if (!buildACLCommands("", NULL, NULL, type, - acls, racls, owner, + acls, acldefault, owner, prefix->data, remoteVersion, sql)) { destroyPQExpBuffer(prefix); @@ -491,7 +475,7 @@ parseAclItem(const char *item, const char *type, buf = pg_strdup(item); /* user or group name is string up to = */ - eqpos = copyAclUserName(grantee, buf); + eqpos = dequoteAclUserName(grantee, buf); if (*eqpos != '=') { pg_free(buf); @@ -503,7 +487,7 @@ parseAclItem(const char *item, const char *type, if (slpos) { *slpos++ = '\0'; - slpos = copyAclUserName(grantor, slpos); + slpos = dequoteAclUserName(grantor, slpos); if (*slpos != '\0') { pg_free(buf); @@ -632,13 +616,46 @@ do { \ return true; } +/* + * Transfer the role name at *input into the output buffer, adding + * quoting according to the same rules as putid() in backend's acl.c. + */ +void +quoteAclUserName(PQExpBuffer output, const char *input) +{ + const char *src; + bool safe = true; + + for (src = input; *src; src++) + { + /* This test had better match what putid() does */ + if (!isalnum((unsigned char) *src) && *src != '_') + { + safe = false; + break; + } + } + if (!safe) + appendPQExpBufferChar(output, '"'); + for (src = input; *src; src++) + { + /* A double quote character in a username is encoded as "" */ + if (*src == '"') + appendPQExpBufferChar(output, '"'); + appendPQExpBufferChar(output, *src); + } + if (!safe) + appendPQExpBufferChar(output, '"'); +} + /* * Transfer a user or group name starting at *input into the output buffer, * dequoting if needed. Returns a pointer to just past the input name. * The name is taken to end at an unquoted '=' or end of string. + * Note: unlike quoteAclUserName(), this first clears the output buffer. */ static char * -copyAclUserName(PQExpBuffer output, char *input) +dequoteAclUserName(PQExpBuffer output, char *input) { resetPQExpBuffer(output); @@ -737,132 +754,6 @@ emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, } } -/* - * 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). - * - * Finally, the order in which privileges are in the ACL string (the order - * they been GRANT'd in, which the backend maintains) must be preserved to - * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on - * those are dumped in the correct order. - */ - printfPQExpBuffer(acl_subquery, - "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM " - "(SELECT acl, row_n FROM " - "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) " - "WITH ORDINALITY AS perm(acl,row_n) " - "WHERE NOT EXISTS ( " - "SELECT 1 FROM " - "pg_catalog.unnest(coalesce(pip.initprivs,pg_catalog.acldefault(%s,%s))) " - "AS init(init_acl) WHERE acl = init_acl)) as foo)", - acl_column, - obj_kind, - acl_owner, - obj_kind, - acl_owner); - - printfPQExpBuffer(racl_subquery, - "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM " - "(SELECT acl, row_n FROM " - "pg_catalog.unnest(coalesce(pip.initprivs,pg_catalog.acldefault(%s,%s))) " - "WITH ORDINALITY AS initp(acl,row_n) " - "WHERE NOT EXISTS ( " - "SELECT 1 FROM " - "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) " - "AS permp(orig_acl) WHERE acl = orig_acl)) 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 pg_catalog.array_agg(acl ORDER BY row_n) FROM " - "(SELECT acl, row_n FROM pg_catalog.unnest(pip.initprivs) " - "WITH ORDINALITY AS initp(acl,row_n) " - "WHERE NOT EXISTS ( " - "SELECT 1 FROM " - "pg_catalog.unnest(pg_catalog.acldefault(%s,%s)) " - "AS privm(orig_acl) WHERE acl = orig_acl)) as foo) END", - obj_kind, - acl_owner); - - printfPQExpBuffer(init_racl_subquery, - "CASE WHEN privtype = 'e' THEN " - "(SELECT pg_catalog.array_agg(acl) FROM " - "(SELECT acl, row_n FROM " - "pg_catalog.unnest(pg_catalog.acldefault(%s,%s)) " - "WITH ORDINALITY AS privp(acl,row_n) " - "WHERE NOT EXISTS ( " - "SELECT 1 FROM pg_catalog.unnest(pip.initprivs) " - "AS initp(init_acl) WHERE acl = init_acl)) as foo) END", - obj_kind, - acl_owner); - } - else - { - printfPQExpBuffer(init_acl_subquery, "NULL"); - printfPQExpBuffer(init_racl_subquery, "NULL"); - } -} /* * Detect whether the given GUC variable is of GUC_LIST_QUOTE type. diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h index 2dc6b38992b..cf6f89c6074 100644 --- a/src/bin/pg_dump/dumputils.h +++ b/src/bin/pg_dump/dumputils.h @@ -37,25 +37,22 @@ extern char *sanitize_line(const char *str, bool want_hyphen); extern bool buildACLCommands(const char *name, const char *subname, const char *nspname, - const char *type, const char *acls, const char *racls, + const char *type, const char *acls, const char *baseacls, const char *owner, const char *prefix, int remoteVersion, PQExpBuffer sql); extern bool buildDefaultACLCommands(const char *type, const char *nspname, - const char *acls, const char *racls, - const char *initacls, const char *initracls, + const char *acls, const char *acldefault, const char *owner, int remoteVersion, PQExpBuffer sql); + +extern void quoteAclUserName(PQExpBuffer output, const char *input); + extern void buildShSecLabelQuery(const char *catalog_name, Oid objectId, PQExpBuffer sql); extern void emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, const char *objtype, 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); - extern bool variable_is_guc_list_quote(const char *name); extern bool SplitGUCList(char *rawstring, char separator, diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index 5f414e18686..9c6a6cddef9 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -26,6 +26,14 @@ #include "fe_utils/simple_list.h" #include "libpq-fe.h" +#define GPDB5_MAJOR_PGVERSION 80300 +#define GPDB6_MAJOR_PGVERSION 90400 +#define GPDB7_MAJOR_PGVERSION 120000 +/* + * GPDB7_MERGE_FIXME: it seems like we don't need those cuz for cbdb we have + * fout->version.type and fout->version.version, which provide better checks + */ +#define CBDB2_MAJOR_PGVERSION 140000 typedef enum trivalue { @@ -58,6 +66,23 @@ typedef enum _teSection SECTION_POST_DATA /* stuff to be processed after data */ } teSection; +/* We need one enum entry per prepared query in pg_dump */ +enum _dumpPreparedQueries +{ + PREPQUERY_DUMPAGG, + PREPQUERY_DUMPBASETYPE, + PREPQUERY_DUMPCOMPOSITETYPE, + PREPQUERY_DUMPDOMAIN, + PREPQUERY_DUMPENUMTYPE, + PREPQUERY_DUMPFUNC, + PREPQUERY_DUMPOPR, + PREPQUERY_DUMPRANGETYPE, + PREPQUERY_DUMPTABLEATTACH, + PREPQUERY_GETCOLUMNACLS, + PREPQUERY_GETDOMAINCONSTRAINTS, + NUM_PREP_QUERIES /* must be last */ +}; + /* Parameters needed by ConnectDatabase; same for dump and restore */ typedef struct _connParams { @@ -186,6 +211,9 @@ typedef struct _dumpOptions int do_nothing; char *restrict_key; + /* GPDB */ + bool dumpGpPolicy; + bool isGPbackend; } DumpOptions; @@ -234,6 +262,9 @@ typedef struct Archive DatabaseVersion version; + /* prepared-query status */ + bool *is_prepared; /* indexed by enum _dumpPreparedQueries */ + /* The rest is private */ } Archive; diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c index 967e35ec7f2..4964b0fd943 100644 --- a/src/bin/pg_dump/pg_backup_db.c +++ b/src/bin/pg_dump/pg_backup_db.c @@ -191,10 +191,17 @@ ConnectDatabase(Archive *AHX, } keywords[i] = "fallback_application_name"; values[i++] = progname; + /* GPDB: If binary upgrade, we need to use the correct + * session GUC to connect in utility mode, which depends on + * the server version. We don't know the server version until + * we connect for the first time, so set the correct GUC and + * reconnect. + */ if (binary_upgrade) { keywords[i] = "options"; - values[i++] = "-c gp_role=utility"; + values[i++] = AH->public.remoteVersion < GPDB7_MAJOR_PGVERSION ? + "-c gp_session_role=utility" : "-c gp_role=utility"; keywords[i] = NULL; values[i++] = NULL; } diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a484f693b43..23757c62573 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -69,6 +69,12 @@ #include "pg_dump.h" #include "storage/block.h" +typedef struct +{ + Oid roleoid; /* role's OID */ + const char *rolename; /* role's name */ +} RoleNameItem; + typedef struct { const char *descr; /* comment for an object */ @@ -96,16 +102,8 @@ typedef enum OidOptions /* global decls */ static bool dosync = true; /* Issue fsync() to make dump durable on disk. */ -/* GPDB_95_MERGE_FIXME: put those flags to DumpOptions to avoid using global variables */ -/* START MPP ADDITION */ -bool dumpGpPolicy; -bool isGPbackend; - /* END MPP ADDITION */ -/* subquery used to convert user ID (eg, datdba) to user name */ -static const char *username_subquery; - /* * For 8.0 and earlier servers, pulled from pg_database, for 8.1+ we use * FirstNormalObjectId - 1. @@ -127,10 +125,13 @@ static SimpleStringList schema_exclude_patterns = {NULL, NULL}; static SimpleOidList schema_exclude_oids = {NULL, NULL}; static SimpleStringList table_include_patterns = {NULL, NULL}; +static SimpleStringList table_include_patterns_and_children = {NULL, NULL}; static SimpleOidList table_include_oids = {NULL, NULL}; static SimpleStringList table_exclude_patterns = {NULL, NULL}; +static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL}; static SimpleOidList table_exclude_oids = {NULL, NULL}; static SimpleStringList tabledata_exclude_patterns = {NULL, NULL}; +static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL}; static SimpleOidList tabledata_exclude_oids = {NULL, NULL}; static SimpleStringList relid_string_list = {NULL, NULL}; @@ -152,15 +153,22 @@ const char *EXT_PARTITION_NAME_POSTFIX = "_external_partition__"; /* pg_class.relstorage value used in GPDB 6.x and below to mark external tables. */ #define RELSTORAGE_EXTERNAL 'x' -/* flag indicating whether or not this GP database supports partitioning */ -static bool gp_partitioning_available = false; - -static DumpId binary_upgrade_dumpid; - /* override for standard extra_float_digits setting */ static bool have_extra_float_digits = false; static int extra_float_digits; +/* sorted table of role names */ +static RoleNameItem *rolenames = NULL; +static int nrolenames = 0; + +/* sorted table of comments */ +static CommentItem *comments = NULL; +static int ncomments = 0; + +/* sorted table of security labels */ +static SecLabelItem *seclabels = NULL; +static int nseclabels = 0; + /* * The default number of rows per INSERT when * --inserts is specified without --rows-per-insert @@ -200,7 +208,8 @@ static void expand_foreign_server_name_patterns(Archive *fout, static void expand_table_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, - bool strict_names); + bool strict_names, + bool with_child_tables); static void prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern); @@ -211,16 +220,19 @@ static void guessConstraintInheritance(TableInfo *tblinfo, int numTables); static void dumpComment(Archive *fout, const char *type, const char *name, const char *namespace, const char *owner, CatalogId catalogId, int subid, DumpId dumpId); +static const char *getRoleName(const char *roleoid_str); +static void collectRoleNames(Archive *fout); +static void getAdditionalACLs(Archive *fout); static int findComments(Archive *fout, Oid classoid, Oid objoid, CommentItem **items); -static int collectComments(Archive *fout, CommentItem **items); +static void collectComments(Archive *fout); static void dumpSecLabel(Archive *fout, const char *type, const char *name, const char *namespace, const char *owner, CatalogId catalogId, int subid, DumpId dumpId); static int findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items); -static int collectSecLabels(Archive *fout, SecLabelItem **items); -static void dumpDumpableObject(Archive *fout, const DumpableObject *dobj); +static void collectSecLabels(Archive *fout); +static void dumpDumpableObject(Archive *fout, DumpableObject *dobj); static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo); static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo); static void dumpType(Archive *fout, const TypeInfo *tyinfo); @@ -275,8 +287,7 @@ static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo); static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId, const char *type, const char *name, const char *subname, const char *nspname, const char *owner, - const char *acls, const char *racls, - const char *initacls, const char *initracls); + const DumpableAcl *dacl); static void getDependencies(Archive *fout); static void BuildArchiveDependencies(Archive *fout); @@ -305,7 +316,6 @@ static char *format_function_signature(Archive *fout, static char *convertRegProcReference(const char *proc); static char *getFormattedOperatorName(const char *oproid); static char *convertTSFunction(Archive *fout, Oid funcOid); -static Oid findLastBuiltinOid_V71(Archive *fout); static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts); static void getBlobs(Archive *fout); static void dumpBlob(Archive *fout, const BlobInfo *binfo); @@ -362,6 +372,7 @@ static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions, const char *prefix, Archive *fout); static char *get_synchronized_snapshot(Archive *fout); static void setupDumpWorker(Archive *AHX); +static void set_restrict_relation_kind(Archive *AH, const char *value); static TableInfo *getRootTableInfo(const TableInfo *tbinfo); static bool forcePartitionRootLoad(const TableInfo *tbinfo); @@ -377,69 +388,15 @@ static void expand_oid_patterns(SimpleStringList *patterns, SimpleOidList *oids); static bool is_returns_table_function(int nallargs, char **argmodes); -static bool testGPbackend(Archive *fout); -static bool testPartitioningSupport(Archive *fout); +static void testGPbackend(Archive *fout); static char *nextToken(register char **stringp, register const char *delim); static void addDistributedBy(Archive *fout, PQExpBuffer q, const TableInfo *tbinfo, int actual_atts); static void addDistributedByOld(Archive *fout, PQExpBuffer q, const TableInfo *tbinfo, int actual_atts); static void addSchedule(Archive *fout, PQExpBuffer q, const TableInfo *tbinfo); -static bool isGPDB(Archive *fout); -static bool isGPDB5000OrLater(Archive *fout); -static bool isGPDB6000OrLater(Archive *fout); /* END MPP ADDITION */ -/* - * Check if we are talking to GPDB - */ -static bool -isGPDB(Archive *fout) -{ - static int value = -1; /* -1 = not known yet, 0 = no, 1 = yes */ - - /* Query the server on first call, and cache the result */ - if (value == -1) - { - const char *query = "select pg_catalog.version()"; - PGresult *res; - char *ver; - - res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK); - - ver = (PQgetvalue(res, 0, 0)); - if (strstr(ver, "Cloudberry") != NULL) - value = 1; - else - value = 0; - - PQclear(res); - } - return (value == 1) ? true : false; -} - - -static bool -isGPDB5000OrLater(Archive *fout) -{ - if (!isGPDB(fout)) - return false; /* Not Cloudberry at all. */ - - /* GPDB 5 is based on PostgreSQL 8.3 */ - return fout->remoteVersion >= 80300; -} - - -static bool -isGPDB6000OrLater(Archive *fout) -{ - if (!isGPDB(fout)) - return false; /* Not Cloudberry at all. */ - - /* GPDB 6 is based on PostgreSQL 9.4 */ - return fout->remoteVersion >= 90400; -} - int main(int argc, char **argv) { @@ -545,6 +502,9 @@ main(int argc, char **argv) {"rows-per-insert", required_argument, NULL, 10}, {"include-foreign-data", required_argument, NULL, 11}, {"restrict-key", required_argument, NULL, 25}, + {"table-and-children", required_argument, NULL, 12}, + {"exclude-table-and-children", required_argument, NULL, 13}, + {"exclude-table-data-and-children", required_argument, NULL, 14}, /* START MPP ADDITION */ @@ -805,6 +765,21 @@ main(int argc, char **argv) simple_string_list_append(&foreign_servers_include_patterns, optarg); break; + case 12: /* include table(s) and their children */ + simple_string_list_append(&table_include_patterns_and_children, + optarg); + dopt.include_everything = false; + break; + + case 13: /* exclude table(s) and their children */ + simple_string_list_append(&table_exclude_patterns_and_children, + optarg); + break; + + case 14: /* exclude data of table(s) and children */ + simple_string_list_append(&tabledata_exclude_patterns_and_children, + optarg); + break; case 25: dopt.restrict_key = pg_strdup(optarg); @@ -952,7 +927,7 @@ main(int argc, char **argv) * We allow the server to be back to 8.3, and up to any minor release of * our own major version. (See also version check in pg_dumpall.c.) */ - fout->minRemoteVersion = 80300; /* we can handle back to 8.3 */ + fout->minRemoteVersion = GPDB5_MAJOR_PGVERSION; /* we can handle back to 8.3 */ fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99; fout->numWorkers = numWorkers; @@ -967,7 +942,7 @@ main(int argc, char **argv) /* * Determine whether or not we're interacting with a GP backend. */ - isGPbackend = testGPbackend(fout); + testGPbackend(fout); /* * Now that the type of backend is known, determine the gp-syntax option @@ -976,15 +951,15 @@ main(int argc, char **argv) switch (gp_syntax_option) { case GPS_NOT_SPECIFIED: - dumpGpPolicy = isGPbackend; + dopt.dumpGpPolicy = dopt.isGPbackend; break; case GPS_DISABLED: - dumpGpPolicy = false; + dopt.dumpGpPolicy = false; break; case GPS_ENABLED: - dumpGpPolicy = isGPbackend; - if (!isGPbackend) - pg_log_warning("server is not a Apache Cloudberry instance; --gp-syntax option ignored"); + dopt.dumpGpPolicy = dopt.isGPbackend; + if (!dopt.isGPbackend) + pg_log_warning("server is not an Apache Cloudberry instance; --gp-syntax option ignored"); break; } @@ -1002,13 +977,6 @@ main(int argc, char **argv) if (fout->isStandby) dopt.no_unlogged_table_data = true; - username_subquery = "SELECT rolname FROM pg_catalog.pg_roles WHERE oid ="; - - /* - * Remember whether or not this GP database supports partitioning. - */ - gp_partitioning_available = testPartitioningSupport(fout); - /* check the version for the synchronized snapshots feature */ if (numWorkers > 1 && fout->remoteVersion < 90200 && !dopt.no_synchronized_snapshots) @@ -1020,15 +988,7 @@ main(int argc, char **argv) if (dumpsnapshot && fout->remoteVersion < 90200) fatal("Exported snapshots are not supported by this server version."); - /* - * Find the last built-in OID, if needed (prior to 8.1) - * - * With 8.1 and above, we can just use FirstNormalObjectId - 1. - */ - if (fout->remoteVersion < 80100) - g_last_builtin_oid = findLastBuiltinOid_V71(fout); - else - g_last_builtin_oid = FirstNormalObjectId - 1; + g_last_builtin_oid = FirstNormalObjectId - 1; pg_log_info("last built-in OID is %u", g_last_builtin_oid); @@ -1041,27 +1001,43 @@ main(int argc, char **argv) if (schema_include_oids.head == NULL) fatal("no matching schemas were found"); } + /* + * As of GPDB7 gp_toolkit is an extension. It gets installed into template1 + * when the cluster is initialized by gpinitsystem. For 6 > 7 upgrade we + * assume it will always be present and excluded it from being dumped. + */ + if (fout->remoteVersion < GPDB7_MAJOR_PGVERSION) + simple_string_list_append(&schema_exclude_patterns, "gp_toolkit"); expand_schema_name_patterns(fout, &schema_exclude_patterns, &schema_exclude_oids, false); /* non-matching exclusion patterns aren't an error */ /* Expand table selection patterns into OID lists */ - if (table_include_patterns.head != NULL) - { - expand_table_name_patterns(fout, &table_include_patterns, - &table_include_oids, - strict_names); - if (table_include_oids.head == NULL) - fatal("no matching tables were found"); - } + expand_table_name_patterns(fout, &table_include_patterns, + &table_include_oids, + strict_names, false); + expand_table_name_patterns(fout, &table_include_patterns_and_children, + &table_include_oids, + strict_names, true); + if ((table_include_patterns.head != NULL || + table_include_patterns_and_children.head != NULL) && + table_include_oids.head == NULL) + fatal("no matching tables were found"); + expand_table_name_patterns(fout, &table_exclude_patterns, &table_exclude_oids, - false); + false, false); + expand_table_name_patterns(fout, &table_exclude_patterns_and_children, + &table_exclude_oids, + false, true); expand_table_name_patterns(fout, &tabledata_exclude_patterns, &tabledata_exclude_oids, - false); + false, false); + expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children, + &tabledata_exclude_oids, + false, true); expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns, &foreign_servers_include_oids); @@ -1094,6 +1070,11 @@ main(int argc, char **argv) if (dopt.include_everything && !dopt.schemaOnly && !dopt.dontOutputBlobs) dopt.outputBlobs = true; + /* + * Collect role names so we can map object owner OIDs to names. + */ + collectRoleNames(fout); + /* * Now scan the database and create DumpableObject structs for all the * objects we intend to dump. @@ -1132,6 +1113,16 @@ main(int argc, char **argv) setExtPartDependency(tblinfo, numTables); + /* + * Collect ACLs, comments, and security labels, if wanted. + */ + if (!dopt.aclsSkip) + getAdditionalACLs(fout); + if (!dopt.no_comments) + collectComments(fout); + if (!dopt.no_security_labels) + collectSecLabels(fout); + /* Lastly, create dummy objects to represent the section boundaries */ boundaryObjs = createBoundaryObjects(); @@ -1291,7 +1282,7 @@ help(const char *progname) " plain-text format\n")); printf(_(" -s, --schema-only dump only the schema, no data\n")); printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n")); - printf(_(" -t, --table=PATTERN dump the specified table(s) only\n")); + printf(_(" -t, --table=PATTERN dump only the specified table(s)\n")); printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n")); printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n")); printf(_(" --binary-upgrade for use by upgrade utilities only\n")); @@ -1300,7 +1291,13 @@ help(const char *progname) printf(_(" --disable-triggers disable triggers during data-only restore\n")); printf(_(" --enable-row-security enable row security (dump only content user has\n" " access to)\n")); + printf(_(" --exclude-table-and-children=PATTERN\n" + " do NOT dump the specified table(s),\n" + " including child and partition tables\n")); printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n")); + printf(_(" --exclude-table-data-and-children=PATTERN\n" + " do NOT dump data for the specified table(s),\n" + " including child and partition tables\n")); printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n")); printf(_(" --if-exists use IF EXISTS when dropping objects\n")); printf(_(" --include-foreign-data=PATTERN\n" @@ -1325,6 +1322,8 @@ help(const char *progname) printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n")); printf(_(" --strict-names require table and/or schema include patterns to\n" " match at least one entity each\n")); + printf(_(" --table-and-children=PATTERN dump only the specified table(s),\n" + " including child and partition tables\n")); printf(_(" --use-set-session-authorization\n" " use SET SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n")); @@ -1430,11 +1429,10 @@ setup_connection(Archive *AH, const char *dumpencoding, ExecuteSqlStatement(AH, "SET extra_float_digits TO 2"); /* - * If synchronized scanning is supported, disable it, to prevent - * unpredictable changes in row ordering across a dump and reload. + * Disable synchronized scanning to prevent unpredictable + * changes in row ordering across a dump and reload. */ - if (AH->remoteVersion >= 80300) - ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off"); + ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off"); /* * The default for enable_nestloop is off in GPDB. However, many of the queries @@ -1454,7 +1452,7 @@ setup_connection(Archive *AH, const char *dumpencoding, /* * Quote all identifiers, if requested. */ - if (quote_all_identifiers && AH->remoteVersion >= 80300) + if (quote_all_identifiers) ExecuteSqlStatement(AH, "SET quote_all_identifiers = true"); /* @@ -1468,6 +1466,19 @@ setup_connection(Archive *AH, const char *dumpencoding, ExecuteSqlStatement(AH, "SET row_security = off"); } + /* + * For security reasons, we restrict the expansion of non-system views and + * access to foreign tables during the pg_dump process. This restriction + * is adjusted when dumping foreign table data. + */ + set_restrict_relation_kind(AH, "view, foreign-table"); + + /* + * Initialize prepared-query state to "nothing prepared". We do this here + * so that a parallel dump worker will have its own state. + */ + AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool)); + /* * Start transaction-snapshot mode transaction to dump consistent data. */ @@ -1764,7 +1775,7 @@ expand_foreign_server_name_patterns(Archive *fout, static void expand_table_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, - bool strict_names) + bool strict_names, bool with_child_tables) { PQExpBuffer query; PGresult *res; @@ -1790,7 +1801,15 @@ expand_table_name_patterns(Archive *fout, * Query must remain ABSOLUTELY devoid of unqualified names. This * would be unnecessary given a pg_table_is_visible() variant taking a * search_path argument. + * + * For with_child_tables, we start with the basic query's results and + * recursively search the inheritance tree to add child tables. */ + if (with_child_tables) + { + appendPQExpBuffer(query, "WITH RECURSIVE partition_tree (relid) AS (\n"); + } + appendPQExpBuffer(query, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" @@ -1813,6 +1832,17 @@ expand_table_name_patterns(Archive *fout, prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val); termPQExpBuffer(&dbbuf); + if (with_child_tables) + { + appendPQExpBuffer(query, "UNION" + "\nSELECT i.inhrelid" + "\nFROM partition_tree p" + "\n JOIN pg_catalog.pg_inherits i" + "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent" + "\n)" + "\nSELECT relid FROM partition_tree"); + } + ExecuteSqlStatement(fout, "RESET search_path"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); PQclear(ExecuteSqlQueryForSingleRow(fout, @@ -2006,6 +2036,13 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout) else nsinfo->dobj.dump = DUMP_COMPONENT_ACL; nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL; + + /* + * Also, make like it has a comment even if it doesn't; this is so + * that we'll emit a command to drop the comment, if appropriate. + * (Without this, we'd not call dumpComment for it.) + */ + nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT; } else nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL; @@ -2123,20 +2160,49 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout) * Mark a function as to be dumped or not */ static void -selectDumpableFunction(FuncInfo *finfo) +selectDumpableFunction(FuncInfo *finfo, Archive *fout) { + + if (checkExtensionMembership(&finfo->dobj, fout)) + return; /* extension membership overrides all else */ + /* * If specific functions are being dumped, dump just those functions; else, dump * according to the parent namespace's dump flag if parent namespace is not null; * else, always dump the function. */ - if (function_include_oids.head != NULL) - finfo->dobj.dump = simple_oid_list_member(&function_include_oids, - finfo->dobj.catId.oid); + if (function_include_oids.head != NULL && + simple_oid_list_member(&function_include_oids, finfo->dobj.catId.oid)) + finfo->dobj.dump = DUMP_COMPONENT_ALL; else if (finfo->dobj.namespace) finfo->dobj.dump = finfo->dobj.namespace->dobj.dump; else - finfo->dobj.dump = true; + finfo->dobj.dump = DUMP_COMPONENT_ALL; +} + +/* + * selectDumpableAggregate: policy-setting subroutine + * Mark a function as to be dumped or not + */ +static void +selectDumpableAggregate(AggInfo *agginfo, Archive *fout) +{ + + if (checkExtensionMembership(&agginfo->aggfn.dobj, fout)) + return; /* extension membership overrides all else */ + + /* + * If specific aggregates are being dumped, dump just those aggregates; else, dump + * according to the parent namespace's dump flag if parent namespace is not null; + * else, always dump the function. + */ + if (function_include_oids.head != NULL && + simple_oid_list_member(&function_include_oids, agginfo->aggfn.dobj.catId.oid)) + agginfo->aggfn.dobj.dump = DUMP_COMPONENT_ALL; + else if (agginfo->aggfn.dobj.namespace) + agginfo->aggfn.dobj.dump = agginfo->aggfn.dobj.namespace->dobj.dump; + else + agginfo->aggfn.dobj.dump = DUMP_COMPONENT_ALL; } /* @@ -2362,6 +2428,11 @@ dumpTableData_copy(Archive *fout, const void *dcontext) */ if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE) { + /* Temporary allows to access to foreign tables to dump data */ + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) + set_restrict_relation_kind(fout, "view"); + + /* Note: this syntax is only supported in 8.2 and up */ appendPQExpBufferStr(q, "COPY (SELECT "); /* klugery to get rid of parens in column list */ if (strlen(column_list) > 2) @@ -2473,6 +2544,11 @@ dumpTableData_copy(Archive *fout, const void *dcontext) classname); destroyPQExpBuffer(q); + + /* Revert back the setting */ + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) + set_restrict_relation_kind(fout, "view, foreign-table"); + return 1; } @@ -2499,6 +2575,10 @@ dumpTableData_insert(Archive *fout, const void *dcontext) int rows_per_statement = dopt->dump_inserts; int rows_this_statement = 0; + /* Temporary allows to access to foreign tables to dump data */ + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) + set_restrict_relation_kind(fout, "view"); + /* * If we're going to emit INSERTs with column names, the most efficient * way to deal with generated columns is to exclude them entirely. For @@ -2738,6 +2818,10 @@ dumpTableData_insert(Archive *fout, const void *dcontext) destroyPQExpBuffer(insertStmt); free(attgenerated); + /* Revert back the setting */ + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) + set_restrict_relation_kind(fout, "view, foreign-table"); + return 1; } @@ -2960,7 +3044,7 @@ getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind) * Make a dumpable object for the data of this specific table * * Note: we make a TableDataInfo if and only if we are going to dump the - * table data; the "dump" flag in such objects isn't used. + * table data; the "dump" field in such objects isn't very interesting. */ static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo) @@ -3023,6 +3107,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo) tdinfo->filtercond = NULL; /* might get set later */ addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId); + /* A TableDataInfo contains data, of course */ + tdinfo->dobj.components |= DUMP_COMPONENT_DATA; + tbinfo->dataObj = tdinfo; /* Make sure that we'll collect per-column info for this table. */ @@ -3328,26 +3415,25 @@ dumpDatabase(Archive *fout) int i_tableoid, i_oid, i_datname, - i_dba, + i_datdba, i_encoding, i_collate, i_ctype, i_frozenxid, i_minmxid, i_datacl, - i_rdatacl, + i_acldefault, i_datistemplate, i_datconnlimit, i_tablespace; CatalogId dbCatId; DumpId dbDumpId; + DumpableAcl dbdacl; const char *datname, *dba, *encoding, *collate, *ctype, - *datacl, - *rdatacl, *datistemplate, *datconnlimit, *tablespace; @@ -3359,89 +3445,62 @@ dumpDatabase(Archive *fout) /* * Fetch the database-level properties for this database. - * - * The order in which privileges are in the ACL string (the order they - * have been GRANT'd in, which the backend maintains) must be preserved to - * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on - * those are dumped in the correct order. Note that initial privileges - * (pg_init_privs) are not supported on databases, so this logic cannot - * make use of buildACLQueries(). */ - if (fout->remoteVersion >= 90600) + if (fout->remoteVersion >= 90300) { appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, " - "(%s datdba) AS dba, " + "datdba, " "pg_encoding_to_char(encoding) AS encoding, " "datcollate, datctype, datfrozenxid, datminmxid, " - "(SELECT array_agg(acl ORDER BY row_n) FROM " - " (SELECT acl, row_n FROM " - " unnest(coalesce(datacl,acldefault('d',datdba))) " - " WITH ORDINALITY AS perm(acl,row_n) " - " WHERE NOT EXISTS ( " - " SELECT 1 " - " FROM unnest(acldefault('d',datdba)) " - " AS init(init_acl) " - " WHERE acl = init_acl)) AS datacls) " - " AS datacl, " - "(SELECT array_agg(acl ORDER BY row_n) FROM " - " (SELECT acl, row_n FROM " - " unnest(acldefault('d',datdba)) " - " WITH ORDINALITY AS initp(acl,row_n) " - " WHERE NOT EXISTS ( " - " SELECT 1 " - " FROM unnest(coalesce(datacl,acldefault('d',datdba))) " - " AS permp(orig_acl) " - " WHERE acl = orig_acl)) AS rdatacls) " - " AS rdatacl, " + "datacl, acldefault('d', datdba) AS acldefault, " "datistemplate, datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " "shobj_description(oid, 'pg_database') AS description " "FROM pg_database " - "WHERE datname = current_database()", - username_subquery); + "WHERE datname = current_database()"); } - else if (fout->remoteVersion >= 90300) + else if (fout->remoteVersion >= 90200) { appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, " - "(%s datdba) AS dba, " + "datdba, " "pg_encoding_to_char(encoding) AS encoding, " - "datcollate, datctype, datfrozenxid, datminmxid, " - "datacl, '' as rdatacl, datistemplate, datconnlimit, " + "datcollate, datctype, datfrozenxid, 0 AS datminmxid, " + "datacl, acldefault('d', datdba) AS acldefault, " + "datistemplate, datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " "shobj_description(oid, 'pg_database') AS description " "FROM pg_database " - "WHERE datname = current_database()", - username_subquery); + "WHERE datname = current_database()"); } else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, " - "(%s datdba) AS dba, " + "datdba, " "pg_encoding_to_char(encoding) AS encoding, " "datcollate, datctype, datfrozenxid, 0 AS datminmxid, " - "datacl, '' as rdatacl, datistemplate, datconnlimit, " + "datacl, NULL AS acldefault, " + "datistemplate, datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " "shobj_description(oid, 'pg_database') AS description " "FROM pg_database " - "WHERE datname = current_database()", - username_subquery); + "WHERE datname = current_database()"); } else { appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, " - "(%s datdba) AS dba, " + "datdba, " "pg_encoding_to_char(encoding) AS encoding, " "NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, " - "datacl, '' as rdatacl, datistemplate, datconnlimit, " + "datacl, NULL AS acldefault, " + "datistemplate, datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " "shobj_description(oid, 'pg_database') AS description " "FROM pg_database " - "WHERE datname = current_database()", - username_subquery); + "WHERE datname = current_database()"); } res = ExecuteSqlQueryForSingleRow(fout, dbQry->data); @@ -3449,14 +3508,14 @@ dumpDatabase(Archive *fout) i_tableoid = PQfnumber(res, "tableoid"); i_oid = PQfnumber(res, "oid"); i_datname = PQfnumber(res, "datname"); - i_dba = PQfnumber(res, "dba"); + i_datdba = PQfnumber(res, "datdba"); i_encoding = PQfnumber(res, "encoding"); i_collate = PQfnumber(res, "datcollate"); i_ctype = PQfnumber(res, "datctype"); i_frozenxid = PQfnumber(res, "datfrozenxid"); i_minmxid = PQfnumber(res, "datminmxid"); i_datacl = PQfnumber(res, "datacl"); - i_rdatacl = PQfnumber(res, "rdatacl"); + i_acldefault = PQfnumber(res, "acldefault"); i_datistemplate = PQfnumber(res, "datistemplate"); i_datconnlimit = PQfnumber(res, "datconnlimit"); i_tablespace = PQfnumber(res, "tablespace"); @@ -3464,14 +3523,14 @@ dumpDatabase(Archive *fout) dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid)); dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid)); datname = PQgetvalue(res, 0, i_datname); - dba = PQgetvalue(res, 0, i_dba); + dba = getRoleName(PQgetvalue(res, 0, i_datdba)); encoding = PQgetvalue(res, 0, i_encoding); collate = PQgetvalue(res, 0, i_collate); ctype = PQgetvalue(res, 0, i_ctype); frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid)); minmxid = atooid(PQgetvalue(res, 0, i_minmxid)); - datacl = PQgetvalue(res, 0, i_datacl); - rdatacl = PQgetvalue(res, 0, i_rdatacl); + dbdacl.acl = PQgetvalue(res, 0, i_datacl); + dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault); datistemplate = PQgetvalue(res, 0, i_datistemplate); datconnlimit = PQgetvalue(res, 0, i_datconnlimit); tablespace = PQgetvalue(res, 0, i_tablespace); @@ -3595,9 +3654,12 @@ dumpDatabase(Archive *fout) * Dump ACL if any. Note that we do not support initial privileges * (pg_init_privs) on databases. */ + dbdacl.privtype = 0; + dbdacl.initprivs = NULL; + dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE", qdatname, NULL, NULL, - dba, datacl, rdatacl, "", ""); + dba, &dbdacl); /* * Now construct a DATABASE PROPERTIES archive entry to restore any @@ -3797,9 +3859,9 @@ dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf, } /* - * If we're upgrading from GPDB 5 or below, use the legacy hash ops. + * If we're upgrading from GPDB 5, use the legacy hash ops. */ - if (AH->dopt->binary_upgrade && AH->remoteVersion < 90400) + if (AH->dopt->binary_upgrade && AH->remoteVersion < GPDB6_MAJOR_PGVERSION) { makeAlterConfigCommand(conn, "gp_use_legacy_hashops=on", "DATABASE", dbname, NULL, NULL, outbuf); @@ -3936,69 +3998,36 @@ getBlobs(Archive *fout) int i_oid; int i_lomowner; int i_lomacl; - int i_rlomacl; - int i_initlomacl; - int i_initrlomacl; + int i_acldefault; pg_log_info("reading large objects"); /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ - if (fout->remoteVersion >= 90600) + if (fout->remoteVersion >= 90200) { - 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 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 = 'pg_largeobject'::regclass " - "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); + "SELECT oid, lomowner, lomacl, " + "acldefault('L', lomowner) AS acldefault " + "FROM pg_largeobject_metadata"); } 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); + "SELECT oid, lomowner, lomacl, " + "NULL AS acldefault " + "FROM pg_largeobject_metadata"); else appendPQExpBufferStr(blobQry, "SELECT DISTINCT loid AS oid, " "NULL::name AS rolname, NULL::oid AS lomacl, " - "NULL::oid AS rlomacl, NULL::oid AS initlomacl, " - "NULL::oid AS initrlomacl " + "NULL::oid AS acldefault " " FROM pg_largeobject"); res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); + i_lomowner = PQfnumber(res, "lomowner"); i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + i_acldefault = PQfnumber(res, "acldefault"); ntups = PQntuples(res); @@ -4015,17 +4044,18 @@ getBlobs(Archive *fout) 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; + binfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + binfo[i].dacl.privtype = 0; + binfo[i].dacl.initprivs = NULL; + binfo[i].rolname = getRoleName(PQgetvalue(res, i, i_lomowner)); + + /* Blobs have data */ + binfo[i].dobj.components |= DUMP_COMPONENT_DATA; + + /* Mark whether blob has an ACL */ + if (!PQgetisnull(res, i, i_lomacl)) + binfo[i].dobj.components |= DUMP_COMPONENT_ACL; /* * In binary-upgrade mode for blobs, we do *not* dump out the blob @@ -4049,6 +4079,7 @@ getBlobs(Archive *fout) bdata->catId = nilCatalogId; AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); + bdata->components |= DUMP_COMPONENT_DATA; } PQclear(res); @@ -4096,11 +4127,10 @@ dumpBlob(Archive *fout, const BlobInfo *binfo) binfo->dobj.catId, 0, binfo->dobj.dumpId); /* Dump ACL if any */ - if (binfo->blobacl && (binfo->dobj.dump & DUMP_COMPONENT_ACL)) + if (binfo->dobj.dump & DUMP_COMPONENT_ACL) dumpACL(fout, binfo->dobj.dumpId, InvalidDumpId, "LARGE OBJECT", binfo->dobj.name, NULL, - NULL, binfo->rolname, binfo->blobacl, binfo->rblobacl, - binfo->initblobacl, binfo->initrblobacl); + NULL, binfo->rolname, &binfo->dacl); destroyPQExpBuffer(cquery); destroyPQExpBuffer(dquery); @@ -4241,6 +4271,8 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables) /* Is RLS enabled? (That's separate from whether it has policies) */ if (tbinfo->rowsec) { + tbinfo->dobj.components |= DUMP_COMPONENT_POLICY; + /* * We represent RLS being enabled on a table by creating a * PolicyInfo object with null polname. @@ -4364,6 +4396,7 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo) const char *cmd; char *tag; + /* Do nothing in data-only dump */ if (dopt->dataOnly) return; @@ -4384,7 +4417,7 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo) * explicitly, because it will not match anything in pg_depend (unlike * the case for other PolicyInfo objects). */ - if (polinfo->dobj.dump & DUMP_COMPONENT_POLICY) + if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId, ARCHIVE_OPTS(.tag = polinfo->dobj.name, .namespace = polinfo->dobj.namespace->dobj.name, @@ -4446,7 +4479,7 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo) tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name); - if (polinfo->dobj.dump & DUMP_COMPONENT_POLICY) + if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId, ARCHIVE_OPTS(.tag = tag, .namespace = polinfo->dobj.namespace->dobj.name, @@ -4482,7 +4515,7 @@ getPublications(Archive *fout, int *numPublications) int i_tableoid; int i_oid; int i_pubname; - int i_rolname; + int i_pubowner; int i_puballtables; int i_pubinsert; int i_pubupdate; @@ -4506,24 +4539,15 @@ getPublications(Archive *fout, int *numPublications) if (fout->remoteVersion >= 130000) appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, p.pubname, " - "(%s p.pubowner) AS rolname, " - "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubviaroot " - "FROM pg_publication p", - username_subquery); - else if (fout->remoteVersion >= 110000) - appendPQExpBuffer(query, - "SELECT p.tableoid, p.oid, p.pubname, " - "(%s p.pubowner) AS rolname, " + "p.pubowner, " "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubviaroot " - "FROM pg_publication p", - username_subquery); + "FROM pg_publication p"); else appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, p.pubname, " - "(%s p.pubowner) AS rolname, " + "p.pubowner, " "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubviaroot " - "FROM pg_publication p", - username_subquery); + "FROM pg_publication p"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -4532,7 +4556,7 @@ getPublications(Archive *fout, int *numPublications) i_tableoid = PQfnumber(res, "tableoid"); i_oid = PQfnumber(res, "oid"); i_pubname = PQfnumber(res, "pubname"); - i_rolname = PQfnumber(res, "rolname"); + i_pubowner = PQfnumber(res, "pubowner"); i_puballtables = PQfnumber(res, "puballtables"); i_pubinsert = PQfnumber(res, "pubinsert"); i_pubupdate = PQfnumber(res, "pubupdate"); @@ -4550,7 +4574,7 @@ getPublications(Archive *fout, int *numPublications) pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); AssignDumpId(&pubinfo[i].dobj); pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname)); - pubinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner)); pubinfo[i].puballtables = (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0); pubinfo[i].pubinsert = @@ -4564,10 +4588,6 @@ getPublications(Archive *fout, int *numPublications) pubinfo[i].pubviaroot = (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0); - if (strlen(pubinfo[i].rolname) == 0) - pg_log_warning("owner of publication \"%s\" appears to be invalid", - pubinfo[i].dobj.name); - /* Decide whether we want to dump it */ selectDumpableObject(&(pubinfo[i].dobj), fout); } @@ -4591,9 +4611,6 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo) char *qpubname; bool first = true; - if (!(pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) - return; - delq = createPQExpBuffer(); query = createPQExpBuffer(); @@ -4649,13 +4666,14 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo) appendPQExpBufferStr(query, ");\n"); - ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId, - ARCHIVE_OPTS(.tag = pubinfo->dobj.name, - .owner = pubinfo->rolname, - .description = "PUBLICATION", - .section = SECTION_POST_DATA, - .createStmt = query->data, - .dropStmt = delq->data)); + if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) + ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId, + ARCHIVE_OPTS(.tag = pubinfo->dobj.name, + .owner = pubinfo->rolname, + .description = "PUBLICATION", + .section = SECTION_POST_DATA, + .createStmt = query->data, + .dropStmt = delq->data)); if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT) dumpComment(fout, "PUBLICATION", qpubname, @@ -4771,9 +4789,6 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo) PQExpBuffer query; char *tag; - if (!(pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) - return; - tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name); query = createPQExpBuffer(); @@ -4790,13 +4805,14 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo) * owner field anyway to ensure that the command is run by the correct * role at restore time. */ - ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId, - ARCHIVE_OPTS(.tag = tag, - .namespace = tbinfo->dobj.namespace->dobj.name, - .owner = pubinfo->rolname, - .description = "PUBLICATION TABLE", - .section = SECTION_POST_DATA, - .createStmt = query->data)); + if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) + ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId, + ARCHIVE_OPTS(.tag = tag, + .namespace = tbinfo->dobj.namespace->dobj.name, + .owner = pubinfo->rolname, + .description = "PUBLICATION TABLE", + .section = SECTION_POST_DATA, + .createStmt = query->data)); free(tag); destroyPQExpBuffer(query); @@ -4819,6 +4835,28 @@ is_superuser(Archive *fout) return false; } +/* + * Set the given value to restrict_nonsystem_relation_kind value. Since + * restrict_nonsystem_relation_kind is introduced in minor version releases, + * the setting query is effective only where available. + */ +static void +set_restrict_relation_kind(Archive *AH, const char *value) +{ + PQExpBuffer query = createPQExpBuffer(); + PGresult *res; + + appendPQExpBuffer(query, + "SELECT set_config(name, '%s', false) " + "FROM pg_settings " + "WHERE name = 'restrict_nonsystem_relation_kind'", + value); + res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK); + + PQclear(res); + destroyPQExpBuffer(query); +} + /* * getSubscriptions * get information about subscriptions @@ -4833,7 +4871,7 @@ getSubscriptions(Archive *fout) int i_tableoid; int i_oid; int i_subname; - int i_rolname; + int i_subowner; int i_substream; int i_subconninfo; int i_subslotname; @@ -4866,26 +4904,21 @@ getSubscriptions(Archive *fout) /* Get the subscriptions in current database. */ appendPQExpBuffer(query, - "SELECT s.tableoid, s.oid, s.subname,\n" - " (%s s.subowner) AS rolname,\n" - " s.subconninfo, s.subslotname, s.subsynccommit,\n" - " s.subpublications,\n", - username_subquery); - - if (fout->remoteVersion >= 140000) - appendPQExpBufferStr(query, " s.subbinary,\n"); - else - appendPQExpBufferStr(query, " false AS subbinary,\n"); - + "SELECT s.tableoid, s.oid, s.subname, " + "s.subowner, " + "s.subconninfo, s.subslotname, s.subsynccommit, " + "s.subpublications, "); + if (fout->remoteVersion >= 140000) - appendPQExpBufferStr(query, " s.substream\n"); + appendPQExpBufferStr(query, " s.subbinary, s.substream\n"); else - appendPQExpBufferStr(query, " false AS substream\n"); - - appendPQExpBufferStr(query, - "FROM pg_subscription s\n" - "WHERE s.subdbid = (SELECT oid FROM pg_database\n" - " WHERE datname = current_database())"); + appendPQExpBufferStr(query, " false AS subbinary, false AS substream\n"); + + + appendPQExpBuffer(query, + "FROM pg_subscription s " + "WHERE s.subdbid = (SELECT oid FROM pg_database" + " WHERE datname = current_database())"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -4894,7 +4927,7 @@ getSubscriptions(Archive *fout) i_tableoid = PQfnumber(res, "tableoid"); i_oid = PQfnumber(res, "oid"); i_subname = PQfnumber(res, "subname"); - i_rolname = PQfnumber(res, "rolname"); + i_subowner = PQfnumber(res, "subowner"); i_subconninfo = PQfnumber(res, "subconninfo"); i_subslotname = PQfnumber(res, "subslotname"); i_subsynccommit = PQfnumber(res, "subsynccommit"); @@ -4912,7 +4945,7 @@ getSubscriptions(Archive *fout) subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); AssignDumpId(&subinfo[i].dobj); subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname)); - subinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner)); subinfo[i].subconninfo = pg_strdup(PQgetvalue(res, i, i_subconninfo)); if (PQgetisnull(res, i, i_subslotname)) subinfo[i].subslotname = NULL; @@ -4927,10 +4960,6 @@ getSubscriptions(Archive *fout) subinfo[i].substream = pg_strdup(PQgetvalue(res, i, i_substream)); - if (strlen(subinfo[i].rolname) == 0) - pg_log_warning("owner of subscription \"%s\" appears to be invalid", - subinfo[i].dobj.name); - /* Decide whether we want to dump it */ selectDumpableObject(&(subinfo[i].dobj), fout); } @@ -4954,9 +4983,6 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo) int npubnames = 0; int i; - if (!(subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) - return; - delq = createPQExpBuffer(); query = createPQExpBuffer(); @@ -4999,13 +5025,14 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo) appendPQExpBufferStr(query, ");\n"); - ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId, - ARCHIVE_OPTS(.tag = subinfo->dobj.name, - .owner = subinfo->rolname, - .description = "SUBSCRIPTION", - .section = SECTION_POST_DATA, - .createStmt = query->data, - .dropStmt = delq->data)); + if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) + ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId, + ARCHIVE_OPTS(.tag = subinfo->dobj.name, + .owner = subinfo->rolname, + .description = "SUBSCRIPTION", + .section = SECTION_POST_DATA, + .createStmt = query->data, + .dropStmt = delq->data)); if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT) dumpComment(fout, "SUBSCRIPTION", qsubname, @@ -5720,7 +5747,6 @@ binary_upgrade_extension_member(PQExpBuffer upgrade_buffer, NamespaceInfo * getNamespaces(Archive *fout, int *numNamespaces) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -5729,11 +5755,9 @@ getNamespaces(Archive *fout, int *numNamespaces) int i_tableoid; int i_oid; int i_nspname; - int i_rolname; + int i_nspowner; int i_nspacl; - int i_rnspacl; - int i_initnspacl; - int i_initrnspacl; + int i_acldefault; query = createPQExpBuffer(); @@ -5741,48 +5765,17 @@ 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. */ - 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); - + if (fout->remoteVersion >= 90200) 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 = 'pg_namespace'::regclass " - "AND pip.objsubid = 0", - username_subquery, - acl_subquery->data, - racl_subquery->data, - init_acl_subquery->data, - init_racl_subquery->data); - - appendPQExpBufferStr(query, ") "); - - destroyPQExpBuffer(acl_subquery); - destroyPQExpBuffer(racl_subquery); - destroyPQExpBuffer(init_acl_subquery); - destroyPQExpBuffer(init_racl_subquery); - } + "n.nspowner, " + "n.nspacl, " + "acldefault('n', n.nspowner) AS acldefault " + "FROM pg_namespace n"); 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); + appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, " + "n.nspowner, " + "n.nspacl, NULL AS acldefault " + "FROM pg_namespace n"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -5793,44 +5786,33 @@ getNamespaces(Archive *fout, int *numNamespaces) i_tableoid = PQfnumber(res, "tableoid"); i_oid = PQfnumber(res, "oid"); i_nspname = PQfnumber(res, "nspname"); - i_rolname = PQfnumber(res, "rolname"); + i_nspowner = PQfnumber(res, "nspowner"); i_nspacl = PQfnumber(res, "nspacl"); - i_rnspacl = PQfnumber(res, "rnspacl"); - i_initnspacl = PQfnumber(res, "initnspacl"); - i_initrnspacl = PQfnumber(res, "initrnspacl"); + i_acldefault = PQfnumber(res, "acldefault"); for (i = 0; i < ntups; i++) { + const char *nspowner; + nsinfo[i].dobj.objType = DO_NAMESPACE; nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); AssignDumpId(&nsinfo[i].dobj); 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)); + nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl)); + nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + nsinfo[i].dacl.privtype = 0; + nsinfo[i].dacl.initprivs = NULL; + nspowner = PQgetvalue(res, i, i_nspowner); + nsinfo[i].nspowner = atooid(nspowner); + nsinfo[i].rolname = getRoleName(nspowner); /* Decide whether to dump this namespace */ 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) - pg_log_warning("owner of schema \"%s\" appears to be invalid", - nsinfo[i].dobj.name); + /* Mark whether namespace has an ACL */ + if (!PQgetisnull(res, i, i_nspacl)) + nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL; } PQclear(res); @@ -5881,15 +5863,6 @@ getExtensions(Archive *fout, int *numExtensions) int i_extconfig; int i_extcondition; - /* - * Before 8.3 (porting from PG 9.1), there are no extensions. - */ - if (fout->remoteVersion < 80300) - { - *numExtensions = 0; - return NULL; - } - query = createPQExpBuffer(); appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, " @@ -5948,8 +5921,6 @@ getBinaryUpgradeObjects(void) AssignDumpId(&binfo->dobj); binfo->dobj.name = pg_strdup("__binary_upgrade"); - binary_upgrade_dumpid = binfo->dobj.dumpId; - return binfo; } @@ -5966,7 +5937,6 @@ getBinaryUpgradeObjects(void) TypeInfo * getTypes(Archive *fout, int *numTypes) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -5978,10 +5948,8 @@ 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_acldefault; + int i_typowner; int i_typelem; int i_typrelid; int i_typrelkind; @@ -6004,26 +5972,14 @@ getTypes(Archive *fout, int *numTypes) * still check for name beginning with '_', though, so as to avoid the * cost of the subselect probe for all standard types. This would have to * be revisited if the backend ever allows renaming of array types. + * */ - - if (fout->remoteVersion >= 90600) + if (fout->remoteVersion >= 90200) { - 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.typnamespace, t.typacl, " + "acldefault('T', t.typowner) AS acldefault, " + "typowner, " "t.typelem, t.typrelid, " "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" " "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, " @@ -6032,51 +5988,23 @@ getTypes(Archive *fout, int *numTypes) "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray, " "coalesce(array_to_string(e.typoptions, ', '), '') AS typstorage " "FROM pg_type t " - "LEFT JOIN pg_type_encoding e ON t.oid = e.typid " - "LEFT JOIN pg_init_privs pip ON " - "(t.oid = pip.objoid " - "AND pip.classoid = 'pg_type'::regclass " - "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, NULL as rtypacl, " - "NULL AS inittypacl, NULL AS initrtypacl, " - "(%s typowner) AS rolname, " - "typelem, typrelid, " - "CASE WHEN typrelid = 0 THEN ' '::\"char\" " - "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " - "typtype, typisdefined, " - "typname[0] = '_' AND typelem != 0 AND " - "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray " - "FROM pg_type", - username_subquery); + "LEFT JOIN pg_type_encoding e ON t.oid = e.typid "); } else { - appendPQExpBuffer(query, "SELECT tableoid, oid, typname, " - "typnamespace, NULL AS typacl, NULL as rtypacl, " - "NULL AS inittypacl, NULL AS initrtypacl, " - "(%s typowner) AS rolname, " - "typelem, typrelid, " - "CASE WHEN typrelid = 0 THEN ' '::\"char\" " - "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " - "typtype, typisdefined, " - "typname[0] = '_' AND typelem != 0 AND " - "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray " - "FROM pg_type", - username_subquery); + appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, " + "t.typnamespace, NULL AS typacl, " + "NULL AS acldefault, " + "typowner, " + "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, " + "coalesce(array_to_string(e.typoptions, ', '), '') AS typstorage " + "FROM pg_type t " + "LEFT JOIN pg_type_encoding e ON t.oid = e.typid "); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -6090,10 +6018,8 @@ 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_acldefault = PQfnumber(res, "acldefault"); + i_typowner = PQfnumber(res, "typowner"); i_typelem = PQfnumber(res, "typelem"); i_typrelid = PQfnumber(res, "typrelid"); i_typrelkind = PQfnumber(res, "typrelkind"); @@ -6111,12 +6037,12 @@ getTypes(Archive *fout, int *numTypes) tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname)); tyinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_typnamespace))); + tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl)); + tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + tyinfo[i].dacl.privtype = 0; + tyinfo[i].dacl.initprivs = NULL; tyinfo[i].ftypname = NULL; /* may get filled later */ - 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].rolname = getRoleName(PQgetvalue(res, i, i_typowner)); 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); @@ -6143,11 +6069,9 @@ getTypes(Archive *fout, int *numTypes) /* Decide whether we want to dump it */ 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; + /* Mark whether type has an ACL */ + if (!PQgetisnull(res, i, i_typacl)) + tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL; /* * If it's a domain, fetch info about its constraints, if any @@ -6188,10 +6112,6 @@ getTypes(Archive *fout, int *numTypes) */ stinfo->dobj.dump = DUMP_COMPONENT_NONE; } - - if (strlen(tyinfo[i].rolname) == 0) - pg_log_warning("owner of data type \"%s\" appears to be invalid", - tyinfo[i].dobj.name); } *numTypes = ntups; @@ -6222,7 +6142,7 @@ getOperators(Archive *fout, int *numOprs) int i_oid; int i_oprname; int i_oprnamespace; - int i_rolname; + int i_oprowner; int i_oprkind; int i_oprcode; @@ -6233,11 +6153,10 @@ getOperators(Archive *fout, int *numOprs) appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, " "oprnamespace, " - "(%s oprowner) AS rolname, " + "oprowner, " "oprkind, " "oprcode::oid AS oprcode " - "FROM pg_operator", - username_subquery); + "FROM pg_operator"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -6250,7 +6169,7 @@ getOperators(Archive *fout, int *numOprs) i_oid = PQfnumber(res, "oid"); i_oprname = PQfnumber(res, "oprname"); i_oprnamespace = PQfnumber(res, "oprnamespace"); - i_rolname = PQfnumber(res, "rolname"); + i_oprowner = PQfnumber(res, "oprowner"); i_oprkind = PQfnumber(res, "oprkind"); i_oprcode = PQfnumber(res, "oprcode"); @@ -6263,19 +6182,12 @@ getOperators(Archive *fout, int *numOprs) oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname)); oprinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace))); - oprinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner)); oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0]; oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode)); /* Decide whether we want to dump it */ selectDumpableObject(&(oprinfo[i].dobj), fout); - - /* Operators do not currently have ACLs. */ - oprinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - - if (strlen(oprinfo[i].rolname) == 0) - pg_log_warning("owner of operator \"%s\" appears to be invalid", - oprinfo[i].dobj.name); } PQclear(res); @@ -6304,7 +6216,7 @@ getCollations(Archive *fout, int *numCollations) int i_oid; int i_collname; int i_collnamespace; - int i_rolname; + int i_collowner; /* Collations didn't exist pre-9.1 */ if (fout->remoteVersion < 90100) @@ -6322,9 +6234,8 @@ getCollations(Archive *fout, int *numCollations) appendPQExpBuffer(query, "SELECT tableoid, oid, collname, " "collnamespace, " - "(%s collowner) AS rolname " - "FROM pg_collation", - username_subquery); + "collowner " + "FROM pg_collation"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -6337,7 +6248,7 @@ getCollations(Archive *fout, int *numCollations) i_oid = PQfnumber(res, "oid"); i_collname = PQfnumber(res, "collname"); i_collnamespace = PQfnumber(res, "collnamespace"); - i_rolname = PQfnumber(res, "rolname"); + i_collowner = PQfnumber(res, "collowner"); for (i = 0; i < ntups; i++) { @@ -6348,13 +6259,10 @@ getCollations(Archive *fout, int *numCollations) collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname)); collinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_collnamespace))); - collinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner)); /* Decide whether we want to dump it */ selectDumpableObject(&(collinfo[i].dobj), fout); - - /* Collations do not currently have ACLs. */ - collinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -6383,7 +6291,7 @@ getConversions(Archive *fout, int *numConversions) int i_oid; int i_conname; int i_connamespace; - int i_rolname; + int i_conowner; query = createPQExpBuffer(); @@ -6394,9 +6302,8 @@ getConversions(Archive *fout, int *numConversions) appendPQExpBuffer(query, "SELECT tableoid, oid, conname, " "connamespace, " - "(%s conowner) AS rolname " - "FROM pg_conversion", - username_subquery); + "conowner " + "FROM pg_conversion"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -6409,7 +6316,7 @@ getConversions(Archive *fout, int *numConversions) i_oid = PQfnumber(res, "oid"); i_conname = PQfnumber(res, "conname"); i_connamespace = PQfnumber(res, "connamespace"); - i_rolname = PQfnumber(res, "rolname"); + i_conowner = PQfnumber(res, "conowner"); for (i = 0; i < ntups; i++) { @@ -6420,13 +6327,10 @@ getConversions(Archive *fout, int *numConversions) convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname)); convinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_connamespace))); - convinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner)); /* Decide whether we want to dump it */ selectDumpableObject(&(convinfo[i].dobj), fout); - - /* Conversions do not currently have ACLs. */ - convinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -6497,9 +6401,6 @@ getAccessMethods(Archive *fout, int *numAccessMethods) /* Decide whether we want to dump it */ selectDumpableAccessMethod(&(aminfo[i]), fout); - - /* Access methods do not currently have ACLs. */ - aminfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -6529,7 +6430,7 @@ getOpclasses(Archive *fout, int *numOpclasses) int i_oid; int i_opcname; int i_opcnamespace; - int i_rolname; + int i_opcowner; /* * find all opclasses, including builtin opclasses; we filter out @@ -6538,9 +6439,8 @@ getOpclasses(Archive *fout, int *numOpclasses) appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, " "opcnamespace, " - "(%s opcowner) AS rolname " - "FROM pg_opclass", - username_subquery); + "opcowner " + "FROM pg_opclass"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -6553,7 +6453,7 @@ getOpclasses(Archive *fout, int *numOpclasses) i_oid = PQfnumber(res, "oid"); i_opcname = PQfnumber(res, "opcname"); i_opcnamespace = PQfnumber(res, "opcnamespace"); - i_rolname = PQfnumber(res, "rolname"); + i_opcowner = PQfnumber(res, "opcowner"); for (i = 0; i < ntups; i++) { @@ -6564,17 +6464,10 @@ getOpclasses(Archive *fout, int *numOpclasses) opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname)); opcinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace))); - opcinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner)); /* Decide whether we want to dump it */ selectDumpableObject(&(opcinfo[i].dobj), fout); - - /* Op Classes do not currently have ACLs. */ - opcinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - - if (strlen(opcinfo[i].rolname) == 0) - pg_log_warning("owner of operator class \"%s\" appears to be invalid", - opcinfo[i].dobj.name); } PQclear(res); @@ -6603,14 +6496,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies) int i_oid; int i_opfname; int i_opfnamespace; - int i_rolname; - - /* Before 8.3, there is no separate concept of opfamilies */ - if (fout->remoteVersion < 80300) - { - *numOpfamilies = 0; - return NULL; - } + int i_opfowner; query = createPQExpBuffer(); @@ -6621,9 +6507,8 @@ getOpfamilies(Archive *fout, int *numOpfamilies) appendPQExpBuffer(query, "SELECT tableoid, oid, opfname, " "opfnamespace, " - "(%s opfowner) AS rolname " - "FROM pg_opfamily", - username_subquery); + "opfowner " + "FROM pg_opfamily"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -6636,7 +6521,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies) i_oid = PQfnumber(res, "oid"); i_opfname = PQfnumber(res, "opfname"); i_opfnamespace = PQfnumber(res, "opfnamespace"); - i_rolname = PQfnumber(res, "rolname"); + i_opfowner = PQfnumber(res, "opfowner"); for (i = 0; i < ntups; i++) { @@ -6647,17 +6532,10 @@ getOpfamilies(Archive *fout, int *numOpfamilies) opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname)); opfinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace))); - opfinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner)); /* Decide whether we want to dump it */ selectDumpableObject(&(opfinfo[i].dobj), fout); - - /* Extensions do not currently have ACLs. */ - opfinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - - if (strlen(opfinfo[i].rolname) == 0) - pg_log_warning("owner of operator family \"%s\" appears to be invalid", - opfinfo[i].dobj.name); } PQclear(res); @@ -6689,11 +6567,9 @@ getAggregates(Archive *fout, int *numAggs) int i_aggnamespace; int i_pronargs; int i_proargtypes; - int i_rolname; + int i_proowner; int i_aggacl; - int i_raggacl; - int i_initaggacl; - int i_initraggacl; + int i_acldefault; /* * Find all interesting aggregates. See comment in getFuncs() for the @@ -6701,16 +6577,8 @@ getAggregates(Archive *fout, int *numAggs) */ if (fout->remoteVersion >= 90600) { - PQExpBuffer acl_subquery = createPQExpBuffer(); - PQExpBuffer racl_subquery = createPQExpBuffer(); - PQExpBuffer initacl_subquery = createPQExpBuffer(); - PQExpBuffer initracl_subquery = createPQExpBuffer(); const char *agg_check; - buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, - initracl_subquery, "p.proacl", "p.proowner", "'f'", - dopt->binary_upgrade); - agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'" : "p.proisagg"); @@ -6718,11 +6586,9 @@ getAggregates(Archive *fout, int *numAggs) "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 " + "p.proowner, " + "p.proacl AS aggacl, " + "acldefault('f', p.proowner) AS acldefault " "FROM pg_proc p " "LEFT JOIN pg_init_privs pip ON " "(p.oid = pip.objoid " @@ -6733,11 +6599,6 @@ getAggregates(Archive *fout, int *numAggs) "(SELECT oid FROM pg_namespace " "WHERE nspname = 'pg_catalog') OR " "p.proacl IS DISTINCT FROM pip.initprivs", - username_subquery, - acl_subquery->data, - racl_subquery->data, - initacl_subquery->data, - initracl_subquery->data, agg_check); if (dopt->binary_upgrade) appendPQExpBufferStr(query, @@ -6747,27 +6608,42 @@ getAggregates(Archive *fout, int *numAggs) "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 >= 90200) + { + appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, " + "pronamespace AS aggnamespace, " + "pronargs, proargtypes, " + "proowner, " + "proacl AS aggacl, " + "acldefault('f', proowner) AS acldefault " + "FROM pg_proc p " + "WHERE proisagg AND (" + "pronamespace != " + "(SELECT oid FROM pg_namespace " + "WHERE nspname = 'pg_catalog')"); + 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, ')'); } else { appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, " "pronamespace AS aggnamespace, " "pronargs, proargtypes, " - "(%s proowner) AS rolname, " + "proowner, " "proacl AS aggacl, " - "NULL AS raggacl, " - "NULL AS initaggacl, NULL AS initraggacl " + "NULL AS acldefault " "FROM pg_proc p " "WHERE proisagg AND (" "pronamespace != " "(SELECT oid FROM pg_namespace " - "WHERE nspname = 'pg_catalog')", - username_subquery); + "WHERE nspname = 'pg_catalog')"); if (dopt->binary_upgrade && fout->remoteVersion >= 90100) appendPQExpBufferStr(query, " OR EXISTS(SELECT 1 FROM pg_depend WHERE " @@ -6791,11 +6667,9 @@ getAggregates(Archive *fout, int *numAggs) i_aggnamespace = PQfnumber(res, "aggnamespace"); i_pronargs = PQfnumber(res, "pronargs"); i_proargtypes = PQfnumber(res, "proargtypes"); - i_rolname = PQfnumber(res, "rolname"); + i_proowner = PQfnumber(res, "proowner"); i_aggacl = PQfnumber(res, "aggacl"); - i_raggacl = PQfnumber(res, "raggacl"); - i_initaggacl = PQfnumber(res, "initaggacl"); - i_initraggacl = PQfnumber(res, "initraggacl"); + i_acldefault = PQfnumber(res, "acldefault"); for (i = 0; i < ntups; i++) { @@ -6806,16 +6680,13 @@ getAggregates(Archive *fout, int *numAggs) agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname)); agginfo[i].aggfn.dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace))); - agginfo[i].aggfn.rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); - if (strlen(agginfo[i].aggfn.rolname) == 0) - pg_log_warning("owner of aggregate function \"%s\" appears to be invalid", - agginfo[i].aggfn.dobj.name); + agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl)); + agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + agginfo[i].aggfn.dacl.privtype = 0; + agginfo[i].aggfn.dacl.initprivs = NULL; + agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner)); 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; @@ -6828,13 +6699,11 @@ getAggregates(Archive *fout, int *numAggs) } /* Decide whether we want to dump it */ - selectDumpableObject(&(agginfo[i].aggfn.dobj), fout); + selectDumpableAggregate(&(agginfo[i]), 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; + /* Mark whether aggregate has an ACL */ + if (!PQgetisnull(res, i, i_aggacl)) + agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL; } PQclear(res); @@ -6863,74 +6732,40 @@ getExtProtocols(Archive *fout, int *numExtProtocols) int i_oid; int i_tableoid; int i_ptcname; - int i_rolname; + int i_ptcowner; int i_ptcacl; - int i_ptcracl; - int i_ptcinitacl; - int i_ptcinitracl; + int i_acldefault; int i_ptctrusted; int i_ptcreadid; int i_ptcwriteid; int i_ptcvalidid; /* find all user-defined external protocol */ - - 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, "ptc.ptcacl", "ptc.ptcowner", "'E'", - fout->dopt->binary_upgrade); - - appendPQExpBuffer(query, "SELECT ptc.tableoid as tableoid, " - " ptc.oid as oid, " - " ptc.ptcname as ptcname, " - " ptcreadfn as ptcreadoid, " - " ptcwritefn as ptcwriteoid, " - " ptcvalidatorfn as ptcvaloid, " - " (%s ptc.ptcowner) as rolname, " - " ptc.ptctrusted as ptctrusted, " - " %s AS ptcacl, " - " %s AS ptcracl, " - " %s AS ptcinitacl, " - " %s AS ptcinitracl " - "FROM pg_extprotocol ptc " - "LEFT JOIN pg_init_privs pip ON " - " (ptc.oid = pip.objoid " - " AND pip.classoid = 'pg_extprotocol'::regclass " - " 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); + appendPQExpBuffer(query, "SELECT tableoid, " + "oid, " + "ptcname, " + "ptcreadfn as ptcreadoid, " + "ptcwritefn as ptcwriteoid, " + "ptcvalidatorfn as ptcvaloid, " + "ptcowner, " + "ptctrusted, ptcacl, " + "acldefault('E', ptcowner) AS acldefault " + "FROM pg_extprotocol "); } else { - appendPQExpBuffer(query, "SELECT ptc.tableoid as tableoid, " - " ptc.oid as oid, " - " ptc.ptcname as ptcname, " - " ptcreadfn as ptcreadoid, " - " ptcwritefn as ptcwriteoid, " - " ptcvalidatorfn as ptcvaloid, " - " (%s ptc.ptcowner) as rolname, " - " ptc.ptctrusted as ptctrusted, " - " ptc.ptcacl as ptcacl, " - " NULL as ptcracl, " - " NULL as ptcinitacl, " - " NULL as ptcinitracl " - "FROM pg_extprotocol ptc", - username_subquery); + appendPQExpBuffer(query, "SELECT tableoid, " + "oid, " + "ptcname, " + "ptcreadfn as ptcreadoid, " + "ptcwritefn as ptcwriteoid, " + "ptcvalidatorfn as ptcvaloid, " + "ptcowner, " + "ptctrusted, ptcacl, " + "NULL AS acldefault " + "FROM pg_extprotocol "); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -6943,11 +6778,9 @@ getExtProtocols(Archive *fout, int *numExtProtocols) i_tableoid = PQfnumber(res, "tableoid"); i_oid = PQfnumber(res, "oid"); i_ptcname = PQfnumber(res, "ptcname"); - i_rolname = PQfnumber(res, "rolname"); + i_ptcowner = PQfnumber(res, "ptcowner"); i_ptcacl = PQfnumber(res, "ptcacl"); - i_ptcracl = PQfnumber(res, "ptcracl"); - i_ptcinitacl = PQfnumber(res, "ptcinitacl"); - i_ptcinitracl = PQfnumber(res, "ptcinitracl"); + i_acldefault = PQfnumber(res, "acldefault"); i_ptctrusted = PQfnumber(res, "ptctrusted"); i_ptcreadid = PQfnumber(res, "ptcreadoid"); i_ptcwriteid = PQfnumber(res, "ptcwriteoid"); @@ -6961,10 +6794,7 @@ getExtProtocols(Archive *fout, int *numExtProtocols) AssignDumpId(&ptcinfo[i].dobj); ptcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_ptcname)); ptcinfo[i].dobj.namespace = NULL; - ptcinfo[i].ptcowner = pg_strdup(PQgetvalue(res, i, i_rolname)); - if (strlen(ptcinfo[i].ptcowner) == 0) - pg_log_warning("owner of external protocol \"%s\" appears to be invalid", - ptcinfo[i].dobj.name); + ptcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_ptcowner)); if (PQgetisnull(res, i, i_ptcreadid)) ptcinfo[i].ptcreadid = InvalidOid; @@ -6981,14 +6811,20 @@ getExtProtocols(Archive *fout, int *numExtProtocols) else ptcinfo[i].ptcvalidid = atooid(PQgetvalue(res, i, i_ptcvalidid)); - ptcinfo[i].ptcacl = pg_strdup(PQgetvalue(res, i, i_ptcacl)); - ptcinfo[i].rproacl = pg_strdup(PQgetvalue(res, i, i_ptcracl)); - ptcinfo[i].initproacl = pg_strdup(PQgetvalue(res, i, i_ptcinitacl)); - ptcinfo[i].initrproacl = pg_strdup(PQgetvalue(res, i, i_ptcinitracl)); ptcinfo[i].ptctrusted = *(PQgetvalue(res, i, i_ptctrusted)) == 't'; + ptcinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_ptcacl)); + ptcinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + ptcinfo[i].dacl.privtype = 0; + ptcinfo[i].dacl.initprivs = NULL; + + /* Decide whether we want to dump it */ selectDumpableObject(&(ptcinfo[i].dobj), fout); + + /* Mark whether function has an ACL */ + if (!PQgetisnull(res, i, i_ptcacl)) + ptcinfo[i].dobj.components |= DUMP_COMPONENT_ACL; } PQclear(res); @@ -7018,15 +6854,13 @@ getFuncs(Archive *fout, int *numFuncs) int i_oid; int i_proname; int i_pronamespace; - int i_rolname; + int i_proowner; int i_prolang; int i_pronargs; int i_proargtypes; int i_prorettype; int i_proacl; - int i_rproacl; - int i_initproacl; - int i_initrproacl; + int i_acldefault; /* * Find all interesting functions. This is a bit complicated: @@ -7048,32 +6882,22 @@ getFuncs(Archive *fout, int *numFuncs) * to gather the information about them, though they won't be dumped if * they are built-in. Also, in 9.6 and up, include functions in * pg_catalog if they have an ACL different from what's shown in - * pg_init_privs. + * pg_init_privs (so we have to join to pg_init_privs; annoying). */ if (fout->remoteVersion >= 90600) { - PQExpBuffer acl_subquery = createPQExpBuffer(); - PQExpBuffer racl_subquery = createPQExpBuffer(); - PQExpBuffer initacl_subquery = createPQExpBuffer(); - PQExpBuffer initracl_subquery = createPQExpBuffer(); const char *not_agg_check; - buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, - initracl_subquery, "p.proacl", "p.proowner", "'f'", - dopt->binary_upgrade); - not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'" : "NOT p.proisagg"); 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.proacl, " + "acldefault('f', p.proowner) AS acldefault, " "p.pronamespace, " - "(%s p.proowner) AS rolname " + "p.proowner " "FROM pg_proc p " "LEFT JOIN pg_init_privs pip ON " "(p.oid = pip.objoid " @@ -7094,11 +6918,6 @@ getFuncs(Archive *fout, int *numFuncs) "\n WHERE pg_transform.oid > %u AND " "\n (p.oid = pg_transform.trffromsql" "\n OR p.oid = pg_transform.trftosql))", - acl_subquery->data, - racl_subquery->data, - initacl_subquery->data, - initracl_subquery->data, - username_subquery, not_agg_check, g_last_builtin_oid, g_last_builtin_oid); @@ -7112,24 +6931,23 @@ getFuncs(Archive *fout, int *numFuncs) appendPQExpBufferStr(query, "\n OR p.proacl IS DISTINCT FROM pip.initprivs"); appendPQExpBufferChar(query, ')'); - - destroyPQExpBuffer(acl_subquery); - destroyPQExpBuffer(racl_subquery); - destroyPQExpBuffer(initacl_subquery); - destroyPQExpBuffer(initracl_subquery); } else { + const char *acldefault_call; + + acldefault_call = (fout->remoteVersion >= 90200 ? + "acldefault('f', proowner)" : "NULL"); + appendPQExpBuffer(query, "SELECT tableoid, oid, proname, prolang, " "pronargs, proargtypes, prorettype, proacl, " - "NULL as rproacl, " - "NULL as initproacl, NULL AS initrproacl, " + "%s AS acldefault, " "pronamespace, " - "(%s proowner) AS rolname " + "proowner " "FROM pg_proc p " "WHERE NOT proisagg", - username_subquery); + acldefault_call); if (fout->remoteVersion >= 90200) appendPQExpBufferStr(query, "\n AND NOT EXISTS (SELECT 1 FROM pg_depend " @@ -7155,12 +6973,9 @@ getFuncs(Archive *fout, int *numFuncs) /* * GPDB: Much of the extension machinery was backported into GPDB 5 from higher - * major versions. So include the clause if we are running against GPDB 5. + * major versions, so include the clause. */ -#if 0 - if (dopt->binary_upgrade && fout->remoteVersion >= 90100) -#endif - if (dopt->binary_upgrade && fout->remoteVersion >= 80300) + if (dopt->binary_upgrade) appendPQExpBufferStr(query, "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE " "classid = 'pg_proc'::regclass AND " @@ -7182,15 +6997,13 @@ getFuncs(Archive *fout, int *numFuncs) i_oid = PQfnumber(res, "oid"); i_proname = PQfnumber(res, "proname"); i_pronamespace = PQfnumber(res, "pronamespace"); - i_rolname = PQfnumber(res, "rolname"); + i_proowner = PQfnumber(res, "proowner"); i_prolang = PQfnumber(res, "prolang"); i_pronargs = PQfnumber(res, "pronargs"); 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"); + i_acldefault = PQfnumber(res, "acldefault"); for (i = 0; i < ntups; i++) { @@ -7201,13 +7014,13 @@ getFuncs(Archive *fout, int *numFuncs) finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname)); finfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_pronamespace))); - finfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl)); + finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + finfo[i].dacl.privtype = 0; + finfo[i].dacl.initprivs = NULL; + finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner)); 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; @@ -7219,18 +7032,11 @@ getFuncs(Archive *fout, int *numFuncs) } /* Decide whether we want to dump it */ - selectDumpableFunction(&finfo[i]); - selectDumpableObject(&(finfo[i].dobj), fout); + selectDumpableFunction(&finfo[i], 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) - pg_log_warning("owner of function \"%s\" appears to be invalid", - finfo[i].dobj.name); + /* Mark whether function has an ACL */ + if (!PQgetisnull(res, i, i_proacl)) + finfo[i].dobj.components |= DUMP_COMPONENT_ACL; } PQclear(res); @@ -7262,12 +7068,8 @@ getTables(Archive *fout, int *numTables) int i_relname; int i_relnamespace; int i_relkind; - int i_relstorage; - int i_relacl; - int i_rrelacl; - int i_initrelacl; - int i_initrrelacl; - int i_rolname; + int i_reltype; + int i_relowner; int i_relchecks; int i_relhastriggers; int i_relhasindex; @@ -7291,14 +7093,14 @@ getTables(Archive *fout, int *numTables) int i_toastreloptions; int i_reloftype; int i_relpages; + int i_relstorage; int i_parrelid; int i_parlevel; int i_foreignserver; int i_is_identity_sequence; - int i_changed_acl; - int i_partkeydef; + int i_relacl; + int i_acldefault; int i_ispartition; - int i_partbound; int i_amname; int i_amoid; int i_isivm; @@ -7307,6 +7109,9 @@ getTables(Archive *fout, int *numTables) /* * Find all the tables and table-like objects. * + * We must fetch all tables in this phase because otherwise we cannot + * correctly identify inherited columns, owned sequences, etc. + * * We include system catalogs, so that we can work if a user table is * defined to inherit from a system catalog (pretty weird, but...) * @@ -7320,571 +7125,234 @@ getTables(Archive *fout, int *numTables) * * Note: in this phase we should collect only a minimal amount of * information about each table, basically just enough to decide if it is - * interesting. We must fetch all tables in this phase because otherwise - * we cannot correctly identify inherited columns, owned sequences, etc. - * - * We purposefully ignore toast OIDs for partitioned tables; the reason is - * that versions 10 and 11 have them, but 12 does not, so emitting them - * causes the upgrade to fail. + * interesting. In particular, since we do not yet have lock on any user + * table, we MUST NOT invoke any server-side data collection functions + * (for instance, pg_get_partkeydef()). Those are likely to fail or give + * wrong answers if any concurrent DDL is happening. */ - if (fout->remoteVersion >= 90600) - { - char *partkeydef = "NULL"; - char *ispartition = "false"; - char *partbound = "NULL"; - char *relhasoids = "c.relhasoids"; + appendPQExpBuffer(query, + "SELECT c.tableoid, c.oid, c.relname, " + "c.relnamespace, c.relkind, c.reltype, " + "c.relowner, " + "c.relchecks, " + "c.relhasindex, c.relhasrules, c.relpages, " + "d.refobjid AS owning_tab, " + "d.refobjsubid AS owning_col, " + "tsp.spcname AS reltablespace, " + "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, " + "tc.oid AS toid, "); - PQExpBuffer acl_subquery = createPQExpBuffer(); - PQExpBuffer racl_subquery = createPQExpBuffer(); - PQExpBuffer initacl_subquery = createPQExpBuffer(); - PQExpBuffer initracl_subquery = createPQExpBuffer(); + if (fout->remoteVersion >= 90600) + appendPQExpBufferStr(query, + "'' AS relstorage, "); + else + appendPQExpBufferStr(query, + "c.relstorage, "); - PQExpBuffer attacl_subquery = createPQExpBuffer(); - PQExpBuffer attracl_subquery = createPQExpBuffer(); - PQExpBuffer attinitacl_subquery = createPQExpBuffer(); - PQExpBuffer attinitracl_subquery = createPQExpBuffer(); + if (fout->remoteVersion >= 120000) + appendPQExpBufferStr(query, + "false AS relhasoids, "); + else + appendPQExpBufferStr(query, + "c.relhasoids, "); - /* - * Collect the information about any partitioned tables, which were - * added in PG10. - */ + if (fout->remoteVersion >= 80400) + appendPQExpBufferStr(query, + "c.relhastriggers, "); + else + appendPQExpBufferStr(query, + "(c.reltriggers <> 0) AS relhastriggers, "); - if (fout->remoteVersion >= 100000) - { - partkeydef = "pg_get_partkeydef(c.oid)"; - ispartition = "c.relispartition"; - partbound = "pg_get_expr(c.relpartbound, c.oid)"; - } + if (fout->remoteVersion >= 90100) + appendPQExpBufferStr(query, + "c.relpersistence, "); + else + appendPQExpBufferStr(query, + "'p' AS relpersistence, "); - /* In PG12 upwards WITH OIDS does not exist anymore. */ - if (fout->remoteVersion >= 120000) - relhasoids = "'f'::bool"; + if (fout->remoteVersion >= 90300) + appendPQExpBufferStr(query, + "c.relispopulated, "); + else + appendPQExpBufferStr(query, + "'t' as relispopulated, "); - /* - * 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. - */ + if (fout->remoteVersion >= 90400) + appendPQExpBufferStr(query, + "c.relreplident, "); + else + appendPQExpBufferStr(query, + "'d' AS relreplident, "); - buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, - initracl_subquery, "c.relacl", "c.relowner", - "CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE) - " THEN 's' ELSE 'r' END::\"char\"", - dopt->binary_upgrade); + if (fout->remoteVersion >= 90500) + appendPQExpBufferStr(query, + "c.relrowsecurity, c.relforcerowsecurity, "); + else + appendPQExpBufferStr(query, + "false AS relrowsecurity, " + "false AS relforcerowsecurity, "); - buildACLQueries(attacl_subquery, attracl_subquery, attinitacl_subquery, - attinitracl_subquery, "at.attacl", "c.relowner", "'c'", - dopt->binary_upgrade); + if (fout->remoteVersion >= 90300) + appendPQExpBufferStr(query, + "c.relminmxid, tc.relminmxid AS tminmxid, "); + else + appendPQExpBufferStr(query, + "0 AS relminmxid, 0 AS tminmxid, "); - appendPQExpBuffer(query, - "SELECT c.tableoid, c.oid, c.relname, " - "'' AS relstorage, " - "%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, %s AS 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, am.amname, " - "am.oid AS amoid, " - "CASE WHEN c.relkind = 'f' THEN " - "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) " - "ELSE 0 END AS foreignserver, " - "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, " + if (fout->remoteVersion >= 90300) + appendPQExpBufferStr(query, "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, " - "0 as parrelid, " - "0 as parlevel, " - "c.relkind = '%c' AND EXISTS (SELECT 1 FROM pg_depend WHERE classid = 'pg_class'::regclass AND objid = c.oid AND objsubid = 0 AND refclassid = 'pg_class'::regclass AND deptype = 'i') AS is_identity_sequence, " - "EXISTS (SELECT 1 FROM pg_attribute at LEFT JOIN pg_init_privs pip ON " - "(c.oid = pip.objoid " - "AND pip.classoid = 'pg_class'::regclass " - "AND pip.objsubid = at.attnum)" - "WHERE at.attrelid = c.oid AND (" - "%s IS NOT NULL " - "OR %s IS NOT NULL " - "OR %s IS NOT NULL " - "OR %s IS NOT NULL" - "))" - "AS changed_acl, " - "%s AS partkeydef, " - "%s AS ispartition, " - "%s AS partbound, " - "c.relisivm AS isivm, " - "%s" - "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 IN ('a', 'i')) " - "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid AND c.relkind <> '%c') " - "LEFT JOIN pg_am am ON (c.relam = am.oid) " - "LEFT JOIN pg_init_privs pip ON " - "(c.oid = pip.objoid " - "AND pip.classoid = 'pg_class'::regclass " - "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " - "AND c.relnamespace <> 7012 " /* BM_BITMAPINDEX_NAMESPACE */ - "ORDER BY c.oid", - acl_subquery->data, - racl_subquery->data, - initacl_subquery->data, - initracl_subquery->data, - username_subquery, - relhasoids, - RELKIND_SEQUENCE, - attacl_subquery->data, - attracl_subquery->data, - attinitacl_subquery->data, - attinitracl_subquery->data, - partkeydef, - ispartition, - partbound, - (fout->version.type == Cloudberry && fout->version.version >= 2) ? "c.relisdynamic AS isdynamic " : "false AS isdynamic ", - RELKIND_SEQUENCE, - RELKIND_PARTITIONED_TABLE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, - RELKIND_PARTITIONED_TABLE); + "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "); + else + appendPQExpBufferStr(query, + "c.reloptions, NULL AS checkoption, "); - destroyPQExpBuffer(acl_subquery); - destroyPQExpBuffer(racl_subquery); - destroyPQExpBuffer(initacl_subquery); - destroyPQExpBuffer(initracl_subquery); + if (fout->remoteVersion >= 80400) + appendPQExpBufferStr(query, + "tc.reloptions AS toast_reloptions, "); + else + appendPQExpBufferStr(query, + "NULL AS toast_reloptions, "); - destroyPQExpBuffer(attacl_subquery); - destroyPQExpBuffer(attracl_subquery); - destroyPQExpBuffer(attinitacl_subquery); - destroyPQExpBuffer(attinitracl_subquery); - } - else if (fout->remoteVersion >= 90500) - { - /* - * Left join to pick up dependency info linking sequences to their - * owning column, if any (note this dependency is AUTO as of 8.2) - */ - appendPQExpBuffer(query, - "SELECT c.tableoid, c.oid, c.relname, " - "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, " - "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, " - "NULL AS amname, " - "CASE WHEN c.relkind = 'f' THEN " - "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) " - "ELSE 0 END AS foreignserver, " - "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, " - "p.parrelid as parrelid, " - "pl.parlevel as parlevel, " - "NULL AS changed_acl, " - "NULL AS partkeydef, " - "false AS ispartition, " - "NULL AS partbound " - "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_partition_rule pr ON c.oid = pr.parchildrelid " - "LEFT JOIN pg_partition p ON pr.paroid = p.oid " - "LEFT JOIN pg_partition pl ON (c.oid = pl.parrelid AND pl.parlevel = 0)" - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " - "AND c.relnamespace <> 7012 " /* BM_BITMAPINDEX_NAMESPACE */ - "AND c.oid NOT IN (SELECT p.parchildrelid FROM pg_partition_rule p LEFT " - "JOIN pg_exttable e ON p.parchildrelid=e.reloid WHERE e.reloid IS NULL)" - "ORDER BY c.oid", - username_subquery, - RELKIND_SEQUENCE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); - } - else if (fout->remoteVersion >= 90400) - { - /* - * Left join to pick up dependency info linking sequences to their - * owning column, if any (note this dependency is AUTO as of 8.2) - */ - appendPQExpBuffer(query, - "SELECT c.tableoid, c.oid, c.relname, " - "c.relstorage, " - "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, " - "'f'::bool AS relrowsecurity, " - "'f'::bool AS 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, " - "NULL AS amname, " - "CASE WHEN c.relkind = 'f' THEN " - "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) " - "ELSE 0 END AS foreignserver, " - "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, " - "p.parrelid as parrelid, " - "pl.parlevel as parlevel, " - "NULL AS changed_acl, " - "NULL AS partkeydef, " - "false AS ispartition, " - "NULL AS partbound " - "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 AND c.relstorage <> 'c') " - "LEFT JOIN pg_partition_rule pr ON c.oid = pr.parchildrelid " - "LEFT JOIN pg_partition p ON pr.paroid = p.oid " - "LEFT JOIN pg_partition pl ON (c.oid = pl.parrelid AND pl.parlevel = 0)" - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " - "AND c.relnamespace <> 7012 " /* BM_BITMAPINDEX_NAMESPACE */ - "AND c.oid NOT IN (SELECT p.parchildrelid FROM pg_partition_rule p LEFT " - "JOIN pg_exttable e ON p.parchildrelid=e.reloid WHERE e.reloid IS NULL)" - "ORDER BY c.oid", - username_subquery, - RELKIND_SEQUENCE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); - } - else if (fout->remoteVersion >= 90300) - { - /* - * Left join to pick up dependency info linking sequences to their - * owning column, if any (note this dependency is AUTO as of 8.2) - */ - appendPQExpBuffer(query, - "SELECT c.tableoid, c.oid, c.relname, " - "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, " - "'f'::bool AS relrowsecurity, " - "'f'::bool AS relforcerowsecurity, " - "c.relfrozenxid, c.relminmxid, tc.oid AS toid, " - "tc.relfrozenxid AS tfrozenxid, " - "tc.relminmxid AS tminmxid, " - "c.relpersistence, c.relispopulated, " - "'d' AS relreplident, c.relpages, " - "NULL AS amname, " - "CASE WHEN c.relkind = 'f' THEN " - "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) " - "ELSE 0 END AS foreignserver, " - "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, " + if (fout->remoteVersion >= 90000) + appendPQExpBufferStr(query, + "c.reloftype, "); + else + appendPQExpBufferStr(query, + "0 AS reloftype, "); + + if (fout->remoteVersion >= 90600) + appendPQExpBufferStr(query, + "am.amname, am.oid as amoid, "); + else + appendPQExpBufferStr(query, + "NULL AS amname, NULL as amoid, "); + + if (fout->remoteVersion >= 90600) + appendPQExpBufferStr(query, + "c.relkind = " CppAsString2(RELKIND_SEQUENCE) + "AND EXISTS (SELECT 1 FROM pg_depend " + "WHERE classid = 'pg_class'::regclass AND " + "objid = c.oid AND objsubid = 0 AND " + "refclassid = 'pg_class'::regclass AND " + "deptype = 'i') AS is_identity_sequence, "); + else + appendPQExpBufferStr(query, + "false AS is_identity_sequence, "); + + if (fout->remoteVersion >= 90600) + appendPQExpBufferStr(query, + "0 as parrelid, " + "0 as parlevel, "); + else + appendPQExpBufferStr(query, "p.parrelid as parrelid, " - "pl.parlevel as parlevel, " - "NULL AS changed_acl, " - "NULL AS partkeydef, " - "false AS ispartition, " - "NULL AS partbound " - "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_partition_rule pr ON c.oid = pr.parchildrelid " - "LEFT JOIN pg_partition p ON pr.paroid = p.oid " - "LEFT JOIN pg_partition pl ON (c.oid = pl.parrelid AND pl.parlevel = 0)" - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " - "AND c.relnamespace <> 7012 " /* BM_BITMAPINDEX_NAMESPACE */ - "AND c.oid NOT IN (SELECT p.parchildrelid FROM pg_partition_rule p LEFT " - "JOIN pg_exttable e ON p.parchildrelid=e.reloid WHERE e.reloid IS NULL)" - "ORDER BY c.oid", - username_subquery, - RELKIND_SEQUENCE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); - } - else if (fout->remoteVersion >= 90100) - { - /* - * Left join to pick up dependency info linking sequences to their - * owning column, if any (note this dependency is AUTO as of 8.2) - */ - appendPQExpBuffer(query, - "SELECT c.tableoid, c.oid, c.relname, " - "c.relstorage, " - "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, " - "'f'::bool AS relrowsecurity, " - "'f'::bool AS relforcerowsecurity, " - "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, " - "tc.relfrozenxid AS tfrozenxid, " - "0 AS tminmxid, " - "c.relpersistence, 't' as relispopulated, " - "'d' AS relreplident, c.relpages, " - "NULL AS amname, " + "pl.parlevel as parlevel, "); + + if (fout->remoteVersion >= 90100) + appendPQExpBufferStr(query, "CASE WHEN c.relkind = 'f' THEN " "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) " - "ELSE 0 END AS foreignserver, " - "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, " - "c.reloptions AS reloptions, " - "tc.reloptions AS toast_reloptions, " - "p.parrelid as parrelid, " - "pl.parlevel as parlevel, " - "NULL AS changed_acl, " - "NULL AS partkeydef, " - "false AS ispartition, " - "NULL AS partbound " - "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_partition_rule pr ON c.oid = pr.parchildrelid " - "LEFT JOIN pg_partition p ON pr.paroid = p.oid " - "LEFT JOIN pg_partition pl ON (c.oid = pl.parrelid AND pl.parlevel = 0)" - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " - "AND c.relnamespace <> 7012 " /* BM_BITMAPINDEX_NAMESPACE */ - "AND c.oid NOT IN (SELECT p.parchildrelid FROM pg_partition_rule p LEFT " - "JOIN pg_exttable e ON p.parchildrelid=e.reloid WHERE e.reloid IS NULL)" - "ORDER BY c.oid", - username_subquery, - RELKIND_SEQUENCE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); - } - else if (fout->remoteVersion >= 90000) - { - /* - * Left join to pick up dependency info linking sequences to their - * owning column, if any (note this dependency is AUTO as of 8.2) - */ - appendPQExpBuffer(query, - "SELECT c.tableoid, c.oid, c.relname, " - "c.relstorage, " - "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, " - "'f'::bool AS relrowsecurity, " - "'f'::bool AS relforcerowsecurity, " - "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, " - "tc.relfrozenxid AS tfrozenxid, " - "0 AS tminmxid, " - "'p' AS relpersistence, 't' as relispopulated, " - "'d' AS relreplident, c.relpages, " - "NULL AS amname, " - "NULL AS foreignserver, " - "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, " - "c.reloptions AS reloptions, " - "tc.reloptions AS toast_reloptions, " - "p.parrelid as parrelid, " - "pl.parlevel as parlevel, " - "NULL AS changed_acl, " - "NULL AS partkeydef, " - "false AS ispartition, " - "NULL AS partbound " - "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_partition_rule pr ON c.oid = pr.parchildrelid " - "LEFT JOIN pg_partition p ON pr.paroid = p.oid " - "LEFT JOIN pg_partition pl ON (c.oid = pl.parrelid AND pl.parlevel = 0)" - "WHERE c.relkind in ('%c', '%c', '%c', '%c') " - "AND c.relnamespace <> 7012 " /* BM_BITMAPINDEX_NAMESPACE */ - "AND c.oid NOT IN (SELECT p.parchildrelid FROM pg_partition_rule p LEFT " - "JOIN pg_exttable e ON p.parchildrelid=e.reloid WHERE e.reloid IS NULL)" - "ORDER BY c.oid", - username_subquery, - RELKIND_SEQUENCE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); - } - else if (fout->remoteVersion >= 80400) - { - /* - * Left join to pick up dependency info linking sequences to their - * owning column, if any (note this dependency is AUTO as of 8.2) - */ - appendPQExpBuffer(query, - "SELECT c.tableoid, c.oid, c.relname, " - "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, " - "'f'::bool AS relrowsecurity, " - "'f'::bool AS relforcerowsecurity, " - "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, " - "tc.relfrozenxid AS tfrozenxid, " - "0 AS tminmxid, " - "'p' AS relpersistence, 't' as relispopulated, " - "'d' AS relreplident, c.relpages, " - "NULL AS amname, " - "NULL AS foreignserver, " - "NULL 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, " - "c.reloptions AS reloptions, " - "tc.reloptions AS toast_reloptions, " - "p.parrelid as parrelid, " - "pl.parlevel as parlevel, " - "NULL AS changed_acl, " - "NULL AS partkeydef, " - "false AS ispartition, " - "NULL AS partbound " - "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_partition_rule pr ON c.oid = pr.parchildrelid " - "LEFT JOIN pg_partition p ON pr.paroid = p.oid " - "LEFT JOIN pg_partition pl ON (c.oid = pl.parrelid AND pl.parlevel = 0)" - "WHERE c.relkind in ('%c', '%c', '%c', '%c') " - "AND c.relnamespace <> 7012 " /* BM_BITMAPINDEX_NAMESPACE */ - "AND c.oid NOT IN (SELECT p.parchildrelid FROM pg_partition_rule p LEFT " - "JOIN pg_exttable e ON p.parchildrelid=e.reloid WHERE e.reloid IS NULL)" - "ORDER BY c.oid", - username_subquery, - RELKIND_SEQUENCE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); - } + "ELSE 0 END AS foreignserver, "); else - { - /* - * Left join to pick up dependency info linking sequences to their - * owning column, if any (note this dependency is AUTO as of 8.2) - */ + appendPQExpBufferStr(query, + "NULL AS foreignserver, "); + + if (fout->remoteVersion >= 90600) appendPQExpBuffer(query, - "SELECT c.tableoid, c.oid, c.relname, " - "c.relstorage, " - "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, " - "'f'::bool AS relrowsecurity, " - "'f'::bool AS relforcerowsecurity, " - "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, " - "tc.relfrozenxid AS tfrozenxid, " - "0 AS tminmxid, " - "'p' AS relpersistence, 't' as relispopulated, " - "'d' AS relreplident, c.relpages, " - "NULL AS amname, " - "NULL AS foreignserver, " - "NULL 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, " - "c.reloptions AS reloptions, " - "p.parrelid as parrelid, " - "pl.parlevel as parlevel, " - "NULL AS toast_reloptions, " - "NULL AS changed_acl, " - "NULL AS partkeydef, " - "false AS ispartition, " - "NULL AS partbound " - "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_partition_rule pr ON c.oid = pr.parchildrelid " - "LEFT JOIN pg_partition p ON pr.paroid = p.oid " - "LEFT JOIN pg_partition pl ON (c.oid = pl.parrelid AND pl.parlevel = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c') " - "AND c.oid NOT IN (select p.parchildrelid from pg_partition_rule p " - "LEFT JOIN pg_exttable e on p.parchildrelid=e.reloid where e.reloid is null) " - "AND c.relnamespace <> 3012 " /* BM_BITMAPINDEX_NAMESPACE in GPDB 5 and below */ - "ORDER BY c.oid", - username_subquery, - RELKIND_SEQUENCE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); - } + "c.relisivm AS isivm, %s", + (fout->version.type == Cloudberry && fout->version.version >= 2) ? + "c.relisdynamic AS isdynamic, " : "false AS isdynamic, "); + else + appendPQExpBufferStr(query, "false as isivm, false as isdynamic, "); + + if (fout->remoteVersion >= 90200) + appendPQExpBufferStr(query, + "c.relacl, " + "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE) + " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "); + else + appendPQExpBufferStr(query, + "c.relacl, NULL AS acldefault, "); + + if (fout->remoteVersion >= 100000) + appendPQExpBufferStr(query, + "c.relispartition AS ispartition "); + else + appendPQExpBufferStr(query, + "false AS ispartition "); + + /* + * Left join to pg_depend to pick up dependency info linking sequences to + * their owning column, if any (note this dependency is AUTO as of 8.2). + * Also join to pg_tablespace to collect the spcname. + */ + appendPQExpBufferStr(query, + "\nFROM pg_class c\n" + "LEFT JOIN pg_depend d ON " + "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND " + "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND " + "d.objsubid = 0 AND " + "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n" + "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n"); + + /* + * In 9.6 and up, left join to pg_am to pick up the amname. + */ + if (fout->remoteVersion >= 90600) + appendPQExpBufferStr(query, + "LEFT JOIN pg_am am ON (c.relam = am.oid)\n"); + + /* + * + * We purposefully ignore toast OIDs for partitioned tables; the reason is + * that versions 10 and 11 have them, but later versions do not, so + * emitting them causes the upgrade to fail. + */ + + appendPQExpBufferStr(query, + "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid" + " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE) + " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"); + + if (fout->remoteVersion < GPDB7_MAJOR_PGVERSION) + appendPQExpBufferStr(query, + "LEFT JOIN pg_partition_rule pr ON c.oid = pr.parchildrelid\n" + "LEFT JOIN pg_partition p ON pr.paroid = p.oid\n" + "LEFT JOIN pg_partition pl ON (c.oid = pl.parrelid AND pl.parlevel = 0)\n"); + + /* + * Restrict to interesting relkinds (in particular, not indexes). Not all + * relkinds are possible in older servers, but it's not worth the trouble + * to emit a version-dependent list. + * + * Composite-type table entries won't be dumped as such, but we have to + * make a DumpableObject for them so that we can track dependencies of the + * composite type (pg_depend entries for columns of the composite type + * link to the pg_class entry not the pg_type entry). + */ + appendPQExpBufferStr(query, + "WHERE c.relkind IN (" + CppAsString2(RELKIND_RELATION) ", " + CppAsString2(RELKIND_SEQUENCE) ", " + CppAsString2(RELKIND_VIEW) ", " + CppAsString2(RELKIND_COMPOSITE_TYPE) ", " + CppAsString2(RELKIND_MATVIEW) ", " + CppAsString2(RELKIND_FOREIGN_TABLE) ", " + CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"); + + + if (fout->remoteVersion >= 80400) + appendPQExpBufferStr(query, + "AND c.relnamespace <> 7012\n"); /* BM_BITMAPINDEX_NAMESPACE */ + else + appendPQExpBufferStr(query, + "AND c.relnamespace <> 3012\n"); /* BM_BITMAPINDEX_NAMESPACE in GPDB 5 and below */ + + appendPQExpBufferStr(query, + "ORDER BY c.oid"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -7907,48 +7375,44 @@ getTables(Archive *fout, int *numTables) i_reloid = PQfnumber(res, "oid"); 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_relstorage = PQfnumber(res, "relstorage"); - i_rolname = PQfnumber(res, "rolname"); + i_reltype = PQfnumber(res, "reltype"); + i_relowner = PQfnumber(res, "relowner"); i_relchecks = PQfnumber(res, "relchecks"); - i_relhastriggers = PQfnumber(res, "relhastriggers"); i_relhasindex = PQfnumber(res, "relhasindex"); i_relhasrules = PQfnumber(res, "relhasrules"); - i_relrowsec = PQfnumber(res, "relrowsecurity"); - i_relforcerowsec = PQfnumber(res, "relforcerowsecurity"); - i_relhasoids = PQfnumber(res, "relhasoids"); - i_relfrozenxid = PQfnumber(res, "relfrozenxid"); - i_relminmxid = PQfnumber(res, "relminmxid"); - i_toastoid = PQfnumber(res, "toid"); - i_toastfrozenxid = PQfnumber(res, "tfrozenxid"); - i_toastminmxid = PQfnumber(res, "tminmxid"); - i_relpersistence = PQfnumber(res, "relpersistence"); - i_relispopulated = PQfnumber(res, "relispopulated"); - i_relreplident = PQfnumber(res, "relreplident"); i_relpages = PQfnumber(res, "relpages"); i_foreignserver = PQfnumber(res, "foreignserver"); i_owning_tab = PQfnumber(res, "owning_tab"); i_owning_col = PQfnumber(res, "owning_col"); i_reltablespace = PQfnumber(res, "reltablespace"); + i_relhasoids = PQfnumber(res, "relhasoids"); + i_relhastriggers = PQfnumber(res, "relhastriggers"); + i_relpersistence = PQfnumber(res, "relpersistence"); + i_relispopulated = PQfnumber(res, "relispopulated"); + i_relreplident = PQfnumber(res, "relreplident"); + i_relrowsec = PQfnumber(res, "relrowsecurity"); + i_relforcerowsec = PQfnumber(res, "relforcerowsecurity"); + i_relfrozenxid = PQfnumber(res, "relfrozenxid"); + i_toastfrozenxid = PQfnumber(res, "tfrozenxid"); + i_toastoid = PQfnumber(res, "toid"); + i_relminmxid = PQfnumber(res, "relminmxid"); + i_toastminmxid = PQfnumber(res, "tminmxid"); i_reloptions = PQfnumber(res, "reloptions"); i_checkoption = PQfnumber(res, "checkoption"); i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); - i_parrelid = PQfnumber(res, "parrelid"); - i_parlevel = PQfnumber(res, "parlevel"); + i_amname = PQfnumber(res, "amname"); i_is_identity_sequence = PQfnumber(res, "is_identity_sequence"); - i_changed_acl = PQfnumber(res, "changed_acl"); - i_partkeydef = PQfnumber(res, "partkeydef"); + i_relacl = PQfnumber(res, "relacl"); + i_acldefault = PQfnumber(res, "acldefault"); i_ispartition = PQfnumber(res, "ispartition"); - i_partbound = PQfnumber(res, "partbound"); - i_amname = PQfnumber(res, "amname"); i_amoid = PQfnumber(res, "amoid"); i_isivm = PQfnumber(res, "isivm"); i_isdynamic = PQfnumber(res, "isdynamic"); + i_relstorage = PQfnumber(res, "relstorage"); + i_parrelid = PQfnumber(res, "parrelid"); + i_parlevel = PQfnumber(res, "parlevel"); if (dopt->lockWaitTimeout) { @@ -7976,33 +7440,17 @@ getTables(Archive *fout, int *numTables) tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname)); tblinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_relnamespace))); - 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].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl)); + tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + tblinfo[i].dacl.privtype = 0; + tblinfo[i].dacl.initprivs = NULL; tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind)); - tblinfo[i].relstorage = *(PQgetvalue(res, i, i_relstorage)); - tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence)); + tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype)); + tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner)); + tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks)); tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0); tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0); - tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0); - tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0); - tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0); - tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0); - tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0); - tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident)); tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages)); - tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid)); - tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid)); - tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid)); - tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid)); - tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid)); - if (PQgetisnull(res, i, i_reloftype)) - tblinfo[i].reloftype = NULL; - else - tblinfo[i].reloftype = pg_strdup(PQgetvalue(res, i, i_reloftype)); - tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks)); if (PQgetisnull(res, i, i_owning_tab)) { tblinfo[i].owning_tab = InvalidOid; @@ -8014,14 +7462,42 @@ getTables(Archive *fout, int *numTables) tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col)); } tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace)); + tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0); + tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0); + tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence)); + tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0); + tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident)); + tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0); + tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0); + tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid)); + tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid)); + tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid)); + tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid)); + tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid)); tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions)); - if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption)) + if (PQgetisnull(res, i, i_checkoption)) tblinfo[i].checkoption = NULL; else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); tblinfo[i].parrelid = atooid(PQgetvalue(res, i, i_parrelid)); - if (tblinfo[i].parrelid != 0) + tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype)); + if (PQgetisnull(res, i, i_amname)) + tblinfo[i].amname = NULL; + else + tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname)); + tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0); + tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0); + + tblinfo[i].relstorage = *(PQgetvalue(res, i, i_relstorage)); + + if (PQgetisnull(res, i, i_parlevel) || + atoi(PQgetvalue(res, i, i_parlevel)) > 0) + tblinfo[i].parparent = false; + else + tblinfo[i].parparent = true; + + if (!tblinfo[i].parparent && tblinfo[i].parrelid != 0 && tblinfo[i].relstorage == 'x') { /* * Length of tmpStr is bigger than the sum of NAMEDATALEN @@ -8031,15 +7507,6 @@ getTables(Archive *fout, int *numTables) snprintf(tmpStr, sizeof(tmpStr), "%s%s", tblinfo[i].dobj.name, EXT_PARTITION_NAME_POSTFIX); tblinfo[i].dobj.name = pg_strdup(tmpStr); } - if (PQgetisnull(res, i, i_parlevel) || - atoi(PQgetvalue(res, i, i_parlevel)) > 0) - tblinfo[i].parparent = false; - else - tblinfo[i].parparent = true; - if (PQgetisnull(res, i, i_amname)) - tblinfo[i].amname = NULL; - else - tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname)); if (PQgetisnull(res, i, i_amoid)) tblinfo[i].amoid = InvalidOid; @@ -8049,45 +7516,52 @@ getTables(Archive *fout, int *numTables) /* other fields were zeroed above */ /* - * Decide whether we want to dump this table. + * GPDB5/6: To gather binary upgrade information for GP partition children, + * they need to exist in the tblinfo array for findTableByOid. + * This requires the getTable query to also collect all + * partition children so they can be referenced, but we do not want + * to dump the partition children as their DDL will be handled by the parent. */ - if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE) + if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE || tblinfo[i].parrelid != 0) tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE; else selectDumpableTable(&tblinfo[i], fout); /* - * If the table-level and all column-level ACLs for this table are - * unchanged, then we don't need to worry about including the ACLs for - * this table. If any column-level ACLs have been changed, the - * 'changed_acl' column from the query will indicate that. + * Now, consider the table "interesting" if we need to dump its + * definition or its data. Later on, we'll skip a lot of data + * collection for uninteresting tables. * - * This can result in a significant performance improvement in cases - * where we are only looking to dump out the ACL (eg: pg_catalog). + * Note: the "interesting" flag will also be set by flagInhTables for + * parents of interesting tables, so that we collect necessary + * inheritance info even when the parents are not themselves being + * dumped. This is the main reason why we need an "interesting" flag + * that's separate from the components-to-dump bitmask. */ - if (PQgetisnull(res, i, i_relacl) && PQgetisnull(res, i, i_rrelacl) && - PQgetisnull(res, i, i_initrelacl) && - PQgetisnull(res, i, i_initrrelacl) && - strcmp(PQgetvalue(res, i, i_changed_acl), "f") == 0) - tblinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + tblinfo[i].interesting = (tblinfo[i].dobj.dump & + (DUMP_COMPONENT_DEFINITION | + DUMP_COMPONENT_DATA)) != 0; - tblinfo[i].interesting = tblinfo[i].dobj.dump ? true : false; tblinfo[i].dummy_view = false; /* might get set during sort */ tblinfo[i].postponed_def = false; /* might get set during sort */ - + tblinfo[i].is_identity_sequence = (i_is_identity_sequence >= 0 && strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0); - /* Partition key string or NULL */ - tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); - tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0); - tblinfo[i].partbound = pg_strdup(PQgetvalue(res, i, i_partbound)); tblinfo[i].isivm = (strcmp(PQgetvalue(res, i, i_isivm), "t") == 0); tblinfo[i].isdynamic = (strcmp(PQgetvalue(res, i, i_isdynamic), "t") == 0); /* foreign server */ tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver)); + /* Tables have data */ + tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA; + + /* Mark whether table has an ACL */ + if (!PQgetisnull(res, i, i_relacl)) + tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL; + tblinfo[i].hascolumnACLs = false; /* may get set later */ + /* * Read-lock target tables to make sure they aren't DROPPED or altered * in schema before we get around to dumping them. @@ -8102,18 +7576,17 @@ getTables(Archive *fout, int *numTables) * * We only need to lock the table for certain components; see * pg_dump.h - * + * * GPDB: Build a single LOCK TABLE statement to lock all interesting tables. * This is more performant than issuing a separate LOCK TABLE statement for each table, * with considerable savings in FE/BE overhead. It does come at the cost of some increased * memory usage in both FE and BE, which we will be able to tolerate. */ + /* GPDB_14_MERGE_FIXME: GPDB_96_MERGE_FIXME: Is the parrelid check still needed? */ - if (tblinfo[i].dobj.dump && + if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) && (tblinfo[i].relkind == RELKIND_RELATION || - tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE) && - tblinfo[i].parrelid == 0 && - (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) + tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE)) { if (!lockTableDumped) appendPQExpBuffer(query, @@ -8125,11 +7598,6 @@ getTables(Archive *fout, int *numTables) fmtQualifiedDumpable(&tblinfo[i])); lockTableDumped = true; } - - /* Emit notice if join for owner failed */ - if (strlen(tblinfo[i].rolname) == 0) - pg_log_warning("owner of table \"%s\" appears to be invalid", - tblinfo[i].dobj.name); } /* Are there any tables to lock? */ if (lockTableDumped) @@ -8292,43 +7760,126 @@ getPartitioningInfo(Archive *fout) if (fout->dopt->schemaOnly) return; - query = createPQExpBuffer(); + query = createPQExpBuffer(); + + /* + * Unsafe partitioning schemes are exactly those for which hash enum_ops + * appears among the partition opclasses. We needn't check partstrat. + * + * Note that this query may well retrieve info about tables we aren't + * going to dump and hence have no lock on. That's okay since we need not + * invoke any unsafe server-side functions. + */ + appendPQExpBufferStr(query, + "SELECT partrelid FROM pg_partitioned_table WHERE\n" + "(SELECT c.oid FROM pg_opclass c JOIN pg_am a " + "ON c.opcmethod = a.oid\n" + "WHERE opcname = 'enum_ops' " + "AND opcnamespace = 'pg_catalog'::regnamespace " + "AND amname = 'hash') = ANY(partclass)"); + + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + + for (int i = 0; i < ntups; i++) + { + Oid tabrelid = atooid(PQgetvalue(res, i, 0)); + TableInfo *tbinfo; + + tbinfo = findTableByOid(tabrelid); + if (tbinfo == NULL) + fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found", + tabrelid); + tbinfo->unsafe_partitions = true; + } + + PQclear(res); + + destroyPQExpBuffer(query); +} + +/* + * getPartitionDefs + * get information about GPDB partition definitions on a dumpable table + */ + +void +getPartitionDefs(Archive *fout, TableInfo tblinfo[], int numTables) +{ + + PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer tbloids = createPQExpBuffer(); + PGresult *res; + int ntups; + int i_oid; + int i_partclause; + int i_parttemplate; + + /* Only relevant for GP5/GP6 */ + if (fout->remoteVersion > GPDB6_MAJOR_PGVERSION) + return; + + /* + * We want to perform just one query against pg_class. + * However, we mustn't try to select every row of those catalogs and then + * sort it out on the client side, because some of the server-side functions + * we need would be unsafe to apply to tables we don't have lock on. + * Hence, we build an array of the OIDs of tables we care about + * (and now have lock on!), and use a WHERE clause to constrain which rows are selected. + */ + appendPQExpBufferChar(tbloids, '{'); + for (int i = 0; i < numTables; i++) + { + TableInfo *tbinfo = &tblinfo[i]; + + /* We're only interested in dumping the partition definition for parent partitions */ + if (!tbinfo->parparent) + continue; + + /* + * We can ignore uninteresting tables, i.e. tables that will not be dumped. + */ + if (!tbinfo->interesting) + continue; + + /* OK, we need info for this table */ + if (tbloids->len > 1) /* do we have more than the '{'? */ + appendPQExpBufferChar(tbloids, ','); + appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid); + } - /* - * Unsafe partitioning schemes are exactly those for which hash enum_ops - * appears among the partition opclasses. We needn't check partstrat. - * - * Note that this query may well retrieve info about tables we aren't - * going to dump and hence have no lock on. That's okay since we need not - * invoke any unsafe server-side functions. - */ - appendPQExpBufferStr(query, - "SELECT partrelid FROM pg_partitioned_table WHERE\n" - "(SELECT c.oid FROM pg_opclass c JOIN pg_am a " - "ON c.opcmethod = a.oid\n" - "WHERE opcname = 'enum_ops' " - "AND opcnamespace = 'pg_catalog'::regnamespace " - "AND amname = 'hash') = ANY(partclass)"); + appendPQExpBufferChar(tbloids, '}'); + resetPQExpBuffer(query); + + appendPQExpBuffer(query, + "SELECT src.oid,\n" + "(SELECT pg_get_partition_def(src.oid, true, true)) AS partclause,\n" + "(SELECT pg_get_partition_template_def(src.oid, true, true)) AS parttemplate\n" + "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n", tbloids->data); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); ntups = PQntuples(res); + + i_oid = PQfnumber(res, "oid"); + i_partclause = PQfnumber(res, "partclause"); + i_parttemplate = PQfnumber(res, "parttemplate"); for (int i = 0; i < ntups; i++) { - Oid tabrelid = atooid(PQgetvalue(res, i, 0)); - TableInfo *tbinfo; + TableInfo *tbinfo = findTableByOid(atooid(PQgetvalue(res, i, i_oid))); + if (tblinfo) + { + tbinfo->partclause = pg_strdup(PQgetvalue(res, i, i_partclause)); + tbinfo->parttemplate = pg_strdup(PQgetvalue(res, i, i_parttemplate)); + } - tbinfo = findTableByOid(tabrelid); - if (tbinfo == NULL) - fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found", - tabrelid); - tbinfo->unsafe_partitions = true; } - PQclear(res); destroyPQExpBuffer(query); + destroyPQExpBuffer(tbloids); } /* @@ -8341,13 +7892,15 @@ getPartitioningInfo(Archive *fout) void getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) { - int i, - j; PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer tbloids = createPQExpBuffer(); PGresult *res; + int ntups; + int curtblindx; IndxInfo *indxinfo; int i_tableoid, i_oid, + i_indrelid, i_indexname, i_parentidx, i_indexdef, @@ -8367,9 +7920,17 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) i_indreloptions, i_indstatcols, i_indstatvals; - int ntups; - for (i = 0; i < numTables; i++) + /* + * We want to perform just one query against pg_index. However, we + * mustn't try to select every row of the catalog and then sort it out on + * the client side, because some of the server-side functions we need + * would be unsafe to apply to tables we don't have lock on. Hence, we + * build an array of the OIDs of tables we care about (and now have lock + * on!), and use a WHERE clause to constrain which rows are selected. + */ + appendPQExpBufferChar(tbloids, '{'); + for (int i = 0; i < numTables; i++) { TableInfo *tbinfo = &tblinfo[i]; @@ -8377,208 +7938,187 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) continue; /* - * Ignore indexes of tables whose definitions are not to be dumped. - * - * We also need indexes on partitioned tables which have partitions to - * be dumped, in order to dump the indexes on the partitions. + * We can ignore indexes of uninteresting tables. */ - if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) && - !tbinfo->interesting) + if (!tbinfo->interesting) continue; - pg_log_info("reading indexes for table \"%s.%s\"", - tbinfo->dobj.namespace->dobj.name, - tbinfo->dobj.name); + /* OK, we need info for this table */ + if (tbloids->len > 1) /* do we have more than the '{'? */ + appendPQExpBufferChar(tbloids, ','); + appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid); + } + appendPQExpBufferChar(tbloids, '}'); + + resetPQExpBuffer(query); + + appendPQExpBuffer(query, + "SELECT t.tableoid, t.oid, i.indrelid, " + "t.relname AS indexname, " + "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " + "i.indkey, i.indisclustered, " + "c.contype, c.conname, " + "c.condeferrable, c.condeferred, " + "c.tableoid AS contableoid, " + "c.oid AS conoid, " + "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, " + "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, " + "t.reloptions AS indreloptions, "); + + + if (fout->remoteVersion >= 90400) + appendPQExpBuffer(query, + "i.indisreplident, "); + else + appendPQExpBuffer(query, + "false AS indisreplident, "); + + if (fout->remoteVersion >= 110000) + appendPQExpBuffer(query, + "inh.inhparent AS parentidx, " + "i.indnkeyatts AS indnkeyatts, " + "i.indnatts AS indnatts, " + "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) " + " FROM pg_catalog.pg_attribute " + " WHERE attrelid = i.indexrelid AND " + " attstattarget >= 0) AS indstatcols, " + "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) " + " FROM pg_catalog.pg_attribute " + " WHERE attrelid = i.indexrelid AND " + " attstattarget >= 0) AS indstatvals "); + else + appendPQExpBuffer(query, + "0 AS parentidx, " + "i.indnatts AS indnkeyatts, " + "i.indnatts AS indnatts, " + "'' AS indstatcols, " + "'' AS indstatvals "); + appendPQExpBuffer(query, + "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n" + "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) " + "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) ", + tbloids->data); + /* + * The point of the messy-looking outer join is to find a constraint that + * is related by an internal dependency link to the index. If we find one, + * create a CONSTRAINT entry linked to the INDEX entry. We assume an + * index won't have more than one internal dependency. + * + * As of 9.0 we don't need to look at pg_depend but can check for a match + * to pg_constraint.conindid. The check on conrelid is redundant but + * useful because that column is indexed while conindid is not. + */ + if (fout->remoteVersion >= 110000) + { + appendPQExpBuffer(query, + "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) " + "LEFT JOIN pg_catalog.pg_constraint c " + "ON (i.indrelid = c.conrelid AND " + "i.indexrelid = c.conindid AND " + "c.contype IN ('p','u','x')) " + "LEFT JOIN pg_catalog.pg_inherits inh " + "ON (inh.inhrelid = indexrelid) " + "WHERE (i.indisvalid OR t2.relkind = 'p') " + "AND i.indisready " + "ORDER BY i.indrelid, indexname"); + } + else if (fout->remoteVersion >= 90400) + { /* - * The point of the messy-looking outer join is to find a constraint - * that is related by an internal dependency link to the index. If we - * find one, create a CONSTRAINT entry linked to the INDEX entry. We - * assume an index won't have more than one internal dependency. - * - * As of 9.0 we don't need to look at pg_depend but can check for a - * match to pg_constraint.conindid. The check on conrelid is - * redundant but useful because that column is indexed while conindid - * is not. + * the test on indisready is necessary in 9.2, and harmless in + * earlier/later versions */ - resetPQExpBuffer(query); - if (fout->remoteVersion >= 110000) - { - appendPQExpBuffer(query, - "SELECT t.tableoid, t.oid, " - "t.relname AS indexname, " - "inh.inhparent AS parentidx, " - "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " - "i.indnkeyatts AS indnkeyatts, " - "i.indnatts AS indnatts, " - "i.indkey, i.indisclustered, " - "i.indisreplident, " - "c.contype, c.conname, " - "c.condeferrable, c.condeferred, " - "c.tableoid AS contableoid, " - "c.oid AS conoid, " - "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, " - "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, " - "t.reloptions AS indreloptions, " - "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) " - " FROM pg_catalog.pg_attribute " - " WHERE attrelid = i.indexrelid AND " - " attstattarget >= 0) AS indstatcols," - "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) " - " FROM pg_catalog.pg_attribute " - " WHERE attrelid = i.indexrelid AND " - " attstattarget >= 0) AS indstatvals " - "FROM pg_catalog.pg_index i " - "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) " - "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) " - "LEFT JOIN pg_catalog.pg_constraint c " - "ON (i.indrelid = c.conrelid AND " - "i.indexrelid = c.conindid AND " - "c.contype IN ('p','u','x')) " - "LEFT JOIN pg_catalog.pg_inherits inh " - "ON (inh.inhrelid = indexrelid) " - "WHERE i.indrelid = '%u'::pg_catalog.oid " - "AND (i.indisvalid OR t2.relkind = 'p') " - "AND i.indisready " - "ORDER BY indexname", - tbinfo->dobj.catId.oid); - } - else if (fout->remoteVersion >= 90400) - { - /* - * the test on indisready is necessary in 9.2, and harmless in - * earlier/later versions - */ - appendPQExpBuffer(query, - "SELECT t.tableoid, t.oid, " - "t.relname AS indexname, " - "0 AS parentidx, " - "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " - "i.indnatts AS indnkeyatts, " - "i.indnatts AS indnatts, " - "i.indkey, i.indisclustered, " - "i.indisreplident, " - "c.contype, c.conname, " - "c.condeferrable, c.condeferred, " - "c.tableoid AS contableoid, " - "c.oid AS conoid, " - "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, " - "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, " - "t.reloptions AS indreloptions, " - "'' AS indstatcols, " - "'' AS indstatvals " - "FROM pg_catalog.pg_index i " - "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) " - "LEFT JOIN pg_catalog.pg_constraint c " - "ON (i.indrelid = c.conrelid AND " - "i.indexrelid = c.conindid AND " - "c.contype IN ('p','u','x')) " - "WHERE i.indrelid = '%u'::pg_catalog.oid " - "AND i.indisvalid AND i.indisready " - "ORDER BY indexname", - tbinfo->dobj.catId.oid); - } - else if (fout->remoteVersion >= 90000) - { - /* - * the test on indisready is necessary in 9.2, and harmless in - * earlier/later versions - */ - appendPQExpBuffer(query, - "SELECT t.tableoid, t.oid, " - "t.relname AS indexname, " - "0 AS parentidx, " - "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " - "i.indnatts AS indnkeyatts, " - "i.indnatts AS indnatts, " - "i.indkey, i.indisclustered, " - "false AS indisreplident, " - "c.contype, c.conname, " - "c.condeferrable, c.condeferred, " - "c.tableoid AS contableoid, " - "c.oid AS conoid, " - "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, " - "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, " - "t.reloptions AS indreloptions, " - "'' AS indstatcols, " - "'' AS indstatvals " - "FROM pg_catalog.pg_index i " - "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) " - "LEFT JOIN pg_catalog.pg_constraint c " - "ON (i.indrelid = c.conrelid AND " - "i.indexrelid = c.conindid AND " - "c.contype IN ('p','u','x')) " - "WHERE i.indrelid = '%u'::pg_catalog.oid " - "AND i.indisvalid AND i.indisready " - "ORDER BY indexname", - tbinfo->dobj.catId.oid); - } - else - { - appendPQExpBuffer(query, - "SELECT t.tableoid, t.oid, " - "t.relname AS indexname, " - "0 AS parentidx, " - "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " - "i.indnatts AS indnkeyatts, " - "i.indnatts AS indnatts, " - "i.indkey, i.indisclustered, " - "false AS indisreplident, " - "c.contype, c.conname, " - "c.condeferrable, c.condeferred, " - "c.tableoid AS contableoid, " - "c.oid AS conoid, " - "null AS condef, " - "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, " - "t.reloptions AS indreloptions, " - "'' AS indstatcols, " - "'' AS indstatvals " - "FROM pg_catalog.pg_index i " - "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) " - "LEFT JOIN pg_catalog.pg_depend d " - "ON (d.classid = t.tableoid " - "AND d.objid = t.oid " - "AND d.deptype = 'i') " - "LEFT JOIN pg_catalog.pg_constraint c " - "ON (d.refclassid = c.tableoid " - "AND d.refobjid = c.oid) " - "WHERE i.indrelid = '%u'::pg_catalog.oid " - "AND i.indisvalid " - "ORDER BY indexname", - tbinfo->dobj.catId.oid); - } + appendPQExpBuffer(query, + "LEFT JOIN pg_catalog.pg_constraint c " + "ON (i.indrelid = c.conrelid AND " + "i.indexrelid = c.conindid AND " + "c.contype IN ('p','u','x')) " + "WHERE i.indisvalid AND i.indisready " + "ORDER BY i.indrelid, indexname"); + } + else + { + appendPQExpBuffer(query, + "LEFT JOIN pg_catalog.pg_depend d " + "ON (d.classid = t.tableoid " + "AND d.objid = t.oid " + "AND d.deptype = 'i') " + "LEFT JOIN pg_catalog.pg_constraint c " + "ON (d.refclassid = c.tableoid " + "AND d.refobjid = c.oid) " + "WHERE i.indisvalid " + "ORDER BY i.indrelid, indexname"); + } - res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); - ntups = PQntuples(res); + ntups = PQntuples(res); - i_tableoid = PQfnumber(res, "tableoid"); - i_oid = PQfnumber(res, "oid"); - i_indexname = PQfnumber(res, "indexname"); - i_parentidx = PQfnumber(res, "parentidx"); - i_indexdef = PQfnumber(res, "indexdef"); - i_indnkeyatts = PQfnumber(res, "indnkeyatts"); - i_indnatts = PQfnumber(res, "indnatts"); - i_indkey = PQfnumber(res, "indkey"); - i_indisclustered = PQfnumber(res, "indisclustered"); - i_indisreplident = PQfnumber(res, "indisreplident"); - i_contype = PQfnumber(res, "contype"); - i_conname = PQfnumber(res, "conname"); - i_condeferrable = PQfnumber(res, "condeferrable"); - i_condeferred = PQfnumber(res, "condeferred"); - i_contableoid = PQfnumber(res, "contableoid"); - i_conoid = PQfnumber(res, "conoid"); - i_condef = PQfnumber(res, "condef"); - i_tablespace = PQfnumber(res, "tablespace"); - i_indreloptions = PQfnumber(res, "indreloptions"); - i_indstatcols = PQfnumber(res, "indstatcols"); - i_indstatvals = PQfnumber(res, "indstatvals"); - - tbinfo->indexes = indxinfo = - (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo)); - tbinfo->numIndexes = ntups; + i_tableoid = PQfnumber(res, "tableoid"); + i_oid = PQfnumber(res, "oid"); + i_indrelid = PQfnumber(res, "indrelid"); + i_indexname = PQfnumber(res, "indexname"); + i_parentidx = PQfnumber(res, "parentidx"); + i_indexdef = PQfnumber(res, "indexdef"); + i_indnkeyatts = PQfnumber(res, "indnkeyatts"); + i_indnatts = PQfnumber(res, "indnatts"); + i_indkey = PQfnumber(res, "indkey"); + i_indisclustered = PQfnumber(res, "indisclustered"); + i_indisreplident = PQfnumber(res, "indisreplident"); + i_contype = PQfnumber(res, "contype"); + i_conname = PQfnumber(res, "conname"); + i_condeferrable = PQfnumber(res, "condeferrable"); + i_condeferred = PQfnumber(res, "condeferred"); + i_contableoid = PQfnumber(res, "contableoid"); + i_conoid = PQfnumber(res, "conoid"); + i_condef = PQfnumber(res, "condef"); + i_tablespace = PQfnumber(res, "tablespace"); + i_indreloptions = PQfnumber(res, "indreloptions"); + i_indstatcols = PQfnumber(res, "indstatcols"); + i_indstatvals = PQfnumber(res, "indstatvals"); - for (j = 0; j < ntups; j++) + indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo)); + + /* + * Outer loop iterates once per table, not once per row. Incrementing of + * j is handled by the inner loop. + */ + curtblindx = -1; + for (int j = 0; j < ntups;) + { + Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid)); + TableInfo *tbinfo = NULL; + int numinds; + + /* Count rows for this table */ + for (numinds = 1; numinds < ntups - j; numinds++) + if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid) + break; + + /* + * Locate the associated TableInfo; we rely on tblinfo[] being in OID + * order. + */ + while (++curtblindx < numTables) + { + tbinfo = &tblinfo[curtblindx]; + if (tbinfo->dobj.catId.oid == indrelid) + break; + } + if (curtblindx >= numTables) + fatal("unrecognized table OID %u", indrelid); + /* cross-check that we only got requested tables */ + if (!tbinfo->hasindex || + !tbinfo->interesting) + fatal("unexpected index data for table \"%s\"", + tbinfo->dobj.name); + + /* Save data for this table */ + tbinfo->indexes = indxinfo + j; + tbinfo->numIndexes = numinds; + + for (int c = 0; c < numinds; c++, j++) { char contype; @@ -8647,11 +8187,12 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) indxinfo[j].indexconstraint = 0; } } - - PQclear(res); } + PQclear(res); + destroyPQExpBuffer(query); + destroyPQExpBuffer(tbloids); } /* @@ -8672,7 +8213,7 @@ getExtendedStatistics(Archive *fout) int i_oid; int i_stxname; int i_stxnamespace; - int i_rolname; + int i_stxowner; int i_stattarget; int i; @@ -8684,14 +8225,13 @@ getExtendedStatistics(Archive *fout) if (fout->remoteVersion < 130000) appendPQExpBuffer(query, "SELECT tableoid, oid, stxname, " - "stxnamespace, (%s stxowner) AS rolname, (-1) AS stxstattarget " - "FROM pg_catalog.pg_statistic_ext", - username_subquery); + "stxnamespace, stxowner, (-1) AS stxstattarget " + "FROM pg_catalog.pg_statistic_ext"); else appendPQExpBuffer(query, "SELECT tableoid, oid, stxname, " - "stxnamespace, (%s stxowner) AS rolname, stxstattarget " - "FROM pg_catalog.pg_statistic_ext", - username_subquery); + "stxnamespace, stxowner, stxstattarget " + "FROM pg_catalog.pg_statistic_ext"); + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -8701,7 +8241,7 @@ getExtendedStatistics(Archive *fout) i_oid = PQfnumber(res, "oid"); i_stxname = PQfnumber(res, "stxname"); i_stxnamespace = PQfnumber(res, "stxnamespace"); - i_rolname = PQfnumber(res, "rolname"); + i_stxowner = PQfnumber(res, "stxowner"); i_stattarget = PQfnumber(res, "stxstattarget"); statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo)); @@ -8715,14 +8255,11 @@ getExtendedStatistics(Archive *fout) statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname)); statsextinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace))); - statsextinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner)); statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget)); /* Decide whether we want to dump it */ selectDumpableObject(&(statsextinfo[i].dobj), fout); - - /* Stats objects do not currently have ACLs. */ - statsextinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -8741,22 +8278,31 @@ getExtendedStatistics(Archive *fout) void getConstraints(Archive *fout, TableInfo tblinfo[], int numTables) { - int i, - j; - ConstraintInfo *constrinfo; - PQExpBuffer query; + PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer tbloids = createPQExpBuffer(); PGresult *res; + int ntups; + int curtblindx; + TableInfo *tbinfo = NULL; + ConstraintInfo *constrinfo; int i_contableoid, i_conoid, + i_conrelid, i_conname, i_confrelid, i_conindid, i_condef; - int ntups; - - query = createPQExpBuffer(); - for (i = 0; i < numTables; i++) + /* + * We want to perform just one query against pg_constraint. However, we + * mustn't try to select every row of the catalog and then sort it out on + * the client side, because some of the server-side functions we need + * would be unsafe to apply to tables we don't have lock on. Hence, we + * build an array of the OIDs of tables we care about (and now have lock + * on!), and use a WHERE clause to constrain which rows are selected. + */ + appendPQExpBufferChar(tbloids, '{'); + for (int i = 0; i < numTables; i++) { TableInfo *tbinfo = &tblinfo[i]; @@ -8769,95 +8315,118 @@ getConstraints(Archive *fout, TableInfo tblinfo[], int numTables) !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) continue; - pg_log_info("reading foreign key constraints for table \"%s.%s\"", - tbinfo->dobj.namespace->dobj.name, - tbinfo->dobj.name); + /* OK, we need info for this table */ + if (tbloids->len > 1) /* do we have more than the '{'? */ + appendPQExpBufferChar(tbloids, ','); + appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid); + } + appendPQExpBufferChar(tbloids, '}'); - resetPQExpBuffer(query); - if (fout->remoteVersion >= 110000) - appendPQExpBuffer(query, - "SELECT tableoid, oid, conname, confrelid, conindid, " - "pg_catalog.pg_get_constraintdef(oid) AS condef " - "FROM pg_catalog.pg_constraint " - "WHERE conrelid = '%u'::pg_catalog.oid " - "AND conparentid = 0 " - "AND contype = 'f'", - tbinfo->dobj.catId.oid); - else - appendPQExpBuffer(query, - "SELECT tableoid, oid, conname, confrelid, 0 as conindid, " - "pg_catalog.pg_get_constraintdef(oid) AS condef " - "FROM pg_catalog.pg_constraint " - "WHERE conrelid = '%u'::pg_catalog.oid " - "AND contype = 'f'", - tbinfo->dobj.catId.oid); - res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + appendPQExpBufferStr(query, + "SELECT c.tableoid, c.oid, " + "conrelid, conname, confrelid, "); + if (fout->remoteVersion >= 110000) + appendPQExpBufferStr(query, "conindid, "); + else + appendPQExpBufferStr(query, "0 AS conindid, "); + appendPQExpBuffer(query, + "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n" + "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n" + "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n" + "WHERE contype = 'f' ", + tbloids->data); + if (fout->remoteVersion >= 110000) + appendPQExpBufferStr(query, + "AND conparentid = 0 "); + appendPQExpBufferStr(query, + "ORDER BY conrelid, conname"); - ntups = PQntuples(res); + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); - i_contableoid = PQfnumber(res, "tableoid"); - i_conoid = PQfnumber(res, "oid"); - i_conname = PQfnumber(res, "conname"); - i_confrelid = PQfnumber(res, "confrelid"); - i_conindid = PQfnumber(res, "conindid"); - i_condef = PQfnumber(res, "condef"); + ntups = PQntuples(res); + + i_contableoid = PQfnumber(res, "tableoid"); + i_conoid = PQfnumber(res, "oid"); + i_conrelid = PQfnumber(res, "conrelid"); + i_conname = PQfnumber(res, "conname"); + i_confrelid = PQfnumber(res, "confrelid"); + i_conindid = PQfnumber(res, "conindid"); + i_condef = PQfnumber(res, "condef"); - constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo)); + constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo)); - for (j = 0; j < ntups; j++) - { - TableInfo *reftable; - - constrinfo[j].dobj.objType = DO_FK_CONSTRAINT; - constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid)); - constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid)); - AssignDumpId(&constrinfo[j].dobj); - constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname)); - constrinfo[j].dobj.namespace = tbinfo->dobj.namespace; - constrinfo[j].contable = tbinfo; - constrinfo[j].condomain = NULL; - constrinfo[j].contype = 'f'; - constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef)); - constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid)); - constrinfo[j].conindex = 0; - constrinfo[j].condeferrable = false; - constrinfo[j].condeferred = false; - constrinfo[j].conislocal = true; - constrinfo[j].separate = true; + curtblindx = -1; + for (int j = 0; j < ntups; j++) + { + Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid)); + TableInfo *reftable; - /* - * Restoring an FK that points to a partitioned table requires - * that all partition indexes have been attached beforehand. - * Ensure that happens by making the constraint depend on each - * index partition attach object. - */ - reftable = findTableByOid(constrinfo[j].confrelid); - if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE) + /* + * Locate the associated TableInfo; we rely on tblinfo[] being in OID + * order. + */ + if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid) + { + while (++curtblindx < numTables) { - Oid indexOid = atooid(PQgetvalue(res, j, i_conindid)); + tbinfo = &tblinfo[curtblindx]; + if (tbinfo->dobj.catId.oid == conrelid) + break; + } + if (curtblindx >= numTables) + fatal("unrecognized table OID %u", conrelid); + } + + constrinfo[j].dobj.objType = DO_FK_CONSTRAINT; + constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid)); + constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid)); + AssignDumpId(&constrinfo[j].dobj); + constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname)); + constrinfo[j].dobj.namespace = tbinfo->dobj.namespace; + constrinfo[j].contable = tbinfo; + constrinfo[j].condomain = NULL; + constrinfo[j].contype = 'f'; + constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef)); + constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid)); + constrinfo[j].conindex = 0; + constrinfo[j].condeferrable = false; + constrinfo[j].condeferred = false; + constrinfo[j].conislocal = true; + constrinfo[j].separate = true; + + /* + * Restoring an FK that points to a partitioned table requires that + * all partition indexes have been attached beforehand. Ensure that + * happens by making the constraint depend on each index partition + * attach object. + */ + reftable = findTableByOid(constrinfo[j].confrelid); + if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE) + { + Oid indexOid = atooid(PQgetvalue(res, j, i_conindid)); - if (indexOid != InvalidOid) + if (indexOid != InvalidOid) + { + for (int k = 0; k < reftable->numIndexes; k++) { - for (int k = 0; k < reftable->numIndexes; k++) - { - IndxInfo *refidx; + IndxInfo *refidx; - /* not our index? */ - if (reftable->indexes[k].dobj.catId.oid != indexOid) - continue; + /* not our index? */ + if (reftable->indexes[k].dobj.catId.oid != indexOid) + continue; - refidx = &reftable->indexes[k]; - addConstrChildIdxDeps(&constrinfo[j].dobj, refidx); - break; - } + refidx = &reftable->indexes[k]; + addConstrChildIdxDeps(&constrinfo[j].dobj, refidx); + break; } } } - - PQclear(res); } + PQclear(res); + destroyPQExpBuffer(query); + destroyPQExpBuffer(tbloids); } /* @@ -8899,7 +8468,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo) { int i; ConstraintInfo *constrinfo; - PQExpBuffer query; + PQExpBuffer query = createPQExpBuffer(); PGresult *res; int i_tableoid, i_oid, @@ -8907,25 +8476,35 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo) i_consrc; int ntups; - query = createPQExpBuffer(); + if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS]) + { + /* Set up query for constraint-specific details */ + appendPQExpBufferStr(query, + "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"); - if (fout->remoteVersion >= 90100) - appendPQExpBuffer(query, "SELECT tableoid, oid, conname, " - "pg_catalog.pg_get_constraintdef(oid) AS consrc, " - "convalidated " - "FROM pg_catalog.pg_constraint " - "WHERE contypid = '%u'::pg_catalog.oid " - "ORDER BY conname", - tyinfo->dobj.catId.oid); + if (fout->remoteVersion >= 90100) + appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, " + "pg_catalog.pg_get_constraintdef(oid) AS consrc, " + "convalidated " + "FROM pg_catalog.pg_constraint " + "WHERE contypid = $1 " + "ORDER BY conname"); + else + appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, " + "pg_catalog.pg_get_constraintdef(oid) AS consrc, " + "true as convalidated " + "FROM pg_catalog.pg_constraint " + "WHERE contypid = $1 " + "ORDER BY conname"); - else - appendPQExpBuffer(query, "SELECT tableoid, oid, conname, " - "pg_catalog.pg_get_constraintdef(oid) AS consrc, " - "true as convalidated " - "FROM pg_catalog.pg_constraint " - "WHERE contypid = '%u'::pg_catalog.oid " - "ORDER BY conname", - tyinfo->dobj.catId.oid); + ExecuteSqlStatement(fout, query->data); + + fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true; + } + + printfPQExpBuffer(query, + "EXECUTE getDomainConstraints('%u')", + tyinfo->dobj.catId.oid); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -9001,24 +8580,12 @@ getRules(Archive *fout, int *numRules) int i_is_instead; int i_ev_enabled; - if (fout->remoteVersion >= 80300) - { - appendPQExpBufferStr(query, "SELECT " - "tableoid, oid, rulename, " - "ev_class AS ruletable, ev_type, is_instead, " - "ev_enabled " - "FROM pg_rewrite " - "ORDER BY oid"); - } - else - { - appendPQExpBufferStr(query, "SELECT " - "tableoid, oid, rulename, " - "ev_class AS ruletable, ev_type, is_instead, " - "'O'::char AS ev_enabled " - "FROM pg_rewrite " - "ORDER BY oid"); - } + appendPQExpBufferStr(query, "SELECT " + "tableoid, oid, rulename, " + "ev_class AS ruletable, ev_type, is_instead, " + "ev_enabled " + "FROM pg_rewrite " + "ORDER BY oid"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -9101,13 +8668,15 @@ getRules(Archive *fout, int *numRules) void getTriggers(Archive *fout, TableInfo tblinfo[], int numTables) { - int i, - j; PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer tbloids = createPQExpBuffer(); PGresult *res; + int ntups; + int curtblindx; TriggerInfo *tginfo; int i_tableoid, i_oid, + i_tgrelid, i_tgname, i_tgfname, i_tgtype, @@ -9122,153 +8691,172 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables) i_tgdeferrable, i_tginitdeferred, i_tgdef; - int ntups; - for (i = 0; i < numTables; i++) + /* + * We want to perform just one query against pg_trigger. However, we + * mustn't try to select every row of the catalog and then sort it out on + * the client side, because some of the server-side functions we need + * would be unsafe to apply to tables we don't have lock on. Hence, we + * build an array of the OIDs of tables we care about (and now have lock + * on!), and use a WHERE clause to constrain which rows are selected. + */ + appendPQExpBufferChar(tbloids, '{'); + for (int i = 0; i < numTables; i++) + { + TableInfo *tbinfo = &tblinfo[i]; + + if (!tbinfo->hastriggers || + !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) + continue; + + /* OK, we need info for this table */ + if (tbloids->len > 1) /* do we have more than the '{'? */ + appendPQExpBufferChar(tbloids, ','); + appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid); + } + appendPQExpBufferChar(tbloids, '}'); + + resetPQExpBuffer(query); + if (fout->remoteVersion >= 130000) { - TableInfo *tbinfo = &tblinfo[i]; + /* + * NB: think not to use pretty=true in pg_get_triggerdef. It could + * result in non-forward-compatible dumps of WHEN clauses due to + * under-parenthesization. + * + * NB: We need to see tgisinternal triggers in partitions, in case the + * tgenabled flag has been changed from the parent. + */ + appendPQExpBuffer(query, + "SELECT t.tgrelid, t.tgname, " + "t.tgfoid::pg_catalog.regproc AS tgfname, " + "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, " + "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n" + "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n" + "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) " + "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) " + "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) " + "ORDER BY t.tgrelid, t.tgname", + tbloids->data); + } + else if (fout->remoteVersion >= 110000) + { + /* + * NB: We need to see tgisinternal triggers in partitions, in case the + * tgenabled flag has been changed from the parent. No tgparentid in + * version 11-12, so we have to match them via pg_depend. + * + * See above about pretty=true in pg_get_triggerdef. + */ + appendPQExpBuffer(query, + "SELECT t.tgrelid, t.tgname, " + "t.tgfoid::pg_catalog.regproc AS tgfname, " + "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, " + "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition " + "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n" + "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) " + "LEFT JOIN pg_catalog.pg_depend AS d ON " + " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND " + " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND " + " d.objid = t.oid " + "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid " + "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) " + "ORDER BY t.tgrelid, t.tgname", + tbloids->data); + } + else if (fout->remoteVersion >= 90000) + { + /* See above about pretty=true in pg_get_triggerdef */ + appendPQExpBuffer(query, + "SELECT t.tgrelid, t.tgname, " + "t.tgfoid::pg_catalog.regproc AS tgfname, " + "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, " + "t.tgenabled, false as tgisinternal, " + "t.tableoid, t.oid " + "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n" + "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) " + "WHERE NOT t.tgisinternal " + "ORDER BY t.tgrelid, t.tgname", + tbloids->data); + } + else + { + /* + * We ignore triggers that are tied to a foreign-key constraint + */ + appendPQExpBuffer(query, + "SELECT t.tgrelid, tgname, " + "tgfoid::pg_catalog.regproc AS tgfname, " + "tgtype, tgnargs, tgargs, tgenabled, " + "false as tgisinternal, " + "tgisconstraint, tgconstrname, tgdeferrable, " + "tgconstrrelid, tginitdeferred, t.tableoid, t.oid, " + "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname " + "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n" + "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) " + "WHERE tgconstraint = 0 " + "ORDER BY t.tgrelid, t.tgname", + tbloids->data); + } - if (!tbinfo->hastriggers || - !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) - continue; + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); - pg_log_info("reading triggers for table \"%s.%s\"", - tbinfo->dobj.namespace->dobj.name, - tbinfo->dobj.name); + ntups = PQntuples(res); - resetPQExpBuffer(query); - if (fout->remoteVersion >= 130000) - { - /* - * NB: think not to use pretty=true in pg_get_triggerdef. It - * could result in non-forward-compatible dumps of WHEN clauses - * due to under-parenthesization. - * - * NB: We need to see tgisinternal triggers in partitions, in case - * the tgenabled flag has been changed from the parent. - */ - appendPQExpBuffer(query, - "SELECT t.tgname, " - "t.tgfoid::pg_catalog.regproc AS tgfname, " - "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, " - "t.tgenabled, t.tableoid, t.oid, t.tgisinternal " - "FROM pg_catalog.pg_trigger t " - "LEFT JOIN pg_catalog.pg_trigger u ON u.oid = t.tgparentid " - "WHERE t.tgrelid = '%u'::pg_catalog.oid " - "AND (NOT t.tgisinternal OR t.tgenabled != u.tgenabled)", - tbinfo->dobj.catId.oid); - } - else if (fout->remoteVersion >= 110000) - { - /* - * NB: We need to see tgisinternal triggers in partitions, in case - * the tgenabled flag has been changed from the parent. No - * tgparentid in version 11-12, so we have to match them via - * pg_depend. - * - * See above about pretty=true in pg_get_triggerdef. - */ - appendPQExpBuffer(query, - "SELECT t.tgname, " - "t.tgfoid::pg_catalog.regproc AS tgfname, " - "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, " - "t.tgenabled, t.tableoid, t.oid, t.tgisinternal " - "FROM pg_catalog.pg_trigger t " - "LEFT JOIN pg_catalog.pg_depend AS d ON " - " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND " - " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND " - " d.objid = t.oid " - "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid " - "WHERE t.tgrelid = '%u'::pg_catalog.oid " - "AND (NOT t.tgisinternal%s)", - tbinfo->dobj.catId.oid, - tbinfo->ispartition ? - " OR t.tgenabled != pt.tgenabled" : ""); - } - else if (fout->remoteVersion >= 90000) - { - /* See above about pretty=true in pg_get_triggerdef */ - appendPQExpBuffer(query, - "SELECT t.tgname, " - "t.tgfoid::pg_catalog.regproc AS tgfname, " - "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, " - "t.tgenabled, false as tgisinternal, " - "t.tableoid, t.oid " - "FROM pg_catalog.pg_trigger t " - "WHERE tgrelid = '%u'::pg_catalog.oid " - "AND NOT tgisinternal", - tbinfo->dobj.catId.oid); - } - else if (fout->remoteVersion >= 80300) - { - /* - * We ignore triggers that are tied to a foreign-key constraint - */ - appendPQExpBuffer(query, - "SELECT tgname, " - "tgfoid::pg_catalog.regproc AS tgfname, " - "tgtype, tgnargs, tgargs, tgenabled, " - "false as tgisinternal, " - "tgisconstraint, tgconstrname, tgdeferrable, " - "tgconstrrelid, tginitdeferred, tableoid, oid, " - "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname " - "FROM pg_catalog.pg_trigger t " - "WHERE tgrelid = '%u'::pg_catalog.oid " - "AND tgconstraint = 0", - tbinfo->dobj.catId.oid); - } - else - { - /* - * We ignore triggers that are tied to a foreign-key constraint, - * but in these versions we have to grovel through pg_constraint - * to find out - */ - appendPQExpBuffer(query, - "SELECT tgname, " - "tgfoid::pg_catalog.regproc AS tgfname, " - "tgtype, tgnargs, tgargs, tgenabled, " - "false as tgisinternal, " - "tgisconstraint, tgconstrname, tgdeferrable, " - "tgconstrrelid, tginitdeferred, tableoid, oid, " - "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname " - "FROM pg_catalog.pg_trigger t " - "WHERE tgrelid = '%u'::pg_catalog.oid " - "AND (NOT tgisconstraint " - " OR NOT EXISTS" - " (SELECT 1 FROM pg_catalog.pg_depend d " - " JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) " - " WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))", - tbinfo->dobj.catId.oid); - } + i_tableoid = PQfnumber(res, "tableoid"); + i_oid = PQfnumber(res, "oid"); + i_tgrelid = PQfnumber(res, "tgrelid"); + i_tgname = PQfnumber(res, "tgname"); + i_tgfname = PQfnumber(res, "tgfname"); + i_tgtype = PQfnumber(res, "tgtype"); + i_tgnargs = PQfnumber(res, "tgnargs"); + i_tgargs = PQfnumber(res, "tgargs"); + i_tgisconstraint = PQfnumber(res, "tgisconstraint"); + i_tgconstrname = PQfnumber(res, "tgconstrname"); + i_tgconstrrelid = PQfnumber(res, "tgconstrrelid"); + i_tgconstrrelname = PQfnumber(res, "tgconstrrelname"); + i_tgenabled = PQfnumber(res, "tgenabled"); + i_tgisinternal = PQfnumber(res, "tgisinternal"); + i_tgdeferrable = PQfnumber(res, "tgdeferrable"); + i_tginitdeferred = PQfnumber(res, "tginitdeferred"); + i_tgdef = PQfnumber(res, "tgdef"); + + tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo)); - res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + /* + * Outer loop iterates once per table, not once per row. Incrementing of + * j is handled by the inner loop. + */ + curtblindx = -1; + for (int j = 0; j < ntups;) + { + Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid)); + TableInfo *tbinfo = NULL; + int numtrigs; - ntups = PQntuples(res); + /* Count rows for this table */ + for (numtrigs = 1; numtrigs < ntups - j; numtrigs++) + if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid) + break; - i_tableoid = PQfnumber(res, "tableoid"); - i_oid = PQfnumber(res, "oid"); - i_tgname = PQfnumber(res, "tgname"); - i_tgfname = PQfnumber(res, "tgfname"); - i_tgtype = PQfnumber(res, "tgtype"); - i_tgnargs = PQfnumber(res, "tgnargs"); - i_tgargs = PQfnumber(res, "tgargs"); - i_tgisconstraint = PQfnumber(res, "tgisconstraint"); - i_tgconstrname = PQfnumber(res, "tgconstrname"); - i_tgconstrrelid = PQfnumber(res, "tgconstrrelid"); - i_tgconstrrelname = PQfnumber(res, "tgconstrrelname"); - i_tgenabled = PQfnumber(res, "tgenabled"); - i_tgisinternal = PQfnumber(res, "tgisinternal"); - i_tgdeferrable = PQfnumber(res, "tgdeferrable"); - i_tginitdeferred = PQfnumber(res, "tginitdeferred"); - i_tgdef = PQfnumber(res, "tgdef"); - - tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo)); - - tbinfo->numTriggers = ntups; - tbinfo->triggers = tginfo; + /* + * Locate the associated TableInfo; we rely on tblinfo[] being in OID + * order. + */ + while (++curtblindx < numTables) + { + tbinfo = &tblinfo[curtblindx]; + if (tbinfo->dobj.catId.oid == tgrelid) + break; + } + if (curtblindx >= numTables) + fatal("unrecognized table OID %u", tgrelid); - for (j = 0; j < ntups; j++) + /* Save data for this table */ + tbinfo->triggers = tginfo + j; + tbinfo->numTriggers = numtrigs; + + for (int c = 0; c < numtrigs; c++, j++) { tginfo[j].dobj.objType = DO_TRIGGER; tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid)); @@ -9331,11 +8919,12 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables) } } } - - PQclear(res); } + PQclear(res); + destroyPQExpBuffer(query); + destroyPQExpBuffer(tbloids); } /* @@ -9370,14 +8959,13 @@ getEventTriggers(Archive *fout, int *numEventTriggers) appendPQExpBuffer(query, "SELECT e.tableoid, e.oid, evtname, evtenabled, " - "evtevent, (%s evtowner) AS evtowner, " + "evtevent, evtowner, " "array_to_string(array(" "select quote_literal(x) " " from unnest(evttags) as t(x)), ', ') as evttags, " "e.evtfoid::regproc as evtfname " "FROM pg_event_trigger e " - "ORDER BY e.oid", - username_subquery); + "ORDER BY e.oid"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -9405,16 +8993,13 @@ getEventTriggers(Archive *fout, int *numEventTriggers) evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname)); evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname)); evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent)); - evtinfo[i].evtowner = pg_strdup(PQgetvalue(res, i, i_evtowner)); + evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner)); evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags)); evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname)); evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled)); /* Decide whether we want to dump it */ selectDumpableObject(&(evtinfo[i].dobj), fout); - - /* Event Triggers do not currently have ACLs. */ - evtinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -9436,7 +9021,6 @@ getEventTriggers(Archive *fout, int *numEventTriggers) ProcLangInfo * getProcLangs(Archive *fout, int *numProcLangs) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -9450,9 +9034,7 @@ 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_acldefault; int i_lanowner; /* @@ -9460,86 +9042,44 @@ getProcLangs(Archive *fout, int *numProcLangs) * Cloudberry 5, so the check needs to go further back than 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 = 'pg_language'::regclass " - "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) + if (fout->remoteVersion >= 90200) { - /* pg_language has a laninline column */ + /* acldefault() exists */ appendPQExpBuffer(query, "SELECT tableoid, oid, " "lanname, lanpltrusted, lanplcallfoid, " - "laninline, lanvalidator, lanacl, NULL AS rlanacl, " - "NULL AS initlanacl, NULL AS initrlanacl, " - "(%s lanowner) AS lanowner " + "laninline, lanvalidator, " + "lanacl, " + "acldefault('l', lanowner) AS acldefault, " + "lanowner " "FROM pg_language " "WHERE lanispl " - "ORDER BY oid", - username_subquery); + "ORDER BY oid"); } - else if (fout->remoteVersion >= 80300) + else if (fout->remoteVersion >= 90000) { /* pg_language has a laninline column */ - /* pg_language has a lanowner column */ appendPQExpBuffer(query, "SELECT tableoid, oid, " "lanname, lanpltrusted, lanplcallfoid, " - "laninline, lanvalidator, lanacl, " - "0 AS laninline, lanvalidator, lanacl, " - "NULL AS rlanacl, " - "NULL AS initlanacl, NULL AS initrlanacl, " - "(%s lanowner) AS lanowner " + "laninline, lanvalidator, " + "lanacl, NULL AS acldefault, " + "lanowner " "FROM pg_language " "WHERE lanispl " - "ORDER BY oid", - username_subquery); + "ORDER BY oid"); } - else + else /* GPDB5 */ { - /* Languages are owned by the bootstrap superuser, OID 10 */ + /* pg_language has a laninline column */ + /* pg_language has a lanowner column */ appendPQExpBuffer(query, "SELECT tableoid, oid, " "lanname, lanpltrusted, lanplcallfoid, " + "laninline, lanvalidator, lanacl, " "0 AS laninline, lanvalidator, lanacl, " - "NULL AS rlanacl, " - "NULL AS initlanacl, NULL AS initrlanacl, " - "(%s '10') AS lanowner " + "NULL AS acldefault, " + "lanowner " "FROM pg_language " "WHERE lanispl " - "ORDER BY oid", - username_subquery); + "ORDER BY oid"); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -9558,9 +9098,7 @@ 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_acldefault = PQfnumber(res, "acldefault"); i_lanowner = PQfnumber(res, "lanowner"); for (i = 0; i < ntups; i++) @@ -9571,24 +9109,22 @@ getProcLangs(Archive *fout, int *numProcLangs) AssignDumpId(&planginfo[i].dobj); planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname)); + planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl)); + planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + planginfo[i].dacl.privtype = 0; + planginfo[i].dacl.initprivs = NULL; planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't'; planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid)); 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)); + planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner)); /* Decide whether we want to dump it */ 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; + /* Mark whether language has an ACL */ + if (!PQgetisnull(res, i, i_lanacl)) + planginfo[i].dobj.components |= DUMP_COMPONENT_ACL; } PQclear(res); @@ -9698,9 +9234,6 @@ getCasts(Archive *fout, int *numCasts) /* Decide whether we want to dump it */ selectDumpableCast(&(castinfo[i]), fout); - - /* Casts do not currently have ACLs. */ - castinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -9822,12 +9355,6 @@ getTransforms(Archive *fout, int *numTransforms) * for each interesting table, read info about its attributes * (names, types, default values, CHECK constraints, etc) * - * This is implemented in a very inefficient way right now, looping - * through the tblinfo and doing a join per table to find the attrs and their - * types. However, because we want type names and so forth to be named - * relative to the schema of each table, we couldn't do it in just one - * query. (Maybe one query per schema?) - * * modifies tblinfo */ void @@ -9835,16 +9362,49 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) { DumpOptions *dopt = fout->dopt; PQExpBuffer q = createPQExpBuffer(); - - /* GPDB_14_MERGE_FIXME: GPDB specific column, need to keep this for easy to use*/ + PQExpBuffer tbloids = createPQExpBuffer(); + PQExpBuffer checkoids = createPQExpBuffer(); + PGresult *res; + int ntups; + int curtblindx; + int i_attrelid; + int i_attnum; + int i_attname; + int i_atttypname; + int i_atttypmod; + int i_attstattarget; + int i_attstorage; + int i_typstorage; + int i_attidentity; + int i_attgenerated; + int i_attisdropped; + int i_attlen; + int i_attalign; + int i_attislocal; + int i_attnotnull; + int i_attoptions; + int i_attcollation; + int i_attcompression; + int i_attfdwoptions; + int i_attmissingval; + int i_atthasdef; int i_attencoding; + /* + * We want to perform just one query against pg_attribute, and then just + * one against pg_attrdef (for DEFAULTs) and one against pg_constraint + * (for CHECK constraints). However, we mustn't try to select every row + * of those catalogs and then sort it out on the client side, because some + * of the server-side functions we need would be unsafe to apply to tables + * we don't have lock on. Hence, we build an array of the OIDs of tables + * we care about (and now have lock on!), and use a WHERE clause to + * constrain which rows are selected. + */ + appendPQExpBufferChar(tbloids, '{'); + appendPQExpBufferChar(checkoids, '{'); for (int i = 0; i < numTables; i++) { TableInfo *tbinfo = &tblinfo[i]; - PGresult *res; - int ntups; - bool hasdefaults; /* Don't bother to collect info for sequences */ if (tbinfo->relkind == RELKIND_SEQUENCE) @@ -9854,170 +9414,236 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) if (!tbinfo->interesting) continue; - /* find all the user attributes and their types */ - - /* - * we must read the attribute names in attribute number order! because - * we will use the attnum to index into the attnames array later. - */ - pg_log_info("finding the columns and types of table \"%s.%s\"", - tbinfo->dobj.namespace->dobj.name, - tbinfo->dobj.name); - - resetPQExpBuffer(q); - - appendPQExpBufferStr(q, - "SELECT\n" - "a.attnum,\n" - "a.attname,\n" - "a.atttypmod,\n" - "a.attstattarget,\n" - "a.attstorage,\n" - "t.typstorage,\n" - "a.attnotnull,\n" - "a.atthasdef,\n" - "a.attisdropped,\n" - "a.attlen,\n" - "a.attalign,\n" - "a.attislocal,\n" - "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"); - - if (fout->remoteVersion >= 90000) - appendPQExpBufferStr(q, - "array_to_string(a.attoptions, ', ') AS attoptions,\n"); - else - appendPQExpBufferStr(q, - "'' AS attoptions,\n"); + /* OK, we need info for this table */ + if (tbloids->len > 1) /* do we have more than the '{'? */ + appendPQExpBufferChar(tbloids, ','); + appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid); - if (fout->remoteVersion >= 90100) + if (tbinfo->ncheck > 0) { - /* - * Since we only want to dump COLLATE clauses for attributes whose - * collation is different from their type's default, we use a CASE - * here to suppress uninteresting attcollations cheaply. - */ - appendPQExpBufferStr(q, - "CASE WHEN a.attcollation <> t.typcollation " - "THEN a.attcollation ELSE 0 END AS attcollation,\n"); + /* Also make a list of the ones with check constraints */ + if (checkoids->len > 1) /* do we have more than the '{'? */ + appendPQExpBufferChar(checkoids, ','); + appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid); } - else - appendPQExpBufferStr(q, - "0 AS attcollation,\n"); + } + appendPQExpBufferChar(tbloids, '}'); + appendPQExpBufferChar(checkoids, '}'); - if (fout->remoteVersion >= 140000) + /* find all the user attributes and their types */ + appendPQExpBufferStr(q, + "SELECT\n" + "a.attrelid,\n" + "a.attnum,\n" + "a.attname,\n" + "a.atttypmod,\n" + "a.attstattarget,\n" + "a.attstorage,\n" + "t.typstorage,\n" + "a.attnotnull,\n" + "a.atthasdef,\n" + "a.attisdropped,\n" + "a.attlen,\n" + "a.attalign,\n" + "a.attislocal,\n" + "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n" + "pg_catalog.array_to_string(e.attoptions, ',') AS attencoding,\n"); + + if (fout->remoteVersion >= 140000) appendPQExpBuffer(q, "a.attcompression AS attcompression,\n"); else appendPQExpBuffer(q, "'' AS attcompression,\n"); - appendPQExpBuffer(q, - "pg_catalog.array_to_string(e.attoptions, ',') AS attencoding,\n"); + if (fout->remoteVersion >= 90000) + appendPQExpBufferStr(q, + "array_to_string(a.attoptions, ', ') AS attoptions,\n"); + else + appendPQExpBufferStr(q, + "'' AS attoptions,\n"); - if (fout->remoteVersion >= 90200) - appendPQExpBufferStr(q, - "pg_catalog.array_to_string(ARRAY(" - "SELECT pg_catalog.quote_ident(option_name) || " - "' ' || pg_catalog.quote_literal(option_value) " - "FROM pg_catalog.pg_options_to_table(attfdwoptions) " - "ORDER BY option_name" - "), E',\n ') AS attfdwoptions,\n"); - else - appendPQExpBufferStr(q, - "'' AS attfdwoptions,\n"); + if (fout->remoteVersion >= 90100) + { + /* + * Since we only want to dump COLLATE clauses for attributes whose + * collation is different from their type's default, we use a CASE + * here to suppress uninteresting attcollations cheaply. + */ + appendPQExpBufferStr(q, + "CASE WHEN a.attcollation <> t.typcollation " + "THEN a.attcollation ELSE 0 END AS attcollation,\n"); + } + else + appendPQExpBufferStr(q, + "0 AS attcollation,\n"); - if (fout->remoteVersion >= 100000) - appendPQExpBufferStr(q, - "a.attidentity,\n"); - else - appendPQExpBufferStr(q, - "'' AS attidentity,\n"); + if (fout->remoteVersion >= 90200) + appendPQExpBufferStr(q, + "pg_catalog.array_to_string(ARRAY(" + "SELECT pg_catalog.quote_ident(option_name) || " + "' ' || pg_catalog.quote_literal(option_value) " + "FROM pg_catalog.pg_options_to_table(attfdwoptions) " + "ORDER BY option_name" + "), E',\n ') AS attfdwoptions,\n"); + else + appendPQExpBufferStr(q, + "'' AS attfdwoptions,\n"); - if (fout->remoteVersion >= 110000) - appendPQExpBufferStr(q, - "CASE WHEN a.atthasmissing AND NOT a.attisdropped " - "THEN a.attmissingval ELSE null END AS attmissingval,\n"); - else - appendPQExpBufferStr(q, - "NULL AS attmissingval,\n"); + if (fout->remoteVersion >= 100000) + appendPQExpBufferStr(q, + "a.attidentity,\n"); + else + appendPQExpBufferStr(q, + "'' AS attidentity,\n"); - if (fout->remoteVersion >= 120000) - appendPQExpBufferStr(q, - "a.attgenerated\n"); - else - appendPQExpBufferStr(q, - "'' AS attgenerated\n"); + if (fout->remoteVersion >= 110000) + appendPQExpBufferStr(q, + "CASE WHEN a.atthasmissing AND NOT a.attisdropped " + "THEN a.attmissingval ELSE null END AS attmissingval,\n"); + else + appendPQExpBufferStr(q, + "NULL AS attmissingval,\n"); - /* need left join here to not fail on dropped columns ... */ - appendPQExpBuffer(q, - "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " - "ON a.atttypid = t.oid\n" - "LEFT OUTER JOIN pg_catalog.pg_attribute_encoding e ON e.attrelid = a.attrelid AND e.attnum = a.attnum \n" - "WHERE a.attrelid = '%u'::pg_catalog.oid " - "AND a.attnum > 0::pg_catalog.int2\n" - "ORDER BY a.attnum", - tbinfo->dobj.catId.oid); + if (fout->remoteVersion >= 120000) + appendPQExpBufferStr(q, + "a.attgenerated\n"); + else + appendPQExpBufferStr(q, + "'' AS attgenerated\n"); - res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK); + /* need left join to pg_type to not fail on dropped columns ... */ + appendPQExpBuffer(q, + "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n" + "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) " + "LEFT JOIN pg_catalog.pg_type t " + "ON (a.atttypid = t.oid)\n" + "LEFT OUTER JOIN pg_catalog.pg_attribute_encoding e " + "ON e.attrelid = a.attrelid AND e.attnum = a.attnum \n" + "WHERE a.attnum > 0::pg_catalog.int2\n" + "ORDER BY a.attrelid, a.attnum", + tbloids->data); - ntups = PQntuples(res); + res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + + i_attrelid = PQfnumber(res, "attrelid"); + i_attnum = PQfnumber(res, "attnum"); + i_attname = PQfnumber(res, "attname"); + i_atttypname = PQfnumber(res, "atttypname"); + i_atttypmod = PQfnumber(res, "atttypmod"); + i_attstattarget = PQfnumber(res, "attstattarget"); + i_attstorage = PQfnumber(res, "attstorage"); + i_typstorage = PQfnumber(res, "typstorage"); + i_attidentity = PQfnumber(res, "attidentity"); + i_attgenerated = PQfnumber(res, "attgenerated"); + i_attisdropped = PQfnumber(res, "attisdropped"); + i_attlen = PQfnumber(res, "attlen"); + i_attalign = PQfnumber(res, "attalign"); + i_attislocal = PQfnumber(res, "attislocal"); + i_attnotnull = PQfnumber(res, "attnotnull"); + i_attoptions = PQfnumber(res, "attoptions"); + i_attcollation = PQfnumber(res, "attcollation"); + i_attcompression = PQfnumber(res, "attcompression"); + i_attfdwoptions = PQfnumber(res, "attfdwoptions"); + i_attmissingval = PQfnumber(res, "attmissingval"); + i_atthasdef = PQfnumber(res, "atthasdef"); + i_attencoding = PQfnumber(res, "attencoding"); + + /* Within the next loop, we'll accumulate OIDs of tables with defaults */ + resetPQExpBuffer(tbloids); + appendPQExpBufferChar(tbloids, '{'); + + /* + * Outer loop iterates once per table, not once per row. Incrementing of + * r is handled by the inner loop. + */ + curtblindx = -1; + for (int r = 0; r < ntups;) + { + Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid)); + TableInfo *tbinfo = NULL; + int numatts; + bool hasdefaults; + + /* Count rows for this table */ + for (numatts = 1; numatts < ntups - r; numatts++) + if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid) + break; + + /* + * Locate the associated TableInfo; we rely on tblinfo[] being in OID + * order. + */ + while (++curtblindx < numTables) + { + tbinfo = &tblinfo[curtblindx]; + if (tbinfo->dobj.catId.oid == attrelid) + break; + } + if (curtblindx >= numTables) + fatal("unrecognized table OID %u", attrelid); + /* cross-check that we only got requested tables */ + if (tbinfo->relkind == RELKIND_SEQUENCE || + !tbinfo->interesting) + fatal("unexpected column data for table \"%s\"", + tbinfo->dobj.name); - i_attencoding = PQfnumber(res, "attencoding"); - - tbinfo->numatts = ntups; - tbinfo->attnames = (char **) pg_malloc(ntups * sizeof(char *)); - tbinfo->atttypnames = (char **) pg_malloc(ntups * sizeof(char *)); - tbinfo->atttypmod = (int *) pg_malloc(ntups * sizeof(int)); - tbinfo->attstattarget = (int *) pg_malloc(ntups * sizeof(int)); - tbinfo->attstorage = (char *) pg_malloc(ntups * sizeof(char)); - tbinfo->typstorage = (char *) pg_malloc(ntups * sizeof(char)); - tbinfo->attidentity = (char *) pg_malloc(ntups * sizeof(char)); - tbinfo->attgenerated = (char *) pg_malloc(ntups * sizeof(char)); - tbinfo->attisdropped = (bool *) pg_malloc(ntups * sizeof(bool)); - tbinfo->attlen = (int *) pg_malloc(ntups * sizeof(int)); - tbinfo->attalign = (char *) pg_malloc(ntups * sizeof(char)); - tbinfo->attislocal = (bool *) pg_malloc(ntups * sizeof(bool)); - tbinfo->attoptions = (char **) pg_malloc(ntups * sizeof(char *)); - tbinfo->attcollation = (Oid *) pg_malloc(ntups * sizeof(Oid)); - tbinfo->attcompression = (char *) pg_malloc(ntups * sizeof(char)); - tbinfo->attfdwoptions = (char **) pg_malloc(ntups * sizeof(char *)); - tbinfo->attmissingval = (char **) pg_malloc(ntups * sizeof(char *)); - tbinfo->notnull = (bool *) pg_malloc(ntups * sizeof(bool)); - tbinfo->inhNotNull = (bool *) pg_malloc(ntups * sizeof(bool)); + /* Save data for this table */ + tbinfo->numatts = numatts; + tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *)); + tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *)); + tbinfo->atttypmod = (int *) pg_malloc(numatts * sizeof(int)); + tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int)); + tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char)); + tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char)); + tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char)); + tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char)); + tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool)); + tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int)); + tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char)); + tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool)); + tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *)); + tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid)); + tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char)); + tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *)); + tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *)); + tbinfo->notnull = (bool *) pg_malloc(numatts * sizeof(bool)); + tbinfo->inhNotNull = (bool *) pg_malloc(numatts * sizeof(bool)); + tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *)); tbinfo->attencoding = (char **) pg_malloc(ntups * sizeof(char *)); - tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *)); hasdefaults = false; - for (int j = 0; j < ntups; j++) + for (int j = 0; j < numatts; j++, r++) { - if (j + 1 != atoi(PQgetvalue(res, j, PQfnumber(res, "attnum")))) + if (j + 1 != atoi(PQgetvalue(res, r, i_attnum))) fatal("invalid column numbering in table \"%s\"", tbinfo->dobj.name); - tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, j, PQfnumber(res, "attname"))); - tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, j, PQfnumber(res, "atttypname"))); - tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, PQfnumber(res, "atttypmod"))); - tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, PQfnumber(res, "attstattarget"))); - tbinfo->attstorage[j] = *(PQgetvalue(res, j, PQfnumber(res, "attstorage"))); - tbinfo->typstorage[j] = *(PQgetvalue(res, j, PQfnumber(res, "typstorage"))); - tbinfo->attidentity[j] = *(PQgetvalue(res, j, PQfnumber(res, "attidentity"))); - tbinfo->attgenerated[j] = *(PQgetvalue(res, j, PQfnumber(res, "attgenerated"))); + tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname)); + tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname)); + tbinfo->atttypmod[j] = atoi(PQgetvalue(res, r, i_atttypmod)); + tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget)); + tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage)); + tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage)); + tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity)); + tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated)); tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS); - tbinfo->attisdropped[j] = (PQgetvalue(res, j, PQfnumber(res, "attisdropped"))[0] == 't'); - tbinfo->attlen[j] = atoi(PQgetvalue(res, j, PQfnumber(res, "attlen"))); - tbinfo->attalign[j] = *(PQgetvalue(res, j, PQfnumber(res, "attalign"))); - tbinfo->attislocal[j] = (PQgetvalue(res, j, PQfnumber(res, "attislocal"))[0] == 't'); - tbinfo->notnull[j] = (PQgetvalue(res, j, PQfnumber(res, "attnotnull"))[0] == 't'); - tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, PQfnumber(res, "attoptions"))); - tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, PQfnumber(res, "attcollation"))); - tbinfo->attcompression[j] = *(PQgetvalue(res, j, PQfnumber(res, "attcompression"))); - tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, PQfnumber(res, "attfdwoptions"))); - tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, j, PQfnumber(res, "attmissingval"))); + tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't'); + tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen)); + tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign)); + tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't'); + tbinfo->notnull[j] = (PQgetvalue(res, r, i_attnotnull)[0] == 't'); + tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions)); + tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation)); + tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression)); + tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions)); + tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval)); tbinfo->attrdefs[j] = NULL; /* fix below */ - if (PQgetvalue(res, j, PQfnumber(res, "atthasdef"))[0] == 't') + if (PQgetvalue(res, r, i_atthasdef)[0] == 't') hasdefaults = true; /* these flags will be set in flagInhAttrs() */ tbinfo->inhNotNull[j] = false; - /* column storage attributes */ if (!PQgetisnull(res, j, i_attencoding)) tbinfo->attencoding[j] = pg_strdup(PQgetvalue(res, j, i_attencoding)); @@ -10025,211 +9651,272 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) tbinfo->attencoding[j] = NULL; } - PQclear(res); - - /* - * Get info about column defaults. This is skipped for a data-only - * dump, as it is only needed for table schemas. - */ - if (!dopt->dataOnly && hasdefaults) + if (hasdefaults) { - AttrDefInfo *attrdefs; - int numDefaults; - - pg_log_info("finding default expressions of table \"%s.%s\"", - tbinfo->dobj.namespace->dobj.name, - tbinfo->dobj.name); - - printfPQExpBuffer(q, "SELECT tableoid, oid, adnum, " - "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc " - "FROM pg_catalog.pg_attrdef " - "WHERE adrelid = '%u'::pg_catalog.oid", - tbinfo->dobj.catId.oid); - - res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK); + /* Collect OIDs of interesting tables that have defaults */ + if (tbloids->len > 1) /* do we have more than the '{'? */ + appendPQExpBufferChar(tbloids, ','); + appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid); + } + } - numDefaults = PQntuples(res); - attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo)); + PQclear(res); - for (int j = 0; j < numDefaults; j++) - { - int adnum; + /* + * Now get info about column defaults. This is skipped for a data-only + * dump, as it is only needed for table schemas. + */ + if (!dopt->dataOnly && tbloids->len > 1) + { + AttrDefInfo *attrdefs; + int numDefaults; + TableInfo *tbinfo = NULL; - adnum = atoi(PQgetvalue(res, j, 2)); + pg_log_info("finding table default expressions"); - if (adnum <= 0 || adnum > ntups) - fatal("invalid adnum value %d for table \"%s\"", - adnum, tbinfo->dobj.name); + appendPQExpBufferChar(tbloids, '}'); - /* - * dropped columns shouldn't have defaults, but just in case, - * ignore 'em - */ - if (tbinfo->attisdropped[adnum - 1]) - continue; + printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, " + "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n" + "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n" + "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n" + "ORDER BY a.adrelid, a.adnum", + tbloids->data); - attrdefs[j].dobj.objType = DO_ATTRDEF; - attrdefs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0)); - attrdefs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1)); - AssignDumpId(&attrdefs[j].dobj); - attrdefs[j].adtable = tbinfo; - attrdefs[j].adnum = adnum; - attrdefs[j].adef_expr = pg_strdup(PQgetvalue(res, j, 3)); + res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK); - attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name); - attrdefs[j].dobj.namespace = tbinfo->dobj.namespace; + numDefaults = PQntuples(res); + attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo)); - attrdefs[j].dobj.dump = tbinfo->dobj.dump; + curtblindx = -1; + for (int j = 0; j < numDefaults; j++) + { + Oid adtableoid = atooid(PQgetvalue(res, j, 0)); + Oid adoid = atooid(PQgetvalue(res, j, 1)); + Oid adrelid = atooid(PQgetvalue(res, j, 2)); + int adnum = atoi(PQgetvalue(res, j, 3)); + char *adsrc = PQgetvalue(res, j, 4); - /* - * Figure out whether the default/generation expression should - * be dumped as part of the main CREATE TABLE (or similar) - * command or as a separate ALTER TABLE (or similar) command. - * The preference is to put it into the CREATE command, but in - * some cases that's not possible. - */ - if (tbinfo->attgenerated[adnum - 1]) - { - /* - * Column generation expressions cannot be dumped - * separately, because there is no syntax for it. The - * !shouldPrintColumn case below will be tempted to set - * them to separate if they are attached to an inherited - * column without a local definition, but that would be - * wrong and unnecessary, because generation expressions - * are always inherited, so there is no need to set them - * again in child tables, and there is no syntax for it - * either. By setting separate to false here we prevent - * the "default" from being processed as its own dumpable - * object, and flagInhAttrs() will remove it from the - * table when it detects that it belongs to an inherited - * column. - */ - attrdefs[j].separate = false; - } - else if (tbinfo->relkind == RELKIND_VIEW) - { - /* - * Defaults on a VIEW must always be dumped as separate - * ALTER TABLE commands. - */ - attrdefs[j].separate = true; - } - else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1)) - { - /* column will be suppressed, print default separately */ - attrdefs[j].separate = true; - } - else + /* + * Locate the associated TableInfo; we rely on tblinfo[] being in + * OID order. + */ + if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid) + { + while (++curtblindx < numTables) { - attrdefs[j].separate = false; + tbinfo = &tblinfo[curtblindx]; + if (tbinfo->dobj.catId.oid == adrelid) + break; } + if (curtblindx >= numTables) + fatal("unrecognized table OID %u", adrelid); + } - if (!attrdefs[j].separate) - { - /* - * Mark the default as needing to appear before the table, - * so that any dependencies it has must be emitted before - * the CREATE TABLE. If this is not possible, we'll - * change to "separate" mode while sorting dependencies. - */ - addObjectDependency(&tbinfo->dobj, - attrdefs[j].dobj.dumpId); - } + if (adnum <= 0 || adnum > tbinfo->numatts) + fatal("invalid adnum value %d for table \"%s\"", + adnum, tbinfo->dobj.name); - tbinfo->attrdefs[adnum - 1] = &attrdefs[j]; - } - PQclear(res); - } + /* + * dropped columns shouldn't have defaults, but just in case, + * ignore 'em + */ + if (tbinfo->attisdropped[adnum - 1]) + continue; - /* - * Get info about table CHECK constraints. This is skipped for a - * data-only dump, as it is only needed for table schemas. - */ - if (tbinfo->ncheck > 0 && !dopt->dataOnly) - { - ConstraintInfo *constrs; - int numConstrs; + attrdefs[j].dobj.objType = DO_ATTRDEF; + attrdefs[j].dobj.catId.tableoid = adtableoid; + attrdefs[j].dobj.catId.oid = adoid; + AssignDumpId(&attrdefs[j].dobj); + attrdefs[j].adtable = tbinfo; + attrdefs[j].adnum = adnum; + attrdefs[j].adef_expr = pg_strdup(adsrc); - pg_log_info("finding check constraints for table \"%s.%s\"", - tbinfo->dobj.namespace->dobj.name, - tbinfo->dobj.name); + attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name); + attrdefs[j].dobj.namespace = tbinfo->dobj.namespace; - resetPQExpBuffer(q); - if (fout->remoteVersion >= 90200) + attrdefs[j].dobj.dump = tbinfo->dobj.dump; + + /* + * Figure out whether the default/generation expression should be + * dumped as part of the main CREATE TABLE (or similar) command or + * as a separate ALTER TABLE (or similar) command. The preference + * is to put it into the CREATE command, but in some cases that's + * not possible. + */ + if (tbinfo->attgenerated[adnum - 1]) + { + /* + * Column generation expressions cannot be dumped separately, + * because there is no syntax for it. The !shouldPrintColumn + * case below will be tempted to set them to separate if they + * are attached to an inherited column without a local + * definition, but that would be wrong and unnecessary, + * because generation expressions are always inherited, so + * there is no need to set them again in child tables, and + * there is no syntax for it either. By setting separate to + * false here we prevent the "default" from being processed as + * its own dumpable object, and flagInhAttrs() will remove it + * from the table when it detects that it belongs to an + * inherited column. + */ + attrdefs[j].separate = false; + } + else if (tbinfo->relkind == RELKIND_VIEW) { /* - * convalidated is new in 9.2 (actually, it is there in 9.1, - * but it wasn't ever false for check constraints until 9.2). + * Defaults on a VIEW must always be dumped as separate ALTER + * TABLE commands. */ - appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " - "pg_catalog.pg_get_constraintdef(oid) AS consrc, " - "conislocal, convalidated " - "FROM pg_catalog.pg_constraint " - "WHERE conrelid = '%u'::pg_catalog.oid " - " AND contype = 'c' " - "ORDER BY conname", - tbinfo->dobj.catId.oid); + attrdefs[j].separate = true; } - else if (fout->remoteVersion >= 80400) + else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1)) { - /* conislocal is new in 8.4 */ - appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " - "pg_catalog.pg_get_constraintdef(oid) AS consrc, " - "conislocal, true AS convalidated " - "FROM pg_catalog.pg_constraint " - "WHERE conrelid = '%u'::pg_catalog.oid " - " AND contype = 'c' " - "ORDER BY conname", - tbinfo->dobj.catId.oid); + /* column will be suppressed, print default separately */ + attrdefs[j].separate = true; } else { - appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " - "pg_catalog.pg_get_constraintdef(oid) AS consrc, " - "true AS conislocal, true AS convalidated " - "FROM pg_catalog.pg_constraint " - "WHERE conrelid = '%u'::pg_catalog.oid " - " AND contype = 'c' " - "ORDER BY conname", - tbinfo->dobj.catId.oid); + attrdefs[j].separate = false; + } + + if (!attrdefs[j].separate) + { + /* + * Mark the default as needing to appear before the table, so + * that any dependencies it has must be emitted before the + * CREATE TABLE. If this is not possible, we'll change to + * "separate" mode while sorting dependencies. + */ + addObjectDependency(&tbinfo->dobj, + attrdefs[j].dobj.dumpId); } - res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK); + tbinfo->attrdefs[adnum - 1] = &attrdefs[j]; + } + + PQclear(res); + } + + /* + * Get info about table CHECK constraints. This is skipped for a + * data-only dump, as it is only needed for table schemas. + */ + if (!dopt->dataOnly && checkoids->len > 2) + { + ConstraintInfo *constrs; + int numConstrs; + int i_tableoid; + int i_oid; + int i_conrelid; + int i_conname; + int i_consrc; + int i_conislocal; + int i_convalidated; + + pg_log_info("finding table check constraints"); + + resetPQExpBuffer(q); + appendPQExpBufferStr(q, + "SELECT c.tableoid, c.oid, conrelid, conname, " + "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "); + if (fout->remoteVersion >= 90200) + { + /* + * convalidated is new in 9.2 (actually, it is there in 9.1, but + * it wasn't ever false for check constraints until 9.2). + */ + appendPQExpBufferStr(q, + "conislocal, convalidated "); + } + else if (fout->remoteVersion >= 80400) + { + /* conislocal is new in 8.4 */ + appendPQExpBufferStr(q, + "conislocal, true AS convalidated "); + } + else + { + appendPQExpBufferStr(q, + "true AS conislocal, true AS convalidated "); + } + appendPQExpBuffer(q, + "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n" + "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n" + "WHERE contype = 'c' " + "ORDER BY c.conrelid, c.conname", + checkoids->data); + + res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK); + + numConstrs = PQntuples(res); + constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo)); + + i_tableoid = PQfnumber(res, "tableoid"); + i_oid = PQfnumber(res, "oid"); + i_conrelid = PQfnumber(res, "conrelid"); + i_conname = PQfnumber(res, "conname"); + i_consrc = PQfnumber(res, "consrc"); + i_conislocal = PQfnumber(res, "conislocal"); + i_convalidated = PQfnumber(res, "convalidated"); + + /* As above, this loop iterates once per table, not once per row */ + curtblindx = -1; + for (int j = 0; j < numConstrs;) + { + Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid)); + TableInfo *tbinfo = NULL; + int numcons; + + /* Count rows for this table */ + for (numcons = 1; numcons < numConstrs - j; numcons++) + if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid) + break; + + /* + * Locate the associated TableInfo; we rely on tblinfo[] being in + * OID order. + */ + while (++curtblindx < numTables) + { + tbinfo = &tblinfo[curtblindx]; + if (tbinfo->dobj.catId.oid == conrelid) + break; + } + if (curtblindx >= numTables) + fatal("unrecognized table OID %u", conrelid); - numConstrs = PQntuples(res); - if (numConstrs != tbinfo->ncheck) + if (numcons != tbinfo->ncheck) { pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d", "expected %d check constraints on table \"%s\" but found %d", tbinfo->ncheck), - tbinfo->ncheck, tbinfo->dobj.name, numConstrs); + tbinfo->ncheck, tbinfo->dobj.name, numcons); pg_log_error("(The system catalogs might be corrupted.)"); exit_nicely(1); } - constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo)); - tbinfo->checkexprs = constrs; + tbinfo->checkexprs = constrs + j; - for (int j = 0; j < numConstrs; j++) + for (int c = 0; c < numcons; c++, j++) { - bool validated = PQgetvalue(res, j, 5)[0] == 't'; + bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't'; constrs[j].dobj.objType = DO_CONSTRAINT; - constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0)); - constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1)); + constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid)); + constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid)); AssignDumpId(&constrs[j].dobj); - constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, 2)); + constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname)); constrs[j].dobj.namespace = tbinfo->dobj.namespace; constrs[j].contable = tbinfo; constrs[j].condomain = NULL; constrs[j].contype = 'c'; - constrs[j].condef = pg_strdup(PQgetvalue(res, j, 3)); + constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc)); constrs[j].confrelid = InvalidOid; constrs[j].conindex = 0; constrs[j].condeferrable = false; constrs[j].condeferred = false; - constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't'); + constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't'); /* * An unvalidated constraint needs to be dumped separately, so @@ -10259,11 +9946,14 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) * constraint must be split out from the table definition. */ } - PQclear(res); } + + PQclear(res); } destroyPQExpBuffer(q); + destroyPQExpBuffer(tbloids); + destroyPQExpBuffer(checkoids); } /* @@ -10319,13 +10009,6 @@ getTSParsers(Archive *fout, int *numTSParsers) int i_prsheadline; int i_prslextype; - /* Before 8.3, there is no built-in text search support */ - if (fout->remoteVersion < 80300) - { - *numTSParsers = 0; - return NULL; - } - query = createPQExpBuffer(); /* @@ -10372,9 +10055,6 @@ getTSParsers(Archive *fout, int *numTSParsers) /* Decide whether we want to dump it */ selectDumpableObject(&(prsinfo[i].dobj), fout); - - /* Text Search Parsers do not currently have ACLs. */ - prsinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -10403,24 +10083,16 @@ getTSDictionaries(Archive *fout, int *numTSDicts) int i_oid; int i_dictname; int i_dictnamespace; - int i_rolname; + int i_dictowner; int i_dicttemplate; int i_dictinitoption; - /* Before 8.3, there is no built-in text search support */ - if (fout->remoteVersion < 80300) - { - *numTSDicts = 0; - return NULL; - } - query = createPQExpBuffer(); appendPQExpBuffer(query, "SELECT tableoid, oid, dictname, " - "dictnamespace, (%s dictowner) AS rolname, " + "dictnamespace, dictowner, " "dicttemplate, dictinitoption " - "FROM pg_ts_dict", - username_subquery); + "FROM pg_ts_dict"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -10433,7 +10105,7 @@ getTSDictionaries(Archive *fout, int *numTSDicts) i_oid = PQfnumber(res, "oid"); i_dictname = PQfnumber(res, "dictname"); i_dictnamespace = PQfnumber(res, "dictnamespace"); - i_rolname = PQfnumber(res, "rolname"); + i_dictowner = PQfnumber(res, "dictowner"); i_dictinitoption = PQfnumber(res, "dictinitoption"); i_dicttemplate = PQfnumber(res, "dicttemplate"); @@ -10446,7 +10118,7 @@ getTSDictionaries(Archive *fout, int *numTSDicts) dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname)); dictinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace))); - dictinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner)); dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate)); if (PQgetisnull(res, i, i_dictinitoption)) dictinfo[i].dictinitoption = NULL; @@ -10455,9 +10127,6 @@ getTSDictionaries(Archive *fout, int *numTSDicts) /* Decide whether we want to dump it */ selectDumpableObject(&(dictinfo[i].dobj), fout); - - /* Text Search Dictionaries do not currently have ACLs. */ - dictinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -10489,13 +10158,6 @@ getTSTemplates(Archive *fout, int *numTSTemplates) int i_tmplinit; int i_tmpllexize; - /* Before 8.3, there is no built-in text search support */ - if (fout->remoteVersion < 80300) - { - *numTSTemplates = 0; - return NULL; - } - query = createPQExpBuffer(); appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, " @@ -10530,9 +10192,6 @@ getTSTemplates(Archive *fout, int *numTSTemplates) /* Decide whether we want to dump it */ selectDumpableObject(&(tmplinfo[i].dobj), fout); - - /* Text Search Templates do not currently have ACLs. */ - tmplinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -10561,22 +10220,14 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) int i_oid; int i_cfgname; int i_cfgnamespace; - int i_rolname; + int i_cfgowner; int i_cfgparser; - /* Before 8.3, there is no built-in text search support */ - if (fout->remoteVersion < 80300) - { - *numTSConfigs = 0; - return NULL; - } - query = createPQExpBuffer(); appendPQExpBuffer(query, "SELECT tableoid, oid, cfgname, " - "cfgnamespace, (%s cfgowner) AS rolname, cfgparser " - "FROM pg_ts_config", - username_subquery); + "cfgnamespace, cfgowner, cfgparser " + "FROM pg_ts_config"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -10589,7 +10240,7 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) i_oid = PQfnumber(res, "oid"); i_cfgname = PQfnumber(res, "cfgname"); i_cfgnamespace = PQfnumber(res, "cfgnamespace"); - i_rolname = PQfnumber(res, "rolname"); + i_cfgowner = PQfnumber(res, "cfgowner"); i_cfgparser = PQfnumber(res, "cfgparser"); for (i = 0; i < ntups; i++) @@ -10601,14 +10252,11 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname)); cfginfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace))); - cfginfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner)); cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser)); /* Decide whether we want to dump it */ selectDumpableObject(&(cfginfo[i].dobj), fout); - - /* Text Search Configurations do not currently have ACLs. */ - cfginfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; } PQclear(res); @@ -10628,7 +10276,6 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) FdwInfo * getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -10637,13 +10284,11 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) int i_tableoid; int i_oid; int i_fdwname; - int i_rolname; + int i_fdwowner; int i_fdwhandler; int i_fdwvalidator; int i_fdwacl; - int i_rfdwacl; - int i_initfdwacl; - int i_initrfdwacl; + int i_acldefault; int i_fdwoptions; /* Before 8.4, there are no foreign-data wrappers */ @@ -10655,80 +10300,51 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) query = createPQExpBuffer(); - if (fout->remoteVersion >= 90600) + if (fout->remoteVersion >= 90200) { - 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, " + appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, " + "fdwowner, " + "fdwhandler::pg_catalog.regproc, " + "fdwvalidator::pg_catalog.regproc, " + "fdwacl, " + "acldefault('F', fdwowner) AS acldefault, " "array_to_string(ARRAY(" "SELECT quote_ident(option_name) || ' ' || " "quote_literal(option_value) " - "FROM pg_options_to_table(f.fdwoptions) " + "FROM pg_options_to_table(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 = 'pg_foreign_data_wrapper'::regclass " - "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); + "FROM pg_foreign_data_wrapper"); } else if (fout->remoteVersion >= 90100) { appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, " - "(%s fdwowner) AS rolname, " + "fdwowner, " "fdwhandler::pg_catalog.regproc, " "fdwvalidator::pg_catalog.regproc, fdwacl, " - "NULL as rfdwacl, " - "NULL as initfdwacl, NULL AS initrfdwacl, " + "NULL AS acldefault, " "array_to_string(ARRAY(" "SELECT quote_ident(option_name) || ' ' || " "quote_literal(option_value) " "FROM pg_options_to_table(fdwoptions) " "ORDER BY option_name" "), E',\n ') AS fdwoptions " - "FROM pg_foreign_data_wrapper", - username_subquery); + "FROM pg_foreign_data_wrapper"); } else { appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, " - "(%s fdwowner) AS rolname, " + "fdwowner, " "'-' AS fdwhandler, " "fdwvalidator::pg_catalog.regproc, fdwacl, " - "NULL as rfdwacl, " - "NULL as initfdwacl, NULL AS initrfdwacl, " + "NULL AS acldefault, " "array_to_string(ARRAY(" "SELECT quote_ident(option_name) || ' ' || " "quote_literal(option_value) " "FROM pg_options_to_table(fdwoptions) " "ORDER BY option_name" "), E',\n ') AS fdwoptions " - "FROM pg_foreign_data_wrapper", - username_subquery); + "FROM pg_foreign_data_wrapper"); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -10741,13 +10357,11 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) i_tableoid = PQfnumber(res, "tableoid"); i_oid = PQfnumber(res, "oid"); i_fdwname = PQfnumber(res, "fdwname"); - i_rolname = PQfnumber(res, "rolname"); + i_fdwowner = PQfnumber(res, "fdwowner"); 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_acldefault = PQfnumber(res, "acldefault"); i_fdwoptions = PQfnumber(res, "fdwoptions"); for (i = 0; i < ntups; i++) @@ -10758,14 +10372,14 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) AssignDumpId(&fdwinfo[i].dobj); fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname)); fdwinfo[i].dobj.namespace = NULL; - fdwinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl)); + fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + fdwinfo[i].dacl.privtype = 0; + fdwinfo[i].dacl.initprivs = NULL; + fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner)); fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler)); 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), fout); @@ -10774,11 +10388,9 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) if (fdwinfo[i].dobj.catId.oid < (Oid) FirstNormalObjectId) fdwinfo[i].dobj.dump = DUMP_COMPONENT_NONE; - /* 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; + /* Mark whether FDW has an ACL */ + if (!PQgetisnull(res, i, i_fdwacl)) + fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL; } PQclear(res); @@ -10798,7 +10410,6 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) ForeignServerInfo * getForeignServers(Archive *fout, int *numForeignServers) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -10807,14 +10418,12 @@ getForeignServers(Archive *fout, int *numForeignServers) int i_tableoid; int i_oid; int i_srvname; - int i_rolname; + int i_srvowner; int i_srvfdw; int i_srvtype; int i_srvversion; int i_srvacl; - int i_rsrvacl; - int i_initsrvacl; - int i_initrsrvacl; + int i_acldefault; int i_srvoptions; /* Before 8.4, there are no foreign servers */ @@ -10826,61 +10435,33 @@ getForeignServers(Archive *fout, int *numForeignServers) query = createPQExpBuffer(); - if (fout->remoteVersion >= 90600) + if (fout->remoteVersion >= 90200) { - 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, " + appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, " + "srvowner, " + "srvfdw, srvtype, srvversion, srvacl, " + "acldefault('S', srvowner) AS acldefault, " "array_to_string(ARRAY(" "SELECT quote_ident(option_name) || ' ' || " "quote_literal(option_value) " - "FROM pg_options_to_table(f.srvoptions) " + "FROM pg_options_to_table(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 = 'pg_foreign_server'::regclass " - "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); + "FROM pg_foreign_server"); } else { appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, " - "(%s srvowner) AS rolname, " + "srvowner, " "srvfdw, srvtype, srvversion, srvacl, " - "NULL AS rsrvacl, " - "NULL AS initsrvacl, NULL AS initrsrvacl, " + "NULL AS acldefault, " "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); + "FROM pg_foreign_server"); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -10893,14 +10474,12 @@ getForeignServers(Archive *fout, int *numForeignServers) i_tableoid = PQfnumber(res, "tableoid"); i_oid = PQfnumber(res, "oid"); i_srvname = PQfnumber(res, "srvname"); - i_rolname = PQfnumber(res, "rolname"); + i_srvowner = PQfnumber(res, "srvowner"); i_srvfdw = PQfnumber(res, "srvfdw"); 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_acldefault = PQfnumber(res, "acldefault"); i_srvoptions = PQfnumber(res, "srvoptions"); for (i = 0; i < ntups; i++) @@ -10911,15 +10490,15 @@ getForeignServers(Archive *fout, int *numForeignServers) AssignDumpId(&srvinfo[i].dobj); srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname)); srvinfo[i].dobj.namespace = NULL; - srvinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl)); + srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + srvinfo[i].dacl.privtype = 0; + srvinfo[i].dacl.initprivs = NULL; + srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner)); srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw)); srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype)); 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), fout); @@ -10928,11 +10507,12 @@ getForeignServers(Archive *fout, int *numForeignServers) if (srvinfo[i].dobj.catId.oid < (Oid) FirstNormalObjectId) srvinfo[i].dobj.dump = DUMP_COMPONENT_NONE; - /* 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; + /* Servers have user mappings */ + srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP; + + /* Mark whether server has an ACL */ + if (!PQgetisnull(res, i, i_srvacl)) + srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL; } PQclear(res); @@ -10962,9 +10542,7 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs) int i_defaclnamespace; int i_defaclobjtype; int i_defaclacl; - int i_rdefaclacl; - int i_initdefaclacl; - int i_initrdefaclacl; + int i_acldefault; int i, ntups; @@ -10976,13 +10554,15 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs) query = createPQExpBuffer(); - if (fout->remoteVersion >= 90600) - { - PQExpBuffer acl_subquery = createPQExpBuffer(); - PQExpBuffer racl_subquery = createPQExpBuffer(); - PQExpBuffer initacl_subquery = createPQExpBuffer(); - PQExpBuffer initracl_subquery = createPQExpBuffer(); + appendPQExpBuffer(query, + "SELECT oid, tableoid, " + "defaclrole, " + "defaclnamespace, " + "defaclobjtype, " + "defaclacl, "); + if (fout->remoteVersion >= 90200) + { /* * Global entries (with defaclnamespace=0) replace the hard-wired * default ACL for their object type. We should dump them as deltas @@ -10990,58 +10570,24 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs) * for interpreting the ALTER DEFAULT PRIVILEGES commands. On the * other hand, non-global entries can only add privileges not revoke * them. We must dump those as-is (i.e., as deltas from an empty - * ACL). We implement that by passing NULL as the object type for - * acldefault(), which works because acldefault() is STRICT. + * ACL). * * We can use defaclobjtype as the object type for acldefault(), * except for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be * converted to 's'. */ - buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, - initracl_subquery, "defaclacl", "defaclrole", - "CASE WHEN defaclnamespace = 0 THEN" - " CASE WHEN defaclobjtype = 'S' THEN 's'::\"char\"" - " ELSE defaclobjtype END " - "ELSE NULL END", - dopt->binary_upgrade); - - appendPQExpBuffer(query, "SELECT d.oid, d.tableoid, " - "(%s d.defaclrole) AS defaclrole, " - "d.defaclnamespace, " - "d.defaclobjtype, " - "%s AS defaclacl, " - "%s AS rdefaclacl, " - "%s AS initdefaclacl, " - "%s AS initrdefaclacl " - "FROM pg_default_acl d " - "LEFT JOIN pg_init_privs pip ON " - "(d.oid = pip.objoid " - "AND pip.classoid = 'pg_default_acl'::regclass " - "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); + appendPQExpBufferStr(query, + "CASE WHEN defaclnamespace = 0 THEN " + "acldefault(CASE WHEN defaclobjtype = 'S' " + "THEN 's'::\"char\" ELSE defaclobjtype END, " + "defaclrole) ELSE '{}' END AS acldefault "); } else - { - appendPQExpBuffer(query, "SELECT oid, tableoid, " - "(%s defaclrole) AS defaclrole, " - "defaclnamespace, " - "defaclobjtype, " - "defaclacl, " - "NULL AS rdefaclacl, " - "NULL AS initdefaclacl, " - "NULL AS initrdefaclacl " - "FROM pg_default_acl", - username_subquery); - } + appendPQExpBufferStr(query, + "NULL AS acldefault "); + + appendPQExpBufferStr(query, + "FROM pg_default_acl"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -11056,9 +10602,7 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs) i_defaclnamespace = PQfnumber(res, "defaclnamespace"); i_defaclobjtype = PQfnumber(res, "defaclobjtype"); i_defaclacl = PQfnumber(res, "defaclacl"); - i_rdefaclacl = PQfnumber(res, "rdefaclacl"); - i_initdefaclacl = PQfnumber(res, "initdefaclacl"); - i_initrdefaclacl = PQfnumber(res, "initrdefaclacl"); + i_acldefault = PQfnumber(res, "acldefault"); for (i = 0; i < ntups; i++) { @@ -11076,12 +10620,15 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs) else daclinfo[i].dobj.namespace = NULL; - daclinfo[i].defaclrole = pg_strdup(PQgetvalue(res, i, i_defaclrole)); + daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl)); + daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + daclinfo[i].dacl.privtype = 0; + daclinfo[i].dacl.initprivs = NULL; + daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole)); daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype)); - daclinfo[i].defaclacl = pg_strdup(PQgetvalue(res, i, i_defaclacl)); - daclinfo[i].rdefaclacl = pg_strdup(PQgetvalue(res, i, i_rdefaclacl)); - daclinfo[i].initdefaclacl = pg_strdup(PQgetvalue(res, i, i_initdefaclacl)); - daclinfo[i].initrdefaclacl = pg_strdup(PQgetvalue(res, i, i_initrdefaclacl)); + + /* Default ACLs are ACLs, of course */ + daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL; /* Decide whether we want to dump it */ selectDumpableDefaultACL(&(daclinfo[i]), dopt); @@ -11094,6 +10641,183 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs) return daclinfo; } +/* + * getRoleName -- look up the name of a role, given its OID + * + * In current usage, we don't expect failures, so error out for a bad OID. + */ +static const char * +getRoleName(const char *roleoid_str) +{ + Oid roleoid = atooid(roleoid_str); + + /* + * Do binary search to find the appropriate item. + */ + if (nrolenames > 0) + { + RoleNameItem *low = &rolenames[0]; + RoleNameItem *high = &rolenames[nrolenames - 1]; + + while (low <= high) + { + RoleNameItem *middle = low + (high - low) / 2; + + if (roleoid < middle->roleoid) + high = middle - 1; + else if (roleoid > middle->roleoid) + low = middle + 1; + else + return middle->rolename; /* found a match */ + } + } + + fatal("role with OID %u does not exist", roleoid); + return NULL; /* keep compiler quiet */ +} + +/* + * collectRoleNames -- + * + * Construct a table of all known roles. + * The table is sorted by OID for speed in lookup. + */ +static void +collectRoleNames(Archive *fout) +{ + PGresult *res; + const char *query; + int i; + + query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1"; + + res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK); + + nrolenames = PQntuples(res); + + rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem)); + + for (i = 0; i < nrolenames; i++) + { + rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0)); + rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1)); + } + + PQclear(res); +} + +/* + * getAdditionalACLs + * + * We have now created all the DumpableObjects, and collected the ACL data + * that appears in the directly-associated catalog entries. However, there's + * more ACL-related info to collect. If any of a table's columns have ACLs, + * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as + * its hascolumnACLs flag (we won't store the ACLs themselves here, though). + * Also, in versions having the pg_init_privs catalog, read that and load the + * information into the relevant DumpableObjects. + */ +static void +getAdditionalACLs(Archive *fout) +{ + PQExpBuffer query = createPQExpBuffer(); + PGresult *res; + int ntups, + i; + + /* Check for per-column ACLs */ + if (fout->remoteVersion >= 80400) + { + appendPQExpBufferStr(query, + "SELECT DISTINCT attrelid FROM pg_attribute " + "WHERE attacl IS NOT NULL"); + + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + for (i = 0; i < ntups; i++) + { + Oid relid = atooid(PQgetvalue(res, i, 0)); + TableInfo *tblinfo; + + tblinfo = findTableByOid(relid); + /* OK to ignore tables we haven't got a DumpableObject for */ + if (tblinfo) + { + tblinfo->dobj.components |= DUMP_COMPONENT_ACL; + tblinfo->hascolumnACLs = true; + } + } + PQclear(res); + } + + /* Fetch initial-privileges data */ + if (fout->remoteVersion >= 90600) + { + printfPQExpBuffer(query, + "SELECT objoid, classoid, objsubid, privtype, initprivs " + "FROM pg_init_privs"); + + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + for (i = 0; i < ntups; i++) + { + Oid objoid = atooid(PQgetvalue(res, i, 0)); + Oid classoid = atooid(PQgetvalue(res, i, 1)); + int objsubid = atoi(PQgetvalue(res, i, 2)); + char privtype = *(PQgetvalue(res, i, 3)); + char *initprivs = PQgetvalue(res, i, 4); + CatalogId objId; + DumpableObject *dobj; + + objId.tableoid = classoid; + objId.oid = objoid; + dobj = findObjectByCatalogId(objId); + /* OK to ignore entries we haven't got a DumpableObject for */ + if (dobj) + { + /* Cope with sub-object initprivs */ + if (objsubid != 0) + { + if (dobj->objType == DO_TABLE) + { + /* For a column initpriv, set the table's ACL flags */ + dobj->components |= DUMP_COMPONENT_ACL; + ((TableInfo *) dobj)->hascolumnACLs = true; + } + else + pg_log_warning("unsupported pg_init_privs entry: %u %u %d", + classoid, objoid, objsubid); + continue; + } + + /* Else it had better be of a type we think has ACLs */ + if (dobj->objType == DO_NAMESPACE || + dobj->objType == DO_TYPE || + dobj->objType == DO_FUNC || + dobj->objType == DO_AGG || + dobj->objType == DO_TABLE || + dobj->objType == DO_PROCLANG || + dobj->objType == DO_FDW || + dobj->objType == DO_FOREIGN_SERVER) + { + DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj; + + daobj->dacl.privtype = privtype; + daobj->dacl.initprivs = pstrdup(initprivs); + } + else + pg_log_warning("unsupported pg_init_privs entry: %u %u %d", + classoid, objoid, objsubid); + } + } + PQclear(res); + } + + destroyPQExpBuffer(query); +} + /* * dumpComment -- * @@ -11303,19 +11027,11 @@ static int findComments(Archive *fout, Oid classoid, Oid objoid, CommentItem **items) { - /* static storage for table of comments */ - static CommentItem *comments = NULL; - static int ncomments = -1; - CommentItem *middle = NULL; CommentItem *low; CommentItem *high; int nmatch; - /* Get comments if we didn't already */ - if (ncomments < 0) - ncomments = collectComments(fout, &comments); - /* * Do binary search to find some item matching the object. */ @@ -11376,15 +11092,17 @@ findComments(Archive *fout, Oid classoid, Oid objoid, /* * collectComments -- * - * Construct a table of all comments available for database objects. + * Construct a table of all comments available for database objects; + * also set the has-comment component flag for each relevant object. + * * We used to do per-object queries for the comments, but it's much faster * to pull them all over at once, and on most databases the memory cost * isn't high. * * The table is sorted by classoid/objid/objsubid for speed in lookup. */ -static int -collectComments(Archive *fout, CommentItem **items) +static void +collectComments(Archive *fout) { PGresult *res; PQExpBuffer query; @@ -11394,7 +11112,7 @@ collectComments(Archive *fout, CommentItem **items) int i_objsubid; int ntups; int i; - CommentItem *comments; + DumpableObject *dobj; query = createPQExpBuffer(); @@ -11414,20 +11132,52 @@ collectComments(Archive *fout, CommentItem **items) ntups = PQntuples(res); comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem)); + ncomments = 0; + dobj = NULL; for (i = 0; i < ntups; i++) { - comments[i].descr = PQgetvalue(res, i, i_description); - comments[i].classoid = atooid(PQgetvalue(res, i, i_classoid)); - comments[i].objoid = atooid(PQgetvalue(res, i, i_objoid)); - comments[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid)); + CatalogId objId; + int subid; + + objId.tableoid = atooid(PQgetvalue(res, i, i_classoid)); + objId.oid = atooid(PQgetvalue(res, i, i_objoid)); + subid = atoi(PQgetvalue(res, i, i_objsubid)); + + /* We needn't remember comments that don't match any dumpable object */ + if (dobj == NULL || + dobj->catId.tableoid != objId.tableoid || + dobj->catId.oid != objId.oid) + dobj = findObjectByCatalogId(objId); + if (dobj == NULL) + continue; + + /* + * Comments on columns of composite types are linked to the type's + * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag + * in the type's own DumpableObject. + */ + if (subid != 0 && dobj->objType == DO_TABLE && + ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE) + { + TypeInfo *cTypeInfo; + + cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype); + if (cTypeInfo) + cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT; + } + else + dobj->components |= DUMP_COMPONENT_COMMENT; + + comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description)); + comments[ncomments].classoid = objId.tableoid; + comments[ncomments].objoid = objId.oid; + comments[ncomments].objsubid = subid; + ncomments++; } - /* Do NOT free the PGresult since we are keeping pointers into it */ + PQclear(res); destroyPQExpBuffer(query); - - *items = comments; - return ntups; } /* @@ -11437,8 +11187,19 @@ collectComments(Archive *fout, CommentItem **items) * ArchiveEntries (TOC objects) for each object to be dumped. */ static void -dumpDumpableObject(Archive *fout, const DumpableObject *dobj) +dumpDumpableObject(Archive *fout, DumpableObject *dobj) { + /* + * Clear any dump-request bits for components that don't exist for this + * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the + * request for every kind of object.) + */ + dobj->dump &= dobj->components; + + /* Now, short-circuit if there's nothing to be done here. */ + if (dobj->dump == 0) + return; + switch (dobj->objType) { case DO_NAMESPACE: @@ -11621,8 +11382,8 @@ dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo) PQExpBuffer delq; char *qnspname; - /* Skip if not to be dumped */ - if (!nspinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; q = createPQExpBuffer(); @@ -11664,8 +11425,7 @@ dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo) if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL) dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA", qnspname, NULL, NULL, - nspinfo->rolname, nspinfo->nspacl, nspinfo->rnspacl, - nspinfo->initnspacl, nspinfo->initrnspacl); + nspinfo->rolname, &nspinfo->dacl); free(qnspname); @@ -11685,8 +11445,8 @@ dumpExtension(Archive *fout, const ExtensionInfo *extinfo) PQExpBuffer delq; char *qextname; - /* Skip if not to be dumped */ - if (!extinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; q = createPQExpBuffer(); @@ -11810,8 +11570,8 @@ dumpType(Archive *fout, const TypeInfo *tyinfo) { DumpOptions *dopt = fout->dopt; - /* Skip if not to be dumped */ - if (!tyinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; /* Dump out in proper style */ @@ -11854,19 +11614,34 @@ dumpEnumType(Archive *fout, const TypeInfo *tyinfo) char *qtypname; char *qualtypname; char *label; + int i_enumlabel; + int i_oid; + + if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE]) + { + /* Set up query for enum-specific details */ + appendPQExpBufferStr(query, + "PREPARE dumpEnumType(pg_catalog.oid) AS\n"); + + if (fout->remoteVersion >= 90100) + appendPQExpBufferStr(query, "SELECT oid, enumlabel " + "FROM pg_catalog.pg_enum " + "WHERE enumtypid = $1 " + "ORDER BY enumsortorder"); + else + appendPQExpBufferStr(query, "SELECT oid, enumlabel " + "FROM pg_catalog.pg_enum " + "WHERE enumtypid = $1 " + "ORDER BY oid"); - if (fout->remoteVersion >= 90100) - appendPQExpBuffer(query, "SELECT oid, enumlabel " - "FROM pg_catalog.pg_enum " - "WHERE enumtypid = '%u'" - "ORDER BY enumsortorder", - tyinfo->dobj.catId.oid); - else - appendPQExpBuffer(query, "SELECT oid, enumlabel " - "FROM pg_catalog.pg_enum " - "WHERE enumtypid = '%u'" - "ORDER BY oid", - tyinfo->dobj.catId.oid); + ExecuteSqlStatement(fout, query->data); + + fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true; + } + + printfPQExpBuffer(query, + "EXECUTE dumpEnumType('%u')", + tyinfo->dobj.catId.oid); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -11893,10 +11668,12 @@ dumpEnumType(Archive *fout, const TypeInfo *tyinfo) if (!dopt->binary_upgrade) { + i_enumlabel = PQfnumber(res, "enumlabel"); + /* Labels with server-assigned oids */ for (i = 0; i < num; i++) { - label = PQgetvalue(res, i, PQfnumber(res, "enumlabel")); + label = PQgetvalue(res, i, i_enumlabel); if (i > 0) appendPQExpBufferChar(q, ','); appendPQExpBufferStr(q, "\n "); @@ -11908,11 +11685,14 @@ dumpEnumType(Archive *fout, const TypeInfo *tyinfo) if (dopt->binary_upgrade) { + i_oid = PQfnumber(res, "oid"); + i_enumlabel = PQfnumber(res, "enumlabel"); + /* Labels with dump-assigned (preserved) oids */ for (i = 0; i < num; i++) { - enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))); - label = PQgetvalue(res, i, PQfnumber(res, "enumlabel")); + enum_oid = atooid(PQgetvalue(res, i, i_oid)); + label = PQgetvalue(res, i, i_enumlabel); if (i == 0) appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n"); @@ -11955,8 +11735,7 @@ dumpEnumType(Archive *fout, const TypeInfo *tyinfo) dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE", qtypname, NULL, tyinfo->dobj.namespace->dobj.name, - tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, - tyinfo->inittypacl, tyinfo->initrtypacl); + tyinfo->rolname, &tyinfo->dacl); PQclear(res); destroyPQExpBuffer(q); @@ -11983,29 +11762,43 @@ dumpRangeType(Archive *fout, const TypeInfo *tyinfo) char *qualtypname; char *procname; - appendPQExpBuffer(query, - "SELECT "); + if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE]) + { + /* Set up query for range-specific details */ + appendPQExpBufferStr(query, + "PREPARE dumpRangeType(pg_catalog.oid) AS\n"); - if (fout->remoteVersion >= 140000) - appendPQExpBuffer(query, - "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, "); - else - appendPQExpBuffer(query, - "NULL AS rngmultitype, "); + appendPQExpBufferStr(query, + "SELECT "); - appendPQExpBuffer(query, - "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, " - "opc.opcname AS opcname, " - "(SELECT nspname FROM pg_catalog.pg_namespace nsp " - " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, " - "opc.opcdefault, " - "CASE WHEN rngcollation = st.typcollation THEN 0 " - " ELSE rngcollation END AS collation, " - "rngcanonical, rngsubdiff " - "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, " - " pg_catalog.pg_opclass opc " - "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND " - "rngtypid = '%u'", + if (fout->remoteVersion >= 140000) + appendPQExpBuffer(query, + "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, "); + else + appendPQExpBuffer(query, + "NULL AS rngmultitype, "); + + appendPQExpBufferStr(query, + "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, " + "opc.opcname AS opcname, " + "(SELECT nspname FROM pg_catalog.pg_namespace nsp " + " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, " + "opc.opcdefault, " + "CASE WHEN rngcollation = st.typcollation THEN 0 " + " ELSE rngcollation END AS collation, " + "rngcanonical, rngsubdiff " + "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, " + " pg_catalog.pg_opclass opc " + "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND " + "rngtypid = $1"); + + ExecuteSqlStatement(fout, query->data); + + fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true; + } + + printfPQExpBuffer(query, + "EXECUTE dumpRangeType('%u')", tyinfo->dobj.catId.oid); res = ExecuteSqlQueryForSingleRow(fout, query->data); @@ -12097,8 +11890,7 @@ dumpRangeType(Archive *fout, const TypeInfo *tyinfo) dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE", qtypname, NULL, tyinfo->dobj.namespace->dobj.name, - tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, - tyinfo->inittypacl, tyinfo->initrtypacl); + tyinfo->rolname, &tyinfo->dacl); PQclear(res); destroyPQExpBuffer(q); @@ -12171,8 +11963,7 @@ dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo) dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE", qtypname, NULL, tyinfo->dobj.namespace->dobj.name, - tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, - tyinfo->inittypacl, tyinfo->initrtypacl); + tyinfo->rolname, &tyinfo->dacl); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); @@ -12219,55 +12010,61 @@ dumpBaseType(Archive *fout, const TypeInfo *tyinfo) char *typdefault; bool typdefault_is_literal = false; - /* Fetch type-specific details */ - appendPQExpBufferStr(query, "SELECT typlen, " - "typinput, typoutput, typreceive, typsend, " - "typreceive::pg_catalog.oid AS typreceiveoid, " - "typsend::pg_catalog.oid AS typsendoid, " - "typanalyze, " - "typanalyze::pg_catalog.oid AS typanalyzeoid, " - "typdelim, typbyval, typalign, typstorage, "); - - if (fout->remoteVersion >= 80300) - appendPQExpBufferStr(query, - "typmodin, typmodout, " - "typmodin::pg_catalog.oid AS typmodinoid, " - "typmodout::pg_catalog.oid AS typmodoutoid, "); - else + if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE]) + { + /* Set up query for type-specific details */ appendPQExpBufferStr(query, - "'-' AS typmodin, '-' AS typmodout, " - "0 AS typmodinoid, 0 AS typmodoutoid, "); + "PREPARE dumpBaseType(pg_catalog.oid) AS\n"); + + appendPQExpBufferStr(query, "SELECT typlen, " + "typinput, typoutput, typreceive, typsend, " + "typreceive::pg_catalog.oid AS typreceiveoid, " + "typsend::pg_catalog.oid AS typsendoid, " + "typanalyze, " + "typanalyze::pg_catalog.oid AS typanalyzeoid, " + "typdelim, typbyval, typalign, typstorage, " + "typmodin, typmodout, " + "typmodin::pg_catalog.oid AS typmodinoid, " + "typmodout::pg_catalog.oid AS typmodoutoid, "); + + if (fout->remoteVersion >= 80400) + appendPQExpBufferStr(query, + "typcategory, typispreferred, "); + else + appendPQExpBufferStr(query, + "'U' AS typcategory, false AS typispreferred, "); - if (fout->remoteVersion >= 80400) - appendPQExpBufferStr(query, - "typcategory, typispreferred, "); - else - appendPQExpBufferStr(query, - "'U' AS typcategory, false AS typispreferred, "); + if (fout->remoteVersion >= 140000) + appendPQExpBufferStr(query, + "typsubscript, " + "typsubscript::pg_catalog.oid AS typsubscriptoid, "); + else + appendPQExpBufferStr(query, + "'-' AS typsubscript, 0 AS typsubscriptoid, "); + + if (fout->remoteVersion >= 90100) + appendPQExpBufferStr(query, "(typcollation <> 0) AS typcollatable, "); + else + appendPQExpBufferStr(query, "false AS typcollatable, "); - if (fout->remoteVersion >= 90100) - appendPQExpBufferStr(query, "(typcollation <> 0) AS typcollatable, "); - else - appendPQExpBufferStr(query, "false AS typcollatable, "); + /* Before 8.4, pg_get_expr does not allow 0 for its second arg */ + if (fout->remoteVersion >= 80400) + appendPQExpBufferStr(query, + "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault "); + else + appendPQExpBufferStr(query, + "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault "); - if (fout->remoteVersion >= 140000) - appendPQExpBufferStr(query, - "typsubscript, " - "typsubscript::pg_catalog.oid AS typsubscriptoid, "); - else - appendPQExpBufferStr(query, - "'-' AS typsubscript, 0 AS typsubscriptoid, "); + appendPQExpBuffer(query, "FROM pg_catalog.pg_type " + "WHERE oid = $1"); - /* Before 8.4, pg_get_expr does not allow 0 for its second arg */ - if (fout->remoteVersion >= 80400) - appendPQExpBufferStr(query, - "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault "); - else - appendPQExpBufferStr(query, - "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault "); + ExecuteSqlStatement(fout, query->data); - appendPQExpBuffer(query, "FROM pg_catalog.pg_type " - "WHERE oid = '%u'::pg_catalog.oid", + fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true; + } + + printfPQExpBuffer(query, + "EXECUTE dumpBaseType('%u')", tyinfo->dobj.catId.oid); res = ExecuteSqlQueryForSingleRow(fout, query->data); @@ -12433,8 +12230,7 @@ dumpBaseType(Archive *fout, const TypeInfo *tyinfo) dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE", qtypname, NULL, tyinfo->dobj.namespace->dobj.name, - tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, - tyinfo->inittypacl, tyinfo->initrtypacl); + tyinfo->rolname, &tyinfo->dacl); PQclear(res); destroyPQExpBuffer(q); @@ -12494,32 +12290,44 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo) Oid typcollation; bool typdefault_is_literal = false; - /* Fetch domain specific details */ - if (fout->remoteVersion >= 90100) - { - /* typcollation is new in 9.1 */ - appendPQExpBuffer(query, "SELECT t.typnotnull, " - "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, " - "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, " - "t.typdefault, " - "CASE WHEN t.typcollation <> u.typcollation " - "THEN t.typcollation ELSE 0 END AS typcollation " - "FROM pg_catalog.pg_type t " - "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) " - "WHERE t.oid = '%u'::pg_catalog.oid", - tyinfo->dobj.catId.oid); - } - else + if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN]) { - appendPQExpBuffer(query, "SELECT typnotnull, " - "pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, " - "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, " - "typdefault, 0 AS typcollation " - "FROM pg_catalog.pg_type " - "WHERE oid = '%u'::pg_catalog.oid", - tyinfo->dobj.catId.oid); + /* Set up query for domain-specific details */ + appendPQExpBufferStr(query, + "PREPARE dumpDomain(pg_catalog.oid) AS\n"); + + if (fout->remoteVersion >= 90100) + { + /* typcollation is new in 9.1 */ + appendPQExpBufferStr(query, "SELECT t.typnotnull, " + "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, " + "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, " + "t.typdefault, " + "CASE WHEN t.typcollation <> u.typcollation " + "THEN t.typcollation ELSE 0 END AS typcollation " + "FROM pg_catalog.pg_type t " + "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) " + "WHERE t.oid = $1"); + } + else + { + appendPQExpBufferStr(query, "SELECT typnotnull, " + "pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, " + "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, " + "typdefault, 0 AS typcollation " + "FROM pg_catalog.pg_type " + "WHERE oid = $1"); + } + + ExecuteSqlStatement(fout, query->data); + + fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true; } + printfPQExpBuffer(query, + "EXECUTE dumpDomain('%u')", + tyinfo->dobj.catId.oid); + res = ExecuteSqlQueryForSingleRow(fout, query->data); typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull")); @@ -12621,8 +12429,7 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo) dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE", qtypname, NULL, tyinfo->dobj.namespace->dobj.name, - tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, - tyinfo->inittypacl, tyinfo->initrtypacl); + tyinfo->rolname, &tyinfo->dacl); /* Dump any per-constraint comments */ for (i = 0; i < tyinfo->nDomChecks; i++) @@ -12675,45 +12482,57 @@ dumpCompositeType(Archive *fout, const TypeInfo *tyinfo) int i; int actual_atts; - /* Fetch type specific details */ - if (fout->remoteVersion >= 90100) - { - /* - * attcollation is new in 9.1. Since we only want to dump COLLATE - * clauses for attributes whose collation is different from their - * type's default, we use a CASE here to suppress uninteresting - * attcollations cheaply. atttypid will be 0 for dropped columns; - * collation does not matter for those. - */ - appendPQExpBuffer(query, "SELECT a.attname, " - "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, " - "a.attlen, a.attalign, a.attisdropped, " - "CASE WHEN a.attcollation <> at.typcollation " - "THEN a.attcollation ELSE 0 END AS attcollation " - "FROM pg_catalog.pg_type ct " - "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid " - "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid " - "WHERE ct.oid = '%u'::pg_catalog.oid " - "ORDER BY a.attnum ", - tyinfo->dobj.catId.oid); - } - else + if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE]) { - /* - * Since ALTER TYPE could not drop columns until 9.1, attisdropped - * should always be false. - */ - appendPQExpBuffer(query, "SELECT a.attname, " - "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, " - "a.attlen, a.attalign, a.attisdropped, " - "0 AS attcollation " - "FROM pg_catalog.pg_type ct, pg_catalog.pg_attribute a " - "WHERE ct.oid = '%u'::pg_catalog.oid " - "AND a.attrelid = ct.typrelid " - "ORDER BY a.attnum ", - tyinfo->dobj.catId.oid); + /* Set up query for type-specific details */ + appendPQExpBufferStr(query, + "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"); + + if (fout->remoteVersion >= 90100) + { + /* + * attcollation is new in 9.1. Since we only want to dump COLLATE + * clauses for attributes whose collation is different from their + * type's default, we use a CASE here to suppress uninteresting + * attcollations cheaply. atttypid will be 0 for dropped columns; + * collation does not matter for those. + */ + appendPQExpBufferStr(query, "SELECT a.attname, " + "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, " + "a.attlen, a.attalign, a.attisdropped, " + "CASE WHEN a.attcollation <> at.typcollation " + "THEN a.attcollation ELSE 0 END AS attcollation " + "FROM pg_catalog.pg_type ct " + "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid " + "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid " + "WHERE ct.oid = $1 " + "ORDER BY a.attnum"); + } + else + { + /* + * Since ALTER TYPE could not drop columns until 9.1, attisdropped + * should always be false. + */ + appendPQExpBufferStr(query, "SELECT a.attname, " + "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, " + "a.attlen, a.attalign, a.attisdropped, " + "0 AS attcollation " + "FROM pg_catalog.pg_type ct, pg_catalog.pg_attribute a " + "WHERE ct.oid = $1 " + "AND a.attrelid = ct.typrelid " + "ORDER BY a.attnum"); + } + + ExecuteSqlStatement(fout, query->data); + + fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true; } + printfPQExpBuffer(query, + "EXECUTE dumpCompositeType('%u')", + tyinfo->dobj.catId.oid); + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); ntups = PQntuples(res); @@ -12847,8 +12666,7 @@ dumpCompositeType(Archive *fout, const TypeInfo *tyinfo) dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE", qtypname, NULL, tyinfo->dobj.namespace->dobj.name, - tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, - tyinfo->inittypacl, tyinfo->initrtypacl); + tyinfo->rolname, &tyinfo->dacl); PQclear(res); destroyPQExpBuffer(q); @@ -12989,8 +12807,8 @@ dumpShellType(Archive *fout, const ShellTypeInfo *stinfo) DumpOptions *dopt = fout->dopt; PQExpBuffer q; - /* Skip if not to be dumped */ - if (!stinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; q = createPQExpBuffer(); @@ -13043,8 +12861,8 @@ dumpProcLang(Archive *fout, const ProcLangInfo *plang) FuncInfo *inlineInfo = NULL; FuncInfo *validatorInfo = NULL; - /* Skip if not to be dumped */ - if (!plang->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; /* @@ -13148,8 +12966,7 @@ dumpProcLang(Archive *fout, const ProcLangInfo *plang) if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL) dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE", qlanname, NULL, NULL, - plang->lanowner, plang->lanacl, plang->rlanacl, - plang->initlanacl, plang->initrlanacl); + plang->lanowner, &plang->dacl); free(qlanname); @@ -13397,7 +13214,6 @@ dumpFunc(Archive *fout, const FuncInfo *finfo) char **allargtypes = NULL; char **argmodes = NULL; char **argnames = NULL; - bool isGE50 = isGPDB5000OrLater(fout); char **configitems = NULL; int nconfigitems = 0; const char *keyword; @@ -13427,117 +13243,92 @@ dumpFunc(Archive *fout, const FuncInfo *finfo) delqry = createPQExpBuffer(); asPart = createPQExpBuffer(); - /* Fetch function-specific details */ - appendPQExpBufferStr(query, - "SELECT\n" - "proretset,\n" - "prosrc,\n" - "probin,\n" - "provolatile,\n" - "proisstrict,\n" - "prosecdef,\n" - "lanname,\n"); - - if (fout->remoteVersion >= 80300) - { - appendPQExpBufferStr(query, - "proconfig,\n" - "procost,\n" - "prorows,\n"); - - if (isGE50 && fout->remoteVersion < 90200) - { - appendPQExpBufferStr(query, - "prodataaccess,\n" - "'a' as proexeclocation,\n" - "(SELECT procallback FROM pg_catalog.pg_proc_callback WHERE profnoid::pg_catalog.oid = p.oid) as callbackfunc,\n"); - } - } - else + if (!fout->is_prepared[PREPQUERY_DUMPFUNC]) { - appendPQExpBufferStr(query, - "null AS proconfig,\n" - "0 AS procost,\n" - "0 AS prorows,\n"); + /* Set up query for function-specific details */ + appendPQExpBufferStr(query, "PREPARE dumpFunc(pg_catalog.oid) AS\n"); appendPQExpBuffer(query, - "%s\n" - "'a' as proexeclocation,\n" - "(SELECT procallback FROM pg_catalog.pg_proc_callback WHERE profnoid::pg_catalog.oid = p.oid) as callbackfunc,\n", - "prodataaccess,"); - } + "SELECT\n" + "proretset,\n" + "prosrc,\n" + "probin,\n" + "provolatile,\n" + "proisstrict,\n" + "prosecdef,\n" + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname,\n " + "proconfig,\n" + "procost,\n" + "prorows,\n" + "prodataaccess,\n" + "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n" + "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n" + "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n" + "(SELECT procallback FROM pg_catalog.pg_proc_callback WHERE profnoid::pg_catalog.oid = p.oid) as callbackfunc,\n"); - if (fout->remoteVersion >= 80400) - { - /* - * In GPDB 5.0 and up we rely on pg_get_function_arguments and - * pg_get_function_result instead of examining proallargtypes etc. - */ - appendPQExpBufferStr(query, - "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n" - "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n" - "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"); - } - else if (fout->remoteVersion >= 80100) - appendPQExpBufferStr(query, - "proallargtypes,\n" - "proargmodes,\n" - "proargnames,\n"); - else - appendPQExpBufferStr(query, - "null AS proallargtypes,\n" - "null AS proargmodes,\n" - "proargnames,\n"); + if (fout->remoteVersion >= 90200) + appendPQExpBuffer(query, + "proleakproof,\n"); + else + appendPQExpBuffer(query, + "false AS proleakproof,\n"); - if (fout->remoteVersion >= 90200) - appendPQExpBufferStr(query, - "prodataaccess,\n" - "proexeclocation,\n" - "(SELECT procallback FROM pg_catalog.pg_proc_callback WHERE profnoid::pg_catalog.oid = p.oid) as callbackfunc,\n" - "proleakproof,\n"); - else - appendPQExpBufferStr(query, - "false AS proleakproof,\n"); + /* GPDB6 added proexeclocation */ + if (fout->remoteVersion >= GPDB6_MAJOR_PGVERSION) + appendPQExpBuffer(query, + "proexeclocation,\n"); + else + appendPQExpBuffer(query, + "'a' as proexeclocation,\n"); - if (fout->remoteVersion >= 90500) - appendPQExpBufferStr(query, - "array_to_string(protrftypes, ' ') AS protrftypes,\n"); + if (fout->remoteVersion >= 90500) + appendPQExpBuffer(query, + "array_to_string(protrftypes, ' ') AS protrftypes,\n"); - if (fout->remoteVersion >= 90600) - appendPQExpBufferStr(query, - "proparallel,\n"); - else - appendPQExpBufferStr(query, - "'u' AS proparallel,\n"); + if (fout->remoteVersion >= 90600) + appendPQExpBuffer(query, + "proparallel,\n"); + else + appendPQExpBuffer(query, + "'u' AS proparallel,\n"); - if (fout->remoteVersion >= 110000) - appendPQExpBufferStr(query, - "prokind,\n"); - else if (fout->remoteVersion >= 80400) - appendPQExpBufferStr(query, - "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n"); - else - appendPQExpBufferStr(query, - "'f' AS prokind,\n"); + if (fout->remoteVersion >= 110000) + appendPQExpBuffer(query, + "prokind,\n"); + else if (fout->remoteVersion >= 80400) + appendPQExpBuffer(query, + "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n"); + else + appendPQExpBuffer(query, + "CASE WHEN proiswin THEN 'w' ELSE 'f' END AS prokind,\n"); - if (fout->remoteVersion >= 120000) - appendPQExpBufferStr(query, - "prosupport,\n"); - else - appendPQExpBufferStr(query, - "'-' AS prosupport,\n"); + if (fout->remoteVersion >= 120000) + appendPQExpBuffer(query, + "prosupport,\n"); + else + appendPQExpBuffer(query, + "'-' AS prosupport,\n"); - if (fout->remoteVersion >= 140000) - appendPQExpBufferStr(query, - "pg_get_function_sqlbody(p.oid) AS prosqlbody\n"); - else - appendPQExpBufferStr(query, - "NULL AS prosqlbody\n"); + if (fout->remoteVersion >= 140000) + appendPQExpBufferStr(query, + "pg_get_function_sqlbody(p.oid) AS prosqlbody\n"); + else + appendPQExpBufferStr(query, + "NULL AS prosqlbody\n"); - appendPQExpBuffer(query, - "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n" - "WHERE p.oid = '%u'::pg_catalog.oid " - "AND l.oid = p.prolang", + appendPQExpBuffer(query, + "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n" + "WHERE p.oid = $1 " + "AND l.oid = p.prolang"); + + ExecuteSqlStatement(fout, query->data); + + fout->is_prepared[PREPQUERY_DUMPFUNC] = true; + + } + + printfPQExpBuffer(query, + "EXECUTE dumpFunc('%u')", finfo->dobj.catId.oid); res = ExecuteSqlQueryForSingleRow(fout, query->data); @@ -13555,12 +13346,8 @@ dumpFunc(Archive *fout, const FuncInfo *finfo) probin = NULL; prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody")); } - /* - * GPDB_14_MERGE_FIXME: - * isGE50 is >= 80300 but upstream is (fout->remoteVersion >= 80400) - * Need to check. - */ - if (isGE50) + + if (fout->remoteVersion >= GPDB5_MAJOR_PGVERSION) { funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs")); funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs")); @@ -13717,17 +13504,11 @@ dumpFunc(Archive *fout, const FuncInfo *finfo) nconfigitems = 0; } - if (funcargs) - { - /* 8.4 or later; we rely on server-side code for most of the work */ - funcfullsig = format_function_arguments(finfo, funcargs, false); - funcsig = format_function_arguments(finfo, funciargs, false); - } - else - /* pre-8.4, do it ourselves */ - funcsig = format_function_arguments_old(fout, - finfo, nallargs, allargtypes, - argmodes, argnames); + + /* 8.4 or later; we rely on server-side code for most of the work */ + funcfullsig = format_function_arguments(finfo, funcargs, false); + funcsig = format_function_arguments(finfo, funciargs, false); + funcsig_tag = format_function_signature(fout, finfo, false); @@ -13971,8 +13752,7 @@ dumpFunc(Archive *fout, const FuncInfo *finfo) dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword, funcsig, NULL, finfo->dobj.namespace->dobj.name, - finfo->rolname, finfo->proacl, finfo->rproacl, - finfo->initproacl, finfo->initrproacl); + finfo->rolname, &finfo->dacl); PQclear(res); @@ -13996,6 +13776,7 @@ dumpFunc(Archive *fout, const FuncInfo *finfo) } + /* * Dump a user-defined cast */ @@ -14011,8 +13792,8 @@ dumpCast(Archive *fout, const CastInfo *cast) const char *sourceType; const char *targetType; - /* Skip if not to be dumped */ - if (!cast->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; /* Cannot dump if we don't have the cast function's info */ @@ -14117,8 +13898,8 @@ dumpTransform(Archive *fout, const TransformInfo *transform) char *lanname; const char *transformType; - /* Skip if not to be dumped */ - if (!transform->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; /* Cannot dump if we don't have the transform functions' info */ @@ -14266,8 +14047,8 @@ dumpOpr(Archive *fout, const OprInfo *oprinfo) char *oprregproc; char *oprref; - /* Skip if not to be dumped */ - if (!oprinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; /* @@ -14283,19 +14064,32 @@ dumpOpr(Archive *fout, const OprInfo *oprinfo) oprid = createPQExpBuffer(); details = createPQExpBuffer(); + if (!fout->is_prepared[PREPQUERY_DUMPOPR]) + { + /* Set up query for operator-specific details */ + appendPQExpBufferStr(query, + "PREPARE dumpOpr(pg_catalog.oid) AS\n"); + + appendPQExpBuffer(query, "SELECT oprkind, " + "oprcode::pg_catalog.regprocedure, " + "oprleft::pg_catalog.regtype, " + "oprright::pg_catalog.regtype, " + "oprcom, " + "oprnegate, " + "oprrest::pg_catalog.regprocedure, " + "oprjoin::pg_catalog.regprocedure, " + "oprcanmerge, oprcanhash " + "FROM pg_catalog.pg_operator " + "WHERE oid = $1"); + + ExecuteSqlStatement(fout, query->data); + + fout->is_prepared[PREPQUERY_DUMPOPR] = true; + } - appendPQExpBuffer(query, "SELECT oprkind, " - "oprcode::pg_catalog.regprocedure, " - "oprleft::pg_catalog.regtype, " - "oprright::pg_catalog.regtype, " - "oprcom, " - "oprnegate, " - "oprrest::pg_catalog.regprocedure, " - "oprjoin::pg_catalog.regprocedure, " - "oprcanmerge, oprcanhash " - "FROM pg_catalog.pg_operator " - "WHERE oid = '%u'::pg_catalog.oid", - oprinfo->dobj.catId.oid); + printfPQExpBuffer(query, + "EXECUTE dumpOpr('%u')", + oprinfo->dobj.catId.oid); res = ExecuteSqlQueryForSingleRow(fout, query->data); @@ -14541,8 +14335,8 @@ dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo) PQExpBuffer delq; char *qamname; - /* Skip if not to be dumped */ - if (!aminfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; q = createPQExpBuffer(); @@ -14646,8 +14440,8 @@ dumpOpclass(Archive *fout, const OpclassInfo *opcinfo) bool needComma; int i; - /* Skip if not to be dumped */ - if (!opcinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; query = createPQExpBuffer(); @@ -14656,32 +14450,17 @@ dumpOpclass(Archive *fout, const OpclassInfo *opcinfo) nameusing = createPQExpBuffer(); /* Get additional fields from the pg_opclass row */ - if (fout->remoteVersion >= 80300) - { - appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, " - "opckeytype::pg_catalog.regtype, " - "opcdefault, opcfamily, " - "opfname AS opcfamilyname, " - "nspname AS opcfamilynsp, " - "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname " - "FROM pg_catalog.pg_opclass c " - "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily " - "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace " - "WHERE c.oid = '%u'::pg_catalog.oid", - opcinfo->dobj.catId.oid); - } - else - { - appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, " - "opckeytype::pg_catalog.regtype, " - "opcdefault, NULL AS opcfamily, " - "NULL AS opcfamilyname, " - "NULL AS opcfamilynsp, " - "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcamid) AS amname " - "FROM pg_catalog.pg_opclass " - "WHERE oid = '%u'::pg_catalog.oid", - opcinfo->dobj.catId.oid); - } + appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, " + "opckeytype::pg_catalog.regtype, " + "opcdefault, opcfamily, " + "opfname AS opcfamilyname, " + "nspname AS opcfamilynsp, " + "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname " + "FROM pg_catalog.pg_opclass c " + "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily " + "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace " + "WHERE c.oid = '%u'::pg_catalog.oid", + opcinfo->dobj.catId.oid); res = ExecuteSqlQueryForSingleRow(fout, query->data); @@ -14781,7 +14560,7 @@ dumpOpclass(Archive *fout, const OpclassInfo *opcinfo) "ORDER BY amopstrategy", opcinfo->dobj.catId.oid); } - else if (fout->remoteVersion >= 80300) + else { appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, " "amopopr::pg_catalog.regoperator, " @@ -14791,22 +14570,7 @@ dumpOpclass(Archive *fout, const OpclassInfo *opcinfo) "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass " "AND refobjid = '%u'::pg_catalog.oid " "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass " - "AND objid = ao.oid " - "ORDER BY amopstrategy", - opcinfo->dobj.catId.oid); - } - else - { - /* - * Here, we print all entries since there are no opfamilies and hence - * no loose operators to worry about. - */ - appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, " - "amopopr::pg_catalog.regoperator, " - "NULL AS sortfamily, " - "NULL AS sortfamilynsp " - "FROM pg_catalog.pg_amop " - "WHERE amopclaid = '%u'::pg_catalog.oid " + "AND objid = ao.oid " "ORDER BY amopstrategy", opcinfo->dobj.catId.oid); } @@ -14864,31 +14628,17 @@ dumpOpclass(Archive *fout, const OpclassInfo *opcinfo) */ resetPQExpBuffer(query); - if (fout->remoteVersion >= 80300) - { - appendPQExpBuffer(query, "SELECT amprocnum, " - "amproc::pg_catalog.regprocedure, " - "amproclefttype::pg_catalog.regtype, " - "amprocrighttype::pg_catalog.regtype " - "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend " - "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass " - "AND refobjid = '%u'::pg_catalog.oid " - "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass " - "AND objid = ap.oid " - "ORDER BY amprocnum", - opcinfo->dobj.catId.oid); - } - else - { - appendPQExpBuffer(query, "SELECT amprocnum, " - "amproc::pg_catalog.regprocedure, " - "'' AS amproclefttype, " - "'' AS amprocrighttype " - "FROM pg_catalog.pg_amproc " - "WHERE amopclaid = '%u'::pg_catalog.oid " - "ORDER BY amprocnum", - opcinfo->dobj.catId.oid); - } + appendPQExpBuffer(query, "SELECT amprocnum, " + "amproc::pg_catalog.regprocedure, " + "amproclefttype::pg_catalog.regtype, " + "amprocrighttype::pg_catalog.regtype " + "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend " + "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass " + "AND refobjid = '%u'::pg_catalog.oid " + "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass " + "AND objid = ap.oid " + "ORDER BY amprocnum", + opcinfo->dobj.catId.oid); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15008,8 +14758,8 @@ dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo) bool needComma; int i; - /* Skip if not to be dumped */ - if (!opfinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; query = createPQExpBuffer(); @@ -15253,8 +15003,8 @@ dumpCollation(Archive *fout, const CollInfo *collinfo) const char *collcollate; const char *collctype; - /* Skip if not to be dumped */ - if (!collinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; query = createPQExpBuffer(); @@ -15405,8 +15155,8 @@ dumpConversion(Archive *fout, const ConvInfo *convinfo) const char *conproc; bool condefault; - /* Skip if not to be dumped */ - if (!convinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; query = createPQExpBuffer(); @@ -15554,8 +15304,8 @@ dumpAgg(Archive *fout, const AggInfo *agginfo) const char *proparallel; char defaultfinalmodify; - /* Skip if not to be dumped */ - if (!agginfo->aggfn.dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; query = createPQExpBuffer(); @@ -15563,97 +15313,97 @@ dumpAgg(Archive *fout, const AggInfo *agginfo) delq = createPQExpBuffer(); details = createPQExpBuffer(); + if (!fout->is_prepared[PREPQUERY_DUMPAGG]) + { + /* Set up query for aggregate-specific details */ + appendPQExpBufferStr(query, + "PREPARE dumpAgg(pg_catalog.oid) AS\n"); - /* - * GPDB_14_MERGE_FIXME: - * Some fields: - * aggcombinefn, aggserialfn, - * aggdeserialfn, aggmtransfn, - * aggminvtransfn, aggmfinalfn, - * aggmtranstype::pg_catalog.regtype, aggfinalextra, - * aggmfinalextra, - * diffs from upsreaam in certain versions between [80100, 90600]. - * But others versions are same. - * I think we should keep the same with upsream here, special - * handles are not need in such versions. - * Need to recheck. - */ + appendPQExpBufferStr(query, + "SELECT " + "aggtransfn,\n" + "aggfinalfn,\n" + "aggtranstype::pg_catalog.regtype,\n" + "agginitval,\n"); - /* Get aggregate-specific details */ - appendPQExpBufferStr(query, - "SELECT\n" - "aggtransfn,\n" - "aggfinalfn,\n" - "aggtranstype::pg_catalog.regtype,\n" - "agginitval,\n"); + if (fout->remoteVersion >= 80100) + appendPQExpBufferStr(query, + "aggsortop,\n"); + else + appendPQExpBufferStr(query, + "0 AS aggsortop,\n"); - if (fout->remoteVersion >= 80100) - appendPQExpBufferStr(query, - "aggsortop,\n"); + if (fout->remoteVersion >= 80400) + appendPQExpBufferStr(query, + "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n" + "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"); - if (fout->remoteVersion >= 80400) - appendPQExpBufferStr(query, - "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n" - "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"); + if (fout->remoteVersion >= 90400) + appendPQExpBufferStr(query, + "aggkind,\n" + "aggmtransfn,\n" + "aggminvtransfn,\n" + "aggmfinalfn,\n" + "aggmtranstype::pg_catalog.regtype,\n" + "aggfinalextra,\n" + "aggmfinalextra,\n" + "aggtransspace,\n" + "aggmtransspace,\n" + "aggminitval,\n"); + else + appendPQExpBufferStr(query, + "'n' AS aggkind,\n" + "'-' AS aggmtransfn,\n" + "'-' AS aggminvtransfn,\n" + "'-' AS aggmfinalfn,\n" + "0 AS aggmtranstype,\n" + "false AS aggfinalextra,\n" + "false AS aggmfinalextra,\n" + "0 AS aggtransspace,\n" + "0 AS aggmtransspace,\n" + "NULL AS aggminitval,\n"); - if (fout->remoteVersion >= 90400) - appendPQExpBufferStr(query, - "aggkind,\n" - "aggmtransfn,\n" - "aggminvtransfn,\n" - "aggmfinalfn,\n" - "aggmtranstype::pg_catalog.regtype,\n" - "aggfinalextra,\n" - "aggmfinalextra,\n" - "aggtransspace,\n" - "aggmtransspace,\n" - "aggminitval,\n"); - else - appendPQExpBufferStr(query, - "'n' AS aggkind,\n" - "'-' AS aggmtransfn,\n" - "'-' AS aggminvtransfn,\n" - "'-' AS aggmfinalfn,\n" - "0 AS aggmtranstype,\n" - "false AS aggfinalextra,\n" - "false AS aggmfinalextra,\n" - "0 AS aggtransspace,\n" - "0 AS aggmtransspace,\n" - "NULL AS aggminitval,\n"); + if (fout->remoteVersion >= 90600) + appendPQExpBufferStr(query, + "aggcombinefn,\n" + "aggserialfn,\n" + "aggdeserialfn,\n" + "proparallel,\n"); + else + appendPQExpBufferStr(query, + "'-' AS aggcombinefn,\n" + "'-' AS aggserialfn,\n" + "'-' AS aggdeserialfn,\n" + "'u' AS proparallel,\n"); - if (fout->remoteVersion >= 90600) - appendPQExpBufferStr(query, - "aggcombinefn,\n" - "aggserialfn,\n" - "aggdeserialfn,\n" - "proparallel,\n"); - else - appendPQExpBufferStr(query, - "'-' AS aggcombinefn,\n" - "'-' AS aggserialfn,\n" - "'-' AS aggdeserialfn,\n" - "'u' AS proparallel,\n"); + if (fout->remoteVersion >= 110000) + appendPQExpBufferStr(query, + "aggfinalmodify,\n" + "aggmfinalmodify\n"); + else + appendPQExpBufferStr(query, + "'0' AS aggfinalmodify,\n" + "'0' AS aggmfinalmodify\n"); + + if (fout->remoteVersion >= 140000 && fout->version.type == Cloudberry && fout->version.version >= 2) + appendPQExpBufferStr(query, + "aggrepsafeexec\n"); + else + appendPQExpBufferStr(query, + "false AS aggrepsafeexec\n"); - if (fout->remoteVersion >= 110000) - appendPQExpBufferStr(query, - "aggfinalmodify,\n" - "aggmfinalmodify,\n"); - else appendPQExpBufferStr(query, - "'0' AS aggfinalmodify,\n" - "'0' AS aggmfinalmodify,\n"); + "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " + "WHERE a.aggfnoid = p.oid " + "AND p.oid = $1"); - if (fout->remoteVersion >= 140000 && fout->version.type == Cloudberry && fout->version.version >= 2) - appendPQExpBufferStr(query, - "aggrepsafeexec\n"); - else - appendPQExpBufferStr(query, - "false AS aggrepsafeexec\n"); + ExecuteSqlStatement(fout, query->data); - appendPQExpBuffer(query, - "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " - "WHERE a.aggfnoid = p.oid " - "AND p.oid = '%u'::pg_catalog.oid", + fout->is_prepared[PREPQUERY_DUMPAGG] = true; + } + + printfPQExpBuffer(query, + "EXECUTE dumpAgg('%u')", agginfo->aggfn.dobj.catId.oid); res = ExecuteSqlQueryForSingleRow(fout, query->data); @@ -15883,9 +15633,7 @@ dumpAgg(Archive *fout, const AggInfo *agginfo) dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId, "FUNCTION", aggsig, NULL, agginfo->aggfn.dobj.namespace->dobj.name, - agginfo->aggfn.rolname, agginfo->aggfn.proacl, - agginfo->aggfn.rproacl, - agginfo->aggfn.initproacl, agginfo->aggfn.initrproacl); + agginfo->aggfn.rolname, &agginfo->aggfn.dacl); free(aggsig); if (aggfullsig) @@ -15963,7 +15711,7 @@ dumpExtProtocol(Archive *fout, const ExtProtInfo *ptcinfo) } ProtoFunc; ProtoFunc protoFuncs[FCOUNT]; - + DumpOptions *dopt = fout->dopt; PQExpBuffer q; PQExpBuffer delq; PQExpBuffer nsq; @@ -15972,8 +15720,8 @@ dumpExtProtocol(Archive *fout, const ExtProtInfo *ptcinfo) int i; bool has_internal = false; - /* Skip if not to be dumped */ - if (!ptcinfo->dobj.dump || fout->dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; /* init and fill the protoFuncs array */ @@ -16033,10 +15781,7 @@ dumpExtProtocol(Archive *fout, const ExtProtInfo *ptcinfo) * qualified with namespace, we must ensure that we have the search_path * set with the namespaces of the referenced functions. We only need the * dump file to have the search_path so inject a SET search_path = .. ; - * into the output stream instead of calling selectSourceSchema(). - * - * GPDB_96_MERGE_FIXME: update the above comment because selectSourceSchema - * has been removed in upstream 9f6e5296a Security: CVE-2018-1058 + * into the output stream. */ prev_ns = NULL; for (i = 0; i < FCOUNT; i++) @@ -16096,7 +15841,7 @@ dumpExtProtocol(Archive *fout, const ExtProtInfo *ptcinfo) ArchiveEntry(fout, ptcinfo->dobj.catId, ptcinfo->dobj.dumpId, ARCHIVE_OPTS(.tag = ptcinfo->dobj.name, - .owner = ptcinfo->ptcowner, + .owner = ptcinfo->rolname, .description = "PROTOCOL", .section = SECTION_PRE_DATA, .createStmt = q->data, @@ -16109,9 +15854,7 @@ dumpExtProtocol(Archive *fout, const ExtProtInfo *ptcinfo) dumpACL(fout, ptcinfo->dobj.dumpId, InvalidDumpId, "PROTOCOL", namecopy, NULL, - NULL, ptcinfo->ptcowner, - ptcinfo->ptcacl, ptcinfo->rproacl, - ptcinfo->initproacl, ptcinfo->initrproacl); + NULL, ptcinfo->rolname, &ptcinfo->dacl); free(namecopy); destroyPQExpBuffer(q); @@ -16136,8 +15879,8 @@ dumpTSParser(Archive *fout, const TSParserInfo *prsinfo) PQExpBuffer delq; char *qprsname; - /* Skip if not to be dumped */ - if (!prsinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; q = createPQExpBuffer(); @@ -16280,8 +16023,8 @@ dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo) PQExpBuffer delq; char *qtmplname; - /* Skip if not to be dumped */ - if (!tmplinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; q = createPQExpBuffer(); @@ -16346,8 +16089,8 @@ dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo) int i_tokenname; int i_dictname; - /* Skip if not to be dumped */ - if (!cfginfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; q = createPQExpBuffer(); @@ -16458,8 +16201,8 @@ dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo) PQExpBuffer delq; char *qfdwname; - /* Skip if not to be dumped */ - if (!fdwinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; q = createPQExpBuffer(); @@ -16508,9 +16251,7 @@ dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo) if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL) dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId, "FOREIGN DATA WRAPPER", qfdwname, NULL, - NULL, fdwinfo->rolname, - fdwinfo->fdwacl, fdwinfo->rfdwacl, - fdwinfo->initfdwacl, fdwinfo->initrfdwacl); + NULL, fdwinfo->rolname, &fdwinfo->dacl); free(qfdwname); @@ -16533,8 +16274,8 @@ dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo) char *qsrvname; char *fdwname; - /* Skip if not to be dumped */ - if (!srvinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; q = createPQExpBuffer(); @@ -16597,9 +16338,7 @@ dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo) if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL) dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId, "FOREIGN SERVER", qsrvname, NULL, - NULL, srvinfo->rolname, - srvinfo->srvacl, srvinfo->rsrvacl, - srvinfo->initsrvacl, srvinfo->initrsrvacl); + NULL, srvinfo->rolname, &srvinfo->dacl); /* Dump user mappings */ if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP) @@ -16726,8 +16465,8 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo) PQExpBuffer tag; const char *type; - /* Skip if not to be dumped */ - if (!daclinfo->dobj.dump || dopt->dataOnly || dopt->aclsSkip) + /* Do nothing in data-only dump, or if we're skipping ACLs */ + if (dopt->dataOnly || dopt->aclsSkip) return; q = createPQExpBuffer(); @@ -16763,15 +16502,13 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo) if (!buildDefaultACLCommands(type, daclinfo->dobj.namespace != NULL ? daclinfo->dobj.namespace->dobj.name : NULL, - daclinfo->defaclacl, - daclinfo->rdefaclacl, - daclinfo->initdefaclacl, - daclinfo->initrdefaclacl, + daclinfo->dacl.acl, + daclinfo->dacl.acldefault, daclinfo->defaclrole, fout->remoteVersion, q)) fatal("could not parse default ACL list (%s)", - daclinfo->defaclacl); + daclinfo->dacl.acl); if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL) ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId, @@ -16801,20 +16538,7 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo) * (Currently we assume that subname is only provided for table columns.) * 'nspname' is the namespace the object is in (NULL if none). * 'owner' is the owner, NULL if there is no owner (for languages). - * 'acls' contains the ACL string of the object from the appropriate system - * catalog field; it will be passed to buildACLCommands for building the - * appropriate GRANT commands. - * 'racls' contains the ACL string of any initial-but-now-revoked ACLs of the - * object; it will be passed to buildACLCommands for building the - * appropriate REVOKE commands. - * 'initacls' In binary-upgrade mode, ACL string of the object's initial - * privileges, to be recorded into pg_init_privs - * 'initracls' In binary-upgrade mode, ACL string of the object's - * revoked-from-default privileges, to be recorded into pg_init_privs - * - * NB: initacls/initracls are needed because extensions can set privileges on - * an object during the extension's script file and we record those into - * pg_init_privs as that object's initial privileges. + * 'dacl' is the DumpableAcl struct fpr the object. * * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if * no ACL entry was created. @@ -16824,11 +16548,15 @@ static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId, const char *type, const char *name, const char *subname, const char *nspname, const char *owner, - const char *acls, const char *racls, - const char *initacls, const char *initracls) + const DumpableAcl *dacl) { DumpId aclDumpId = InvalidDumpId; DumpOptions *dopt = fout->dopt; + const char *acls = dacl->acl; + const char *acldefault = dacl->acldefault; + char privtype = dacl->privtype; + const char *initprivs = dacl->initprivs; + const char *baseacls; PQExpBuffer sql; /* Do nothing if ACL dump is not enabled */ @@ -16842,29 +16570,52 @@ dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId, sql = createPQExpBuffer(); /* - * 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. + * In binary upgrade mode, we don't run an extension's script but instead + * dump out the objects independently and then recreate them. To preserve + * any initial privileges which were set on extension objects, we need to + * compute the set of GRANT and REVOKE commands necessary to get from the + * default privileges of an object to its initial privileges as recorded + * in pg_init_privs. + * + * At restore time, we apply these commands after having called + * binary_upgrade_set_record_init_privs(true). That tells the backend to + * copy the results into pg_init_privs. This is how we preserve the + * contents of that catalog across binary upgrades. */ - if (strlen(initacls) != 0 || strlen(initracls) != 0) + if (dopt->binary_upgrade && privtype == 'e' && + initprivs && *initprivs != '\0') { appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n"); if (!buildACLCommands(name, subname, nspname, type, - initacls, initracls, owner, + initprivs, acldefault, owner, "", fout->remoteVersion, sql)) - fatal("could not parse initial GRANT ACL list (%s) or initial REVOKE ACL list (%s) for object \"%s\" (%s)", - initacls, initracls, name, type); + fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)", + initprivs, acldefault, name, type); appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n"); } + /* + * Now figure the GRANT and REVOKE commands needed to get to the object's + * actual current ACL, starting from the initprivs if given, else from the + * object-type-specific default. Also, while buildACLCommands will assume + * that a NULL/empty acls string means it needn't do anything, what that + * actually represents is the object-type-specific default; so we need to + * substitute the acldefault string to get the right results in that case. + */ + if (initprivs && *initprivs != '\0') + { + baseacls = initprivs; + if (acls == NULL || *acls == '\0') + acls = acldefault; + } + else + baseacls = acldefault; + if (!buildACLCommands(name, subname, nspname, type, - acls, racls, owner, + acls, baseacls, owner, "", fout->remoteVersion, sql)) - fatal("could not parse GRANT ACL list (%s) or REVOKE ACL list (%s) for object \"%s\" (%s)", - acls, racls, name, type); + fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)", + acls, baseacls, name, type); if (sql->len > 0) { @@ -17082,20 +16833,12 @@ dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypenam static int findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items) { - /* static storage for table of security labels */ - static SecLabelItem *labels = NULL; - static int nlabels = -1; - SecLabelItem *middle = NULL; SecLabelItem *low; SecLabelItem *high; int nmatch; - /* Get security labels if we didn't already */ - if (nlabels < 0) - nlabels = collectSecLabels(fout, &labels); - - if (nlabels <= 0) /* no labels, so no match is possible */ + if (nseclabels <= 0) /* no labels, so no match is possible */ { *items = NULL; return 0; @@ -17104,8 +16847,8 @@ findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items) /* * Do binary search to find some item matching the object. */ - low = &labels[0]; - high = &labels[nlabels - 1]; + low = &seclabels[0]; + high = &seclabels[nseclabels - 1]; while (low <= high) { middle = low + (high - low) / 2; @@ -17161,13 +16904,13 @@ findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items) /* * collectSecLabels * - * Construct a table of all security labels available for database objects. - * It's much faster to pull them all at once. + * Construct a table of all security labels available for database objects; + * also set the has-seclabel component flag for each relevant object. * * The table is sorted by classoid/objid/objsubid for speed in lookup. */ -static int -collectSecLabels(Archive *fout, SecLabelItem **items) +static void +collectSecLabels(Archive *fout) { PGresult *res; PQExpBuffer query; @@ -17178,7 +16921,7 @@ collectSecLabels(Archive *fout, SecLabelItem **items) int i_objsubid; int ntups; int i; - SecLabelItem *labels; + DumpableObject *dobj; query = createPQExpBuffer(); @@ -17198,22 +16941,54 @@ collectSecLabels(Archive *fout, SecLabelItem **items) ntups = PQntuples(res); - labels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem)); + seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem)); + nseclabels = 0; + dobj = NULL; for (i = 0; i < ntups; i++) { - labels[i].label = PQgetvalue(res, i, i_label); - labels[i].provider = PQgetvalue(res, i, i_provider); - labels[i].classoid = atooid(PQgetvalue(res, i, i_classoid)); - labels[i].objoid = atooid(PQgetvalue(res, i, i_objoid)); - labels[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid)); + CatalogId objId; + int subid; + + objId.tableoid = atooid(PQgetvalue(res, i, i_classoid)); + objId.oid = atooid(PQgetvalue(res, i, i_objoid)); + subid = atoi(PQgetvalue(res, i, i_objsubid)); + + /* We needn't remember labels that don't match any dumpable object */ + if (dobj == NULL || + dobj->catId.tableoid != objId.tableoid || + dobj->catId.oid != objId.oid) + dobj = findObjectByCatalogId(objId); + if (dobj == NULL) + continue; + + /* + * Labels on columns of composite types are linked to the type's + * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag + * in the type's own DumpableObject. + */ + if (subid != 0 && dobj->objType == DO_TABLE && + ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE) + { + TypeInfo *cTypeInfo; + + cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype); + if (cTypeInfo) + cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL; + } + else + dobj->components |= DUMP_COMPONENT_SECLABEL; + + seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label)); + seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider)); + seclabels[nseclabels].classoid = objId.tableoid; + seclabels[nseclabels].objoid = objId.oid; + seclabels[nseclabels].objsubid = subid; + nseclabels++; } - /* Do NOT free the PGresult since we are keeping pointers into it */ + PQclear(res); destroyPQExpBuffer(query); - - *items = labels; - return ntups; } /* @@ -17227,17 +17002,17 @@ dumpTable(Archive *fout, const TableInfo *tbinfo) DumpId tableAclDumpId = InvalidDumpId; char *namecopy; - /* - * noop if we are not dumping anything about this table, or if we are - * doing a data-only dump - */ - if (!tbinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; - if (tbinfo->relkind == RELKIND_SEQUENCE) - dumpSequence(fout, tbinfo); - else - dumpTableSchema(fout, tbinfo); + if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) + { + if (tbinfo->relkind == RELKIND_SEQUENCE) + dumpSequence(fout, tbinfo); + else + dumpTableSchema(fout, tbinfo); + } /* Handle the ACL here */ namecopy = pg_strdup(fmtId(tbinfo->dobj.name)); @@ -17250,90 +17025,90 @@ dumpTable(Archive *fout, const TableInfo *tbinfo) dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId, objtype, namecopy, NULL, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, - tbinfo->relacl, tbinfo->rrelacl, - tbinfo->initrelacl, tbinfo->initrrelacl); + &tbinfo->dacl); } /* * 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. + * miss ACLs on system columns. Doing it this way also allows us to dump + * ACLs for catalogs that we didn't mark "interesting" back in getTables. */ - if (fout->remoteVersion >= 80400 && tbinfo->dobj.dump & DUMP_COMPONENT_ACL) + if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs) { PQExpBuffer query = createPQExpBuffer(); PGresult *res; int i; - if (fout->remoteVersion >= 90600) + if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS]) { - PQExpBuffer acl_subquery = createPQExpBuffer(); - PQExpBuffer racl_subquery = createPQExpBuffer(); - PQExpBuffer initacl_subquery = createPQExpBuffer(); - PQExpBuffer initracl_subquery = createPQExpBuffer(); + /* Set up query for column ACLs */ + appendPQExpBufferStr(query, + "PREPARE getColumnACLs(pg_catalog.oid) AS\n"); - buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, - initracl_subquery, "at.attacl", "c.relowner", "'c'", - dopt->binary_upgrade); + if (fout->remoteVersion >= 90600) + { + /* + * In principle we should call acldefault('c', relowner) to + * get the default ACL for a column. However, we don't + * currently store the numeric OID of the relowner in + * TableInfo. We could convert the owner name using regrole, + * but that creates a risk of failure due to concurrent role + * renames. Given that the default ACL for columns is empty + * and is likely to stay that way, it's not worth extra cycles + * and risk to avoid hard-wiring that knowledge here. + */ + appendPQExpBufferStr(query, + "SELECT at.attname, " + "at.attacl, " + "'{}' AS acldefault, " + "pip.privtype, pip.initprivs " + "FROM pg_catalog.pg_attribute at " + "LEFT JOIN pg_catalog.pg_init_privs pip ON " + "(at.attrelid = pip.objoid " + "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass " + "AND at.attnum = pip.objsubid) " + "WHERE at.attrelid = $1 AND " + "NOT at.attisdropped " + "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) " + "ORDER BY at.attnum"); + } + else + { + appendPQExpBufferStr(query, + "SELECT attname, attacl, '{}' AS acldefault, " + "NULL AS privtype, NULL AS initprivs " + "FROM pg_catalog.pg_attribute " + "WHERE attrelid = $1 AND NOT attisdropped " + "AND attacl IS NOT NULL " + "ORDER BY attnum"); + } - 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_catalog.pg_init_privs pip ON " - "(at.attrelid = pip.objoid " - "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass " - "AND at.attnum = pip.objsubid) " - "WHERE at.attrelid = '%u'::pg_catalog.oid AND " - "NOT at.attisdropped " - "AND (" - "%s IS NOT NULL OR " - "%s IS NOT NULL OR " - "%s IS NOT NULL OR " - "%s IS NOT NULL)" - "ORDER BY at.attnum", - acl_subquery->data, - racl_subquery->data, - initacl_subquery->data, - initracl_subquery->data, - tbinfo->dobj.catId.oid, - 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 attname, attacl, NULL as rattacl, " - "NULL AS initattacl, NULL AS initrattacl " - "FROM pg_catalog.pg_attribute " - "WHERE attrelid = '%u'::pg_catalog.oid AND NOT attisdropped " - "AND attacl IS NOT NULL " - "ORDER BY attnum", - tbinfo->dobj.catId.oid); + ExecuteSqlStatement(fout, query->data); + + fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true; } + printfPQExpBuffer(query, + "EXECUTE getColumnACLs('%u')", + 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 *acldefault = PQgetvalue(res, i, 2); + char privtype = *(PQgetvalue(res, i, 3)); + char *initprivs = PQgetvalue(res, i, 4); + DumpableAcl coldacl; char *attnamecopy; + coldacl.acl = attacl; + coldacl.acldefault = acldefault; + coldacl.privtype = privtype; + coldacl.initprivs = initprivs; attnamecopy = pg_strdup(fmtId(attname)); /* @@ -17344,7 +17119,7 @@ dumpTable(Archive *fout, const TableInfo *tbinfo) dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId, "TABLE", namecopy, attnamecopy, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, - attacl, rattacl, initattacl, initrattacl); + &coldacl); free(attnamecopy); } PQclear(res); @@ -17375,7 +17150,6 @@ dumpExternal(Archive *fout, const TableInfo *tbinfo, PQExpBuffer q, PQExpBuffer bool isweb = false; bool iswritable = false; char *options; - bool gpdb6OrLater = isGPDB6000OrLater(fout); char *logerrors = NULL; char *on_clause; char *qualrelname; @@ -17391,7 +17165,7 @@ dumpExternal(Archive *fout, const TableInfo *tbinfo, PQExpBuffer q, PQExpBuffer qualrelname); /* Now get required information from pg_exttable */ - if (gpdb6OrLater) + if (fout->remoteVersion >= GPDB6_MAJOR_PGVERSION) { appendPQExpBuffer(query, "SELECT x.urilocation, x.execlocation, x.fmttype, x.fmtopts, x.command, " @@ -17777,7 +17551,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) char *ftoptions = NULL; char *srvname = NULL; char *foreign = ""; - + char *partkeydef = NULL; /* We had better have loaded per-column details about this table */ Assert(tbinfo->interesting); @@ -17790,8 +17564,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) qrelname); if (dopt->binary_upgrade) - binary_upgrade_set_type_oids_by_rel_oid(fout, q, - tbinfo->dobj.catId.oid); + binary_upgrade_set_type_oids_by_rel_oid(fout, q, tbinfo->dobj.catId.oid); /* Is it a table or a view? */ if (tbinfo->relkind == RELKIND_VIEW) @@ -17833,8 +17606,29 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) } else { + /* + * Set reltypename, and collect any relkind-specific data that we + * didn't fetch during getTables(). + */ switch (tbinfo->relkind) { + case RELKIND_PARTITIONED_TABLE: + { + PQExpBuffer query = createPQExpBuffer(); + PGresult *res; + + reltypename = "TABLE"; + + /* retrieve partition key definition */ + appendPQExpBuffer(query, + "SELECT pg_get_partkeydef('%u')", + tbinfo->dobj.catId.oid); + res = ExecuteSqlQueryForSingleRow(fout, query->data); + partkeydef = pg_strdup(PQgetvalue(res, 0, 0)); + PQclear(res); + destroyPQExpBuffer(query); + break; + } case RELKIND_FOREIGN_TABLE: { PQExpBuffer query = createPQExpBuffer(); @@ -17877,10 +17671,10 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) break; default: reltypename = "TABLE"; - /* Is it an external table (server GPDB 6.x and below.) */ if (tbinfo->relstorage == RELSTORAGE_EXTERNAL) reltypename = "EXTERNAL TABLE"; + break; } } @@ -17936,6 +17730,9 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) { Oid part_oid = atooid(PQgetvalue(partres, i, 0)); + if (tbinfo->relstorage == 'x') + hasExternalPartitions = true; + binary_upgrade_set_pg_class_oids(fout, q, part_oid, false); binary_upgrade_set_type_oids_by_rel_oid(fout, q, part_oid); } @@ -17967,8 +17764,10 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) * Attach to type, if reloftype; except in case of a binary upgrade, * we dump the table normally and attach it to the type afterward. */ - if (tbinfo->reloftype && !dopt->binary_upgrade) - appendPQExpBuffer(q, " OF %s", tbinfo->reloftype); + if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade) + appendPQExpBuffer(q, " OF %s", + getFormattedTypeName(fout, tbinfo->reloftype, + zeroIsError)); if (tbinfo->relkind != RELKIND_MATVIEW) { @@ -18006,7 +17805,8 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) * Skip column if fully defined by reloftype, except in * binary upgrade */ - if (tbinfo->reloftype && !print_default && !print_notnull && + if (OidIsValid(tbinfo->reloftype) && + !print_default && !print_notnull && !dopt->binary_upgrade) continue; @@ -18047,7 +17847,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) * table ('OF type_name'), but in binary-upgrade mode, * print it in that case too. */ - if (dopt->binary_upgrade || !tbinfo->reloftype) + if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype)) { appendPQExpBuffer(q, " %s", tbinfo->atttypnames[j]); @@ -18137,7 +17937,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) if (actual_atts) appendPQExpBufferStr(q, "\n)"); - else if (!(tbinfo->reloftype && !dopt->binary_upgrade)) + else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)) { /* * No attributes? we must have a parenthesized attribute list, @@ -18166,7 +17966,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) } if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) - appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef); if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); @@ -18232,125 +18032,29 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) /* * Dump distributed by clause. */ - if (dumpGpPolicy && tbinfo->relkind != RELKIND_FOREIGN_TABLE) + if (dopt->dumpGpPolicy && tbinfo->relkind != RELKIND_FOREIGN_TABLE) addDistributedBy(fout, q, tbinfo, actual_atts); /* - * If GP partitioning is supported add the partitioning constraints to - * the table definition. + * Add the GPDB partitioning constraints to the table definition. + * These do not exist on GPDB7, so first check if we are dumping + * from <= GPDB6. */ - if (gp_partitioning_available) + if (fout->remoteVersion < GPDB7_MAJOR_PGVERSION && + (tbinfo->partclause && *tbinfo->partclause != '\0')) { - bool isTemplatesSupported = fout->remoteVersion >= 80214; - bool isPartitioned = false; - PQExpBuffer query = createPQExpBuffer(); - PGresult *res; - - /* does support GP partitioning. */ - resetPQExpBuffer(query); - /* MPP-6297: dump by tablename */ - if (isTemplatesSupported) - /* use 4.x version of function */ - appendPQExpBuffer(query, "SELECT " - "pg_get_partition_def('%u'::pg_catalog.oid, true, true) ", - tbinfo->dobj.catId.oid); - else /* use 3.x version of function */ - appendPQExpBuffer(query, "SELECT " - "pg_get_partition_def('%u'::pg_catalog.oid, true) ", - tbinfo->dobj.catId.oid); - - res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); - - if (PQntuples(res) != 1) - { - if (PQntuples(res) < 1) - pg_log_warning("query to obtain definition of table \"%s\" returned no data", - tbinfo->dobj.name); - else - pg_log_warning("query to obtain definition of table \"%s\" returned more than one definition", - tbinfo->dobj.name); - exit_nicely(1); - } - isPartitioned = !PQgetisnull(res, 0, 0); - if (isPartitioned) - appendPQExpBuffer(q, " %s", PQgetvalue(res, 0, 0)); - - PQclear(res); - - /* - * MPP-6095: dump ALTER TABLE statements for subpartition - * templates - */ - if (isTemplatesSupported) - { - resetPQExpBuffer(query); - - appendPQExpBuffer( - query, "SELECT " - "pg_get_partition_template_def('%u'::pg_catalog.oid, true, true) ", - tbinfo->dobj.catId.oid); - - res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); - - if (PQntuples(res) != 1) - { - if (PQntuples(res) < 1) - pg_log_warning("query to obtain definition of table \"%s\" returned no data", - tbinfo->dobj.name); - else - pg_log_warning("query to obtain definition of table \"%s\" returned more than one definition", - tbinfo->dobj.name); - exit_nicely(1); - } - - /* - * MPP-9537: terminate (with semicolon) the previous - * statement, and dump the template definitions - */ - if (!PQgetisnull(res, 0, 0) && - PQgetlength(res, 0, 0)) - appendPQExpBuffer(q, ";\n %s", PQgetvalue(res, 0, 0)); - - PQclear(res); - } - - if (isPartitioned) - { - /* - * Find out if there are any external partitions. - * - * In GPDB 6.X and below, external tables have - * relkind=RELKIND_RELATION and relstorage=RELSTORAGE_EXTERNAL. - * In later versions, they are just foreign tables. This query - * works on all server versions. - */ - resetPQExpBuffer(query); - appendPQExpBuffer(query, "SELECT EXISTS (SELECT 1 " - " FROM pg_class part " - " JOIN pg_partition_rule pr ON (part.oid = pr.parchildrelid) " - " JOIN pg_partition p ON (pr.paroid = p.oid) " - "WHERE p.parrelid = '%u'::pg_catalog.oid " - " AND (part.relstorage = '%c' OR part.relkind = '%c')) " - "AS has_external_partitions;", - tbinfo->dobj.catId.oid, - RELSTORAGE_EXTERNAL, - RELKIND_FOREIGN_TABLE); - - res = ExecuteSqlQueryForSingleRow(fout, query->data); - hasExternalPartitions = (PQgetvalue(res, 0, 0)[0] == 't'); - PQclear(res); - } - - destroyPQExpBuffer(query); - } - - /* END MPP ADDITION */ + /* partition by clause */ + appendPQExpBuffer(q, " %s", tbinfo->partclause); + /* subpartition template */ + if (tbinfo->parttemplate) + appendPQExpBuffer(q, ";\n %s", tbinfo->parttemplate); + } /* END MPP ADDITION */ /* Dump generic options if any */ if (ftoptions && ftoptions[0]) appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions); - if (dumpGpPolicy && tbinfo->relkind == RELKIND_FOREIGN_TABLE && strcmp(srvname, GP_EXTTABLE_SERVER_NAME) == 0) + if (dopt->dumpGpPolicy && tbinfo->relkind == RELKIND_FOREIGN_TABLE && strcmp(srvname, GP_EXTTABLE_SERVER_NAME) == 0) addDistributedBy(fout, q, tbinfo, actual_atts); appendPQExpBufferStr(q, ";\n"); @@ -18516,7 +18220,8 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) tbinfo->attalign[j]); appendStringLiteralAH(q, tbinfo->attnames[j], fout); - if (gp_partitioning_available) + /* GPDB partitioning */ + if (fout->remoteVersion < GPDB7_MAJOR_PGVERSION) { /* * Do for all descendants of a partition table. @@ -18618,12 +18323,13 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) } } - if (tbinfo->reloftype) + if (OidIsValid(tbinfo->reloftype)) { appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n"); appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n", qualrelname, - tbinfo->reloftype); + getFormattedTypeName(fout, tbinfo->reloftype, + zeroIsError)); } appendPQExpBuffer(q, "RESET allow_system_table_mods;\n"); } @@ -18812,6 +18518,8 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) tbinfo->attfdwoptions[j]); } /* end loop over columns */ + if (partkeydef) + free(partkeydef); if (ftoptions) free(ftoptions); if (srvname) @@ -18919,7 +18627,10 @@ dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo) { DumpOptions *dopt = fout->dopt; PQExpBuffer q; + PGresult *res; + char *partbound; + /* Do nothing in data-only dump */ if (dopt->dataOnly) return; @@ -18928,14 +18639,23 @@ dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo) q = createPQExpBuffer(); - /* Perform ALTER TABLE on the parent */ + /* Fetch the partition's partbound */ appendPQExpBuffer(q, + "SELECT pg_get_expr(c.relpartbound, c.oid) " + "FROM pg_class c " + "WHERE c.oid = '%u'", + attachinfo->partitionTbl->dobj.catId.oid); + res = ExecuteSqlQueryForSingleRow(fout, q->data); + partbound = PQgetvalue(res, 0, 0); + + /* Perform ALTER TABLE on the parent */ + printfPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtQualifiedDumpable(attachinfo->parentTbl)); appendPQExpBuffer(q, "ATTACH PARTITION %s %s;\n", fmtQualifiedDumpable(attachinfo->partitionTbl), - attachinfo->partitionTbl->partbound); + partbound); /* * There is no point in creating a drop query as the drop is done by table @@ -18952,6 +18672,7 @@ dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo) .section = SECTION_PRE_DATA, .createStmt = q->data)); + PQclear(res); destroyPQExpBuffer(q); } @@ -18970,8 +18691,8 @@ dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo) char *tag; char *foreign; - /* Skip if table definition not to be dumped */ - if (!tbinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; /* Skip if not "separate"; it was dumped in the table's definition */ @@ -19067,6 +18788,7 @@ dumpIndex(Archive *fout, const IndxInfo *indxinfo) char *qindxname; char *qqindxname; + /* Do nothing in data-only dump */ if (dopt->dataOnly) return; @@ -19200,6 +18922,7 @@ dumpIndex(Archive *fout, const IndxInfo *indxinfo) static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo) { + /* Do nothing in data-only dump */ if (fout->dopt->dataOnly) return; @@ -19246,8 +18969,8 @@ dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo) PGresult *res; char *stxdef; - /* Skip if not to be dumped */ - if (!statsextinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; q = createPQExpBuffer(); @@ -19323,8 +19046,8 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo) char *tag = NULL; char *foreign; - /* Skip if not to be dumped */ - if (!coninfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; q = createPQExpBuffer(); @@ -19612,29 +19335,6 @@ dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo) free(qtabname); } -/* - * findLastBuiltinOid_V71 - - * - * find the last built in oid - * - * For 7.1 through 8.0, we do this by retrieving datlastsysoid from the - * pg_database entry for the current database. (Note: current_database() - * requires 7.3; pg_dump requires 8.0 now.) - */ -static Oid -findLastBuiltinOid_V71(Archive *fout) -{ - PGresult *res; - Oid last_oid; - - res = ExecuteSqlQueryForSingleRow(fout, - "SELECT datlastsysoid FROM pg_database WHERE datname = current_database()"); - last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "datlastsysoid"))); - PQclear(res); - - return last_oid; -} - /* * dumpSequence * write the declaration (not data) of one user-defined sequence @@ -19975,10 +19675,7 @@ dumpTrigger(Archive *fout, const TriggerInfo *tginfo) int findx; char *tag; - /* - * we needn't check dobj.dump because TriggerInfo wouldn't have been - * created in the first place for non-dumpable triggers - */ + /* Do nothing in data-only dump */ if (dopt->dataOnly) return; @@ -20214,8 +19911,8 @@ dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo) PQExpBuffer delqry; char *qevtname; - /* Skip if not to be dumped */ - if (!evtinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; query = createPQExpBuffer(); @@ -20305,8 +20002,8 @@ dumpRule(Archive *fout, const RuleInfo *rinfo) PGresult *res; char *tag; - /* Skip if not to be dumped */ - if (!rinfo->dobj.dump || dopt->dataOnly) + /* Do nothing in data-only dump */ + if (dopt->dataOnly) return; /* @@ -21153,50 +20850,22 @@ findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj, /* * isGPbackend - returns true if the connected backend is a GreenPlum DB backend. */ -static bool +static void testGPbackend(Archive *fout) { - PQExpBuffer query; - PGresult *res; - bool isGPbackend; - - query = createPQExpBuffer(); - - appendPQExpBuffer(query, "SELECT current_setting('gp_role');"); - ArchiveHandle *AH = (ArchiveHandle *) fout; - res = PQexec(AH->connection, query->data); - - isGPbackend = (PQresultStatus(res) == PGRES_TUPLES_OK); - - PQclear(res); - destroyPQExpBuffer(query); - - return isGPbackend; -} - -/* - * testPartitioningSupport - tests whether or not the current GP - * database includes support for partitioning. - */ -static bool -testPartitioningSupport(Archive *fout) -{ - PQExpBuffer query; + DumpOptions *dopt = fout->dopt; + PQExpBuffer query = createPQExpBuffer(); PGresult *res; - bool isSupported; - query = createPQExpBuffer(); - - appendPQExpBuffer(query, "SELECT 1 FROM pg_class WHERE relname = 'pg_partition' and relnamespace = 11;"); + appendPQExpBuffer(query, "SELECT 1 FROM current_setting('gp_role');"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); - isSupported = (PQntuples(res) == 1); + dopt->isGPbackend = (PQntuples(res) == 1); PQclear(res); destroyPQExpBuffer(query); - - return isSupported; } + /* * addSchedule * @@ -21233,7 +20902,7 @@ addSchedule(Archive *fout, PQExpBuffer q, const TableInfo *tbinfo) static void addDistributedBy(Archive *fout, PQExpBuffer q, const TableInfo *tbinfo, int actual_atts) { - if (isGPDB6000OrLater(fout)) + if (fout->remoteVersion >= GPDB6_MAJOR_PGVERSION) { PQExpBuffer query = createPQExpBuffer(); PGresult *res; diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 81fa36289b3..d53d2ab623e 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -88,8 +88,13 @@ typedef enum DO_SUBSCRIPTION } DumpableObjectType; -/* component types of an object which can be selected for dumping */ -typedef uint32 DumpComponents; /* a bitmask of dump object components */ +/* + * DumpComponents is a bitmask of the potentially dumpable components of + * a database object: its core definition, plus optional attributes such + * as ACL, comments, etc. The NONE and ALL symbols are convenient + * shorthands. + */ +typedef uint32 DumpComponents; #define DUMP_COMPONENT_NONE (0) #define DUMP_COMPONENT_DEFINITION (1 << 0) #define DUMP_COMPONENT_DATA (1 << 1) @@ -134,8 +139,9 @@ typedef struct _dumpableObject DumpId dumpId; /* assigned by AssignDumpId() */ char *name; /* object name (should never be NULL) */ struct _namespaceInfo *namespace; /* containing namespace, or NULL */ - DumpComponents dump; /* bitmask of components to dump */ + DumpComponents dump; /* bitmask of components requested to dump */ DumpComponents dump_contains; /* as above, but for contained objects */ + DumpComponents components; /* bitmask of components available to dump */ bool ext_member; /* true if object is member of extension */ bool depends_on_ext; /* true if object depends on an extension */ DumpId *dependencies; /* dumpIds of objects this one depends on */ @@ -148,14 +154,34 @@ typedef struct _binaryupgradeinfo DumpableObject dobj; } BinaryUpgradeInfo; +/* + * Object types that have ACLs must store them in a DumpableAcl sub-struct, + * which must immediately follow the DumpableObject base struct. + * + * Note: when dumping from a pre-9.2 server, which lacks the acldefault() + * function, acldefault will be NULL or empty. + */ +typedef struct _dumpableAcl +{ + char *acl; /* the object's actual ACL string */ + char *acldefault; /* default ACL for the object's type & owner */ + /* these fields come from the object's pg_init_privs entry, if any: */ + char privtype; /* entry type, 'i' or 'e'; 0 if no entry */ + char *initprivs; /* the object's initial ACL string, or NULL */ +} DumpableAcl; + +/* Generic struct that can be used to access any object type having an ACL */ +typedef struct _dumpableObjectWithAcl +{ + DumpableObject dobj; + DumpableAcl dacl; +} DumpableObjectWithAcl; typedef struct _namespaceInfo { DumpableObject dobj; - char *rolname; /* name of owner, or empty string */ - char *nspacl; - char *rnspacl; - char *initnspacl; - char *initrnspacl; + DumpableAcl dacl; + Oid nspowner; /* OID of owner */ + const char *rolname; /* name of owner */ } NamespaceInfo; typedef struct _extensionInfo @@ -171,6 +197,7 @@ typedef struct _extensionInfo typedef struct _typeInfo { DumpableObject dobj; + DumpableAcl dacl; /* * Note: dobj.name is the raw pg_type.typname entry. ftypname is the @@ -178,7 +205,7 @@ typedef struct _typeInfo * schema-qualified too. */ char *ftypname; - char *rolname; /* name of owner, or empty string */ + const char *rolname; /* name of owner, or empty string */ char *typacl; char *rtypacl; char *inittypacl; @@ -209,15 +236,12 @@ typedef struct _shellTypeInfo typedef struct _funcInfo { DumpableObject dobj; - char *rolname; /* name of owner, or empty string */ + DumpableAcl dacl; + const char *rolname; Oid lang; int nargs; Oid *argtypes; Oid prorettype; - char *proacl; - char *rproacl; - char *initproacl; - char *initrproacl; } FuncInfo; /* AggInfo is a superset of FuncInfo */ @@ -230,13 +254,11 @@ typedef struct _aggInfo typedef struct _ptcInfo { DumpableObject dobj; + DumpableAcl dacl; char *ptcreadfn; char *ptcwritefn; - char *ptcowner; + const char *rolname; char *ptcacl; - char *rproacl; - char *initproacl; - char *initrproacl; bool ptctrusted; Oid ptcreadid; Oid ptcwriteid; @@ -246,7 +268,7 @@ typedef struct _ptcInfo typedef struct _oprInfo { DumpableObject dobj; - char *rolname; + const char *rolname; char oprkind; Oid oprcode; } OprInfo; @@ -261,25 +283,25 @@ typedef struct _accessMethodInfo typedef struct _opclassInfo { DumpableObject dobj; - char *rolname; + const char *rolname; } OpclassInfo; typedef struct _opfamilyInfo { DumpableObject dobj; - char *rolname; + const char *rolname; } OpfamilyInfo; typedef struct _collInfo { DumpableObject dobj; - char *rolname; + const char *rolname; } CollInfo; typedef struct _convInfo { DumpableObject dobj; - char *rolname; + const char *rolname; } ConvInfo; typedef struct _tableInfo @@ -288,11 +310,8 @@ typedef struct _tableInfo * These fields are collected for every table in the database. */ DumpableObject dobj; - char *rolname; /* name of owner, or empty string */ - char *relacl; - char *rrelacl; - char *initrelacl; - char *initrrelacl; + DumpableAcl dacl; + const char *rolname; char relkind; char relstorage; char relpersistence; /* relation persistence */ @@ -305,6 +324,7 @@ typedef struct _tableInfo bool hasindex; /* does it have any indexes? */ bool hasrules; /* does it have any rules? */ bool hastriggers; /* does it have any triggers? */ + bool hascolumnACLs; /* do any columns have non-default ACLs? */ bool rowsec; /* is row security enabled? */ bool forcerowsec; /* is row security forced? */ bool hasoids; /* does it have OIDs? */ @@ -314,7 +334,8 @@ typedef struct _tableInfo uint32 toast_frozenxid; /* toast table's relfrozenxid, if any */ uint32 toast_minmxid; /* toast table's relminmxid */ int ncheck; /* # of CHECK expressions */ - char *reloftype; /* underlying type for typed table */ + Oid reltype; /* OID of table's composite type, if any */ + Oid reloftype; /* underlying type for typed table */ Oid foreign_server; /* foreign server oid, if applicable */ /* these two are set only if table is a sequence owned by a column: */ Oid owning_tab; /* OID of table owning sequence */ @@ -355,8 +376,6 @@ typedef struct _tableInfo char **attencoding; /* the attribute encoding values */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ - char *partkeydef; /* partition key definition */ - char *partbound; /* partition bound definition */ bool needs_override; /* has GENERATED ALWAYS AS IDENTITY */ char *amname; /* relation access method */ Oid amoid; /* relation access method oid */ @@ -366,15 +385,19 @@ typedef struct _tableInfo */ int numParents; /* number of (immediate) parent tables */ struct _tableInfo **parents; /* TableInfos of immediate parents */ - Oid parrelid; /* external partition's parent oid */ + Oid parrelid; /* partition's parent oid */ bool parparent; /* true if the table is partition parent */ int numIndexes; /* number of indexes */ struct _indxInfo *indexes; /* indexes */ struct _tableDataInfo *dataObj; /* TableDataInfo, if dumping its data */ int numTriggers; /* number of triggers for table */ struct _triggerInfo *triggers; /* array of TriggerInfo structs */ - bool isivm; /* is incrementally maintainable materialized view? */ - bool isdynamic; /* is dynamic table? */ + + /* GPDB */ + bool isivm; /* is incrementally maintainable materialized view? */ + bool isdynamic; /* is dynamic table? */ + char *partclause; /* partition definition, if table is partition parent */ + char *parttemplate; /* subpartition template */ } TableInfo; typedef struct _tableAttachInfo @@ -432,7 +455,7 @@ typedef struct _indexAttachInfo typedef struct _statsExtInfo { DumpableObject dobj; - char *rolname; /* name of owner, or empty string */ + const char *rolname; /* name of owner, or empty string */ int stattarget; /* statistics target */ } StatsExtInfo; @@ -471,7 +494,7 @@ typedef struct _evttriggerInfo DumpableObject dobj; char *evtname; char *evtevent; - char *evtowner; + const char *evtowner; char *evttags; char *evtfname; char evtenabled; @@ -503,15 +526,12 @@ typedef struct _constraintInfo typedef struct _procLangInfo { DumpableObject dobj; + DumpableAcl dacl; bool lanpltrusted; Oid lanplcallfoid; Oid laninline; Oid lanvalidator; - char *lanacl; - char *rlanacl; - char *initlanacl; - char *initrlanacl; - char *lanowner; /* name of owner, or empty string */ + const char *lanowner; } ProcLangInfo; typedef struct _castInfo @@ -553,7 +573,7 @@ typedef struct _prsInfo typedef struct _dictInfo { DumpableObject dobj; - char *rolname; + const char *rolname; Oid dicttemplate; char *dictinitoption; } TSDictInfo; @@ -568,56 +588,44 @@ typedef struct _tmplInfo typedef struct _cfgInfo { DumpableObject dobj; - char *rolname; + const char *rolname; Oid cfgparser; } TSConfigInfo; typedef struct _fdwInfo { DumpableObject dobj; - char *rolname; + DumpableAcl dacl; + const char *rolname; char *fdwhandler; char *fdwvalidator; char *fdwoptions; - char *fdwacl; - char *rfdwacl; - char *initfdwacl; - char *initrfdwacl; } FdwInfo; typedef struct _foreignServerInfo { DumpableObject dobj; - char *rolname; + DumpableAcl dacl; + const char *rolname; Oid srvfdw; char *srvtype; char *srvversion; - char *srvacl; - char *rsrvacl; - char *initsrvacl; - char *initrsrvacl; char *srvoptions; } ForeignServerInfo; typedef struct _defaultACLInfo { DumpableObject dobj; - char *defaclrole; + DumpableAcl dacl; + const char *defaclrole; char defaclobjtype; - char *defaclacl; - char *rdefaclacl; - char *initdefaclacl; - char *initrdefaclacl; } DefaultACLInfo; typedef struct _blobInfo { DumpableObject dobj; - char *rolname; - char *blobacl; - char *rblobacl; - char *initblobacl; - char *initrblobacl; + DumpableAcl dacl; + const char *rolname; } BlobInfo; /* @@ -644,7 +652,7 @@ typedef struct _policyInfo typedef struct _PublicationInfo { DumpableObject dobj; - char *rolname; + const char *rolname; bool puballtables; bool pubinsert; bool pubupdate; @@ -670,7 +678,7 @@ typedef struct _PublicationRelInfo typedef struct _SubscriptionInfo { DumpableObject dobj; - char *rolname; + const char *rolname; char *subconninfo; char *subslotname; char *subbinary; @@ -766,8 +774,7 @@ extern void getSubscriptions(Archive *fout); /* START MPP ADDITION */ extern ExtProtInfo *getExtProtocols(Archive *fout, int *numExtProtocols); extern BinaryUpgradeInfo *getBinaryUpgradeObjects(void); - -extern bool testExtProtocolSupport(Archive *fout); +extern void getPartitionDefs(Archive *fout, TableInfo tblinfo[], int numTables); /* END MPP ADDITION */ #endif /* PG_DUMP_H */ diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 1684a2b87ca..73a898fb220 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -615,7 +615,7 @@ main(int argc, char *argv[]) } /* Force quoting of all identifiers if requested. */ - if (quote_all_identifiers && server_version >= 80300) + if (quote_all_identifiers) executeCommand(conn, "SET quote_all_identifiers = true"); fprintf(OPF,"--\n-- Apache Cloudberry cluster dump\n--\n\n"); @@ -845,16 +845,31 @@ dumpResGroups(PGconn *conn) i_cpu_weight, i_cpuset; - printfPQExpBuffer(buf, "SELECT g.rsgname AS groupname, " - "t1.value AS concurrency, " - "t2.value AS cpu_max_percent, " - "t3.value AS cpu_weight, " - "t4.value AS cpuset " - "FROM pg_resgroup g " - " JOIN pg_resgroupcapability t1 ON g.oid = t1.resgroupid AND t1.reslimittype = 1 " - " JOIN pg_resgroupcapability t2 ON g.oid = t2.resgroupid AND t2.reslimittype = 2 " - " JOIN pg_resgroupcapability t3 ON g.oid = t3.resgroupid AND t3.reslimittype = 3 " - "LEFT JOIN pg_resgroupcapability t4 ON g.oid = t4.resgroupid AND t4.reslimittype = 4;"); + if (server_version >= GPDB7_MAJOR_PGVERSION) + { + printfPQExpBuffer(buf, "SELECT g.rsgname AS groupname, " + "t1.value AS concurrency, " + "t2.value AS cpu_max_percent, " + "t3.value AS cpu_weight, " + "t4.value AS cpuset " + "FROM pg_resgroup g " + " JOIN pg_resgroupcapability t1 ON g.oid = t1.resgroupid AND t1.reslimittype = 1 " + " JOIN pg_resgroupcapability t2 ON g.oid = t2.resgroupid AND t2.reslimittype = 2 " + " JOIN pg_resgroupcapability t3 ON g.oid = t3.resgroupid AND t3.reslimittype = 3 " + "LEFT JOIN pg_resgroupcapability t4 ON g.oid = t4.resgroupid AND t4.reslimittype = 4;"); + } + else + { + printfPQExpBuffer(buf, "SELECT g.rsgname AS groupname, " + "t1.value AS concurrency, " + "t2.value AS cpu_max_percent, " + " 100 AS cpu_weight, " + "t7.value AS cpuset " + "FROM pg_resgroup g " + " JOIN pg_resgroupcapability t1 ON g.oid = t1.resgroupid AND t1.reslimittype = 1 " + " JOIN pg_resgroupcapability t2 ON g.oid = t2.resgroupid AND t2.reslimittype = 2 " + "LEFT JOIN pg_resgroupcapability t7 ON g.oid = t7.resgroupid AND t7.reslimittype = 7;"); + } res = executeQuery(conn, buf->data); @@ -917,7 +932,7 @@ dumpResGroups(PGconn *conn) else { printfPQExpBuffer(buf, "CREATE RESOURCE GROUP %s WITH (" - "concurrency=%s, cpu_set=%s);\n", + "concurrency=%s, cpuset=%s);\n", fmtId(groupname), concurrency, cpuset); } @@ -1395,10 +1410,13 @@ dumpRoles(PGconn *conn) else appendPQExpBufferStr(buf, " NOREPLICATION"); - if (strcmp(PQgetvalue(res, i, i_rolbypassrls), "t") == 0) - appendPQExpBufferStr(buf, " BYPASSRLS"); - else - appendPQExpBufferStr(buf, " NOBYPASSRLS"); + if (server_version >= 90600) + { + if (strcmp(PQgetvalue(res, i, i_rolbypassrls), "t") == 0) + appendPQExpBufferStr(buf, " BYPASSRLS"); + else + appendPQExpBufferStr(buf, " NOBYPASSRLS"); + } if (strcmp(PQgetvalue(res, i, i_rolconnlimit), "-1") != 0) appendPQExpBuffer(buf, " CONNECTION LIMIT %s", @@ -1673,89 +1691,12 @@ dumpTablespaces(PGconn *conn) * [FIXME] the queries need to be slightly different if the backend isn't * Cloudberry, and the dump format should vary depending on if the dump is * --gp-syntax or --no-gp-syntax. - * - * For the tablespace ACLs, as of 9.6, we extract both the positive (as - * spcacl) and negative (as rspcacl) ACLs, relative to the default ACL for - * tablespaces, which are then passed to buildACLCommands() below. - * - * See buildACLQueries() and buildACLCommands(). - * - * The order in which privileges are in the ACL string (the order they - * have been GRANT'd in, which the backend maintains) must be preserved to - * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on - * those are dumped in the correct order. - * - * Note that we do not support initial privileges (pg_init_privs) on - * tablespaces, so this logic cannot make use of buildACLQueries(). */ - - if (server_version >= 140000) - { - res = executeQuery(conn, "SELECT oid, spcname, " - "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " - "pg_catalog.pg_tablespace_location(oid), " - "(SELECT array_agg(acl ORDER BY row_n) FROM " - " (SELECT acl, row_n FROM " - " unnest(coalesce(spcacl,acldefault('t',spcowner))) " - " WITH ORDINALITY AS perm(acl,row_n) " - " WHERE NOT EXISTS ( " - " SELECT 1 " - " FROM unnest(acldefault('t',spcowner)) " - " AS init(init_acl) " - " WHERE acl = init_acl)) AS spcacls) " - " AS spcacl, " - "(SELECT array_agg(acl ORDER BY row_n) FROM " - " (SELECT acl, row_n FROM " - " unnest(acldefault('t',spcowner)) " - " WITH ORDINALITY AS initp(acl,row_n) " - " WHERE NOT EXISTS ( " - " SELECT 1 " - " FROM unnest(coalesce(spcacl,acldefault('t',spcowner))) " - " AS permp(orig_acl) " - " WHERE acl = orig_acl)) AS rspcacls) " - " AS rspcacl, " - "array_to_string(spcoptions, ', ')," - "pg_catalog.shobj_description(oid, 'pg_tablespace'), " - "spcfilehandlersrc AS spchandlersrc, " - "spcfilehandlerbin AS spchandlerbin " - "FROM pg_catalog.pg_tablespace " - "WHERE spcname !~ '^pg_' " - "ORDER BY 1"); - } - else if (server_version >= 90600) - res = executeQuery(conn, "SELECT oid, spcname, " - "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " - "pg_catalog.pg_tablespace_location(oid), " - "(SELECT array_agg(acl ORDER BY row_n) FROM " - " (SELECT acl, row_n FROM " - " unnest(coalesce(spcacl,acldefault('t',spcowner))) " - " WITH ORDINALITY AS perm(acl,row_n) " - " WHERE NOT EXISTS ( " - " SELECT 1 " - " FROM unnest(acldefault('t',spcowner)) " - " AS init(init_acl) " - " WHERE acl = init_acl)) AS spcacls) " - " AS spcacl, " - "(SELECT array_agg(acl ORDER BY row_n) FROM " - " (SELECT acl, row_n FROM " - " unnest(acldefault('t',spcowner)) " - " WITH ORDINALITY AS initp(acl,row_n) " - " WHERE NOT EXISTS ( " - " SELECT 1 " - " FROM unnest(coalesce(spcacl,acldefault('t',spcowner))) " - " AS permp(orig_acl) " - " WHERE acl = orig_acl)) AS rspcacls) " - " AS rspcacl, " - "array_to_string(spcoptions, ', ')," - "pg_catalog.shobj_description(oid, 'pg_tablespace'), null " - "FROM pg_catalog.pg_tablespace " - "WHERE spcname !~ '^pg_' " - "ORDER BY 1"); - else if (server_version >= 90200) + if (server_version >= 90200) res = executeQuery(conn, "SELECT oid, spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " "pg_catalog.pg_tablespace_location(oid), " - "spcacl, '' as rspcacl, " + "spcacl, acldefault('t', spcowner) AS acldefault, " "array_to_string(spcoptions, ', ')," "pg_catalog.shobj_description(oid, 'pg_tablespace'), null " "FROM pg_catalog.pg_tablespace " @@ -1764,7 +1705,7 @@ dumpTablespaces(PGconn *conn) else if (server_version >= 90000) res = executeQuery(conn, "SELECT oid, spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " - "spclocation, spcacl, '' as rspcacl, " + "spclocation, spcacl, NULL AS acldefault, " "array_to_string(spcoptions, ', ')," "pg_catalog.shobj_description(oid, 'pg_tablespace'), null " "FROM pg_catalog.pg_tablespace " @@ -1773,8 +1714,8 @@ dumpTablespaces(PGconn *conn) else res = executeQuery(conn, "SELECT oid, spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " - "spclocation, spcacl, '' as rspcacl, null, " - "pg_catalog.shobj_description(oid, 'pg_tablespace'), null " + "spclocation, spcacl, NULL AS acldefault, null, " + "pg_catalog.shobj_description(oid, 'pg_tablespace') " "FROM pg_catalog.pg_tablespace " "WHERE spcname !~ '^pg_' " "ORDER BY 1"); @@ -1790,7 +1731,7 @@ dumpTablespaces(PGconn *conn) char *spcowner = PQgetvalue(res, i, 2); char *spclocation = PQgetvalue(res, i, 3); char *spcacl = PQgetvalue(res, i, 4); - char *rspcacl = PQgetvalue(res, i, 5); + char *acldefault = PQgetvalue(res, i, 5); char *spcoptions = PQgetvalue(res, i, 6); char *spccomment = PQgetvalue(res, i, 7); char *spchandlersrc = PQgetvalue(res, i, 8); @@ -1824,9 +1765,11 @@ dumpTablespaces(PGconn *conn) appendPQExpBuffer(buf, "ALTER TABLESPACE %s SET (%s);\n", fspcname, spcoptions); + /* tablespaces can't have initprivs */ + if (!skip_acls && !buildACLCommands(fspcname, NULL, NULL, "TABLESPACE", - spcacl, rspcacl, + spcacl, acldefault, spcowner, "", server_version, buf)) { pg_log_error("could not parse ACL list (%s) for tablespace \"%s\"", @@ -2710,7 +2653,7 @@ connectDatabase(const char *dbname, const char *connection_string, * our own major version. (See also version check in pg_dump.c.) */ if (my_version != server_version - && (server_version < 80300 || /* we can handle back to 8.3 */ + && (server_version < GPDB5_MAJOR_PGVERSION || /* we can handle back to 8.3 */ (server_version / 100) > (my_version / 100))) { pg_log_error("server version: %s; %s version: %s", diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 1a6ab11a3ed..5ec7e3f2b20 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -198,6 +198,24 @@ '--exclude-table=dump_test.test_table', 'postgres', ], }, + exclude_measurement => { + dump_cmd => [ + 'pg_dump', '--no-sync', + "--file=$tempdir/exclude_measurement.sql", + '--exclude-table-and-children=dump_test.measurement', + 'postgres', + ], + }, + exclude_measurement_data => { + dump_cmd => [ + 'pg_dump', + '--no-sync', + "--file=$tempdir/exclude_measurement_data.sql", + '--exclude-table-data-and-children=dump_test.measurement', + '--no-unlogged-table-data', + 'postgres', + ], + }, exclude_test_table_data => { dump_cmd => [ 'pg_dump', @@ -285,6 +303,17 @@ 'postgres', ], }, + only_dump_measurement => { + dump_cmd => [ + 'pg_dump', + '--no-sync', + "--file=$tempdir/only_dump_measurement.sql", + '--table-and-children=dump_test.measurement', + '--lock-wait-timeout=' + . (1000 * $TestLib::timeout_default), + 'postgres', + ], + }, role => { dump_cmd => [ 'pg_dump', @@ -402,7 +431,8 @@ # Tests which target the 'dump_test' schema, specifically. my %dump_test_schema_runs = ( only_dump_test_schema => 1, - test_schema_plus_blobs => 1,); + test_schema_plus_blobs => 1, + only_dump_measurement => 1); # Tests which are considered 'full' dumps by pg_dump, but there # are flags used to exclude specific items (ACLs, blobs, etc). @@ -417,6 +447,8 @@ exclude_test_table_data => 1, no_toast_compression => 1, no_blobs => 1, + exclude_measurement => 1, + exclude_measurement_data => 1, no_owner => 1, no_privs => 1, pg_dumpall_dbprivs => 1, @@ -450,6 +482,7 @@ unlike => { exclude_dump_test_schema => 1, no_privs => 1, + only_dump_measurement => 1, }, }, @@ -469,6 +502,7 @@ unlike => { exclude_dump_test_schema => 1, no_privs => 1, + only_dump_measurement => 1, }, }, @@ -547,6 +581,7 @@ unlike => { exclude_dump_test_schema => 1, no_owner => 1, + only_dump_measurement => 1, }, }, @@ -560,6 +595,7 @@ unlike => { exclude_dump_test_schema => 1, no_owner => 1, + only_dump_measurement => 1, }, }, @@ -592,7 +628,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'ALTER OPERATOR CLASS dump_test.op_class OWNER TO' => { @@ -605,6 +644,7 @@ unlike => { exclude_dump_test_schema => 1, no_owner => 1, + only_dump_measurement => 1, }, }, @@ -645,6 +685,7 @@ unlike => { exclude_dump_test_schema => 1, no_owner => 1, + only_dump_measurement => 1, }, }, @@ -671,6 +712,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -688,6 +730,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -707,6 +750,7 @@ }, unlike => { exclude_dump_test_schema => 1, + only_dump_measurement => 1, }, }, @@ -726,6 +770,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -745,6 +790,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -764,6 +810,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -783,6 +830,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -797,6 +845,10 @@ role => 1, section_pre_data => 1, binary_upgrade => 1, + only_dump_measurement => 1, + }, + unlike => { + exclude_measurement => 1, }, }, @@ -816,6 +868,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -837,7 +890,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'ALTER TABLE test_table OWNER TO' => { @@ -851,6 +907,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, no_owner => 1, }, }, @@ -870,6 +927,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -881,16 +939,22 @@ unlike => { exclude_dump_test_schema => 1, no_owner => 1, + only_dump_measurement => 1, }, }, 'ALTER TABLE measurement OWNER TO' => { regexp => qr/^\QALTER TABLE dump_test.measurement OWNER TO \E.+;/m, - like => - { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, + like => { + %full_runs, + %dump_test_schema_runs, + section_pre_data => 1, + only_dump_measurement => 1, + }, unlike => { exclude_dump_test_schema => 1, no_owner => 1, + exclude_measurement => 1, }, }, @@ -901,8 +965,12 @@ %full_runs, role => 1, section_pre_data => 1, + only_dump_measurement => 1, + }, + unlike => { + no_owner => 1, + exclude_measurement => 1, }, - unlike => { no_owner => 1, }, }, 'ALTER FOREIGN TABLE foreign_table OWNER TO' => { @@ -913,6 +981,7 @@ unlike => { exclude_dump_test_schema => 1, no_owner => 1, + only_dump_measurement => 1, }, }, @@ -924,6 +993,7 @@ unlike => { exclude_dump_test_schema => 1, no_owner => 1, + only_dump_measurement => 1, }, }, @@ -937,6 +1007,7 @@ only_dump_test_table => 1, no_owner => 1, role => 1, + only_dump_measurement => 1, }, }, @@ -1010,6 +1081,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -1029,6 +1101,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -1041,7 +1114,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'COMMENT ON COLUMN dump_test.test_second_table.col1' => { @@ -1053,7 +1129,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'COMMENT ON COLUMN dump_test.test_second_table.col2' => { @@ -1065,7 +1144,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'COMMENT ON CONVERSION dump_test.test_conversion' => { @@ -1076,7 +1158,10 @@ qr/^\QCOMMENT ON CONVERSION dump_test.test_conversion IS 'comment on test conversion';\E/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'COMMENT ON COLLATION test0' => { @@ -1143,7 +1228,10 @@ qr/^\QCOMMENT ON TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1 IS 'comment on text search configuration';\E/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'COMMENT ON TEXT SEARCH DICTIONARY dump_test.alt_ts_dict1' => { @@ -1155,7 +1243,10 @@ qr/^\QCOMMENT ON TEXT SEARCH DICTIONARY dump_test.alt_ts_dict1 IS 'comment on text search dictionary';\E/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'COMMENT ON TEXT SEARCH PARSER dump_test.alt_ts_prs1' => { @@ -1166,7 +1257,10 @@ qr/^\QCOMMENT ON TEXT SEARCH PARSER dump_test.alt_ts_prs1 IS 'comment on text search parser';\E/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'COMMENT ON TEXT SEARCH TEMPLATE dump_test.alt_ts_temp1' => { @@ -1177,7 +1271,10 @@ qr/^\QCOMMENT ON TEXT SEARCH TEMPLATE dump_test.alt_ts_temp1 IS 'comment on text search template';\E/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'COMMENT ON TYPE dump_test.planets - ENUM' => { @@ -1188,7 +1285,10 @@ qr/^\QCOMMENT ON TYPE dump_test.planets IS 'comment on enum type';\E/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'COMMENT ON TYPE dump_test.textrange - RANGE' => { @@ -1199,7 +1299,10 @@ qr/^\QCOMMENT ON TYPE dump_test.textrange IS 'comment on range type';\E/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'COMMENT ON TYPE dump_test.int42 - Regular' => { @@ -1210,7 +1313,10 @@ qr/^\QCOMMENT ON TYPE dump_test.int42 IS 'comment on regular type';\E/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'COMMENT ON TYPE dump_test.undefined - Undefined' => { @@ -1221,7 +1327,10 @@ qr/^\QCOMMENT ON TYPE dump_test.undefined IS 'comment on undefined type';\E/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'COPY test_table' => { @@ -1245,6 +1354,7 @@ exclude_test_table => 1, exclude_test_table_data => 1, schema_only => 1, + only_dump_measurement => 1, }, }, @@ -1268,6 +1378,7 @@ binary_upgrade => 1, exclude_dump_test_schema => 1, schema_only => 1, + only_dump_measurement => 1, }, }, @@ -1303,6 +1414,7 @@ binary_upgrade => 1, exclude_dump_test_schema => 1, schema_only => 1, + only_dump_measurement => 1, }, }, @@ -1324,6 +1436,7 @@ binary_upgrade => 1, exclude_dump_test_schema => 1, schema_only => 1, + only_dump_measurement => 1, }, }, @@ -1346,6 +1459,7 @@ binary_upgrade => 1, exclude_dump_test_schema => 1, schema_only => 1, + only_dump_measurement => 1, }, }, @@ -1367,6 +1481,7 @@ binary_upgrade => 1, exclude_dump_test_schema => 1, schema_only => 1, + only_dump_measurement => 1, }, }, @@ -1388,6 +1503,7 @@ binary_upgrade => 1, exclude_dump_test_schema => 1, schema_only => 1, + only_dump_measurement => 1, }, }, @@ -1567,7 +1683,10 @@ exclude_test_table => 1, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE CONVERSION dump_test.test_conversion' => { @@ -1578,7 +1697,10 @@ qr/^\QCREATE DEFAULT CONVERSION dump_test.test_conversion FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8;\E/xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE DOMAIN dump_test.us_postal_code' => { @@ -1597,7 +1719,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE FUNCTION dump_test.pltestlang_call_handler' => { @@ -1614,7 +1739,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE FUNCTION dump_test.trigger_func' => { @@ -1630,7 +1758,10 @@ \$\$;/xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE FUNCTION dump_test.event_trigger_func' => { @@ -1646,7 +1777,10 @@ \$\$;/xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE OPERATOR FAMILY dump_test.op_family' => { @@ -1658,7 +1792,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE OPERATOR CLASS dump_test.op_class' => { @@ -1688,7 +1825,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, # verify that a custom operator/opclass/range type is dumped in right order @@ -1718,7 +1858,10 @@ /xms, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE OPERATOR CLASS dump_test.op_class_empty' => { @@ -1733,7 +1876,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE EVENT TRIGGER test_event_trigger' => { @@ -1769,6 +1915,7 @@ unlike => { exclude_test_table => 1, exclude_dump_test_schema => 1, + only_dump_measurement => 1, }, }, @@ -1787,6 +1934,7 @@ unlike => { binary_upgrade => 1, exclude_dump_test_schema => 1, + only_dump_measurement => 1, }, }, @@ -1815,7 +1963,10 @@ \n\);/xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TYPE dump_test.int42' => { @@ -1824,7 +1975,10 @@ regexp => qr/^\QCREATE TYPE dump_test.int42;\E/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1' => { @@ -1836,7 +1990,10 @@ \s+\QPARSER = pg_catalog."default" );\E/xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'ALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1 ...' => { @@ -1901,7 +2058,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TEXT SEARCH TEMPLATE dump_test.alt_ts_temp1' => { @@ -1913,7 +2073,10 @@ \s+\QLEXIZE = dsimple_lexize );\E/xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TEXT SEARCH PARSER dump_test.alt_ts_prs1' => { @@ -1929,7 +2092,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TEXT SEARCH DICTIONARY dump_test.alt_ts_dict1' => { @@ -1942,7 +2108,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE FUNCTION dump_test.int42_in' => { @@ -1957,7 +2126,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE FUNCTION dump_test.int42_out' => { @@ -1972,7 +2144,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE FUNCTION ... SUPPORT' => { @@ -1986,7 +2161,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE PROCEDURE dump_test.ptest1' => { @@ -2000,7 +2178,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TYPE dump_test.int42 populated' => { @@ -2024,7 +2205,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TYPE dump_test.composite' => { @@ -2041,7 +2225,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TYPE dump_test.undefined' => { @@ -2050,7 +2237,10 @@ regexp => qr/^\QCREATE TYPE dump_test.undefined;\E/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'ALTER TYPE dump_test.int42 SET DEFAULT ENCODING' => { @@ -2060,7 +2250,10 @@ regexp => qr/^\QALTER TYPE dump_test.int42 SET DEFAULT ENCODING (compresstype=rle_type, blocksize=8192, compresslevel=4);\E/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE FOREIGN DATA WRAPPER dummy' => { @@ -2093,7 +2286,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE USER MAPPING FOR regress_dump_test_role SERVER s1' => { @@ -2138,7 +2334,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE MATERIALIZED VIEW matview_second' => { @@ -2154,7 +2353,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE MATERIALIZED VIEW matview_third' => { @@ -2170,7 +2372,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE MATERIALIZED VIEW matview_fourth' => { @@ -2186,7 +2391,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE MATERIALIZED VIEW matview_compression' => { @@ -2282,6 +2490,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -2302,6 +2511,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -2322,6 +2532,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -2342,6 +2553,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -2362,6 +2574,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -2382,6 +2595,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -2457,7 +2671,10 @@ regexp => qr/^CREATE SCHEMA dump_test;/m, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE SCHEMA dump_test_second_schema' => { @@ -2498,6 +2715,7 @@ unlike => { exclude_dump_test_schema => 1, exclude_test_table => 1, + only_dump_measurement => 1, }, }, @@ -2513,7 +2731,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TABLE test_second_table' => { @@ -2530,7 +2751,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TABLE test_compression' => { @@ -2574,11 +2798,16 @@ \)\n \QPARTITION BY RANGE (logdate);\E\n /xm, - like => - { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, + like => { + %full_runs, + %dump_test_schema_runs, + section_pre_data => 1, + only_dump_measurement => 1, + }, unlike => { binary_upgrade => 1, exclude_dump_test_schema => 1, + exclude_measurement => 1, }, }, @@ -2605,6 +2834,10 @@ section_pre_data => 1, role => 1, binary_upgrade => 1, + only_dump_measurement => 1, + }, + unlike => { + exclude_measurement => 1, }, }, @@ -2620,9 +2853,40 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_post_data => 1, + only_dump_measurement => 1, }, unlike => { exclude_dump_test_schema => 1, + exclude_measurement => 1, + }, + }, + + 'COPY measurement' => { + create_order => 93, + create_sql => 'INSERT INTO dump_test.measurement (city_id, logdate, peaktemp, unitsales) ' + . "VALUES (1, '2006-02-12', 35, 1);", + regexp => qr/^ + \QCOPY dump_test_second_schema.measurement_y2006m2 (city_id, logdate, peaktemp, unitsales) FROM stdin;\E + \n(?:1\t2006-02-12\t35\t1\n)\\\.\n + /xm, + like => { + %full_runs, + %dump_test_schema_runs, + data_only => 1, + only_dump_measurement => 1, + section_data => 1, + only_dump_test_schema => 1, + role_parallel => 1, + role => 1, + }, + unlike => { + binary_upgrade => 1, + schema_only => 1, + exclude_measurement => 1, + only_dump_test_schema => 1, + test_schema_plus_blobs => 1, + exclude_measurement => 1, + exclude_measurement_data => 1, }, }, @@ -2650,6 +2914,10 @@ section_post_data => 1, role => 1, binary_upgrade => 1, + only_dump_measurement => 1, + }, + unlike => { + exclude_measurement => 1, }, }, @@ -2662,6 +2930,10 @@ section_post_data => 1, role => 1, binary_upgrade => 1, + only_dump_measurement => 1, + }, + unlike => { + exclude_measurement => 1, }, }, @@ -2674,6 +2946,10 @@ section_post_data => 1, role => 1, binary_upgrade => 1, + only_dump_measurement => 1, + }, + unlike => { + exclude_measurement => 1, }, }, @@ -2709,7 +2985,11 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { binary_upgrade => 1, exclude_dump_test_schema => 1, }, + unlike => { + binary_upgrade => 1, + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TABLE test_fourth_table_zero_col' => { @@ -2722,7 +3002,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TABLE test_fifth_table' => { @@ -2745,7 +3028,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TABLE test_table_identity' => { @@ -2771,7 +3057,10 @@ /xms, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TABLE test_table_generated' => { @@ -2788,7 +3077,10 @@ /xms, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TABLE test_table_generated_child1 (without local columns)' => { @@ -2805,6 +3097,7 @@ unlike => { binary_upgrade => 1, exclude_dump_test_schema => 1, + only_dump_measurement => 1, }, }, @@ -2834,6 +3127,7 @@ unlike => { binary_upgrade => 1, exclude_dump_test_schema => 1, + only_dump_measurement => 1, }, }, @@ -2856,7 +3150,10 @@ /xms, like => { %full_runs, %dump_test_schema_runs, section_post_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TABLE test_inheritance_parent' => { @@ -2874,7 +3171,10 @@ /xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE TABLE test_inheritance_child' => { @@ -2896,6 +3196,7 @@ unlike => { binary_upgrade => 1, exclude_dump_test_schema => 1, + only_dump_measurement => 1, }, }, @@ -2908,7 +3209,10 @@ /xms, like => { %full_runs, %dump_test_schema_runs, section_post_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE STATISTICS extended_stats_options' => { @@ -2920,7 +3224,10 @@ /xms, like => { %full_runs, %dump_test_schema_runs, section_post_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'ALTER STATISTICS extended_stats_options' => { @@ -2963,7 +3270,10 @@ only_dump_test_table => 1, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE INDEX ON ONLY measurement' => { @@ -2991,6 +3301,8 @@ schema_only => 1, section_post_data => 1, test_schema_plus_blobs => 1, + only_dump_measurement => 1, + exclude_measurement_data => 1, }, unlike => { exclude_dump_test_schema => 1, @@ -2999,6 +3311,7 @@ pg_dumpall_globals_clean => 1, role => 1, section_pre_data => 1, + exclude_measurement => 1, }, }, @@ -3011,9 +3324,16 @@ \QALTER TABLE ONLY dump_test.measurement\E \n^\s+ \QADD CONSTRAINT measurement_pkey PRIMARY KEY (city_id, logdate);\E /xm, - like => - { %full_runs, %dump_test_schema_runs, section_post_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + like => { + %full_runs, + %dump_test_schema_runs, + section_post_data => 1, + only_dump_measurement => 1, + }, + unlike => { + exclude_dump_test_schema => 1, + exclude_measurement => 1, + }, }, 'CREATE INDEX ... ON measurement_y2006_m2' => { @@ -3024,6 +3344,10 @@ %full_runs, role => 1, section_post_data => 1, + only_dump_measurement => 1, + }, + unlike => { + exclude_measurement => 1, }, }, @@ -3035,6 +3359,11 @@ %full_runs, role => 1, section_post_data => 1, + only_dump_measurement => 1, + exclude_measurement_data => 1, + }, + unlike => { + exclude_measurement => 1, }, }, @@ -3061,6 +3390,8 @@ role => 1, schema_only => 1, section_post_data => 1, + only_dump_measurement => 1, + exclude_measurement_data => 1, }, unlike => { only_dump_test_schema => 1, @@ -3069,6 +3400,7 @@ pg_dumpall_globals_clean => 1, section_pre_data => 1, test_schema_plus_blobs => 1, + exclude_measurement => 1, }, }, @@ -3084,7 +3416,10 @@ \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'ALTER VIEW test_view SET DEFAULT' => { @@ -3095,7 +3430,10 @@ \QALTER TABLE dump_test.test_view ALTER COLUMN col1 SET DEFAULT 1;\E/xm, like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1, }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, # FIXME @@ -3270,6 +3608,7 @@ unlike => { exclude_dump_test_schema => 1, no_privs => 1, + only_dump_measurement => 1, }, }, @@ -3285,6 +3624,7 @@ unlike => { exclude_dump_test_schema => 1, no_privs => 1, + only_dump_measurement => 1, }, }, @@ -3300,6 +3640,7 @@ unlike => { exclude_dump_test_schema => 1, no_privs => 1, + only_dump_measurement => 1, }, }, @@ -3315,6 +3656,7 @@ unlike => { exclude_dump_test_schema => 1, no_privs => 1, + only_dump_measurement => 1, }, }, @@ -3344,6 +3686,7 @@ exclude_dump_test_schema => 1, exclude_test_table => 1, no_privs => 1, + only_dump_measurement => 1, }, }, @@ -3354,11 +3697,16 @@ TO regress_dump_test_role;', regexp => qr/^\QGRANT SELECT ON TABLE dump_test.measurement TO regress_dump_test_role;\E/m, - like => - { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, + like => { + %full_runs, + %dump_test_schema_runs, + section_pre_data => 1, + only_dump_measurement => 1, + }, unlike => { exclude_dump_test_schema => 1, no_privs => 1, + exclude_measurement => 1, }, }, @@ -3376,8 +3724,12 @@ %full_runs, role => 1, section_pre_data => 1, + only_dump_measurement => 1, + }, + unlike => { + no_privs => 1, + exclude_measurement => 1, }, - unlike => { no_privs => 1, }, }, # Disabled, because GPDB doesn't support large objects @@ -3421,6 +3773,7 @@ unlike => { exclude_dump_test_schema => 1, no_privs => 1, + only_dump_measurement => 1, }, }, @@ -3522,6 +3875,7 @@ binary_upgrade => 1, exclude_dump_test_schema => 1, schema_only => 1, + only_dump_measurement => 1, }, }, @@ -3537,6 +3891,7 @@ binary_upgrade => 1, exclude_dump_test_schema => 1, schema_only => 1, + only_dump_measurement => 1, }, }, @@ -3608,6 +3963,7 @@ only_dump_test_table => 1, role => 1, section_pre_data => 1, + only_dump_measurement => 1, }, unlike => { no_privs => 1, }, }, @@ -3646,7 +4002,10 @@ like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1 }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }, 'CREATE MATERIALIZED VIEW regress_pg_dump_matview_am' => { @@ -3666,7 +4025,10 @@ like => { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, - unlike => { exclude_dump_test_schema => 1 }, + unlike => { + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, }); ######################################### diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 90c8bb777fa..7423459e690 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -52,7 +52,7 @@ static bool describeOneTSConfig(const char *oid, const char *nspname, static void printACLColumn(PQExpBuffer buf, const char *colname); static bool listOneExtensionContents(const char *extname, const char *oid); -static bool isGPDB(void); +static bool isMPP(void); static bool isGPDB4200OrLater(void); static bool isGPDB5000OrLater(void); static bool isGPDB6000OrLater(void); @@ -64,7 +64,7 @@ static bool validateSQLNamePattern(PQExpBuffer buf, const char *pattern, const char *visibilityrule, bool *added_clause, int maxparts); -static bool isGPDB(void) +static bool isMPP(void) { static enum { @@ -86,7 +86,7 @@ static bool isGPDB(void) return false; ver = PQgetvalue(res, 0, 0); - if (strstr(ver, "Cloudberry") != NULL) + if (strstr(ver, "Cloudberry") != NULL || strstr(ver, "Greenplum") != NULL) { PQclear(res); talking_to_gpdb = gpdb_yes; @@ -113,7 +113,7 @@ static bool isGPDB4200OrLater(void) { bool retValue = false; - if (isGPDB() == true) + if (isMPP() == true) { PGresult *result; @@ -134,7 +134,7 @@ isGPDB4300OrLater(void) { bool retValue = false; - if (isGPDB() == true) + if (isMPP() == true) { PGresult *result; @@ -157,7 +157,7 @@ static bool isGPDB5000OrLater(void) { bool retValue = false; - if (isGPDB() == true) + if (isMPP() == true) { PGresult *res; @@ -171,8 +171,8 @@ static bool isGPDB5000OrLater(void) static bool isGPDB6000OrLater(void) { - if (!isGPDB()) - return false; /* Not Cloudberry at all. */ + if (!isMPP()) + return false; /* Not GP-based at all. */ /* GPDB 6 is based on PostgreSQL 9.4 */ return pset.sversion >= 90400; @@ -181,8 +181,8 @@ isGPDB6000OrLater(void) static bool isGPDB6000OrBelow(void) { - if (!isGPDB()) - return false; /* Not Cloudberry at all. */ + if (!isMPP()) + return false; /* Not GP-based at all. */ /* GPDB 6 is based on PostgreSQL 9.4 */ return pset.sversion <= 90400; @@ -191,8 +191,8 @@ isGPDB6000OrBelow(void) static bool isGPDB7000OrLater(void) { - if (!isGPDB()) - return false; /* Not Cloudberry at all. */ + if (!isMPP()) + return false; /* Not GP-based at all. */ /* GPDB 7 is based on PostgreSQL v12 */ return pset.sversion >= 120000; @@ -2007,7 +2007,7 @@ describeOneTableDetails(const char *schemaname, "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n" : "''"), /* GPDB Only: relstorage */ - (isGPDB() ? "c.relstorage" : "'h'"), + (isMPP() ? "c.relstorage" : "'h'"), oid); } else if (pset.sversion >= 90400) @@ -2027,7 +2027,7 @@ describeOneTableDetails(const char *schemaname, "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n" : "''"), /* GPDB Only: relstorage */ - (isGPDB() ? "c.relstorage" : "'h'"), + (isMPP() ? "c.relstorage" : "'h'"), oid); } else if (pset.sversion >= 90100) @@ -2047,7 +2047,7 @@ describeOneTableDetails(const char *schemaname, "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n" : "''"), /* GPDB Only: relstorage */ - (isGPDB() ? "c.relstorage" : "'h'"), + (isMPP() ? "c.relstorage" : "'h'"), oid); } else if (pset.sversion >= 90000) @@ -2066,7 +2066,7 @@ describeOneTableDetails(const char *schemaname, "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n" : "''"), /* GPDB Only: relstorage */ - (isGPDB() ? "c.relstorage" : "'h'"), + (isMPP() ? "c.relstorage" : "'h'"), oid); } else if (pset.sversion >= 80400) @@ -2084,7 +2084,7 @@ describeOneTableDetails(const char *schemaname, "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n" : "''"), /* GPDB Only: relstorage */ - (isGPDB() ? "c.relstorage" : "'h'"), + (isMPP() ? "c.relstorage" : "'h'"), oid); } else if (pset.sversion >= 80200) @@ -2098,7 +2098,7 @@ describeOneTableDetails(const char *schemaname, (verbose ? "pg_catalog.array_to_string(reloptions, E', ')" : "''"), /* GPDB Only: relstorage */ - (isGPDB() ? "relstorage" : "'h'"), + (isMPP() ? "relstorage" : "'h'"), oid); } else if (pset.sversion >= 80000) @@ -2161,7 +2161,7 @@ describeOneTableDetails(const char *schemaname, tableinfo.isdynamic = strcmp(PQgetvalue(res, 0, 16), "t") == 0; /* GPDB Only: relstorage */ - if (pset.sversion < 120000 && isGPDB()) + if (pset.sversion < 120000 && isMPP()) tableinfo.relstorage = *(PQgetvalue(res, 0, PQfnumber(res, "relstorage"))); else tableinfo.relstorage = 'h'; @@ -3840,7 +3840,7 @@ describeOneTableDetails(const char *schemaname, * listing them. */ tgdef = PQgetvalue(result, i, 1); - if (isGPDB() && strstr(tgdef, "RI_FKey_") != NULL) + if (isMPP() && strstr(tgdef, "RI_FKey_") != NULL) list_trigger = false; break; @@ -5145,7 +5145,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys cols_so_far = 4; /* Show Storage type for tables */ - if (showTables && isGPDB()) + if (showTables && isMPP()) { if (isGPDB7000OrLater()) { diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c index ba409e63879..462c5bc8cea 100644 --- a/src/fe_utils/string_utils.c +++ b/src/fe_utils/string_utils.c @@ -909,6 +909,69 @@ parsePGArray(const char *atext, char ***itemarray, int *nitems) } +/* + * Append one element to the text representation of a 1-dimensional Postgres + * array. + * + * The caller must provide the initial '{' and closing '}' of the array. + * This function handles all else, including insertion of commas and + * quoting of values. + * + * We assume that typdelim is ','. + */ +void +appendPGArray(PQExpBuffer buffer, const char *value) +{ + bool needquote; + const char *tmp; + + if (buffer->data[buffer->len - 1] != '{') + appendPQExpBufferChar(buffer, ','); + + /* Decide if we need quotes; this should match array_out()'s choices. */ + if (value[0] == '\0') + needquote = true; /* force quotes for empty string */ + else if (pg_strcasecmp(value, "NULL") == 0) + needquote = true; /* force quotes for literal NULL */ + else + needquote = false; + + if (!needquote) + { + for (tmp = value; *tmp; tmp++) + { + char ch = *tmp; + + if (ch == '"' || ch == '\\' || + ch == '{' || ch == '}' || ch == ',' || + /* these match array_isspace(): */ + ch == ' ' || ch == '\t' || ch == '\n' || + ch == '\r' || ch == '\v' || ch == '\f') + { + needquote = true; + break; + } + } + } + + if (needquote) + { + appendPQExpBufferChar(buffer, '"'); + for (tmp = value; *tmp; tmp++) + { + char ch = *tmp; + + if (ch == '"' || ch == '\\') + appendPQExpBufferChar(buffer, '\\'); + appendPQExpBufferChar(buffer, ch); + } + appendPQExpBufferChar(buffer, '"'); + } + else + appendPQExpBufferStr(buffer, value); +} + + /* * Format a reloptions array and append it to the given buffer. * diff --git a/src/include/fe_utils/string_utils.h b/src/include/fe_utils/string_utils.h index 3bd6f26dcd4..4a25a959f00 100644 --- a/src/include/fe_utils/string_utils.h +++ b/src/include/fe_utils/string_utils.h @@ -52,6 +52,7 @@ extern void appendConnStrVal(PQExpBuffer buf, const char *str); extern void appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname); extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems); +extern void appendPGArray(PQExpBuffer buffer, const char *value); extern bool appendReloptionsArray(PQExpBuffer buffer, const char *reloptions, const char *prefix, int encoding, bool std_strings); diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index 33c929e9082..c7c534417a2 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -47,6 +47,12 @@ typedef enum extern PGDLLIMPORT int log_statement; +/* Flags for restrict_nonsystem_relation_kind value */ +#define RESTRICT_RELKIND_VIEW 0x01 +#define RESTRICT_RELKIND_FOREIGN_TABLE 0x02 + +extern PGDLLIMPORT int restrict_nonsystem_relation_kind; + extern List *pg_parse_query(const char *query_string); extern List *pg_rewrite_query(Query *query); extern List *pg_analyze_and_rewrite(RawStmt *parsetree, @@ -67,6 +73,9 @@ extern List *pg_plan_queries(List *querytrees, const char *query_string, extern bool check_max_stack_depth(int *newval, void **extra, GucSource source); extern void assign_max_stack_depth(int newval, void *extra); +extern bool check_restrict_nonsystem_relation_kind(char **newval, void **extra, + GucSource source); +extern void assign_restrict_nonsystem_relation_kind(const char *newval, void *extra); extern void die(SIGNAL_ARGS); extern void quickdie(SIGNAL_ARGS) pg_attribute_noreturn(); diff --git a/src/include/utils/unsync_guc_name.h b/src/include/utils/unsync_guc_name.h index cba11770a81..55a81df5bae 100644 --- a/src/include/utils/unsync_guc_name.h +++ b/src/include/utils/unsync_guc_name.h @@ -537,6 +537,7 @@ "resource_select_only", "restart_after_crash", "restore_command", + "restrict_nonsystem_relation_kind", "role", "runaway_detector_activation_percent", "segment_size", diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index ac88c92f398..2a1fdca3562 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -1999,6 +1999,44 @@ select pg_get_viewdef('tt26v', true); FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) +-- Test that changing the relkind of a relcache entry doesn't cause +-- trouble. Prior instances of where it did: +-- CALDaNm2yXz+zOtv7y5zBd5WKT8O0Ld3YxikuU3dcyCvxF7gypA@mail.gmail.com +-- CALDaNm3oZA-8Wbps2Jd1g5_Gjrr-x3YWrJPek-mF5Asrrvz2Dg@mail.gmail.com +CREATE TABLE tt26(c int); +BEGIN; +CREATE TABLE tt27(c int); +SAVEPOINT q; +CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26; +SELECT * FROM tt27; + c +--- +(0 rows) + +ROLLBACK TO q; +CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26; +ROLLBACK; +BEGIN; +CREATE TABLE tt28(c int); +CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26; +CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26; +ERROR: "tt28" is already a view +ROLLBACK; +-- test restriction on non-system view expansion. +create table tt27v_tbl (a int); +create view tt27v as select a from tt27v_tbl; +set restrict_nonsystem_relation_kind to 'view'; +select a from tt27v where a > 0; -- Error +ERROR: access to non-system view "tt27v" is restricted +insert into tt27v values (1); -- Error +ERROR: access to non-system view "tt27v" is restricted +select viewname from pg_views where viewname = 'tt27v'; -- Ok to access a system view. + viewname +---------- + tt27v +(1 row) + +reset restrict_nonsystem_relation_kind; -- test display negative operator of const-folder expression create table tdis(a int, b int, c int); NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Greenplum Database data distribution key for this table. @@ -2046,7 +2084,7 @@ drop cascades to view aliased_view_2 drop cascades to view aliased_view_3 drop cascades to view aliased_view_4 DROP SCHEMA testviewschm2 CASCADE; -NOTICE: drop cascades to 76 other objects +NOTICE: drop cascades to 79 other objects DETAIL: drop cascades to table t1 drop cascades to view temporal1 drop cascades to view temporal2 @@ -2121,5 +2159,8 @@ drop cascades to view tt23v drop cascades to view tt24v drop cascades to view tt25v drop cascades to view tt26v +drop cascades to table tt26 +drop cascades to table tt27v_tbl +drop cascades to view tt27v drop cascades to table tdis drop cascades to view tdis_v1 diff --git a/src/test/regress/expected/create_view_optimizer.out b/src/test/regress/expected/create_view_optimizer.out index ece2034d92d..c17ae313cac 100755 --- a/src/test/regress/expected/create_view_optimizer.out +++ b/src/test/regress/expected/create_view_optimizer.out @@ -1997,6 +1997,44 @@ select pg_get_viewdef('tt26v', true); FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) +-- Test that changing the relkind of a relcache entry doesn't cause +-- trouble. Prior instances of where it did: +-- CALDaNm2yXz+zOtv7y5zBd5WKT8O0Ld3YxikuU3dcyCvxF7gypA@mail.gmail.com +-- CALDaNm3oZA-8Wbps2Jd1g5_Gjrr-x3YWrJPek-mF5Asrrvz2Dg@mail.gmail.com +CREATE TABLE tt26(c int); +BEGIN; +CREATE TABLE tt27(c int); +SAVEPOINT q; +CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26; +SELECT * FROM tt27; + c +--- +(0 rows) + +ROLLBACK TO q; +CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26; +ROLLBACK; +BEGIN; +CREATE TABLE tt28(c int); +CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26; +CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26; +ERROR: "tt28" is already a view +ROLLBACK; +-- test restriction on non-system view expansion. +create table tt27v_tbl (a int); +create view tt27v as select a from tt27v_tbl; +set restrict_nonsystem_relation_kind to 'view'; +select a from tt27v where a > 0; -- Error +ERROR: access to non-system view "tt27v" is restricted +insert into tt27v values (1); -- Error +ERROR: access to non-system view "tt27v" is restricted +select viewname from pg_views where viewname = 'tt27v'; -- Ok to access a system view. + viewname +---------- + tt27v +(1 row) + +reset restrict_nonsystem_relation_kind; -- test display negative operator of const-folder expression create table tdis(a int, b int, c int); NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. @@ -2044,7 +2082,7 @@ drop cascades to view aliased_view_2 drop cascades to view aliased_view_3 drop cascades to view aliased_view_4 DROP SCHEMA testviewschm2 CASCADE; -NOTICE: drop cascades to 76 other objects +NOTICE: drop cascades to 79 other objects DETAIL: drop cascades to table t1 drop cascades to view temporal1 drop cascades to view temporal2 @@ -2119,5 +2157,8 @@ drop cascades to view tt23v drop cascades to view tt24v drop cascades to view tt25v drop cascades to view tt26v +drop cascades to table tt26 +drop cascades to table tt27v_tbl +drop cascades to view tt27v drop cascades to table tdis drop cascades to view tdis_v1 diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql index f35364b8a23..4605fdb71b1 100644 --- a/src/test/regress/sql/create_view.sql +++ b/src/test/regress/sql/create_view.sql @@ -693,6 +693,38 @@ select x + y + z as c1, from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); + +-- Test that changing the relkind of a relcache entry doesn't cause +-- trouble. Prior instances of where it did: +-- CALDaNm2yXz+zOtv7y5zBd5WKT8O0Ld3YxikuU3dcyCvxF7gypA@mail.gmail.com +-- CALDaNm3oZA-8Wbps2Jd1g5_Gjrr-x3YWrJPek-mF5Asrrvz2Dg@mail.gmail.com +CREATE TABLE tt26(c int); + +BEGIN; +CREATE TABLE tt27(c int); +SAVEPOINT q; +CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26; +SELECT * FROM tt27; +ROLLBACK TO q; +CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26; +ROLLBACK; + +BEGIN; +CREATE TABLE tt28(c int); +CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26; +CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26; +ROLLBACK; + + +-- test restriction on non-system view expansion. +create table tt27v_tbl (a int); +create view tt27v as select a from tt27v_tbl; +set restrict_nonsystem_relation_kind to 'view'; +select a from tt27v where a > 0; -- Error +insert into tt27v values (1); -- Error +select viewname from pg_views where viewname = 'tt27v'; -- Ok to access a system view. +reset restrict_nonsystem_relation_kind; + -- test display negative operator of const-folder expression create table tdis(a int, b int, c int); create view tdis_v1 as select a,b,c, -1::int from tdis group by 1,2,3,4;