From 8c5e5a16017580c95d3fc4d1c63de9f92ce3fb8b Mon Sep 17 00:00:00 2001 From: Masahiko Sawada Date: Mon, 5 Aug 2024 06:05:23 -0700 Subject: [PATCH 01/32] Restrict accesses to non-system views and foreign tables during pg_dump. When pg_dump retrieves the list of database objects and performs the data dump, there was possibility that objects are replaced with others of the same name, such as views, and access them. This vulnerability could result in code execution with superuser privileges during the pg_dump process. This issue can arise when dumping data of sequences, foreign tables (only 13 or later), or tables registered with a WHERE clause in the extension configuration table. To address this, pg_dump now utilizes the newly introduced restrict_nonsystem_relation_kind GUC parameter to restrict the accesses to non-system views and foreign tables during the dump process. This new GUC parameter is added to back branches too, but these changes do not require cluster recreation. Back-patch to all supported branches. Reviewed-by: Noah Misch Security: CVE-2024-7348 Backpatch-through: 12 --- .../postgres_fdw/expected/postgres_fdw.out | 11 ++++ contrib/postgres_fdw/sql/postgres_fdw.sql | 8 +++ doc/src/sgml/config.sgml | 17 +++++ doc/src/sgml/ref/pg_dump.sgml | 8 +++ src/backend/foreign/foreign.c | 10 +++ src/backend/optimizer/plan/createplan.c | 13 ++++ src/backend/optimizer/util/plancat.c | 12 ++++ src/backend/rewrite/rewriteHandler.c | 17 +++++ src/backend/tcop/postgres.c | 63 +++++++++++++++++++ src/backend/utils/misc/guc.c | 15 ++++- src/bin/pg_dump/pg_dump.c | 48 ++++++++++++++ src/include/tcop/tcopprot.h | 9 +++ src/include/utils/unsync_guc_name.h | 1 + src/test/regress/expected/create_view.out | 43 ++++++++++++- .../expected/create_view_optimizer.out | 43 ++++++++++++- src/test/regress/sql/create_view.sql | 32 ++++++++++ 16 files changed, 347 insertions(+), 3 deletions(-) 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..c405fef866b 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -831,6 +831,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, 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/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a484f693b43..e85e39cf9f5 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -362,6 +362,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); @@ -1468,6 +1469,13 @@ 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"); + /* * Start transaction-snapshot mode transaction to dump consistent data. */ @@ -2362,6 +2370,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 +2486,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 +2517,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 +2760,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; } @@ -4819,6 +4845,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 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; From 888dc266c0ccf1aa2dde5291d452ae04f4fb9f13 Mon Sep 17 00:00:00 2001 From: Maxim Smyatkin Date: Fri, 3 Apr 2026 17:43:02 +0300 Subject: [PATCH 02/32] pg_dump/psql: properly recognize GP During a batch rebranding from Greenplum to Cloudberry we've lost ability to work with Greenplum from pg_dump and psql --- src/bin/pg_dump/pg_dump.c | 16 +++++++-------- src/bin/psql/describe.c | 42 +++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index e85e39cf9f5..f1c2644bfdd 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -385,17 +385,17 @@ 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 isMPP(Archive *fout); static bool isGPDB5000OrLater(Archive *fout); static bool isGPDB6000OrLater(Archive *fout); /* END MPP ADDITION */ /* - * Check if we are talking to GPDB + * Check if we are talking to Greenplum or Cloudberry */ static bool -isGPDB(Archive *fout) +isMPP(Archive *fout) { static int value = -1; /* -1 = not known yet, 0 = no, 1 = yes */ @@ -409,7 +409,7 @@ isGPDB(Archive *fout) res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK); ver = (PQgetvalue(res, 0, 0)); - if (strstr(ver, "Cloudberry") != NULL) + if (strstr(ver, "Cloudberry") != NULL || strstr(ver, "Greenplum") != NULL) value = 1; else value = 0; @@ -423,8 +423,8 @@ isGPDB(Archive *fout) static bool isGPDB5000OrLater(Archive *fout) { - if (!isGPDB(fout)) - return false; /* Not Cloudberry at all. */ + if (!isMPP(fout)) + return false; /* Not GP-based at all. */ /* GPDB 5 is based on PostgreSQL 8.3 */ return fout->remoteVersion >= 80300; @@ -434,8 +434,8 @@ isGPDB5000OrLater(Archive *fout) static bool isGPDB6000OrLater(Archive *fout) { - if (!isGPDB(fout)) - return false; /* Not Cloudberry at all. */ + if (!isMPP(fout)) + return false; /* Not GP-based at all. */ /* GPDB 6 is based on PostgreSQL 9.4 */ return fout->remoteVersion >= 90400; 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()) { From 025d5d300d48c305a66b6252e4c504743014cdc2 Mon Sep 17 00:00:00 2001 From: Daniel Gustafsson Date: Fri, 27 Aug 2021 16:24:33 +0200 Subject: [PATCH 03/32] Avoid invoking PQfnumber in loop constructs Backported from PG15 d782d5987e1022ba70171bcf3507cd87564ef23c Original commit message follows: When looping over the resultset from a SQL query, extracting the field number before the loop body to avoid repeated calls to PQfnumber is an established pattern. On very wide tables this can have a performance impact, but it wont be noticeable in the common case. This fixes a few queries which were extracting the field number in the loop body. Author: Hou Zhijie Reviewed-by: Nathan Bossart Discussion: https://postgr.es/m/OS0PR01MB57164C392783F29F6D0ECA0B94139@OS0PR01MB5716.jpnprd01.prod.outlook.com --- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index f1c2644bfdd..7702d689d52 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -9883,6 +9883,26 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) { DumpOptions *dopt = fout->dopt; PQExpBuffer q = createPQExpBuffer(); + 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; /* GPDB_14_MERGE_FIXME: GPDB specific column, need to keep this for easy to use*/ int i_attencoding; @@ -10036,32 +10056,53 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *)); hasdefaults = false; + 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"); + for (int j = 0; j < ntups; j++) { - if (j + 1 != atoi(PQgetvalue(res, j, PQfnumber(res, "attnum")))) + if (j + 1 != atoi(PQgetvalue(res, j, 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, j, i_attname)); + tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, j, i_atttypname)); + tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod)); + tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget)); + tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage)); + tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage)); + tbinfo->attidentity[j] = *(PQgetvalue(res, j, i_attidentity)); + tbinfo->attgenerated[j] = *(PQgetvalue(res, j, 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, j, i_attisdropped)[0] == 't'); + tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen)); + tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign)); + tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't'); + tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't'); + tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions)); + tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation)); + tbinfo->attcompression[j] = *(PQgetvalue(res, j, i_attcompression)); + tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions)); + tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, j, i_attmissingval)); tbinfo->attrdefs[j] = NULL; /* fix below */ - if (PQgetvalue(res, j, PQfnumber(res, "atthasdef"))[0] == 't') + if (PQgetvalue(res, j, i_atthasdef)[0] == 't') hasdefaults = true; /* these flags will be set in flagInhAttrs() */ tbinfo->inhNotNull[j] = false; @@ -11902,6 +11943,8 @@ dumpEnumType(Archive *fout, const TypeInfo *tyinfo) char *qtypname; char *qualtypname; char *label; + int i_enumlabel; + int i_oid; if (fout->remoteVersion >= 90100) appendPQExpBuffer(query, "SELECT oid, enumlabel " @@ -11941,10 +11984,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 "); @@ -11956,11 +12001,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"); From a07c2ea09282cbaf71a507f1c051fce9f34c0739 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 19 Oct 2021 17:22:22 -0400 Subject: [PATCH 04/32] pg_dump: Reorganize getTables() Backported from PG15 4438eb4a49 Conflicts resolved by CBDB: * get foreignserver back * add islvm, isdynamic and amoid Conflicts resolved by GP7: * Include GPDB specific fields: relstorage, parrelid, parlevel * Excluded BM_BITMAPINDEX_NAMESPACE * Excluded upstream foreignserver field * Removed checks for version <= 80200 Original commit message follows: Along the same lines as 047329624, ed2c7f65b and daa9fe8a5, reduce code duplication by having just one copy of the parts of the query that are the same across all server versions; and make the conditionals control the smallest possible amount of code. This also gets rid of the confusing assortment of different ways to accomplish the same result that we had here before. While at it, make sure all three relevant parts of the function list the fields in the same order. This is just neatnik-ism, of course. Discussion: https://postgr.es/m/1240992.1634419055@sss.pgh.pa.us --- src/bin/pg_dump/pg_dump.c | 871 +++++++++++++------------------------- 1 file changed, 297 insertions(+), 574 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 7702d689d52..6926e9f4bdf 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -7310,7 +7310,6 @@ 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; @@ -7351,6 +7350,7 @@ getTables(Archive *fout, int *numTables) int i_amoid; int i_isivm; int i_isdynamic; + int i_relstorage; /* * Find all the tables and table-like objects. @@ -7370,53 +7370,155 @@ getTables(Archive *fout, int *numTables) * 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. */ + appendPQExpBuffer(query, + "SELECT c.tableoid, c.oid, c.relname, " + "c.relnamespace, c.relkind, " + "(%s c.relowner) AS rolname, " + "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, ", + username_subquery); + if (fout->remoteVersion >= 90600) - { - char *partkeydef = "NULL"; - char *ispartition = "false"; - char *partbound = "NULL"; - char *relhasoids = "c.relhasoids"; + appendPQExpBufferStr(query, + "'' AS relstorage, "); + else + appendPQExpBufferStr(query, + "c.relstorage, "); + + if (fout->remoteVersion >= 120000) + appendPQExpBufferStr(query, + "false AS relhasoids, "); + else + appendPQExpBufferStr(query, + "c.relhasoids, "); + + if (fout->remoteVersion >= 80400) + appendPQExpBufferStr(query, + "c.relhastriggers, "); + else + appendPQExpBufferStr(query, + "(c.reltriggers <> 0) AS relhastriggers, "); + + if (fout->remoteVersion >= 90100) + appendPQExpBufferStr(query, + "c.relpersistence, "); + else + appendPQExpBufferStr(query, + "'p' AS relpersistence, "); + if (fout->remoteVersion >= 90300) + appendPQExpBufferStr(query, + "c.relispopulated, "); + else + appendPQExpBufferStr(query, + "'t' as relispopulated, "); + + if (fout->remoteVersion >= 90400) + appendPQExpBufferStr(query, + "c.relreplident, "); + else + appendPQExpBufferStr(query, + "'d' AS relreplident, "); + + if (fout->remoteVersion >= 90500) + appendPQExpBufferStr(query, + "c.relrowsecurity, c.relforcerowsecurity, "); + else + appendPQExpBufferStr(query, + "false AS relrowsecurity, " + "false AS relforcerowsecurity, "); + + if (fout->remoteVersion >= 90300) + appendPQExpBufferStr(query, + "c.relminmxid, tc.relminmxid AS tminmxid, "); + else + appendPQExpBufferStr(query, + "0 AS relminmxid, 0 AS tminmxid, "); + + 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, "); + else + appendPQExpBufferStr(query, + "c.reloptions, NULL AS checkoption, "); + + if (fout->remoteVersion >= 80400) + appendPQExpBufferStr(query, + "tc.reloptions AS toast_reloptions, "); + else + appendPQExpBufferStr(query, + "NULL AS toast_reloptions, "); + + if (fout->remoteVersion >= 90000) + appendPQExpBufferStr(query, + "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "); + else + appendPQExpBufferStr(query, + "NULL 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, "); + + 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, "); + else + appendPQExpBufferStr(query, + "NULL AS foreignserver, "); + + if (fout->remoteVersion >= 90600) + appendPQExpBuffer(query, + "c.relisivm AS isivm, %s, ", + (fout->version.type == Cloudberry && fout->version.version >= 2) ? "c.relisdynamic AS isdynamic " : "false AS isdynamic "); + + + if (fout->remoteVersion >= 90600) + { PQExpBuffer acl_subquery = createPQExpBuffer(); PQExpBuffer racl_subquery = createPQExpBuffer(); PQExpBuffer initacl_subquery = createPQExpBuffer(); PQExpBuffer initracl_subquery = createPQExpBuffer(); - PQExpBuffer attacl_subquery = createPQExpBuffer(); PQExpBuffer attracl_subquery = createPQExpBuffer(); PQExpBuffer attinitacl_subquery = createPQExpBuffer(); PQExpBuffer attinitracl_subquery = createPQExpBuffer(); - /* - * Collect the information about any partitioned tables, which were - * added in PG10. - */ - - if (fout->remoteVersion >= 100000) - { - partkeydef = "pg_get_partkeydef(c.oid)"; - ispartition = "c.relispartition"; - partbound = "pg_get_expr(c.relpartbound, c.oid)"; - } - - /* In PG12 upwards WITH OIDS does not exist anymore. */ - if (fout->remoteVersion >= 120000) - relhasoids = "'f'::bool"; - - /* - * Left join to pick up dependency info linking sequences to their - * owning column, if any (note this dependency is AUTO as of 8.2) - * - * Left join to detect if any privileges are still as-set-at-init, in - * which case we won't dump out ACL commands for those. - */ - buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, initracl_subquery, "c.relacl", "c.relowner", "CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE) @@ -7424,39 +7526,18 @@ getTables(Archive *fout, int *numTables) dopt->binary_upgrade); buildACLQueries(attacl_subquery, attracl_subquery, attinitacl_subquery, - attinitracl_subquery, "at.attacl", "c.relowner", "'c'", + attinitracl_subquery, "at.attacl", "c.relowner","'c'", dopt->binary_upgrade); 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, " - "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, " + "%s AS initrelacl, %s as initrrelacl, ", + acl_subquery->data, + racl_subquery->data, + initacl_subquery->data, + initracl_subquery->data); + + appendPQExpBuffer(query, "EXISTS (SELECT 1 FROM pg_attribute at LEFT JOIN pg_init_privs pip ON " "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " @@ -7467,472 +7548,110 @@ getTables(Archive *fout, int *numTables) "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, + "AS changed_acl, ", 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); + attinitracl_subquery->data); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); destroyPQExpBuffer(initacl_subquery); destroyPQExpBuffer(initracl_subquery); - 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, " - "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, " - "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 - { - /* - * 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, " + appendPQExpBufferStr(query, "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, " + "false AS changed_acl, "); + + if (fout->remoteVersion >= 100000) + appendPQExpBufferStr(query, + "pg_get_partkeydef(c.oid) AS partkeydef, " + "c.relispartition AS ispartition, " + "pg_get_expr(c.relpartbound, c.oid) AS partbound "); + else + appendPQExpBufferStr(query, "NULL AS partkeydef, " "false AS ispartition, " - "NULL AS partbound " - "FROM pg_class c " + "NULL AS partbound "); + + /* + * 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 = '%c' AND " + "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " 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); - } + "d.refclassid = c.tableoid 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_init_privs to detect if any privileges + * are still as-set-at-init, in which case we won't dump out ACL commands + * for those. We also are interested in the amname as of 9.6. + * + * 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 <= 90426) // GPDB 6 or below + appendPQExpBufferStr(query, + "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid)\n" + "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 */ + + if (fout->remoteVersion >= 90600) + appendPQExpBufferStr(query, + "ORDER BY c.oid"); + else + appendPQExpBufferStr(query, + "AND c.oid NOT IN (select p.parchildrelid from pg_partition_rule p\n" + "LEFT JOIN pg_exttable e on p.parchildrelid=e.reloid where e.reloid is null)\n" + "ORDER BY c.oid"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -7955,48 +7674,48 @@ 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_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_relacl = PQfnumber(res, "relacl"); + i_rrelacl = PQfnumber(res, "rrelacl"); + i_initrelacl = PQfnumber(res, "initrelacl"); + i_initrrelacl = PQfnumber(res, "initrrelacl"); i_changed_acl = PQfnumber(res, "changed_acl"); i_partkeydef = PQfnumber(res, "partkeydef"); 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) { @@ -8024,33 +7743,12 @@ 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].relkind = *(PQgetvalue(res, i, i_relkind)); - tblinfo[i].relstorage = *(PQgetvalue(res, i, i_relstorage)); - tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence)); + tblinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + 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; @@ -8062,13 +7760,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 (PQgetisnull(res, i, i_reloftype)) + tblinfo[i].reloftype = NULL; + else + tblinfo[i].reloftype = pg_strdup(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].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].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].relstorage = *(PQgetvalue(res, i, i_relstorage)); if (tblinfo[i].parrelid != 0) { /* @@ -8084,10 +7811,6 @@ getTables(Archive *fout, int *numTables) 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; @@ -8104,25 +7827,10 @@ getTables(Archive *fout, int *numTables) 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. - * - * This can result in a significant performance improvement in cases - * where we are only looking to dump out the ACL (eg: pg_catalog). - */ - 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 ? 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); @@ -8136,6 +7844,21 @@ getTables(Archive *fout, int *numTables) /* foreign server */ tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver)); + /* + * 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. + * + * This can result in a significant performance improvement in cases + * where we are only looking to dump out the ACL (eg: pg_catalog). + */ + 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; + /* * Read-lock target tables to make sure they aren't DROPPED or altered * in schema before we get around to dumping them. From dee7e1ef5223a7037654d59a1ca2c7d718b0514e Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 6 Dec 2021 12:25:48 -0500 Subject: [PATCH 05/32] Refactor pg_dump's tracking of object components to be dumped. Backported from PG15 5209c0ba0bfd16f23e38f707e487c0626e70564c Conflicts Resolved: * Rejected changes to binary_upgrade functions. There is a large diff with GPDB specific changes that will be refactored in a future commit. * Retain GPDB_96_MERGE_FIXME for parrelid check. This will be revisited in a future commit. * GPDB specific dumpFunctionName caused changes to dumpTSParser to be misaligned. Manually resolved. Original commit message follows: Split the DumpableObject.dump bitmask field into separate bitmasks tracking which components are requested to be dumped (in the existing "dump" field) and which components exist for the particular object (in the new "components" field). This gets rid of some klugy and easily-broken logic that involved setting bits and later clearing them. More importantly, it restores the originally intended behavior that pg_dump's secondary data-gathering queries should not be executed for objects we have no interest in dumping. That optimization got broken when the dump flag was turned into a bitmask, because irrelevant bits tended to remain set in many cases. Since the "components" field starts from a minimal set of bits and is added onto as needed, ANDing it with "dump" provides a reliable indicator of what we actually have to dump, without having to complicate the logic that manages the request bits. This makes a significant difference in the number of queries needed when, for example, there are many functions in extensions. Discussion: https://postgr.es/m/2273648.1634764485@sss.pgh.pa.us Discussion: https://postgr.es/m/7d7eb6128f40401d81b3b7a898b6b4de@W2012-02.nidsa.loc --- src/bin/pg_dump/common.c | 2 + src/bin/pg_dump/pg_dump.c | 619 +++++++++++++++++++++----------------- src/bin/pg_dump/pg_dump.h | 13 +- 3 files changed, 360 insertions(+), 274 deletions(-) diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 661e19605f6..75872b4473b 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -606,6 +606,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/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 6926e9f4bdf..a44b8bd8192 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -161,6 +161,14 @@ static DumpId binary_upgrade_dumpid; static bool have_extra_float_digits = false; static int extra_float_digits; +/* 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 @@ -213,14 +221,14 @@ static void dumpComment(Archive *fout, const char *type, const char *name, CatalogId catalogId, int subid, DumpId dumpId); 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); @@ -1133,6 +1141,14 @@ main(int argc, char **argv) setExtPartDependency(tblinfo, numTables); + /* + * Collect comments and security labels, if wanted. + */ + if (!dopt.no_comments) + collectComments(fout); + if (!dopt.no_security_labels) + collectSecLabels(fout); + /* Lastly, create dummy objects to represent the section boundaries */ boundaryObjs = createBoundaryObjects(); @@ -2014,6 +2030,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 dumpCommentExtended for it.) + */ + nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT; } else nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL; @@ -2986,7 +3009,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) @@ -3049,6 +3072,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. */ @@ -4047,11 +4073,15 @@ getBlobs(Archive *fout) 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; + /* Blobs have data */ + binfo[i].dobj.components |= DUMP_COMPONENT_DATA; + + /* Mark whether blob has an ACL */ + if (!(PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl))) + binfo[i].dobj.components |= DUMP_COMPONENT_ACL; /* * In binary-upgrade mode for blobs, we do *not* dump out the blob @@ -4075,6 +4105,7 @@ getBlobs(Archive *fout) bdata->catId = nilCatalogId; AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); + bdata->components |= DUMP_COMPONENT_DATA; } PQclear(res); @@ -4122,7 +4153,7 @@ 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, @@ -4267,6 +4298,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. @@ -4390,6 +4423,7 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo) const char *cmd; char *tag; + /* Do nothing in data-only dump */ if (dopt->dataOnly) return; @@ -4410,7 +4444,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, @@ -4472,7 +4506,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, @@ -4617,9 +4651,6 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo) char *qpubname; bool first = true; - if (!(pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) - return; - delq = createPQExpBuffer(); query = createPQExpBuffer(); @@ -4675,13 +4706,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, @@ -4797,9 +4829,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(); @@ -4816,13 +4845,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); @@ -5002,9 +5032,6 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo) int npubnames = 0; int i; - if (!(subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) - return; - delq = createPQExpBuffer(); query = createPQExpBuffer(); @@ -5047,13 +5074,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, @@ -5863,18 +5891,12 @@ getNamespaces(Archive *fout, int *numNamespaces) /* 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; + /* Mark whether namespace has an ACL */ + if (!(PQgetisnull(res, i, i_nspacl) && + PQgetisnull(res, i, i_rnspacl) && + PQgetisnull(res, i, i_initnspacl) && + PQgetisnull(res, i, i_initrnspacl))) + nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL; if (strlen(nsinfo[i].rolname) == 0) pg_log_warning("owner of schema \"%s\" appears to be invalid", @@ -6191,11 +6213,12 @@ 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) && + PQgetisnull(res, i, i_rtypacl) && + PQgetisnull(res, i, i_inittypacl) && + PQgetisnull(res, i, i_initrtypacl))) + tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL; /* * If it's a domain, fetch info about its constraints, if any @@ -6318,9 +6341,6 @@ getOperators(Archive *fout, int *numOprs) /* 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); @@ -6400,9 +6420,6 @@ getCollations(Archive *fout, int *numCollations) /* 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); @@ -6472,9 +6489,6 @@ getConversions(Archive *fout, int *numConversions) /* 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); @@ -6545,9 +6559,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); @@ -6617,9 +6628,6 @@ getOpclasses(Archive *fout, int *numOpclasses) /* 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); @@ -6700,9 +6708,6 @@ getOpfamilies(Archive *fout, int *numOpfamilies) /* 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); @@ -6878,11 +6883,12 @@ getAggregates(Archive *fout, int *numAggs) /* Decide whether we want to dump it */ selectDumpableObject(&(agginfo[i].aggfn.dobj), fout); - /* Do not try to dump ACL if no ACL exists. */ - if (PQgetisnull(res, i, i_aggacl) && PQgetisnull(res, i, i_raggacl) && - PQgetisnull(res, i, i_initaggacl) && - PQgetisnull(res, i, i_initraggacl)) - agginfo[i].aggfn.dobj.dump &= ~DUMP_COMPONENT_ACL; + /* Mark whether aggregate has an ACL */ + 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.components |= DUMP_COMPONENT_ACL; } PQclear(res); @@ -7270,11 +7276,12 @@ getFuncs(Archive *fout, int *numFuncs) selectDumpableFunction(&finfo[i]); selectDumpableObject(&(finfo[i].dobj), fout); - /* Do not try to dump ACL if no ACL exists. */ - if (PQgetisnull(res, i, i_proacl) && PQgetisnull(res, i, i_rproacl) && - PQgetisnull(res, i, i_initproacl) && - PQgetisnull(res, i, i_initrproacl)) - finfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* Mark whether function has an ACL */ + if (!(PQgetisnull(res, i, i_proacl) && + PQgetisnull(res, i, i_rproacl) && + PQgetisnull(res, i, i_initproacl) && + PQgetisnull(res, i, i_initrproacl))) + finfo[i].dobj.components |= DUMP_COMPONENT_ACL; if (strlen(finfo[i].rolname) == 0) pg_log_warning("owner of function \"%s\" appears to be invalid", @@ -7314,6 +7321,7 @@ getTables(Archive *fout, int *numTables) int i_rrelacl; int i_initrelacl; int i_initrrelacl; + int i_reltype; int i_rolname; int i_relchecks; int i_relhastriggers; @@ -7374,7 +7382,7 @@ getTables(Archive *fout, int *numTables) appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.relname, " - "c.relnamespace, c.relkind, " + "c.relnamespace, c.relkind, c.reltype, " "(%s c.relowner) AS rolname, " "c.relchecks, " "c.relhasindex, c.relhasrules, c.relpages, " @@ -7593,11 +7601,19 @@ getTables(Archive *fout, int *numTables) "d.objsubid = 0 AND " "d.refclassid = c.tableoid 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_init_privs to detect if any privileges * are still as-set-at-init, in which case we won't dump out ACL commands * for those. We also are interested in the amname as of 9.6. + */ + if (fout->remoteVersion >= 90600) + appendPQExpBufferStr(query, + "LEFT JOIN pg_am am ON (c.relam = am.oid)\n" + "LEFT JOIN pg_init_privs pip ON " + "(c.oid = pip.objoid " + "AND pip.classoid = 'pg_class'::regclass " + "AND pip.objsubid = 0)\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 @@ -7637,17 +7653,17 @@ getTables(Archive *fout, int *numTables) CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"); - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 80400) appendPQExpBufferStr(query, "AND c.relnamespace <> 7012\n"); /* BM_BITMAPINDEX_NAMESPACE */ - else + else appendPQExpBufferStr(query, "AND c.relnamespace <> 3012\n"); /* BM_BITMAPINDEX_NAMESPACE in GPDB 5 and below */ if (fout->remoteVersion >= 90600) appendPQExpBufferStr(query, "ORDER BY c.oid"); - else + else appendPQExpBufferStr(query, "AND c.oid NOT IN (select p.parchildrelid from pg_partition_rule p\n" "LEFT JOIN pg_exttable e on p.parchildrelid=e.reloid where e.reloid is null)\n" @@ -7675,6 +7691,7 @@ getTables(Archive *fout, int *numTables) i_relname = PQfnumber(res, "relname"); i_relnamespace = PQfnumber(res, "relnamespace"); i_relkind = PQfnumber(res, "relkind"); + i_reltype = PQfnumber(res, "reltype"); i_rolname = PQfnumber(res, "rolname"); i_relchecks = PQfnumber(res, "relchecks"); i_relhasindex = PQfnumber(res, "relhasindex"); @@ -7744,6 +7761,7 @@ getTables(Archive *fout, int *numTables) tblinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_relnamespace))); tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind)); + tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype)); tblinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks)); tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0); @@ -7827,7 +7845,21 @@ getTables(Archive *fout, int *numTables) else selectDumpableTable(&tblinfo[i], fout); - tblinfo[i].interesting = tblinfo[i].dobj.dump ? true : false; + /* + * 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. + * + * 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. + */ + tblinfo[i].interesting = (tblinfo[i].dobj.dump & + (DUMP_COMPONENT_DEFINITION | + DUMP_COMPONENT_DATA)) != 0; + tblinfo[i].dummy_view = false; /* might get set during sort */ tblinfo[i].postponed_def = false; /* might get set during sort */ @@ -7844,7 +7876,12 @@ getTables(Archive *fout, int *numTables) /* 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 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 @@ -7853,11 +7890,12 @@ getTables(Archive *fout, int *numTables) * This can result in a significant performance improvement in cases * where we are only looking to dump out the ACL (eg: pg_catalog). */ - 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; + 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.components |= DUMP_COMPONENT_ACL; /* * Read-lock target tables to make sure they aren't DROPPED or altered @@ -7879,12 +7917,12 @@ getTables(Archive *fout, int *numTables) * 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 && - (tblinfo[i].relkind == RELKIND_RELATION || - tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE) && + if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) && tblinfo[i].parrelid == 0 && - (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE)) { if (!lockTableDumped) appendPQExpBuffer(query, @@ -8148,13 +8186,9 @@ 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\"", @@ -8491,9 +8525,6 @@ getExtendedStatistics(Archive *fout) /* 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); @@ -9183,9 +9214,6 @@ getEventTriggers(Archive *fout, int *numEventTriggers) /* 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); @@ -9355,11 +9383,12 @@ getProcLangs(Archive *fout, int *numProcLangs) /* 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) && + PQgetisnull(res, i, i_rlanacl) && + PQgetisnull(res, i, i_initlanacl) && + PQgetisnull(res, i, i_initrlanacl))) + planginfo[i].dobj.components |= DUMP_COMPONENT_ACL; } PQclear(res); @@ -9469,9 +9498,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); @@ -10184,9 +10210,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); @@ -10267,9 +10290,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); @@ -10342,9 +10362,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); @@ -10418,9 +10435,6 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) /* 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); @@ -10586,11 +10600,12 @@ 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) && + PQgetisnull(res, i, i_rfdwacl) && + PQgetisnull(res, i, i_initfdwacl) && + PQgetisnull(res, i, i_initrfdwacl))) + fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL; } PQclear(res); @@ -10740,11 +10755,15 @@ 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) && + PQgetisnull(res, i, i_rsrvacl) && + PQgetisnull(res, i, i_initsrvacl) && + PQgetisnull(res, i, i_initrsrvacl))) + srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL; } PQclear(res); @@ -10895,6 +10914,9 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs) 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); } @@ -11115,19 +11137,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. */ @@ -11188,15 +11202,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; @@ -11206,7 +11222,7 @@ collectComments(Archive *fout, CommentItem **items) int i_objsubid; int ntups; int i; - CommentItem *comments; + DumpableObject *dobj; query = createPQExpBuffer(); @@ -11226,20 +11242,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; } /* @@ -11249,8 +11297,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: @@ -11433,8 +11492,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(); @@ -11497,8 +11556,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(); @@ -11622,8 +11681,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 */ @@ -12808,8 +12867,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(); @@ -12862,8 +12921,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; /* @@ -13536,17 +13595,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); @@ -13830,8 +13883,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 */ @@ -13936,8 +13989,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 */ @@ -14085,8 +14138,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; /* @@ -14360,8 +14413,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(); @@ -14465,8 +14518,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(); @@ -14827,8 +14880,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(); @@ -15072,8 +15125,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(); @@ -15224,8 +15277,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(); @@ -15373,8 +15426,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(); @@ -15782,7 +15835,7 @@ dumpExtProtocol(Archive *fout, const ExtProtInfo *ptcinfo) } ProtoFunc; ProtoFunc protoFuncs[FCOUNT]; - + DumpOptions *dopt = fout->dopt; PQExpBuffer q; PQExpBuffer delq; PQExpBuffer nsq; @@ -15791,8 +15844,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 */ @@ -15955,8 +16008,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(); @@ -16099,8 +16152,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(); @@ -16165,8 +16218,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(); @@ -16277,8 +16330,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(); @@ -16352,8 +16405,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(); @@ -16545,8 +16598,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(); @@ -16901,20 +16954,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; @@ -16923,8 +16968,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; @@ -16980,13 +17025,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; @@ -16997,7 +17042,7 @@ collectSecLabels(Archive *fout, SecLabelItem **items) int i_objsubid; int ntups; int i; - SecLabelItem *labels; + DumpableObject *dobj; query = createPQExpBuffer(); @@ -17017,22 +17062,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; } /* @@ -17046,17 +17123,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)); @@ -17076,7 +17153,8 @@ dumpTable(Archive *fout, const TableInfo *tbinfo) /* * 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) { @@ -17609,8 +17687,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) @@ -18739,6 +18816,7 @@ dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo) DumpOptions *dopt = fout->dopt; PQExpBuffer q; + /* Do nothing in data-only dump */ if (dopt->dataOnly) return; @@ -18789,8 +18867,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 */ @@ -18886,6 +18964,7 @@ dumpIndex(Archive *fout, const IndxInfo *indxinfo) char *qindxname; char *qqindxname; + /* Do nothing in data-only dump */ if (dopt->dataOnly) return; @@ -19019,6 +19098,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; @@ -19065,8 +19145,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(); @@ -19142,8 +19222,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(); @@ -19794,10 +19874,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; @@ -20033,8 +20110,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(); @@ -20124,8 +20201,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; /* diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 81fa36289b3..8c84fee05fb 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 */ @@ -314,6 +320,7 @@ typedef struct _tableInfo uint32 toast_frozenxid; /* toast table's relfrozenxid, if any */ uint32 toast_minmxid; /* toast table's relminmxid */ int ncheck; /* # of CHECK expressions */ + Oid reltype; /* OID of table's composite type, if any */ char *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: */ From 9e954152e3d659cc9335f310e316f045393c22de Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 6 Dec 2021 12:39:45 -0500 Subject: [PATCH 06/32] Rethink pg_dump's handling of object ACLs. Backported from PG15 0c9d84427f Conflicts resolved: * Rejected changes to special handling of public schema from upstream a7a7be1f2 and b073c3ccd which have not been backported. These changes cause pg_dump to believe that schema public has a specific initprivs value even when it doesn't. In GPDB pg_upgrade we drop and recreate the public schema and with the above changes the public schema ACL would be different in the destination cluster. Ref: https://www.postgresql.org/message-id/20201031163518.GB4039133@rfd.leadboat.com * Refactored getExtProtocols to use new ACL logic * Removed code handling pre 8.3 servers Original commit message follows: Throw away most of the existing logic for this, as it was very inefficient thanks to expensive sub-selects executed to collect ACL data that we very possibly would have no interest in dumping. Reduce the ACL handling in the initial per-object-type queries to be just collection of the catalog ACL fields, as it was originally. Fetch pg_init_privs data separately in a single scan of that catalog, and do the merging calculations on the client side. Remove the separate code path used for pre-9.6 source servers; there is no good reason to treat them differently from newer servers that happen to have empty pg_init_privs. Discussion: https://postgr.es/m/2273648.1634764485@sss.pgh.pa.us Discussion: https://postgr.es/m/7d7eb6128f40401d81b3b7a898b6b4de@W2012-02.nidsa.loc --- src/bin/pg_dump/dumputils.c | 507 +++++------ src/bin/pg_dump/dumputils.h | 13 +- src/bin/pg_dump/pg_dump.c | 1285 ++++++++++----------------- src/bin/pg_dump/pg_dump.h | 68 +- src/bin/pg_dump/pg_dumpall.c | 93 +- src/fe_utils/string_utils.c | 63 ++ src/include/fe_utils/string_utils.h | 1 + 7 files changed, 793 insertions(+), 1237 deletions(-) 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_dump.c b/src/bin/pg_dump/pg_dump.c index a44b8bd8192..0b14c04097e 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -219,6 +219,7 @@ 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 void getAdditionalACLs(Archive *fout); static int findComments(Archive *fout, Oid classoid, Oid objoid, CommentItem **items); static void collectComments(Archive *fout); @@ -283,8 +284,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); @@ -1142,8 +1142,10 @@ main(int argc, char **argv) setExtPartDependency(tblinfo, numTables); /* - * Collect comments and security labels, if wanted. + * Collect ACLs, comments, and security labels, if wanted. */ + if (!dopt.aclsSkip) + getAdditionalACLs(fout); if (!dopt.no_comments) collectComments(fout); if (!dopt.no_security_labels) @@ -2034,7 +2036,7 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout) /* * 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 dumpCommentExtended for it.) + * (Without this, we'd not call dumpComment for it.) */ nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT; } @@ -3387,19 +3389,18 @@ dumpDatabase(Archive *fout) 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; @@ -3411,40 +3412,14 @@ 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, " "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 " @@ -3453,13 +3428,14 @@ dumpDatabase(Archive *fout) "WHERE datname = current_database()", username_subquery); } - else if (fout->remoteVersion >= 90300) + else if (fout->remoteVersion >= 90200) { appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, " "(%s datdba) AS dba, " "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 " @@ -3473,7 +3449,8 @@ dumpDatabase(Archive *fout) "(%s datdba) AS dba, " "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 " @@ -3487,7 +3464,8 @@ dumpDatabase(Archive *fout) "(%s datdba) AS dba, " "pg_encoding_to_char(encoding) AS encoding, " "NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, " - "datacl, '' as rdatacl, datistemplate, datconnlimit, " + "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 " @@ -3508,7 +3486,7 @@ dumpDatabase(Archive *fout) 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"); @@ -3522,8 +3500,8 @@ dumpDatabase(Archive *fout) 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); @@ -3647,9 +3625,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 @@ -3988,59 +3969,30 @@ 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, (%s lomowner) AS rolname, lomacl, " + "acldefault('L', lomowner) AS acldefault " + "FROM pg_largeobject_metadata", + username_subquery); } else if (fout->remoteVersion >= 90000) appendPQExpBuffer(blobQry, "SELECT oid, (%s lomowner) AS rolname, lomacl, " - "NULL AS rlomacl, NULL AS initlomacl, " - "NULL AS initrlomacl " - " FROM pg_largeobject_metadata", + "NULL AS acldefault " + "FROM pg_largeobject_metadata", username_subquery); 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); @@ -4048,9 +4000,7 @@ getBlobs(Archive *fout) i_oid = PQfnumber(res, "oid"); i_lomowner = PQfnumber(res, "rolname"); i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + i_acldefault = PQfnumber(res, "acldefault"); ntups = PQntuples(res); @@ -4067,20 +4017,17 @@ getBlobs(Archive *fout) AssignDumpId(&binfo[i].dobj); binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + 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 = 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)); /* Blobs have data */ binfo[i].dobj.components |= DUMP_COMPONENT_DATA; /* Mark whether blob has an ACL */ - if (!(PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl))) + if (!PQgetisnull(res, i, i_lomacl)) binfo[i].dobj.components |= DUMP_COMPONENT_ACL; /* @@ -4156,8 +4103,7 @@ dumpBlob(Archive *fout, const BlobInfo *binfo) 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); @@ -5796,7 +5742,6 @@ binary_upgrade_extension_member(PQExpBuffer upgrade_buffer, NamespaceInfo * getNamespaces(Archive *fout, int *numNamespaces) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -5807,9 +5752,7 @@ getNamespaces(Archive *fout, int *numNamespaces) int i_nspname; int i_rolname; int i_nspacl; - int i_rnspacl; - int i_initnspacl; - int i_initrnspacl; + int i_acldefault; query = createPQExpBuffer(); @@ -5817,48 +5760,18 @@ 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); - } - else - appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, " - "(%s nspowner) AS rolname, " - "nspacl, NULL as rnspacl, " - "NULL AS initnspacl, NULL as initrnspacl " - "FROM pg_namespace", + "n.nspacl, " + "acldefault('n', n.nspowner) AS acldefault " + "FROM pg_namespace n", username_subquery); + else + 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); @@ -5871,9 +5784,7 @@ getNamespaces(Archive *fout, int *numNamespaces) i_nspname = PQfnumber(res, "nspname"); i_rolname = PQfnumber(res, "rolname"); i_nspacl = PQfnumber(res, "nspacl"); - i_rnspacl = PQfnumber(res, "rnspacl"); - i_initnspacl = PQfnumber(res, "initnspacl"); - i_initrnspacl = PQfnumber(res, "initrnspacl"); + i_acldefault = PQfnumber(res, "acldefault"); for (i = 0; i < ntups; i++) { @@ -5882,22 +5793,21 @@ getNamespaces(Archive *fout, int *numNamespaces) 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].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; nsinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); - nsinfo[i].nspacl = pg_strdup(PQgetvalue(res, i, i_nspacl)); - nsinfo[i].rnspacl = pg_strdup(PQgetvalue(res, i, i_rnspacl)); - nsinfo[i].initnspacl = pg_strdup(PQgetvalue(res, i, i_initnspacl)); - nsinfo[i].initrnspacl = pg_strdup(PQgetvalue(res, i, i_initrnspacl)); /* Decide whether to dump this namespace */ selectDumpableNamespace(&nsinfo[i], fout); /* Mark whether namespace has an ACL */ - if (!(PQgetisnull(res, i, i_nspacl) && - PQgetisnull(res, i, i_rnspacl) && - PQgetisnull(res, i, i_initnspacl) && - PQgetisnull(res, i, i_initrnspacl))) + if (!PQgetisnull(res, i, i_nspacl)) nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL; + + if (strlen(nsinfo[i].rolname) == 0) pg_log_warning("owner of schema \"%s\" appears to be invalid", nsinfo[i].dobj.name); @@ -6036,7 +5946,6 @@ getBinaryUpgradeObjects(void) TypeInfo * getTypes(Archive *fout, int *numTypes) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -6048,9 +5957,7 @@ 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_acldefault; int i_rolname; int i_typelem; int i_typrelid; @@ -6074,25 +5981,13 @@ 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, " + "t.typnamespace, t.typacl, " + "acldefault('T', t.typowner) AS acldefault, " "(%s t.typowner) AS rolname, " "t.typelem, t.typrelid, " "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" " @@ -6102,50 +5997,24 @@ 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", + "LEFT JOIN pg_type_encoding e ON t.oid = e.typid ", username_subquery); } 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", + appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, " + "t.typnamespace, t.typacl, " + "NULL AS acldefault, " + "(%s t.typowner) AS rolname, " + "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 ", username_subquery); } @@ -6160,9 +6029,7 @@ 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_acldefault = PQfnumber(res, "acldefault"); i_rolname = PQfnumber(res, "rolname"); i_typelem = PQfnumber(res, "typelem"); i_typrelid = PQfnumber(res, "typrelid"); @@ -6181,12 +6048,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].typelem = atooid(PQgetvalue(res, i, i_typelem)); tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid)); tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind); @@ -6214,10 +6081,7 @@ getTypes(Archive *fout, int *numTypes) selectDumpableType(&tyinfo[i], fout); /* Mark whether type has an ACL */ - if (!(PQgetisnull(res, i, i_typacl) && - PQgetisnull(res, i, i_rtypacl) && - PQgetisnull(res, i, i_inittypacl) && - PQgetisnull(res, i, i_initrtypacl))) + if (!PQgetisnull(res, i, i_typacl)) tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL; /* @@ -6744,9 +6608,7 @@ getAggregates(Archive *fout, int *numAggs) int i_proargtypes; int i_rolname; 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 @@ -6754,16 +6616,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"); @@ -6772,10 +6626,8 @@ getAggregates(Archive *fout, int *numAggs) "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.proacl AS aggacl, " + "acldefault('f', p.proowner) AS acldefault " "FROM pg_proc p " "LEFT JOIN pg_init_privs pip ON " "(p.oid = pip.objoid " @@ -6787,10 +6639,6 @@ getAggregates(Archive *fout, int *numAggs) "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, @@ -6800,11 +6648,29 @@ 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, " + "(%s proowner) AS rolname, " + "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')", + username_subquery); + 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 { @@ -6813,8 +6679,7 @@ getAggregates(Archive *fout, int *numAggs) "pronargs, proargtypes, " "(%s proowner) AS rolname, " "proacl AS aggacl, " - "NULL AS raggacl, " - "NULL AS initaggacl, NULL AS initraggacl " + "NULL AS acldefault " "FROM pg_proc p " "WHERE proisagg AND (" "pronamespace != " @@ -6846,9 +6711,7 @@ getAggregates(Archive *fout, int *numAggs) i_proargtypes = PQfnumber(res, "proargtypes"); i_rolname = PQfnumber(res, "rolname"); i_aggacl = PQfnumber(res, "aggacl"); - i_raggacl = PQfnumber(res, "raggacl"); - i_initaggacl = PQfnumber(res, "initaggacl"); - i_initraggacl = PQfnumber(res, "initraggacl"); + i_acldefault = PQfnumber(res, "acldefault"); for (i = 0; i < ntups; i++) { @@ -6859,16 +6722,16 @@ 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.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 = 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.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; @@ -6884,10 +6747,7 @@ getAggregates(Archive *fout, int *numAggs) selectDumpableObject(&(agginfo[i].aggfn.dobj), fout); /* Mark whether aggregate has an ACL */ - if (!(PQgetisnull(res, i, i_aggacl) && - PQgetisnull(res, i, i_raggacl) && - PQgetisnull(res, i, i_initaggacl) && - PQgetisnull(res, i, i_initraggacl))) + if (!PQgetisnull(res, i, i_aggacl)) agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL; } @@ -6919,72 +6779,40 @@ getExtProtocols(Archive *fout, int *numExtProtocols) int i_ptcname; int i_rolname; 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) + 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, "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, " + "(%s ptcowner) as rolname, " + "ptctrusted, ptcacl, " + "acldefault('E', ptcowner) AS acldefault " + "FROM pg_extprotocol ", + username_subquery); } 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, " + "(%s ptcowner) as rolname, " + "ptctrusted, ptcacl, " + "NULL AS acldefault " + "FROM pg_extprotocol ", + username_subquery); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -6999,9 +6827,7 @@ getExtProtocols(Archive *fout, int *numExtProtocols) i_ptcname = PQfnumber(res, "ptcname"); i_rolname = PQfnumber(res, "rolname"); 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"); @@ -7035,14 +6861,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); @@ -7078,9 +6910,7 @@ getFuncs(Archive *fout, int *numFuncs) 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: @@ -7102,30 +6932,20 @@ 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 " "FROM pg_proc p " @@ -7148,10 +6968,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, @@ -7166,23 +6982,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 " "FROM pg_proc p " "WHERE NOT proisagg", + acldefault_call, username_subquery); if (fout->remoteVersion >= 90200) appendPQExpBufferStr(query, @@ -7242,9 +7058,7 @@ getFuncs(Archive *fout, int *numFuncs) i_proargtypes = PQfnumber(res, "proargtypes"); i_prorettype = PQfnumber(res, "prorettype"); i_proacl = PQfnumber(res, "proacl"); - i_rproacl = PQfnumber(res, "rproacl"); - i_initproacl = PQfnumber(res, "initproacl"); - i_initrproacl = PQfnumber(res, "initrproacl"); + i_acldefault = PQfnumber(res, "acldefault"); for (i = 0; i < ntups; i++) { @@ -7255,13 +7069,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].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 = pg_strdup(PQgetvalue(res, i, i_rolname)); 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; @@ -7277,10 +7091,7 @@ getFuncs(Archive *fout, int *numFuncs) selectDumpableObject(&(finfo[i].dobj), fout); /* Mark whether function has an ACL */ - if (!(PQgetisnull(res, i, i_proacl) && - PQgetisnull(res, i, i_rproacl) && - PQgetisnull(res, i, i_initproacl) && - PQgetisnull(res, i, i_initrproacl))) + if (!PQgetisnull(res, i, i_proacl)) finfo[i].dobj.components |= DUMP_COMPONENT_ACL; if (strlen(finfo[i].rolname) == 0) @@ -7317,10 +7128,6 @@ getTables(Archive *fout, int *numTables) int i_relname; int i_relnamespace; int i_relkind; - int i_relacl; - int i_rrelacl; - int i_initrelacl; - int i_initrrelacl; int i_reltype; int i_rolname; int i_relchecks; @@ -7350,7 +7157,8 @@ getTables(Archive *fout, int *numTables) int i_parlevel; int i_foreignserver; int i_is_identity_sequence; - int i_changed_acl; + int i_relacl; + int i_acldefault; int i_partkeydef; int i_ispartition; int i_partbound; @@ -7501,7 +7309,7 @@ getTables(Archive *fout, int *numTables) "p.parrelid as parrelid, " "pl.parlevel as parlevel, "); - if (fout->remoteVersion >= 90100) + if (fout->remoteVersion >= 90100) appendPQExpBufferStr(query, "CASE WHEN c.relkind = 'f' THEN " "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) " @@ -7512,70 +7320,20 @@ getTables(Archive *fout, int *numTables) if (fout->remoteVersion >= 90600) appendPQExpBuffer(query, - "c.relisivm AS isivm, %s, ", - (fout->version.type == Cloudberry && fout->version.version >= 2) ? "c.relisdynamic AS isdynamic " : "false AS isdynamic "); - - - if (fout->remoteVersion >= 90600) - { - PQExpBuffer acl_subquery = createPQExpBuffer(); - PQExpBuffer racl_subquery = createPQExpBuffer(); - PQExpBuffer initacl_subquery = createPQExpBuffer(); - PQExpBuffer initracl_subquery = createPQExpBuffer(); - PQExpBuffer attacl_subquery = createPQExpBuffer(); - PQExpBuffer attracl_subquery = createPQExpBuffer(); - PQExpBuffer attinitacl_subquery = createPQExpBuffer(); - PQExpBuffer attinitracl_subquery = createPQExpBuffer(); - - 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); - - buildACLQueries(attacl_subquery, attracl_subquery, attinitacl_subquery, - attinitracl_subquery, "at.attacl", "c.relowner","'c'", - dopt->binary_upgrade); - - appendPQExpBuffer(query, - "%s AS relacl, %s as rrelacl, " - "%s AS initrelacl, %s as initrrelacl, ", - acl_subquery->data, - racl_subquery->data, - initacl_subquery->data, - initracl_subquery->data); + "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, "); - appendPQExpBuffer(query, - "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, ", - attacl_subquery->data, - attracl_subquery->data, - attinitacl_subquery->data, - attinitracl_subquery->data); - - destroyPQExpBuffer(acl_subquery); - destroyPQExpBuffer(racl_subquery); - destroyPQExpBuffer(initacl_subquery); - destroyPQExpBuffer(initracl_subquery); - destroyPQExpBuffer(attacl_subquery); - destroyPQExpBuffer(attracl_subquery); - destroyPQExpBuffer(attinitacl_subquery); - destroyPQExpBuffer(attinitracl_subquery); - } + 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 rrelacl, " - "NULL AS initrelacl, NULL AS initrrelacl, " - "false AS changed_acl, "); + "c.relacl, NULL AS acldefault, "); if (fout->remoteVersion >= 100000) appendPQExpBufferStr(query, @@ -7594,25 +7352,21 @@ getTables(Archive *fout, int *numTables) * 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 = c.tableoid AND d.objid = c.oid AND " - "d.objsubid = 0 AND " - "d.refclassid = c.tableoid AND d.deptype IN ('a', 'i'))\n" - "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n"); + "\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_init_privs to detect if any privileges - * are still as-set-at-init, in which case we won't dump out ACL commands - * for those. We also are interested in the amname as of 9.6. + * 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" - "LEFT JOIN pg_init_privs pip ON " - "(c.oid = pip.objoid " - "AND pip.classoid = 'pg_class'::regclass " - "AND pip.objsubid = 0)\n"); + "LEFT JOIN pg_am am ON (c.relam = am.oid)\n"); + /* * * We purposefully ignore toast OIDs for partitioned tables; the reason is @@ -7720,10 +7474,7 @@ getTables(Archive *fout, int *numTables) i_amname = PQfnumber(res, "amname"); i_is_identity_sequence = PQfnumber(res, "is_identity_sequence"); i_relacl = PQfnumber(res, "relacl"); - i_rrelacl = PQfnumber(res, "rrelacl"); - i_initrelacl = PQfnumber(res, "initrelacl"); - i_initrrelacl = PQfnumber(res, "initrrelacl"); - i_changed_acl = PQfnumber(res, "changed_acl"); + i_acldefault = PQfnumber(res, "acldefault"); i_partkeydef = PQfnumber(res, "partkeydef"); i_ispartition = PQfnumber(res, "ispartition"); i_partbound = PQfnumber(res, "partbound"); @@ -7760,6 +7511,10 @@ 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].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].reltype = atooid(PQgetvalue(res, i, i_reltype)); tblinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); @@ -7805,10 +7560,6 @@ getTables(Archive *fout, int *numTables) 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].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].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)); @@ -7879,23 +7630,10 @@ getTables(Archive *fout, int *numTables) /* Tables have data */ tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA; - /* - * Mark whether table has an ACL. - * - * 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. - * - * This can result in a significant performance improvement in cases - * where we are only looking to dump out the ACL (eg: pg_catalog). - */ - 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)) + /* 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 @@ -9235,7 +8973,6 @@ getEventTriggers(Archive *fout, int *numEventTriggers) ProcLangInfo * getProcLangs(Archive *fout, int *numProcLangs) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -9249,9 +8986,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; /* @@ -9259,82 +8994,43 @@ 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, " + "laninline, lanvalidator, " + "lanacl, " + "acldefault('l', lanowner) AS acldefault, " "(%s lanowner) AS lanowner " "FROM pg_language " "WHERE lanispl " "ORDER BY oid", username_subquery); } - 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, " + "laninline, lanvalidator, " + "lanacl, NULL AS acldefault, " "(%s lanowner) AS lanowner " "FROM pg_language " "WHERE lanispl " "ORDER BY oid", username_subquery); } - 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, " + "(%s lanowner) AS lanowner " "FROM pg_language " "WHERE lanispl " "ORDER BY oid", @@ -9357,9 +9053,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++) @@ -9370,24 +9064,21 @@ 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)); /* Decide whether we want to dump it */ selectDumpableProcLang(&(planginfo[i]), fout); /* Mark whether language has an ACL */ - if (!(PQgetisnull(res, i, i_lanacl) && - PQgetisnull(res, i, i_rlanacl) && - PQgetisnull(res, i, i_initlanacl) && - PQgetisnull(res, i, i_initrlanacl))) + if (!PQgetisnull(res, i, i_lanacl)) planginfo[i].dobj.components |= DUMP_COMPONENT_ACL; } @@ -10454,7 +10145,6 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) FdwInfo * getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -10467,9 +10157,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) int i_fdwhandler; int i_fdwvalidator; int i_fdwacl; - int i_rfdwacl; - int i_initfdwacl; - int i_initrfdwacl; + int i_acldefault; int i_fdwoptions; /* Before 8.4, there are no foreign-data wrappers */ @@ -10481,46 +10169,22 @@ 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, " + "(%s fdwowner) AS rolname, " + "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", + username_subquery); } else if (fout->remoteVersion >= 90100) { @@ -10528,8 +10192,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) "(%s fdwowner) AS rolname, " "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) " @@ -10545,8 +10208,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) "(%s fdwowner) AS rolname, " "'-' AS fdwhandler, " "fdwvalidator::pg_catalog.regproc, fdwacl, " - "NULL as rfdwacl, " - "NULL as initfdwacl, NULL AS initrfdwacl, " + "NULL AS acldefault, " "array_to_string(ARRAY(" "SELECT quote_ident(option_name) || ' ' || " "quote_literal(option_value) " @@ -10571,9 +10233,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) i_fdwhandler = PQfnumber(res, "fdwhandler"); i_fdwvalidator = PQfnumber(res, "fdwvalidator"); i_fdwacl = PQfnumber(res, "fdwacl"); - i_rfdwacl = PQfnumber(res, "rfdwacl"); - i_initfdwacl = PQfnumber(res, "initfdwacl"); - i_initrfdwacl = PQfnumber(res, "initrfdwacl"); + i_acldefault = PQfnumber(res, "acldefault"); i_fdwoptions = PQfnumber(res, "fdwoptions"); for (i = 0; i < ntups; i++) @@ -10584,14 +10244,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].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 = pg_strdup(PQgetvalue(res, i, i_rolname)); 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); @@ -10601,10 +10261,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) fdwinfo[i].dobj.dump = DUMP_COMPONENT_NONE; /* Mark whether FDW has an ACL */ - if (!(PQgetisnull(res, i, i_fdwacl) && - PQgetisnull(res, i, i_rfdwacl) && - PQgetisnull(res, i, i_initfdwacl) && - PQgetisnull(res, i, i_initrfdwacl))) + if (!PQgetisnull(res, i, i_fdwacl)) fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL; } @@ -10625,7 +10282,6 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) ForeignServerInfo * getForeignServers(Archive *fout, int *numForeignServers) { - DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -10639,9 +10295,7 @@ getForeignServers(Archive *fout, int *numForeignServers) int i_srvtype; int i_srvversion; int i_srvacl; - int i_rsrvacl; - int i_initsrvacl; - int i_initrsrvacl; + int i_acldefault; int i_srvoptions; /* Before 8.4, there are no foreign servers */ @@ -10653,53 +10307,27 @@ 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, " + "(%s srvowner) AS rolname, " + "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", + username_subquery); } else { appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, " "(%s srvowner) AS rolname, " "srvfdw, srvtype, srvversion, srvacl, " - "NULL AS rsrvacl, " - "NULL AS initsrvacl, NULL AS initrsrvacl, " + "NULL AS acldefault, " "array_to_string(ARRAY(" "SELECT quote_ident(option_name) || ' ' || " "quote_literal(option_value) " @@ -10725,9 +10353,7 @@ getForeignServers(Archive *fout, int *numForeignServers) i_srvtype = PQfnumber(res, "srvtype"); i_srvversion = PQfnumber(res, "srvversion"); i_srvacl = PQfnumber(res, "srvacl"); - i_rsrvacl = PQfnumber(res, "rsrvacl"); - i_initsrvacl = PQfnumber(res, "initsrvacl"); - i_initrsrvacl = PQfnumber(res, "initrsrvacl"); + i_acldefault = PQfnumber(res, "acldefault"); i_srvoptions = PQfnumber(res, "srvoptions"); for (i = 0; i < ntups; i++) @@ -10738,15 +10364,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].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 = pg_strdup(PQgetvalue(res, i, i_rolname)); 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); @@ -10759,10 +10385,7 @@ getForeignServers(Archive *fout, int *numForeignServers) srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP; /* Mark whether server has an ACL */ - if (!(PQgetisnull(res, i, i_srvacl) && - PQgetisnull(res, i, i_rsrvacl) && - PQgetisnull(res, i, i_initsrvacl) && - PQgetisnull(res, i, i_initrsrvacl))) + if (!PQgetisnull(res, i, i_srvacl)) srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL; } @@ -10793,9 +10416,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; @@ -10807,13 +10428,16 @@ 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, " + "(%s defaclrole) AS defaclrole, " + "defaclnamespace, " + "defaclobjtype, " + "defaclacl, ", + username_subquery); + 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 @@ -10821,58 +10445,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); @@ -10887,9 +10477,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++) { @@ -10907,12 +10495,12 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs) else daclinfo[i].dobj.namespace = NULL; + 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 = pg_strdup(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; @@ -10928,6 +10516,118 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs) return daclinfo; } +/* + * 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 -- * @@ -11535,8 +11235,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); @@ -11833,8 +11532,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); @@ -11975,8 +11673,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); @@ -12049,8 +11746,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); @@ -12311,8 +12007,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); @@ -12499,8 +12194,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++) @@ -12725,8 +12419,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); @@ -13026,8 +12719,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); @@ -13843,8 +13535,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); @@ -15755,9 +15446,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) @@ -15981,9 +15670,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->ptcowner, &ptcinfo->dacl); free(namecopy); destroyPQExpBuffer(q); @@ -16380,9 +16067,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); @@ -16469,9 +16154,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) @@ -16635,15 +16318,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, @@ -16673,20 +16354,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. @@ -16696,11 +16364,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 */ @@ -16714,29 +16386,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) { @@ -17146,8 +16841,7 @@ 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); } /* @@ -17156,7 +16850,7 @@ dumpTable(Archive *fout, const TableInfo *tbinfo) * 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; @@ -17164,55 +16858,37 @@ dumpTable(Archive *fout, const TableInfo *tbinfo) if (fout->remoteVersion >= 90600) { - PQExpBuffer acl_subquery = createPQExpBuffer(); - PQExpBuffer racl_subquery = createPQExpBuffer(); - PQExpBuffer initacl_subquery = createPQExpBuffer(); - PQExpBuffer initracl_subquery = createPQExpBuffer(); - - buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, - initracl_subquery, "at.attacl", "c.relowner", "'c'", - dopt->binary_upgrade); - + /* + * 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. + */ appendPQExpBuffer(query, "SELECT at.attname, " - "%s AS attacl, " - "%s AS rattacl, " - "%s AS initattacl, " - "%s AS initrattacl " + "at.attacl, " + "'{}' AS acldefault, " + "pip.privtype, pip.initprivs " "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)" + "AND (at.attacl IS NOT NULL OR pip.initprivs 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); + tbinfo->dobj.catId.oid); } else { appendPQExpBuffer(query, - "SELECT attname, attacl, NULL as rattacl, " - "NULL AS initattacl, NULL AS initrattacl " + "SELECT attname, attacl, '{}' AS acldefault, " + "NULL AS privtype, NULL AS initprivs " "FROM pg_catalog.pg_attribute " "WHERE attrelid = '%u'::pg_catalog.oid AND NOT attisdropped " "AND attacl IS NOT NULL " @@ -17226,11 +16902,16 @@ dumpTable(Archive *fout, const TableInfo *tbinfo) { 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)); /* @@ -17241,7 +16922,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); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 8c84fee05fb..1badb50b482 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -154,14 +154,33 @@ 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; + DumpableAcl dacl; char *rolname; /* name of owner, or empty string */ - char *nspacl; - char *rnspacl; - char *initnspacl; - char *initrnspacl; } NamespaceInfo; typedef struct _extensionInfo @@ -177,6 +196,7 @@ typedef struct _extensionInfo typedef struct _typeInfo { DumpableObject dobj; + DumpableAcl dacl; /* * Note: dobj.name is the raw pg_type.typname entry. ftypname is the @@ -215,15 +235,12 @@ typedef struct _shellTypeInfo typedef struct _funcInfo { DumpableObject dobj; + DumpableAcl dacl; char *rolname; /* name of owner, or empty string */ Oid lang; int nargs; Oid *argtypes; Oid prorettype; - char *proacl; - char *rproacl; - char *initproacl; - char *initrproacl; } FuncInfo; /* AggInfo is a superset of FuncInfo */ @@ -236,13 +253,11 @@ typedef struct _aggInfo typedef struct _ptcInfo { DumpableObject dobj; + DumpableAcl dacl; char *ptcreadfn; char *ptcwritefn; char *ptcowner; char *ptcacl; - char *rproacl; - char *initproacl; - char *initrproacl; bool ptctrusted; Oid ptcreadid; Oid ptcwriteid; @@ -294,11 +309,8 @@ typedef struct _tableInfo * These fields are collected for every table in the database. */ DumpableObject dobj; + DumpableAcl dacl; char *rolname; /* name of owner, or empty string */ - char *relacl; - char *rrelacl; - char *initrelacl; - char *initrrelacl; char relkind; char relstorage; char relpersistence; /* relation persistence */ @@ -311,6 +323,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? */ @@ -510,14 +523,11 @@ 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 */ } ProcLangInfo; @@ -582,49 +592,37 @@ typedef struct _cfgInfo typedef struct _fdwInfo { DumpableObject dobj; + DumpableAcl dacl; char *rolname; char *fdwhandler; char *fdwvalidator; char *fdwoptions; - char *fdwacl; - char *rfdwacl; - char *initfdwacl; - char *initrfdwacl; } FdwInfo; typedef struct _foreignServerInfo { DumpableObject dobj; + DumpableAcl dacl; char *rolname; Oid srvfdw; char *srvtype; char *srvversion; - char *srvacl; - char *rsrvacl; - char *initsrvacl; - char *initrsrvacl; char *srvoptions; } ForeignServerInfo; typedef struct _defaultACLInfo { DumpableObject dobj; + DumpableAcl dacl; char *defaclrole; char defaclobjtype; - char *defaclacl; - char *rdefaclacl; - char *initdefaclacl; - char *initrdefaclacl; } DefaultACLInfo; typedef struct _blobInfo { DumpableObject dobj; + DumpableAcl dacl; char *rolname; - char *blobacl; - char *rblobacl; - char *initblobacl; - char *initrblobacl; } BlobInfo; /* diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 1684a2b87ca..b1ff242a5dd 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -1673,89 +1673,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) + if (server_version >= 90200) 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) - 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 +1687,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 +1696,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 +1713,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 +1747,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\"", 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); From 8b3c336841bee6d2f78314ff94b84119030e5153 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 6 Dec 2021 13:07:31 -0500 Subject: [PATCH 07/32] Avoid per-object queries in performance-critical paths in pg_dump. Backported from PG15 9895961529ef8ff3fc12b39229f9a93e08bca7b7 Conflicts resolved: * Handle GPDB specific attencoding field in getTableAttrs. * Rejected changes to getTriggers introduced by f0e21f2f6 which has not been backported because triggers are not supported in GPDB. * Rejected attcompression field in getTableAttrs which was introduced in PG14 and has not been backported because it is not supported in GPDB. Original commit message follows: Instead of issuing a secondary data-collection query against each table to be dumped, issue just one query, with a WHERE clause restricting it to be applied to only the tables we intend to dump. Likewise for indexes, constraints, and triggers. This greatly reduces the number of queries needed to dump a database containing many tables. It might seem that WHERE clauses listing many target OIDs could be inefficient, but at least on recent server versions this provides a very substantial speedup. (In principle the same thing could be done with other object types such as functions; but that would require significant refactoring of pg_dump, so those will be tackled in a different way in a following patch.) The new WHERE clauses depend on the unnest() function, which is only present in 8.4 and above. We could implement them differently for older servers, but there is an ongoing discussion that will probably result in dropping pg_dump support for servers before 9.2, so that seems like it'd be wasted work. For now, just bump the server version check to require >= 8.4, without stopping to remove any of the code that's thereby rendered dead. We'll mop that situation up soon. Patch by me, based on an idea from Andres Freund. Discussion: https://postgr.es/m/7d7eb6128f40401d81b3b7a898b6b4de@W2012-02.nidsa.loc --- src/bin/pg_dump/pg_dump.c | 1690 +++++++++++++++++++++---------------- 1 file changed, 954 insertions(+), 736 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 0b14c04097e..211209f7b45 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -6003,7 +6003,7 @@ getTypes(Archive *fout, int *numTypes) else { appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, " - "t.typnamespace, t.typacl, " + "t.typnamespace, NULL AS typacl, " "NULL AS acldefault, " "(%s t.typowner) AS rolname, " "t.typelem, t.typrelid, " @@ -7381,7 +7381,6 @@ getTables(Archive *fout, int *numTables) if (fout->remoteVersion <= 90426) // GPDB 6 or below appendPQExpBufferStr(query, - "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid)\n" "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"); @@ -7888,13 +7887,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, @@ -7914,9 +7915,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]; @@ -7929,199 +7938,237 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) 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, '}'); + /* + * 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, + "SELECT t.tableoid, t.oid, i.indrelid, " + "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 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) " + "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", + tbloids->data); + } + 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, + "SELECT t.tableoid, t.oid, i.indrelid, " + "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 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) " + "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", + tbloids->data); + } + 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, i.indrelid, " + "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 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) " + "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", + tbloids->data); + } + else + { + appendPQExpBuffer(query, + "SELECT t.tableoid, t.oid, i.indrelid, " + "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 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) " + "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", + tbloids->data); + } - 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; @@ -8190,11 +8237,12 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) indxinfo[j].indexconstraint = 0; } } - - PQclear(res); } + PQclear(res); + destroyPQExpBuffer(query); + destroyPQExpBuffer(tbloids); } /* @@ -8281,22 +8329,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]; @@ -8309,95 +8366,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); - constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo)); + 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"); - 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; + constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo)); - /* - * 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) + curtblindx = -1; + for (int j = 0; j < ntups; j++) + { + Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid)); + TableInfo *reftable; + + /* + * 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); } /* @@ -8641,13 +8721,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, @@ -8662,9 +8744,17 @@ 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]; @@ -8672,143 +8762,154 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables) !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) continue; - pg_log_info("reading triggers 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 >= 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); - } + 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.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); + } - 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_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; + 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)); - for (j = 0; j < ntups; j++) + /* + * 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; + + /* Count rows for this table */ + for (numtrigs = 1; numtrigs < ntups - j; numtrigs++) + if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid) + break; + + /* + * 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); + + /* 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)); @@ -8871,11 +8972,12 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables) } } } - - PQclear(res); } + PQclear(res); + destroyPQExpBuffer(query); + destroyPQExpBuffer(tbloids); } /* @@ -9310,12 +9412,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 @@ -9323,6 +9419,12 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) { DumpOptions *dopt = fout->dopt; PQExpBuffer q = createPQExpBuffer(); + PQExpBuffer tbloids = createPQExpBuffer(); + PQExpBuffer checkoids = createPQExpBuffer(); + PGresult *res; + int ntups; + int curtblindx; + int i_attrelid; int i_attnum; int i_attname; int i_atttypname; @@ -9343,16 +9445,23 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) int i_attfdwoptions; int i_attmissingval; int i_atthasdef; - - /* GPDB_14_MERGE_FIXME: GPDB specific column, need to keep this for easy to use*/ 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) @@ -9362,191 +9471,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; - 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"); - - for (int j = 0; j < ntups; j++) + for (int j = 0; j < numatts; j++, r++) { - if (j + 1 != atoi(PQgetvalue(res, j, i_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, i_attname)); - tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, j, i_atttypname)); - tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod)); - tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget)); - tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage)); - tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage)); - tbinfo->attidentity[j] = *(PQgetvalue(res, j, i_attidentity)); - tbinfo->attgenerated[j] = *(PQgetvalue(res, j, i_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, i_attisdropped)[0] == 't'); - tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen)); - tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign)); - tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't'); - tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't'); - tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions)); - tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation)); - tbinfo->attcompression[j] = *(PQgetvalue(res, j, i_attcompression)); - tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions)); - tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, j, i_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, i_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)); @@ -9554,211 +9708,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); + + attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name); + attrdefs[j].dobj.namespace = tbinfo->dobj.namespace; - pg_log_info("finding check constraints for table \"%s.%s\"", - tbinfo->dobj.namespace->dobj.name, - tbinfo->dobj.name); + attrdefs[j].dobj.dump = tbinfo->dobj.dump; - resetPQExpBuffer(q); - if (fout->remoteVersion >= 90200) + /* + * 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]) { /* - * 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). + * 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. */ - 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 = false; } - else if (fout->remoteVersion >= 80400) + else if (tbinfo->relkind == RELKIND_VIEW) { - /* 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); + /* + * 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 { - 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; } - res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK); + 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); + } + + 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; - numConstrs = PQntuples(res); - if (numConstrs != tbinfo->ncheck) + /* + * 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); + + 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 @@ -9788,11 +10003,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); } /* From 7796f9e14c694eb45703b18bc90f545c91302b8a Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 6 Dec 2021 13:14:29 -0500 Subject: [PATCH 08/32] Use PREPARE/EXECUTE for repetitive per-object queries in pg_dump. Backported from PG15 be85727a3d Conflicts resolved: * Reject changes to dumpTableAttach, which added a query for partbound details in e3fcbbd623b9ccc16cdbda374654d91a4727d173 and wasn't backported as it was considered too invasive. * Handle GPDB specific callbackfunc and prodataaccess fields in dumpFunc Original commit message follows: For objects such as functions, pg_dump issues the same secondary data-collection query against each object to be dumped. This can't readily be refactored to avoid the repetitive queries, but we can PREPARE these queries to reduce planning costs. This patch applies the idea to functions, aggregates, operators, and data types. While it could be carried further, the remaining sorts of objects aren't likely to appear in typical databases enough times to be worth worrying over. Moreover, doing the PREPARE is likely to be a net loss if there aren't at least some dozens of objects to apply the prepared query to. Discussion: https://postgr.es/m/7d7eb6128f40401d81b3b7a898b6b4de@W2012-02.nidsa.loc --- src/bin/pg_dump/pg_backup.h | 20 + src/bin/pg_dump/pg_dump.c | 849 ++++++++++++++++++++---------------- 2 files changed, 486 insertions(+), 383 deletions(-) diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index 5f414e18686..07d207f9593 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -58,6 +58,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 { @@ -234,6 +251,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_dump.c b/src/bin/pg_dump/pg_dump.c index 211209f7b45..be9290b49bf 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1494,6 +1494,12 @@ setup_connection(Archive *AH, const char *dumpencoding, */ 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. */ @@ -8519,7 +8525,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo) { int i; ConstraintInfo *constrinfo; - PQExpBuffer query; + PQExpBuffer query = createPQExpBuffer(); PGresult *res; int i_tableoid, i_oid, @@ -8527,25 +8533,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) + 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"); - 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); + ExecuteSqlStatement(fout, query->data); - 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); + fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true; + } + + printfPQExpBuffer(query, + "EXECUTE getDomainConstraints('%u')", + tyinfo->dobj.catId.oid); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -11645,18 +11661,31 @@ dumpEnumType(Archive *fout, const TypeInfo *tyinfo) int i_enumlabel; int i_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); + 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"); + + 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); @@ -11777,29 +11806,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); @@ -12011,55 +12054,68 @@ 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"); - if (fout->remoteVersion >= 80400) - appendPQExpBufferStr(query, - "typcategory, typispreferred, "); - else - appendPQExpBufferStr(query, - "'U' AS typcategory, false AS typispreferred, "); + 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 >= 90100) - appendPQExpBufferStr(query, "(typcollation <> 0) AS typcollatable, "); - else - appendPQExpBufferStr(query, "false AS typcollatable, "); + if (fout->remoteVersion >= 80300) + appendPQExpBufferStr(query, + "typmodin, typmodout, " + "typmodin::pg_catalog.oid AS typmodinoid, " + "typmodout::pg_catalog.oid AS typmodoutoid, "); + else + appendPQExpBufferStr(query, + "'-' AS typmodin, '-' AS typmodout, " + "0 AS typmodinoid, 0 AS typmodoutoid, "); - if (fout->remoteVersion >= 140000) - appendPQExpBufferStr(query, - "typsubscript, " - "typsubscript::pg_catalog.oid AS typsubscriptoid, "); - else - appendPQExpBufferStr(query, - "'-' AS typsubscript, 0 AS typsubscriptoid, "); + if (fout->remoteVersion >= 80400) + appendPQExpBufferStr(query, + "typcategory, typispreferred, "); + else + appendPQExpBufferStr(query, + "'U' AS typcategory, false AS typispreferred, "); - /* 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, "); + + if (fout->remoteVersion >= 90100) + appendPQExpBufferStr(query, "(typcollation <> 0) AS typcollatable, "); + else + appendPQExpBufferStr(query, "false AS typcollatable, "); - appendPQExpBuffer(query, "FROM pg_catalog.pg_type " - "WHERE oid = '%u'::pg_catalog.oid", + /* 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 "); + + appendPQExpBuffer(query, "FROM pg_catalog.pg_type " + "WHERE oid = $1"); + + ExecuteSqlStatement(fout, query->data); + + fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true; + } + + printfPQExpBuffer(query, + "EXECUTE dumpBaseType('%u')", tyinfo->dobj.catId.oid); res = ExecuteSqlQueryForSingleRow(fout, query->data); @@ -12285,32 +12341,44 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo) Oid typcollation; bool typdefault_is_literal = false; - /* Fetch domain specific details */ - if (fout->remoteVersion >= 90100) + if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN]) { - /* 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 - { - 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")); @@ -12465,45 +12533,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); @@ -13214,118 +13294,94 @@ dumpFunc(Archive *fout, const FuncInfo *finfo) q = createPQExpBuffer(); delqry = createPQExpBuffer(); asPart = createPQExpBuffer(); + + if (!fout->is_prepared[PREPQUERY_DUMPFUNC]) + { + /* GPDB_95_MERGE_FIXME: use isGE70 instead? */ + /* Set up query for function-specific details */ + appendPQExpBufferStr(query, "PREPARE dumpFunc(pg_catalog.oid) AS\n"); - /* Fetch function-specific details */ - appendPQExpBufferStr(query, - "SELECT\n" - "proretset,\n" - "prosrc,\n" - "probin,\n" - "provolatile,\n" - "proisstrict,\n" - "prosecdef,\n" - "lanname,\n"); + appendPQExpBuffer(query, + "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 >= 80300) - { - appendPQExpBufferStr(query, - "proconfig,\n" - "procost,\n" - "prorows,\n"); + if (fout->remoteVersion >= 90200) + appendPQExpBuffer(query, + "proleakproof,\n"); + else + appendPQExpBuffer(query, + "false AS proleakproof,\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 - { - appendPQExpBufferStr(query, - "null AS proconfig,\n" - "0 AS procost,\n" - "0 AS prorows,\n"); + /* GPDB6 added proexeclocation */ + if (fout->remoteVersion >= 90400) + appendPQExpBuffer(query, + "proexeclocation,\n"); + else + appendPQExpBuffer(query, + "'a' as proexeclocation,\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,"); - } + if (fout->remoteVersion >= 90500) + appendPQExpBuffer(query, + "array_to_string(protrftypes, ' ') AS protrftypes,\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 >= 90600) + appendPQExpBuffer(query, + "proparallel,\n"); + else + appendPQExpBuffer(query, + "'u' AS proparallel,\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"); + 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 >= 90500) - appendPQExpBufferStr(query, - "array_to_string(protrftypes, ' ') AS protrftypes,\n"); + if (fout->remoteVersion >= 120000) + appendPQExpBuffer(query, + "prosupport,\n"); + else + appendPQExpBuffer(query, + "'-' AS prosupport,\n"); - if (fout->remoteVersion >= 90600) - appendPQExpBufferStr(query, - "proparallel,\n"); - else - appendPQExpBufferStr(query, - "'u' AS proparallel,\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 >= 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"); + appendPQExpBuffer(query, + "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n" + "WHERE p.oid = $1 " + "AND l.oid = p.prolang"); - if (fout->remoteVersion >= 120000) - appendPQExpBufferStr(query, - "prosupport,\n"); - else - appendPQExpBufferStr(query, - "'-' AS prosupport,\n"); + ExecuteSqlStatement(fout, query->data); - if (fout->remoteVersion >= 140000) - appendPQExpBufferStr(query, - "pg_get_function_sqlbody(p.oid) AS prosqlbody\n"); - else - appendPQExpBufferStr(query, - "NULL AS prosqlbody\n"); + fout->is_prepared[PREPQUERY_DUMPFUNC] = true; - 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", + } + + printfPQExpBuffer(query, + "EXECUTE dumpFunc('%u')", finfo->dobj.catId.oid); res = ExecuteSqlQueryForSingleRow(fout, query->data); @@ -13777,6 +13833,7 @@ dumpFunc(Archive *fout, const FuncInfo *finfo) } + /* * Dump a user-defined cast */ @@ -14064,19 +14121,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"); - 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); + ExecuteSqlStatement(fout, query->data); + + fout->is_prepared[PREPQUERY_DUMPOPR] = true; + } + + printfPQExpBuffer(query, + "EXECUTE dumpOpr('%u')", + oprinfo->dobj.catId.oid); res = ExecuteSqlQueryForSingleRow(fout, query->data); @@ -15344,97 +15414,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); @@ -17074,46 +17144,59 @@ dumpTable(Archive *fout, const TableInfo *tbinfo) PGresult *res; int i; - 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. - */ - appendPQExpBuffer(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 = '%u'::pg_catalog.oid AND " - "NOT at.attisdropped " - "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) " - "ORDER BY at.attnum", - tbinfo->dobj.catId.oid); - } - else + if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS]) { - appendPQExpBuffer(query, - "SELECT attname, attacl, '{}' AS acldefault, " - "NULL AS privtype, NULL AS initprivs " - "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); + /* Set up query for column ACLs */ + appendPQExpBufferStr(query, + "PREPARE getColumnACLs(pg_catalog.oid) AS\n"); + + 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"); + } + + 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++) From 3f311d37e68d636ac7b79d14e8bfac45cc398178 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Mon, 20 Dec 2021 11:08:05 +0100 Subject: [PATCH 09/32] pg_dump: Refactor getIndexes() Rearrange the version-dependent pieces in the new more modular style. Discussion: https://www.postgresql.org/message-id/flat/67a28a3f-7b79-a5a9-fcc7-947b170e66f0%40enterprisedb.com (cherry-picked from e2c52beecdea152ca680a22ef35c6a7da55aa30f) --- src/bin/pg_dump/pg_dump.c | 175 +++++++++++++------------------------- 1 file changed, 60 insertions(+), 115 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index be9290b49bf..1f61c046578 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -7951,6 +7951,55 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) } 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, @@ -7964,32 +8013,6 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) if (fout->remoteVersion >= 110000) { appendPQExpBuffer(query, - "SELECT t.tableoid, t.oid, i.indrelid, " - "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 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) " "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) " "LEFT JOIN pg_catalog.pg_constraint c " "ON (i.indrelid = c.conrelid AND " @@ -7999,8 +8022,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) "ON (inh.inhrelid = indexrelid) " "WHERE (i.indisvalid OR t2.relkind = 'p') " "AND i.indisready " - "ORDER BY i.indrelid, indexname", - tbloids->data); + "ORDER BY i.indrelid, indexname"); } else if (fout->remoteVersion >= 90400) { @@ -8009,102 +8031,25 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) * earlier/later versions */ appendPQExpBuffer(query, - "SELECT t.tableoid, t.oid, i.indrelid, " - "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 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) " - "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", - tbloids->data); - } - 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, i.indrelid, " - "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 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) " "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", - tbloids->data); + "ORDER BY i.indrelid, indexname"); } else { appendPQExpBuffer(query, - "SELECT t.tableoid, t.oid, i.indrelid, " - "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 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) " - "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", - tbloids->data); + "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); From ad4716fc4a8050e99364ebbd8b69b261791b9675 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 31 Dec 2021 11:39:26 -0500 Subject: [PATCH 10/32] pg_dump: minor performance improvements from eliminating sub-SELECTs. Backported from PG15 d5e8930f50e31d836d84b353b9dadedd5007bb70 Conflicts resolved: * Updated getExtProtocols to use new method of role name lookups. Original commit message follows: Get rid of the "username_subquery" mechanism in favor of doing local lookups of role names from role OIDs. The PG backend isn't terribly smart about scalar SubLinks in SELECT output lists, so this offers a small performance improvement, at least in installations with more than a couple of users. In any case the old method didn't make for particularly readable SQL code. While at it, I removed the various custom warning messages about failing to find an object's owner, in favor of just fatal'ing in the local lookup function. AFAIK there is no reason any longer to treat that as anything but a catalog-corruption case, and certainly no reason to make translators deal with a dozen different messages where one would do. (If it turns out that fatal() is indeed a bad idea, we can back off to issuing pg_log_warning() and returning an empty string, resulting in the same behavior as before, except more consistent.) Also drop an entirely unnecessary sub-SELECT to check on the pg_depend status of a sequence relation: we already have a LEFT JOIN to fetch the row of interest in the FROM clause. Discussion: https://postgr.es/m/2460369.1640903318@sss.pgh.pa.us --- src/bin/pg_dump/pg_dump.c | 489 +++++++++++++++++++------------------- src/bin/pg_dump/pg_dump.h | 43 ++-- 2 files changed, 260 insertions(+), 272 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 1f61c046578..69a05f30660 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 */ @@ -103,9 +109,6 @@ 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. @@ -161,6 +164,10 @@ static DumpId binary_upgrade_dumpid; 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; @@ -219,6 +226,8 @@ 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); @@ -1011,8 +1020,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. */ @@ -1103,6 +1110,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. @@ -3388,7 +3400,7 @@ dumpDatabase(Archive *fout) int i_tableoid, i_oid, i_datname, - i_dba, + i_datdba, i_encoding, i_collate, i_ctype, @@ -3422,7 +3434,7 @@ dumpDatabase(Archive *fout) 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, " "datacl, acldefault('d', datdba) AS acldefault, " @@ -3431,13 +3443,12 @@ dumpDatabase(Archive *fout) "shobj_description(oid, 'pg_database') AS description " "FROM pg_database " - "WHERE datname = current_database()", - username_subquery); + "WHERE datname = current_database()"); } 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, 0 AS datminmxid, " "datacl, acldefault('d', datdba) AS acldefault, " @@ -3446,13 +3457,12 @@ dumpDatabase(Archive *fout) "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, NULL AS acldefault, " @@ -3461,13 +3471,12 @@ dumpDatabase(Archive *fout) "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, NULL AS acldefault, " @@ -3476,8 +3485,7 @@ dumpDatabase(Archive *fout) "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); @@ -3485,7 +3493,7 @@ 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"); @@ -3500,7 +3508,7 @@ 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); @@ -3983,17 +3991,15 @@ getBlobs(Archive *fout) if (fout->remoteVersion >= 90200) { appendPQExpBuffer(blobQry, - "SELECT oid, (%s lomowner) AS rolname, lomacl, " + "SELECT oid, lomowner, lomacl, " "acldefault('L', lomowner) AS acldefault " - "FROM pg_largeobject_metadata", - username_subquery); + "FROM pg_largeobject_metadata"); } else if (fout->remoteVersion >= 90000) appendPQExpBuffer(blobQry, - "SELECT oid, (%s lomowner) AS rolname, lomacl, " + "SELECT oid, lomowner, lomacl, " "NULL AS acldefault " - "FROM pg_largeobject_metadata", - username_subquery); + "FROM pg_largeobject_metadata"); else appendPQExpBufferStr(blobQry, "SELECT DISTINCT loid AS oid, " @@ -4004,7 +4010,7 @@ getBlobs(Archive *fout) 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_acldefault = PQfnumber(res, "acldefault"); @@ -4027,7 +4033,7 @@ getBlobs(Archive *fout) binfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); binfo[i].dacl.privtype = 0; binfo[i].dacl.initprivs = NULL; - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].rolname = getRoleName(PQgetvalue(res, i, i_lomowner)); /* Blobs have data */ binfo[i].dobj.components |= DUMP_COMPONENT_DATA; @@ -4494,7 +4500,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; @@ -4518,24 +4524,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); @@ -4544,7 +4541,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"); @@ -4562,7 +4559,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 = @@ -4576,10 +4573,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); } @@ -4863,7 +4856,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; @@ -4896,26 +4889,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); @@ -4924,7 +4912,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"); @@ -4942,7 +4930,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; @@ -4957,10 +4945,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); } @@ -5756,7 +5740,7 @@ 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_acldefault; @@ -5768,11 +5752,10 @@ getNamespaces(Archive *fout, int *numNamespaces) */ if (fout->remoteVersion >= 90200) appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, " - "(%s nspowner) AS rolname, " + "n.nspowner, " "n.nspacl, " "acldefault('n', n.nspowner) AS acldefault " - "FROM pg_namespace n", - username_subquery); + "FROM pg_namespace n"); else appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, " "n.nspowner, " @@ -5788,12 +5771,14 @@ 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_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)); @@ -5803,7 +5788,9 @@ getNamespaces(Archive *fout, int *numNamespaces) nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); nsinfo[i].dacl.privtype = 0; nsinfo[i].dacl.initprivs = NULL; - nsinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + 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); @@ -5811,12 +5798,6 @@ getNamespaces(Archive *fout, int *numNamespaces) /* Mark whether namespace has an ACL */ if (!PQgetisnull(res, i, i_nspacl)) nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL; - - - - if (strlen(nsinfo[i].rolname) == 0) - pg_log_warning("owner of schema \"%s\" appears to be invalid", - nsinfo[i].dobj.name); } PQclear(res); @@ -5964,7 +5945,7 @@ getTypes(Archive *fout, int *numTypes) int i_typnamespace; int i_typacl; int i_acldefault; - int i_rolname; + int i_typowner; int i_typelem; int i_typrelid; int i_typrelkind; @@ -5994,7 +5975,7 @@ getTypes(Archive *fout, int *numTypes) appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, " "t.typnamespace, t.typacl, " "acldefault('T', t.typowner) AS acldefault, " - "(%s t.typowner) AS rolname, " + "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, " @@ -6003,15 +5984,14 @@ 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 ", - username_subquery); + "LEFT JOIN pg_type_encoding e ON t.oid = e.typid "); } else { appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, " "t.typnamespace, NULL AS typacl, " "NULL AS acldefault, " - "(%s t.typowner) AS rolname, " + "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, " @@ -6020,8 +6000,7 @@ 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 ", - username_subquery); + "LEFT JOIN pg_type_encoding e ON t.oid = e.typid "); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -6036,7 +6015,7 @@ getTypes(Archive *fout, int *numTypes) i_typnamespace = PQfnumber(res, "typnamespace"); i_typacl = PQfnumber(res, "typacl"); i_acldefault = PQfnumber(res, "acldefault"); - i_rolname = PQfnumber(res, "rolname"); + i_typowner = PQfnumber(res, "typowner"); i_typelem = PQfnumber(res, "typelem"); i_typrelid = PQfnumber(res, "typrelid"); i_typrelkind = PQfnumber(res, "typrelkind"); @@ -6059,7 +6038,7 @@ getTypes(Archive *fout, int *numTypes) 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].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); @@ -6129,10 +6108,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; @@ -6163,7 +6138,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; @@ -6174,11 +6149,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); @@ -6191,7 +6165,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"); @@ -6204,16 +6178,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); - - if (strlen(oprinfo[i].rolname) == 0) - pg_log_warning("owner of operator \"%s\" appears to be invalid", - oprinfo[i].dobj.name); } PQclear(res); @@ -6242,7 +6212,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) @@ -6260,9 +6230,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); @@ -6275,7 +6244,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++) { @@ -6286,7 +6255,7 @@ 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); @@ -6318,7 +6287,7 @@ getConversions(Archive *fout, int *numConversions) int i_oid; int i_conname; int i_connamespace; - int i_rolname; + int i_conowner; query = createPQExpBuffer(); @@ -6329,9 +6298,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); @@ -6344,7 +6312,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++) { @@ -6355,7 +6323,7 @@ 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); @@ -6458,7 +6426,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 @@ -6467,9 +6435,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); @@ -6482,7 +6449,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++) { @@ -6493,14 +6460,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); - - if (strlen(opcinfo[i].rolname) == 0) - pg_log_warning("owner of operator class \"%s\" appears to be invalid", - opcinfo[i].dobj.name); } PQclear(res); @@ -6529,7 +6492,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies) int i_oid; int i_opfname; int i_opfnamespace; - int i_rolname; + int i_opfowner; /* Before 8.3, there is no separate concept of opfamilies */ if (fout->remoteVersion < 80300) @@ -6547,9 +6510,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); @@ -6562,7 +6524,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++) { @@ -6573,14 +6535,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); - - if (strlen(opfinfo[i].rolname) == 0) - pg_log_warning("owner of operator family \"%s\" appears to be invalid", - opfinfo[i].dobj.name); } PQclear(res); @@ -6612,7 +6570,7 @@ 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_acldefault; @@ -6631,7 +6589,7 @@ getAggregates(Archive *fout, int *numAggs) "p.proname AS aggname, " "p.pronamespace AS aggnamespace, " "p.pronargs, p.proargtypes, " - "(%s p.proowner) AS rolname, " + "p.proowner, " "p.proacl AS aggacl, " "acldefault('f', p.proowner) AS acldefault " "FROM pg_proc p " @@ -6644,7 +6602,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, agg_check); if (dopt->binary_upgrade) appendPQExpBufferStr(query, @@ -6660,15 +6617,14 @@ getAggregates(Archive *fout, int *numAggs) appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, " "pronamespace AS aggnamespace, " "pronargs, proargtypes, " - "(%s proowner) AS rolname, " + "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')", - username_subquery); + "WHERE nspname = 'pg_catalog')"); if (dopt->binary_upgrade) appendPQExpBufferStr(query, " OR EXISTS(SELECT 1 FROM pg_depend WHERE " @@ -6683,15 +6639,14 @@ getAggregates(Archive *fout, int *numAggs) appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, " "pronamespace AS aggnamespace, " "pronargs, proargtypes, " - "(%s proowner) AS rolname, " + "proowner, " "proacl AS aggacl, " "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 " @@ -6715,7 +6670,7 @@ 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_acldefault = PQfnumber(res, "acldefault"); @@ -6732,10 +6687,7 @@ getAggregates(Archive *fout, int *numAggs) 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 = 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.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.nargs = atoi(PQgetvalue(res, i, i_pronargs)); @@ -6783,7 +6735,7 @@ 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_acldefault; int i_ptctrusted; @@ -6800,11 +6752,10 @@ getExtProtocols(Archive *fout, int *numExtProtocols) "ptcreadfn as ptcreadoid, " "ptcwritefn as ptcwriteoid, " "ptcvalidatorfn as ptcvaloid, " - "(%s ptcowner) as rolname, " + "ptcowner, " "ptctrusted, ptcacl, " "acldefault('E', ptcowner) AS acldefault " - "FROM pg_extprotocol ", - username_subquery); + "FROM pg_extprotocol "); } else { @@ -6814,11 +6765,10 @@ getExtProtocols(Archive *fout, int *numExtProtocols) "ptcreadfn as ptcreadoid, " "ptcwritefn as ptcwriteoid, " "ptcvalidatorfn as ptcvaloid, " - "(%s ptcowner) as rolname, " + "ptcowner, " "ptctrusted, ptcacl, " "NULL AS acldefault " - "FROM pg_extprotocol ", - username_subquery); + "FROM pg_extprotocol "); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -6831,7 +6781,7 @@ 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_acldefault = PQfnumber(res, "acldefault"); i_ptctrusted = PQfnumber(res, "ptctrusted"); @@ -6847,10 +6797,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; @@ -6910,7 +6857,7 @@ 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; @@ -6953,7 +6900,7 @@ getFuncs(Archive *fout, int *numFuncs) "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 " @@ -6974,7 +6921,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))", - username_subquery, not_agg_check, g_last_builtin_oid, g_last_builtin_oid); @@ -7001,11 +6947,10 @@ getFuncs(Archive *fout, int *numFuncs) "pronargs, proargtypes, prorettype, proacl, " "%s AS acldefault, " "pronamespace, " - "(%s proowner) AS rolname " + "proowner " "FROM pg_proc p " "WHERE NOT proisagg", - acldefault_call, - username_subquery); + acldefault_call); if (fout->remoteVersion >= 90200) appendPQExpBufferStr(query, "\n AND NOT EXISTS (SELECT 1 FROM pg_depend " @@ -7058,7 +7003,7 @@ 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"); @@ -7079,7 +7024,7 @@ getFuncs(Archive *fout, int *numFuncs) finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); finfo[i].dacl.privtype = 0; finfo[i].dacl.initprivs = NULL; - finfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + 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].nargs = atoi(PQgetvalue(res, i, i_pronargs)); @@ -7099,10 +7044,6 @@ getFuncs(Archive *fout, int *numFuncs) /* Mark whether function has an ACL */ if (!PQgetisnull(res, i, i_proacl)) finfo[i].dobj.components |= DUMP_COMPONENT_ACL; - - if (strlen(finfo[i].rolname) == 0) - pg_log_warning("owner of function \"%s\" appears to be invalid", - finfo[i].dobj.name); } PQclear(res); @@ -7135,7 +7076,7 @@ getTables(Archive *fout, int *numTables) int i_relnamespace; int i_relkind; int i_reltype; - int i_rolname; + int i_relowner; int i_relchecks; int i_relhastriggers; int i_relhasindex; @@ -7197,15 +7138,14 @@ getTables(Archive *fout, int *numTables) appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.relname, " "c.relnamespace, c.relkind, c.reltype, " - "(%s c.relowner) AS rolname, " + "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, ", - username_subquery); + "tc.oid AS toid, "); if (fout->remoteVersion >= 90600) appendPQExpBufferStr(query, @@ -7451,7 +7391,7 @@ getTables(Archive *fout, int *numTables) i_relnamespace = PQfnumber(res, "relnamespace"); i_relkind = PQfnumber(res, "relkind"); i_reltype = PQfnumber(res, "reltype"); - i_rolname = PQfnumber(res, "rolname"); + i_relowner = PQfnumber(res, "relowner"); i_relchecks = PQfnumber(res, "relchecks"); i_relhasindex = PQfnumber(res, "relhasindex"); i_relhasrules = PQfnumber(res, "relhasrules"); @@ -7522,7 +7462,7 @@ getTables(Archive *fout, int *numTables) tblinfo[i].dacl.initprivs = NULL; tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind)); tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype)); - tblinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + 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); @@ -7677,11 +7617,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) @@ -8214,7 +8149,7 @@ getExtendedStatistics(Archive *fout) int i_oid; int i_stxname; int i_stxnamespace; - int i_rolname; + int i_stxowner; int i_stattarget; int i; @@ -8226,14 +8161,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); @@ -8243,7 +8177,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)); @@ -8257,7 +8191,7 @@ 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 */ @@ -8973,14 +8907,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); @@ -9008,7 +8941,7 @@ 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)); @@ -9065,11 +8998,10 @@ getProcLangs(Archive *fout, int *numProcLangs) "laninline, lanvalidator, " "lanacl, " "acldefault('l', lanowner) AS acldefault, " - "(%s lanowner) AS lanowner " + "lanowner " "FROM pg_language " "WHERE lanispl " - "ORDER BY oid", - username_subquery); + "ORDER BY oid"); } else if (fout->remoteVersion >= 90000) { @@ -9078,11 +9010,10 @@ getProcLangs(Archive *fout, int *numProcLangs) "lanname, lanpltrusted, lanplcallfoid, " "laninline, lanvalidator, " "lanacl, NULL AS acldefault, " - "(%s lanowner) AS lanowner " + "lanowner " "FROM pg_language " "WHERE lanispl " - "ORDER BY oid", - username_subquery); + "ORDER BY oid"); } else /* GPDB5 */ { @@ -9093,11 +9024,10 @@ getProcLangs(Archive *fout, int *numProcLangs) "laninline, lanvalidator, lanacl, " "0 AS laninline, lanvalidator, lanacl, " "NULL AS acldefault, " - "(%s lanowner) AS lanowner " + "lanowner " "FROM pg_language " "WHERE lanispl " - "ORDER BY oid", - username_subquery); + "ORDER BY oid"); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -9135,7 +9065,7 @@ getProcLangs(Archive *fout, int *numProcLangs) 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].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); @@ -10108,7 +10038,7 @@ 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; @@ -10122,10 +10052,9 @@ getTSDictionaries(Archive *fout, int *numTSDicts) 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); @@ -10138,7 +10067,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"); @@ -10151,7 +10080,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; @@ -10260,7 +10189,7 @@ 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 */ @@ -10273,9 +10202,8 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) 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); @@ -10288,7 +10216,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++) @@ -10300,7 +10228,7 @@ 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 */ @@ -10332,7 +10260,7 @@ 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; @@ -10351,7 +10279,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) if (fout->remoteVersion >= 90200) { appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, " - "(%s fdwowner) AS rolname, " + "fdwowner, " "fdwhandler::pg_catalog.regproc, " "fdwvalidator::pg_catalog.regproc, " "fdwacl, " @@ -10362,13 +10290,12 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) "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 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 acldefault, " @@ -10378,13 +10305,12 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) "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 acldefault, " @@ -10394,8 +10320,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) "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); @@ -10408,7 +10333,7 @@ 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"); @@ -10427,7 +10352,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); fdwinfo[i].dacl.privtype = 0; fdwinfo[i].dacl.initprivs = NULL; - fdwinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + 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)); @@ -10469,7 +10394,7 @@ 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; @@ -10489,7 +10414,7 @@ getForeignServers(Archive *fout, int *numForeignServers) if (fout->remoteVersion >= 90200) { appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, " - "(%s srvowner) AS rolname, " + "srvowner, " "srvfdw, srvtype, srvversion, srvacl, " "acldefault('S', srvowner) AS acldefault, " "array_to_string(ARRAY(" @@ -10498,13 +10423,12 @@ getForeignServers(Archive *fout, int *numForeignServers) "FROM pg_options_to_table(srvoptions) " "ORDER BY option_name" "), E',\n ') AS srvoptions " - "FROM pg_foreign_server", - username_subquery); + "FROM pg_foreign_server"); } else { appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, " - "(%s srvowner) AS rolname, " + "srvowner, " "srvfdw, srvtype, srvversion, srvacl, " "NULL AS acldefault, " "array_to_string(ARRAY(" @@ -10513,8 +10437,7 @@ getForeignServers(Archive *fout, int *numForeignServers) "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); @@ -10527,7 +10450,7 @@ 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"); @@ -10547,7 +10470,7 @@ getForeignServers(Archive *fout, int *numForeignServers) srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); srvinfo[i].dacl.privtype = 0; srvinfo[i].dacl.initprivs = NULL; - srvinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + 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)); @@ -10609,11 +10532,10 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs) appendPQExpBuffer(query, "SELECT oid, tableoid, " - "(%s defaclrole) AS defaclrole, " + "defaclrole, " "defaclnamespace, " "defaclobjtype, " - "defaclacl, ", - username_subquery); + "defaclacl, "); if (fout->remoteVersion >= 90200) { @@ -10678,7 +10600,7 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs) daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); daclinfo[i].dacl.privtype = 0; daclinfo[i].dacl.initprivs = NULL; - daclinfo[i].defaclrole = pg_strdup(PQgetvalue(res, i, i_defaclrole)); + daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole)); daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype)); /* Default ACLs are ACLs, of course */ @@ -10695,6 +10617,71 @@ 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 * @@ -15890,7 +15877,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, @@ -15903,7 +15890,7 @@ dumpExtProtocol(Archive *fout, const ExtProtInfo *ptcinfo) dumpACL(fout, ptcinfo->dobj.dumpId, InvalidDumpId, "PROTOCOL", namecopy, NULL, - NULL, ptcinfo->ptcowner, &ptcinfo->dacl); + NULL, ptcinfo->rolname, &ptcinfo->dacl); free(namecopy); destroyPQExpBuffer(q); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 1badb50b482..dc6791deb49 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -180,7 +180,8 @@ typedef struct _namespaceInfo { DumpableObject dobj; DumpableAcl dacl; - char *rolname; /* name of owner, or empty string */ + Oid nspowner; /* OID of owner */ + const char *rolname; /* name of owner */ } NamespaceInfo; typedef struct _extensionInfo @@ -204,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; @@ -236,7 +237,7 @@ typedef struct _funcInfo { DumpableObject dobj; DumpableAcl dacl; - char *rolname; /* name of owner, or empty string */ + const char *rolname; Oid lang; int nargs; Oid *argtypes; @@ -256,7 +257,7 @@ typedef struct _ptcInfo DumpableAcl dacl; char *ptcreadfn; char *ptcwritefn; - char *ptcowner; + const char *rolname; char *ptcacl; bool ptctrusted; Oid ptcreadid; @@ -267,7 +268,7 @@ typedef struct _ptcInfo typedef struct _oprInfo { DumpableObject dobj; - char *rolname; + const char *rolname; char oprkind; Oid oprcode; } OprInfo; @@ -282,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 @@ -310,7 +311,7 @@ typedef struct _tableInfo */ DumpableObject dobj; DumpableAcl dacl; - char *rolname; /* name of owner, or empty string */ + const char *rolname; char relkind; char relstorage; char relpersistence; /* relation persistence */ @@ -452,7 +453,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; @@ -491,7 +492,7 @@ typedef struct _evttriggerInfo DumpableObject dobj; char *evtname; char *evtevent; - char *evtowner; + const char *evtowner; char *evttags; char *evtfname; char evtenabled; @@ -528,7 +529,7 @@ typedef struct _procLangInfo Oid lanplcallfoid; Oid laninline; Oid lanvalidator; - char *lanowner; /* name of owner, or empty string */ + const char *lanowner; } ProcLangInfo; typedef struct _castInfo @@ -570,7 +571,7 @@ typedef struct _prsInfo typedef struct _dictInfo { DumpableObject dobj; - char *rolname; + const char *rolname; Oid dicttemplate; char *dictinitoption; } TSDictInfo; @@ -585,7 +586,7 @@ typedef struct _tmplInfo typedef struct _cfgInfo { DumpableObject dobj; - char *rolname; + const char *rolname; Oid cfgparser; } TSConfigInfo; @@ -593,7 +594,7 @@ typedef struct _fdwInfo { DumpableObject dobj; DumpableAcl dacl; - char *rolname; + const char *rolname; char *fdwhandler; char *fdwvalidator; char *fdwoptions; @@ -603,7 +604,7 @@ typedef struct _foreignServerInfo { DumpableObject dobj; DumpableAcl dacl; - char *rolname; + const char *rolname; Oid srvfdw; char *srvtype; char *srvversion; @@ -614,7 +615,7 @@ typedef struct _defaultACLInfo { DumpableObject dobj; DumpableAcl dacl; - char *defaclrole; + const char *defaclrole; char defaclobjtype; } DefaultACLInfo; @@ -622,7 +623,7 @@ typedef struct _blobInfo { DumpableObject dobj; DumpableAcl dacl; - char *rolname; + const char *rolname; } BlobInfo; /* @@ -649,7 +650,7 @@ typedef struct _policyInfo typedef struct _PublicationInfo { DumpableObject dobj; - char *rolname; + const char *rolname; bool puballtables; bool pubinsert; bool pubupdate; @@ -675,7 +676,7 @@ typedef struct _PublicationRelInfo typedef struct _SubscriptionInfo { DumpableObject dobj; - char *rolname; + const char *rolname; char *subconninfo; char *subslotname; char *subbinary; From 119b2b09701e0abd30ab6cf3befc921c22896306 Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Thu, 21 Jul 2022 14:57:43 -0400 Subject: [PATCH 11/32] pg_dump: Remove dead code. Remove remaining checks for versions pre-8.3 Authored-by: Brent Doil --- src/bin/pg_dump/pg_dump.c | 144 +++++++++----------------------------- src/bin/pg_dump/pg_dump.h | 2 - 2 files changed, 32 insertions(+), 114 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 69a05f30660..b4a618785ab 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -322,7 +322,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); @@ -1036,15 +1035,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); @@ -5848,15 +5839,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, " @@ -9957,13 +9939,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(); /* @@ -10042,13 +10017,6 @@ getTSDictionaries(Archive *fout, int *numTSDicts) 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, " @@ -10120,13 +10088,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, " @@ -10192,13 +10153,6 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) 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, " @@ -18051,23 +18005,15 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) */ if (gp_partitioning_available) { - 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); + appendPQExpBuffer(query, "SELECT " + "pg_get_partition_def('%u'::pg_catalog.oid, true, true) ", + tbinfo->dobj.catId.oid); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -18087,42 +18033,39 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) PQclear(res); - /* - * MPP-6095: dump ALTER TABLE statements for subpartition - * templates - */ - if (isTemplatesSupported) - { - resetPQExpBuffer(query); + /* + * MPP-6095: dump ALTER TABLE statements for subpartition + * templates + */ + resetPQExpBuffer(query); - appendPQExpBuffer( - query, "SELECT " - "pg_get_partition_template_def('%u'::pg_catalog.oid, true, true) ", - tbinfo->dobj.catId.oid); + 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); + 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); - } + 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)); + /* + * 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); - } + PQclear(res); if (isPartitioned) { @@ -19425,29 +19368,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 diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index dc6791deb49..3d431c02081 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -772,8 +772,6 @@ extern void getSubscriptions(Archive *fout); /* START MPP ADDITION */ extern ExtProtInfo *getExtProtocols(Archive *fout, int *numExtProtocols); extern BinaryUpgradeInfo *getBinaryUpgradeObjects(void); - -extern bool testExtProtocolSupport(Archive *fout); /* END MPP ADDITION */ #endif /* PG_DUMP_H */ From 2057075ab42295f1d6e0f1c025e4913834a22d7c Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Tue, 26 Jul 2022 16:02:54 -0400 Subject: [PATCH 12/32] pg_dump: Fix getTables parrelid Make sure tblinfo[i].parrelid is set. This was lost in the upstream backports, and fixes a bug when dumping from GPDB5/GPDB6. Authored-by: Brent Doil --- src/bin/pg_dump/pg_dump.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index b4a618785ab..cbef0525d7a 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -7478,6 +7478,7 @@ getTables(Archive *fout, int *numTables) 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 (PQgetisnull(res, i, i_reloftype)) tblinfo[i].reloftype = NULL; else From e40da991c71421fdaeeb3d348451a2f35a080a80 Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Tue, 26 Jul 2022 23:47:12 -0400 Subject: [PATCH 13/32] pg_dump: Put GPDB global vars in DumpOptions Authored-by: Brent Doil --- src/bin/pg_dump/pg_backup.h | 4 +++ src/bin/pg_dump/pg_dump.c | 65 +++++++++++++------------------------ 2 files changed, 27 insertions(+), 42 deletions(-) diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index 07d207f9593..3445c7594fd 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -203,6 +203,10 @@ typedef struct _dumpOptions int do_nothing; char *restrict_key; + /* GPDB */ + bool dumpGpPolicy; + bool isGPbackend; + bool gp_partitioning_available; } DumpOptions; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index cbef0525d7a..0d49ebed91b 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -102,11 +102,6 @@ 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 */ /* @@ -155,9 +150,6 @@ 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 */ @@ -394,8 +386,8 @@ 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, DumpOptions *dopt); +static void testPartitioningSupport(Archive *fout, DumpOptions *dopt); static char *nextToken(register char **stringp, register const char *delim); static void addDistributedBy(Archive *fout, PQExpBuffer q, const TableInfo *tbinfo, int actual_atts); @@ -984,7 +976,7 @@ main(int argc, char **argv) /* * Determine whether or not we're interacting with a GP backend. */ - isGPbackend = testGPbackend(fout); + testGPbackend(fout, &dopt); /* * Now that the type of backend is known, determine the gp-syntax option @@ -993,15 +985,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; } @@ -1022,7 +1014,7 @@ main(int argc, char **argv) /* * Remember whether or not this GP database supports partitioning. */ - gp_partitioning_available = testPartitioningSupport(fout); + testPartitioningSupport(fout, &dopt); /* check the version for the synchronized snapshots feature */ if (numWorkers > 1 && fout->remoteVersion < 90200 @@ -17997,14 +17989,14 @@ 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. */ - if (gp_partitioning_available) + if (dopt->gp_partitioning_available) { bool isPartitioned = false; PQExpBuffer query = createPQExpBuffer(); @@ -18104,7 +18096,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) 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"); @@ -18270,7 +18262,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) tbinfo->attalign[j]); appendStringLiteralAH(q, tbinfo->attnames[j], fout); - if (gp_partitioning_available) + if (dopt->gp_partitioning_available) { /* * Do for all descendants of a partition table. @@ -20884,49 +20876,38 @@ findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj, /* * isGPbackend - returns true if the connected backend is a GreenPlum DB backend. */ -static bool -testGPbackend(Archive *fout) +static void +testGPbackend(Archive *fout, DumpOptions *dopt) { - PQExpBuffer query; + PQExpBuffer query = createPQExpBuffer(); PGresult *res; - bool isGPbackend; - query = createPQExpBuffer(); - - appendPQExpBuffer(query, "SELECT current_setting('gp_role');"); - ArchiveHandle *AH = (ArchiveHandle *) fout; - res = PQexec(AH->connection, query->data); + appendPQExpBuffer(query, "SELECT 1 FROM current_setting('gp_role');"); + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); - isGPbackend = (PQresultStatus(res) == PGRES_TUPLES_OK); + dopt->isGPbackend = (PQntuples(res) == 1); PQclear(res); destroyPQExpBuffer(query); - - return isGPbackend; } /* * testPartitioningSupport - tests whether or not the current GP * database includes support for partitioning. */ -static bool -testPartitioningSupport(Archive *fout) +static void +testPartitioningSupport(Archive *fout, DumpOptions *dopt) { - PQExpBuffer query; + PQExpBuffer query = createPQExpBuffer(); PGresult *res; - bool isSupported; - - query = createPQExpBuffer(); appendPQExpBuffer(query, "SELECT 1 FROM pg_class WHERE relname = 'pg_partition' and relnamespace = 11;"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); - isSupported = (PQntuples(res) == 1); + dopt->gp_partitioning_available = (PQntuples(res) == 1); PQclear(res); destroyPQExpBuffer(query); - - return isSupported; } /* * addSchedule From 5dddb9509310106ac13fac999d2d3fab44de948f Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Wed, 27 Jul 2022 00:52:29 -0400 Subject: [PATCH 14/32] pg_dump: Formatting fixes and code cleanup Remove remaining references to pre 8.3 servers. Authored-by: Brent Doil --- src/bin/pg_dump/pg_dump.c | 177 +++++++++++--------------------------- 1 file changed, 49 insertions(+), 128 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 0d49ebed91b..0d1f32a3741 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -150,8 +150,6 @@ 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' -static DumpId binary_upgrade_dumpid; - /* override for standard extra_float_digits setting */ static bool have_extra_float_digits = false; static int extra_float_digits; @@ -1444,11 +1442,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 @@ -1468,7 +1465,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"); /* @@ -5889,8 +5886,6 @@ getBinaryUpgradeObjects(void) AssignDumpId(&binfo->dobj); binfo->dobj.name = pg_strdup("__binary_upgrade"); - binary_upgrade_dumpid = binfo->dobj.dumpId; - return binfo; } @@ -6468,13 +6463,6 @@ getOpfamilies(Archive *fout, int *numOpfamilies) int i_opfnamespace; int i_opfowner; - /* Before 8.3, there is no separate concept of opfamilies */ - if (fout->remoteVersion < 80300) - { - *numOpfamilies = 0; - return NULL; - } - query = createPQExpBuffer(); /* @@ -6950,12 +6938,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 " @@ -7951,15 +7936,15 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) 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"); + "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); @@ -8491,24 +8476,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); @@ -11945,17 +11918,10 @@ dumpBaseType(Archive *fout, const TypeInfo *tyinfo) "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 - appendPQExpBufferStr(query, - "'-' AS typmodin, '-' AS typmodout, " - "0 AS typmodinoid, 0 AS typmodoutoid, "); + "typdelim, typbyval, typalign, typstorage, " + "typmodin, typmodout, " + "typmodin::pg_catalog.oid AS typmodinoid, " + "typmodout::pg_catalog.oid AS typmodoutoid, "); if (fout->remoteVersion >= 80400) appendPQExpBufferStr(query, @@ -13173,10 +13139,9 @@ dumpFunc(Archive *fout, const FuncInfo *finfo) q = createPQExpBuffer(); delqry = createPQExpBuffer(); asPart = createPQExpBuffer(); - + if (!fout->is_prepared[PREPQUERY_DUMPFUNC]) { - /* GPDB_95_MERGE_FIXME: use isGE70 instead? */ /* Set up query for function-specific details */ appendPQExpBufferStr(query, "PREPARE dumpFunc(pg_catalog.oid) AS\n"); @@ -14386,32 +14351,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); @@ -14511,7 +14461,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, " @@ -14525,21 +14475,6 @@ dumpOpclass(Archive *fout, const OpclassInfo *opcinfo) "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 " - "ORDER BY amopstrategy", - opcinfo->dobj.catId.oid); - } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -14594,31 +14529,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); From 7291f27da6cd330fd8c9551cec23a30d739ef1d6 Mon Sep 17 00:00:00 2001 From: Ashwin Agrawal Date: Thu, 7 Jul 2022 15:15:45 -0700 Subject: [PATCH 15/32] Remove unnecessary partitioning checks in pg_dump Partitioning has existed in GPDB for a long time (4.2 and before). So, no reason to check for existence of these features since we do not support dumps from servers <= GPDB5. Removing the check because we expect these to exist. Fixes https://github.com/greenplum-db/gpdb/issues/5170. --- src/bin/pg_dump/pg_backup.h | 1 - src/bin/pg_dump/pg_dump.c | 39 ++++++++----------------------------- 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index 3445c7594fd..73e0e7c7c0c 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -206,7 +206,6 @@ typedef struct _dumpOptions /* GPDB */ bool dumpGpPolicy; bool isGPbackend; - bool gp_partitioning_available; } DumpOptions; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 0d1f32a3741..430b6fd3dd1 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -385,7 +385,6 @@ static void expand_oid_patterns(SimpleStringList *patterns, static bool is_returns_table_function(int nallargs, char **argmodes); static void testGPbackend(Archive *fout, DumpOptions *dopt); -static void testPartitioningSupport(Archive *fout, DumpOptions *dopt); static char *nextToken(register char **stringp, register const char *delim); static void addDistributedBy(Archive *fout, PQExpBuffer q, const TableInfo *tbinfo, int actual_atts); @@ -1009,11 +1008,6 @@ main(int argc, char **argv) if (fout->isStandby) dopt.no_unlogged_table_data = true; - /* - * Remember whether or not this GP database supports partitioning. - */ - testPartitioningSupport(fout, &dopt); - /* check the version for the synchronized snapshots feature */ if (numWorkers > 1 && fout->remoteVersion < 90200 && !dopt.no_synchronized_snapshots) @@ -17914,10 +17908,12 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) addDistributedBy(fout, q, tbinfo, actual_atts); /* - * If GP partitioning is supported add the partitioning constraints to - * the table definition. + * Add the partitioning constraints to the table definition. This + * check used to look for existence of pg_partition table to make the + * decision, instead seems better to decide based on version. GPDB6 + * and below have pg_get_partition_def functions. */ - if (dopt->gp_partitioning_available) + if (fout->remoteVersion <= 90400) { bool isPartitioned = false; PQExpBuffer query = createPQExpBuffer(); @@ -18009,9 +18005,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) } destroyPQExpBuffer(query); - } - - /* END MPP ADDITION */ + } /* END MPP ADDITION */ /* Dump generic options if any */ if (ftoptions && ftoptions[0]) @@ -18183,7 +18177,8 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) tbinfo->attalign[j]); appendStringLiteralAH(q, tbinfo->attnames[j], fout); - if (dopt->gp_partitioning_available) + /* GPDB partitioning */ + if (fout->remoteVersion <= 90400) { /* * Do for all descendants of a partition table. @@ -20812,24 +20807,6 @@ testGPbackend(Archive *fout, DumpOptions *dopt) destroyPQExpBuffer(query); } -/* - * testPartitioningSupport - tests whether or not the current GP - * database includes support for partitioning. - */ -static void -testPartitioningSupport(Archive *fout, DumpOptions *dopt) -{ - PQExpBuffer query = createPQExpBuffer(); - PGresult *res; - - appendPQExpBuffer(query, "SELECT 1 FROM pg_class WHERE relname = 'pg_partition' and relnamespace = 11;"); - res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); - - dopt->gp_partitioning_available = (PQntuples(res) == 1); - - PQclear(res); - destroyPQExpBuffer(query); -} /* * addSchedule * From f38a5b82aba072c00d613526631e6aded08833b5 Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Wed, 17 Aug 2022 10:52:40 -0400 Subject: [PATCH 16/32] pg_dump: Remove DumpOptions argument from testGPbackend --- src/bin/pg_dump/pg_dump.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 430b6fd3dd1..9423956a672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -384,7 +384,7 @@ static void expand_oid_patterns(SimpleStringList *patterns, SimpleOidList *oids); static bool is_returns_table_function(int nallargs, char **argmodes); -static void testGPbackend(Archive *fout, DumpOptions *dopt); +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); @@ -973,7 +973,7 @@ main(int argc, char **argv) /* * Determine whether or not we're interacting with a GP backend. */ - testGPbackend(fout, &dopt); + testGPbackend(fout); /* * Now that the type of backend is known, determine the gp-syntax option @@ -20793,8 +20793,9 @@ findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj, * isGPbackend - returns true if the connected backend is a GreenPlum DB backend. */ static void -testGPbackend(Archive *fout, DumpOptions *dopt) +testGPbackend(Archive *fout) { + DumpOptions *dopt = fout->dopt; PQExpBuffer query = createPQExpBuffer(); PGresult *res; From 1c72ee192290b8ff9ea76272e1420fd0d4b72513 Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Wed, 7 Sep 2022 18:17:07 -0400 Subject: [PATCH 17/32] pg_dump: Update child partition metadata for GPDB{5,6} To gather binary upgrade information for GPDB{5,6} partition children, they need to exist in the tblinfo array for findTableByOid. This requires the getTable query to also collect all GPDB partition children, then filter them out of the dump so we only dump the partition root DDL. Previously, partition children were being excluded from the getTables query and gathered separately in dumpTableSchema. This allows dumpTableSchema to perform lookups of the partition children to dump their OIDs for binary upgrade. Authored-by: Brent Doil --- src/bin/pg_dump/pg_dump.c | 32 +++++++++++++++++--------------- src/bin/pg_dump/pg_dump.h | 2 +- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 9423956a672..06837d09ea3 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -7312,14 +7312,8 @@ getTables(Archive *fout, int *numTables) appendPQExpBufferStr(query, "AND c.relnamespace <> 3012\n"); /* BM_BITMAPINDEX_NAMESPACE in GPDB 5 and below */ - if (fout->remoteVersion >= 90600) - appendPQExpBufferStr(query, - "ORDER BY c.oid"); - else - appendPQExpBufferStr(query, - "AND c.oid NOT IN (select p.parchildrelid from pg_partition_rule p\n" - "LEFT JOIN pg_exttable e on p.parchildrelid=e.reloid where e.reloid is null)\n" - "ORDER BY c.oid"); + appendPQExpBufferStr(query, + "ORDER BY c.oid"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -7464,7 +7458,8 @@ getTables(Archive *fout, int *numTables) tblinfo[i].partbound = pg_strdup(PQgetvalue(res, i, i_partbound)); tblinfo[i].relstorage = *(PQgetvalue(res, i, i_relstorage)); - if (tblinfo[i].parrelid != 0) + + if (tblinfo[i].parparent && tblinfo[i].parrelid != 0 && tblinfo[i].relstorage == 'x') { /* * Length of tmpStr is bigger than the sum of NAMEDATALEN @@ -7488,9 +7483,13 @@ 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); @@ -7557,7 +7556,6 @@ getTables(Archive *fout, int *numTables) /* GPDB_14_MERGE_FIXME: GPDB_96_MERGE_FIXME: Is the parrelid check still needed? */ if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) && - tblinfo[i].parrelid == 0 && (tblinfo[i].relkind == RELKIND_RELATION || tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE)) { @@ -17913,7 +17911,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) * decision, instead seems better to decide based on version. GPDB6 * and below have pg_get_partition_def functions. */ - if (fout->remoteVersion <= 90400) + if (fout->remoteVersion <= 90400 && tbinfo->parparent) { bool isPartitioned = false; PQExpBuffer query = createPQExpBuffer(); @@ -17976,8 +17974,12 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) appendPQExpBuffer(q, ";\n %s", PQgetvalue(res, 0, 0)); PQclear(res); - - if (isPartitioned) + + /* + * If GP partitioning is supported and table is a parent partition + * add the partitioning constraints to the table definition. + */ + if (isPartitioned && tbinfo->parparent) { /* * Find out if there are any external partitions. diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 3d431c02081..f1a309d20b8 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -387,7 +387,7 @@ 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 */ From 21feac1f4273e23a7b9530b0c457eccbaf5cb6ed Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Sat, 10 Sep 2022 11:40:37 -0400 Subject: [PATCH 18/32] pg_dump: Omit BYPASSRLS clause for pre GPDB7 server GPDB5/6 do not support RLS. Without the version gate a dump and restore on these server versions will fail. Authored-by: Brent Doil --- src/bin/pg_dump/pg_dumpall.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index b1ff242a5dd..83473746578 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -1395,10 +1395,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", From bdf2086d2702dcb951e9e5d016bdff2de05d6fad Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Mon, 12 Sep 2022 13:15:54 -0400 Subject: [PATCH 19/32] pg_dump: Move partition def queries to getTables Move pg_get_partition_def and pg_get_partition_template_def queries from dumpTableSchema to getTables. The functions are only run for parent tables. This change reduces the number of repetitive queries and simplifies dumpTableSchema. Authored-by: Brent Doil --- src/bin/pg_dump/pg_dump.c | 147 ++++++++++---------------------------- src/bin/pg_dump/pg_dump.h | 8 ++- 2 files changed, 44 insertions(+), 111 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 06837d09ea3..512434f9496 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -7067,6 +7067,8 @@ getTables(Archive *fout, int *numTables) int i_isivm; int i_isdynamic; int i_relstorage; + int i_partclause; + int i_parttemplate; /* * Find all the tables and table-like objects. @@ -7238,12 +7240,18 @@ getTables(Archive *fout, int *numTables) appendPQExpBufferStr(query, "pg_get_partkeydef(c.oid) AS partkeydef, " "c.relispartition AS ispartition, " - "pg_get_expr(c.relpartbound, c.oid) AS partbound "); + "pg_get_expr(c.relpartbound, c.oid) AS partbound, " + "NULL as partclause, " + "NULL as parttemplate"); else appendPQExpBufferStr(query, "NULL AS partkeydef, " - "false AS ispartition, " - "NULL AS partbound "); + "0 AS ispartition," + "NULL AS partbound, " + "CASE WHEN pl.parlevel = 0 THEN " + "(SELECT pg_get_partition_def(c.oid, true, true)) END AS partclause, " + "CASE WHEN pl.parlevel = 0 THEN " + "(SELECT pg_get_partition_template_def(c.oid, true, true)) END as parttemplate "); /* * Left join to pg_depend to pick up dependency info linking sequences to @@ -7376,6 +7384,8 @@ getTables(Archive *fout, int *numTables) i_relstorage = PQfnumber(res, "relstorage"); i_parrelid = PQfnumber(res, "parrelid"); i_parlevel = PQfnumber(res, "parlevel"); + i_partclause = PQfnumber(res, "partclause"); + i_parttemplate = PQfnumber(res, "parttemplate"); if (dopt->lockWaitTimeout) { @@ -7459,7 +7469,17 @@ getTables(Archive *fout, int *numTables) tblinfo[i].relstorage = *(PQgetvalue(res, i, i_relstorage)); - if (tblinfo[i].parparent && tblinfo[i].parrelid != 0 && tblinfo[i].relstorage == 'x') + if (PQgetisnull(res, i, i_parlevel) || + atoi(PQgetvalue(res, i, i_parlevel)) > 0) + tblinfo[i].parparent = false; + else + { + tblinfo[i].parparent = true; + tblinfo[i].partclause = pg_strdup(PQgetvalue(res, i, i_partclause)); + tblinfo[i].parttemplate = pg_strdup(PQgetvalue(res, i, i_parttemplate)); + } + + if (!tblinfo[i].parparent && tblinfo[i].parrelid != 0 && tblinfo[i].relstorage == 'x') { /* * Length of tmpStr is bigger than the sum of NAMEDATALEN @@ -7469,11 +7489,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_amoid)) tblinfo[i].amoid = InvalidOid; @@ -7547,7 +7562,7 @@ 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 @@ -17606,6 +17621,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); } @@ -17906,107 +17924,18 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) addDistributedBy(fout, q, tbinfo, actual_atts); /* - * Add the partitioning constraints to the table definition. This - * check used to look for existence of pg_partition table to make the - * decision, instead seems better to decide based on version. GPDB6 - * and below have pg_get_partition_def functions. + * 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 (fout->remoteVersion <= 90400 && tbinfo->parparent) + if (fout->remoteVersion <= 90400 && + (*tbinfo->partclause && *tbinfo->partclause != '\0')) { - bool isPartitioned = false; - PQExpBuffer query = createPQExpBuffer(); - PGresult *res; - - /* does support GP partitioning. */ - resetPQExpBuffer(query); - appendPQExpBuffer(query, "SELECT " - "pg_get_partition_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); - } - 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 - */ - 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 GP partitioning is supported and table is a parent partition - * add the partitioning constraints to the table definition. - */ - if (isPartitioned && tbinfo->parparent) - { - /* - * 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); + /* 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 */ diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index f1a309d20b8..71cf6866ebf 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -394,8 +394,12 @@ typedef struct _tableInfo 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 From 04baa70f8ebba6c1d9c9348d8fabbf49db33439c Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Wed, 21 Sep 2022 06:26:00 -0400 Subject: [PATCH 20/32] pg_dump: Define macros for GPDB{5-7} Postgres major version Two conditionals in getTableSchema were testing remote version <= 90400 for GPDB5/6 specific code, which is a bug because it omits GPDB6 (90424 or 90426) altogether. This commit fixes the issue by defining and using a macro for the major Postgres versions for GPDB5-7. These are then used in GPDB specific version checks. Also cleans up some now unused functions. Also check tbinfo->partclause before trying to dereference the pointer. Authored-by: Brent Doil --- src/bin/pg_dump/pg_backup.h | 8 ++++ src/bin/pg_dump/pg_backup_db.c | 9 +++- src/bin/pg_dump/pg_dump.c | 81 +++++----------------------------- src/bin/pg_dump/pg_dumpall.c | 2 +- 4 files changed, 28 insertions(+), 72 deletions(-) diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index 73e0e7c7c0c..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 { 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 512434f9496..5087fac372b 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -390,62 +390,9 @@ 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 isMPP(Archive *fout); -static bool isGPDB5000OrLater(Archive *fout); -static bool isGPDB6000OrLater(Archive *fout); /* END MPP ADDITION */ -/* - * Check if we are talking to Greenplum or Cloudberry - */ -static bool -isMPP(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 || strstr(ver, "Greenplum") != NULL) - value = 1; - else - value = 0; - - PQclear(res); - } - return (value == 1) ? true : false; -} - - -static bool -isGPDB5000OrLater(Archive *fout) -{ - if (!isMPP(fout)) - return false; /* Not GP-based at all. */ - - /* GPDB 5 is based on PostgreSQL 8.3 */ - return fout->remoteVersion >= 80300; -} - - -static bool -isGPDB6000OrLater(Archive *fout) -{ - if (!isMPP(fout)) - return false; /* Not GP-based at all. */ - - /* GPDB 6 is based on PostgreSQL 9.4 */ - return fout->remoteVersion >= 90400; -} - int main(int argc, char **argv) { @@ -958,7 +905,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; @@ -3818,9 +3765,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); @@ -7286,7 +7233,7 @@ getTables(Archive *fout, int *numTables) " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE) " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"); - if (fout->remoteVersion <= 90426) // GPDB 6 or below + 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" @@ -13117,7 +13064,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; @@ -13178,7 +13124,7 @@ dumpFunc(Archive *fout, const FuncInfo *finfo) "false AS proleakproof,\n"); /* GPDB6 added proexeclocation */ - if (fout->remoteVersion >= 90400) + if (fout->remoteVersion >= GPDB6_MAJOR_PGVERSION) appendPQExpBuffer(query, "proexeclocation,\n"); else @@ -13250,12 +13196,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")); @@ -17061,7 +17003,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; @@ -17077,7 +17018,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, " @@ -17928,7 +17869,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) * These do not exist on GPDB7, so first check if we are dumping * from <= GPDB6. */ - if (fout->remoteVersion <= 90400 && + if (fout->remoteVersion < GPDB7_MAJOR_PGVERSION && (*tbinfo->partclause && *tbinfo->partclause != '\0')) { /* partition by clause */ @@ -18109,7 +18050,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) appendStringLiteralAH(q, tbinfo->attnames[j], fout); /* GPDB partitioning */ - if (fout->remoteVersion <= 90400) + if (fout->remoteVersion < GPDB7_MAJOR_PGVERSION) { /* * Do for all descendants of a partition table. @@ -20775,7 +20716,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_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 83473746578..43fc2e52a8e 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -2638,7 +2638,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", From 2830f8e0f2a05c7821751f5ca36f39d01f237235 Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Thu, 29 Sep 2022 14:19:54 -0400 Subject: [PATCH 21/32] pg_dump: Remove unneeded version check for quote_all_identifiers Authored-by: Brent Doil --- src/bin/pg_dump/pg_dumpall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 43fc2e52a8e..dfb6f3f0c67 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"); From e61e4942124a10ddf5e600f800ec71dda8251426 Mon Sep 17 00:00:00 2001 From: "xuqi.wxq" Date: Tue, 27 Sep 2022 14:56:27 +0800 Subject: [PATCH 22/32] Fix external protocol dump from gp6 using gp7 pg_dump. As in gp6 there is no objtype abbreviation E in 'acldefault' fuction. --- src/bin/pg_dump/pg_dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 5087fac372b..32b5de212f1 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -6647,7 +6647,7 @@ getExtProtocols(Archive *fout, int *numExtProtocols) int i_ptcvalidid; /* find all user-defined external protocol */ - if (fout->remoteVersion >= 90200) + if (fout->remoteVersion >= 90600) { appendPQExpBuffer(query, "SELECT tableoid, " "oid, " From c6c1d3e2be47813af572a043de70bafcb8ac2abd Mon Sep 17 00:00:00 2001 From: xiaoxiao <53000479+xiaoxiaoHe-E@users.noreply.github.com> Date: Thu, 2 Mar 2023 17:42:36 +0800 Subject: [PATCH 23/32] Fix pg_dump small bug (#14991) I met this error when I tried to use pg_dump in main branch to dump ao table from gpdb6. Co-authored-by: gpadmin --- src/bin/pg_dump/pg_dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 32b5de212f1..e848667baa6 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -17870,7 +17870,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) * from <= GPDB6. */ if (fout->remoteVersion < GPDB7_MAJOR_PGVERSION && - (*tbinfo->partclause && *tbinfo->partclause != '\0')) + (tbinfo->partclause && *tbinfo->partclause != '\0')) { /* partition by clause */ appendPQExpBuffer(q, " %s", tbinfo->partclause); From e22febb5f1b7b535914a664a4809f7e2b166e3e0 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 14 Mar 2023 16:09:03 -0400 Subject: [PATCH 24/32] Allow pg_dump to include/exclude child tables automatically. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport of upstream commit: a563c24c9574b74f4883c004c89275bba03c3c26 Resolved merge conflicts in: * 002_pg_dump.pl due to multiple tests present in upstream that are not yet present in gpdb codebase. * minor merge conflicts in pg_dump.sgml, unclear their source, likely formatting-derived backported by: Andrew Repp Original commit message: This patch adds new pg_dump switches --table-and-children=pattern --exclude-table-and-children=pattern --exclude-table-data-and-children=pattern which act the same as the existing --table, --exclude-table, and --exclude-table-data switches, except that any partitions or inheritance child tables of the table(s) matching the pattern are also included or excluded. Gilles Darold, reviewed by Stéphane Tachoires Discussion: https://postgr.es/m/5aa393b5-5f67-8447-b83e-544516990ee2@migops.com --- doc/src/sgml/ref/pg_dump.sgml | 38 +++ src/bin/pg_dump/pg_dump.c | 84 ++++- src/bin/pg_dump/t/002_pg_dump.pl | 508 ++++++++++++++++++++++++++----- 3 files changed, 544 insertions(+), 86 deletions(-) diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index c405fef866b..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. + + + + @@ -1169,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/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index e848667baa6..791091870d3 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -125,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}; @@ -205,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); @@ -498,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 */ @@ -758,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); @@ -985,21 +1007,30 @@ main(int argc, char **argv) /* 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); @@ -1244,7 +1275,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")); @@ -1253,7 +1284,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" @@ -1278,6 +1315,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")); @@ -1729,7 +1768,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; @@ -1755,7 +1794,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" @@ -1778,6 +1825,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, 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, + }, }); ######################################### From 5753b22920e83889cb81f449923737fe16879033 Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Tue, 11 Jul 2023 13:58:39 -0400 Subject: [PATCH 25/32] Add getPartitionDefs function to pg_dump Currently, when backing up a GP5 or GP6 database, getTables will hold a lock on all partition tables in the database by calling `pg_get_partition_def` and `pg_get_partition_template_def` on each table, resulting in tables being locked that are not part of the backup set. This commit extracts the `pg_get_partition_def` and `pg_get_partition_template_def` function calls out of getTables and into getPartitionDefs. Now, only partition tables in the backup set will be included in calls to these functions. Authored-by: Brent Doil --- src/bin/pg_dump/common.c | 2 + src/bin/pg_dump/pg_dump.c | 99 +++++++++++++++++++++++++++++++++------ src/bin/pg_dump/pg_dump.h | 1 + 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 75872b4473b..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"); diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 791091870d3..2f9749cdffa 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -7072,8 +7072,6 @@ getTables(Archive *fout, int *numTables) int i_isivm; int i_isdynamic; int i_relstorage; - int i_partclause; - int i_parttemplate; /* * Find all the tables and table-like objects. @@ -7252,11 +7250,7 @@ getTables(Archive *fout, int *numTables) appendPQExpBufferStr(query, "NULL AS partkeydef, " "0 AS ispartition," - "NULL AS partbound, " - "CASE WHEN pl.parlevel = 0 THEN " - "(SELECT pg_get_partition_def(c.oid, true, true)) END AS partclause, " - "CASE WHEN pl.parlevel = 0 THEN " - "(SELECT pg_get_partition_template_def(c.oid, true, true)) END as parttemplate "); + "NULL AS partbound "); /* * Left join to pg_depend to pick up dependency info linking sequences to @@ -7389,8 +7383,6 @@ getTables(Archive *fout, int *numTables) i_relstorage = PQfnumber(res, "relstorage"); i_parrelid = PQfnumber(res, "parrelid"); i_parlevel = PQfnumber(res, "parlevel"); - i_partclause = PQfnumber(res, "partclause"); - i_parttemplate = PQfnumber(res, "parttemplate"); if (dopt->lockWaitTimeout) { @@ -7478,11 +7470,7 @@ getTables(Archive *fout, int *numTables) atoi(PQgetvalue(res, i, i_parlevel)) > 0) tblinfo[i].parparent = false; else - { tblinfo[i].parparent = true; - tblinfo[i].partclause = pg_strdup(PQgetvalue(res, i, i_partclause)); - tblinfo[i].parttemplate = pg_strdup(PQgetvalue(res, i, i_parttemplate)); - } if (!tblinfo[i].parparent && tblinfo[i].parrelid != 0 && tblinfo[i].relstorage == 'x') { @@ -7768,7 +7756,7 @@ getPartitioningInfo(Archive *fout) "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); @@ -7790,6 +7778,89 @@ getPartitioningInfo(Archive *fout) 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); + } + + 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++) + { + 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)); + } + + } + PQclear(res); + + destroyPQExpBuffer(query); + destroyPQExpBuffer(tbloids); +} + /* * getIndexes * get information about every index on a dumpable table diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 71cf6866ebf..f26d764d5b6 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -776,6 +776,7 @@ extern void getSubscriptions(Archive *fout); /* START MPP ADDITION */ extern ExtProtInfo *getExtProtocols(Archive *fout, int *numExtProtocols); extern BinaryUpgradeInfo *getBinaryUpgradeObjects(void); +extern void getPartitionDefs(Archive *fout, TableInfo tblinfo[], int numTables); /* END MPP ADDITION */ #endif /* PG_DUMP_H */ From 12409e97edc913ea4803695ded77aa0c8982b3b7 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 6 Dec 2021 12:49:49 -0500 Subject: [PATCH 26/32] Postpone calls of unsafe server-side functions in pg_dump. Backported from PG15 e3fcbbd Conflicts resolved: * OidOptions enum value zeroIsError does not exist, instead used the existing zeroAsOpaque. * Minor conflicts in dumpTableSchema resulting from foreign server related code introduced in PG13 commit 4e62091 Original commit message follows: Avoid calling pg_get_partkeydef(), pg_get_expr(relpartbound), and regtypeout until we have lock on the relevant tables. The existing coding is at serious risk of failure if there are any concurrent DROP TABLE commands going on --- including drops of other sessions' temp tables. Arguably this is a bug fix that should be back-patched, but it's moderately invasive and we've not had all that many complaints about such failures. Let's just put it in HEAD for now. Discussion: https://postgr.es/m/2273648.1634764485@sss.pgh.pa.us Discussion: https://postgr.es/m/7d7eb6128f40401d81b3b7a898b6b4de@W2012-02.nidsa.loc --- src/bin/pg_dump/pg_dump.c | 109 +++++++++++++++++++++++--------------- src/bin/pg_dump/pg_dump.h | 4 +- 2 files changed, 68 insertions(+), 45 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 2f9749cdffa..d09cd899445 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -7058,24 +7058,25 @@ 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_relacl; int i_acldefault; - int i_partkeydef; int i_ispartition; - int i_partbound; int i_amname; int i_amoid; int i_isivm; int i_isdynamic; - int i_relstorage; /* * 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...) * @@ -7089,8 +7090,10 @@ 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. + * 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. */ appendPQExpBuffer(query, @@ -7180,10 +7183,10 @@ getTables(Archive *fout, int *numTables) if (fout->remoteVersion >= 90000) appendPQExpBufferStr(query, - "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "); + "c.reloftype, "); else appendPQExpBufferStr(query, - "NULL AS reloftype, "); + "0 AS reloftype, "); if (fout->remoteVersion >= 90600) appendPQExpBufferStr(query, @@ -7241,16 +7244,10 @@ getTables(Archive *fout, int *numTables) if (fout->remoteVersion >= 100000) appendPQExpBufferStr(query, - "pg_get_partkeydef(c.oid) AS partkeydef, " - "c.relispartition AS ispartition, " - "pg_get_expr(c.relpartbound, c.oid) AS partbound, " - "NULL as partclause, " - "NULL as parttemplate"); + "c.relispartition AS ispartition "); else appendPQExpBufferStr(query, - "NULL AS partkeydef, " - "0 AS ispartition," - "NULL AS partbound "); + "false AS ispartition "); /* * Left join to pg_depend to pick up dependency info linking sequences to @@ -7374,15 +7371,13 @@ getTables(Archive *fout, int *numTables) i_is_identity_sequence = PQfnumber(res, "is_identity_sequence"); i_relacl = PQfnumber(res, "relacl"); i_acldefault = PQfnumber(res, "acldefault"); - i_partkeydef = PQfnumber(res, "partkeydef"); i_ispartition = PQfnumber(res, "ispartition"); - i_partbound = PQfnumber(res, "partbound"); 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"); + i_relstorage = PQfnumber(res, "relstorage"); + i_parrelid = PQfnumber(res, "parrelid"); + i_parlevel = PQfnumber(res, "parlevel"); if (dopt->lockWaitTimeout) { @@ -7451,18 +7446,13 @@ getTables(Archive *fout, int *numTables) 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 (PQgetisnull(res, i, i_reloftype)) - tblinfo[i].reloftype = NULL; - else - tblinfo[i].reloftype = pg_strdup(PQgetvalue(res, i, i_reloftype)); + 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].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].relstorage = *(PQgetvalue(res, i, i_relstorage)); @@ -7523,10 +7513,6 @@ getTables(Archive *fout, int *numTables) 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); @@ -17533,7 +17519,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); @@ -17588,8 +17574,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(); @@ -17632,10 +17639,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; } } @@ -17725,8 +17732,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) { @@ -17764,7 +17773,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; @@ -17805,7 +17815,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]); @@ -17895,7 +17905,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, @@ -17924,7 +17934,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)); @@ -18281,12 +18291,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"); } @@ -18475,6 +18486,8 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) tbinfo->attfdwoptions[j]); } /* end loop over columns */ + if (partkeydef) + free(partkeydef); if (ftoptions) free(ftoptions); if (srvname) @@ -18582,6 +18595,8 @@ 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) @@ -18592,14 +18607,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 @@ -18616,6 +18640,7 @@ dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo) .section = SECTION_PRE_DATA, .createStmt = q->data)); + PQclear(res); destroyPQExpBuffer(q); } diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index f26d764d5b6..d53d2ab623e 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -335,7 +335,7 @@ typedef struct _tableInfo uint32 toast_minmxid; /* toast table's relminmxid */ int ncheck; /* # of CHECK expressions */ Oid reltype; /* OID of table's composite type, if any */ - char *reloftype; /* underlying type for typed table */ + 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 */ @@ -376,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 */ From 71fd435d78fe1c3c35d873c8317ed8498ae72817 Mon Sep 17 00:00:00 2001 From: Kevin Yeap Date: Mon, 20 Nov 2023 17:31:55 -0800 Subject: [PATCH 27/32] pg_dumpall: dump 6x resource groups correctly for 6 > 7 upgrade In order to support 6 > 7 upgrade using pg_upgrade. 7x pg_dumpall needs to be able to dump 6x resource groups correctly. Supporting the dumping of 6x resource group resolves the following error when trying to upgrade from 6 > 7. ``` ALTER RESOURCE GROUP "default_group" SET cpu_weight 0; psql:pg_upgrade_dump_globals.sql:27: ERROR: cpu_weight range is [1, 500] ``` This happens because some of the attribute ordering in pg_resgroupcapability changed from 6x to 7x. To resolve this issue, the following mapping was done based on 6x and 7x resource group docs. New attribute cpu_weight is set to 7x default value of 100. 6x | 7x ---------------+----------------- concurrency | concurrency cpu_rate_limit | cpu_max_percent 100 | cpu_weight cpuset | cputset reference resource group docs: https://docs.vmware.com/en/VMware-Greenplum/6/greenplum-database/admin_guide-workload_mgmt_resgroups.html --- src/bin/pg_dump/pg_dumpall.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index dfb6f3f0c67..074505a2852 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -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); From ed82386143e128fa32f1fe2ba3fc4ab55973ca76 Mon Sep 17 00:00:00 2001 From: Kevin Yeap Date: Fri, 15 Dec 2023 14:47:23 -0800 Subject: [PATCH 28/32] pg_dump: fix --function-oid when --relation-oid is also used pg_dump does not correctly dump functions when both greenplum specific internal flags `--function-oid` and `--relation-oid` are used at the same time. This is because functions are being marked dumpable or not twice. The first time is by selectDumpableFunction and then again by selectDumpableObject. When `--relation-oid` is used, all namespaces are marked not dumpable which is why selectDumpableObject will mark the function to not be dumped. This must be a regression. The original commit that introduces greenplum specific selectDumpableFunction removes the usage of the more generic selectDumpableObject. selectDumpableFunction reference commit: https://github.com/greenplum-db/gpdb/commit/318b86afc3eb5745f07ddfb390970b1769c63a1b --- gpMgmt/test/behave/mgmt_utils/environment.py | 1 + gpMgmt/test/behave/mgmt_utils/minirepro.feature | 12 ++++++++++++ src/bin/pg_dump/pg_dump.c | 1 - 3 files changed, 13 insertions(+), 1 deletion(-) 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/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index d09cd899445..d623077ccb0 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -6997,7 +6997,6 @@ getFuncs(Archive *fout, int *numFuncs) /* Decide whether we want to dump it */ selectDumpableFunction(&finfo[i]); - selectDumpableObject(&(finfo[i].dobj), fout); /* Mark whether function has an ACL */ if (!PQgetisnull(res, i, i_proacl)) From d47b9178bac96112e40effb54df3165ac269e885 Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Fri, 5 Jan 2024 19:55:41 -0500 Subject: [PATCH 29/32] Fix dumping funcs/aggs with --function-oids flag. Commit 992b4dd fixes a pg_dump regression when using --function-oid where functions were incorrectly being marked to not dump. However, this fix revealed a second pg_dump regression with selectDumpableFunction which was being masked by the first regression. selectDumpableFunction now needed updating to work with the new method of dumping that uses bit masking that was backported in github.com/greenplum-db/gpdb/commits/07a3ffef6422275f76818d9c445c31495460889a. It also needed updating to work with function dumping when extensions are involved. selectDumpableAggregate was created so aggregate dumping works as intended when using --function-oid on an aggregate function. This is necessary because pg_dump treats aggregates separately from functions. See the comment in getFuncs for details. --- src/bin/pg_dump/pg_dump.c | 43 ++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index d623077ccb0..96026aa8ab4 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -2153,20 +2153,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; } /* @@ -6663,7 +6692,7 @@ getAggregates(Archive *fout, int *numAggs) } /* Decide whether we want to dump it */ - selectDumpableObject(&(agginfo[i].aggfn.dobj), fout); + selectDumpableAggregate(&(agginfo[i]), fout); /* Mark whether aggregate has an ACL */ if (!PQgetisnull(res, i, i_aggacl)) @@ -6996,7 +7025,7 @@ getFuncs(Archive *fout, int *numFuncs) } /* Decide whether we want to dump it */ - selectDumpableFunction(&finfo[i]); + selectDumpableFunction(&finfo[i], fout); /* Mark whether function has an ACL */ if (!PQgetisnull(res, i, i_proacl)) From 3a60eaad9e9fb47b57815f30085814d952fdd574 Mon Sep 17 00:00:00 2001 From: Kevin Yeap Date: Thu, 18 Jan 2024 19:12:46 -0800 Subject: [PATCH 30/32] pg_dump: exclude gp_toolkit from being dumped when upgrading from GPDB6 As of GPDB7, gp_toolkit is an extension. During gpinitsystem gp_toolkit extension is automatically created in template1. During 6 > 7 upgrade, we should assume that this extension will always be there. In future upgrades, gp_toolkit will be treated like any other extension. --- src/bin/pg_dump/pg_dump.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 96026aa8ab4..0d51ed45015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1001,6 +1001,13 @@ 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); From 23470d51977b3ae404d2907e3ebb0f1167d15f85 Mon Sep 17 00:00:00 2001 From: Brent Doil Date: Tue, 30 Jan 2024 19:35:46 -0500 Subject: [PATCH 31/32] pg_dumpall: Fix syntax error when dumping RGs --- src/bin/pg_dump/pg_dumpall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 074505a2852..73a898fb220 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -932,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); } From 5d34e1293235dac8e15d7d3f162fa8ae22bcb048 Mon Sep 17 00:00:00 2001 From: Adam Lee Date: Mon, 18 Mar 2024 14:57:28 +0800 Subject: [PATCH 32/32] Remove a fixme referring selectSourceSchema() selectSourceSchema() was removed, remove the fixme but keep the comment since it's still valid. --- src/bin/pg_dump/pg_dump.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 0d51ed45015..23757c62573 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15781,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++)