From f11201365006ced5d7708429fec91b5fbb1ca24f Mon Sep 17 00:00:00 2001 From: Mark Dilger Date: Tue, 14 Jul 2020 15:13:01 -0700 Subject: [PATCH v3] Refactoring relkind handling Cleaning up the #define RELKIND_XXX stuff using a new RelKind enum and special macros while keeping the relkind fields as type 'char'. --- contrib/oid2name/oid2name.c | 18 +- contrib/pageinspect/rawpage.c | 65 +- contrib/pg_visibility/pg_visibility.c | 26 +- contrib/pgrowlocks/pgrowlocks.c | 36 +- contrib/pgstattuple/pgstatapprox.c | 26 +- contrib/pgstattuple/pgstatindex.c | 28 +- contrib/pgstattuple/pgstattuple.c | 8 +- contrib/postgres_fdw/postgres_fdw.c | 10 +- contrib/sepgsql/dml.c | 52 +- contrib/sepgsql/label.c | 49 +- contrib/sepgsql/relation.c | 380 ++- contrib/vacuumlo/vacuumlo.c | 2 +- src/backend/access/common/reloptions.c | 20 +- src/backend/access/heap/heapam.c | 82 +- src/backend/access/heap/heapam_handler.c | 26 +- src/backend/access/heap/heaptoast.c | 38 +- src/backend/access/index/indexam.c | 25 +- src/backend/access/table/table.c | 108 +- src/backend/access/table/tableam.c | 53 +- src/backend/catalog/aclchk.c | 403 ++- src/backend/catalog/dependency.c | 44 +- src/backend/catalog/heap.c | 513 ++-- src/backend/catalog/index.c | 103 +- src/backend/catalog/objectaddress.c | 182 +- src/backend/catalog/partition.c | 20 +- src/backend/catalog/pg_depend.c | 53 +- src/backend/catalog/pg_publication.c | 51 +- src/backend/catalog/pg_type.c | 21 +- src/backend/catalog/toasting.c | 43 +- src/backend/commands/analyze.c | 212 +- src/backend/commands/cluster.c | 148 +- src/backend/commands/comment.c | 30 +- src/backend/commands/copy.c | 203 +- src/backend/commands/extension.c | 4 +- src/backend/commands/indexcmds.c | 188 +- src/backend/commands/lockcmds.c | 95 +- src/backend/commands/policy.c | 72 +- src/backend/commands/publicationcmds.c | 67 +- src/backend/commands/seclabel.c | 29 +- src/backend/commands/sequence.c | 27 +- src/backend/commands/statscmds.c | 27 +- src/backend/commands/tablecmds.c | 2483 +++++++++++------ src/backend/commands/trigger.c | 435 ++- src/backend/commands/typecmds.c | 19 +- src/backend/commands/vacuum.c | 97 +- src/backend/executor/execMain.c | 117 +- src/backend/executor/execReplication.c | 43 +- src/backend/executor/nodeModifyTable.c | 156 +- src/backend/optimizer/path/allpaths.c | 169 +- src/backend/optimizer/plan/planner.c | 138 +- src/backend/optimizer/util/inherit.c | 243 +- src/backend/optimizer/util/plancat.c | 72 +- src/backend/parser/parse_utilcmd.c | 178 +- src/backend/partitioning/partbounds.c | 47 +- src/backend/postmaster/autovacuum.c | 88 +- src/backend/replication/basebackup.c | 6 +- .../replication/logical/reorderbuffer.c | 18 +- src/backend/replication/logical/tablesync.c | 45 +- src/backend/replication/logical/worker.c | 158 +- src/backend/replication/slot.c | 52 +- src/backend/rewrite/rewriteDefine.c | 179 +- src/backend/rewrite/rewriteHandler.c | 462 +-- src/backend/rewrite/rowsecurity.c | 19 +- src/backend/storage/buffer/bufmgr.c | 12 +- src/backend/storage/lmgr/proc.c | 27 +- src/backend/storage/lmgr/spin.c | 2 +- src/backend/tcop/utility.c | 85 +- src/backend/utils/adt/amutils.c | 19 +- src/backend/utils/adt/dbsize.c | 14 +- src/backend/utils/adt/partitionfuncs.c | 40 +- src/backend/utils/adt/tid.c | 36 +- src/backend/utils/adt/xml.c | 12 +- src/backend/utils/cache/partcache.c | 18 +- src/backend/utils/cache/relcache.c | 338 ++- src/bin/initdb/initdb.c | 20 +- src/bin/pg_dump/common.c | 40 +- src/bin/pg_dump/pg_backup_tar.c | 2 +- src/bin/pg_dump/pg_dump.c | 184 +- src/bin/pg_dump/pg_dump_sort.c | 180 +- src/bin/pg_upgrade/info.c | 4 +- src/bin/pg_upgrade/pg_upgrade.c | 12 +- src/bin/pg_upgrade/version.c | 6 +- src/bin/psql/command.c | 21 +- src/bin/psql/describe.c | 2266 ++++++++------- src/bin/psql/tab-complete.c | 66 +- src/bin/scripts/reindexdb.c | 8 +- src/bin/scripts/vacuumdb.c | 4 +- src/include/catalog/pg_class.h | 48 +- src/include/nodes/execnodes.h | 4 +- src/include/storage/proc.h | 2 +- src/include/utils/builtins.h | 10 +- src/pl/plpgsql/src/pl_comp.c | 24 +- src/test/regress/regress.c | 22 +- src/tools/findoidjoins/findoidjoins.c | 6 +- 94 files changed, 8020 insertions(+), 4323 deletions(-) diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c index c7d0f9025a..cff14f7c47 100644 --- a/contrib/oid2name/oid2name.c +++ b/contrib/oid2name/oid2name.c @@ -481,8 +481,8 @@ sql_exec_dumpalltables(PGconn *conn, struct options *opts) " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " " LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database()," " pg_catalog.pg_tablespace t " - "WHERE relkind IN (" CppAsString2(RELKIND_RELATION) "," - CppAsString2(RELKIND_MATVIEW) "%s%s) AND " + "WHERE relkind IN (" RelKindAsString(RELKIND_RELATION) "," + RelKindAsString(RELKIND_MATVIEW) "%s%s) AND " " %s" " t.oid = CASE" " WHEN reltablespace <> 0 THEN reltablespace" @@ -490,8 +490,8 @@ sql_exec_dumpalltables(PGconn *conn, struct options *opts) " END " "ORDER BY relname", opts->extended ? addfields : "", - opts->indexes ? "," CppAsString2(RELKIND_INDEX) "," CppAsString2(RELKIND_SEQUENCE) : "", - opts->systables ? "," CppAsString2(RELKIND_TOASTVALUE) : "", + opts->indexes ? "," RelKindAsString(RELKIND_INDEX) "," RelKindAsString(RELKIND_SEQUENCE) : "", + opts->systables ? "," RelKindAsString(RELKIND_TOASTVALUE) : "", opts->systables ? "" : "n.nspname NOT IN ('pg_catalog', 'information_schema') AND n.nspname !~ '^pg_toast' AND"); sql_exec(conn, todo, opts->quiet); @@ -551,11 +551,11 @@ sql_exec_searchtables(PGconn *conn, struct options *opts) " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" " LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database(),\n" " pg_catalog.pg_tablespace t\n" - "WHERE relkind IN (" CppAsString2(RELKIND_RELATION) "," - CppAsString2(RELKIND_MATVIEW) "," - CppAsString2(RELKIND_INDEX) "," - CppAsString2(RELKIND_SEQUENCE) "," - CppAsString2(RELKIND_TOASTVALUE) ") AND\n" + "WHERE relkind IN (" RelKindAsString(RELKIND_RELATION) "," + RelKindAsString(RELKIND_MATVIEW) "," + RelKindAsString(RELKIND_INDEX) "," + RelKindAsString(RELKIND_SEQUENCE) "," + RelKindAsString(RELKIND_TOASTVALUE) ") AND\n" " t.oid = CASE\n" " WHEN reltablespace <> 0 THEN reltablespace\n" " ELSE dattablespace\n" diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c index c0181506a5..b455f95e1a 100644 --- a/contrib/pageinspect/rawpage.c +++ b/contrib/pageinspect/rawpage.c @@ -108,31 +108,46 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno) rel = relation_openrv(relrv, AccessShareLock); /* Check that this relation has storage */ - if (rel->rd_rel->relkind == RELKIND_VIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot get raw page from view \"%s\"", - RelationGetRelationName(rel)))); - if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot get raw page from composite type \"%s\"", - RelationGetRelationName(rel)))); - if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot get raw page from foreign table \"%s\"", - RelationGetRelationName(rel)))); - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot get raw page from partitioned table \"%s\"", - RelationGetRelationName(rel)))); - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot get raw page from partitioned index \"%s\"", - RelationGetRelationName(rel)))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from view \"%s\"", + RelationGetRelationName(rel)))); + break; + case RELKIND_COMPOSITE_TYPE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from composite type \"%s\"", + RelationGetRelationName(rel)))); + break; + case RELKIND_FOREIGN_TABLE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from foreign table \"%s\"", + RelationGetRelationName(rel)))); + break; + case RELKIND_PARTITIONED_TABLE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from partitioned table \"%s\"", + RelationGetRelationName(rel)))); + break; + case RELKIND_PARTITIONED_INDEX: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from partitioned index \"%s\"", + RelationGetRelationName(rel)))); + break; + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; + } /* * Reject attempts to read non-local temporary relations; we would be diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c index 68d580ed1e..eb722801bb 100644 --- a/contrib/pg_visibility/pg_visibility.c +++ b/contrib/pg_visibility/pg_visibility.c @@ -778,11 +778,23 @@ tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer) static void check_relation_relkind(Relation rel) { - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_MATVIEW && - rel->rd_rel->relkind != RELKIND_TOASTVALUE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, materialized view, or TOAST table", - RelationGetRelationName(rel)))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, materialized view, or TOAST table", + RelationGetRelationName(rel)))); + } } diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c index 714398831b..11a386f8ef 100644 --- a/contrib/pgrowlocks/pgrowlocks.c +++ b/contrib/pgrowlocks/pgrowlocks.c @@ -111,17 +111,31 @@ pgrowlocks(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("only heap AM is supported"))); - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a partitioned table", - RelationGetRelationName(rel)), - errdetail("Partitioned tables do not contain rows."))); - else if (rel->rd_rel->relkind != RELKIND_RELATION) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table", - RelationGetRelationName(rel)))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + break; + case RELKIND_PARTITIONED_TABLE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a partitioned table", + RelationGetRelationName(rel)), + errdetail("Partitioned tables do not contain rows."))); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", + RelationGetRelationName(rel)))); + } /* * check permissions: must have SELECT on table or be in diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c index dbc0fa11f6..c4e5de69b5 100644 --- a/contrib/pgstattuple/pgstatapprox.c +++ b/contrib/pgstattuple/pgstatapprox.c @@ -281,13 +281,25 @@ pgstattuple_approx_internal(Oid relid, FunctionCallInfo fcinfo) * We support only relation kinds with a visibility map and a free space * map. */ - if (!(rel->rd_rel->relkind == RELKIND_RELATION || - rel->rd_rel->relkind == RELKIND_MATVIEW || - rel->rd_rel->relkind == RELKIND_TOASTVALUE)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("\"%s\" is not a table, materialized view, or TOAST table", - RelationGetRelationName(rel)))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("\"%s\" is not a table, materialized view, or TOAST table", + RelationGetRelationName(rel)))); + } if (rel->rd_rel->relam != HEAP_TABLE_AM_OID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c index b1ce0d77d7..f2f3531c82 100644 --- a/contrib/pgstattuple/pgstatindex.c +++ b/contrib/pgstattuple/pgstatindex.c @@ -758,13 +758,23 @@ GetHashPageStats(Page page, HashIndexStat *stats) static void check_relation_relkind(Relation rel) { - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_INDEX && - rel->rd_rel->relkind != RELKIND_MATVIEW && - rel->rd_rel->relkind != RELKIND_SEQUENCE && - rel->rd_rel->relkind != RELKIND_TOASTVALUE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, index, materialized view, sequence, or TOAST table", - RelationGetRelationName(rel)))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, index, materialized view, sequence, or TOAST table", + RelationGetRelationName(rel)))); + } } diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c index 69179d4104..576a3aaf4e 100644 --- a/contrib/pgstattuple/pgstattuple.c +++ b/contrib/pgstattuple/pgstattuple.c @@ -252,7 +252,8 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); - switch (rel->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { case RELKIND_RELATION: case RELKIND_MATVIEW: @@ -282,7 +283,6 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo) break; default: err = "unknown index"; - break; } break; case RELKIND_VIEW: @@ -299,10 +299,6 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo) break; case RELKIND_PARTITIONED_INDEX: err = "partitioned index"; - break; - default: - err = "unknown"; - break; } ereport(ERROR, diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 9fc53cad68..77e28757ef 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -4842,11 +4842,11 @@ postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid) appendStringInfoString(&buf, "WHERE c.relkind IN (" - CppAsString2(RELKIND_RELATION) "," - CppAsString2(RELKIND_VIEW) "," - CppAsString2(RELKIND_FOREIGN_TABLE) "," - CppAsString2(RELKIND_MATVIEW) "," - CppAsString2(RELKIND_PARTITIONED_TABLE) ") " + RelKindAsString(RELKIND_RELATION) "," + RelKindAsString(RELKIND_VIEW) "," + RelKindAsString(RELKIND_FOREIGN_TABLE) "," + RelKindAsString(RELKIND_MATVIEW) "," + RelKindAsString(RELKIND_PARTITIONED_TABLE) ") " " AND n.nspname = "); deparseStringLiteral(&buf, stmt->remote_schema); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 53f6f41c5c..bf5de226e7 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -167,10 +167,26 @@ check_relation_privileges(Oid relOid, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("SELinux: hardwired security policy violation"))); - if (relkind == RELKIND_TOASTVALUE) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("SELinux: hardwired security policy violation"))); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("SELinux: hardwired security policy violation"))); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_VIEW: + break; + } + } /* @@ -180,7 +196,8 @@ check_relation_privileges(Oid relOid, object.objectId = relOid; object.objectSubId = 0; audit_name = getObjectIdentity(&object); - switch (relkind) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { case RELKIND_RELATION: case RELKIND_PARTITIONED_TABLE: @@ -210,7 +227,12 @@ check_relation_privileges(Oid relOid, abort_on_violation); break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: /* nothing to be checked */ break; } @@ -219,8 +241,22 @@ check_relation_privileges(Oid relOid, /* * Only columns owned by relations shall be checked */ - if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) - return true; + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + return true; + } /* * Check permissions on the columns diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c index 147ab67f32..f272662ddf 100644 --- a/contrib/sepgsql/label.c +++ b/contrib/sepgsql/label.c @@ -763,15 +763,27 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId) case RelationRelationId: relForm = (Form_pg_class) GETSTRUCT(tuple); - if (relForm->relkind == RELKIND_RELATION || - relForm->relkind == RELKIND_PARTITIONED_TABLE) - objtype = SELABEL_DB_TABLE; - else if (relForm->relkind == RELKIND_SEQUENCE) - objtype = SELABEL_DB_SEQUENCE; - else if (relForm->relkind == RELKIND_VIEW) - objtype = SELABEL_DB_VIEW; - else - continue; /* no need to assign security label */ + Assert(RELKIND_IS_VALID((RelKind) relForm->relkind)); + switch ((RelKind) relForm->relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + objtype = SELABEL_DB_TABLE; + break; + case RELKIND_SEQUENCE: + objtype = SELABEL_DB_SEQUENCE; + break; + case RELKIND_VIEW: + objtype = SELABEL_DB_VIEW; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + continue; /* no need to assign security label */ + } namespace_name = get_namespace_name(relForm->relnamespace); objname = quote_object_name(database_name, @@ -788,9 +800,22 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId) case AttributeRelationId: attForm = (Form_pg_attribute) GETSTRUCT(tuple); - if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION && - get_rel_relkind(attForm->attrelid) != RELKIND_PARTITIONED_TABLE) - continue; /* no need to assign security label */ + Assert(RELKIND_IS_VALID((RelKind) get_rel_relkind(attForm->attrelid))); + switch ((RelKind) get_rel_relkind(attForm->attrelid)) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + continue; /* no need to assign security label */ + } objtype = SELABEL_DB_COLUMN; diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c index 380bc6094d..b3fc647ce2 100644 --- a/contrib/sepgsql/relation.c +++ b/contrib/sepgsql/relation.c @@ -59,8 +59,22 @@ sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum) * Only attributes within regular relations or partition relations have * individual security labels. */ - if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) - return; + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + return; + } /* * Compute a default security label of the new column underlying the @@ -137,8 +151,22 @@ sepgsql_attribute_drop(Oid relOid, AttrNumber attnum) char *audit_name; char relkind = get_rel_relkind(relOid); - if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) - return; + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + return; + } /* * check db_column:{drop} permission @@ -170,10 +198,24 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, char *audit_name; char relkind = get_rel_relkind(relOid); - if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot set security label on non-regular columns"))); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot set security label on non-regular columns"))); + } object.classId = RelationRelationId; object.objectId = relOid; @@ -213,8 +255,24 @@ sepgsql_attribute_setattr(Oid relOid, AttrNumber attnum) char *audit_name; char relkind = get_rel_relkind(relOid); - if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) - return; + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + return; + } /* * check db_column:{setattr} permission @@ -275,9 +333,25 @@ sepgsql_relation_post_create(Oid relOid) classForm = (Form_pg_class) GETSTRUCT(tuple); /* ignore indexes on toast tables */ - if (classForm->relkind == RELKIND_INDEX && - classForm->relnamespace == PG_TOAST_NAMESPACE) - goto out; + Assert(RELKIND_IS_VALID((RelKind) classForm->relkind)); + switch ((RelKind) classForm->relkind) + { + case RELKIND_INDEX: + if (classForm->relnamespace == PG_TOAST_NAMESPACE) + goto out; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } + /* * check db_schema:{add_name} permission of the namespace @@ -291,7 +365,8 @@ sepgsql_relation_post_create(Oid relOid) getObjectIdentity(&object), true); - switch (classForm->relkind) + Assert(RELKIND_IS_VALID((RelKind) classForm->relkind)); + switch ((RelKind) classForm->relkind) { case RELKIND_RELATION: case RELKIND_PARTITIONED_TABLE: @@ -307,7 +382,11 @@ sepgsql_relation_post_create(Oid relOid) /* deal with indexes specially; no need for tclass */ sepgsql_index_modify(relOid); goto out; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: /* ignore other relkinds */ goto out; } @@ -348,59 +427,74 @@ sepgsql_relation_post_create(Oid relOid) /* * We also assign a default security label on columns of a new table. */ - if (classForm->relkind == RELKIND_RELATION || - classForm->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) classForm->relkind)); + switch ((RelKind) classForm->relkind) { - Relation arel; - ScanKeyData akey; - SysScanDesc ascan; - HeapTuple atup; - Form_pg_attribute attForm; - - arel = table_open(AttributeRelationId, AccessShareLock); - - ScanKeyInit(&akey, - Anum_pg_attribute_attrelid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relOid)); - - ascan = systable_beginscan(arel, AttributeRelidNumIndexId, true, - SnapshotSelf, 1, &akey); - - while (HeapTupleIsValid(atup = systable_getnext(ascan))) - { - attForm = (Form_pg_attribute) GETSTRUCT(atup); - - resetStringInfo(&audit_name); - appendStringInfo(&audit_name, "%s.%s.%s", - quote_identifier(nsp_name), - quote_identifier(NameStr(classForm->relname)), - quote_identifier(NameStr(attForm->attname))); - - ccontext = sepgsql_compute_create(scontext, - rcontext, - SEPG_CLASS_DB_COLUMN, - NameStr(attForm->attname)); - - /* - * check db_column:{create} permission - */ - sepgsql_avc_check_perms_label(ccontext, - SEPG_CLASS_DB_COLUMN, - SEPG_DB_COLUMN__CREATE, - audit_name.data, - true); - - object.classId = RelationRelationId; - object.objectId = relOid; - object.objectSubId = attForm->attnum; - SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext); - - pfree(ccontext); - } - systable_endscan(ascan); - table_close(arel, AccessShareLock); + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + { + Relation arel; + ScanKeyData akey; + SysScanDesc ascan; + HeapTuple atup; + Form_pg_attribute attForm; + + arel = table_open(AttributeRelationId, AccessShareLock); + + ScanKeyInit(&akey, + Anum_pg_attribute_attrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relOid)); + + ascan = systable_beginscan(arel, AttributeRelidNumIndexId, true, + SnapshotSelf, 1, &akey); + + while (HeapTupleIsValid(atup = systable_getnext(ascan))) + { + attForm = (Form_pg_attribute) GETSTRUCT(atup); + + resetStringInfo(&audit_name); + appendStringInfo(&audit_name, "%s.%s.%s", + quote_identifier(nsp_name), + quote_identifier(NameStr(classForm->relname)), + quote_identifier(NameStr(attForm->attname))); + + ccontext = sepgsql_compute_create(scontext, + rcontext, + SEPG_CLASS_DB_COLUMN, + NameStr(attForm->attname)); + + /* + * check db_column:{create} permission + */ + sepgsql_avc_check_perms_label(ccontext, + SEPG_CLASS_DB_COLUMN, + SEPG_DB_COLUMN__CREATE, + audit_name.data, + true); + + object.classId = RelationRelationId; + object.objectId = relOid; + object.objectSubId = attForm->attnum; + SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext); + + pfree(ccontext); + } + systable_endscan(ascan); + table_close(arel, AccessShareLock); + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } + pfree(rcontext); out: @@ -421,7 +515,8 @@ sepgsql_relation_drop(Oid relOid) uint16_t tclass = 0; char relkind = get_rel_relkind(relOid); - switch (relkind) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { case RELKIND_RELATION: case RELKIND_PARTITIONED_TABLE: @@ -439,7 +534,11 @@ sepgsql_relation_drop(Oid relOid) return; /* other indexes are handled specially below; no need for tclass */ break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: /* ignore other relkinds */ return; } @@ -460,10 +559,22 @@ sepgsql_relation_drop(Oid relOid) pfree(audit_name); /* deal with indexes specially */ - if (relkind == RELKIND_INDEX) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - sepgsql_index_modify(relOid); - return; + case RELKIND_INDEX: + sepgsql_index_modify(relOid); + return; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } /* @@ -484,35 +595,50 @@ sepgsql_relation_drop(Oid relOid) /* * check db_column:{drop} permission */ - if (relkind == RELKIND_RELATION || relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - Form_pg_attribute attForm; - CatCList *attrList; - HeapTuple atttup; - int i; - - attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid)); - for (i = 0; i < attrList->n_members; i++) - { - atttup = &attrList->members[i]->tuple; - attForm = (Form_pg_attribute) GETSTRUCT(atttup); - - if (attForm->attisdropped) - continue; - - object.classId = RelationRelationId; - object.objectId = relOid; - object.objectSubId = attForm->attnum; - audit_name = getObjectIdentity(&object); - - sepgsql_avc_check_perms(&object, - SEPG_CLASS_DB_COLUMN, - SEPG_DB_COLUMN__DROP, - audit_name, - true); - pfree(audit_name); - } - ReleaseCatCacheList(attrList); + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + { + Form_pg_attribute attForm; + CatCList *attrList; + HeapTuple atttup; + int i; + + attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid)); + for (i = 0; i < attrList->n_members; i++) + { + atttup = &attrList->members[i]->tuple; + attForm = (Form_pg_attribute) GETSTRUCT(atttup); + + if (attForm->attisdropped) + continue; + + object.classId = RelationRelationId; + object.objectId = relOid; + object.objectSubId = attForm->attnum; + audit_name = getObjectIdentity(&object); + + sepgsql_avc_check_perms(&object, + SEPG_CLASS_DB_COLUMN, + SEPG_DB_COLUMN__DROP, + audit_name, + true); + pfree(audit_name); + } + ReleaseCatCacheList(attrList); + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } } @@ -529,13 +655,21 @@ sepgsql_relation_truncate(Oid relOid) uint16_t tclass = 0; char relkind = get_rel_relkind(relOid); - switch (relkind) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { case RELKIND_RELATION: case RELKIND_PARTITIONED_TABLE: tclass = SEPG_CLASS_DB_TABLE; break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: /* ignore other relkinds */ return; } @@ -569,17 +703,30 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel) char relkind = get_rel_relkind(relOid); uint16_t tclass = 0; - if (relkind == RELKIND_RELATION || relkind == RELKIND_PARTITIONED_TABLE) - tclass = SEPG_CLASS_DB_TABLE; - else if (relkind == RELKIND_SEQUENCE) - tclass = SEPG_CLASS_DB_SEQUENCE; - else if (relkind == RELKIND_VIEW) - tclass = SEPG_CLASS_DB_VIEW; - else - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot set security labels on relations except " - "for tables, sequences or views"))); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + tclass = SEPG_CLASS_DB_TABLE; + break; + case RELKIND_SEQUENCE: + tclass = SEPG_CLASS_DB_SEQUENCE; + break; + case RELKIND_VIEW: + tclass = SEPG_CLASS_DB_VIEW; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot set security labels on relations except " + "for tables, sequences or views"))); + } object.classId = RelationRelationId; object.objectId = relOid; @@ -626,7 +773,8 @@ sepgsql_relation_setattr(Oid relOid) char *audit_name; uint16_t tclass; - switch (get_rel_relkind(relOid)) + Assert(RELKIND_IS_VALID((RelKind) get_rel_relkind(relOid))); + switch ((RelKind) get_rel_relkind(relOid)) { case RELKIND_RELATION: case RELKIND_PARTITIONED_TABLE: @@ -642,7 +790,11 @@ sepgsql_relation_setattr(Oid relOid) /* deal with indexes specially */ sepgsql_index_modify(relOid); return; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: /* other relkinds don't need additional work */ return; } diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c index 92bdf71356..e07f8be8e3 100644 --- a/contrib/vacuumlo/vacuumlo.c +++ b/contrib/vacuumlo/vacuumlo.c @@ -203,7 +203,7 @@ vacuumlo(const char *database, const struct _param *param) strcat(buf, " AND a.atttypid = t.oid "); strcat(buf, " AND c.relnamespace = s.oid "); strcat(buf, " AND t.typname in ('oid', 'lo') "); - strcat(buf, " AND c.relkind in (" CppAsString2(RELKIND_RELATION) ", " CppAsString2(RELKIND_MATVIEW) ")"); + strcat(buf, " AND c.relkind in (" RelKindAsString(RELKIND_RELATION) ", " RelKindAsString(RELKIND_MATVIEW) ")"); strcat(buf, " AND s.nspname !~ '^pg_'"); res = PQexec(conn, buf); if (PQresultStatus(res) != PGRES_TUPLES_OK) diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index 8ccc228a8c..979a8f5a04 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -1372,7 +1372,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, classForm = (Form_pg_class) GETSTRUCT(tuple); /* Parse into appropriate format; don't error out here */ - switch (classForm->relkind) + Assert(RELKIND_IS_VALID((RelKind) classForm->relkind)); + switch ((RelKind) classForm->relkind) { case RELKIND_RELATION: case RELKIND_TOASTVALUE: @@ -1392,10 +1393,10 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, case RELKIND_FOREIGN_TABLE: options = NULL; break; - default: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: Assert(false); /* can't get here */ options = NULL; /* keep compiler quiet */ - break; } return options; @@ -1997,7 +1998,8 @@ heap_reloptions(char relkind, Datum reloptions, bool validate) { StdRdOptions *rdopts; - switch (relkind) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { case RELKIND_TOASTVALUE: rdopts = (StdRdOptions *) @@ -2013,13 +2015,19 @@ heap_reloptions(char relkind, Datum reloptions, bool validate) case RELKIND_RELATION: case RELKIND_MATVIEW: return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP); - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: /* other relkinds are not supported */ return NULL; } + return NULL; /* keep compiler happy */ } - /* * Parse options for indexes. * diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index d881f4cd46..ed5355ada3 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -2040,17 +2040,27 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid, * If the new tuple is too big for storage or contains already toasted * out-of-line attributes from some other relation, invoke the toaster. */ - if (relation->rd_rel->relkind != RELKIND_RELATION && - relation->rd_rel->relkind != RELKIND_MATVIEW) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { - /* toast table entries should never be recursively toasted */ - Assert(!HeapTupleHasExternal(tup)); - return tup; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD) + return heap_toast_insert_or_update(relation, tup, NULL, options); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + /* toast table entries should never be recursively toasted */ + Assert(!HeapTupleHasExternal(tup)); + break; } - else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD) - return heap_toast_insert_or_update(relation, tup, NULL, options); - else - return tup; + return tup; } /* @@ -2773,14 +2783,26 @@ l1: * because we need to look at the contents of the tuple, but it's OK to * release the content lock on the buffer first. */ - if (relation->rd_rel->relkind != RELKIND_RELATION && - relation->rd_rel->relkind != RELKIND_MATVIEW) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { - /* toast table entries should never be recursively toasted */ - Assert(!HeapTupleHasExternal(&tp)); + case RELKIND_RELATION: + case RELKIND_MATVIEW: + if (HeapTupleHasExternal(&tp)) + heap_toast_delete(relation, &tp, false); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + /* toast table entries should never be recursively toasted */ + Assert(!HeapTupleHasExternal(&tp)); + break; } - else if (HeapTupleHasExternal(&tp)) - heap_toast_delete(relation, &tp, false); /* * Mark tuple for invalidation from system caches at next command @@ -3362,18 +3384,28 @@ l2: * We need to invoke the toaster if there are already any out-of-line * toasted values present, or if the new tuple is over-threshold. */ - if (relation->rd_rel->relkind != RELKIND_RELATION && - relation->rd_rel->relkind != RELKIND_MATVIEW) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { - /* toast table entries should never be recursively toasted */ - Assert(!HeapTupleHasExternal(&oldtup)); - Assert(!HeapTupleHasExternal(newtup)); - need_toast = false; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + need_toast = (HeapTupleHasExternal(&oldtup) || + HeapTupleHasExternal(newtup) || + newtup->t_len > TOAST_TUPLE_THRESHOLD); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + /* toast table entries should never be recursively toasted */ + Assert(!HeapTupleHasExternal(&oldtup)); + Assert(!HeapTupleHasExternal(newtup)); + need_toast = false; } - else - need_toast = (HeapTupleHasExternal(&oldtup) || - HeapTupleHasExternal(newtup) || - newtup->t_len > TOAST_TUPLE_THRESHOLD); pagefree = PageGetHeapFreeSpace(page); diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 56b35622f1..bb8f6197c8 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -600,12 +600,26 @@ heapam_relation_set_new_filenode(Relation rel, */ if (persistence == RELPERSISTENCE_UNLOGGED) { - Assert(rel->rd_rel->relkind == RELKIND_RELATION || - rel->rd_rel->relkind == RELKIND_MATVIEW || - rel->rd_rel->relkind == RELKIND_TOASTVALUE); - smgrcreate(srel, INIT_FORKNUM, false); - log_smgrcreate(newrnode, INIT_FORKNUM); - smgrimmedsync(srel, INIT_FORKNUM); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + smgrcreate(srel, INIT_FORKNUM, false); + log_smgrcreate(newrnode, INIT_FORKNUM); + smgrimmedsync(srel, INIT_FORKNUM); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: + Assert(false); + break; + } } smgrclose(srel); diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 584f101dd9..764f6a41d7 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -50,8 +50,23 @@ heap_toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative) * We should only ever be called for tuples of plain relations or * materialized views --- recursing on a toast rel is bad news. */ - Assert(rel->rd_rel->relkind == RELKIND_RELATION || - rel->rd_rel->relkind == RELKIND_MATVIEW); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + Assert(false); + break; + } /* * Get the tuple descriptor and break down the tuple into fields. @@ -122,8 +137,23 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, * We should only ever be called for tuples of plain relations or * materialized views --- recursing on a toast rel is bad news. */ - Assert(rel->rd_rel->relkind == RELKIND_RELATION || - rel->rd_rel->relkind == RELKIND_MATVIEW); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + Assert(false); + break; + } /* * Get the tuple descriptor and break down the tuple(s) into fields. diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 6b9750c244..644dc17045 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -135,12 +135,25 @@ index_open(Oid relationId, LOCKMODE lockmode) r = relation_open(relationId, lockmode); - if (r->rd_rel->relkind != RELKIND_INDEX && - r->rd_rel->relkind != RELKIND_PARTITIONED_INDEX) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not an index", - RelationGetRelationName(r)))); + Assert(RELKIND_IS_VALID((RelKind) r->rd_rel->relkind)); + switch ((RelKind) r->rd_rel->relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not an index", + RelationGetRelationName(r)))); + } return r; } diff --git a/src/backend/access/table/table.c b/src/backend/access/table/table.c index 1aa01a54b3..623d451ee1 100644 --- a/src/backend/access/table/table.c +++ b/src/backend/access/table/table.c @@ -42,17 +42,31 @@ table_open(Oid relationId, LOCKMODE lockmode) r = relation_open(relationId, lockmode); - if (r->rd_rel->relkind == RELKIND_INDEX || - r->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is an index", - RelationGetRelationName(r)))); - else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a composite type", - RelationGetRelationName(r)))); + Assert(RELKIND_IS_VALID((RelKind) r->rd_rel->relkind)); + switch ((RelKind) r->rd_rel->relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is an index", + RelationGetRelationName(r)))); + break; + case RELKIND_COMPOSITE_TYPE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a composite type", + RelationGetRelationName(r)))); + break; + case RELKIND_SEQUENCE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } return r; } @@ -71,17 +85,31 @@ table_openrv(const RangeVar *relation, LOCKMODE lockmode) r = relation_openrv(relation, lockmode); - if (r->rd_rel->relkind == RELKIND_INDEX || - r->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is an index", - RelationGetRelationName(r)))); - else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a composite type", - RelationGetRelationName(r)))); + Assert(RELKIND_IS_VALID((RelKind) r->rd_rel->relkind)); + switch ((RelKind) r->rd_rel->relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is an index", + RelationGetRelationName(r)))); + break; + case RELKIND_COMPOSITE_TYPE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a composite type", + RelationGetRelationName(r)))); + break; + case RELKIND_SEQUENCE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } return r; } @@ -104,17 +132,31 @@ table_openrv_extended(const RangeVar *relation, LOCKMODE lockmode, if (r) { - if (r->rd_rel->relkind == RELKIND_INDEX || - r->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is an index", - RelationGetRelationName(r)))); - else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a composite type", - RelationGetRelationName(r)))); + Assert(RELKIND_IS_VALID((RelKind) r->rd_rel->relkind)); + switch ((RelKind) r->rd_rel->relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is an index", + RelationGetRelationName(r)))); + break; + case RELKIND_COMPOSITE_TYPE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a composite type", + RelationGetRelationName(r)))); + break; + case RELKIND_SEQUENCE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } } return r; diff --git a/src/backend/access/table/tableam.c b/src/backend/access/table/tableam.c index 4b2bb29559..1ea399ba8f 100644 --- a/src/backend/access/table/tableam.c +++ b/src/backend/access/table/tableam.c @@ -47,27 +47,42 @@ table_slot_callbacks(Relation relation) if (relation->rd_tableam) tts_cb = relation->rd_tableam->slot_callbacks(relation); - else if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - { - /* - * Historically FDWs expect to store heap tuples in slots. Continue - * handing them one, to make it less painful to adapt FDWs to new - * versions. The cost of a heap slot over a virtual slot is pretty - * small. - */ - tts_cb = &TTSOpsHeapTuple; - } else { - /* - * These need to be supported, as some parts of the code (like COPY) - * need to create slots for such relations too. It seems better to - * centralize the knowledge that a heap slot is the right thing in - * that case here. - */ - Assert(relation->rd_rel->relkind == RELKIND_VIEW || - relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); - tts_cb = &TTSOpsVirtual; + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_FOREIGN_TABLE: + + /* + * Historically FDWs expect to store heap tuples in slots. + * Continue handing them one, to make it less painful to adapt + * FDWs to new versions. The cost of a heap slot over a + * virtual slot is pretty small. + */ + tts_cb = &TTSOpsHeapTuple; + break; + case RELKIND_VIEW: + case RELKIND_PARTITIONED_TABLE: + + /* + * These need to be supported, as some parts of the code (like + * COPY) need to create slots for such relations too. It seems + * better to centralize the knowledge that a heap slot is the + * right thing in that case here. + */ + tts_cb = &TTSOpsVirtual; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + Assert(false); + break; + } } return tts_cb; diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index c626161408..ac08a4924a 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -1765,36 +1765,61 @@ ExecGrant_Relation(InternalGrant *istmt) elog(ERROR, "cache lookup failed for relation %u", relOid); pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple); - /* Not sensible to grant on an index */ - if (pg_class_tuple->relkind == RELKIND_INDEX || - pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is an index", - NameStr(pg_class_tuple->relname)))); + Assert(RELKIND_IS_VALID((RelKind) pg_class_tuple->relkind)); + switch ((RelKind) pg_class_tuple->relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + /* Not sensible to grant on an index */ + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is an index", + NameStr(pg_class_tuple->relname)))); - /* Composite types aren't tables either */ - if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a composite type", - NameStr(pg_class_tuple->relname)))); + case RELKIND_COMPOSITE_TYPE: + /* Composite types aren't tables either */ + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a composite type", + NameStr(pg_class_tuple->relname)))); + + case RELKIND_SEQUENCE: + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + /* Used GRANT SEQUENCE on a non-sequence? */ + if (istmt->objtype == OBJECT_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + NameStr(pg_class_tuple->relname)))); + } - /* Used GRANT SEQUENCE on a non-sequence? */ - if (istmt->objtype == OBJECT_SEQUENCE && - pg_class_tuple->relkind != RELKIND_SEQUENCE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a sequence", - NameStr(pg_class_tuple->relname)))); /* Adjust the default permissions based on object type */ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) { - if (pg_class_tuple->relkind == RELKIND_SEQUENCE) - this_privileges = ACL_ALL_RIGHTS_SEQUENCE; - else - this_privileges = ACL_ALL_RIGHTS_RELATION; + Assert(RELKIND_IS_VALID((RelKind) pg_class_tuple->relkind)); + switch ((RelKind) pg_class_tuple->relkind) + { + case RELKIND_SEQUENCE: + this_privileges = ACL_ALL_RIGHTS_SEQUENCE; + break; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + this_privileges = ACL_ALL_RIGHTS_RELATION; + } } else this_privileges = istmt->privileges; @@ -1807,42 +1832,52 @@ ExecGrant_Relation(InternalGrant *istmt) */ if (istmt->objtype == OBJECT_TABLE) { - if (pg_class_tuple->relkind == RELKIND_SEQUENCE) + Assert(RELKIND_IS_VALID((RelKind) pg_class_tuple->relkind)); + switch ((RelKind) pg_class_tuple->relkind) { - /* - * For backward compatibility, just throw a warning for - * invalid sequence permissions when using the non-sequence - * GRANT syntax. - */ - if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE)) - { - /* - * Mention the object name because the user needs to know - * which operations succeeded. This is required because - * WARNING allows the command to continue. - */ - ereport(WARNING, - (errcode(ERRCODE_INVALID_GRANT_OPERATION), - errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE privileges", - NameStr(pg_class_tuple->relname)))); - this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE; - } - } - else - { - if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION)) - { + case RELKIND_SEQUENCE: + /* - * USAGE is the only permission supported by sequences but - * not by non-sequences. Don't mention the object name - * because we didn't in the combined TABLE | SEQUENCE - * check. + * For backward compatibility, just throw a warning for + * invalid sequence permissions when using the + * non-sequence GRANT syntax. */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_GRANT_OPERATION), - errmsg("invalid privilege type %s for table", - "USAGE"))); - } + if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE)) + { + /* + * Mention the object name because the user needs to + * know which operations succeeded. This is required + * because WARNING allows the command to continue. + */ + ereport(WARNING, + (errcode(ERRCODE_INVALID_GRANT_OPERATION), + errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE privileges", + NameStr(pg_class_tuple->relname)))); + this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE; + } + break; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION)) + { + /* + * USAGE is the only permission supported by sequences + * but not by non-sequences. Don't mention the object + * name because we didn't in the combined TABLE | + * SEQUENCE check. + */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_GRANT_OPERATION), + errmsg("invalid privilege type %s for table", + "USAGE"))); + } } } @@ -1881,14 +1916,22 @@ ExecGrant_Relation(InternalGrant *istmt) &isNull); if (isNull) { - switch (pg_class_tuple->relkind) + Assert(RELKIND_IS_VALID((RelKind) pg_class_tuple->relkind)); + switch ((RelKind) pg_class_tuple->relkind) { case RELKIND_SEQUENCE: old_acl = acldefault(OBJECT_SEQUENCE, ownerId); break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: old_acl = acldefault(OBJECT_TABLE, ownerId); - break; } /* There are no old member roles according to the catalogs */ noldmembers = 0; @@ -1925,14 +1968,22 @@ ExecGrant_Relation(InternalGrant *istmt) old_acl, ownerId, &grantorId, &avail_goptions); - switch (pg_class_tuple->relkind) + Assert(RELKIND_IS_VALID((RelKind) pg_class_tuple->relkind)); + switch ((RelKind) pg_class_tuple->relkind) { case RELKIND_SEQUENCE: objtype = OBJECT_SEQUENCE; break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: objtype = OBJECT_TABLE; - break; } /* @@ -2009,20 +2060,35 @@ ExecGrant_Relation(InternalGrant *istmt) errmsg("invalid privilege type %s for column", privilege_to_string(this_privileges)))); - if (pg_class_tuple->relkind == RELKIND_SEQUENCE && - this_privileges & ~((AclMode) ACL_SELECT)) + Assert(RELKIND_IS_VALID((RelKind) pg_class_tuple->relkind)); + switch ((RelKind) pg_class_tuple->relkind) { - /* - * The only column privilege allowed on sequences is SELECT. - * This is a warning not error because we do it that way for - * relation-level privileges. - */ - ereport(WARNING, - (errcode(ERRCODE_INVALID_GRANT_OPERATION), - errmsg("sequence \"%s\" only supports SELECT column privileges", - NameStr(pg_class_tuple->relname)))); + case RELKIND_SEQUENCE: + if (this_privileges & ~((AclMode) ACL_SELECT)) + { + /* + * The only column privilege allowed on sequences is + * SELECT. This is a warning not error because we do + * it that way for relation-level privileges. + */ + ereport(WARNING, + (errcode(ERRCODE_INVALID_GRANT_OPERATION), + errmsg("sequence \"%s\" only supports SELECT column privileges", + NameStr(pg_class_tuple->relname)))); - this_privileges &= (AclMode) ACL_SELECT; + this_privileges &= (AclMode) ACL_SELECT; + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } expand_col_privileges(col_privs->cols, relOid, @@ -3824,14 +3890,22 @@ pg_class_aclmask(Oid table_oid, Oid roleid, if (isNull) { /* No ACL, so build default ACL */ - switch (classForm->relkind) + Assert(RELKIND_IS_VALID((RelKind) classForm->relkind)); + switch ((RelKind) classForm->relkind) { case RELKIND_SEQUENCE: acl = acldefault(OBJECT_SEQUENCE, ownerId); break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: acl = acldefault(OBJECT_TABLE, ownerId); - break; } aclDatum = (Datum) 0; } @@ -5519,58 +5593,82 @@ recordExtObjInitPriv(Oid objoid, Oid classoid) * composite types. (These cases are unreachable given the * restrictions in ALTER EXTENSION ADD, but let's check anyway.) */ - if (pg_class_tuple->relkind == RELKIND_INDEX || - pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX || - pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE) + Assert(RELKIND_IS_VALID((RelKind) pg_class_tuple->relkind)); + switch ((RelKind) pg_class_tuple->relkind) { - ReleaseSysCache(tuple); - return; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + ReleaseSysCache(tuple); + return; + case RELKIND_SEQUENCE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } /* * If this isn't a sequence then it's possibly going to have * column-level ACLs associated with it. */ - if (pg_class_tuple->relkind != RELKIND_SEQUENCE) + Assert(RELKIND_IS_VALID((RelKind) pg_class_tuple->relkind)); + switch ((RelKind) pg_class_tuple->relkind) { - AttrNumber curr_att; - AttrNumber nattrs = pg_class_tuple->relnatts; - - for (curr_att = 1; curr_att <= nattrs; curr_att++) - { - HeapTuple attTuple; - Datum attaclDatum; - - attTuple = SearchSysCache2(ATTNUM, - ObjectIdGetDatum(objoid), - Int16GetDatum(curr_att)); - - if (!HeapTupleIsValid(attTuple)) - continue; - - /* ignore dropped columns */ - if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped) + case RELKIND_SEQUENCE: + break; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: { - ReleaseSysCache(attTuple); - continue; - } - - attaclDatum = SysCacheGetAttr(ATTNUM, attTuple, - Anum_pg_attribute_attacl, - &isNull); + AttrNumber curr_att; + AttrNumber nattrs = pg_class_tuple->relnatts; - /* no need to do anything for a NULL ACL */ - if (isNull) - { - ReleaseSysCache(attTuple); - continue; + for (curr_att = 1; curr_att <= nattrs; curr_att++) + { + HeapTuple attTuple; + Datum attaclDatum; + + attTuple = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(objoid), + Int16GetDatum(curr_att)); + + if (!HeapTupleIsValid(attTuple)) + continue; + + /* ignore dropped columns */ + if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped) + { + ReleaseSysCache(attTuple); + continue; + } + + attaclDatum = SysCacheGetAttr(ATTNUM, attTuple, + Anum_pg_attribute_attacl, + &isNull); + + /* no need to do anything for a NULL ACL */ + if (isNull) + { + ReleaseSysCache(attTuple); + continue; + } + + recordExtensionInitPrivWorker(objoid, classoid, curr_att, + DatumGetAclP(attaclDatum)); + + ReleaseSysCache(attTuple); + } } - - recordExtensionInitPrivWorker(objoid, classoid, curr_att, - DatumGetAclP(attaclDatum)); - - ReleaseSysCache(attTuple); - } } aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl, @@ -5813,40 +5911,67 @@ removeExtObjInitPriv(Oid objoid, Oid classoid) * composite types. (These cases are unreachable given the * restrictions in ALTER EXTENSION DROP, but let's check anyway.) */ - if (pg_class_tuple->relkind == RELKIND_INDEX || - pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX || - pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE) + Assert(RELKIND_IS_VALID((RelKind) pg_class_tuple->relkind)); + switch ((RelKind) pg_class_tuple->relkind) { - ReleaseSysCache(tuple); - return; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + ReleaseSysCache(tuple); + return; + case RELKIND_SEQUENCE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } /* * If this isn't a sequence then it's possibly going to have * column-level ACLs associated with it. */ - if (pg_class_tuple->relkind != RELKIND_SEQUENCE) + Assert(RELKIND_IS_VALID((RelKind) pg_class_tuple->relkind)); + switch ((RelKind) pg_class_tuple->relkind) { - AttrNumber curr_att; - AttrNumber nattrs = pg_class_tuple->relnatts; + case RELKIND_SEQUENCE: + break; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + { + AttrNumber curr_att; + AttrNumber nattrs = pg_class_tuple->relnatts; - for (curr_att = 1; curr_att <= nattrs; curr_att++) - { - HeapTuple attTuple; + for (curr_att = 1; curr_att <= nattrs; curr_att++) + { + HeapTuple attTuple; - attTuple = SearchSysCache2(ATTNUM, - ObjectIdGetDatum(objoid), - Int16GetDatum(curr_att)); + attTuple = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(objoid), + Int16GetDatum(curr_att)); - if (!HeapTupleIsValid(attTuple)) - continue; + if (!HeapTupleIsValid(attTuple)) + continue; - /* when removing, remove all entries, even dropped columns */ + /* + * when removing, remove all entries, even dropped + * columns + */ - recordExtensionInitPrivWorker(objoid, classoid, curr_att, NULL); + recordExtensionInitPrivWorker(objoid, classoid, curr_att, NULL); - ReleaseSysCache(attTuple); - } + ReleaseSysCache(attTuple); + } + } } ReleaseSysCache(tuple); diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index b33a2f94af..31c575bf50 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -1395,22 +1395,34 @@ doDeletion(const ObjectAddress *object, int flags) { char relKind = get_rel_relkind(object->objectId); - if (relKind == RELKIND_INDEX || - relKind == RELKIND_PARTITIONED_INDEX) + Assert(RELKIND_IS_VALID((RelKind) relKind)); + switch ((RelKind) relKind) { - bool concurrent = ((flags & PERFORM_DELETION_CONCURRENTLY) != 0); - bool concurrent_lock_mode = ((flags & PERFORM_DELETION_CONCURRENT_LOCK) != 0); - - Assert(object->objectSubId == 0); - index_drop(object->objectId, concurrent, concurrent_lock_mode); - } - else - { - if (object->objectSubId != 0) - RemoveAttributeById(object->objectId, - object->objectSubId); - else - heap_drop_with_catalog(object->objectId); + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + { + bool concurrent = ((flags & PERFORM_DELETION_CONCURRENTLY) != 0); + bool concurrent_lock_mode = ((flags & PERFORM_DELETION_CONCURRENT_LOCK) != 0); + + Assert(object->objectSubId == 0); + index_drop(object->objectId, concurrent, concurrent_lock_mode); + } + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + { + if (object->objectSubId != 0) + RemoveAttributeById(object->objectId, + object->objectSubId); + else + heap_drop_with_catalog(object->objectId); + } } /* @@ -1419,8 +1431,8 @@ doDeletion(const ObjectAddress *object, int flags) */ if (relKind == RELKIND_SEQUENCE) DeleteSequenceTuple(object->objectId); - break; } + break; case OCLASS_PROC: RemoveFunctionById(object->objectId); diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 3985326df6..b7b6c0e4e8 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -335,7 +335,8 @@ heap_create(const char *relname, *relminmxid = InvalidMultiXactId; /* Handle reltablespace for specific relkinds. */ - switch (relkind) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { case RELKIND_VIEW: case RELKIND_COMPOSITE_TYPE: @@ -360,7 +361,13 @@ heap_create(const char *relname, */ reltablespace = InvalidOid; break; - default: + + case RELKIND_PARTITIONED_INDEX: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: break; } @@ -415,21 +422,13 @@ heap_create(const char *relname, { RelationOpenSmgr(rel); - switch (rel->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - case RELKIND_VIEW: - case RELKIND_COMPOSITE_TYPE: - case RELKIND_FOREIGN_TABLE: - case RELKIND_PARTITIONED_TABLE: - case RELKIND_PARTITIONED_INDEX: - Assert(false); - break; - case RELKIND_INDEX: case RELKIND_SEQUENCE: RelationCreateStorage(rel->rd_node, relpersistence); break; - case RELKIND_RELATION: case RELKIND_TOASTVALUE: case RELKIND_MATVIEW: @@ -437,6 +436,13 @@ heap_create(const char *relname, relpersistence, relfrozenxid, relminmxid); break; + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_PARTITIONED_INDEX: + Assert(false); + break; } } @@ -506,18 +512,30 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind, * Skip this for a view or type relation, since those don't have system * attributes. */ - if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - for (i = 0; i < natts; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + for (i = 0; i < natts; i++) + { + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (SystemAttributeByName(NameStr(attr->attname)) != NULL) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_COLUMN), - errmsg("column name \"%s\" conflicts with a system column name", - NameStr(attr->attname)))); - } + if (SystemAttributeByName(NameStr(attr->attname)) != NULL) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_COLUMN), + errmsg("column name \"%s\" conflicts with a system column name", + NameStr(attr->attname)))); + } } /* @@ -836,19 +854,31 @@ AddNewAttributeTuples(Oid new_rel_oid, * all for a view or type relation. We don't bother with making datatype * dependencies here, since presumably all these types are pinned. */ - if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - for (i = 0; i < (int) lengthof(SysAtt); i++) - { - FormData_pg_attribute attStruct; + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + for (i = 0; i < (int) lengthof(SysAtt); i++) + { + FormData_pg_attribute attStruct; - memcpy(&attStruct, SysAtt[i], sizeof(FormData_pg_attribute)); + memcpy(&attStruct, SysAtt[i], sizeof(FormData_pg_attribute)); - /* Fill in the correct relation OID in the copied tuple */ - attStruct.attrelid = new_rel_oid; + /* Fill in the correct relation OID in the copied tuple */ + attStruct.attrelid = new_rel_oid; - InsertPgAttributeTuple(rel, &attStruct, (Datum) 0, indstate); - } + InsertPgAttributeTuple(rel, &attStruct, (Datum) 0, indstate); + } } /* @@ -966,7 +996,8 @@ AddNewRelationTuple(Relation pg_class_desc, */ new_rel_reltup = new_rel_desc->rd_rel; - switch (relkind) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { case RELKIND_RELATION: case RELKIND_MATVIEW: @@ -983,12 +1014,15 @@ AddNewRelationTuple(Relation pg_class_desc, new_rel_reltup->reltuples = 1; new_rel_reltup->relallvisible = 0; break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: /* Views, etc, have no disk storage */ new_rel_reltup->relpages = 0; new_rel_reltup->reltuples = 0; new_rel_reltup->relallvisible = 0; - break; } new_rel_reltup->relfrozenxid = relfrozenxid; @@ -1184,29 +1218,41 @@ heap_create_with_catalog(const char *relname, if (!OidIsValid(relid)) { /* Use binary-upgrade override for pg_class.oid/relfilenode? */ - if (IsBinaryUpgrade && - (relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE || - relkind == RELKIND_VIEW || relkind == RELKIND_MATVIEW || - relkind == RELKIND_COMPOSITE_TYPE || relkind == RELKIND_FOREIGN_TABLE || - relkind == RELKIND_PARTITIONED_TABLE)) - { - if (!OidIsValid(binary_upgrade_next_heap_pg_class_oid)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("pg_class heap OID value not set when in binary upgrade mode"))); - - relid = binary_upgrade_next_heap_pg_class_oid; - binary_upgrade_next_heap_pg_class_oid = InvalidOid; - } - /* There might be no TOAST table, so we have to test for it. */ - else if (IsBinaryUpgrade && - OidIsValid(binary_upgrade_next_toast_pg_class_oid) && - relkind == RELKIND_TOASTVALUE) + bool handled = false; + switch (relkind) { - relid = binary_upgrade_next_toast_pg_class_oid; - binary_upgrade_next_toast_pg_class_oid = InvalidOid; + case RELKIND_RELATION: + case RELKIND_SEQUENCE: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + if (IsBinaryUpgrade) + { + if (!OidIsValid(binary_upgrade_next_heap_pg_class_oid)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("pg_class heap OID value not set when in binary upgrade mode"))); + + relid = binary_upgrade_next_heap_pg_class_oid; + binary_upgrade_next_heap_pg_class_oid = InvalidOid; + handled = true; + } + break; + case RELKIND_TOASTVALUE: + if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_toast_pg_class_oid)) + { + relid = binary_upgrade_next_toast_pg_class_oid; + binary_upgrade_next_toast_pg_class_oid = InvalidOid; + handled = true; + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_INDEX: + break; } - else + if (!handled) relid = GetNewRelFileNode(reltablespace, pg_class_desc, relpersistence); } @@ -1216,7 +1262,8 @@ heap_create_with_catalog(const char *relname, */ if (use_user_acl) { - switch (relkind) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { case RELKIND_RELATION: case RELKIND_VIEW: @@ -1230,9 +1277,11 @@ heap_create_with_catalog(const char *relname, relacl = get_user_default_acl(OBJECT_SEQUENCE, ownerid, relnamespace); break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: relacl = NULL; - break; } } else @@ -1267,85 +1316,93 @@ heap_create_with_catalog(const char *relname, * These types are made except where the use of a relation as such is an * implementation detail: toast tables, sequences and indexes. */ - if (!(relkind == RELKIND_SEQUENCE || - relkind == RELKIND_TOASTVALUE || - relkind == RELKIND_INDEX || - relkind == RELKIND_PARTITIONED_INDEX)) - { - Oid new_array_oid; - ObjectAddress new_type_addr; - char *relarrayname; - - /* - * We'll make an array over the composite type, too. For largely - * historical reasons, the array type's OID is assigned first. - */ - new_array_oid = AssignTypeArrayOid(); - - /* - * Make the pg_type entry for the composite type. The OID of the - * composite type can be preselected by the caller, but if reltypeid - * is InvalidOid, we'll generate a new OID for it. - * - * NOTE: we could get a unique-index failure here, in case someone - * else is creating the same type name in parallel but hadn't - * committed yet when we checked for a duplicate name above. - */ - new_type_addr = AddNewRelationType(relname, - relnamespace, - relid, - relkind, - ownerid, - reltypeid, - new_array_oid); - new_type_oid = new_type_addr.objectId; - if (typaddress) - *typaddress = new_type_addr; - - /* Now create the array type. */ - relarrayname = makeArrayTypeName(relname, relnamespace); - - TypeCreate(new_array_oid, /* force the type's OID to this */ - relarrayname, /* Array type name */ - relnamespace, /* Same namespace as parent */ - InvalidOid, /* Not composite, no relationOid */ - 0, /* relkind, also N/A here */ - ownerid, /* owner's ID */ - -1, /* Internal size (varlena) */ - TYPTYPE_BASE, /* Not composite - typelem is */ - TYPCATEGORY_ARRAY, /* type-category (array) */ - false, /* array types are never preferred */ - DEFAULT_TYPDELIM, /* default array delimiter */ - F_ARRAY_IN, /* array input proc */ - F_ARRAY_OUT, /* array output proc */ - F_ARRAY_RECV, /* array recv (bin) proc */ - F_ARRAY_SEND, /* array send (bin) proc */ - InvalidOid, /* typmodin procedure - none */ - InvalidOid, /* typmodout procedure - none */ - F_ARRAY_TYPANALYZE, /* array analyze procedure */ - new_type_oid, /* array element type - the rowtype */ - true, /* yes, this is an array type */ - InvalidOid, /* this has no array type */ - InvalidOid, /* domain base type - irrelevant */ - NULL, /* default value - none */ - NULL, /* default binary representation */ - false, /* passed by reference */ - TYPALIGN_DOUBLE, /* alignment - must be the largest! */ - TYPSTORAGE_EXTENDED, /* fully TOASTable */ - -1, /* typmod */ - 0, /* array dimensions for typBaseType */ - false, /* Type NOT NULL */ - InvalidOid); /* rowtypes never have a collation */ - - pfree(relarrayname); - } - else + switch (relkind) { - /* Caller should not be expecting a type to be created. */ - Assert(reltypeid == InvalidOid); - Assert(typaddress == NULL); + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + /* Caller should not be expecting a type to be created. */ + Assert(reltypeid == InvalidOid); + Assert(typaddress == NULL); - new_type_oid = InvalidOid; + new_type_oid = InvalidOid; + break; + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + { + Oid new_array_oid; + ObjectAddress new_type_addr; + char *relarrayname; + + /* + * We'll make an array over the composite type, too. For largely + * historical reasons, the array type's OID is assigned first. + */ + new_array_oid = AssignTypeArrayOid(); + + /* + * Make the pg_type entry for the composite type. The OID of the + * composite type can be preselected by the caller, but if reltypeid + * is InvalidOid, we'll generate a new OID for it. + * + * NOTE: we could get a unique-index failure here, in case someone + * else is creating the same type name in parallel but hadn't + * committed yet when we checked for a duplicate name above. + */ + new_type_addr = AddNewRelationType(relname, + relnamespace, + relid, + relkind, + ownerid, + reltypeid, + new_array_oid); + new_type_oid = new_type_addr.objectId; + if (typaddress) + *typaddress = new_type_addr; + + /* Now create the array type. */ + relarrayname = makeArrayTypeName(relname, relnamespace); + + TypeCreate(new_array_oid, /* force the type's OID to this */ + relarrayname, /* Array type name */ + relnamespace, /* Same namespace as parent */ + InvalidOid, /* Not composite, no relationOid */ + 0, /* relkind, also N/A here */ + ownerid, /* owner's ID */ + -1, /* Internal size (varlena) */ + TYPTYPE_BASE, /* Not composite - typelem is */ + TYPCATEGORY_ARRAY, /* type-category (array) */ + false, /* array types are never preferred */ + DEFAULT_TYPDELIM, /* default array delimiter */ + F_ARRAY_IN, /* array input proc */ + F_ARRAY_OUT, /* array output proc */ + F_ARRAY_RECV, /* array recv (bin) proc */ + F_ARRAY_SEND, /* array send (bin) proc */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ + F_ARRAY_TYPANALYZE, /* array analyze procedure */ + new_type_oid, /* array element type - the rowtype */ + true, /* yes, this is an array type */ + InvalidOid, /* this has no array type */ + InvalidOid, /* domain base type - irrelevant */ + NULL, /* default value - none */ + NULL, /* default binary representation */ + false, /* passed by reference */ + TYPALIGN_DOUBLE, /* alignment - must be the largest! */ + TYPSTORAGE_EXTENDED, /* fully TOASTable */ + -1, /* typmod */ + 0, /* array dimensions for typBaseType */ + false, /* Type NOT NULL */ + InvalidOid); /* rowtypes never have a collation */ + + pfree(relarrayname); + } + break; } /* @@ -1387,51 +1444,65 @@ heap_create_with_catalog(const char *relname, * Also, skip this in bootstrap mode, since we don't make dependencies * while bootstrapping. */ - if (relkind != RELKIND_COMPOSITE_TYPE && - relkind != RELKIND_TOASTVALUE && - !IsBootstrapProcessingMode()) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - ObjectAddress myself, - referenced; - - myself.classId = RelationRelationId; - myself.objectId = relid; - myself.objectSubId = 0; - - referenced.classId = NamespaceRelationId; - referenced.objectId = relnamespace; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - - recordDependencyOnOwner(RelationRelationId, relid, ownerid); - - recordDependencyOnNewAcl(RelationRelationId, relid, 0, ownerid, relacl); - - recordDependencyOnCurrentExtension(&myself, false); - - if (reloftypeid) - { - referenced.classId = TypeRelationId; - referenced.objectId = reloftypeid; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } - - /* - * Make a dependency link to force the relation to be deleted if its - * access method is. Do this only for relation and materialized views. - * - * No need to add an explicit dependency for the toast table, as the - * main table depends on it. - */ - if (relkind == RELKIND_RELATION || - relkind == RELKIND_MATVIEW) - { - referenced.classId = AccessMethodRelationId; - referenced.objectId = accessmtd; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + case RELKIND_COMPOSITE_TYPE: + case RELKIND_TOASTVALUE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_VIEW: + if (!IsBootstrapProcessingMode()) + { + ObjectAddress myself, + referenced; + + myself.classId = RelationRelationId; + myself.objectId = relid; + myself.objectSubId = 0; + + referenced.classId = NamespaceRelationId; + referenced.objectId = relnamespace; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + recordDependencyOnOwner(RelationRelationId, relid, ownerid); + + recordDependencyOnNewAcl(RelationRelationId, relid, 0, ownerid, relacl); + + recordDependencyOnCurrentExtension(&myself, false); + + if (reloftypeid) + { + referenced.classId = TypeRelationId; + referenced.objectId = reloftypeid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + /* + * Make a dependency link to force the relation to be deleted + * if its access method is. Do this only for relation and + * materialized views. + * + * No need to add an explicit dependency for the toast table, + * as the main table depends on it. + */ + if (relkind == RELKIND_RELATION || + relkind == RELKIND_MATVIEW) + { + referenced.classId = AccessMethodRelationId; + referenced.objectId = accessmtd; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + } } /* Post creation hook for new relation */ @@ -2390,12 +2461,27 @@ StoreRelCheck(Relation rel, const char *ccname, Node *expr, * Partitioned tables do not contain any rows themselves, so a NO INHERIT * constraint makes no sense. */ - if (is_no_inherit && - rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("cannot add NO INHERIT constraint to partitioned table \"%s\"", - RelationGetRelationName(rel)))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + if (is_no_inherit) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot add NO INHERIT constraint to partitioned table \"%s\"", + RelationGetRelationName(rel)))); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } /* * Create the Check Constraint @@ -3261,8 +3347,22 @@ heap_truncate_one_rel(Relation rel) * Truncate the relation. Partitioned tables have no storage, so there is * nothing to do for them here. */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - return; + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + return; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } /* Truncate the underlying relation */ table_relation_nontransactional_truncate(rel); @@ -3315,9 +3415,24 @@ heap_truncate_check_FKs(List *relations, bool tempTables) { Relation rel = lfirst(cell); - if (rel->rd_rel->relhastriggers || - rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - oids = lappend_oid(oids, RelationGetRelid(rel)); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + if (!rel->rd_rel->relhastriggers) + break; + /* fallthrough */ + case RELKIND_PARTITIONED_TABLE: + oids = lappend_oid(oids, RelationGetRelid(rel)); + } } /* @@ -3540,7 +3655,23 @@ StorePartitionKey(Relation rel, ObjectAddress myself; ObjectAddress referenced; - Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + Assert(false); + break; + } /* Copy the partition attribute numbers, opclass OIDs into arrays */ partattrs_vec = buildint2vector(partattrs, partnatts); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 8ec2864c76..0fedf58617 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -2203,8 +2203,22 @@ index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode) /* * Schedule physical removal of the files (if any) */ - if (userIndexRelation->rd_rel->relkind != RELKIND_PARTITIONED_INDEX) - RelationDropStorage(userIndexRelation); + Assert(RELKIND_IS_VALID((RelKind) userIndexRelation->rd_rel->relkind)); + switch ((RelKind) userIndexRelation->rd_rel->relkind) + { + case RELKIND_PARTITIONED_INDEX: + break; + case RELKIND_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + RelationDropStorage(userIndexRelation); + } /* * Close and flush the index's relcache entry, to ensure relcache doesn't @@ -2731,7 +2745,22 @@ index_update_stats(Relation rel, rd_rel = (Form_pg_class) GETSTRUCT(tuple); /* Should this be a more comprehensive test? */ - Assert(rd_rel->relkind != RELKIND_PARTITIONED_INDEX); + Assert(RELKIND_IS_VALID((RelKind) rd_rel->relkind)); + switch ((RelKind) rd_rel->relkind) + { + case RELKIND_PARTITIONED_INDEX: + Assert(false); + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + case RELKIND_INDEX: + break; + } /* Apply required updates, if any, to copied tuple */ @@ -2747,10 +2776,23 @@ index_update_stats(Relation rel, BlockNumber relpages = RelationGetNumberOfBlocks(rel); BlockNumber relallvisible; - if (rd_rel->relkind != RELKIND_INDEX) - visibilitymap_count(rel, &relallvisible, NULL); - else /* don't bother for indexes */ - relallvisible = 0; + Assert(RELKIND_IS_VALID((RelKind) rd_rel->relkind)); + switch ((RelKind) rd_rel->relkind) + { + case RELKIND_INDEX: + relallvisible = 0; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + visibilitymap_count(rel, &relallvisible, NULL); + } if (rd_rel->relpages != (int32) relpages) { @@ -3461,9 +3503,24 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence, * The case of reindexing partitioned tables and indexes is handled * differently by upper layers, so this case shouldn't arise. */ - if (iRel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) - elog(ERROR, "unsupported relation kind for index \"%s\"", - RelationGetRelationName(iRel)); + Assert(RELKIND_IS_VALID((RelKind) iRel->rd_rel->relkind)); + switch ((RelKind) iRel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_INDEX: + elog(ERROR, "unsupported relation kind for index \"%s\"", + RelationGetRelationName(iRel)); + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } /* * Don't allow reindex on temp tables of other backends ... their local @@ -3677,14 +3734,26 @@ reindex_relation(Oid relid, int flags, int options) * (REINDEX SCHEMA) and happen to come across a partitioned table. The * partitions may be reindexed on their own anyway. */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - ereport(WARNING, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("REINDEX of partitioned tables is not yet implemented, skipping \"%s\"", - RelationGetRelationName(rel)))); - table_close(rel, ShareLock); - return false; + case RELKIND_PARTITIONED_TABLE: + ereport(WARNING, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("REINDEX of partitioned tables is not yet implemented, skipping \"%s\"", + RelationGetRelationName(rel)))); + table_close(rel, ShareLock); + return false; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } toast_relid = rel->rd_rel->reltoastrelid; diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 534df8e802..1c82310c24 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -97,7 +97,8 @@ */ typedef struct { - const char *class_descr; /* string describing the catalog, for internal error messages */ + const char *class_descr; /* string describing the catalog, for internal + * error messages */ Oid class_oid; /* oid of catalog */ Oid oid_index_oid; /* oid of index on system oid column */ int oid_catcache_id; /* id of catcache on system oid column */ @@ -1342,48 +1343,130 @@ get_relation_by_qualified_name(ObjectType objtype, List *object, switch (objtype) { case OBJECT_INDEX: - if (relation->rd_rel->relkind != RELKIND_INDEX && - relation->rd_rel->relkind != RELKIND_PARTITIONED_INDEX) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not an index", - RelationGetRelationName(relation)))); + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not an index", + RelationGetRelationName(relation)))); + } break; case OBJECT_SEQUENCE: - if (relation->rd_rel->relkind != RELKIND_SEQUENCE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a sequence", - RelationGetRelationName(relation)))); + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_SEQUENCE: + break; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + RelationGetRelationName(relation)))); + } break; case OBJECT_TABLE: - if (relation->rd_rel->relkind != RELKIND_RELATION && - relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table", - RelationGetRelationName(relation)))); + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + break; + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", + RelationGetRelationName(relation)))); + } break; case OBJECT_VIEW: - if (relation->rd_rel->relkind != RELKIND_VIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a view", - RelationGetRelationName(relation)))); + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_VIEW: + break; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a view", + RelationGetRelationName(relation)))); + } break; case OBJECT_MATVIEW: - if (relation->rd_rel->relkind != RELKIND_MATVIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a materialized view", - RelationGetRelationName(relation)))); + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_MATVIEW: + break; + case RELKIND_VIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a materialized view", + RelationGetRelationName(relation)))); + } break; case OBJECT_FOREIGN_TABLE: - if (relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a foreign table", - RelationGetRelationName(relation)))); + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_FOREIGN_TABLE: + break; + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a foreign table", + RelationGetRelationName(relation)))); + } break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); @@ -3795,7 +3878,8 @@ getRelationDescription(StringInfo buffer, Oid relid) relname = quote_qualified_identifier(nspname, NameStr(relForm->relname)); - switch (relForm->relkind) + Assert(RELKIND_IS_VALID((RelKind) relForm->relkind)); + switch ((RelKind) relForm->relkind) { case RELKIND_RELATION: case RELKIND_PARTITIONED_TABLE: @@ -3830,12 +3914,6 @@ getRelationDescription(StringInfo buffer, Oid relid) case RELKIND_FOREIGN_TABLE: appendStringInfo(buffer, _("foreign table %s"), relname); - break; - default: - /* shouldn't get here */ - appendStringInfo(buffer, _("relation %s"), - relname); - break; } ReleaseSysCache(relTup); @@ -4276,7 +4354,8 @@ getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId) elog(ERROR, "cache lookup failed for relation %u", relid); relForm = (Form_pg_class) GETSTRUCT(relTup); - switch (relForm->relkind) + Assert(RELKIND_IS_VALID((RelKind) relForm->relkind)); + switch ((RelKind) relForm->relkind) { case RELKIND_RELATION: case RELKIND_PARTITIONED_TABLE: @@ -4303,11 +4382,6 @@ getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId) break; case RELKIND_FOREIGN_TABLE: appendStringInfoString(buffer, "foreign table"); - break; - default: - /* shouldn't get here */ - appendStringInfoString(buffer, "relation"); - break; } if (objectSubId != 0) @@ -5451,11 +5525,9 @@ strlist_to_textarray(List *list) ObjectType get_relkind_objtype(char relkind) { - switch (relkind) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - case RELKIND_RELATION: - case RELKIND_PARTITIONED_TABLE: - return OBJECT_TABLE; case RELKIND_INDEX: case RELKIND_PARTITIONED_INDEX: return OBJECT_INDEX; @@ -5467,10 +5539,12 @@ get_relkind_objtype(char relkind) return OBJECT_MATVIEW; case RELKIND_FOREIGN_TABLE: return OBJECT_FOREIGN_TABLE; + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: case RELKIND_TOASTVALUE: - return OBJECT_TABLE; - default: - /* Per above, don't raise an error */ - return OBJECT_TABLE; + /* Per above, don't raise an error */ + case RELKIND_COMPOSITE_TYPE: + break; } + return OBJECT_TABLE; } diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 239ac017fa..973bddf544 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -235,8 +235,24 @@ has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr) ListCell *partexprs_item; int i; - if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - return false; + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + if (attnums == NULL) + return false; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + return false; + } key = RelationGetPartitionKey(rel); partnatts = get_partition_natts(key); diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 21cfdcace9..4df83df10d 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -836,15 +836,35 @@ getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype) * We assume any auto or internal dependency of a sequence on a column * must be what we are looking for. (We need the relkind test because * indexes can also have auto dependencies on columns.) + * + * Beware that relkind here may be NULL, which is not a valid member + * of enum RelKind. */ - if (deprec->classid == RelationRelationId && - deprec->objsubid == 0 && - deprec->refobjsubid != 0 && - (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) && - get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE) + Assert(get_rel_relkind(deprec->objid) == '\0' || + RELKIND_IS_VALID((RelKind) get_rel_relkind(deprec->objid))); + switch ((RelKind) get_rel_relkind(deprec->objid)) { - if (!deptype || deprec->deptype == deptype) - result = lappend_oid(result, deprec->objid); + case RELKIND_SEQUENCE: + if (deprec->classid == RelationRelationId && + deprec->objsubid == 0 && + deprec->refobjsubid != 0 && + (deprec->deptype == DEPENDENCY_AUTO || + deprec->deptype == DEPENDENCY_INTERNAL) && + (!deptype || deprec->deptype == deptype)) + { + result = lappend_oid(result, deprec->objid); + } + break; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } } @@ -940,9 +960,22 @@ get_constraint_index(Oid constraintId) * This is pure paranoia; there shouldn't be any other relkinds * dependent on a constraint. */ - if (relkind != RELKIND_INDEX && - relkind != RELKIND_PARTITIONED_INDEX) - continue; + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + continue; + } indexId = deprec->objid; break; diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c index 09946be788..b34a97a6fa 100644 --- a/src/backend/catalog/pg_publication.c +++ b/src/backend/catalog/pg_publication.c @@ -50,13 +50,26 @@ static void check_publication_add_relation(Relation targetrel) { /* Must be a regular or partitioned table */ - if (RelationGetForm(targetrel)->relkind != RELKIND_RELATION && - RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("\"%s\" is not a table", - RelationGetRelationName(targetrel)), - errdetail("Only tables can be added to publications."))); + Assert(RELKIND_IS_VALID((RelKind) RelationGetForm(targetrel)->relkind)); + switch ((RelKind) RelationGetForm(targetrel)->relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("\"%s\" is not a table", + RelationGetRelationName(targetrel)), + errdetail("Only tables can be added to publications."))); + } /* Can't be system table */ if (IsCatalogRelation(targetrel)) @@ -97,11 +110,25 @@ check_publication_add_relation(Relation targetrel) static bool is_publishable_class(Oid relid, Form_pg_class reltuple) { - return (reltuple->relkind == RELKIND_RELATION || - reltuple->relkind == RELKIND_PARTITIONED_TABLE) && - !IsCatalogRelationOid(relid) && - reltuple->relpersistence == RELPERSISTENCE_PERMANENT && - relid >= FirstNormalObjectId; + Assert(RELKIND_IS_VALID((RelKind) reltuple->relkind)); + switch ((RelKind) reltuple->relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + return (!IsCatalogRelationOid(relid) && + reltuple->relpersistence == RELPERSISTENCE_PERMANENT && + relid >= FirstNormalObjectId); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + return false; + } } /* diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 79ffe317dd..d846e27667 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -658,10 +658,23 @@ GenerateTypeDependencies(HeapTuple typeTuple, { ObjectAddressSet(referenced, RelationRelationId, typeForm->typrelid); - if (relationKind != RELKIND_COMPOSITE_TYPE) - recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); - else - recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); + Assert(RELKIND_IS_VALID((RelKind) relationKind)); + switch ((RelKind) relationKind) + { + case RELKIND_COMPOSITE_TYPE: + recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); + } } /* diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index c40d25b301..b25de92a17 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -96,12 +96,25 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid) rel = table_openrv(makeRangeVar(NULL, relName, -1), AccessExclusiveLock); - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_MATVIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or materialized view", - relName))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table or materialized view", + relName))); + } /* create_toast_table does all the work */ if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0, @@ -378,8 +391,22 @@ needs_toast_table(Relation rel) /* * No need to create a TOAST table for partitioned tables. */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - return false; + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + return false; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } /* * We cannot allow toasting a shared relation after initdb (because diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 924ef37c81..ac36d93aa7 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -196,54 +196,67 @@ analyze_rel(Oid relid, RangeVar *relation, /* * Check that it's of an analyzable relkind, and set up appropriately. */ - if (onerel->rd_rel->relkind == RELKIND_RELATION || - onerel->rd_rel->relkind == RELKIND_MATVIEW) + Assert(RELKIND_IS_VALID((RelKind) onerel->rd_rel->relkind)); + switch ((RelKind) onerel->rd_rel->relkind) { - /* Regular table, so we'll use the regular row acquisition function */ - acquirefunc = acquire_sample_rows; - /* Also get regular table's size */ - relpages = RelationGetNumberOfBlocks(onerel); - } - else if (onerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - { - /* - * For a foreign table, call the FDW's hook function to see whether it - * supports analysis. - */ - FdwRoutine *fdwroutine; - bool ok = false; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + /* + * Regular table, so we'll use the regular row acquisition + * function + */ + acquirefunc = acquire_sample_rows; + /* Also get regular table's size */ + relpages = RelationGetNumberOfBlocks(onerel); + break; + case RELKIND_FOREIGN_TABLE: + { + /* + * For a foreign table, call the FDW's hook function to see + * whether it supports analysis. + */ + FdwRoutine *fdwroutine; + bool ok = false; - fdwroutine = GetFdwRoutineForRelation(onerel, false); + fdwroutine = GetFdwRoutineForRelation(onerel, false); - if (fdwroutine->AnalyzeForeignTable != NULL) - ok = fdwroutine->AnalyzeForeignTable(onerel, - &acquirefunc, - &relpages); + if (fdwroutine->AnalyzeForeignTable != NULL) + ok = fdwroutine->AnalyzeForeignTable(onerel, + &acquirefunc, + &relpages); - if (!ok) - { - ereport(WARNING, - (errmsg("skipping \"%s\" --- cannot analyze this foreign table", - RelationGetRelationName(onerel)))); + if (!ok) + { + ereport(WARNING, + (errmsg("skipping \"%s\" --- cannot analyze this foreign table", + RelationGetRelationName(onerel)))); + relation_close(onerel, ShareUpdateExclusiveLock); + return; + } + } + break; + case RELKIND_PARTITIONED_TABLE: + /* + * For partitioned tables, we want to do the recursive ANALYZE + * below. + */ + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + /* + * No need for a WARNING if we already complained during + * VACUUM + */ + if (!(params->options & VACOPT_VACUUM)) + ereport(WARNING, + (errmsg("skipping \"%s\" --- cannot analyze non-tables or special system tables", + RelationGetRelationName(onerel)))); relation_close(onerel, ShareUpdateExclusiveLock); return; - } - } - else if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - { - /* - * For partitioned tables, we want to do the recursive ANALYZE below. - */ - } - else - { - /* No need for a WARNING if we already complained during VACUUM */ - if (!(params->options & VACOPT_VACUUM)) - ereport(WARNING, - (errmsg("skipping \"%s\" --- cannot analyze non-tables or special system tables", - RelationGetRelationName(onerel)))); - relation_close(onerel, ShareUpdateExclusiveLock); - return; } /* @@ -259,9 +272,23 @@ analyze_rel(Oid relid, RangeVar *relation, * Do the normal non-recursive ANALYZE. We can skip this for partitioned * tables, which don't contain any rows. */ - if (onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - do_analyze_rel(onerel, params, va_cols, acquirefunc, - relpages, false, in_outer_xact, elevel); + Assert(RELKIND_IS_VALID((RelKind) onerel->rd_rel->relkind)); + switch ((RelKind) onerel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + do_analyze_rel(onerel, params, va_cols, acquirefunc, + relpages, false, in_outer_xact, elevel); + } /* * If there are child tables, do recursive ANALYZE. @@ -1283,49 +1310,66 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, } /* Check table type (MATVIEW can't happen, but might as well allow) */ - if (childrel->rd_rel->relkind == RELKIND_RELATION || - childrel->rd_rel->relkind == RELKIND_MATVIEW) + Assert(RELKIND_IS_VALID((RelKind) childrel->rd_rel->relkind)); + switch ((RelKind) childrel->rd_rel->relkind) { - /* Regular table, so use the regular row acquisition function */ - acquirefunc = acquire_sample_rows; - relpages = RelationGetNumberOfBlocks(childrel); - } - else if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - { - /* - * For a foreign table, call the FDW's hook function to see - * whether it supports analysis. - */ - FdwRoutine *fdwroutine; - bool ok = false; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + { + /* + * Regular table, so use the regular row acquisition + * function + */ + acquirefunc = acquire_sample_rows; + relpages = RelationGetNumberOfBlocks(childrel); + } + break; + case RELKIND_FOREIGN_TABLE: + { + /* + * For a foreign table, call the FDW's hook function to + * see whether it supports analysis. + */ + FdwRoutine *fdwroutine; + bool ok = false; - fdwroutine = GetFdwRoutineForRelation(childrel, false); + fdwroutine = GetFdwRoutineForRelation(childrel, false); - if (fdwroutine->AnalyzeForeignTable != NULL) - ok = fdwroutine->AnalyzeForeignTable(childrel, - &acquirefunc, - &relpages); + if (fdwroutine->AnalyzeForeignTable != NULL) + ok = fdwroutine->AnalyzeForeignTable(childrel, + &acquirefunc, + &relpages); - if (!ok) - { - /* ignore, but release the lock on it */ - Assert(childrel != onerel); - table_close(childrel, AccessShareLock); - continue; - } - } - else - { - /* - * ignore, but release the lock on it. don't try to unlock the - * passed-in relation - */ - Assert(childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); - if (childrel != onerel) - table_close(childrel, AccessShareLock); - else - table_close(childrel, NoLock); - continue; + if (!ok) + { + /* ignore, but release the lock on it */ + Assert(childrel != onerel); + table_close(childrel, AccessShareLock); + continue; + } + } + break; + case RELKIND_PARTITIONED_TABLE: + { + /* + * ignore, but release the lock on it. don't try to + * unlock the passed-in relation + */ + if (childrel != onerel) + table_close(childrel, AccessShareLock); + else + table_close(childrel, NoLock); + continue; + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + Assert(false); + break; } /* OK, we'll process this child */ diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 04d12a7ece..9d228407c6 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -127,10 +127,25 @@ cluster(ClusterStmt *stmt, bool isTopLevel) /* * Reject clustering a partitioned table. */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot cluster a partitioned table"))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot cluster a partitioned table"))); + break; + case RELKIND_RELATION: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } if (stmt->indexname == NULL) { @@ -383,12 +398,27 @@ cluster_rel(Oid tableOid, Oid indexOid, int options) * multi-relation request -- for example, CLUSTER was run on the entire * database. */ - if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW && - !RelationIsPopulated(OldHeap)) + Assert(RELKIND_IS_VALID((RelKind) OldHeap->rd_rel->relkind)); + switch ((RelKind) OldHeap->rd_rel->relkind) { - relation_close(OldHeap, AccessExclusiveLock); - pgstat_progress_end_command(); - return; + case RELKIND_MATVIEW: + if (!RelationIsPopulated(OldHeap)) + { + relation_close(OldHeap, AccessExclusiveLock); + pgstat_progress_end_command(); + return; + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } /* @@ -484,10 +514,25 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal) ListCell *index; /* Disallow applying to a partitioned table */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot mark index clustered in partitioned table"))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot mark index clustered in partitioned table"))); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } /* * If the index is already marked clustered, no need to do anything. @@ -1103,12 +1148,24 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, */ /* set rel1's frozen Xid and minimum MultiXid */ - if (relform1->relkind != RELKIND_INDEX) + Assert(RELKIND_IS_VALID((RelKind) relform1->relkind)); + switch ((RelKind) relform1->relkind) { - Assert(!TransactionIdIsValid(frozenXid) || - TransactionIdIsNormal(frozenXid)); - relform1->relfrozenxid = frozenXid; - relform1->relminmxid = cutoffMulti; + case RELKIND_INDEX: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + Assert(!TransactionIdIsValid(frozenXid) || + TransactionIdIsNormal(frozenXid)); + relform1->relfrozenxid = frozenXid; + relform1->relminmxid = cutoffMulti; } /* swap size statistics too, since new rel has freshly-updated stats */ @@ -1267,27 +1324,42 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, * valid index. The swap can actually be safely done only if the relations * have indexes. */ - if (swap_toast_by_content && - relform1->relkind == RELKIND_TOASTVALUE && - relform2->relkind == RELKIND_TOASTVALUE) + Assert(RELKIND_IS_VALID((RelKind) relform1->relkind)); + switch ((RelKind) relform1->relkind) { - Oid toastIndex1, - toastIndex2; - - /* Get valid index for each relation */ - toastIndex1 = toast_get_valid_index(r1, - AccessExclusiveLock); - toastIndex2 = toast_get_valid_index(r2, - AccessExclusiveLock); - - swap_relation_files(toastIndex1, - toastIndex2, - target_is_pg_class, - swap_toast_by_content, - is_internal, - InvalidTransactionId, - InvalidMultiXactId, - mapped_tables); + case RELKIND_TOASTVALUE: + if (swap_toast_by_content && + relform2->relkind == RELKIND_TOASTVALUE) + { + Oid toastIndex1, + toastIndex2; + + /* Get valid index for each relation */ + toastIndex1 = toast_get_valid_index(r1, + AccessExclusiveLock); + toastIndex2 = toast_get_valid_index(r2, + AccessExclusiveLock); + + swap_relation_files(toastIndex1, + toastIndex2, + target_is_pg_class, + swap_toast_by_content, + is_internal, + InvalidTransactionId, + InvalidMultiXactId, + mapped_tables); + } + break; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_VIEW: + break; } /* Clean up. */ diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index 0ff9ca9f2c..3213bee3ce 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -90,16 +90,26 @@ CommentObject(CommentStmt *stmt) * versions, so dumping per-column comments could create reload * failures. */ - if (relation->rd_rel->relkind != RELKIND_RELATION && - relation->rd_rel->relkind != RELKIND_VIEW && - relation->rd_rel->relkind != RELKIND_MATVIEW && - relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE && - relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE && - relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table", - RelationGetRelationName(relation)))); + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table", + RelationGetRelationName(relation)))); + break; + } break; default: break; diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 44da71c4cb..19d5f7757a 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -1834,42 +1834,56 @@ BeginCopyTo(ParseState *pstate, bool pipe = (filename == NULL); MemoryContext oldcontext; - if (rel != NULL && rel->rd_rel->relkind != RELKIND_RELATION) + if (rel != NULL) { - if (rel->rd_rel->relkind == RELKIND_VIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from view \"%s\"", - RelationGetRelationName(rel)), - errhint("Try the COPY (SELECT ...) TO variant."))); - else if (rel->rd_rel->relkind == RELKIND_MATVIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from materialized view \"%s\"", - RelationGetRelationName(rel)), - errhint("Try the COPY (SELECT ...) TO variant."))); - else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from foreign table \"%s\"", - RelationGetRelationName(rel)), - errhint("Try the COPY (SELECT ...) TO variant."))); - else if (rel->rd_rel->relkind == RELKIND_SEQUENCE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from sequence \"%s\"", - RelationGetRelationName(rel)))); - else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from partitioned table \"%s\"", - RelationGetRelationName(rel)), - errhint("Try the COPY (SELECT ...) TO variant."))); - else - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from non-table relation \"%s\"", - RelationGetRelationName(rel)))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + break; + case RELKIND_SEQUENCE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from sequence \"%s\"", + RelationGetRelationName(rel)))); + break; + case RELKIND_FOREIGN_TABLE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from foreign table \"%s\"", + RelationGetRelationName(rel)), + errhint("Try the COPY (SELECT ...) TO variant."))); + break; + case RELKIND_MATVIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from materialized view \"%s\"", + RelationGetRelationName(rel)), + errhint("Try the COPY (SELECT ...) TO variant."))); + break; + case RELKIND_PARTITIONED_TABLE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from partitioned table \"%s\"", + RelationGetRelationName(rel)), + errhint("Try the COPY (SELECT ...) TO variant."))); + break; + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from view \"%s\"", + RelationGetRelationName(rel)), + errhint("Try the COPY (SELECT ...) TO variant."))); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from non-table relation \"%s\"", + RelationGetRelationName(rel)))); + } } cstate = BeginCopy(pstate, false, rel, query, queryRelId, attnamelist, @@ -2393,8 +2407,22 @@ CopyMultiInsertInfoInit(CopyMultiInsertInfo *miinfo, ResultRelInfo *rri, * Buffers for partitioned tables will just be setup when we need to send * tuples their way for the first time. */ - if (rri->ri_RelationDesc->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - CopyMultiInsertInfoSetupBuffer(miinfo, rri); + Assert(RELKIND_IS_VALID((RelKind) rri->ri_RelationDesc->rd_rel->relkind)); + switch ((RelKind) rri->ri_RelationDesc->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + CopyMultiInsertInfoSetupBuffer(miinfo, rri); + } } /* @@ -2681,33 +2709,44 @@ CopyFrom(CopyState cstate) * an INSTEAD OF INSERT row trigger. (Currently, such triggers are only * allowed on views, so we only hint about them in the view case.) */ - if (cstate->rel->rd_rel->relkind != RELKIND_RELATION && - cstate->rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && - cstate->rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE && - !(cstate->rel->trigdesc && + if (!(cstate->rel->trigdesc && cstate->rel->trigdesc->trig_insert_instead_row)) { - if (cstate->rel->rd_rel->relkind == RELKIND_VIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy to view \"%s\"", - RelationGetRelationName(cstate->rel)), - errhint("To enable copying to a view, provide an INSTEAD OF INSERT trigger."))); - else if (cstate->rel->rd_rel->relkind == RELKIND_MATVIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy to materialized view \"%s\"", - RelationGetRelationName(cstate->rel)))); - else if (cstate->rel->rd_rel->relkind == RELKIND_SEQUENCE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy to sequence \"%s\"", - RelationGetRelationName(cstate->rel)))); - else - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy to non-table relation \"%s\"", - RelationGetRelationName(cstate->rel)))); + Assert(RELKIND_IS_VALID((RelKind) cstate->rel->rd_rel->relkind)); + switch ((RelKind) cstate->rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy to view \"%s\"", + RelationGetRelationName(cstate->rel)), + errhint("To enable copying to a view, provide an INSTEAD OF INSERT trigger."))); + break; + case RELKIND_MATVIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy to materialized view \"%s\"", + RelationGetRelationName(cstate->rel)))); + break; + case RELKIND_SEQUENCE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy to sequence \"%s\"", + RelationGetRelationName(cstate->rel)))); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy to non-table relation \"%s\"", + RelationGetRelationName(cstate->rel)))); + } } /* @@ -2742,11 +2781,24 @@ CopyFrom(CopyState cstate) * tuples to a small number of partitions. It seems better just to * raise an ERROR for partitioned tables. */ - if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) cstate->rel->rd_rel->relkind)); + switch ((RelKind) cstate->rel->rd_rel->relkind) { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform COPY FREEZE on a partitioned table"))); + case RELKIND_PARTITIONED_TABLE: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot perform COPY FREEZE on a partitioned table"))); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } /* @@ -2831,8 +2883,23 @@ CopyFrom(CopyState cstate) * If the named relation is a partitioned table, initialize state for * CopyFrom tuple routing. */ - if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - proute = ExecSetupPartitionTupleRouting(estate, NULL, cstate->rel); + Assert(RELKIND_IS_VALID((RelKind) cstate->rel->rd_rel->relkind)); + switch ((RelKind) cstate->rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + proute = ExecSetupPartitionTupleRouting(estate, NULL, cstate->rel); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } if (cstate->whereClause) cstate->qualexpr = ExecInitQual(castNode(List, cstate->whereClause), diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 3b69ab7ed5..5979f8224c 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -3280,8 +3280,8 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt, case OBJECT_SUBSCRIPTION: case OBJECT_TABLESPACE: ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("cannot add an object of this type to an extension"))); + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("cannot add an object of this type to an extension"))); break; default: /* OK */ diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 2baca12c5f..273d00bdb9 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -615,7 +615,8 @@ DefineIndex(Oid relationId, namespaceId = RelationGetNamespace(rel); /* Ensure that it makes sense to index this kind of relation */ - switch (rel->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { case RELKIND_RELATION: case RELKIND_MATVIEW: @@ -633,12 +634,16 @@ DefineIndex(Oid relationId, errmsg("cannot create index on foreign table \"%s\"", RelationGetRelationName(rel)))); break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table or materialized view", RelationGetRelationName(rel)))); - break; } /* @@ -1190,18 +1195,33 @@ DefineIndex(Oid relationId, * those if a regular index, or fail if trying to create a * constraint index. */ - if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + Assert(RELKIND_IS_VALID((RelKind) childrel->rd_rel->relkind)); + switch ((RelKind) childrel->rd_rel->relkind) { - if (stmt->unique || stmt->primary) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot create unique index on partitioned table \"%s\"", - RelationGetRelationName(rel)), - errdetail("Table \"%s\" contains partitions that are foreign tables.", - RelationGetRelationName(rel)))); - - table_close(childrel, lockmode); - continue; + case RELKIND_FOREIGN_TABLE: + { + if (stmt->unique || stmt->primary) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot create unique index on partitioned table \"%s\"", + RelationGetRelationName(rel)), + errdetail("Table \"%s\" contains partitions that are foreign tables.", + RelationGetRelationName(rel)))); + + table_close(childrel, lockmode); + continue; + } + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_VIEW: + break; } childidxs = RelationGetIndexList(childrel); @@ -2451,10 +2471,22 @@ ReindexIndex(RangeVar *indexRelation, int options, bool concurrent) */ irel = index_open(indOid, NoLock); - if (irel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) + Assert(RELKIND_IS_VALID((RelKind) irel->rd_rel->relkind)); + switch ((RelKind) irel->rd_rel->relkind) { - ReindexPartitionedIndex(irel); - return; + case RELKIND_PARTITIONED_INDEX: + ReindexPartitionedIndex(irel); + return; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } persistence = irel->rd_rel->relpersistence; @@ -2510,11 +2542,24 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation, relkind = get_rel_relkind(relId); if (!relkind) return; - if (relkind != RELKIND_INDEX && - relkind != RELKIND_PARTITIONED_INDEX) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not an index", relation->relname))); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not an index", relation->relname))); + } /* Check permissions */ if (!pg_class_ownercheck(relId, GetUserId())) @@ -2694,9 +2739,22 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind, * partitioned tables, and expand that afterwards into relids, * ignoring any duplicates. */ - if (classtuple->relkind != RELKIND_RELATION && - classtuple->relkind != RELKIND_MATVIEW) - continue; + Assert(RELKIND_IS_VALID((RelKind) classtuple->relkind)); + switch ((RelKind) classtuple->relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + continue; + } /* Skip temp tables of other backends; we can't reindex them at all */ if (classtuple->relpersistence == RELPERSISTENCE_TEMP && @@ -2867,7 +2925,8 @@ ReindexRelationConcurrently(Oid relationOid, int options) * Extract the list of indexes that are going to be rebuilt based on the * relation Oid given by caller. */ - switch (relkind) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { case RELKIND_RELATION: case RELKIND_MATVIEW: @@ -3017,12 +3076,15 @@ ReindexRelationConcurrently(Oid relationOid, int options) errmsg("REINDEX of partitioned tables is not yet implemented, skipping \"%s\"", get_rel_name(relationOid)))); return false; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: /* Return error if type of relation is not supported */ ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot reindex this type of relation concurrently"))); - break; } /* Definitely no indexes, so leave */ @@ -3443,30 +3505,41 @@ ReindexRelationConcurrently(Oid relationOid, int options) /* Log what we did */ if (options & REINDEXOPT_VERBOSE) { - if (relkind == RELKIND_INDEX) - ereport(INFO, - (errmsg("index \"%s.%s\" was reindexed", - relationNamespace, relationName), - errdetail("%s.", - pg_rusage_show(&ru0)))); - else + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - foreach(lc, newIndexIds) - { - Oid indOid = lfirst_oid(lc); - + case RELKIND_INDEX: ereport(INFO, (errmsg("index \"%s.%s\" was reindexed", - get_namespace_name(get_rel_namespace(indOid)), - get_rel_name(indOid)))); - /* Don't show rusage here, since it's not per index. */ - } + relationNamespace, relationName), + errdetail("%s.", + pg_rusage_show(&ru0)))); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + foreach(lc, newIndexIds) + { + Oid indOid = lfirst_oid(lc); - ereport(INFO, - (errmsg("table \"%s.%s\" was reindexed", - relationNamespace, relationName), - errdetail("%s.", - pg_rusage_show(&ru0)))); + ereport(INFO, + (errmsg("index \"%s.%s\" was reindexed", + get_namespace_name(get_rel_namespace(indOid)), + get_rel_name(indOid)))); + /* Don't show rusage here, since it's not per index. */ + } + + ereport(INFO, + (errmsg("table \"%s.%s\" was reindexed", + relationNamespace, relationName), + errdetail("%s.", + pg_rusage_show(&ru0)))); } } @@ -3508,8 +3581,23 @@ IndexSetParentIndex(Relation partitionIdx, Oid parentOid) bool fix_dependencies; /* Make sure this is an index */ - Assert(partitionIdx->rd_rel->relkind == RELKIND_INDEX || - partitionIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX); + Assert(RELKIND_IS_VALID((RelKind) partitionIdx->rd_rel->relkind)); + switch ((RelKind) partitionIdx->rd_rel->relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + break; /* ok */ + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + Assert(false); + break; + } /* * Scan pg_inherits for rows linking our index to some parent. diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index d8cafc42bb..56c6b2973c 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -56,10 +56,24 @@ LockTableCommand(LockStmt *lockstmt) RangeVarCallbackForLockTable, (void *) &lockstmt->mode); - if (get_rel_relkind(reloid) == RELKIND_VIEW) - LockViewRecurse(reloid, lockstmt->mode, lockstmt->nowait, NIL); - else if (recurse) - LockTableRecurse(reloid, lockstmt->mode, lockstmt->nowait); + Assert(RELKIND_IS_VALID((RelKind) get_rel_relkind(reloid))); + switch ((RelKind) get_rel_relkind(reloid)) + { + case RELKIND_VIEW: + LockViewRecurse(reloid, lockstmt->mode, lockstmt->nowait, NIL); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + if (recurse) + LockTableRecurse(reloid, lockstmt->mode, lockstmt->nowait); + } } } @@ -84,12 +98,25 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid, * check */ /* Currently, we only allow plain tables or views to be locked */ - if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE && - relkind != RELKIND_VIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or view", - rv->relname))); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table or view", + rv->relname))); + } /* * Make note if a temporary relation has been accessed in this @@ -201,11 +228,30 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context) strcmp(rte->eref->aliasname, "new") == 0)) continue; - /* Currently, we only allow plain tables or views to be locked. */ - if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE && - relkind != RELKIND_VIEW) + if (relkind == '\0') continue; + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + /* + * Currently, we only allow plain tables or views to be + * locked. + */ + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + continue; + } + /* Check infinite recursion in the view definition. */ if (list_member_oid(context->ancestor_views, relid)) ereport(ERROR, @@ -227,10 +273,25 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context) errmsg("could not obtain lock on relation \"%s\"", relname))); - if (relkind == RELKIND_VIEW) - LockViewRecurse(relid, context->lockmode, context->nowait, context->ancestor_views); - else if (rte->inh) - LockTableRecurse(relid, context->lockmode, context->nowait); + /* We already checked for relkind == '\0', above */ + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_VIEW: + LockViewRecurse(relid, context->lockmode, context->nowait, context->ancestor_views); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + if (rte->inh) + LockTableRecurse(relid, context->lockmode, context->nowait); + } } return query_tree_walker(query, diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c index 4b4e469493..ff8affa074 100644 --- a/src/backend/commands/policy.c +++ b/src/backend/commands/policy.c @@ -89,10 +89,24 @@ RangeVarCallbackForPolicy(const RangeVar *rv, Oid relid, Oid oldrelid, rv->relname))); /* Relation type MUST be a table. */ - if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table", rv->relname))); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", rv->relname))); + } ReleaseSysCache(tuple); } @@ -388,12 +402,25 @@ RemovePolicyById(Oid policy_id) relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid; rel = table_open(relid, AccessExclusiveLock); - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table", - RelationGetRelationName(rel)))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", + RelationGetRelationName(rel)))); + } if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, @@ -478,12 +505,25 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id) rel = relation_open(relid, AccessExclusiveLock); - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table", - RelationGetRelationName(rel)))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", + RelationGetRelationName(rel)))); + } if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index eabbc7473b..dbc5624285 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -548,33 +548,48 @@ OpenTableList(List *tables) * children other than its partitions, which need not be explicitly * added to the publication. */ - if (recurse && rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - List *children; - ListCell *child; - - children = find_all_inheritors(myrelid, ShareUpdateExclusiveLock, - NULL); - - foreach(child, children) - { - Oid childrelid = lfirst_oid(child); - - /* Allow query cancel in case this takes a long time */ - CHECK_FOR_INTERRUPTS(); - - /* - * Skip duplicates if user specified both parent and child - * tables. - */ - if (list_member_oid(relids, childrelid)) - continue; - - /* find_all_inheritors already got lock */ - rel = table_open(childrelid, NoLock); - rels = lappend(rels, rel); - relids = lappend_oid(relids, childrelid); - } + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + if (recurse) + { + List *children; + ListCell *child; + + children = find_all_inheritors(myrelid, ShareUpdateExclusiveLock, + NULL); + + foreach(child, children) + { + Oid childrelid = lfirst_oid(child); + + /* Allow query cancel in case this takes a long time */ + CHECK_FOR_INTERRUPTS(); + + /* + * Skip duplicates if user specified both parent and + * child tables. + */ + if (list_member_oid(relids, childrelid)) + continue; + + /* find_all_inheritors already got lock */ + rel = table_open(childrelid, NoLock); + rels = lappend(rels, rel); + relids = lappend_oid(relids, childrelid); + } + } } } diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c index ee036e9087..4ddf7616de 100644 --- a/src/backend/commands/seclabel.c +++ b/src/backend/commands/seclabel.c @@ -180,16 +180,25 @@ ExecSecLabelStmt(SecLabelStmt *stmt) * materialized views, composite types, and foreign tables (which * are the only relkinds for which pg_dump will dump labels). */ - if (relation->rd_rel->relkind != RELKIND_RELATION && - relation->rd_rel->relkind != RELKIND_VIEW && - relation->rd_rel->relkind != RELKIND_MATVIEW && - relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE && - relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE && - relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table", - RelationGetRelationName(relation)))); + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table", + RelationGetRelationName(relation)))); + } break; default: break; diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 6aab73bfd4..803eb6bc8a 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1675,14 +1675,25 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity) tablerel = relation_openrv(rel, AccessShareLock); /* Must be a regular or foreign table */ - if (!(tablerel->rd_rel->relkind == RELKIND_RELATION || - tablerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE || - tablerel->rd_rel->relkind == RELKIND_VIEW || - tablerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("referenced relation \"%s\" is not a table or foreign table", - RelationGetRelationName(tablerel)))); + Assert(RELKIND_IS_VALID((RelKind) tablerel->rd_rel->relkind)); + switch ((RelKind) tablerel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("referenced relation \"%s\" is not a table or foreign table", + RelationGetRelationName(tablerel)))); + } /* We insist on same owner and schema */ if (seqrel->rd_rel->relowner != tablerel->rd_rel->relowner) diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c index 974828545c..1cec7552d4 100644 --- a/src/backend/commands/statscmds.c +++ b/src/backend/commands/statscmds.c @@ -122,14 +122,25 @@ CreateStatistics(CreateStatsStmt *stmt) rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock); /* Restrict to allowed relation types */ - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_MATVIEW && - rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && - rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("relation \"%s\" is not a table, foreign table, or materialized view", - RelationGetRelationName(rel)))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("relation \"%s\" is not a table, foreign table, or materialized view", + RelationGetRelationName(rel)))); + } /* You must own the relation to create stats on it */ if (!pg_class_ownercheck(RelationGetRelid(rel), stxowner)) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 20049f2c55..7eba9cc4dc 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -619,8 +619,22 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, if (stmt->partspec != NULL) { - if (relkind != RELKIND_RELATION) - elog(ERROR, "unexpected relkind: %d", (int) relkind); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + elog(ERROR, "unexpected relkind: %d", (int) relkind); + } relkind = RELKIND_PARTITIONED_TABLE; partitioned = true; @@ -746,7 +760,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps, true, false); - switch (relkind) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { case RELKIND_VIEW: (void) view_reloptions(reloptions, true); @@ -754,7 +769,14 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, case RELKIND_PARTITIONED_TABLE: (void) partitioned_table_reloptions(reloptions, true); break; - default: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: (void) heap_reloptions(relkind, reloptions, true); } @@ -866,10 +888,26 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, errmsg("specifying a table access method is not supported on a partitioned table"))); } - else if (relkind == RELKIND_RELATION || - relkind == RELKIND_TOASTVALUE || - relkind == RELKIND_MATVIEW) - accessMethod = default_table_access_method; + else + { + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + accessMethod = default_table_access_method; + break; + case RELKIND_VIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + break; + } + } /* look up the access method, verify it is for a table */ if (accessMethod != NULL) @@ -956,11 +994,25 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, * We are going to try to validate the partition bound specification * against the partition key of parentRel, so it better have one. */ - if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("\"%s\" is not partitioned", - RelationGetRelationName(parent)))); + Assert(RELKIND_IS_VALID((RelKind) parent->rd_rel->relkind)); + switch ((RelKind) parent->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("\"%s\" is not partitioned", + RelationGetRelationName(parent)))); + } /* * The partition constraint of the default partition depends on the @@ -1104,20 +1156,33 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, IndexStmt *idxstmt; Oid constraintOid; - if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - if (idxRel->rd_index->indisunique) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot create foreign partition of partitioned table \"%s\"", - RelationGetRelationName(parent)), - errdetail("Table \"%s\" contains indexes that are unique.", - RelationGetRelationName(parent)))); - else - { - index_close(idxRel, AccessShareLock); - continue; - } + case RELKIND_FOREIGN_TABLE: + if (idxRel->rd_index->indisunique) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot create foreign partition of partitioned table \"%s\"", + RelationGetRelationName(parent)), + errdetail("Table \"%s\" contains indexes that are unique.", + RelationGetRelationName(parent)))); + else + { + index_close(idxRel, AccessShareLock); + continue; + } + break; + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + break; } attmap = build_attrmap_by_name(RelationGetDescr(rel), @@ -1448,12 +1513,25 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem * exists with indexes. */ - if (classform->relkind == RELKIND_PARTITIONED_TABLE) - expected_relkind = RELKIND_RELATION; - else if (classform->relkind == RELKIND_PARTITIONED_INDEX) - expected_relkind = RELKIND_INDEX; - else - expected_relkind = classform->relkind; + Assert(RELKIND_IS_VALID((RelKind) classform->relkind)); + switch ((RelKind) classform->relkind) + { + case RELKIND_PARTITIONED_TABLE: + expected_relkind = RELKIND_RELATION; + break; + case RELKIND_PARTITIONED_INDEX: + expected_relkind = RELKIND_INDEX; + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + expected_relkind = classform->relkind; + } if (relkind != expected_relkind) DropErrorMsgWrongType(rel->relname, classform->relkind, relkind); @@ -1470,26 +1548,42 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, * only concerns indexes of toast relations that became invalid during a * REINDEX CONCURRENTLY process. */ - if (IsSystemClass(relOid, classform) && relkind == RELKIND_INDEX) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - HeapTuple locTuple; - Form_pg_index indexform; - bool indisvalid; + case RELKIND_INDEX: + if (IsSystemClass(relOid, classform)) + { + HeapTuple locTuple; + Form_pg_index indexform; + bool indisvalid; - locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid)); - if (!HeapTupleIsValid(locTuple)) - { - ReleaseSysCache(tuple); - return; - } + locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid)); + if (!HeapTupleIsValid(locTuple)) + { + ReleaseSysCache(tuple); + return; + } - indexform = (Form_pg_index) GETSTRUCT(locTuple); - indisvalid = indexform->indisvalid; - ReleaseSysCache(locTuple); + indexform = (Form_pg_index) GETSTRUCT(locTuple); + indisvalid = indexform->indisvalid; + ReleaseSysCache(locTuple); - /* Mark object as being an invalid index of system catalogs */ - if (!indisvalid) - invalid_system_index = true; + /* Mark object as being an invalid index of system catalogs */ + if (!indisvalid) + invalid_system_index = true; + } + break; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_SEQUENCE: + break; } /* In the case of an invalid index, it is fine to bypass this check */ @@ -1508,12 +1602,27 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, * we do it the other way around. No error if we don't find a pg_index * entry, though --- the relation may have been dropped. */ - if ((relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX) && - relOid != oldRelOid) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - state->heapOid = IndexGetRelation(relOid, true); - if (OidIsValid(state->heapOid)) - LockRelationOid(state->heapOid, heap_lockmode); + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + if (relOid != oldRelOid) + { + state->heapOid = IndexGetRelation(relOid, true); + if (OidIsValid(state->heapOid)) + LockRelationOid(state->heapOid, heap_lockmode); + } + break; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_SEQUENCE: + break; } /* @@ -1635,11 +1744,29 @@ ExecuteTruncate(TruncateStmt *stmt) relids_logged = lappend_oid(relids_logged, childrelid); } } - else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot truncate only a partitioned table"), - errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly."))); + else + { + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot truncate only a partitioned table"), + errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly."))); + break; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_SEQUENCE: + break; + } + } } ExecuteTruncateGuts(rels, relids, relids_logged, @@ -1815,9 +1942,23 @@ ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged, { Relation rel = (Relation) lfirst(cell); - /* Skip partitioned tables as there is nothing to do */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - continue; + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + /* Skip partitioned tables as there is nothing to do */ + case RELKIND_PARTITIONED_TABLE: + continue; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_SEQUENCE: + break; + } /* * Normally, we need a transaction-safe truncation here. However, if @@ -1969,11 +2110,24 @@ truncate_check_rel(Oid relid, Form_pg_class reltuple) * the latter are only being included here for the following checks; no * physical truncation will occur in their case.) */ - if (reltuple->relkind != RELKIND_RELATION && - reltuple->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table", relname))); + Assert(RELKIND_IS_VALID((RelKind) reltuple->relkind)); + switch ((RelKind) reltuple->relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_FOREIGN_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_SEQUENCE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", relname))); + } if (!allowSystemTableMods && IsSystemClass(relid, reltuple)) ereport(ERROR, @@ -2235,26 +2389,38 @@ MergeAttributes(List *schema, List *supers, char relpersistence, * We do not allow partitioned tables and partitions to participate in * regular inheritance. */ - if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && - !is_partition) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot inherit from partitioned table \"%s\"", - RelationGetRelationName(relation)))); + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + if (!is_partition) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot inherit from partitioned table \"%s\"", + RelationGetRelationName(relation)))); + break; + case RELKIND_RELATION: + case RELKIND_FOREIGN_TABLE: + break; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_SEQUENCE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("inherited relation \"%s\" is not a table or foreign table", + RelationGetRelationName(relation)))); + } + if (relation->rd_rel->relispartition && !is_partition) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot inherit from partition \"%s\"", RelationGetRelationName(relation)))); - if (relation->rd_rel->relkind != RELKIND_RELATION && - relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE && - relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("inherited relation \"%s\" is not a table or foreign table", - RelationGetRelationName(relation)))); - /* * If the parent is permanent, so must be all of its partitions. Note * that inheritance allows that case. @@ -2996,18 +3162,25 @@ renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing) * change names that are hardcoded into the system, hence the following * restriction. */ - if (relkind != RELKIND_RELATION && - relkind != RELKIND_VIEW && - relkind != RELKIND_MATVIEW && - relkind != RELKIND_COMPOSITE_TYPE && - relkind != RELKIND_INDEX && - relkind != RELKIND_PARTITIONED_INDEX && - relkind != RELKIND_FOREIGN_TABLE && - relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, materialized view, composite type, index, or foreign table", - NameStr(classform->relname)))); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_TOASTVALUE: + case RELKIND_SEQUENCE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, view, materialized view, composite type, index, or foreign table", + NameStr(classform->relname)))); + } /* * permissions checking. only the owner of a class can change its schema. @@ -3105,17 +3278,32 @@ renameatt_internal(Oid myrelid, } /* rename attributes in typed tables of composite type */ - if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) + Assert(RELKIND_IS_VALID((RelKind) targetrelation->rd_rel->relkind)); + switch ((RelKind) targetrelation->rd_rel->relkind) { - List *child_oids; - ListCell *lo; + case RELKIND_COMPOSITE_TYPE: + { + List *child_oids; + ListCell *lo; - child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype, - RelationGetRelationName(targetrelation), - behavior); + child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype, + RelationGetRelationName(targetrelation), + behavior); - foreach(lo, child_oids) - renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior); + foreach(lo, child_oids) + renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior); + } + break; + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_SEQUENCE: + break; } attrelation = table_open(AttributeRelationId, RowExclusiveLock); @@ -3489,13 +3677,27 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bo /* * Also rename the associated constraint, if any. */ - if (targetrelation->rd_rel->relkind == RELKIND_INDEX || - targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) + Assert(RELKIND_IS_VALID((RelKind) targetrelation->rd_rel->relkind)); + switch ((RelKind) targetrelation->rd_rel->relkind) { - Oid constraintId = get_index_constraint(myrelid); + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + { + Oid constraintId = get_index_constraint(myrelid); - if (OidIsValid(constraintId)) - RenameConstraintById(constraintId, newrelname); + if (OidIsValid(constraintId)) + RenameConstraintById(constraintId, newrelname); + } + break; + case RELKIND_COMPOSITE_TYPE: + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_SEQUENCE: + break; } /* @@ -3542,14 +3744,27 @@ CheckTableNotInUse(Relation rel, const char *stmt) errmsg("cannot %s \"%s\" because it is being used by active queries in this session", stmt, RelationGetRelationName(rel)))); - if (rel->rd_rel->relkind != RELKIND_INDEX && - rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX && - AfterTriggerPendingOnRel(RelationGetRelid(rel))) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_IN_USE), - /* translator: first %s is a SQL command, eg ALTER TABLE */ - errmsg("cannot %s \"%s\" because it has pending trigger events", - stmt, RelationGetRelationName(rel)))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + break; + case RELKIND_COMPOSITE_TYPE: + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_SEQUENCE: + if (AfterTriggerPendingOnRel(RelationGetRelid(rel))) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_IN_USE), + /* translator: first %s is a SQL command, eg ALTER TABLE */ + errmsg("cannot %s \"%s\" because it has pending trigger events", + stmt, RelationGetRelationName(rel)))); + } } /* @@ -4364,11 +4579,26 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, * not modify anything about it that will change its toasting * requirement, so no need to check. */ - if (((tab->relkind == RELKIND_RELATION || - tab->relkind == RELKIND_PARTITIONED_TABLE) && - tab->partition_constraint == NULL) || - tab->relkind == RELKIND_MATVIEW) - AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode); + Assert(RELKIND_IS_VALID((RelKind) tab->relkind)); + switch ((RelKind) tab->relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + if (tab->partition_constraint != NULL) + break; + /* fallthrough */ + case RELKIND_MATVIEW: + AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode); + break; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_VIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_SEQUENCE: + break; + } } } @@ -4546,15 +4776,28 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, break; case AT_SetTableSpace: /* SET TABLESPACE */ - /* - * Only do this for partitioned tables and indexes, for which this - * is just a catalog change. Other relation types which have - * storage are handled by Phase 3. - */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE || - rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) - ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace); - + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + /* + * Only do this for partitioned tables and indexes, for + * which this is just a catalog change. Other relation + * types which have storage are handled by Phase 3. + */ + case RELKIND_PARTITIONED_TABLE: + case RELKIND_PARTITIONED_INDEX: + ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace); + break; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_VIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_SEQUENCE: + break; + } break; case AT_SetRelOptions: /* SET (...) */ case AT_ResetRelOptions: /* RESET (...) */ @@ -4645,11 +4888,24 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode, cur_pass, context); Assert(cmd != NULL); - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def); - else - ATExecAttachPartitionIdx(wqueue, rel, - ((PartitionCmd *) cmd->def)->name); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_VIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_SEQUENCE: + ATExecAttachPartitionIdx(wqueue, rel, + ((PartitionCmd *) cmd->def)->name); + } break; case AT_DetachPartition: cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode, @@ -5477,7 +5733,8 @@ ATSimplePermissions(Relation rel, int allowed_targets) { int actual_target; - switch (rel->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { case RELKIND_RELATION: case RELKIND_PARTITIONED_TABLE: @@ -5501,9 +5758,9 @@ ATSimplePermissions(Relation rel, int allowed_targets) case RELKIND_FOREIGN_TABLE: actual_target = ATT_FOREIGN_TABLE; break; - default: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: actual_target = 0; - break; } /* Wrong target type? */ @@ -5600,40 +5857,53 @@ ATSimpleRecursion(List **wqueue, Relation rel, * and partitioned tables have children, so no need to search for other * relkinds. */ - if (recurse && - (rel->rd_rel->relkind == RELKIND_RELATION || - rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE || - rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - Oid relid = RelationGetRelid(rel); - ListCell *child; - List *children; - - children = find_all_inheritors(relid, lockmode, NULL); + case RELKIND_RELATION: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + if (recurse) + { + Oid relid = RelationGetRelid(rel); + ListCell *child; + List *children; - /* - * find_all_inheritors does the recursive search of the inheritance - * hierarchy, so all we have to do is process all of the relids in the - * list that it returns. - */ - foreach(child, children) - { - Oid childrelid = lfirst_oid(child); - Relation childrel; + children = find_all_inheritors(relid, lockmode, NULL); - if (childrelid == relid) - continue; - /* find_all_inheritors already got lock */ - childrel = relation_open(childrelid, NoLock); - CheckTableNotInUse(childrel, "ALTER TABLE"); - ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context); - relation_close(childrel, NoLock); - } - } -} + /* + * find_all_inheritors does the recursive search of the + * inheritance hierarchy, so all we have to do is process all + * of the relids in the list that it returns. + */ + foreach(child, children) + { + Oid childrelid = lfirst_oid(child); + Relation childrel; -/* - * Obtain list of partitions of the given table, locking them all at the given + if (childrelid == relid) + continue; + /* find_all_inheritors already got lock */ + childrel = relation_open(childrelid, NoLock); + CheckTableNotInUse(childrel, "ALTER TABLE"); + ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context); + relation_close(childrel, NoLock); + } + } + break; + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } +} + +/* + * Obtain list of partitions of the given table, locking them all at the given * lockmode and ensuring that they all pass CheckTableNotInUse. * * This function is a no-op if the given relation is not a partitioned table; @@ -5773,47 +6043,60 @@ find_composite_type_dependencies(Oid typeOid, Relation origRelation, rel = relation_open(pg_depend->objid, AccessShareLock); att = TupleDescAttr(rel->rd_att, pg_depend->objsubid - 1); - if (rel->rd_rel->relkind == RELKIND_RELATION || - rel->rd_rel->relkind == RELKIND_MATVIEW || - rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - { - if (origTypeName) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it", - origTypeName, - RelationGetRelationName(rel), - NameStr(att->attname)))); - else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it", - RelationGetRelationName(origRelation), - RelationGetRelationName(rel), - NameStr(att->attname)))); - else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type", - RelationGetRelationName(origRelation), - RelationGetRelationName(rel), - NameStr(att->attname)))); - else - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type", - RelationGetRelationName(origRelation), - RelationGetRelationName(rel), - NameStr(att->attname)))); - } - else if (OidIsValid(rel->rd_rel->reltype)) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - /* - * A view or composite type itself isn't a problem, but we must - * recursively check for indirect dependencies via its rowtype. - */ - find_composite_type_dependencies(rel->rd_rel->reltype, - origRelation, origTypeName); + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + { + if (origTypeName) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it", + origTypeName, + RelationGetRelationName(rel), + NameStr(att->attname)))); + else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it", + RelationGetRelationName(origRelation), + RelationGetRelationName(rel), + NameStr(att->attname)))); + else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type", + RelationGetRelationName(origRelation), + RelationGetRelationName(rel), + NameStr(att->attname)))); + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type", + RelationGetRelationName(origRelation), + RelationGetRelationName(rel), + NameStr(att->attname)))); + } + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + if (OidIsValid(rel->rd_rel->reltype)) + { + /* + * A view or composite type itself isn't a problem, but we + * must recursively check for indirect dependencies via + * its rowtype. + */ + find_composite_type_dependencies(rel->rd_rel->reltype, + origRelation, origTypeName); + } } relation_close(rel, AccessShareLock); @@ -5934,8 +6217,23 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot add column to typed table"))); - if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) - ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_COMPOSITE_TYPE: + ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context); + break; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } if (recurse && !is_view) cmd->subtype = AT_AddColumnRecurse; @@ -6455,16 +6753,31 @@ ATPrepDropNotNull(Relation rel, bool recurse, bool recursing) * If the parent is a partitioned table, like check constraints, we do not * support removing the NOT NULL while partitions exist. */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - PartitionDesc partdesc = RelationGetPartitionDesc(rel); + case RELKIND_PARTITIONED_TABLE: + { + PartitionDesc partdesc = RelationGetPartitionDesc(rel); - Assert(partdesc != NULL); - if (partdesc->nparts > 0 && !recurse && !recursing) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("cannot remove constraint from only the partitioned table when partitions exist"), - errhint("Do not specify the ONLY keyword."))); + Assert(partdesc != NULL); + if (partdesc->nparts > 0 && !recurse && !recursing) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot remove constraint from only the partitioned table when partitions exist"), + errhint("Do not specify the ONLY keyword."))); + } + break; + case RELKIND_COMPOSITE_TYPE: + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } } @@ -6614,17 +6927,31 @@ ATPrepSetNotNull(List **wqueue, Relation rel, * apply ALTER TABLE ... CHECK NOT NULL to every child. Otherwise, use * normal recursion logic. */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && - !recurse) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - AlterTableCmd *newcmd = makeNode(AlterTableCmd); + case RELKIND_PARTITIONED_TABLE: + if (!recurse) + { + AlterTableCmd *newcmd = makeNode(AlterTableCmd); - newcmd->subtype = AT_CheckNotNull; - newcmd->name = pstrdup(cmd->name); - ATSimpleRecursion(wqueue, rel, newcmd, true, lockmode, context); + newcmd->subtype = AT_CheckNotNull; + newcmd->name = pstrdup(cmd->name); + ATSimpleRecursion(wqueue, rel, newcmd, true, lockmode, context); + break; + } + /* fallthrough */ + case RELKIND_COMPOSITE_TYPE: + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context); } - else - ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context); } /* @@ -7249,12 +7576,25 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa * We allow referencing columns by numbers only for indexes, since table * column numbers could contain gaps if columns are later dropped. */ - if (rel->rd_rel->relkind != RELKIND_INDEX && - rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX && - !colName) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot refer to non-index column by number"))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + break; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + if (!colName) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot refer to non-index column by number"))); + } Assert(IsA(newValue, Integer)); newtarget = intVal(newValue); @@ -7310,20 +7650,34 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa errmsg("cannot alter system column \"%s\"", colName))); - if (rel->rd_rel->relkind == RELKIND_INDEX || - rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - if (attnum > rel->rd_index->indnkeyatts) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"", - NameStr(attrtuple->attname), RelationGetRelationName(rel)))); - else if (rel->rd_index->indkey.values[attnum - 1] != 0) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"", - NameStr(attrtuple->attname), RelationGetRelationName(rel)), - errhint("Alter statistics on table column instead."))); + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + { + if (attnum > rel->rd_index->indnkeyatts) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"", + NameStr(attrtuple->attname), RelationGetRelationName(rel)))); + else if (rel->rd_index->indkey.values[attnum - 1] != 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"", + NameStr(attrtuple->attname), RelationGetRelationName(rel)), + errhint("Alter statistics on table column instead."))); + } + break; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } attrtuple->attstattarget = newtarget; @@ -7566,8 +7920,23 @@ ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot drop column from typed table"))); - if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) - ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_COMPOSITE_TYPE: + ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context); + break; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } if (recurse) cmd->subtype = AT_DropColumnRecurse; @@ -7676,15 +8045,31 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, Relation attr_rel; ListCell *child; - /* - * In case of a partitioned table, the column must be dropped from the - * partitions as well. - */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("cannot drop column from only the partitioned table when partitions exist"), - errhint("Do not specify the ONLY keyword."))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + /* + * In case of a partitioned table, the column must be dropped + * from the partitions as well. + */ + case RELKIND_PARTITIONED_TABLE: + if (!recurse) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot drop column from only the partitioned table when partitions exist"), + errhint("Do not specify the ONLY keyword."))); + break; + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } attr_rel = table_open(AttributeRelationId, RowExclusiveLock); foreach(child, children) @@ -7861,10 +8246,25 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, * Doing this on partitioned tables is not a simple feature to implement, * so let's punt for now. */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables"))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables"))); + break; + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } indexRel = index_open(index_oid, AccessShareLock); @@ -8218,30 +8618,42 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, * Validity checks (permission checks wait till we have the column * numbers) */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - if (!recurse) + case RELKIND_PARTITIONED_TABLE: + { + if (!recurse) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"", + RelationGetRelationName(rel), + RelationGetRelationName(pkrel)))); + if (fkconstraint->skip_validation && !fkconstraint->initially_valid) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"", + RelationGetRelationName(rel), + RelationGetRelationName(pkrel)), + errdetail("This feature is not yet supported on partitioned tables."))); + } + break; + case RELKIND_RELATION: + break; + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"", - RelationGetRelationName(rel), + errmsg("referenced relation \"%s\" is not a table", RelationGetRelationName(pkrel)))); - if (fkconstraint->skip_validation && !fkconstraint->initially_valid) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"", - RelationGetRelationName(rel), - RelationGetRelationName(pkrel)), - errdetail("This feature is not yet supported on partitioned tables."))); } - if (pkrel->rd_rel->relkind != RELKIND_RELATION && - pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("referenced relation \"%s\" is not a table", - RelationGetRelationName(pkrel)))); - if (!allowSystemTableMods && IsSystemRelation(pkrel)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -8640,12 +9052,25 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * Verify relkind for each referenced partition. At the top level, this * is redundant with a previous check, but we need it when recursing. */ - if (pkrel->rd_rel->relkind != RELKIND_RELATION && - pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("referenced relation \"%s\" is not a table", - RelationGetRelationName(pkrel)))); + Assert(RELKIND_IS_VALID((RelKind) pkrel->rd_rel->relkind)); + switch ((RelKind) pkrel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("referenced relation \"%s\" is not a table", + RelationGetRelationName(pkrel)))); + } /* * Caller supplies us with a constraint name; however, it may be used in @@ -8675,7 +9100,22 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, /* * always inherit for partitioned tables, never for legacy inheritance */ - connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE; + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + connoinherit = false; + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + connoinherit = true; + } } /* @@ -8730,69 +9170,83 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, /* make new constraint visible, in case we add more */ CommandCounterIncrement(); - /* - * If the referenced table is a plain relation, create the action triggers - * that enforce the constraint. - */ - if (pkrel->rd_rel->relkind == RELKIND_RELATION) + Assert(RELKIND_IS_VALID((RelKind) pkrel->rd_rel->relkind)); + switch ((RelKind) pkrel->rd_rel->relkind) { - createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel), - fkconstraint, - constrOid, indexOid); - } - - /* - * If the referenced table is partitioned, recurse on ourselves to handle - * each partition. We need one pg_constraint row created for each - * partition in addition to the pg_constraint row for the parent table. - */ - if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - { - PartitionDesc pd = RelationGetPartitionDesc(pkrel); - - for (int i = 0; i < pd->nparts; i++) - { - Relation partRel; - AttrMap *map; - AttrNumber *mapped_pkattnum; - Oid partIndexId; - - partRel = table_open(pd->oids[i], ShareRowExclusiveLock); + /* + * If the referenced table is a plain relation, create the action + * triggers that enforce the constraint. + */ + case RELKIND_RELATION: + createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel), + fkconstraint, + constrOid, indexOid); + break; /* - * Map the attribute numbers in the referenced side of the FK - * definition to match the partition's column layout. + * If the referenced table is partitioned, recurse on ourselves to + * handle each partition. We need one pg_constraint row created + * for each partition in addition to the pg_constraint row for the + * parent table. */ - map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); - if (map) - { - mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); - for (int j = 0; j < numfks; j++) - mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1]; - } - else - mapped_pkattnum = pkattnum; - - /* do the deed */ - partIndexId = index_get_partition(partRel, indexOid); - if (!OidIsValid(partIndexId)) - elog(ERROR, "index for %u not found in partition %s", - indexOid, RelationGetRelationName(partRel)); - addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel, - partIndexId, constrOid, numfks, - mapped_pkattnum, fkattnum, - pfeqoperators, ppeqoperators, ffeqoperators, - old_check_ok); - - /* Done -- clean up (but keep the lock) */ - table_close(partRel, NoLock); - if (map) + case RELKIND_PARTITIONED_TABLE: { - pfree(mapped_pkattnum); - free_attrmap(map); + PartitionDesc pd = RelationGetPartitionDesc(pkrel); + + for (int i = 0; i < pd->nparts; i++) + { + Relation partRel; + AttrMap *map; + AttrNumber *mapped_pkattnum; + Oid partIndexId; + + partRel = table_open(pd->oids[i], ShareRowExclusiveLock); + + /* + * Map the attribute numbers in the referenced side of the + * FK definition to match the partition's column layout. + */ + map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), + RelationGetDescr(pkrel)); + if (map) + { + mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); + for (int j = 0; j < numfks; j++) + mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1]; + } + else + mapped_pkattnum = pkattnum; + + /* do the deed */ + partIndexId = index_get_partition(partRel, indexOid); + if (!OidIsValid(partIndexId)) + elog(ERROR, "index for %u not found in partition %s", + indexOid, RelationGetRelationName(partRel)); + addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel, + partIndexId, constrOid, numfks, + mapped_pkattnum, fkattnum, + pfeqoperators, ppeqoperators, ffeqoperators, + old_check_ok); + + /* Done -- clean up (but keep the lock) */ + table_close(partRel, NoLock); + if (map) + { + pfree(mapped_pkattnum); + free_attrmap(map); + } + } } - } + break; + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } return address; @@ -8836,177 +9290,195 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, { AssertArg(OidIsValid(parentConstr)); - if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("foreign key constraints are not supported on foreign tables"))); - - /* - * If the referencing relation is a plain table, add the check triggers to - * it and, if necessary, schedule it to be checked in Phase 3. - * - * If the relation is partitioned, drill down to do it to its partitions. - */ - if (rel->rd_rel->relkind == RELKIND_RELATION) - { - createForeignKeyCheckTriggers(RelationGetRelid(rel), - RelationGetRelid(pkrel), - fkconstraint, - parentConstr, - indexOid); - - /* - * Tell Phase 3 to check that the constraint is satisfied by existing - * rows. We can skip this during table creation, when requested - * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command, - * and when we're recreating a constraint following a SET DATA TYPE - * operation that did not impugn its validity. - */ - if (wqueue && !old_check_ok && !fkconstraint->skip_validation) - { - NewConstraint *newcon; - AlteredTableInfo *tab; - - tab = ATGetQueueEntry(wqueue, rel); - - newcon = (NewConstraint *) palloc0(sizeof(NewConstraint)); - newcon->name = get_constraint_name(parentConstr); - newcon->contype = CONSTR_FOREIGN; - newcon->refrelid = RelationGetRelid(pkrel); - newcon->refindid = indexOid; - newcon->conid = parentConstr; - newcon->qual = (Node *) fkconstraint; - - tab->constraints = lappend(tab->constraints, newcon); - } - } - else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - PartitionDesc pd = RelationGetPartitionDesc(rel); - - /* - * Recurse to take appropriate action on each partition; either we - * find an existing constraint to reparent to ours, or we create a new - * one. - */ - for (int i = 0; i < pd->nparts; i++) - { - Oid partitionId = pd->oids[i]; - Relation partition = table_open(partitionId, lockmode); - List *partFKs; - AttrMap *attmap; - AttrNumber mapped_fkattnum[INDEX_MAX_KEYS]; - bool attached; - char *conname; - Oid constrOid; - ObjectAddress address, - referenced; - ListCell *cell; - - CheckTableNotInUse(partition, "ALTER TABLE"); - - attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); - for (int j = 0; j < numfks; j++) - mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; - - /* Check whether an existing constraint can be repurposed */ - partFKs = copyObject(RelationGetFKeyList(partition)); - attached = false; - foreach(cell, partFKs) - { - ForeignKeyCacheInfo *fk; - - fk = lfirst_node(ForeignKeyCacheInfo, cell); - if (tryAttachPartitionForeignKey(fk, - partitionId, - parentConstr, - numfks, - mapped_fkattnum, - pkattnum, - pfeqoperators)) - { - attached = true; - break; - } - } - if (attached) - { - table_close(partition, NoLock); - continue; - } - /* - * No luck finding a good constraint to reuse; create our own. + * If the referencing relation is a plain table, add the check + * triggers to it and, if necessary, schedule it to be checked in + * Phase 3. + * + * If the relation is partitioned, drill down to do it to its + * partitions. */ - if (ConstraintNameIsUsed(CONSTRAINT_RELATION, - RelationGetRelid(partition), - fkconstraint->conname)) - conname = ChooseConstraintName(RelationGetRelationName(partition), - ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs), - "fkey", - RelationGetNamespace(partition), NIL); - else - conname = fkconstraint->conname; - constrOid = - CreateConstraintEntry(conname, - RelationGetNamespace(partition), - CONSTRAINT_FOREIGN, - fkconstraint->deferrable, - fkconstraint->initdeferred, - fkconstraint->initially_valid, - parentConstr, - partitionId, - mapped_fkattnum, - numfks, - numfks, - InvalidOid, - indexOid, - RelationGetRelid(pkrel), - pkattnum, - pfeqoperators, - ppeqoperators, - ffeqoperators, - numfks, - fkconstraint->fk_upd_action, - fkconstraint->fk_del_action, - fkconstraint->fk_matchtype, - NULL, - NULL, - NULL, - false, - 1, - false, - false); + case RELKIND_RELATION: + { + createForeignKeyCheckTriggers(RelationGetRelid(rel), + RelationGetRelid(pkrel), + fkconstraint, + parentConstr, + indexOid); - /* - * Give this constraint partition-type dependencies on the parent - * constraint as well as the table. - */ - ObjectAddressSet(address, ConstraintRelationId, constrOid); - ObjectAddressSet(referenced, ConstraintRelationId, parentConstr); - recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI); - ObjectAddressSet(referenced, RelationRelationId, partitionId); - recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC); + /* + * Tell Phase 3 to check that the constraint is satisfied by + * existing rows. We can skip this during table creation, when + * requested explicitly by specifying NOT VALID in an ADD + * FOREIGN KEY command, and when we're recreating a constraint + * following a SET DATA TYPE operation that did not impugn its + * validity. + */ + if (wqueue && !old_check_ok && !fkconstraint->skip_validation) + { + NewConstraint *newcon; + AlteredTableInfo *tab; - /* Make all this visible before recursing */ - CommandCounterIncrement(); + tab = ATGetQueueEntry(wqueue, rel); - /* call ourselves to finalize the creation and we're done */ - addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel, - indexOid, - constrOid, - numfks, - pkattnum, - mapped_fkattnum, - pfeqoperators, - ppeqoperators, - ffeqoperators, - old_check_ok, - lockmode); - - table_close(partition, NoLock); - } + newcon = (NewConstraint *) palloc0(sizeof(NewConstraint)); + newcon->name = get_constraint_name(parentConstr); + newcon->contype = CONSTR_FOREIGN; + newcon->refrelid = RelationGetRelid(pkrel); + newcon->refindid = indexOid; + newcon->conid = parentConstr; + newcon->qual = (Node *) fkconstraint; + + tab->constraints = lappend(tab->constraints, newcon); + } + } + break; + case RELKIND_PARTITIONED_TABLE: + { + PartitionDesc pd = RelationGetPartitionDesc(rel); + + /* + * Recurse to take appropriate action on each partition; + * either we find an existing constraint to reparent to ours, + * or we create a new one. + */ + for (int i = 0; i < pd->nparts; i++) + { + Oid partitionId = pd->oids[i]; + Relation partition = table_open(partitionId, lockmode); + List *partFKs; + AttrMap *attmap; + AttrNumber mapped_fkattnum[INDEX_MAX_KEYS]; + bool attached; + char *conname; + Oid constrOid; + ObjectAddress address, + referenced; + ListCell *cell; + + CheckTableNotInUse(partition, "ALTER TABLE"); + + attmap = build_attrmap_by_name(RelationGetDescr(partition), + RelationGetDescr(rel)); + for (int j = 0; j < numfks; j++) + mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; + + /* Check whether an existing constraint can be repurposed */ + partFKs = copyObject(RelationGetFKeyList(partition)); + attached = false; + foreach(cell, partFKs) + { + ForeignKeyCacheInfo *fk; + + fk = lfirst_node(ForeignKeyCacheInfo, cell); + if (tryAttachPartitionForeignKey(fk, + partitionId, + parentConstr, + numfks, + mapped_fkattnum, + pkattnum, + pfeqoperators)) + { + attached = true; + break; + } + } + if (attached) + { + table_close(partition, NoLock); + continue; + } + + /* + * No luck finding a good constraint to reuse; create our + * own. + */ + if (ConstraintNameIsUsed(CONSTRAINT_RELATION, + RelationGetRelid(partition), + fkconstraint->conname)) + conname = ChooseConstraintName(RelationGetRelationName(partition), + ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs), + "fkey", + RelationGetNamespace(partition), NIL); + else + conname = fkconstraint->conname; + constrOid = + CreateConstraintEntry(conname, + RelationGetNamespace(partition), + CONSTRAINT_FOREIGN, + fkconstraint->deferrable, + fkconstraint->initdeferred, + fkconstraint->initially_valid, + parentConstr, + partitionId, + mapped_fkattnum, + numfks, + numfks, + InvalidOid, + indexOid, + RelationGetRelid(pkrel), + pkattnum, + pfeqoperators, + ppeqoperators, + ffeqoperators, + numfks, + fkconstraint->fk_upd_action, + fkconstraint->fk_del_action, + fkconstraint->fk_matchtype, + NULL, + NULL, + NULL, + false, + 1, + false, + false); + + /* + * Give this constraint partition-type dependencies on the + * parent constraint as well as the table. + */ + ObjectAddressSet(address, ConstraintRelationId, constrOid); + ObjectAddressSet(referenced, ConstraintRelationId, parentConstr); + recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI); + ObjectAddressSet(referenced, RelationRelationId, partitionId); + recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC); + + /* Make all this visible before recursing */ + CommandCounterIncrement(); + + /* call ourselves to finalize the creation and we're done */ + addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel, + indexOid, + constrOid, + numfks, + pkattnum, + mapped_fkattnum, + pfeqoperators, + ppeqoperators, + ffeqoperators, + old_check_ok, + lockmode); + + table_close(partition, NoLock); + } + } + break; + case RELKIND_FOREIGN_TABLE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("foreign key constraints are not supported on foreign tables"))); + break; + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } } @@ -9229,10 +9701,25 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) if (clone == NIL) return; - if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("foreign key constraints are not supported on foreign tables"))); + Assert(RELKIND_IS_VALID((RelKind) partRel->rd_rel->relkind)); + switch ((RelKind) partRel->rd_rel->relkind) + { + case RELKIND_FOREIGN_TABLE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("foreign key constraints are not supported on foreign tables"))); + break; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } /* * The constraint key may differ, if the columns in the partition are @@ -9282,9 +9769,24 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * relation, that means to lock all partitions. */ pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock); - if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - (void) find_all_inheritors(RelationGetRelid(pkrel), - ShareRowExclusiveLock, NULL); + Assert(RELKIND_IS_VALID((RelKind) pkrel->rd_rel->relkind)); + switch ((RelKind) pkrel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + (void) find_all_inheritors(RelationGetRelid(pkrel), + ShareRowExclusiveLock, NULL); + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey, conpfeqop, conppeqop, conffeqop); @@ -10650,11 +11152,26 @@ ATExecDropConstraint(Relation rel, const char *constrName, * For partitioned tables, non-CHECK inherited constraints are dropped via * the dependency mechanism, so we're done here. */ - if (contype != CONSTRAINT_CHECK && - rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - table_close(conrel, RowExclusiveLock); - return; + case RELKIND_PARTITIONED_TABLE: + if (contype != CONSTRAINT_CHECK) + { + table_close(conrel, RowExclusiveLock); + return; + } + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } /* @@ -10672,12 +11189,27 @@ ATExecDropConstraint(Relation rel, const char *constrName, * recurse, it's a user error. It doesn't make sense to have a constraint * be defined only on the parent, especially if it's a partitioned table. */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && - children != NIL && !recurse) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("cannot remove constraint from only the partitioned table when partitions exist"), - errhint("Do not specify the ONLY keyword."))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + if (children != NIL && !recurse) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot remove constraint from only the partitioned table when partitions exist"), + errhint("Do not specify the ONLY keyword."))); + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } foreach(child, children) { @@ -10869,87 +11401,97 @@ ATPrepAlterColumnType(List **wqueue, list_make1_oid(rel->rd_rel->reltype), 0); - if (tab->relkind == RELKIND_RELATION || - tab->relkind == RELKIND_PARTITIONED_TABLE) + switch (tab->relkind) { - /* - * Set up an expression to transform the old data value to the new - * type. If a USING option was given, use the expression as - * transformed by transformAlterTableStmt, else just take the old - * value and try to coerce it. We do this first so that type - * incompatibility can be detected before we waste effort, and because - * we need the expression to be parsed against the original table row - * type. - */ - if (!transform) - { - transform = (Node *) makeVar(1, attnum, - attTup->atttypid, attTup->atttypmod, - attTup->attcollation, - 0); - } - - transform = coerce_to_target_type(pstate, - transform, exprType(transform), - targettype, targettypmod, - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); - if (transform == NULL) - { - /* error text depends on whether USING was specified or not */ - if (def->cooked_default != NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("result of USING clause for column \"%s\"" - " cannot be cast automatically to type %s", - colName, format_type_be(targettype)), - errhint("You might need to add an explicit cast."))); - else + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + /* + * Set up an expression to transform the old data value to the new + * type. If a USING option was given, use the expression as + * transformed by transformAlterTableStmt, else just take the old + * value and try to coerce it. We do this first so that type + * incompatibility can be detected before we waste effort, and because + * we need the expression to be parsed against the original table row + * type. + */ + if (!transform) + { + transform = (Node *) makeVar(1, attnum, + attTup->atttypid, attTup->atttypmod, + attTup->attcollation, + 0); + } + + transform = coerce_to_target_type(pstate, + transform, exprType(transform), + targettype, targettypmod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + if (transform == NULL) + { + /* error text depends on whether USING was specified or not */ + if (def->cooked_default != NULL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("result of USING clause for column \"%s\"" + " cannot be cast automatically to type %s", + colName, format_type_be(targettype)), + errhint("You might need to add an explicit cast."))); + else + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("column \"%s\" cannot be cast automatically to type %s", + colName, format_type_be(targettype)), + /* translator: USING is SQL, don't translate it */ + errhint("You might need to specify \"USING %s::%s\".", + quote_identifier(colName), + format_type_with_typemod(targettype, + targettypmod)))); + } + + /* Fix collations after all else */ + assign_expr_collations(pstate, transform); + + /* Plan the expr now so we can accurately assess the need to rewrite. */ + transform = (Node *) expression_planner((Expr *) transform); + + /* + * Add a work queue item to make ATRewriteTable update the column + * contents. + */ + newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue)); + newval->attnum = attnum; + newval->expr = (Expr *) transform; + newval->is_generated = false; + + tab->newvals = lappend(tab->newvals, newval); + if (ATColumnChangeRequiresRewrite(transform, attnum)) + tab->rewrite |= AT_REWRITE_COLUMN_REWRITE; + break; + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + if (!transform) + { + /* + * For composite types and foreign tables, do this check now. Regular + * tables will check it later when the table is being rewritten. + */ + find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL); + break; + } + /* fallthrough */ + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + if (transform) ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("column \"%s\" cannot be cast automatically to type %s", - colName, format_type_be(targettype)), - /* translator: USING is SQL, don't translate it */ - errhint("You might need to specify \"USING %s::%s\".", - quote_identifier(colName), - format_type_with_typemod(targettype, - targettypmod)))); - } - - /* Fix collations after all else */ - assign_expr_collations(pstate, transform); - - /* Plan the expr now so we can accurately assess the need to rewrite. */ - transform = (Node *) expression_planner((Expr *) transform); - - /* - * Add a work queue item to make ATRewriteTable update the column - * contents. - */ - newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue)); - newval->attnum = attnum; - newval->expr = (Expr *) transform; - newval->is_generated = false; - - tab->newvals = lappend(tab->newvals, newval); - if (ATColumnChangeRequiresRewrite(transform, attnum)) - tab->rewrite |= AT_REWRITE_COLUMN_REWRITE; - } - else if (transform) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table", - RelationGetRelationName(rel)))); - - if (tab->relkind == RELKIND_COMPOSITE_TYPE || - tab->relkind == RELKIND_FOREIGN_TABLE) - { - /* - * For composite types and foreign tables, do this check now. Regular - * tables will check it later when the table is being rewritten. - */ - find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL); + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", + RelationGetRelationName(rel)))); } ReleaseSysCache(tuple); @@ -11053,8 +11595,23 @@ ATPrepAlterColumnType(List **wqueue, errmsg("type of inherited column \"%s\" must be changed in child tables too", colName))); - if (tab->relkind == RELKIND_COMPOSITE_TYPE) - ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context); + Assert(RELKIND_IS_VALID((RelKind) tab->relkind)); + switch ((RelKind) tab->relkind) + { + case RELKIND_COMPOSITE_TYPE: + ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context); + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } } /* @@ -11271,44 +11828,51 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, { char relKind = get_rel_relkind(foundObject.objectId); - if (relKind == RELKIND_INDEX || - relKind == RELKIND_PARTITIONED_INDEX) + Assert(RELKIND_IS_VALID((RelKind) relKind)); + switch ((RelKind) relKind) { - Assert(foundObject.objectSubId == 0); - RememberIndexForRebuilding(foundObject.objectId, tab); - } - else if (relKind == RELKIND_SEQUENCE) - { - /* - * This must be a SERIAL column's sequence. We need - * not do anything to it. - */ - Assert(foundObject.objectSubId == 0); - } - else if (relKind == RELKIND_RELATION && - foundObject.objectSubId != 0 && - get_attgenerated(foundObject.objectId, foundObject.objectSubId)) - { - /* - * Changing the type of a column that is used by a - * generated column is not allowed by SQL standard. It - * might be doable with some thinking and effort. - */ - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("cannot alter type of a column used by a generated column"), - errdetail("Column \"%s\" is used by generated column \"%s\".", - colName, get_attname(foundObject.objectId, foundObject.objectSubId, false)))); - } - else - { - /* Not expecting any other direct dependencies... */ - elog(ERROR, "unexpected object depending on column: %s", - getObjectDescription(&foundObject)); + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + Assert(foundObject.objectSubId == 0); + RememberIndexForRebuilding(foundObject.objectId, tab); + break; + case RELKIND_SEQUENCE: + + /* + * This must be a SERIAL column's sequence. We + * need not do anything to it. + */ + Assert(foundObject.objectSubId == 0); + break; + case RELKIND_RELATION: + if (foundObject.objectSubId != 0 && + get_attgenerated(foundObject.objectId, foundObject.objectSubId)) + { + /* + * Changing the type of a column that is used + * by a generated column is not allowed by SQL + * standard. It might be doable with some + * thinking and effort. + */ + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("cannot alter type of a column used by a generated column"), + errdetail("Column \"%s\" is used by generated column \"%s\".", + colName, get_attname(foundObject.objectId, foundObject.objectSubId, false)))); + } + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_TOASTVALUE: + /* Not expecting any other direct dependencies... */ + elog(ERROR, "unexpected object depending on column: %s", + getObjectDescription(&foundObject)); } - break; } - + break; case OCLASS_CONSTRAINT: Assert(foundObject.objectSubId == 0); RememberConstraintForRebuilding(foundObject.objectId, tab); @@ -12141,13 +12705,26 @@ TryReuseIndex(Oid oldId, IndexStmt *stmt) { Relation irel = index_open(oldId, NoLock); - /* If it's a partitioned index, there is no storage to share. */ - if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX) + Assert(RELKIND_IS_VALID((RelKind) irel->rd_rel->relkind)); + switch ((RelKind) irel->rd_rel->relkind) { - stmt->oldNode = irel->rd_node.relNode; - stmt->oldCreateSubid = irel->rd_createSubid; - stmt->oldFirstRelfilenodeSubid = irel->rd_firstRelfilenodeSubid; + case RELKIND_PARTITIONED_INDEX: + /* If it's a partitioned index, there is no storage to share. */ + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + stmt->oldNode = irel->rd_node.relNode; + stmt->oldCreateSubid = irel->rd_createSubid; + stmt->oldFirstRelfilenodeSubid = irel->rd_firstRelfilenodeSubid; } + index_close(irel, NoLock); } } @@ -12342,7 +12919,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock tuple_class = (Form_pg_class) GETSTRUCT(tuple); /* Can we change the ownership of this tuple? */ - switch (tuple_class->relkind) + Assert(RELKIND_IS_VALID((RelKind) tuple_class->relkind)); + switch ((RelKind) tuple_class->relkind) { case RELKIND_RELATION: case RELKIND_VIEW: @@ -12411,8 +12989,6 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock case RELKIND_TOASTVALUE: if (recursing) break; - /* FALL THRU */ - default: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table, view, sequence, or foreign table", @@ -12517,22 +13093,33 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock * the ownership of any indexes and sequences that belong to the * relation, as well as its toast table (if it has one). */ - if (tuple_class->relkind == RELKIND_RELATION || - tuple_class->relkind == RELKIND_PARTITIONED_TABLE || - tuple_class->relkind == RELKIND_MATVIEW || - tuple_class->relkind == RELKIND_TOASTVALUE) + switch (tuple_class->relkind) { - List *index_oid_list; - ListCell *i; + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + { + List *index_oid_list; + ListCell *i; - /* Find all the indexes belonging to this relation */ - index_oid_list = RelationGetIndexList(target_rel); + /* Find all the indexes belonging to this relation */ + index_oid_list = RelationGetIndexList(target_rel); - /* For each index, recursively change its ownership */ - foreach(i, index_oid_list) - ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode); + /* For each index, recursively change its ownership */ + foreach(i, index_oid_list) + ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode); - list_free(index_oid_list); + list_free(index_oid_list); + } + break; + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_SEQUENCE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_INDEX: + break; } /* If it has a toast table, recurse to change its ownership */ @@ -12665,11 +13252,23 @@ change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lock seqRel = relation_open(depForm->objid, lockmode); /* skip non-sequence relations */ - if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE) + Assert(RELKIND_IS_VALID((RelKind) RelationGetForm(seqRel)->relkind)); + switch ((RelKind) RelationGetForm(seqRel)->relkind) { - /* No need to keep the lock */ - relation_close(seqRel, lockmode); - continue; + case RELKIND_SEQUENCE: + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_TOASTVALUE: + /* No need to keep the lock */ + relation_close(seqRel, lockmode); + continue; } /* We don't need to close the sequence while we alter it. */ @@ -12811,7 +13410,8 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, operation == AT_ResetRelOptions); /* Validate */ - switch (rel->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { case RELKIND_RELATION: case RELKIND_TOASTVALUE: @@ -12823,50 +13423,50 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, break; case RELKIND_VIEW: (void) view_reloptions(newOptions, true); + + /* Special-case validation of view options */ + { + Query *view_query = get_view_query(rel); + List *view_options = untransformRelOptions(newOptions); + ListCell *cell; + bool check_option = false; + + foreach(cell, view_options) + { + DefElem *defel = (DefElem *) lfirst(cell); + + if (strcmp(defel->defname, "check_option") == 0) + check_option = true; + } + + /* + * If the check option is specified, look to see if the view + * is actually auto-updatable or not. + */ + if (check_option) + { + const char *view_updatable_error = + view_query_is_auto_updatable(view_query, true); + + if (view_updatable_error) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WITH CHECK OPTION is supported only on automatically updatable views"), + errhint("%s", _(view_updatable_error)))); + } + } break; case RELKIND_INDEX: case RELKIND_PARTITIONED_INDEX: (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true); break; - default: + case RELKIND_FOREIGN_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_SEQUENCE: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table", RelationGetRelationName(rel)))); - break; - } - - /* Special-case validation of view options */ - if (rel->rd_rel->relkind == RELKIND_VIEW) - { - Query *view_query = get_view_query(rel); - List *view_options = untransformRelOptions(newOptions); - ListCell *cell; - bool check_option = false; - - foreach(cell, view_options) - { - DefElem *defel = (DefElem *) lfirst(cell); - - if (strcmp(defel->defname, "check_option") == 0) - check_option = true; - } - - /* - * If the check option is specified, look to see if the view is - * actually auto-updatable or not. - */ - if (check_option) - { - const char *view_updatable_error = - view_query_is_auto_updatable(view_query, true); - - if (view_updatable_error) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("WITH CHECK OPTION is supported only on automatically updatable views"), - errhint("%s", _(view_updatable_error)))); - } } /* @@ -13053,16 +13653,25 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) newrnode.spcNode = newTableSpace; /* hand off to AM to actually create the new filenode and copy the data */ - if (rel->rd_rel->relkind == RELKIND_INDEX) - { - index_copy_data(rel, newrnode); - } - else + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - Assert(rel->rd_rel->relkind == RELKIND_RELATION || - rel->rd_rel->relkind == RELKIND_MATVIEW || - rel->rd_rel->relkind == RELKIND_TOASTVALUE); - table_relation_copy_data(rel, &newrnode); + case RELKIND_INDEX: + index_copy_data(rel, newrnode); + break; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + table_relation_copy_data(rel, &newrnode); + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + Assert(false); + break; } /* @@ -13433,10 +14042,25 @@ ATPrepAddInherit(Relation child_rel) (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot change inheritance of a partition"))); - if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot change inheritance of partitioned table"))); + Assert(RELKIND_IS_VALID((RelKind) child_rel->rd_rel->relkind)); + switch ((RelKind) child_rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot change inheritance of partitioned table"))); + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } } /* @@ -13485,11 +14109,26 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode) errmsg("cannot inherit to temporary relation of another session"))); /* Prevent partitioned tables from becoming inheritance parents */ - if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot inherit from partitioned table \"%s\"", - parent->relname))); + Assert(RELKIND_IS_VALID((RelKind) parent_rel->rd_rel->relkind)); + switch ((RelKind) parent_rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot inherit from partitioned table \"%s\"", + parent->relname))); + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } /* Likewise for partitions */ if (parent_rel->rd_rel->relispartition) @@ -13693,8 +14332,23 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel) parent_natts = tupleDesc->natts; /* If parent_rel is a partitioned table, child_rel must be a partition */ - if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - child_is_partition = true; + Assert(RELKIND_IS_VALID((RelKind) parent_rel->rd_rel->relkind)); + switch ((RelKind) parent_rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + child_is_partition = true; + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } for (parent_attno = 1; parent_attno <= parent_natts; parent_attno++) { @@ -13802,8 +14456,23 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel) tuple_desc = RelationGetDescr(catalog_relation); /* If parent_rel is a partitioned table, child_rel must be a partition */ - if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - child_is_partition = true; + Assert(RELKIND_IS_VALID((RelKind) parent_rel->rd_rel->relkind)); + switch ((RelKind) parent_rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + child_is_partition = true; + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } /* Outer loop scans through the parent's constraint definitions */ ScanKeyInit(&parent_key, @@ -13984,8 +14653,23 @@ RemoveInheritance(Relation child_rel, Relation parent_rel) bool child_is_partition = false; /* If parent_rel is a partitioned table, child_rel must be a partition */ - if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - child_is_partition = true; + Assert(RELKIND_IS_VALID((RelKind) parent_rel->rd_rel->relkind)); + switch ((RelKind) parent_rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + child_is_partition = true; + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } found = DeleteInheritsTuple(RelationGetRelid(child_rel), RelationGetRelid(parent_rel)); @@ -14896,20 +15580,35 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema) oldNspOid = RelationGetNamespace(rel); - /* If it's an owned sequence, disallow moving it by itself. */ - if (rel->rd_rel->relkind == RELKIND_SEQUENCE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - Oid tableId; - int32 colId; + /* If it's an owned sequence, disallow moving it by itself. */ + case RELKIND_SEQUENCE: + { + Oid tableId; + int32 colId; - if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) || - sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot move an owned sequence into another schema"), - errdetail("Sequence \"%s\" is linked to table \"%s\".", - RelationGetRelationName(rel), - get_rel_name(tableId)))); + if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) || + sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot move an owned sequence into another schema"), + errdetail("Sequence \"%s\" is linked to table \"%s\".", + RelationGetRelationName(rel), + get_rel_name(tableId)))); + } + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_TOASTVALUE: + break; } /* Get and lock schema OID and check its permissions. */ @@ -14959,15 +15658,26 @@ AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, nspOid, false, false, objsMoved); /* Fix other dependent stuff */ - if (rel->rd_rel->relkind == RELKIND_RELATION || - rel->rd_rel->relkind == RELKIND_MATVIEW || - rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved); - AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid, - objsMoved, AccessExclusiveLock); - AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid, - false, objsMoved); + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved); + AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid, + objsMoved, AccessExclusiveLock); + AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid, + false, objsMoved); + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } table_close(classRel, RowExclusiveLock); @@ -15138,11 +15848,23 @@ AlterSeqNamespaces(Relation classRel, Relation rel, seqRel = relation_open(depForm->objid, lockmode); /* skip non-sequence relations */ - if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE) + Assert(RELKIND_IS_VALID((RelKind) RelationGetForm(seqRel)->relkind)); + switch ((RelKind) RelationGetForm(seqRel)->relkind) { - /* No need to keep the lock */ - relation_close(seqRel, lockmode); - continue; + case RELKIND_SEQUENCE: + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_TOASTVALUE: + /* No need to keep the lock */ + relation_close(seqRel, lockmode); + continue; } /* Fix the pg_class and pg_depend entries */ @@ -15422,11 +16144,24 @@ RangeVarCallbackOwnsTable(const RangeVar *relation, relkind = get_rel_relkind(relId); if (!relkind) return; - if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE && - relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or materialized view", relation->relname))); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table or materialized view", relation->relname))); + } /* Check permissions */ if (!pg_class_ownercheck(relId, GetUserId())) @@ -15585,31 +16320,40 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not an index", rv->relname))); - /* - * Don't allow ALTER TABLE on composite types. We want people to use ALTER - * TYPE for that. - */ - if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a composite type", rv->relname), - errhint("Use ALTER TYPE instead."))); - /* * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved * to a different schema, such as indexes and TOAST tables. */ - if (IsA(stmt, AlterObjectSchemaStmt) && - relkind != RELKIND_RELATION && - relkind != RELKIND_VIEW && - relkind != RELKIND_MATVIEW && - relkind != RELKIND_SEQUENCE && - relkind != RELKIND_FOREIGN_TABLE && - relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, materialized view, sequence, or foreign table", - rv->relname))); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_SEQUENCE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_COMPOSITE_TYPE: + /* + * Don't allow ALTER TABLE on composite types. We want people to use ALTER + * TYPE for that. + */ + if (reltype != OBJECT_TYPE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a composite type", rv->relname), + errhint("Use ALTER TYPE instead."))); + /* fallthrough */ + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_TOASTVALUE: + if (IsA(stmt, AlterObjectSchemaStmt)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, view, materialized view, sequence, or foreign table", + rv->relname))); + } ReleaseSysCache(tuple); } @@ -16084,44 +16828,59 @@ QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, * validation item now; for partitioned tables, recurse to process each * partition. */ - if (scanrel->rd_rel->relkind == RELKIND_RELATION) + Assert(RELKIND_IS_VALID((RelKind) scanrel->rd_rel->relkind)); + switch ((RelKind) scanrel->rd_rel->relkind) { - AlteredTableInfo *tab; + case RELKIND_RELATION: + { + AlteredTableInfo *tab; - /* Grab a work queue entry. */ - tab = ATGetQueueEntry(wqueue, scanrel); - Assert(tab->partition_constraint == NULL); - tab->partition_constraint = (Expr *) linitial(partConstraint); - tab->validate_default = validate_default; - } - else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - { - PartitionDesc partdesc = RelationGetPartitionDesc(scanrel); - int i; + /* Grab a work queue entry. */ + tab = ATGetQueueEntry(wqueue, scanrel); + Assert(tab->partition_constraint == NULL); + tab->partition_constraint = (Expr *) linitial(partConstraint); + tab->validate_default = validate_default; + } + break; + case RELKIND_PARTITIONED_TABLE: + { + PartitionDesc partdesc = RelationGetPartitionDesc(scanrel); + int i; - for (i = 0; i < partdesc->nparts; i++) - { - Relation part_rel; - List *thisPartConstraint; + for (i = 0; i < partdesc->nparts; i++) + { + Relation part_rel; + List *thisPartConstraint; - /* - * This is the minimum lock we need to prevent deadlocks. - */ - part_rel = table_open(partdesc->oids[i], AccessExclusiveLock); + /* + * This is the minimum lock we need to prevent deadlocks. + */ + part_rel = table_open(partdesc->oids[i], AccessExclusiveLock); - /* - * Adjust the constraint for scanrel so that it matches this - * partition's attribute numbers. - */ - thisPartConstraint = - map_partition_varattnos(partConstraint, 1, - part_rel, scanrel); - - QueuePartitionConstraintValidation(wqueue, part_rel, - thisPartConstraint, - validate_default); - table_close(part_rel, NoLock); /* keep lock till commit */ - } + /* + * Adjust the constraint for scanrel so that it matches + * this partition's attribute numbers. + */ + thisPartConstraint = + map_partition_varattnos(partConstraint, 1, + part_rel, scanrel); + + QueuePartitionConstraintValidation(wqueue, part_rel, + thisPartConstraint, + validate_default); + table_close(part_rel, NoLock); /* keep lock till commit */ + } + } + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } } @@ -16205,11 +16964,28 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) ObjectIdGetDatum(RelationGetRelid(attachrel))); scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL, 1, &skey); - if (HeapTupleIsValid(systable_getnext(scan)) && - attachrel->rd_rel->relkind == RELKIND_RELATION) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot attach inheritance parent as partition"))); + + Assert(RELKIND_IS_VALID((RelKind) attachrel->rd_rel->relkind)); + switch ((RelKind) attachrel->rd_rel->relkind) + { + case RELKIND_RELATION: + if (HeapTupleIsValid(systable_getnext(scan))) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot attach inheritance parent as partition"))); + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } + systable_endscan(scan); table_close(catalog, AccessShareLock); @@ -16455,26 +17231,37 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) * before starting work, to avoid wasting the effort of building a few * non-unique indexes before coming across a unique one. */ - if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + Assert(RELKIND_IS_VALID((RelKind) attachrel->rd_rel->relkind)); + switch ((RelKind) attachrel->rd_rel->relkind) { - foreach(cell, idxes) - { - Oid idx = lfirst_oid(cell); - Relation idxRel = index_open(idx, AccessShareLock); - - if (idxRel->rd_index->indisunique || - idxRel->rd_index->indisprimary) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"", - RelationGetRelationName(attachrel), - RelationGetRelationName(rel)), - errdetail("Table \"%s\" contains unique indexes.", - RelationGetRelationName(rel)))); - index_close(idxRel, AccessShareLock); - } + case RELKIND_FOREIGN_TABLE: + foreach(cell, idxes) + { + Oid idx = lfirst_oid(cell); + Relation idxRel = index_open(idx, AccessShareLock); - goto out; + if (idxRel->rd_index->indisunique || + idxRel->rd_index->indisprimary) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"", + RelationGetRelationName(attachrel), + RelationGetRelationName(rel)), + errdetail("Table \"%s\" contains unique indexes.", + RelationGetRelationName(rel)))); + index_close(idxRel, AccessShareLock); + } + goto out; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } /* @@ -16494,10 +17281,22 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) * Ignore indexes in the partitioned table other than partitioned * indexes. */ - if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX) + Assert(RELKIND_IS_VALID((RelKind) idxRel->rd_rel->relkind)); + switch ((RelKind) idxRel->rd_rel->relkind) { - index_close(idxRel, AccessShareLock); - continue; + case RELKIND_PARTITIONED_INDEX: + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + index_close(idxRel, AccessShareLock); + continue; } /* construct an indexinfo to compare existing indexes against */ @@ -17034,11 +17833,25 @@ RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, if (!HeapTupleIsValid(tuple)) return; /* concurrently dropped, so nothing to do */ classform = (Form_pg_class) GETSTRUCT(tuple); - if (classform->relkind != RELKIND_PARTITIONED_INDEX && - classform->relkind != RELKIND_INDEX) - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("\"%s\" is not an index", rv->relname))); + + Assert(RELKIND_IS_VALID((RelKind) classform->relkind)); + switch ((RelKind) classform->relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("\"%s\" is not an index", rv->relname))); + } ReleaseSysCache(tuple); /* diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 672fccff5b..b59c636fda 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -194,106 +194,120 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, * Triggers must be on tables or views, and there are additional * relation-type-specific restrictions. */ - if (rel->rd_rel->relkind == RELKIND_RELATION) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - /* Tables can't have INSTEAD OF triggers */ - if (stmt->timing != TRIGGER_TYPE_BEFORE && - stmt->timing != TRIGGER_TYPE_AFTER) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a table", - RelationGetRelationName(rel)), - errdetail("Tables cannot have INSTEAD OF triggers."))); - } - else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - { - /* Partitioned tables can't have INSTEAD OF triggers */ - if (stmt->timing != TRIGGER_TYPE_BEFORE && - stmt->timing != TRIGGER_TYPE_AFTER) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a table", - RelationGetRelationName(rel)), - errdetail("Tables cannot have INSTEAD OF triggers."))); + case RELKIND_RELATION: + { + /* Tables can't have INSTEAD OF triggers */ + if (stmt->timing != TRIGGER_TYPE_BEFORE && + stmt->timing != TRIGGER_TYPE_AFTER) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a table", + RelationGetRelationName(rel)), + errdetail("Tables cannot have INSTEAD OF triggers."))); + } + break; + case RELKIND_PARTITIONED_TABLE: + { + /* Partitioned tables can't have INSTEAD OF triggers */ + if (stmt->timing != TRIGGER_TYPE_BEFORE && + stmt->timing != TRIGGER_TYPE_AFTER) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a table", + RelationGetRelationName(rel)), + errdetail("Tables cannot have INSTEAD OF triggers."))); - /* - * FOR EACH ROW triggers have further restrictions - */ - if (stmt->row) - { - /* - * Disallow use of transition tables. - * - * Note that we have another restriction about transition tables - * in partitions; search for 'has_superclass' below for an - * explanation. The check here is just to protect from the fact - * that if we allowed it here, the creation would succeed for a - * partitioned table with no partitions, but would be blocked by - * the other restriction when the first partition was created, - * which is very unfriendly behavior. - */ - if (stmt->transitionRels != NIL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("\"%s\" is a partitioned table", - RelationGetRelationName(rel)), - errdetail("Triggers on partitioned tables cannot have transition tables."))); - } - } - else if (rel->rd_rel->relkind == RELKIND_VIEW) - { - /* - * Views can have INSTEAD OF triggers (which we check below are - * row-level), or statement-level BEFORE/AFTER triggers. - */ - if (stmt->timing != TRIGGER_TYPE_INSTEAD && stmt->row) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a view", - RelationGetRelationName(rel)), - errdetail("Views cannot have row-level BEFORE or AFTER triggers."))); - /* Disallow TRUNCATE triggers on VIEWs */ - if (TRIGGER_FOR_TRUNCATE(stmt->events)) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a view", - RelationGetRelationName(rel)), - errdetail("Views cannot have TRUNCATE triggers."))); - } - else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - { - if (stmt->timing != TRIGGER_TYPE_BEFORE && - stmt->timing != TRIGGER_TYPE_AFTER) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a foreign table", - RelationGetRelationName(rel)), - errdetail("Foreign tables cannot have INSTEAD OF triggers."))); + /* + * FOR EACH ROW triggers have further restrictions + */ + if (stmt->row) + { + /* + * Disallow use of transition tables. + * + * Note that we have another restriction about transition + * tables in partitions; search for 'has_superclass' below + * for an explanation. The check here is just to protect + * from the fact that if we allowed it here, the creation + * would succeed for a partitioned table with no + * partitions, but would be blocked by the other + * restriction when the first partition was created, which + * is very unfriendly behavior. + */ + if (stmt->transitionRels != NIL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("\"%s\" is a partitioned table", + RelationGetRelationName(rel)), + errdetail("Triggers on partitioned tables cannot have transition tables."))); + } + } + break; + case RELKIND_VIEW: + { + /* + * Views can have INSTEAD OF triggers (which we check below + * are row-level), or statement-level BEFORE/AFTER triggers. + */ + if (stmt->timing != TRIGGER_TYPE_INSTEAD && stmt->row) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a view", + RelationGetRelationName(rel)), + errdetail("Views cannot have row-level BEFORE or AFTER triggers."))); + /* Disallow TRUNCATE triggers on VIEWs */ + if (TRIGGER_FOR_TRUNCATE(stmt->events)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a view", + RelationGetRelationName(rel)), + errdetail("Views cannot have TRUNCATE triggers."))); + } + break; + case RELKIND_FOREIGN_TABLE: + { + if (stmt->timing != TRIGGER_TYPE_BEFORE && + stmt->timing != TRIGGER_TYPE_AFTER) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a foreign table", + RelationGetRelationName(rel)), + errdetail("Foreign tables cannot have INSTEAD OF triggers."))); - if (TRIGGER_FOR_TRUNCATE(stmt->events)) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a foreign table", - RelationGetRelationName(rel)), - errdetail("Foreign tables cannot have TRUNCATE triggers."))); + if (TRIGGER_FOR_TRUNCATE(stmt->events)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a foreign table", + RelationGetRelationName(rel)), + errdetail("Foreign tables cannot have TRUNCATE triggers."))); - /* - * We disallow constraint triggers to protect the assumption that - * triggers on FKs can't be deferred. See notes with AfterTriggers - * data structures, below. - */ - if (stmt->isconstraint) + /* + * We disallow constraint triggers to protect the assumption + * that triggers on FKs can't be deferred. See notes with + * AfterTriggers data structures, below. + */ + if (stmt->isconstraint) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a foreign table", + RelationGetRelationName(rel)), + errdetail("Foreign tables cannot have constraint triggers."))); + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a foreign table", - RelationGetRelationName(rel)), - errdetail("Foreign tables cannot have constraint triggers."))); + errmsg("\"%s\" is not a table or view", + RelationGetRelationName(rel)))); } - else - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or view", - RelationGetRelationName(rel)))); if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, @@ -345,8 +359,24 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, * * For that, we'd better hold lock on all of them ahead of time. */ - partition_recurse = !isInternal && stmt->row && - rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE; + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + partition_recurse = !isInternal && stmt->row; + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + partition_recurse = false; + } + if (partition_recurse) list_free(find_all_inheritors(RelationGetRelid(rel), ShareRowExclusiveLock, NULL)); @@ -416,19 +446,33 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, * adjustments will be needed below. */ - if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a foreign table", - RelationGetRelationName(rel)), - errdetail("Triggers on foreign tables cannot have transition tables."))); - - if (rel->rd_rel->relkind == RELKIND_VIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a view", - RelationGetRelationName(rel)), - errdetail("Triggers on views cannot have transition tables."))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_FOREIGN_TABLE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a foreign table", + RelationGetRelationName(rel)), + errdetail("Triggers on foreign tables cannot have transition tables."))); + break; + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a view", + RelationGetRelationName(rel)), + errdetail("Triggers on views cannot have transition tables."))); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; + } /* * We currently don't allow row-level triggers with transition @@ -1191,14 +1235,25 @@ RemoveTriggerById(Oid trigOid) rel = table_open(relid, AccessExclusiveLock); - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_VIEW && - rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && - rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, or foreign table", - RelationGetRelationName(rel)))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, view, or foreign table", + RelationGetRelationName(rel)))); + } if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, @@ -1298,13 +1353,25 @@ RangeVarCallbackForRenameTrigger(const RangeVar *rv, Oid relid, Oid oldrelid, form = (Form_pg_class) GETSTRUCT(tuple); /* only tables and views can have triggers */ - if (form->relkind != RELKIND_RELATION && form->relkind != RELKIND_VIEW && - form->relkind != RELKIND_FOREIGN_TABLE && - form->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, or foreign table", - rv->relname))); + Assert(RELKIND_IS_VALID((RelKind) form->relkind)); + switch ((RelKind) form->relkind) + { + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, view, or foreign table", + rv->relname))); + } /* you must own the table to rename one of its triggers */ if (!pg_class_ownercheck(relid, GetUserId())) @@ -1534,21 +1601,36 @@ EnableDisableTrigger(Relation rel, const char *tgname, * When altering FOR EACH ROW triggers on a partitioned table, do * the same on the partitions as well. */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && - (TRIGGER_FOR_ROW(oldtrig->tgtype))) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - PartitionDesc partdesc = RelationGetPartitionDesc(rel); - int i; - - for (i = 0; i < partdesc->nparts; i++) - { - Relation part; - - part = relation_open(partdesc->oids[i], lockmode); - EnableDisableTrigger(part, NameStr(oldtrig->tgname), - fires_when, skip_system, lockmode); - table_close(part, NoLock); /* keep lock till commit */ - } + case RELKIND_PARTITIONED_TABLE: + if (TRIGGER_FOR_ROW(oldtrig->tgtype)) + { + PartitionDesc partdesc = RelationGetPartitionDesc(rel); + int i; + + for (i = 0; i < partdesc->nparts; i++) + { + Relation part; + + part = relation_open(partdesc->oids[i], lockmode); + EnableDisableTrigger(part, NameStr(oldtrig->tgname), + fires_when, skip_system, lockmode); + table_close(part, NoLock); /* keep lock till commit */ + } + } + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } changed = true; @@ -4167,12 +4249,25 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events, ExecDropSingleTupleTableSlot(slot2); slot1 = slot2 = NULL; } - if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - slot1 = MakeSingleTupleTableSlot(rel->rd_att, - &TTSOpsMinimalTuple); - slot2 = MakeSingleTupleTableSlot(rel->rd_att, - &TTSOpsMinimalTuple); + case RELKIND_FOREIGN_TABLE: + slot1 = MakeSingleTupleTableSlot(rel->rd_att, + &TTSOpsMinimalTuple); + slot2 = MakeSingleTupleTableSlot(rel->rd_att, + &TTSOpsMinimalTuple); + break; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } if (trigdesc == NULL) /* should not happen */ elog(ERROR, "relation %u has no triggers", @@ -5566,9 +5661,25 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, break; } - if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger)) - new_event.ate_flags = (row_trigger && event == TRIGGER_EVENT_UPDATE) ? - AFTER_TRIGGER_2CTID : AFTER_TRIGGER_1CTID; + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_FOREIGN_TABLE: + if (row_trigger) + break; + /* fallthrough */ + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + new_event.ate_flags = (row_trigger && event == TRIGGER_EVENT_UPDATE) ? + AFTER_TRIGGER_2CTID : AFTER_TRIGGER_1CTID; + } /* else, we'll initialize ate_flags for each trigger */ tgtype_level = (row_trigger ? TRIGGER_TYPE_ROW : TRIGGER_TYPE_STATEMENT); @@ -5586,16 +5697,32 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, modifiedCols, oldslot, newslot)) continue; - if (relkind == RELKIND_FOREIGN_TABLE && row_trigger) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - if (fdw_tuplestore == NULL) - { - fdw_tuplestore = GetCurrentFDWTuplestore(); - new_event.ate_flags = AFTER_TRIGGER_FDW_FETCH; - } - else - /* subsequent event for the same tuple */ - new_event.ate_flags = AFTER_TRIGGER_FDW_REUSE; + case RELKIND_FOREIGN_TABLE: + if (row_trigger) + { + if (fdw_tuplestore == NULL) + { + fdw_tuplestore = GetCurrentFDWTuplestore(); + new_event.ate_flags = AFTER_TRIGGER_FDW_FETCH; + } + else + /* subsequent event for the same tuple */ + new_event.ate_flags = AFTER_TRIGGER_FDW_REUSE; + } + break; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } /* diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 9e5938b10e..7b2f05acf4 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -2918,11 +2918,22 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode) * a suitable expression index, this should also check expression * index columns. */ - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_MATVIEW) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - relation_close(rel, lockmode); - continue; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + relation_close(rel, lockmode); + continue; } /* Build the RelToCheck entry with enough space for all atts */ diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 576c7e63e9..5bcf3fe03f 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -881,10 +881,22 @@ get_all_vacuum_rels(int options) * to be performed, caller will decide whether to process or ignore * them. */ - if (classForm->relkind != RELKIND_RELATION && - classForm->relkind != RELKIND_MATVIEW && - classForm->relkind != RELKIND_PARTITIONED_TABLE) - continue; + Assert(RELKIND_IS_VALID((RelKind) classForm->relkind)); + switch ((RelKind) classForm->relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + continue; + } /* * Build VacuumRelation(s) specifying the table OIDs to be processed. @@ -1383,13 +1395,23 @@ vac_update_datfrozenxid(void) * Only consider relations able to hold unfrozen XIDs (anything else * should have InvalidTransactionId in relfrozenxid anyway). */ - if (classForm->relkind != RELKIND_RELATION && - classForm->relkind != RELKIND_MATVIEW && - classForm->relkind != RELKIND_TOASTVALUE) + Assert(RELKIND_IS_VALID((RelKind) classForm->relkind)); + switch ((RelKind) classForm->relkind) { - Assert(!TransactionIdIsValid(classForm->relfrozenxid)); - Assert(!MultiXactIdIsValid(classForm->relminmxid)); - continue; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: + Assert(!TransactionIdIsValid(classForm->relfrozenxid)); + Assert(!MultiXactIdIsValid(classForm->relminmxid)); + continue; } /* @@ -1762,18 +1784,27 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params) /* * Check that it's of a vacuumable relkind. */ - if (onerel->rd_rel->relkind != RELKIND_RELATION && - onerel->rd_rel->relkind != RELKIND_MATVIEW && - onerel->rd_rel->relkind != RELKIND_TOASTVALUE && - onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) onerel->rd_rel->relkind)); + switch ((RelKind) onerel->rd_rel->relkind) { - ereport(WARNING, - (errmsg("skipping \"%s\" --- cannot vacuum non-tables or special system tables", - RelationGetRelationName(onerel)))); - relation_close(onerel, lmode); - PopActiveSnapshot(); - CommitTransactionCommand(); - return false; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_VIEW: + ereport(WARNING, + (errmsg("skipping \"%s\" --- cannot vacuum non-tables or special system tables", + RelationGetRelationName(onerel)))); + relation_close(onerel, lmode); + PopActiveSnapshot(); + CommitTransactionCommand(); + return false; } /* @@ -1796,13 +1827,25 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params) * useful work is on their child partitions, which have been queued up for * us separately. */ - if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) onerel->rd_rel->relkind)); + switch ((RelKind) onerel->rd_rel->relkind) { - relation_close(onerel, lmode); - PopActiveSnapshot(); - CommitTransactionCommand(); - /* It's OK to proceed with ANALYZE on this table */ - return true; + case RELKIND_PARTITIONED_TABLE: + relation_close(onerel, lmode); + PopActiveSnapshot(); + CommitTransactionCommand(); + /* It's OK to proceed with ANALYZE on this table */ + return true; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } /* diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 4fdffad6f3..f170d8f570 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1079,7 +1079,8 @@ CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation) TriggerDesc *trigDesc = resultRel->trigdesc; FdwRoutine *fdwroutine; - switch (resultRel->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) resultRel->rd_rel->relkind)); + switch ((RelKind) resultRel->rd_rel->relkind) { case RELKIND_RELATION: case RELKIND_PARTITIONED_TABLE: @@ -1193,12 +1194,13 @@ CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation) break; } break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot change relation \"%s\"", RelationGetRelationName(resultRel)))); - break; } } @@ -1213,7 +1215,8 @@ CheckValidRowMarkRel(Relation rel, RowMarkType markType) { FdwRoutine *fdwroutine; - switch (rel->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { case RELKIND_RELATION: case RELKIND_PARTITIONED_TABLE: @@ -1257,12 +1260,13 @@ CheckValidRowMarkRel(Relation rel, RowMarkType markType) errmsg("cannot lock rows in foreign table \"%s\"", RelationGetRelationName(rel)))); break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot lock rows in relation \"%s\"", RelationGetRelationName(rel)))); - break; } } @@ -1308,10 +1312,24 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigWhenExprs = NULL; resultRelInfo->ri_TrigInstrument = NULL; } - if (resultRelationDesc->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - resultRelInfo->ri_FdwRoutine = GetFdwRoutineForRelation(resultRelationDesc, true); - else - resultRelInfo->ri_FdwRoutine = NULL; + + Assert(RELKIND_IS_VALID((RelKind) resultRelationDesc->rd_rel->relkind)); + switch ((RelKind) resultRelationDesc->rd_rel->relkind) + { + case RELKIND_FOREIGN_TABLE: + resultRelInfo->ri_FdwRoutine = GetFdwRoutineForRelation(resultRelationDesc, true); + break; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + resultRelInfo->ri_FdwRoutine = NULL; + } /* The following fields are set later if needed */ resultRelInfo->ri_FdwState = NULL; @@ -2614,42 +2632,57 @@ EvalPlanQualFetchRowMark(EPQState *epqstate, Index rti, TupleTableSlot *slot) return false; /* fetch requests on foreign tables must be passed to their FDW */ - if (erm->relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + Assert(RELKIND_IS_VALID((RelKind) erm->relation->rd_rel->relkind)); + switch ((RelKind) erm->relation->rd_rel->relkind) { - FdwRoutine *fdwroutine; - bool updated = false; + case RELKIND_FOREIGN_TABLE: + { + FdwRoutine *fdwroutine; + bool updated = false; - fdwroutine = GetFdwRoutineForRelation(erm->relation, false); - /* this should have been checked already, but let's be safe */ - if (fdwroutine->RefetchForeignRow == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot lock rows in foreign table \"%s\"", - RelationGetRelationName(erm->relation)))); + fdwroutine = GetFdwRoutineForRelation(erm->relation, false); - fdwroutine->RefetchForeignRow(epqstate->recheckestate, - erm, - datum, - slot, - &updated); - if (TupIsNull(slot)) - elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck"); + /* + * this should have been checked already, but let's be + * safe + */ + if (fdwroutine->RefetchForeignRow == NULL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot lock rows in foreign table \"%s\"", + RelationGetRelationName(erm->relation)))); - /* - * Ideally we'd insist on updated == false here, but that assumes - * that FDWs can track that exactly, which they might not be able - * to. So just ignore the flag. - */ - return true; - } - else - { - /* ordinary table, fetch the tuple */ - if (!table_tuple_fetch_row_version(erm->relation, - (ItemPointer) DatumGetPointer(datum), - SnapshotAny, slot)) - elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck"); - return true; + fdwroutine->RefetchForeignRow(epqstate->recheckestate, + erm, + datum, + slot, + &updated); + if (TupIsNull(slot)) + elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck"); + + /* + * Ideally we'd insist on updated == false here, but that + * assumes that FDWs can track that exactly, which they + * might not be able to. So just ignore the flag. + */ + return true; + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + /* ordinary table, fetch the tuple */ + if (!table_tuple_fetch_row_version(erm->relation, + (ItemPointer) DatumGetPointer(datum), + SnapshotAny, slot)) + elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck"); + return true; } } else diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index 8f474faed0..391ccf7564 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -607,19 +607,32 @@ CheckSubscriptionRelkind(char relkind, const char *nspname, /* * Give a more specific error for foreign tables. */ - if (relkind == RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot use relation \"%s.%s\" as logical replication target", - nspname, relname), - errdetail("\"%s.%s\" is a foreign table.", - nspname, relname))); - - if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot use relation \"%s.%s\" as logical replication target", - nspname, relname), - errdetail("\"%s.%s\" is not a table.", - nspname, relname))); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_FOREIGN_TABLE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot use relation \"%s.%s\" as logical replication target", + nspname, relname), + errdetail("\"%s.%s\" is a foreign table.", + nspname, relname))); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot use relation \"%s.%s\" as logical replication target", + nspname, relname), + errdetail("\"%s.%s\" is not a table.", + nspname, relname))); + } } diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 20a4c474cc..275fc8fa8b 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -2177,56 +2177,73 @@ ExecModifyTable(PlanState *pstate) bool isNull; relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind; - if (relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - datum = ExecGetJunkAttribute(slot, - junkfilter->jf_junkAttNo, - &isNull); - /* shouldn't ever get a null result... */ - if (isNull) - elog(ERROR, "ctid is NULL"); - - tupleid = (ItemPointer) DatumGetPointer(datum); - tuple_ctid = *tupleid; /* be sure we don't free ctid!! */ - tupleid = &tuple_ctid; - } + case RELKIND_RELATION: + case RELKIND_MATVIEW: + datum = ExecGetJunkAttribute(slot, + junkfilter->jf_junkAttNo, + &isNull); + /* shouldn't ever get a null result... */ + if (isNull) + elog(ERROR, "ctid is NULL"); + + tupleid = (ItemPointer) DatumGetPointer(datum); + tuple_ctid = *tupleid; /* be sure we don't free + * ctid!! */ + tupleid = &tuple_ctid; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + /* + * Use the wholerow attribute, when available, to + * reconstruct the old relation tuple. + * + * Foreign table updates have a wholerow attribute + * when the relation has a row-level trigger. Note + * that the wholerow attribute does not carry system + * columns. Foreign table triggers miss seeing those, + * except that we know enough here to set t_tableOid. + * Quite separately from this, the FDW may fetch its + * own junk attrs to identify the row. + * + * Other relevant relkinds, currently limited to + * views, always have a wholerow attribute. + */ + if (AttributeNumberIsValid(junkfilter->jf_junkAttNo)) + { + datum = ExecGetJunkAttribute(slot, + junkfilter->jf_junkAttNo, + &isNull); + /* shouldn't ever get a null result... */ + if (isNull) + elog(ERROR, "wholerow is NULL"); + + oldtupdata.t_data = DatumGetHeapTupleHeader(datum); + oldtupdata.t_len = + HeapTupleHeaderGetDatumLength(oldtupdata.t_data); + ItemPointerSetInvalid(&(oldtupdata.t_self)); - /* - * Use the wholerow attribute, when available, to reconstruct - * the old relation tuple. - * - * Foreign table updates have a wholerow attribute when the - * relation has a row-level trigger. Note that the wholerow - * attribute does not carry system columns. Foreign table - * triggers miss seeing those, except that we know enough here - * to set t_tableOid. Quite separately from this, the FDW may - * fetch its own junk attrs to identify the row. - * - * Other relevant relkinds, currently limited to views, always - * have a wholerow attribute. - */ - else if (AttributeNumberIsValid(junkfilter->jf_junkAttNo)) - { - datum = ExecGetJunkAttribute(slot, - junkfilter->jf_junkAttNo, - &isNull); - /* shouldn't ever get a null result... */ - if (isNull) - elog(ERROR, "wholerow is NULL"); - - oldtupdata.t_data = DatumGetHeapTupleHeader(datum); - oldtupdata.t_len = - HeapTupleHeaderGetDatumLength(oldtupdata.t_data); - ItemPointerSetInvalid(&(oldtupdata.t_self)); - /* Historically, view triggers see invalid t_tableOid. */ - oldtupdata.t_tableOid = - (relkind == RELKIND_VIEW) ? InvalidOid : - RelationGetRelid(resultRelInfo->ri_RelationDesc); - - oldtuple = &oldtupdata; + /* + * Historically, view triggers see invalid + * t_tableOid. + */ + oldtupdata.t_tableOid = + (relkind == RELKIND_VIEW) ? InvalidOid : + RelationGetRelid(resultRelInfo->ri_RelationDesc); + + oldtuple = &oldtupdata; + } + else + Assert(relkind == RELKIND_FOREIGN_TABLE); } - else - Assert(relkind == RELKIND_FOREIGN_TABLE); } /* @@ -2697,27 +2714,32 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) char relkind; relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind; - if (relkind == RELKIND_RELATION || - relkind == RELKIND_MATVIEW || - relkind == RELKIND_PARTITIONED_TABLE) - { - j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid"); - if (!AttributeNumberIsValid(j->jf_junkAttNo)) - elog(ERROR, "could not find junk ctid column"); - } - else if (relkind == RELKIND_FOREIGN_TABLE) - { - /* - * When there is a row-level trigger, there should be - * a wholerow attribute. - */ - j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow"); - } - else + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow"); - if (!AttributeNumberIsValid(j->jf_junkAttNo)) - elog(ERROR, "could not find junk wholerow column"); + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid"); + if (!AttributeNumberIsValid(j->jf_junkAttNo)) + elog(ERROR, "could not find junk ctid column"); + break; + case RELKIND_FOREIGN_TABLE: + /* + * When there is a row-level trigger, there + * should be a wholerow attribute. + */ + j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow"); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow"); + if (!AttributeNumberIsValid(j->jf_junkAttNo)) + elog(ERROR, "could not find junk wholerow column"); } } diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 6da0dcd61c..c328bdbc10 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -387,29 +387,40 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel, switch (rel->rtekind) { case RTE_RELATION: - if (rte->relkind == RELKIND_FOREIGN_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rte->relkind)); + switch ((RelKind) rte->relkind) { - /* Foreign table */ - set_foreign_size(root, rel, rte); - } - else if (rte->relkind == RELKIND_PARTITIONED_TABLE) - { - /* - * We could get here if asked to scan a partitioned table - * with ONLY. In that case we shouldn't scan any of the - * partitions, so mark it as a dummy rel. - */ - set_dummy_rel_pathlist(rel); - } - else if (rte->tablesample != NULL) - { - /* Sampled relation */ - set_tablesample_rel_size(root, rel, rte); - } - else - { - /* Plain relation */ - set_plain_rel_size(root, rel, rte); + case RELKIND_FOREIGN_TABLE: + /* Foreign table */ + set_foreign_size(root, rel, rte); + break; + case RELKIND_PARTITIONED_TABLE: + + /* + * We could get here if asked to scan a partitioned + * table with ONLY. In that case we shouldn't scan + * any of the partitions, so mark it as a dummy rel. + */ + set_dummy_rel_pathlist(rel); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + if (rte->tablesample != NULL) + { + /* Sampled relation */ + set_tablesample_rel_size(root, rel, rte); + } + else + { + /* Plain relation */ + set_plain_rel_size(root, rel, rte); + } } break; case RTE_SUBQUERY: @@ -484,20 +495,32 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, switch (rel->rtekind) { case RTE_RELATION: - if (rte->relkind == RELKIND_FOREIGN_TABLE) - { - /* Foreign table */ - set_foreign_pathlist(root, rel, rte); - } - else if (rte->tablesample != NULL) + Assert(RELKIND_IS_VALID((RelKind) rte->relkind)); + switch ((RelKind) rte->relkind) { - /* Sampled relation */ - set_tablesample_rel_pathlist(root, rel, rte); - } - else - { - /* Plain relation */ - set_plain_rel_pathlist(root, rel, rte); + case RELKIND_FOREIGN_TABLE: + /* Foreign table */ + set_foreign_pathlist(root, rel, rte); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + if (rte->tablesample != NULL) + { + /* Sampled relation */ + set_tablesample_rel_pathlist(root, rel, rte); + } + else + { + /* Plain relation */ + set_plain_rel_pathlist(root, rel, rte); + } } break; case RTE_SUBQUERY: @@ -643,13 +666,26 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel, * up with a separate connection, and these connections might not * be appropriately coordinated between workers and the leader. */ - if (rte->relkind == RELKIND_FOREIGN_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rte->relkind)); + switch ((RelKind) rte->relkind) { - Assert(rel->fdwroutine); - if (!rel->fdwroutine->IsForeignScanParallelSafe) - return; - if (!rel->fdwroutine->IsForeignScanParallelSafe(root, rel, rte)) - return; + case RELKIND_FOREIGN_TABLE: + Assert(rel->fdwroutine); + if (!rel->fdwroutine->IsForeignScanParallelSafe) + return; + if (!rel->fdwroutine->IsForeignScanParallelSafe(root, rel, rte)) + return; + break; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } /* @@ -962,20 +998,57 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, * the indexes of partitioned relations that appear down in the tree, so * that when we've created Paths for all the children, the root * partitioned table's list will contain all such indexes. + * + * Beware that rte->relkind here may be NULL, which is not a valid member + * of enum RelKind. */ - if (rte->relkind == RELKIND_PARTITIONED_TABLE) - rel->partitioned_child_rels = list_make1_int(rti); + Assert(rte->relkind == '\0' || RELKIND_IS_VALID((RelKind) rte->relkind)); + switch ((RelKind) rte->relkind) + { + case RELKIND_PARTITIONED_TABLE: + rel->partitioned_child_rels = list_make1_int(rti); + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } /* * If this is a partitioned baserel, set the consider_partitionwise_join * flag; currently, we only consider partitionwise joins with the baserel * if its targetlist doesn't contain a whole-row Var. + * + * Beware that rte->relkind here may be NULL, which is not a valid member + * of enum RelKind. */ - if (enable_partitionwise_join && - rel->reloptkind == RELOPT_BASEREL && - rte->relkind == RELKIND_PARTITIONED_TABLE && - rel->attr_needed[InvalidAttrNumber - rel->min_attr] == NULL) - rel->consider_partitionwise_join = true; + Assert(rte->relkind == '\0' || RELKIND_IS_VALID((RelKind) rte->relkind)); + switch ((RelKind) rte->relkind) + { + case RELKIND_PARTITIONED_TABLE: + if (enable_partitionwise_join && + rel->reloptkind == RELOPT_BASEREL && + rte->relkind == RELKIND_PARTITIONED_TABLE && + rel->attr_needed[InvalidAttrNumber - rel->min_attr] == NULL) + rel->consider_partitionwise_join = true; + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; + } /* * Initialize to compute size estimates for whole append relation. diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index b406d41e91..e23edcc739 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1280,10 +1280,23 @@ inheritance_planner(PlannerInfo *root) */ parent_rte = rt_fetch(top_parentRTindex, parse->rtable); Assert(parent_rte->inh); - if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) parent_rte->relkind)); + switch ((RelKind) parent_rte->relkind) { - nominalRelation = top_parentRTindex; - rootRelation = top_parentRTindex; + case RELKIND_PARTITIONED_TABLE: + nominalRelation = top_parentRTindex; + rootRelation = top_parentRTindex; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } /* @@ -2338,11 +2351,23 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, * If target is a partition root table, we need to mark the * ModifyTable node appropriately for that. */ - if (rt_fetch(parse->resultRelation, parse->rtable)->relkind == - RELKIND_PARTITIONED_TABLE) - rootRelation = parse->resultRelation; - else - rootRelation = 0; + Assert(RELKIND_IS_VALID((RelKind) rt_fetch(parse->resultRelation, parse->rtable)->relkind)); + switch ((RelKind) rt_fetch(parse->resultRelation, parse->rtable)->relkind) + { + case RELKIND_PARTITIONED_TABLE: + rootRelation = parse->resultRelation; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + rootRelation = 0; + } /* * Set up the WITH CHECK OPTION and RETURNING lists-of-lists, if @@ -2765,44 +2790,58 @@ select_rowmark_type(RangeTblEntry *rte, LockClauseStrength strength) /* If it's not a table at all, use ROW_MARK_COPY */ return ROW_MARK_COPY; } - else if (rte->relkind == RELKIND_FOREIGN_TABLE) - { - /* Let the FDW select the rowmark type, if it wants to */ - FdwRoutine *fdwroutine = GetFdwRoutineByRelId(rte->relid); - - if (fdwroutine->GetForeignRowMarkType != NULL) - return fdwroutine->GetForeignRowMarkType(rte, strength); - /* Otherwise, use ROW_MARK_COPY by default */ - return ROW_MARK_COPY; - } else { - /* Regular table, apply the appropriate lock type */ - switch (strength) + Assert(RELKIND_IS_VALID((RelKind) rte->relkind)); + switch ((RelKind) rte->relkind) { - case LCS_NONE: + case RELKIND_FOREIGN_TABLE: + { + /* Let the FDW select the rowmark type, if it wants to */ + FdwRoutine *fdwroutine = GetFdwRoutineByRelId(rte->relid); - /* - * We don't need a tuple lock, only the ability to re-fetch - * the row. - */ - return ROW_MARK_REFERENCE; - break; - case LCS_FORKEYSHARE: - return ROW_MARK_KEYSHARE; - break; - case LCS_FORSHARE: - return ROW_MARK_SHARE; - break; - case LCS_FORNOKEYUPDATE: - return ROW_MARK_NOKEYEXCLUSIVE; - break; - case LCS_FORUPDATE: - return ROW_MARK_EXCLUSIVE; + if (fdwroutine->GetForeignRowMarkType != NULL) + return fdwroutine->GetForeignRowMarkType(rte, strength); + /* Otherwise, use ROW_MARK_COPY by default */ + return ROW_MARK_COPY; + } break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + /* Regular table, apply the appropriate lock type */ + switch (strength) + { + case LCS_NONE: + + /* + * We don't need a tuple lock, only the ability to + * re-fetch the row. + */ + return ROW_MARK_REFERENCE; + break; + case LCS_FORKEYSHARE: + return ROW_MARK_KEYSHARE; + break; + case LCS_FORSHARE: + return ROW_MARK_SHARE; + break; + case LCS_FORNOKEYUPDATE: + return ROW_MARK_NOKEYEXCLUSIVE; + break; + case LCS_FORUPDATE: + return ROW_MARK_EXCLUSIVE; + break; + } + elog(ERROR, "unrecognized LockClauseStrength %d", (int) strength); + return ROW_MARK_EXCLUSIVE; /* keep compiler quiet */ } - elog(ERROR, "unrecognized LockClauseStrength %d", (int) strength); - return ROW_MARK_EXCLUSIVE; /* keep compiler quiet */ } } @@ -3142,8 +3181,23 @@ remove_useless_groupby_columns(PlannerInfo *root) * may cause duplicate rows. This cannot happen with partitioned * tables, however. */ - if (rte->inh && rte->relkind != RELKIND_PARTITIONED_TABLE) - continue; + Assert(RELKIND_IS_VALID((RelKind) rte->relkind)); + switch ((RelKind) rte->relkind) + { + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + if (rte->inh) + continue; + } /* Nothing to do unless this rel has multiple Vars in GROUP BY */ relattnos = groupbyattnos[relid]; diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 3132fd35a5..5045927bec 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -129,88 +129,111 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, } /* Scan the inheritance set and expand it */ - if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) oldrelation->rd_rel->relkind)); + switch ((RelKind) oldrelation->rd_rel->relkind) { - /* - * Partitioned table, so set up for partitioning. - */ - Assert(rte->relkind == RELKIND_PARTITIONED_TABLE); - - /* - * Recursively expand and lock the partitions. While at it, also - * extract the partition key columns of all the partitioned tables. - */ - expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); - } - else - { - /* - * Ordinary table, so process traditional-inheritance children. (Note - * that partitioned tables are not allowed to have inheritance - * children, so it's not possible for both cases to apply.) - */ - List *inhOIDs; - ListCell *l; - - /* Scan for all members of inheritance set, acquire needed locks */ - inhOIDs = find_all_inheritors(parentOID, lockmode, NULL); - - /* - * We used to special-case the situation where the table no longer has - * any children, by clearing rte->inh and exiting. That no longer - * works, because this function doesn't get run until after decisions - * have been made that depend on rte->inh. We have to treat such - * situations as normal inheritance. The table itself should always - * have been found, though. - */ - Assert(inhOIDs != NIL); - Assert(linitial_oid(inhOIDs) == parentOID); - - /* Expand simple_rel_array and friends to hold child objects. */ - expand_planner_arrays(root, list_length(inhOIDs)); - - /* - * Expand inheritance children in the order the OIDs were returned by - * find_all_inheritors. - */ - foreach(l, inhOIDs) - { - Oid childOID = lfirst_oid(l); - Relation newrelation; - RangeTblEntry *childrte; - Index childRTindex; - - /* Open rel if needed; we already have required locks */ - if (childOID != parentOID) - newrelation = table_open(childOID, NoLock); - else - newrelation = oldrelation; - - /* - * It is possible that the parent table has children that are temp - * tables of other backends. We cannot safely access such tables - * (because of buffering issues), and the best thing to do seems - * to be to silently ignore them. - */ - if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation)) + case RELKIND_PARTITIONED_TABLE: { - table_close(newrelation, lockmode); - continue; + /* + * Partitioned table, so set up for partitioning. + */ + Assert(rte->relkind == RELKIND_PARTITIONED_TABLE); + + /* + * Recursively expand and lock the partitions. While at it, + * also extract the partition key columns of all the + * partitioned tables. + */ + expand_partitioned_rtentry(root, rel, rte, rti, + oldrelation, oldrc, lockmode); + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + { + /* + * Ordinary table, so process traditional-inheritance + * children. (Note that partitioned tables are not allowed to + * have inheritance children, so it's not possible for both + * cases to apply.) + */ + List *inhOIDs; + ListCell *l; + + /* + * Scan for all members of inheritance set, acquire needed + * locks + */ + inhOIDs = find_all_inheritors(parentOID, lockmode, NULL); + + /* + * We used to special-case the situation where the table no + * longer has any children, by clearing rte->inh and exiting. + * That no longer works, because this function doesn't get run + * until after decisions have been made that depend on + * rte->inh. We have to treat such situations as normal + * inheritance. The table itself should always have been + * found, though. + */ + Assert(inhOIDs != NIL); + Assert(linitial_oid(inhOIDs) == parentOID); + + /* Expand simple_rel_array and friends to hold child objects. */ + expand_planner_arrays(root, list_length(inhOIDs)); + + /* + * Expand inheritance children in the order the OIDs were + * returned by find_all_inheritors. + */ + foreach(l, inhOIDs) + { + Oid childOID = lfirst_oid(l); + Relation newrelation; + RangeTblEntry *childrte; + Index childRTindex; + + /* Open rel if needed; we already have required locks */ + if (childOID != parentOID) + newrelation = table_open(childOID, NoLock); + else + newrelation = oldrelation; + + /* + * It is possible that the parent table has children that + * are temp tables of other backends. We cannot safely + * access such tables (because of buffering issues), and + * the best thing to do seems to be to silently ignore + * them. + */ + if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation)) + { + table_close(newrelation, lockmode); + continue; + } + + /* + * Create RTE and AppendRelInfo, plus PlanRowMark if + * needed. + */ + expand_single_inheritance_child(root, rte, rti, oldrelation, + oldrc, newrelation, + &childrte, &childRTindex); + + /* Create the otherrel RelOptInfo too. */ + (void) build_simple_rel(root, childRTindex, rel); + + /* Close child relations, but keep locks */ + if (childOID != parentOID) + table_close(newrelation, NoLock); + } } - - /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */ - expand_single_inheritance_child(root, rte, rti, oldrelation, - oldrc, newrelation, - &childrte, &childRTindex); - - /* Create the otherrel RelOptInfo too. */ - (void) build_simple_rel(root, childRTindex, rel); - - /* Close child relations, but keep locks */ - if (childOID != parentOID) - table_close(newrelation, NoLock); - } } /* @@ -380,10 +403,25 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, childrelinfo->relids); /* If this child is itself partitioned, recurse */ - if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - expand_partitioned_rtentry(root, childrelinfo, - childrte, childRTindex, - childrel, top_parentrc, lockmode); + Assert(RELKIND_IS_VALID((RelKind) childrel->rd_rel->relkind)); + switch ((RelKind) childrel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + expand_partitioned_rtentry(root, childrelinfo, + childrte, childRTindex, + childrel, top_parentrc, lockmode); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -448,13 +486,24 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->relid = childOID; childrte->relkind = childrel->rd_rel->relkind; /* A partitioned child will need to be expanded further. */ - if (childrte->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) childrte->relkind)); + switch ((RelKind) childrte->relkind) { - Assert(childOID != parentOID); - childrte->inh = true; + case RELKIND_PARTITIONED_TABLE: + Assert(childOID != parentOID); + childrte->inh = true; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + childrte->inh = false; } - else - childrte->inh = false; childrte->requiredPerms = 0; childrte->securityQuals = NIL; @@ -578,7 +627,23 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, * that the executor ignores them (except their existence means that * the child tables will be locked using the appropriate mode). */ - childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE); + Assert(RELKIND_IS_VALID((RelKind) childrte->relkind)); + switch ((RelKind) childrte->relkind) + { + case RELKIND_PARTITIONED_TABLE: + childrc->isParent = true; + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + childrc->isParent = false; + } /* Include child's rowmark type in top parent's allMarkTypes */ top_parentrc->allMarkTypes |= childrc->allMarkTypes; diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 25545029d7..92a3361401 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -215,10 +215,22 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, * Ignore partitioned indexes, since they are not usable for * queries. */ - if (indexRelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) + Assert(RELKIND_IS_VALID((RelKind) indexRelation->rd_rel->relkind)); + switch ((RelKind) indexRelation->rd_rel->relkind) { - index_close(indexRelation, NoLock); - continue; + case RELKIND_PARTITIONED_INDEX: + index_close(indexRelation, NoLock); + continue; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } /* @@ -442,15 +454,24 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, rel->statlist = get_relation_statistics(rel, relation); /* Grab foreign-table info using the relcache, while we have it */ - if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { - rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation)); - rel->fdwroutine = GetFdwRoutineForRelation(relation, true); - } - else - { - rel->serverid = InvalidOid; - rel->fdwroutine = NULL; + case RELKIND_FOREIGN_TABLE: + rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation)); + rel->fdwroutine = GetFdwRoutineForRelation(relation, true); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + rel->serverid = InvalidOid; + rel->fdwroutine = NULL; } /* Collect info about relation's foreign keys, if relevant */ @@ -460,8 +481,24 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, * Collect info about relation's partitioning scheme, if any. Only * inheritance parents may be partitioned. */ - if (inhparent && relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - set_relation_partition_info(root, rel, relation); + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + if (inhparent) + set_relation_partition_info(root, rel, relation); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } table_close(relation, NoLock); @@ -955,7 +992,8 @@ estimate_rel_size(Relation rel, int32 *attr_widths, BlockNumber relallvisible; double density; - switch (rel->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { case RELKIND_RELATION: case RELKIND_MATVIEW: @@ -1063,12 +1101,14 @@ estimate_rel_size(Relation rel, int32 *attr_widths, *tuples = rel->rd_rel->reltuples; *allvisfrac = 0; break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: /* else it has no disk storage; probably shouldn't get here? */ *pages = 0; *tuples = 0; *allvisfrac = 0; - break; } } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 25abc544fc..9119d71ca0 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -944,37 +944,55 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla relation = relation_openrv(table_like_clause->relation, AccessShareLock); - if (relation->rd_rel->relkind != RELKIND_RELATION && - relation->rd_rel->relkind != RELKIND_VIEW && - relation->rd_rel->relkind != RELKIND_MATVIEW && - relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE && - relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE && - relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table", - RelationGetRelationName(relation)))); + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table", + RelationGetRelationName(relation)))); + } cancel_parser_errposition_callback(&pcbstate); /* * Check for privileges */ - if (relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) - { - aclresult = pg_type_aclcheck(relation->rd_rel->reltype, GetUserId(), - ACL_USAGE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, OBJECT_TYPE, - RelationGetRelationName(relation)); - } - else + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { - aclresult = pg_class_aclcheck(RelationGetRelid(relation), GetUserId(), - ACL_SELECT); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, get_relkind_objtype(relation->rd_rel->relkind), - RelationGetRelationName(relation)); + case RELKIND_COMPOSITE_TYPE: + aclresult = pg_type_aclcheck(relation->rd_rel->reltype, GetUserId(), + ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, OBJECT_TYPE, + RelationGetRelationName(relation)); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + aclresult = pg_class_aclcheck(RelationGetRelid(relation), GetUserId(), + ACL_SELECT); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_relkind_objtype(relation->rd_rel->relkind), + RelationGetRelationName(relation)); } tupleDesc = RelationGetDescr(relation); @@ -2306,13 +2324,25 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) rel = table_openrv(inh, AccessShareLock); /* check user requested inheritance from valid relkind */ - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && - rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("inherited relation \"%s\" is not a table or foreign table", - inh->relname))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("inherited relation \"%s\" is not a table or foreign table", + inh->relname))); + } for (count = 0; count < rel->rd_att->natts; count++) { Form_pg_attribute inhattr = TupleDescAttr(rel->rd_att, @@ -2448,13 +2478,25 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) rel = table_openrv(inh, AccessShareLock); /* check user requested inheritance from valid relkind */ - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && - rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("inherited relation \"%s\" is not a table or foreign table", - inh->relname))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("inherited relation \"%s\" is not a table or foreign table", + inh->relname))); + } for (count = 0; count < rel->rd_att->natts; count++) { Form_pg_attribute inhattr = TupleDescAttr(rel->rd_att, @@ -2763,10 +2805,26 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, */ rel = table_openrv(stmt->relation, AccessExclusiveLock); - if (rel->rd_rel->relkind == RELKIND_MATVIEW) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("rules on materialized views are not supported"))); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_MATVIEW: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("rules on materialized views are not supported"))); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } + /* Set up pstate */ pstate = make_parsestate(NULL); @@ -3097,15 +3155,24 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, /* Set up CreateStmtContext */ cxt.pstate = pstate; - if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - cxt.stmtType = "ALTER FOREIGN TABLE"; - cxt.isforeign = true; - } - else - { - cxt.stmtType = "ALTER TABLE"; - cxt.isforeign = false; + case RELKIND_FOREIGN_TABLE: + cxt.stmtType = "ALTER FOREIGN TABLE"; + cxt.isforeign = true; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + cxt.stmtType = "ALTER TABLE"; + cxt.isforeign = false; } cxt.relation = stmt->relation; cxt.rel = rel; @@ -3722,7 +3789,8 @@ transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd) { Relation parentRel = cxt->rel; - switch (parentRel->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) parentRel->rd_rel->relkind)); + switch ((RelKind) parentRel->rd_rel->relkind) { case RELKIND_PARTITIONED_TABLE: /* transform the partition bound, if any */ @@ -3757,11 +3825,15 @@ transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd) errmsg("index \"%s\" is not partitioned", RelationGetRelationName(parentRel)))); break; - default: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: /* parser shouldn't let this case through */ elog(ERROR, "\"%s\" is not a partitioned table or index", RelationGetRelationName(parentRel)); - break; } } diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c index 7553d55987..f6bc2066e3 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -3137,11 +3137,24 @@ check_default_partition_contents(Relation parent, Relation default_rel, * Scan the default partition and its subpartitions, and check for rows * that do not satisfy the revised partition constraints. */ - if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - all_parts = find_all_inheritors(RelationGetRelid(default_rel), - AccessExclusiveLock, NULL); - else - all_parts = list_make1_oid(RelationGetRelid(default_rel)); + Assert(RELKIND_IS_VALID((RelKind) default_rel->rd_rel->relkind)); + switch ((RelKind) default_rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + all_parts = find_all_inheritors(RelationGetRelid(default_rel), + AccessExclusiveLock, NULL); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + all_parts = list_make1_oid(RelationGetRelid(default_rel)); + } foreach(lc, all_parts) { @@ -3196,19 +3209,29 @@ check_default_partition_contents(Relation parent, Relation default_rel, * Only RELKIND_RELATION relations (i.e. leaf partitions) need to be * scanned. */ - if (part_rel->rd_rel->relkind != RELKIND_RELATION) + Assert(RELKIND_IS_VALID((RelKind) part_rel->rd_rel->relkind)); + switch ((RelKind) part_rel->rd_rel->relkind) { - if (part_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + case RELKIND_RELATION: + break; + case RELKIND_FOREIGN_TABLE: ereport(WARNING, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("skipped scanning foreign table \"%s\" which is a partition of default partition \"%s\"", RelationGetRelationName(part_rel), RelationGetRelationName(default_rel)))); - - if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel)) - table_close(part_rel, NoLock); - - continue; + /* fallthrough */ + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel)) + table_close(part_rel, NoLock); + continue; } estate = CreateExecutorState(); diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 9c7d4b0c60..4fc26fec7f 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -2058,9 +2058,22 @@ do_autovacuum(void) bool doanalyze; bool wraparound; - if (classForm->relkind != RELKIND_RELATION && - classForm->relkind != RELKIND_MATVIEW) - continue; + Assert(RELKIND_IS_VALID((RelKind) classForm->relkind)); + switch ((RelKind) classForm->relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + continue; + } relid = classForm->oid; @@ -2720,9 +2733,23 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc) bytea *relopts; AutoVacOpts *av; - Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION || - ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW || - ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE); + Assert(RELKIND_IS_VALID((RelKind) ((Form_pg_class) GETSTRUCT(tup))->relkind)); + switch ((RelKind) ((Form_pg_class) GETSTRUCT(tup))->relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: + Assert(false); + break; + } relopts = extractRelOptions(tup, pg_class_desc, NULL); if (relopts == NULL) @@ -2800,17 +2827,33 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, * main table reloptions if the toast table itself doesn't have. */ avopts = extract_autovac_opts(classTup, pg_class_desc); - if (classForm->relkind == RELKIND_TOASTVALUE && - avopts == NULL && table_toast_map != NULL) + Assert(RELKIND_IS_VALID((RelKind) classForm->relkind)); + switch ((RelKind) classForm->relkind) { - av_relation *hentry; - bool found; + case RELKIND_TOASTVALUE: + if (avopts == NULL && table_toast_map != NULL) + { + av_relation *hentry; + bool found; - hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found); - if (found && hentry->ar_hasrelopts) - avopts = &hentry->ar_reloptions; + hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found); + if (found && hentry->ar_hasrelopts) + avopts = &hentry->ar_reloptions; + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_VIEW: + break; } + /* fetch the pgstat table entry */ tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared, shared, dbentry); @@ -2820,8 +2863,23 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, &dovacuum, &doanalyze, &wraparound); /* ignore ANALYZE for toast tables */ - if (classForm->relkind == RELKIND_TOASTVALUE) - doanalyze = false; + Assert(RELKIND_IS_VALID((RelKind) classForm->relkind)); + switch ((RelKind) classForm->relkind) + { + case RELKIND_TOASTVALUE: + doanalyze = false; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_VIEW: + break; + } /* OK, it needs something done */ if (doanalyze || dovacuum) diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index 6064384e32..91459a7327 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -414,7 +414,7 @@ perform_base_backup(basebackup_options *opt) if (ti->path == NULL) { struct stat statbuf; - bool sendtblspclinks = true; + bool sendtblspclinks = true; /* In the main tar, include the backup_label first... */ sendFileWithContent(BACKUP_LABEL_FILE, labelfile->data, @@ -1777,8 +1777,8 @@ sendFile(const char *readfilename, const char *tarfilename, } /* - * Pad to a block boundary, per tar format requirements. (This small - * piece of data is probably not worth throttling, and is not checksummed + * Pad to a block boundary, per tar format requirements. (This small piece + * of data is probably not worth throttling, and is not checksummed * because it's not actually part of the file.) */ pad = tarPaddingBytesRequired(len); diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index 5251932669..11aed95301 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -1639,8 +1639,22 @@ ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid, * understand, so it doesn't make sense to handle the few * cases we do. */ - if (relation->rd_rel->relkind == RELKIND_SEQUENCE) - goto change_done; + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) + { + case RELKIND_SEQUENCE: + goto change_done; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } /* user-triggered change */ if (!IsToastRelation(relation)) diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c index c27d970589..b6a154270b 100644 --- a/src/backend/replication/logical/tablesync.c +++ b/src/backend/replication/logical/tablesync.c @@ -765,24 +765,35 @@ copy_table(Relation rel) /* Start copy on the publisher. */ initStringInfo(&cmd); - if (lrel.relkind == RELKIND_RELATION) - appendStringInfo(&cmd, "COPY %s TO STDOUT", - quote_qualified_identifier(lrel.nspname, lrel.relname)); - else + Assert(RELKIND_IS_VALID((RelKind) lrel.relkind)); + switch ((RelKind) lrel.relkind) { - /* - * For non-tables, we need to do COPY (SELECT ...), but we can't just - * do SELECT * because we need to not copy generated columns. - */ - appendStringInfo(&cmd, "COPY (SELECT "); - for (int i = 0; i < lrel.natts; i++) - { - appendStringInfoString(&cmd, quote_identifier(lrel.attnames[i])); - if (i < lrel.natts - 1) - appendStringInfoString(&cmd, ", "); - } - appendStringInfo(&cmd, " FROM %s) TO STDOUT", - quote_qualified_identifier(lrel.nspname, lrel.relname)); + case RELKIND_RELATION: + appendStringInfo(&cmd, "COPY %s TO STDOUT", + quote_qualified_identifier(lrel.nspname, lrel.relname)); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + /* + * For non-tables, we need to do COPY (SELECT ...), but we can't + * just do SELECT * because we need to not copy generated columns. + */ + appendStringInfo(&cmd, "COPY (SELECT "); + for (int i = 0; i < lrel.natts; i++) + { + appendStringInfoString(&cmd, quote_identifier(lrel.attnames[i])); + if (i < lrel.natts - 1) + appendStringInfoString(&cmd, ", "); + } + appendStringInfo(&cmd, " FROM %s) TO STDOUT", + quote_qualified_identifier(lrel.nspname, lrel.relname)); } res = walrcv_exec(wrconn, cmd.data, 0, NULL); pfree(cmd.data); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index f90a896fc3..ba159a5bde 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -646,12 +646,25 @@ apply_handle_insert(StringInfo s) MemoryContextSwitchTo(oldctx); /* For a partitioned table, insert the tuple into a partition. */ - if (rel->localrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - apply_handle_tuple_routing(estate->es_result_relation_info, estate, - remoteslot, NULL, rel, CMD_INSERT); - else - apply_handle_insert_internal(estate->es_result_relation_info, estate, - remoteslot); + Assert(RELKIND_IS_VALID((RelKind) rel->localrel->rd_rel->relkind)); + switch ((RelKind) rel->localrel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + apply_handle_tuple_routing(estate->es_result_relation_info, estate, + remoteslot, NULL, rel, CMD_INSERT); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + apply_handle_insert_internal(estate->es_result_relation_info, estate, + remoteslot); + } PopActiveSnapshot(); @@ -781,12 +794,25 @@ apply_handle_update(StringInfo s) MemoryContextSwitchTo(oldctx); /* For a partitioned table, apply update to correct partition. */ - if (rel->localrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - apply_handle_tuple_routing(estate->es_result_relation_info, estate, - remoteslot, &newtup, rel, CMD_UPDATE); - else - apply_handle_update_internal(estate->es_result_relation_info, estate, - remoteslot, &newtup, rel); + Assert(RELKIND_IS_VALID((RelKind) rel->localrel->rd_rel->relkind)); + switch ((RelKind) rel->localrel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + apply_handle_tuple_routing(estate->es_result_relation_info, estate, + remoteslot, &newtup, rel, CMD_UPDATE); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + apply_handle_update_internal(estate->es_result_relation_info, estate, + remoteslot, &newtup, rel); + } PopActiveSnapshot(); @@ -904,12 +930,25 @@ apply_handle_delete(StringInfo s) MemoryContextSwitchTo(oldctx); /* For a partitioned table, apply delete to correct partition. */ - if (rel->localrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - apply_handle_tuple_routing(estate->es_result_relation_info, estate, - remoteslot, NULL, rel, CMD_DELETE); - else - apply_handle_delete_internal(estate->es_result_relation_info, estate, - remoteslot, &rel->remoterel); + Assert(RELKIND_IS_VALID((RelKind) rel->localrel->rd_rel->relkind)); + switch ((RelKind) rel->localrel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + apply_handle_tuple_routing(estate->es_result_relation_info, estate, + remoteslot, NULL, rel, CMD_DELETE); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + apply_handle_delete_internal(estate->es_result_relation_info, estate, + remoteslot, &rel->remoterel); + } PopActiveSnapshot(); @@ -1273,41 +1312,60 @@ apply_handle_truncate(StringInfo s) * Truncate partitions if we got a message to truncate a partitioned * table. */ - if (rel->localrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->localrel->rd_rel->relkind)); + switch ((RelKind) rel->localrel->rd_rel->relkind) { - ListCell *child; - List *children = find_all_inheritors(rel->localreloid, - RowExclusiveLock, - NULL); - - foreach(child, children) - { - Oid childrelid = lfirst_oid(child); - Relation childrel; - - if (list_member_oid(relids, childrelid)) - continue; - - /* find_all_inheritors already got lock */ - childrel = table_open(childrelid, NoLock); - - /* - * Ignore temp tables of other backends. See similar code in - * ExecuteTruncate(). - */ - if (RELATION_IS_OTHER_TEMP(childrel)) + case RELKIND_PARTITIONED_TABLE: { - table_close(childrel, RowExclusiveLock); - continue; - } + ListCell *child; + List *children = find_all_inheritors(rel->localreloid, + RowExclusiveLock, + NULL); - rels = lappend(rels, childrel); - part_rels = lappend(part_rels, childrel); - relids = lappend_oid(relids, childrelid); - /* Log this relation only if needed for logical decoding */ - if (RelationIsLogicallyLogged(childrel)) - relids_logged = lappend_oid(relids_logged, childrelid); - } + foreach(child, children) + { + Oid childrelid = lfirst_oid(child); + Relation childrel; + + if (list_member_oid(relids, childrelid)) + continue; + + /* find_all_inheritors already got lock */ + childrel = table_open(childrelid, NoLock); + + /* + * Ignore temp tables of other backends. See similar + * code in ExecuteTruncate(). + */ + if (RELATION_IS_OTHER_TEMP(childrel)) + { + table_close(childrel, RowExclusiveLock); + continue; + } + + rels = lappend(rels, childrel); + part_rels = lappend(part_rels, childrel); + relids = lappend_oid(relids, childrelid); + + /* + * Log this relation only if needed for logical + * decoding + */ + if (RelationIsLogicallyLogged(childrel)) + relids_logged = lappend_oid(relids_logged, childrelid); + } + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } } diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index 57bbb6288c..54ae4af52b 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -100,8 +100,8 @@ int max_replication_slots = 0; /* the maximum number of replication * slots */ static ReplicationSlot *SearchNamedReplicationSlot(const char *name); -static int ReplicationSlotAcquireInternal(ReplicationSlot *slot, - const char *name, SlotAcquireBehavior behavior); +static int ReplicationSlotAcquireInternal(ReplicationSlot *slot, + const char *name, SlotAcquireBehavior behavior); static void ReplicationSlotDropAcquired(void); static void ReplicationSlotDropPtr(ReplicationSlot *slot); @@ -335,7 +335,7 @@ static ReplicationSlot * SearchNamedReplicationSlot(const char *name) { int i; - ReplicationSlot *slot = NULL; + ReplicationSlot *slot = NULL; Assert(LWLockHeldByMeInMode(ReplicationSlotControlLock, LW_SHARED)); @@ -434,8 +434,8 @@ retry: /* * If we found the slot but it's already active in another process, we - * either error out, return the PID of the owning process, or retry - * after a short wait, as caller specified. + * either error out, return the PID of the owning process, or retry after + * a short wait, as caller specified. */ if (active_pid != MyProcPid) { @@ -454,7 +454,7 @@ retry: goto retry; } else if (behavior == SAB_Block) - ConditionVariableCancelSleep(); /* no sleep needed after all */ + ConditionVariableCancelSleep(); /* no sleep needed after all */ /* Let everybody know we've modified this slot */ ConditionVariableBroadcast(&s->active_cv); @@ -1143,8 +1143,8 @@ restart: ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; XLogRecPtr restart_lsn = InvalidXLogRecPtr; NameData slotname; - int wspid; - int last_signaled_pid = 0; + int wspid; + int last_signaled_pid = 0; if (!s->in_use) continue; @@ -1167,20 +1167,20 @@ restart: /* * Try to mark this slot as used by this process. * - * Note that ReplicationSlotAcquireInternal(SAB_Inquire) - * should not cancel the prepared condition variable - * if this slot is active in other process. Because in this case - * we have to wait on that CV for the process owning - * the slot to be terminated, later. + * Note that ReplicationSlotAcquireInternal(SAB_Inquire) should + * not cancel the prepared condition variable if this slot is + * active in other process. Because in this case we have to wait + * on that CV for the process owning the slot to be terminated, + * later. */ wspid = ReplicationSlotAcquireInternal(s, NULL, SAB_Inquire); /* - * Exit the loop if we successfully acquired the slot or - * the slot was dropped during waiting for the owning process - * to be terminated. For example, the latter case is likely to - * happen when the slot is temporary because it's automatically - * dropped by the termination of the owning process. + * Exit the loop if we successfully acquired the slot or the slot + * was dropped during waiting for the owning process to be + * terminated. For example, the latter case is likely to happen + * when the slot is temporary because it's automatically dropped + * by the termination of the owning process. */ if (wspid <= 0) break; @@ -1188,13 +1188,13 @@ restart: /* * Signal to terminate the process that owns the slot. * - * There is the race condition where other process may own - * the slot after the process using it was terminated and before - * this process owns it. To handle this case, we signal again - * if the PID of the owning process is changed than the last. + * There is the race condition where other process may own the + * slot after the process using it was terminated and before this + * process owns it. To handle this case, we signal again if the + * PID of the owning process is changed than the last. * - * XXX This logic assumes that the same PID is not reused - * very quickly. + * XXX This logic assumes that the same PID is not reused very + * quickly. */ if (last_signaled_pid != wspid) { @@ -1211,8 +1211,8 @@ restart: ConditionVariableCancelSleep(); /* - * Do nothing here and start from scratch if the slot has - * already been dropped. + * Do nothing here and start from scratch if the slot has already been + * dropped. */ if (wspid == -1) goto restart; diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 9989df1107..8de3d902df 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -262,14 +262,25 @@ DefineQueryRewrite(const char *rulename, * Internal callers can target materialized views, but transformRuleStmt() * blocks them for users. Don't mention them in the error message. */ - if (event_relation->rd_rel->relkind != RELKIND_RELATION && - event_relation->rd_rel->relkind != RELKIND_MATVIEW && - event_relation->rd_rel->relkind != RELKIND_VIEW && - event_relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or view", - RelationGetRelationName(event_relation)))); + Assert(RELKIND_IS_VALID((RelKind) event_relation->rd_rel->relkind)); + switch ((RelKind) event_relation->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table or view", + RelationGetRelationName(event_relation)))); + } if (!allowSystemTableMods && IsSystemRelation(event_relation)) ereport(ERROR, @@ -421,69 +432,81 @@ DefineQueryRewrite(const char *rulename, * views is just a kluge to allow dump/reload of views that * participate in circular dependencies.) */ - if (event_relation->rd_rel->relkind != RELKIND_VIEW && - event_relation->rd_rel->relkind != RELKIND_MATVIEW) + Assert(RELKIND_IS_VALID((RelKind) event_relation->rd_rel->relkind)); + switch ((RelKind) event_relation->rd_rel->relkind) { - TableScanDesc scanDesc; - Snapshot snapshot; - TupleTableSlot *slot; - - if (event_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + case RELKIND_VIEW: + case RELKIND_MATVIEW: + break; + case RELKIND_PARTITIONED_TABLE: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot convert partitioned table \"%s\" to a view", RelationGetRelationName(event_relation)))); - - if (event_relation->rd_rel->relispartition) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot convert partition \"%s\" to a view", - RelationGetRelationName(event_relation)))); - - snapshot = RegisterSnapshot(GetLatestSnapshot()); - scanDesc = table_beginscan(event_relation, snapshot, 0, NULL); - slot = table_slot_create(event_relation, NULL); - if (table_scan_getnextslot(scanDesc, ForwardScanDirection, slot)) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("could not convert table \"%s\" to a view because it is not empty", - RelationGetRelationName(event_relation)))); - ExecDropSingleTupleTableSlot(slot); - table_endscan(scanDesc); - UnregisterSnapshot(snapshot); - - if (event_relation->rd_rel->relhastriggers) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("could not convert table \"%s\" to a view because it has triggers", - RelationGetRelationName(event_relation)), - errhint("In particular, the table cannot be involved in any foreign key relationships."))); - - if (event_relation->rd_rel->relhasindex) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("could not convert table \"%s\" to a view because it has indexes", - RelationGetRelationName(event_relation)))); - - if (event_relation->rd_rel->relhassubclass) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("could not convert table \"%s\" to a view because it has child tables", - RelationGetRelationName(event_relation)))); - - if (event_relation->rd_rel->relrowsecurity) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("could not convert table \"%s\" to a view because it has row security enabled", - RelationGetRelationName(event_relation)))); - - if (relation_has_policies(event_relation)) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("could not convert table \"%s\" to a view because it has row security policies", - RelationGetRelationName(event_relation)))); - - RelisBecomingView = true; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + { + TableScanDesc scanDesc; + Snapshot snapshot; + TupleTableSlot *slot; + + if (event_relation->rd_rel->relispartition) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot convert partition \"%s\" to a view", + RelationGetRelationName(event_relation)))); + + snapshot = RegisterSnapshot(GetLatestSnapshot()); + scanDesc = table_beginscan(event_relation, snapshot, 0, NULL); + slot = table_slot_create(event_relation, NULL); + if (table_scan_getnextslot(scanDesc, ForwardScanDirection, slot)) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("could not convert table \"%s\" to a view because it is not empty", + RelationGetRelationName(event_relation)))); + ExecDropSingleTupleTableSlot(slot); + table_endscan(scanDesc); + UnregisterSnapshot(snapshot); + + if (event_relation->rd_rel->relhastriggers) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("could not convert table \"%s\" to a view because it has triggers", + RelationGetRelationName(event_relation)), + errhint("In particular, the table cannot be involved in any foreign key relationships."))); + + if (event_relation->rd_rel->relhasindex) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("could not convert table \"%s\" to a view because it has indexes", + RelationGetRelationName(event_relation)))); + + if (event_relation->rd_rel->relhassubclass) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("could not convert table \"%s\" to a view because it has child tables", + RelationGetRelationName(event_relation)))); + + if (event_relation->rd_rel->relrowsecurity) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("could not convert table \"%s\" to a view because it has row security enabled", + RelationGetRelationName(event_relation)))); + + if (relation_has_policies(event_relation)) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("could not convert table \"%s\" to a view because it has row security policies", + RelationGetRelationName(event_relation)))); + + RelisBecomingView = true; + } } } else @@ -920,12 +943,24 @@ RangeVarCallbackForRenameRule(const RangeVar *rv, Oid relid, Oid oldrelid, form = (Form_pg_class) GETSTRUCT(tuple); /* only tables and views can have rules */ - if (form->relkind != RELKIND_RELATION && - form->relkind != RELKIND_VIEW && - form->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or view", rv->relname))); + Assert(RELKIND_IS_VALID((RelKind) form->relkind)); + switch ((RelKind) form->relkind) + { + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table or view", rv->relname))); + } if (!allowSystemTableMods && IsSystemClass(relid, form)) ereport(ERROR, diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index fe777c3103..11053907be 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -902,25 +902,41 @@ rewriteTargetListIU(List *targetList, * For an UPDATE on a trigger-updatable view, provide a dummy entry * whenever there is no explicit assignment. */ - if (new_tle == NULL && commandType == CMD_UPDATE && - target_relation->rd_rel->relkind == RELKIND_VIEW && - view_has_instead_trigger(target_relation, CMD_UPDATE)) + Assert(RELKIND_IS_VALID((RelKind) target_relation->rd_rel->relkind)); + switch ((RelKind) target_relation->rd_rel->relkind) { - Node *new_expr; - - new_expr = (Node *) makeVar(result_rti, - attrno, - att_tup->atttypid, - att_tup->atttypmod, - att_tup->attcollation, - 0); + case RELKIND_VIEW: + if (new_tle == NULL && commandType == CMD_UPDATE && + view_has_instead_trigger(target_relation, CMD_UPDATE)) + { + Node *new_expr; - new_tle = makeTargetEntry((Expr *) new_expr, - attrno, - pstrdup(NameStr(att_tup->attname)), - false); + new_expr = (Node *) makeVar(result_rti, + attrno, + att_tup->atttypid, + att_tup->atttypmod, + att_tup->attcollation, + 0); + + new_tle = makeTargetEntry((Expr *) new_expr, + attrno, + pstrdup(NameStr(att_tup->attname)), + false); + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; } + if (new_tle) new_tlist = lappend(new_tlist, new_tle); } @@ -1316,39 +1332,53 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, * skip this check in that case --- it isn't an auto-updatable view. */ isAutoUpdatableView = false; - if (!force_nulls && - target_relation->rd_rel->relkind == RELKIND_VIEW && - !view_has_instead_trigger(target_relation, CMD_INSERT)) + Assert(RELKIND_IS_VALID((RelKind) target_relation->rd_rel->relkind)); + switch ((RelKind) target_relation->rd_rel->relkind) { - List *locks; - bool hasUpdate; - bool found; - ListCell *l; + case RELKIND_VIEW: + if (!force_nulls && !view_has_instead_trigger(target_relation, CMD_INSERT)) + { + List *locks; + bool hasUpdate; + bool found; + ListCell *l; - /* Look for an unconditional DO INSTEAD rule */ - locks = matchLocks(CMD_INSERT, target_relation->rd_rules, - parsetree->resultRelation, parsetree, &hasUpdate); + /* Look for an unconditional DO INSTEAD rule */ + locks = matchLocks(CMD_INSERT, target_relation->rd_rules, + parsetree->resultRelation, parsetree, &hasUpdate); - found = false; - foreach(l, locks) - { - RewriteRule *rule_lock = (RewriteRule *) lfirst(l); + found = false; + foreach(l, locks) + { + RewriteRule *rule_lock = (RewriteRule *) lfirst(l); - if (rule_lock->isInstead && - rule_lock->qual == NULL) - { - found = true; - break; - } - } + if (rule_lock->isInstead && + rule_lock->qual == NULL) + { + found = true; + break; + } + } - /* - * If we didn't find an unconditional DO INSTEAD rule, assume that the - * view is auto-updatable. If it isn't, rewriteTargetView() will - * throw an error. - */ - if (!found) - isAutoUpdatableView = true; + /* + * If we didn't find an unconditional DO INSTEAD rule, assume + * that the view is auto-updatable. If it isn't, + * rewriteTargetView() will throw an error. + */ + if (!found) + isAutoUpdatableView = true; + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; } newValues = NIL; @@ -1450,55 +1480,71 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte, const char *attrname; TargetEntry *tle; - if (target_relation->rd_rel->relkind == RELKIND_RELATION || - target_relation->rd_rel->relkind == RELKIND_MATVIEW || - target_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - { - /* - * Emit CTID so that executor can find the row to update or delete. - */ - var = makeVar(parsetree->resultRelation, - SelfItemPointerAttributeNumber, - TIDOID, - -1, - InvalidOid, - 0); - - attrname = "ctid"; - } - else if (target_relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + Assert(RELKIND_IS_VALID((RelKind) target_relation->rd_rel->relkind)); + switch ((RelKind) target_relation->rd_rel->relkind) { - /* - * Let the foreign table's FDW add whatever junk TLEs it wants. - */ - FdwRoutine *fdwroutine; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + { + /* + * Emit CTID so that executor can find the row to update or + * delete. + */ + var = makeVar(parsetree->resultRelation, + SelfItemPointerAttributeNumber, + TIDOID, + -1, + InvalidOid, + 0); + + attrname = "ctid"; + } + break; + case RELKIND_FOREIGN_TABLE: + { + /* + * Let the foreign table's FDW add whatever junk TLEs it + * wants. + */ + FdwRoutine *fdwroutine; - fdwroutine = GetFdwRoutineForRelation(target_relation, false); + fdwroutine = GetFdwRoutineForRelation(target_relation, false); - if (fdwroutine->AddForeignUpdateTargets != NULL) - fdwroutine->AddForeignUpdateTargets(parsetree, target_rte, - target_relation); + if (fdwroutine->AddForeignUpdateTargets != NULL) + fdwroutine->AddForeignUpdateTargets(parsetree, target_rte, + target_relation); - /* - * If we have a row-level trigger corresponding to the operation, emit - * a whole-row Var so that executor will have the "old" row to pass to - * the trigger. Alas, this misses system columns. - */ - if (target_relation->trigdesc && - ((parsetree->commandType == CMD_UPDATE && - (target_relation->trigdesc->trig_update_after_row || - target_relation->trigdesc->trig_update_before_row)) || - (parsetree->commandType == CMD_DELETE && - (target_relation->trigdesc->trig_delete_after_row || - target_relation->trigdesc->trig_delete_before_row)))) - { - var = makeWholeRowVar(target_rte, - parsetree->resultRelation, - 0, - false); + /* + * If we have a row-level trigger corresponding to the + * operation, emit a whole-row Var so that executor will have + * the "old" row to pass to the trigger. Alas, this misses + * system columns. + */ + if (target_relation->trigdesc && + ((parsetree->commandType == CMD_UPDATE && + (target_relation->trigdesc->trig_update_after_row || + target_relation->trigdesc->trig_update_before_row)) || + (parsetree->commandType == CMD_DELETE && + (target_relation->trigdesc->trig_delete_after_row || + target_relation->trigdesc->trig_delete_before_row)))) + { + var = makeWholeRowVar(target_rte, + parsetree->resultRelation, + 0, + false); - attrname = "wholerow"; - } + attrname = "wholerow"; + } + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } if (var != NULL) @@ -1909,8 +1955,22 @@ fireRIRrules(Query *parsetree, List *activeRIRs) * In that case this test would need to be postponed till after we've * opened the rel, so that we could check its state. */ - if (rte->relkind == RELKIND_MATVIEW) - continue; + Assert(RELKIND_IS_VALID((RelKind) rte->relkind)); + switch ((RelKind) rte->relkind) + { + case RELKIND_MATVIEW: + continue; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } /* * In INSERT ... ON CONFLICT, ignore the EXCLUDED pseudo-relation; @@ -2032,10 +2092,24 @@ fireRIRrules(Query *parsetree, List *activeRIRs) ++rt_index; /* Only normal relations can have RLS policies */ - if (rte->rtekind != RTE_RELATION || - (rte->relkind != RELKIND_RELATION && - rte->relkind != RELKIND_PARTITIONED_TABLE)) + if (rte->rtekind != RTE_RELATION) continue; + Assert(RELKIND_IS_VALID((RelKind) rte->relkind)); + switch ((RelKind) rte->relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + continue; + } rel = table_open(rte->relid, NoLock); @@ -2417,6 +2491,7 @@ view_query_is_auto_updatable(Query *viewquery, bool check_cols) { RangeTblRef *rtr; RangeTblEntry *base_rte; + bool updatable; /*---------- * Check if the view is simply updatable. According to SQL-92 this means: @@ -2498,11 +2573,31 @@ view_query_is_auto_updatable(Query *viewquery, bool check_cols) return gettext_noop("Views that do not select from a single table or view are not automatically updatable."); base_rte = rt_fetch(rtr->rtindex, viewquery->rtable); - if (base_rte->rtekind != RTE_RELATION || - (base_rte->relkind != RELKIND_RELATION && - base_rte->relkind != RELKIND_FOREIGN_TABLE && - base_rte->relkind != RELKIND_VIEW && - base_rte->relkind != RELKIND_PARTITIONED_TABLE)) + + /* + * Beware that base_rte->relkind here may be NULL, which is not a valid member + * of enum RelKind. + */ + updatable = false; + Assert(base_rte->relkind == '\0' || RELKIND_IS_VALID((RelKind) base_rte->relkind)); + switch ((RelKind) base_rte->relkind) + { + case RELKIND_RELATION: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + case RELKIND_PARTITIONED_TABLE: + if (base_rte->rtekind == RTE_RELATION) + updatable = true; + /* fallthrough */ + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + break; + } + if (!updatable) return gettext_noop("Views that do not select from a single table or view are not automatically updatable."); if (base_rte->tablesample) @@ -2677,11 +2772,22 @@ relation_is_updatable(Oid reloid, } /* If the relation is a table, it is always updatable */ - if (rel->rd_rel->relkind == RELKIND_RELATION || - rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - relation_close(rel, AccessShareLock); - return ALL_EVENTS; + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + relation_close(rel, AccessShareLock); + return ALL_EVENTS; + case RELKIND_FOREIGN_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } /* Look for unconditional DO INSTEAD rules, and note supported events */ @@ -2730,84 +2836,102 @@ relation_is_updatable(Oid reloid, } } - /* If this is a foreign table, check which update events it supports */ - if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) { - FdwRoutine *fdwroutine = GetFdwRoutineForRelation(rel, false); - - if (fdwroutine->IsForeignRelUpdatable != NULL) - events |= fdwroutine->IsForeignRelUpdatable(rel); - else - { - /* Assume presence of executor functions is sufficient */ - if (fdwroutine->ExecForeignInsert != NULL) - events |= (1 << CMD_INSERT); - if (fdwroutine->ExecForeignUpdate != NULL) - events |= (1 << CMD_UPDATE); - if (fdwroutine->ExecForeignDelete != NULL) - events |= (1 << CMD_DELETE); - } + /* + * If this is a foreign table, check which update events it + * supports + */ + case RELKIND_FOREIGN_TABLE: + { + FdwRoutine *fdwroutine = GetFdwRoutineForRelation(rel, false); - relation_close(rel, AccessShareLock); - return events; - } + if (fdwroutine->IsForeignRelUpdatable != NULL) + events |= fdwroutine->IsForeignRelUpdatable(rel); + else + { + /* Assume presence of executor functions is sufficient */ + if (fdwroutine->ExecForeignInsert != NULL) + events |= (1 << CMD_INSERT); + if (fdwroutine->ExecForeignUpdate != NULL) + events |= (1 << CMD_UPDATE); + if (fdwroutine->ExecForeignDelete != NULL) + events |= (1 << CMD_DELETE); + } - /* Check if this is an automatically updatable view */ - if (rel->rd_rel->relkind == RELKIND_VIEW) - { - Query *viewquery = get_view_query(rel); + relation_close(rel, AccessShareLock); + return events; + } + break; + /* Check if this is an automatically updatable view */ + case RELKIND_VIEW: + { + Query *viewquery = get_view_query(rel); - if (view_query_is_auto_updatable(viewquery, false) == NULL) - { - Bitmapset *updatable_cols; - int auto_events; - RangeTblRef *rtr; - RangeTblEntry *base_rte; - Oid baseoid; + if (view_query_is_auto_updatable(viewquery, false) == NULL) + { + Bitmapset *updatable_cols; + int auto_events; + RangeTblRef *rtr; + RangeTblEntry *base_rte; + Oid baseoid; - /* - * Determine which of the view's columns are updatable. If there - * are none within the set of columns we are looking at, then the - * view doesn't support INSERT/UPDATE, but it may still support - * DELETE. - */ - view_cols_are_auto_updatable(viewquery, NULL, - &updatable_cols, NULL); + /* + * Determine which of the view's columns are updatable. If + * there are none within the set of columns we are looking + * at, then the view doesn't support INSERT/UPDATE, but it + * may still support DELETE. + */ + view_cols_are_auto_updatable(viewquery, NULL, + &updatable_cols, NULL); - if (include_cols != NULL) - updatable_cols = bms_int_members(updatable_cols, include_cols); + if (include_cols != NULL) + updatable_cols = bms_int_members(updatable_cols, include_cols); - if (bms_is_empty(updatable_cols)) - auto_events = (1 << CMD_DELETE); /* May support DELETE */ - else - auto_events = ALL_EVENTS; /* May support all events */ + if (bms_is_empty(updatable_cols)) + auto_events = (1 << CMD_DELETE); /* May support DELETE */ + else + auto_events = ALL_EVENTS; /* May support all events */ - /* - * The base relation must also support these update commands. - * Tables are always updatable, but for any other kind of base - * relation we must do a recursive check limited to the columns - * referenced by the locally updatable columns in this view. - */ - rtr = (RangeTblRef *) linitial(viewquery->jointree->fromlist); - base_rte = rt_fetch(rtr->rtindex, viewquery->rtable); - Assert(base_rte->rtekind == RTE_RELATION); + /* + * The base relation must also support these update + * commands. Tables are always updatable, but for any + * other kind of base relation we must do a recursive + * check limited to the columns referenced by the locally + * updatable columns in this view. + */ + rtr = (RangeTblRef *) linitial(viewquery->jointree->fromlist); + base_rte = rt_fetch(rtr->rtindex, viewquery->rtable); + Assert(base_rte->rtekind == RTE_RELATION); - if (base_rte->relkind != RELKIND_RELATION && - base_rte->relkind != RELKIND_PARTITIONED_TABLE) - { - baseoid = base_rte->relid; - outer_reloids = lappend_oid(outer_reloids, - RelationGetRelid(rel)); - include_cols = adjust_view_column_set(updatable_cols, - viewquery->targetList); - auto_events &= relation_is_updatable(baseoid, - outer_reloids, - include_triggers, - include_cols); - outer_reloids = list_delete_last(outer_reloids); + if (base_rte->relkind != RELKIND_RELATION && + base_rte->relkind != RELKIND_PARTITIONED_TABLE) + { + baseoid = base_rte->relid; + outer_reloids = lappend_oid(outer_reloids, + RelationGetRelid(rel)); + include_cols = adjust_view_column_set(updatable_cols, + viewquery->targetList); + auto_events &= relation_is_updatable(baseoid, + outer_reloids, + include_triggers, + include_cols); + outer_reloids = list_delete_last(outer_reloids); + } + events |= auto_events; + } } - events |= auto_events; - } + break; + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + break; } /* If we reach here, the relation may support some update commands */ diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index 0fe2f9ca83..34e439cd83 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -123,9 +123,22 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasSubLinks = false; /* If this is not a normal relation, just return immediately */ - if (rte->relkind != RELKIND_RELATION && - rte->relkind != RELKIND_PARTITIONED_TABLE) - return; + Assert(RELKIND_IS_VALID((RelKind) rte->relkind)); + switch ((RelKind) rte->relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + return; + } /* Switch to checkAsUser if it's set */ user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 29c920800a..2f3053389e 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -2819,7 +2819,8 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln) BlockNumber RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum) { - switch (relation->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { case RELKIND_SEQUENCE: case RELKIND_INDEX: @@ -2846,13 +2847,16 @@ RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum) return (szbytes + (BLCKSZ - 1)) / BLCKSZ; } case RELKIND_VIEW: + elog(FATAL, "RELKIND_VIEW in RelationGetNumberOfBlocksInFork"); case RELKIND_COMPOSITE_TYPE: + elog(FATAL, "RELKIND_COMPOSITE_TYPE in RelationGetNumberOfBlocksInFork"); case RELKIND_FOREIGN_TABLE: + elog(FATAL, "RELKIND_FOREIGN_TABLE in RelationGetNumberOfBlocksInFork"); case RELKIND_PARTITIONED_TABLE: - default: - Assert(false); - break; + elog(FATAL, "RELKIND_PARTITIONED_TABLE in RelationGetNumberOfBlocksInFork"); } + elog(FATAL, "Unexpected relkind %d in RelationGetNumberOfBlocksInFork", + (int) relation->rd_rel->relkind); return 0; /* keep compiler quiet */ } diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index e57fcd2538..615670ff41 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -1236,8 +1236,8 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable) /* * Set timer so we can wake up after awhile and check for a deadlock. If a * deadlock is detected, the handler sets MyProc->waitStatus = - * PROC_WAIT_STATUS_ERROR, allowing us to know that we must report failure rather - * than success. + * PROC_WAIT_STATUS_ERROR, allowing us to know that we must report failure + * rather than success. * * By delaying the check until we've waited for a bit, we can avoid * running the rather expensive deadlock-check code in most cases. @@ -1302,9 +1302,9 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable) } /* - * waitStatus could change from PROC_WAIT_STATUS_WAITING to something else - * asynchronously. Read it just once per loop to prevent surprising - * behavior (such as missing log messages). + * waitStatus could change from PROC_WAIT_STATUS_WAITING to something + * else asynchronously. Read it just once per loop to prevent + * surprising behavior (such as missing log messages). */ myWaitStatus = *((volatile ProcWaitStatus *) &MyProc->waitStatus); @@ -1504,11 +1504,12 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable) /* * Currently, the deadlock checker always kicks its own - * process, which means that we'll only see PROC_WAIT_STATUS_ERROR when - * deadlock_state == DS_HARD_DEADLOCK, and there's no need to - * print redundant messages. But for completeness and - * future-proofing, print a message if it looks like someone - * else kicked us off the lock. + * process, which means that we'll only see + * PROC_WAIT_STATUS_ERROR when deadlock_state == + * DS_HARD_DEADLOCK, and there's no need to print redundant + * messages. But for completeness and future-proofing, print + * a message if it looks like someone else kicked us off the + * lock. */ if (deadlock_state != DS_HARD_DEADLOCK) ereport(LOG, @@ -1737,9 +1738,9 @@ CheckDeadLock(void) * preserve the flexibility to kill some other transaction than the * one detecting the deadlock.) * - * RemoveFromWaitQueue sets MyProc->waitStatus to PROC_WAIT_STATUS_ERROR, so - * ProcSleep will report an error after we return from the signal - * handler. + * RemoveFromWaitQueue sets MyProc->waitStatus to + * PROC_WAIT_STATUS_ERROR, so ProcSleep will report an error after we + * return from the signal handler. */ Assert(MyProc->waitLock != NULL); RemoveFromWaitQueue(MyProc, LockTagHashCode(&(MyProc->waitLock->tag))); diff --git a/src/backend/storage/lmgr/spin.c b/src/backend/storage/lmgr/spin.c index 9f7eae9339..6d76b95c27 100644 --- a/src/backend/storage/lmgr/spin.c +++ b/src/backend/storage/lmgr/spin.c @@ -37,7 +37,7 @@ #define NUM_EMULATION_SEMAPHORES (NUM_SPINLOCK_SEMAPHORES + NUM_ATOMICS_SEMAPHORES) #else #define NUM_EMULATION_SEMAPHORES (NUM_SPINLOCK_SEMAPHORES) -#endif /* DISABLE_ATOMICS */ +#endif /* DISABLE_ATOMICS */ PGSemaphore *SpinlockSemaArray; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 9b0c376c8c..d7e93cbacc 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1436,34 +1436,65 @@ ProcessUtilitySlow(ParseState *pstate, * partitions are something we can put an index on, to * avoid building some indexes only to fail later. */ - if (stmt->relation->inh && - get_rel_relkind(relid) == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) get_rel_relkind(relid))); + switch ((RelKind) get_rel_relkind(relid)) { - ListCell *lc; - List *inheritors = NIL; - - inheritors = find_all_inheritors(relid, lockmode, NULL); - foreach(lc, inheritors) - { - char relkind = get_rel_relkind(lfirst_oid(lc)); - - if (relkind != RELKIND_RELATION && - relkind != RELKIND_MATVIEW && - relkind != RELKIND_PARTITIONED_TABLE && - relkind != RELKIND_FOREIGN_TABLE) - elog(ERROR, "unexpected relkind \"%c\" on partition \"%s\"", - relkind, stmt->relation->relname); - - if (relkind == RELKIND_FOREIGN_TABLE && - (stmt->unique || stmt->primary)) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot create unique index on partitioned table \"%s\"", - stmt->relation->relname), - errdetail("Table \"%s\" contains partitions that are foreign tables.", - stmt->relation->relname))); - } - list_free(inheritors); + case RELKIND_PARTITIONED_TABLE: + if (stmt->relation->inh) + { + ListCell *lc; + List *inheritors = NIL; + + inheritors = find_all_inheritors(relid, lockmode, NULL); + foreach(lc, inheritors) + { + char relkind = get_rel_relkind(lfirst_oid(lc)); + + /* + * Beware that relkind here may be NULL, which is not + * a valid member of enum RelKind. + */ + Assert(relkind == '\0' || + RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + continue; + case RELKIND_FOREIGN_TABLE: + if (stmt->unique || stmt->primary) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot create unique index on partitioned table \"%s\"", + stmt->relation->relname), + errdetail("Table \"%s\" contains partitions that are foreign tables.", + stmt->relation->relname))); + continue; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } + elog(ERROR, "unexpected relkind \"%c\" on partition \"%s\"", + relkind, stmt->relation->relname); + } + list_free(inheritors); + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } /* Run parse analysis ... */ diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c index 220cd8fc52..50af704c8e 100644 --- a/src/backend/utils/adt/amutils.c +++ b/src/backend/utils/adt/amutils.c @@ -175,11 +175,22 @@ indexam_property(FunctionCallInfo fcinfo, if (!HeapTupleIsValid(tuple)) PG_RETURN_NULL(); rd_rel = (Form_pg_class) GETSTRUCT(tuple); - if (rd_rel->relkind != RELKIND_INDEX && - rd_rel->relkind != RELKIND_PARTITIONED_INDEX) + Assert(RELKIND_IS_VALID((RelKind) rd_rel->relkind)); + switch ((RelKind) rd_rel->relkind) { - ReleaseSysCache(tuple); - PG_RETURN_NULL(); + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + ReleaseSysCache(tuple); + PG_RETURN_NULL(); } amoid = rd_rel->relam; natts = rd_rel->relnatts; diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c index 2320c06a9b..9fe10a2f22 100644 --- a/src/backend/utils/adt/dbsize.c +++ b/src/backend/utils/adt/dbsize.c @@ -878,7 +878,7 @@ pg_relation_filenode(PG_FUNCTION_ARGS) { if (relform->relfilenode) result = relform->relfilenode; - else /* Consult the relation mapper */ + else /* Consult the relation mapper */ result = RelationMapOidToFilenode(relid, relform->relisshared); } @@ -957,17 +957,17 @@ pg_relation_filepath(PG_FUNCTION_ARGS) rnode.dbNode = MyDatabaseId; if (relform->relfilenode) rnode.relNode = relform->relfilenode; - else /* Consult the relation mapper */ + else /* Consult the relation mapper */ rnode.relNode = RelationMapOidToFilenode(relid, relform->relisshared); } else { - /* no storage, return NULL */ - rnode.relNode = InvalidOid; - /* some compilers generate warnings without these next two lines */ - rnode.dbNode = InvalidOid; - rnode.spcNode = InvalidOid; + /* no storage, return NULL */ + rnode.relNode = InvalidOid; + /* some compilers generate warnings without these next two lines */ + rnode.dbNode = InvalidOid; + rnode.spcNode = InvalidOid; } if (!OidIsValid(rnode.relNode)) diff --git a/src/backend/utils/adt/partitionfuncs.c b/src/backend/utils/adt/partitionfuncs.c index c1120403fd..88432a68df 100644 --- a/src/backend/utils/adt/partitionfuncs.c +++ b/src/backend/utils/adt/partitionfuncs.c @@ -45,10 +45,23 @@ check_rel_can_be_partition(Oid relid) relispartition = get_rel_relispartition(relid); /* Only allow relation types that can appear in partition trees. */ - if (!relispartition && - relkind != RELKIND_PARTITIONED_TABLE && - relkind != RELKIND_PARTITIONED_INDEX) - return false; + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_PARTITIONED_TABLE: + case RELKIND_PARTITIONED_INDEX: + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + if (!relispartition) + return false; + } return true; } @@ -143,8 +156,23 @@ pg_partition_tree(PG_FUNCTION_ARGS) nulls[1] = true; /* isleaf */ - values[2] = BoolGetDatum(relkind != RELKIND_PARTITIONED_TABLE && - relkind != RELKIND_PARTITIONED_INDEX); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_PARTITIONED_TABLE: + case RELKIND_PARTITIONED_INDEX: + values[2] = BoolGetDatum(false); + break; + case RELKIND_FOREIGN_TABLE: + case RELKIND_RELATION: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_VIEW: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + values[2] = BoolGetDatum(true); + } /* level */ if (relid != rootrelid) diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c index 509a0fdffc..134b2ac817 100644 --- a/src/backend/utils/adt/tid.c +++ b/src/backend/utils/adt/tid.c @@ -381,8 +381,22 @@ currtid_byreloid(PG_FUNCTION_ARGS) aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), RelationGetRelationName(rel)); - if (rel->rd_rel->relkind == RELKIND_VIEW) - return currtid_for_view(rel, tid); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_VIEW: + return currtid_for_view(rel, tid); + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; + } if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind)) elog(ERROR, "cannot look at latest visible tid for relation \"%s.%s\"", @@ -423,8 +437,22 @@ currtid_byrelname(PG_FUNCTION_ARGS) aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), RelationGetRelationName(rel)); - if (rel->rd_rel->relkind == RELKIND_VIEW) - return currtid_for_view(rel, tid); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_VIEW: + return currtid_for_view(rel, tid); + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; + } if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind)) elog(ERROR, "cannot look at latest visible tid for relation \"%s.%s\"", diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 4c299057a6..c2711075ef 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -2475,9 +2475,9 @@ schema_get_xml_visible_tables(Oid nspid) initStringInfo(&query); appendStringInfo(&query, "SELECT oid FROM pg_catalog.pg_class" " WHERE relnamespace = %u AND relkind IN (" - CppAsString2(RELKIND_RELATION) "," - CppAsString2(RELKIND_MATVIEW) "," - CppAsString2(RELKIND_VIEW) ")" + RelKindAsString(RELKIND_RELATION) "," + RelKindAsString(RELKIND_MATVIEW) "," + RelKindAsString(RELKIND_VIEW) ")" " AND pg_catalog.has_table_privilege (oid, 'SELECT')" " ORDER BY relname;", nspid); @@ -2507,9 +2507,9 @@ database_get_xml_visible_tables(void) /* At the moment there is no order required here. */ return query_to_oid_list("SELECT oid FROM pg_catalog.pg_class" " WHERE relkind IN (" - CppAsString2(RELKIND_RELATION) "," - CppAsString2(RELKIND_MATVIEW) "," - CppAsString2(RELKIND_VIEW) ")" + RelKindAsString(RELKIND_RELATION) "," + RelKindAsString(RELKIND_MATVIEW) "," + RelKindAsString(RELKIND_VIEW) ")" " AND pg_catalog.has_table_privilege(pg_class.oid, 'SELECT')" " AND relnamespace IN (" XML_VISIBLE_SCHEMAS ");"); } diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c index acf8a44f30..2deeb14a6a 100644 --- a/src/backend/utils/cache/partcache.c +++ b/src/backend/utils/cache/partcache.c @@ -53,8 +53,22 @@ static List *generate_partition_qual(Relation rel); PartitionKey RelationGetPartitionKey(Relation rel) { - if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - return NULL; + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + return NULL; + } if (unlikely(rel->rd_partkey == NULL)) RelationBuildPartitionKey(rel); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index a2453cf1f4..b4515a1a51 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -448,7 +448,8 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple) * Look up any AM-specific parse function; fall out if relkind should not * have options. */ - switch (relation->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { case RELKIND_RELATION: case RELKIND_TOASTVALUE: @@ -461,7 +462,9 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple) case RELKIND_PARTITIONED_INDEX: amoptsfn = relation->rd_indam->amoptions; break; - default: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: return; } @@ -1182,7 +1185,8 @@ RelationBuildDesc(Oid targetRelId, bool insertIt) /* * initialize access method information */ - switch (relation->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { case RELKIND_INDEX: case RELKIND_PARTITIONED_INDEX: @@ -1747,38 +1751,51 @@ RelationInitTableAccessMethod(Relation relation) HeapTuple tuple; Form_pg_am aform; - if (relation->rd_rel->relkind == RELKIND_SEQUENCE) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { - /* - * Sequences are currently accessed like heap tables, but it doesn't - * seem prudent to show that in the catalog. So just overwrite it - * here. - */ - relation->rd_amhandler = HEAP_TABLE_AM_HANDLER_OID; - } - else if (IsCatalogRelation(relation)) - { - /* - * Avoid doing a syscache lookup for catalog tables. - */ - Assert(relation->rd_rel->relam == HEAP_TABLE_AM_OID); - relation->rd_amhandler = HEAP_TABLE_AM_HANDLER_OID; - } - else - { - /* - * Look up the table access method, save the OID of its handler - * function. - */ - Assert(relation->rd_rel->relam != InvalidOid); - tuple = SearchSysCache1(AMOID, - ObjectIdGetDatum(relation->rd_rel->relam)); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "cache lookup failed for access method %u", - relation->rd_rel->relam); - aform = (Form_pg_am) GETSTRUCT(tuple); - relation->rd_amhandler = aform->amhandler; - ReleaseSysCache(tuple); + case RELKIND_SEQUENCE: + + /* + * Sequences are currently accessed like heap tables, but it + * doesn't seem prudent to show that in the catalog. So just + * overwrite it here. + */ + relation->rd_amhandler = HEAP_TABLE_AM_HANDLER_OID; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + if (IsCatalogRelation(relation)) + { + /* + * Avoid doing a syscache lookup for catalog tables. + */ + Assert(relation->rd_rel->relam == HEAP_TABLE_AM_OID); + relation->rd_amhandler = HEAP_TABLE_AM_HANDLER_OID; + } + else + { + /* + * Look up the table access method, save the OID of its + * handler function. + */ + Assert(relation->rd_rel->relam != InvalidOid); + tuple = SearchSysCache1(AMOID, + ObjectIdGetDatum(relation->rd_rel->relam)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for access method %u", + relation->rd_rel->relam); + aform = (Form_pg_am) GETSTRUCT(tuple); + relation->rd_amhandler = aform->amhandler; + ReleaseSysCache(tuple); + } } /* @@ -2024,11 +2041,23 @@ RelationIdGetRelation(Oid relationId) * and we don't want to use the full-blown procedure because it's * a headache for indexes that reload itself depends on. */ - if (rd->rd_rel->relkind == RELKIND_INDEX || - rd->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) - RelationReloadIndexInfo(rd); - else - RelationClearRelation(rd, true); + Assert(RELKIND_IS_VALID((RelKind) rd->rd_rel->relkind)); + switch ((RelKind) rd->rd_rel->relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + RelationReloadIndexInfo(rd); + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + RelationClearRelation(rd, true); + } /* * Normally entries need to be valid here, but before the relcache @@ -2285,46 +2314,57 @@ RelationReloadNailed(Relation relation) if (!IsTransactionState() || relation->rd_refcnt <= 1) return; - if (relation->rd_rel->relkind == RELKIND_INDEX) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { - /* - * If it's a nailed-but-not-mapped index, then we need to re-read the - * pg_class row to see if its relfilenode changed. - */ - RelationReloadIndexInfo(relation); - } - else - { - /* - * Reload a non-index entry. We can't easily do so if relcaches - * aren't yet built, but that's fine because at that stage the - * attributes that need to be current (like relfrozenxid) aren't yet - * accessed. To ensure the entry will later be revalidated, we leave - * it in invalid state, but allow use (cf. RelationIdGetRelation()). - */ - if (criticalRelcachesBuilt) - { - HeapTuple pg_class_tuple; - Form_pg_class relp; + case RELKIND_INDEX: /* - * NB: Mark the entry as valid before starting to scan, to avoid - * self-recursion when re-building pg_class. + * If it's a nailed-but-not-mapped index, then we need to re-read + * the pg_class row to see if its relfilenode changed. */ - relation->rd_isvalid = true; - - pg_class_tuple = ScanPgRelation(RelationGetRelid(relation), - true, false); - relp = (Form_pg_class) GETSTRUCT(pg_class_tuple); - memcpy(relation->rd_rel, relp, CLASS_TUPLE_SIZE); - heap_freetuple(pg_class_tuple); - + RelationReloadIndexInfo(relation); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: /* - * Again mark as valid, to protect against concurrently arriving - * invalidations. + * Reload a non-index entry. We can't easily do so if relcaches + * aren't yet built, but that's fine because at that stage the + * attributes that need to be current (like relfrozenxid) aren't + * yet accessed. To ensure the entry will later be revalidated, + * we leave it in invalid state, but allow use (cf. + * RelationIdGetRelation()). */ - relation->rd_isvalid = true; - } + if (criticalRelcachesBuilt) + { + HeapTuple pg_class_tuple; + Form_pg_class relp; + + /* + * NB: Mark the entry as valid before starting to scan, to + * avoid self-recursion when re-building pg_class. + */ + relation->rd_isvalid = true; + + pg_class_tuple = ScanPgRelation(RelationGetRelid(relation), + true, false); + relp = (Form_pg_class) GETSTRUCT(pg_class_tuple); + memcpy(relation->rd_rel, relp, CLASS_TUPLE_SIZE); + heap_freetuple(pg_class_tuple); + + /* + * Again mark as valid, to protect against concurrently + * arriving invalidations. + */ + relation->rd_isvalid = true; + } } } @@ -2471,14 +2511,27 @@ RelationClearRelation(Relation relation, bool rebuild) * re-read the pg_class row to handle possible physical relocation of the * index, and we check for pg_index updates too. */ - if ((relation->rd_rel->relkind == RELKIND_INDEX || - relation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) && - relation->rd_refcnt > 0 && - relation->rd_indexcxt != NULL) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { - if (IsTransactionState()) - RelationReloadIndexInfo(relation); - return; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + if (relation->rd_refcnt > 0 && relation->rd_indexcxt != NULL) + { + if (IsTransactionState()) + RelationReloadIndexInfo(relation); + return; + } + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } /* @@ -3479,10 +3532,23 @@ RelationBuildLocalRelation(const char *relname, } /* if it's a materialized view, it's not populated initially */ - if (relkind == RELKIND_MATVIEW) - rel->rd_rel->relispopulated = false; - else - rel->rd_rel->relispopulated = true; + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_MATVIEW: + rel->rd_rel->relispopulated = false; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + rel->rd_rel->relispopulated = true; + } /* set replica identity -- system catalogs and non-tables don't have one */ if (!IsCatalogNamespace(relnamespace) && @@ -3522,11 +3588,23 @@ RelationBuildLocalRelation(const char *relname, rel->rd_rel->relam = accessmtd; - if (relkind == RELKIND_RELATION || - relkind == RELKIND_SEQUENCE || - relkind == RELKIND_TOASTVALUE || - relkind == RELKIND_MATVIEW) - RelationInitTableAccessMethod(rel); + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) + { + case RELKIND_RELATION: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + RelationInitTableAccessMethod(rel); + break; + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_VIEW: + break; + } /* * Okay to insert into the relcache hash table. @@ -3619,7 +3697,8 @@ RelationSetNewRelfilenode(Relation relation, char persistence) newrnode = relation->rd_node; newrnode.relNode = newrelfilenode; - switch (relation->rd_rel->relkind) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { case RELKIND_INDEX: case RELKIND_SEQUENCE: @@ -3631,7 +3710,6 @@ RelationSetNewRelfilenode(Relation relation, char persistence) smgrclose(srel); } break; - case RELKIND_RELATION: case RELKIND_TOASTVALUE: case RELKIND_MATVIEW: @@ -3639,12 +3717,14 @@ RelationSetNewRelfilenode(Relation relation, char persistence) persistence, &freezeXid, &minmulti); break; - - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: /* we shouldn't be called for anything else */ elog(ERROR, "relation \"%s\" does not have storage", RelationGetRelationName(relation)); - break; } /* @@ -3689,11 +3769,23 @@ RelationSetNewRelfilenode(Relation relation, char persistence) classform->relfilenode = newrelfilenode; /* relpages etc. never change for sequences */ - if (relation->rd_rel->relkind != RELKIND_SEQUENCE) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { - classform->relpages = 0; /* it's empty until further notice */ - classform->reltuples = 0; - classform->relallvisible = 0; + case RELKIND_SEQUENCE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + classform->relpages = 0; /* it's empty until further notice */ + classform->reltuples = 0; + classform->relallvisible = 0; } classform->relfrozenxid = freezeXid; classform->relminmxid = minmulti; @@ -4086,16 +4178,28 @@ RelationCacheInitializePhase3(void) } /* Reload tableam data if needed */ - if (relation->rd_tableam == NULL && - (relation->rd_rel->relkind == RELKIND_RELATION || - relation->rd_rel->relkind == RELKIND_SEQUENCE || - relation->rd_rel->relkind == RELKIND_TOASTVALUE || - relation->rd_rel->relkind == RELKIND_MATVIEW)) + Assert(RELKIND_IS_VALID((RelKind) relation->rd_rel->relkind)); + switch ((RelKind) relation->rd_rel->relkind) { - RelationInitTableAccessMethod(relation); - Assert(relation->rd_tableam != NULL); + case RELKIND_RELATION: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + if (relation->rd_tableam == NULL) + { + RelationInitTableAccessMethod(relation); + Assert(relation->rd_tableam != NULL); - restart = true; + restart = true; + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: + break; } /* Release hold on the relation */ @@ -5864,11 +5968,23 @@ load_relcache_init_file(bool shared) nailed_rels++; /* Load table AM data */ - if (rel->rd_rel->relkind == RELKIND_RELATION || - rel->rd_rel->relkind == RELKIND_SEQUENCE || - rel->rd_rel->relkind == RELKIND_TOASTVALUE || - rel->rd_rel->relkind == RELKIND_MATVIEW) - RelationInitTableAccessMethod(rel); + Assert(RELKIND_IS_VALID((RelKind) rel->rd_rel->relkind)); + switch ((RelKind) rel->rd_rel->relkind) + { + case RELKIND_RELATION: + case RELKIND_SEQUENCE: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + RelationInitTableAccessMethod(rel); + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_VIEW: + break; + } Assert(rel->rd_index == NULL); Assert(rel->rd_indextuple == NULL); diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 786672b1b6..3b0cf8a234 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -1743,12 +1743,12 @@ setup_privileges(FILE *cmdfd) " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = " CppAsString2(RELKIND_SEQUENCE) " THEN 's' " + " CASE WHEN relkind = " RelKindAsString(RELKIND_SEQUENCE) " THEN 's' " " ELSE 'r' END::\"char\"," CppAsString2(BOOTSTRAP_SUPERUSERID) "::oid))" " ) as a) " - " WHERE relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_VIEW) ", " CppAsString2(RELKIND_MATVIEW) ", " - CppAsString2(RELKIND_SEQUENCE) ")" + " WHERE relkind IN (" RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_VIEW) ", " RelKindAsString(RELKIND_MATVIEW) ", " + RelKindAsString(RELKIND_SEQUENCE) ")" " AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", "GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n\n", @@ -1765,9 +1765,9 @@ setup_privileges(FILE *cmdfd) " pg_class" " WHERE" " relacl IS NOT NULL" - " AND relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_VIEW) ", " CppAsString2(RELKIND_MATVIEW) ", " - CppAsString2(RELKIND_SEQUENCE) ");\n\n", + " AND relkind IN (" RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_VIEW) ", " RelKindAsString(RELKIND_MATVIEW) ", " + RelKindAsString(RELKIND_SEQUENCE) ");\n\n", "INSERT INTO pg_init_privs " " (objoid, classoid, objsubid, initprivs, privtype)" " SELECT" @@ -1781,9 +1781,9 @@ setup_privileges(FILE *cmdfd) " JOIN pg_attribute ON (pg_class.oid = pg_attribute.attrelid)" " WHERE" " pg_attribute.attacl IS NOT NULL" - " AND pg_class.relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_VIEW) ", " CppAsString2(RELKIND_MATVIEW) ", " - CppAsString2(RELKIND_SEQUENCE) ");\n\n", + " AND pg_class.relkind IN (" RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_VIEW) ", " RelKindAsString(RELKIND_MATVIEW) ", " + RelKindAsString(RELKIND_SEQUENCE) ");\n\n", "INSERT INTO pg_init_privs " " (objoid, classoid, objsubid, initprivs, privtype)" " SELECT" diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 08239dde4f..4aeefcf1a7 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -283,10 +283,22 @@ flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables, bool mark_parents = true; /* Some kinds never have parents */ - if (tblinfo[i].relkind == RELKIND_SEQUENCE || - tblinfo[i].relkind == RELKIND_VIEW || - tblinfo[i].relkind == RELKIND_MATVIEW) - continue; + Assert(RELKIND_IS_VALID((RelKind) tblinfo[i].relkind)); + switch ((RelKind) tblinfo[i].relkind) + { + case RELKIND_SEQUENCE: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + continue; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; + } /* * Normally, we don't bother computing anything for non-target tables, @@ -449,10 +461,22 @@ flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables) TableInfo **parents; /* Some kinds never have parents */ - if (tbinfo->relkind == RELKIND_SEQUENCE || - tbinfo->relkind == RELKIND_VIEW || - tbinfo->relkind == RELKIND_MATVIEW) - continue; + Assert(RELKIND_IS_VALID((RelKind) tbinfo->relkind)); + switch ((RelKind) tbinfo->relkind) + { + case RELKIND_SEQUENCE: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + continue; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; + } /* Don't bother computing anything for non-target tables, either */ if (!tbinfo->dobj.dump) diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c index b4f5942959..0b04578b9a 100644 --- a/src/bin/pg_dump/pg_backup_tar.c +++ b/src/bin/pg_dump/pg_backup_tar.c @@ -1191,7 +1191,7 @@ _tarPositionTo(ArchiveHandle *AH, const char *filename) /* Header doesn't match, so read to next header */ len = th->fileLen; len += tarPaddingBytesRequired(th->fileLen); - blks = len / TAR_BLOCK_SIZE; /* # of tar blocks */ + blks = len / TAR_BLOCK_SIZE; /* # of tar blocks */ for (i = 0; i < blks; i++) _tarReadRaw(AH, &header[0], TAR_BLOCK_SIZE, NULL, ctx->tarFH); diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index e758b5c50a..24011d6e7e 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -2399,17 +2399,34 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo) return; /* Skip VIEWs (no data to dump) */ - if (tbinfo->relkind == RELKIND_VIEW) - return; - /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */ - if (tbinfo->relkind == RELKIND_FOREIGN_TABLE && - (foreign_servers_include_oids.head == NULL || - !simple_oid_list_member(&foreign_servers_include_oids, - tbinfo->foreign_server))) - return; - /* Skip partitioned tables (data in partitions) */ - if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) - return; + Assert(RELKIND_IS_VALID((RelKind) tbinfo->relkind)); + switch ((RelKind) tbinfo->relkind) + { + case RELKIND_VIEW: + return; + + /* + * Skip FOREIGN TABLEs (no data to dump) unless requested + * explicitly + */ + case RELKIND_FOREIGN_TABLE: + if (foreign_servers_include_oids.head == NULL || + !simple_oid_list_member(&foreign_servers_include_oids, + tbinfo->foreign_server)) + return; + break; + /* Skip partitioned tables (data in partitions) */ + case RELKIND_PARTITIONED_TABLE: + return; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; + } /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -2424,12 +2441,25 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo) /* OK, let's dump it */ tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo)); - if (tbinfo->relkind == RELKIND_MATVIEW) - tdinfo->dobj.objType = DO_REFRESH_MATVIEW; - else if (tbinfo->relkind == RELKIND_SEQUENCE) - tdinfo->dobj.objType = DO_SEQUENCE_SET; - else - tdinfo->dobj.objType = DO_TABLE_DATA; + Assert(RELKIND_IS_VALID((RelKind) tbinfo->relkind)); + switch ((RelKind) tbinfo->relkind) + { + case RELKIND_MATVIEW: + tdinfo->dobj.objType = DO_REFRESH_MATVIEW; + break; + case RELKIND_SEQUENCE: + tdinfo->dobj.objType = DO_SEQUENCE_SET; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + tdinfo->dobj.objType = DO_TABLE_DATA; + } /* * Note: use tableoid 0 so that this object won't be mistaken for @@ -2476,14 +2506,14 @@ buildMatViewRefreshDependencies(Archive *fout) "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind " "FROM pg_depend d1 " "JOIN pg_class c1 ON c1.oid = d1.objid " - "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW) + "AND c1.relkind = " RelKindAsString(RELKIND_MATVIEW) " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid " "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass " "AND d2.objid = r1.oid " "AND d2.refobjid <> d1.objid " "JOIN pg_class c2 ON c2.oid = d2.refobjid " - "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) "," - CppAsString2(RELKIND_VIEW) ") " + "AND c2.relkind IN (" RelKindAsString(RELKIND_MATVIEW) "," + RelKindAsString(RELKIND_VIEW) ") " "WHERE d1.classid = 'pg_class'::regclass " "UNION " "SELECT w.objid, d3.refobjid, c3.relkind " @@ -2493,12 +2523,12 @@ buildMatViewRefreshDependencies(Archive *fout) "AND d3.objid = r3.oid " "AND d3.refobjid <> w.refobjid " "JOIN pg_class c3 ON c3.oid = d3.refobjid " - "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) "," - CppAsString2(RELKIND_VIEW) ") " + "AND c3.relkind IN (" RelKindAsString(RELKIND_MATVIEW) "," + RelKindAsString(RELKIND_VIEW) ") " ") " "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid " "FROM w " - "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW)); + "WHERE refrelkind = " RelKindAsString(RELKIND_MATVIEW)); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -2637,9 +2667,22 @@ guessConstraintInheritance(TableInfo *tblinfo, int numTables) TableInfo *parent; /* Sequences and views never have parents */ - if (tbinfo->relkind == RELKIND_SEQUENCE || - tbinfo->relkind == RELKIND_VIEW) - continue; + Assert(RELKIND_IS_VALID((RelKind) tbinfo->relkind)); + switch ((RelKind) tbinfo->relkind) + { + case RELKIND_SEQUENCE: + case RELKIND_VIEW: + continue; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; + } /* Don't bother computing anything for non-target tables, either */ if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) @@ -4068,9 +4111,22 @@ getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables) /* * Only regular and partitioned tables can be added to publications. */ - if (tbinfo->relkind != RELKIND_RELATION && - tbinfo->relkind != RELKIND_PARTITIONED_TABLE) - continue; + Assert(RELKIND_IS_VALID((RelKind) tbinfo->relkind)); + switch ((RelKind) tbinfo->relkind) + { + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + continue; + } /* * Ignore publication membership of tables whose definitions are not @@ -6145,7 +6201,7 @@ getTables(Archive *fout, int *numTables) buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, initracl_subquery, "c.relacl", "c.relowner", - "CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE) + "CASE WHEN c.relkind = " RelKindAsString(RELKIND_SEQUENCE) " THEN 's' ELSE 'r' END::\"char\"", dopt->binary_upgrade); @@ -6774,10 +6830,23 @@ getTables(Archive *fout, int *numTables) /* * Decide whether we want to dump this table. */ - if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE) - tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE; - else - selectDumpableTable(&tblinfo[i], fout); + Assert(RELKIND_IS_VALID((RelKind) tblinfo[i].relkind)); + switch ((RelKind) tblinfo[i].relkind) + { + case RELKIND_COMPOSITE_TYPE: + tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + selectDumpableTable(&tblinfo[i], fout); + } /* * If the table-level and all column-level ACLs for this table are @@ -6824,16 +6893,30 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && - (tblinfo[i].relkind == RELKIND_RELATION || - tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && - (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) + Assert(RELKIND_IS_VALID((RelKind) tblinfo[i].relkind)); + switch ((RelKind) tblinfo[i].relkind) { - resetPQExpBuffer(query); - appendPQExpBuffer(query, - "LOCK TABLE %s IN ACCESS SHARE MODE", - fmtQualifiedDumpable(&tblinfo[i])); - ExecuteSqlStatement(fout, query->data); + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + if (tblinfo[i].dobj.dump && + (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) + { + resetPQExpBuffer(query); + appendPQExpBuffer(query, + "LOCK TABLE %s IN ACCESS SHARE MODE", + fmtQualifiedDumpable(&tblinfo[i])); + ExecuteSqlStatement(fout, query->data); + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } /* Emit notice if join for owner failed */ @@ -15719,7 +15802,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) char *srvname = NULL; char *foreign = ""; - switch (tbinfo->relkind) + Assert(RELKIND_IS_VALID((RelKind) tbinfo->relkind)); + switch ((RelKind) tbinfo->relkind) { case RELKIND_FOREIGN_TABLE: { @@ -15758,8 +15842,18 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) case RELKIND_MATVIEW: reltypename = "MATERIALIZED VIEW"; break; - default: + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: reltypename = "TABLE"; + break; + case RELKIND_SEQUENCE: + case RELKIND_VIEW: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + Assert(false); + reltypename = "XXX"; } numParents = tbinfo->numParents; diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c index 654e2ec514..d528dba474 100644 --- a/src/bin/pg_dump/pg_dump_sort.c +++ b/src/bin/pg_dump/pg_dump_sort.c @@ -801,8 +801,28 @@ repairMatViewBoundaryMultiLoop(DumpableObject *boundaryobj, { TableInfo *nextinfo = (TableInfo *) nextobj; - if (nextinfo->relkind == RELKIND_MATVIEW) - nextinfo->postponed_def = true; + /* + * Beware that relkind here may be NULL, which is not a valid member + * of enum RelKind. + */ + Assert(nextinfo->relkind == '\0' || + RELKIND_IS_VALID((RelKind) nextinfo->relkind)); + switch ((RelKind) nextinfo->relkind) + { + case RELKIND_MATVIEW: + nextinfo->postponed_def = true; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; + } } } @@ -928,30 +948,64 @@ repairDependencyLoop(DumpableObject **loop, return; } - /* View (including matview) and its ON SELECT rule */ - if (nLoop == 2 && - loop[0]->objType == DO_TABLE && - loop[1]->objType == DO_RULE && - (((TableInfo *) loop[0])->relkind == RELKIND_VIEW || - ((TableInfo *) loop[0])->relkind == RELKIND_MATVIEW) && - ((RuleInfo *) loop[1])->ev_type == '1' && - ((RuleInfo *) loop[1])->is_instead && - ((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0]) + /* + * Beware that relkind here is not guaranteed to be a valid member + * of enum RelKind. + */ + switch ((RelKind) ((TableInfo *) loop[0])->relkind) { - repairViewRuleLoop(loop[0], loop[1]); - return; + case RELKIND_VIEW: + case RELKIND_MATVIEW: + /* View (including matview) and its ON SELECT rule */ + if (nLoop == 2 && + loop[0]->objType == DO_TABLE && + loop[1]->objType == DO_RULE && + ((RuleInfo *) loop[1])->ev_type == '1' && + ((RuleInfo *) loop[1])->is_instead && + ((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0]) + { + repairViewRuleLoop(loop[0], loop[1]); + return; + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; } - if (nLoop == 2 && - loop[1]->objType == DO_TABLE && - loop[0]->objType == DO_RULE && - (((TableInfo *) loop[1])->relkind == RELKIND_VIEW || - ((TableInfo *) loop[1])->relkind == RELKIND_MATVIEW) && - ((RuleInfo *) loop[0])->ev_type == '1' && - ((RuleInfo *) loop[0])->is_instead && - ((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1]) + /* + * Beware that relkind here is not guaranteed to be a valid member + * of enum RelKind. + */ + switch ((RelKind) ((TableInfo *) loop[1])->relkind) { - repairViewRuleLoop(loop[1], loop[0]); - return; + case RELKIND_VIEW: + case RELKIND_MATVIEW: + if (nLoop == 2 && + loop[1]->objType == DO_TABLE && + loop[0]->objType == DO_RULE && + ((RuleInfo *) loop[0])->ev_type == '1' && + ((RuleInfo *) loop[0])->is_instead && + ((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1]) + { + repairViewRuleLoop(loop[1], loop[0]); + return; + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; } /* Indirect loop involving view (but not matview) and ON SELECT rule */ @@ -959,20 +1013,38 @@ repairDependencyLoop(DumpableObject **loop, { for (i = 0; i < nLoop; i++) { - if (loop[i]->objType == DO_TABLE && - ((TableInfo *) loop[i])->relkind == RELKIND_VIEW) + /* + * Beware that relkind here is not guaranteed to be a valid member + * of enum RelKind. + */ + switch ((RelKind) ((TableInfo *) loop[i])->relkind) { - for (j = 0; j < nLoop; j++) - { - if (loop[j]->objType == DO_RULE && - ((RuleInfo *) loop[j])->ev_type == '1' && - ((RuleInfo *) loop[j])->is_instead && - ((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i]) + case RELKIND_VIEW: + if (loop[i]->objType == DO_TABLE) { - repairViewRuleMultiLoop(loop[i], loop[j]); - return; + for (j = 0; j < nLoop; j++) + { + if (loop[j]->objType == DO_RULE && + ((RuleInfo *) loop[j])->ev_type == '1' && + ((RuleInfo *) loop[j])->is_instead && + ((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i]) + { + repairViewRuleMultiLoop(loop[i], loop[j]); + return; + } + } } - } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; } } } @@ -982,20 +1054,38 @@ repairDependencyLoop(DumpableObject **loop, { for (i = 0; i < nLoop; i++) { - if (loop[i]->objType == DO_TABLE && - ((TableInfo *) loop[i])->relkind == RELKIND_MATVIEW) + /* + * Beware that relkind here is not guaranteed to be a valid member + * of enum RelKind. + */ + switch ((RelKind) ((TableInfo *) loop[i])->relkind) { - for (j = 0; j < nLoop; j++) - { - if (loop[j]->objType == DO_PRE_DATA_BOUNDARY) + case RELKIND_MATVIEW: + if (loop[i]->objType == DO_TABLE) { - DumpableObject *nextobj; - - nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0]; - repairMatViewBoundaryMultiLoop(loop[j], nextobj); - return; + for (j = 0; j < nLoop; j++) + { + if (loop[j]->objType == DO_PRE_DATA_BOUNDARY) + { + DumpableObject *nextobj; + + nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0]; + repairMatViewBoundaryMultiLoop(loop[j], nextobj); + return; + } + } } - } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } } } diff --git a/src/bin/pg_upgrade/info.c b/src/bin/pg_upgrade/info.c index 7e524ea192..d67da370c5 100644 --- a/src/bin/pg_upgrade/info.c +++ b/src/bin/pg_upgrade/info.c @@ -446,8 +446,8 @@ get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo) " SELECT c.oid, 0::oid, 0::oid " " FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n " " ON c.relnamespace = n.oid " - " WHERE relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_MATVIEW) ") AND " + " WHERE relkind IN (" RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_MATVIEW) ") AND " /* exclude possible orphaned temp tables */ " ((n.nspname !~ '^pg_temp_' AND " " n.nspname !~ '^pg_toast_temp_' AND " diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c index 70194eb096..996bf8bf70 100644 --- a/src/bin/pg_upgrade/pg_upgrade.c +++ b/src/bin/pg_upgrade/pg_upgrade.c @@ -643,9 +643,9 @@ set_frozenxids(bool minmxid_only) "SET relfrozenxid = '%u' " /* only heap, materialized view, and TOAST are vacuumed */ "WHERE relkind IN (" - CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_MATVIEW) ", " - CppAsString2(RELKIND_TOASTVALUE) ")", + RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_MATVIEW) ", " + RelKindAsString(RELKIND_TOASTVALUE) ")", old_cluster.controldata.chkpnt_nxtxid)); /* set pg_class.relminmxid */ @@ -654,9 +654,9 @@ set_frozenxids(bool minmxid_only) "SET relminmxid = '%u' " /* only heap, materialized view, and TOAST are vacuumed */ "WHERE relkind IN (" - CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_MATVIEW) ", " - CppAsString2(RELKIND_TOASTVALUE) ")", + RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_MATVIEW) ", " + RelKindAsString(RELKIND_TOASTVALUE) ")", old_cluster.controldata.chkpnt_nxtmulti)); PQfinish(conn); diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c index 4e5d27f76e..5fb158c093 100644 --- a/src/bin/pg_upgrade/version.c +++ b/src/bin/pg_upgrade/version.c @@ -176,9 +176,9 @@ check_for_data_type_usage(ClusterInfo *cluster, const char *typename, " NOT a.attisdropped AND " " a.atttypid IN (SELECT oid FROM oids) AND " " c.relkind IN (" - CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_MATVIEW) ", " - CppAsString2(RELKIND_INDEX) ") AND " + RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_MATVIEW) ", " + RelKindAsString(RELKIND_INDEX) ") AND " " c.relnamespace = n.oid AND " /* exclude possible orphaned temp tables */ " n.nspname !~ '^pg_temp_' AND " diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 9902a4a2ba..e1d3bca4a0 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -4942,21 +4942,28 @@ get_create_object_cmd(EditableObjectType obj_type, Oid oid, * does not, so editing a matview definition in this way * is impossible. */ - switch (relkind[0]) + Assert(RELKIND_IS_VALID((RelKind) relkind[0])); + switch ((RelKind) relkind[0]) { -#ifdef NOT_USED + case RELKIND_VIEW: + appendPQExpBufferStr(buf, "CREATE OR REPLACE VIEW "); + break; case RELKIND_MATVIEW: +#ifdef NOT_USED appendPQExpBufferStr(buf, "CREATE OR REPLACE MATERIALIZED VIEW "); break; #endif - case RELKIND_VIEW: - appendPQExpBufferStr(buf, "CREATE OR REPLACE VIEW "); - break; - default: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: pg_log_error("\"%s.%s\" is not a view", nspname, relname); result = false; - break; } appendPQExpBuffer(buf, "%s.", fmtId(nspname)); appendPQExpBufferStr(buf, fmtId(relname)); diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 3b870c3b17..28fcd388f6 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -734,7 +734,7 @@ describeTypes(const char *pattern, bool verbose, bool showSystem) * composite types */ appendPQExpBufferStr(&buf, "WHERE (t.typrelid = 0 "); - appendPQExpBufferStr(&buf, "OR (SELECT c.relkind = " CppAsString2(RELKIND_COMPOSITE_TYPE) + appendPQExpBufferStr(&buf, "OR (SELECT c.relkind = " RelKindAsString(RELKIND_COMPOSITE_TYPE) " FROM pg_catalog.pg_class c " "WHERE c.oid = t.typrelid))\n"); @@ -943,12 +943,12 @@ permissionsList(const char *pattern) "SELECT n.nspname as \"%s\",\n" " c.relname as \"%s\",\n" " CASE c.relkind" - " WHEN " CppAsString2(RELKIND_RELATION) " THEN '%s'" - " WHEN " CppAsString2(RELKIND_VIEW) " THEN '%s'" - " WHEN " CppAsString2(RELKIND_MATVIEW) " THEN '%s'" - " WHEN " CppAsString2(RELKIND_SEQUENCE) " THEN '%s'" - " WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'" - " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_RELATION) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_VIEW) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_MATVIEW) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_SEQUENCE) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_FOREIGN_TABLE) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_PARTITIONED_TABLE) " THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -1040,12 +1040,12 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" "WHERE c.relkind IN (" - CppAsString2(RELKIND_RELATION) "," - CppAsString2(RELKIND_VIEW) "," - CppAsString2(RELKIND_MATVIEW) "," - CppAsString2(RELKIND_SEQUENCE) "," - CppAsString2(RELKIND_FOREIGN_TABLE) "," - CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"); + RelKindAsString(RELKIND_RELATION) "," + RelKindAsString(RELKIND_VIEW) "," + RelKindAsString(RELKIND_MATVIEW) "," + RelKindAsString(RELKIND_SEQUENCE) "," + RelKindAsString(RELKIND_FOREIGN_TABLE) "," + RelKindAsString(RELKIND_PARTITIONED_TABLE) ")\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1692,134 +1692,147 @@ describeOneTableDetails(const char *schemaname, /* * If it's a sequence, deal with it here separately. */ - if (tableinfo.relkind == RELKIND_SEQUENCE) + Assert(RELKIND_IS_VALID((RelKind) tableinfo.relkind)); + switch ((RelKind) tableinfo.relkind) { - PGresult *result = NULL; - printQueryOpt myopt = pset.popt; - char *footers[2] = {NULL, NULL}; - - if (pset.sversion >= 100000) - { - printfPQExpBuffer(&buf, - "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n" - " seqstart AS \"%s\",\n" - " seqmin AS \"%s\",\n" - " seqmax AS \"%s\",\n" - " seqincrement AS \"%s\",\n" - " CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n" - " seqcache AS \"%s\"\n", - gettext_noop("Type"), - gettext_noop("Start"), - gettext_noop("Minimum"), - gettext_noop("Maximum"), - gettext_noop("Increment"), - gettext_noop("yes"), - gettext_noop("no"), - gettext_noop("Cycles?"), - gettext_noop("Cache")); - appendPQExpBuffer(&buf, - "FROM pg_catalog.pg_sequence\n" - "WHERE seqrelid = '%s';", - oid); - } - else - { - printfPQExpBuffer(&buf, - "SELECT 'bigint' AS \"%s\",\n" - " start_value AS \"%s\",\n" - " min_value AS \"%s\",\n" - " max_value AS \"%s\",\n" - " increment_by AS \"%s\",\n" - " CASE WHEN is_cycled THEN '%s' ELSE '%s' END AS \"%s\",\n" - " cache_value AS \"%s\"\n", - gettext_noop("Type"), - gettext_noop("Start"), - gettext_noop("Minimum"), - gettext_noop("Maximum"), - gettext_noop("Increment"), - gettext_noop("yes"), - gettext_noop("no"), - gettext_noop("Cycles?"), - gettext_noop("Cache")); - appendPQExpBuffer(&buf, "FROM %s", fmtId(schemaname)); - /* must be separate because fmtId isn't reentrant */ - appendPQExpBuffer(&buf, ".%s;", fmtId(relationname)); - } + case RELKIND_SEQUENCE: + { + PGresult *result = NULL; + printQueryOpt myopt = pset.popt; + char *footers[2] = {NULL, NULL}; - res = PSQLexec(buf.data); - if (!res) - goto error_return; + if (pset.sversion >= 100000) + { + printfPQExpBuffer(&buf, + "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n" + " seqstart AS \"%s\",\n" + " seqmin AS \"%s\",\n" + " seqmax AS \"%s\",\n" + " seqincrement AS \"%s\",\n" + " CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n" + " seqcache AS \"%s\"\n", + gettext_noop("Type"), + gettext_noop("Start"), + gettext_noop("Minimum"), + gettext_noop("Maximum"), + gettext_noop("Increment"), + gettext_noop("yes"), + gettext_noop("no"), + gettext_noop("Cycles?"), + gettext_noop("Cache")); + appendPQExpBuffer(&buf, + "FROM pg_catalog.pg_sequence\n" + "WHERE seqrelid = '%s';", + oid); + } + else + { + printfPQExpBuffer(&buf, + "SELECT 'bigint' AS \"%s\",\n" + " start_value AS \"%s\",\n" + " min_value AS \"%s\",\n" + " max_value AS \"%s\",\n" + " increment_by AS \"%s\",\n" + " CASE WHEN is_cycled THEN '%s' ELSE '%s' END AS \"%s\",\n" + " cache_value AS \"%s\"\n", + gettext_noop("Type"), + gettext_noop("Start"), + gettext_noop("Minimum"), + gettext_noop("Maximum"), + gettext_noop("Increment"), + gettext_noop("yes"), + gettext_noop("no"), + gettext_noop("Cycles?"), + gettext_noop("Cache")); + appendPQExpBuffer(&buf, "FROM %s", fmtId(schemaname)); + /* must be separate because fmtId isn't reentrant */ + appendPQExpBuffer(&buf, ".%s;", fmtId(relationname)); + } - /* Footer information about a sequence */ + res = PSQLexec(buf.data); + if (!res) + goto error_return; + + /* Footer information about a sequence */ + + /* Get the column that owns this sequence */ + printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||" + "\n pg_catalog.quote_ident(relname) || '.' ||" + "\n pg_catalog.quote_ident(attname)," + "\n d.deptype" + "\nFROM pg_catalog.pg_class c" + "\nINNER JOIN pg_catalog.pg_depend d ON c.oid=d.refobjid" + "\nINNER JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace" + "\nINNER JOIN pg_catalog.pg_attribute a ON (" + "\n a.attrelid=c.oid AND" + "\n a.attnum=d.refobjsubid)" + "\nWHERE d.classid='pg_catalog.pg_class'::pg_catalog.regclass" + "\n AND d.refclassid='pg_catalog.pg_class'::pg_catalog.regclass" + "\n AND d.objid='%s'" + "\n AND d.deptype IN ('a', 'i')", + oid); - /* Get the column that owns this sequence */ - printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||" - "\n pg_catalog.quote_ident(relname) || '.' ||" - "\n pg_catalog.quote_ident(attname)," - "\n d.deptype" - "\nFROM pg_catalog.pg_class c" - "\nINNER JOIN pg_catalog.pg_depend d ON c.oid=d.refobjid" - "\nINNER JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace" - "\nINNER JOIN pg_catalog.pg_attribute a ON (" - "\n a.attrelid=c.oid AND" - "\n a.attnum=d.refobjsubid)" - "\nWHERE d.classid='pg_catalog.pg_class'::pg_catalog.regclass" - "\n AND d.refclassid='pg_catalog.pg_class'::pg_catalog.regclass" - "\n AND d.objid='%s'" - "\n AND d.deptype IN ('a', 'i')", - oid); + result = PSQLexec(buf.data); - result = PSQLexec(buf.data); + /* + * If we get no rows back, don't show anything (obviously). We + * should never get more than one row back, but if we do, just + * ignore it and don't print anything. + */ + if (!result) + goto error_return; + else if (PQntuples(result) == 1) + { + switch (PQgetvalue(result, 0, 1)[0]) + { + case 'a': + footers[0] = psprintf(_("Owned by: %s"), + PQgetvalue(result, 0, 0)); + break; + case 'i': + footers[0] = psprintf(_("Sequence for identity column: %s"), + PQgetvalue(result, 0, 0)); + break; + } + } + PQclear(result); - /* - * If we get no rows back, don't show anything (obviously). We should - * never get more than one row back, but if we do, just ignore it and - * don't print anything. - */ - if (!result) - goto error_return; - else if (PQntuples(result) == 1) - { - switch (PQgetvalue(result, 0, 1)[0]) - { - case 'a': - footers[0] = psprintf(_("Owned by: %s"), - PQgetvalue(result, 0, 0)); - break; - case 'i': - footers[0] = psprintf(_("Sequence for identity column: %s"), - PQgetvalue(result, 0, 0)); - break; - } - } - PQclear(result); + printfPQExpBuffer(&title, _("Sequence \"%s.%s\""), + schemaname, relationname); - printfPQExpBuffer(&title, _("Sequence \"%s.%s\""), - schemaname, relationname); + myopt.footers = footers; + myopt.topt.default_footer = false; + myopt.title = title.data; + myopt.translate_header = true; - myopt.footers = footers; - myopt.topt.default_footer = false; - myopt.title = title.data; - myopt.translate_header = true; + printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (footers[0]) + free(footers[0]); - if (footers[0]) - free(footers[0]); + retval = true; + goto error_return; /* not an error, just return early */ + } + break; - retval = true; - goto error_return; /* not an error, just return early */ + /* + * Identify whether we should print collation, nullable, default + * vals + */ + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_PARTITIONED_TABLE: + show_column_details = true; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + break; } - /* Identify whether we should print collation, nullable, default vals */ - if (tableinfo.relkind == RELKIND_RELATION || - tableinfo.relkind == RELKIND_VIEW || - tableinfo.relkind == RELKIND_MATVIEW || - tableinfo.relkind == RELKIND_FOREIGN_TABLE || - tableinfo.relkind == RELKIND_COMPOSITE_TYPE || - tableinfo.relkind == RELKIND_PARTITIONED_TABLE) - show_column_details = true; - /* * Get per-column info * @@ -1861,58 +1874,88 @@ describeOneTableDetails(const char *schemaname, appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attgenerated"); attgenerated_col = cols++; } - if (tableinfo.relkind == RELKIND_INDEX || - tableinfo.relkind == RELKIND_PARTITIONED_INDEX) - { - if (pset.sversion >= 110000) - { - appendPQExpBuffer(&buf, ",\n CASE WHEN a.attnum <= (SELECT i.indnkeyatts FROM pg_catalog.pg_index i WHERE i.indexrelid = '%s') THEN '%s' ELSE '%s' END AS is_key", - oid, - gettext_noop("yes"), - gettext_noop("no")); - isindexkey_col = cols++; - } - appendPQExpBufferStr(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef"); - indexdef_col = cols++; - } - /* FDW options for foreign table column, only for 9.2 or later */ - if (tableinfo.relkind == RELKIND_FOREIGN_TABLE && pset.sversion >= 90200) + Assert(RELKIND_IS_VALID((RelKind) tableinfo.relkind)); + switch ((RelKind) tableinfo.relkind) { - appendPQExpBufferStr(&buf, ",\n CASE WHEN attfdwoptions IS NULL THEN '' ELSE " - " '(' || 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)), ', ') || ')' END AS attfdwoptions"); - fdwopts_col = cols++; + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + if (pset.sversion >= 110000) + { + appendPQExpBuffer(&buf, ",\n CASE WHEN a.attnum <= (SELECT i.indnkeyatts FROM pg_catalog.pg_index i WHERE i.indexrelid = '%s') THEN '%s' ELSE '%s' END AS is_key", + oid, + gettext_noop("yes"), + gettext_noop("no")); + isindexkey_col = cols++; + } + appendPQExpBufferStr(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef"); + indexdef_col = cols++; + break; + case RELKIND_FOREIGN_TABLE: + /* FDW options for foreign table column, only for 9.2 or later */ + if (pset.sversion >= 90200) + { + appendPQExpBufferStr(&buf, ",\n CASE WHEN attfdwoptions IS NULL THEN '' ELSE " + " '(' || 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)), ', ') || ')' END AS attfdwoptions"); + fdwopts_col = cols++; + } + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_MATVIEW: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } + if (verbose) { appendPQExpBufferStr(&buf, ",\n a.attstorage"); attstorage_col = cols++; /* stats target, if relevant to relkind */ - if (tableinfo.relkind == RELKIND_RELATION || - tableinfo.relkind == RELKIND_INDEX || - tableinfo.relkind == RELKIND_PARTITIONED_INDEX || - tableinfo.relkind == RELKIND_MATVIEW || - tableinfo.relkind == RELKIND_FOREIGN_TABLE || - tableinfo.relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) tableinfo.relkind)); + switch ((RelKind) tableinfo.relkind) { - appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget"); - attstattarget_col = cols++; + case RELKIND_RELATION: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget"); + attstattarget_col = cols++; + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_TOASTVALUE: + case RELKIND_VIEW: + break; } /* * In 9.0+, we have column comments for: relations, views, composite * types, and foreign tables (cf. CommentObject() in comment.c). */ - if (tableinfo.relkind == RELKIND_RELATION || - tableinfo.relkind == RELKIND_VIEW || - tableinfo.relkind == RELKIND_MATVIEW || - tableinfo.relkind == RELKIND_FOREIGN_TABLE || - tableinfo.relkind == RELKIND_COMPOSITE_TYPE || - tableinfo.relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) tableinfo.relkind)); + switch ((RelKind) tableinfo.relkind) { - appendPQExpBufferStr(&buf, ",\n pg_catalog.col_description(a.attrelid, a.attnum)"); - attdescr_col = cols++; + case RELKIND_RELATION: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_PARTITIONED_TABLE: + appendPQExpBufferStr(&buf, ",\n pg_catalog.col_description(a.attrelid, a.attnum)"); + attdescr_col = cols++; + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + break; } } @@ -1926,7 +1969,8 @@ describeOneTableDetails(const char *schemaname, numrows = PQntuples(res); /* Make title */ - switch (tableinfo.relkind) + Assert(RELKIND_IS_VALID((RelKind) tableinfo.relkind)); + switch ((RelKind) tableinfo.relkind) { case RELKIND_RELATION: if (tableinfo.relpersistence == 'u') @@ -1964,11 +2008,6 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Partitioned index \"%s.%s\""), schemaname, relationname); break; - case 's': - /* not used as of 8.2, but keep it for backwards compatibility */ - printfPQExpBuffer(&title, _("Special relation \"%s.%s\""), - schemaname, relationname); - break; case RELKIND_TOASTVALUE: printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""), schemaname, relationname); @@ -1989,11 +2028,20 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), schemaname, relationname); break; - default: - /* untranslated unknown relkind */ - printfPQExpBuffer(&title, "?%c? \"%s.%s\"", - tableinfo.relkind, schemaname, relationname); - break; + case RELKIND_SEQUENCE: + /* + * 's' is not used as of 8.2, but we keep it for backwards + * compatibility. We cannot handle 's' as a case value without + * adding it to enum RelKind, but that would trigger warnings and + * errors elsewhere. Handle it here, instead. + */ + if ((RelKind) tableinfo.relkind == 's') + printfPQExpBuffer(&title, _("Special relation \"%s.%s\""), + schemaname, relationname); + else + /* untranslated unknown relkind */ + printfPQExpBuffer(&title, "?%c? \"%s.%s\"", + tableinfo.relkind, schemaname, relationname); } /* Fill headers[] with the names of the columns we will output */ @@ -2150,742 +2198,803 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } - if (tableinfo.relkind == RELKIND_PARTITIONED_TABLE) + Assert(RELKIND_IS_VALID((RelKind) tableinfo.relkind)); + switch ((RelKind) tableinfo.relkind) { - /* Footer information for a partitioned table (partitioning parent) */ - PGresult *result; - - printfPQExpBuffer(&buf, - "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", - oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; + case RELKIND_PARTITIONED_TABLE: + { + /* + * Footer information for a partitioned table (partitioning + * parent) + */ + PGresult *result; - if (PQntuples(result) == 1) - { - char *partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result) + goto error_return; - printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); - printTableAddFooter(&cont, tmpbuf.data); - } - PQclear(result); - } + if (PQntuples(result) == 1) + { + char *partkeydef = PQgetvalue(result, 0, 0); - if (tableinfo.relkind == RELKIND_TOASTVALUE) - { - /* For a TOAST table, print name of owning table */ - PGresult *result; + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + } + PQclear(result); + } + break; - printfPQExpBuffer(&buf, - "SELECT n.nspname, c.relname\n" - "FROM pg_catalog.pg_class c" - " JOIN pg_catalog.pg_namespace n" - " ON n.oid = c.relnamespace\n" - "WHERE reltoastrelid = '%s';", oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; + case RELKIND_TOASTVALUE: + { + /* For a TOAST table, print name of owning table */ + PGresult *result; - if (PQntuples(result) == 1) - { - char *schemaname = PQgetvalue(result, 0, 0); - char *relname = PQgetvalue(result, 0, 1); + printfPQExpBuffer(&buf, + "SELECT n.nspname, c.relname\n" + "FROM pg_catalog.pg_class c" + " JOIN pg_catalog.pg_namespace n" + " ON n.oid = c.relnamespace\n" + "WHERE reltoastrelid = '%s';", oid); + result = PSQLexec(buf.data); + if (!result) + goto error_return; + + if (PQntuples(result) == 1) + { + char *schemaname = PQgetvalue(result, 0, 0); + char *relname = PQgetvalue(result, 0, 1); - printfPQExpBuffer(&tmpbuf, _("Owning table: \"%s.%s\""), - schemaname, relname); - printTableAddFooter(&cont, tmpbuf.data); - } - PQclear(result); + printfPQExpBuffer(&tmpbuf, _("Owning table: \"%s.%s\""), + schemaname, relname); + printTableAddFooter(&cont, tmpbuf.data); + } + PQclear(result); + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_MATVIEW: + case RELKIND_RELATION: + case RELKIND_VIEW: + break; } - if (tableinfo.relkind == RELKIND_INDEX || - tableinfo.relkind == RELKIND_PARTITIONED_INDEX) + Assert(RELKIND_IS_VALID((RelKind) tableinfo.relkind)); + switch ((RelKind) tableinfo.relkind) { - /* Footer information about an index */ - PGresult *result; - - printfPQExpBuffer(&buf, - "SELECT i.indisunique, i.indisprimary, i.indisclustered, "); - if (pset.sversion >= 80200) - appendPQExpBufferStr(&buf, "i.indisvalid,\n"); - else - appendPQExpBufferStr(&buf, "true AS indisvalid,\n"); - if (pset.sversion >= 90000) - appendPQExpBufferStr(&buf, - " (NOT i.indimmediate) AND " - "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint " - "WHERE conrelid = i.indrelid AND " - "conindid = i.indexrelid AND " - "contype IN ('p','u','x') AND " - "condeferrable) AS condeferrable,\n" - " (NOT i.indimmediate) AND " - "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint " - "WHERE conrelid = i.indrelid AND " - "conindid = i.indexrelid AND " - "contype IN ('p','u','x') AND " - "condeferred) AS condeferred,\n"); - else - appendPQExpBufferStr(&buf, - " false AS condeferrable, false AS condeferred,\n"); + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + { + /* Footer information about an index */ + PGresult *result; - if (pset.sversion >= 90400) - appendPQExpBufferStr(&buf, "i.indisreplident,\n"); - else - appendPQExpBufferStr(&buf, "false AS indisreplident,\n"); + printfPQExpBuffer(&buf, + "SELECT i.indisunique, i.indisprimary, i.indisclustered, "); + if (pset.sversion >= 80200) + appendPQExpBufferStr(&buf, "i.indisvalid,\n"); + else + appendPQExpBufferStr(&buf, "true AS indisvalid,\n"); + if (pset.sversion >= 90000) + appendPQExpBufferStr(&buf, + " (NOT i.indimmediate) AND " + "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint " + "WHERE conrelid = i.indrelid AND " + "conindid = i.indexrelid AND " + "contype IN ('p','u','x') AND " + "condeferrable) AS condeferrable,\n" + " (NOT i.indimmediate) AND " + "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint " + "WHERE conrelid = i.indrelid AND " + "conindid = i.indexrelid AND " + "contype IN ('p','u','x') AND " + "condeferred) AS condeferred,\n"); + else + appendPQExpBufferStr(&buf, + " false AS condeferrable, false AS condeferred,\n"); - appendPQExpBuffer(&buf, " a.amname, c2.relname, " - "pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n" - "FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n" - "WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n" - "AND i.indrelid = c2.oid;", - oid); + if (pset.sversion >= 90400) + appendPQExpBufferStr(&buf, "i.indisreplident,\n"); + else + appendPQExpBufferStr(&buf, "false AS indisreplident,\n"); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else if (PQntuples(result) != 1) - { - PQclear(result); - goto error_return; - } - else - { - char *indisunique = PQgetvalue(result, 0, 0); - char *indisprimary = PQgetvalue(result, 0, 1); - char *indisclustered = PQgetvalue(result, 0, 2); - char *indisvalid = PQgetvalue(result, 0, 3); - char *deferrable = PQgetvalue(result, 0, 4); - char *deferred = PQgetvalue(result, 0, 5); - char *indisreplident = PQgetvalue(result, 0, 6); - char *indamname = PQgetvalue(result, 0, 7); - char *indtable = PQgetvalue(result, 0, 8); - char *indpred = PQgetvalue(result, 0, 9); - - if (strcmp(indisprimary, "t") == 0) - printfPQExpBuffer(&tmpbuf, _("primary key, ")); - else if (strcmp(indisunique, "t") == 0) - printfPQExpBuffer(&tmpbuf, _("unique, ")); - else - resetPQExpBuffer(&tmpbuf); - appendPQExpBuffer(&tmpbuf, "%s, ", indamname); + appendPQExpBuffer(&buf, " a.amname, c2.relname, " + "pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n" + "FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n" + "WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n" + "AND i.indrelid = c2.oid;", + oid); - /* we assume here that index and table are in same schema */ - appendPQExpBuffer(&tmpbuf, _("for table \"%s.%s\""), - schemaname, indtable); + result = PSQLexec(buf.data); + if (!result) + goto error_return; + else if (PQntuples(result) != 1) + { + PQclear(result); + goto error_return; + } + else + { + char *indisunique = PQgetvalue(result, 0, 0); + char *indisprimary = PQgetvalue(result, 0, 1); + char *indisclustered = PQgetvalue(result, 0, 2); + char *indisvalid = PQgetvalue(result, 0, 3); + char *deferrable = PQgetvalue(result, 0, 4); + char *deferred = PQgetvalue(result, 0, 5); + char *indisreplident = PQgetvalue(result, 0, 6); + char *indamname = PQgetvalue(result, 0, 7); + char *indtable = PQgetvalue(result, 0, 8); + char *indpred = PQgetvalue(result, 0, 9); + + if (strcmp(indisprimary, "t") == 0) + printfPQExpBuffer(&tmpbuf, _("primary key, ")); + else if (strcmp(indisunique, "t") == 0) + printfPQExpBuffer(&tmpbuf, _("unique, ")); + else + resetPQExpBuffer(&tmpbuf); + appendPQExpBuffer(&tmpbuf, "%s, ", indamname); - if (strlen(indpred)) - appendPQExpBuffer(&tmpbuf, _(", predicate (%s)"), indpred); + /* we assume here that index and table are in same schema */ + appendPQExpBuffer(&tmpbuf, _("for table \"%s.%s\""), + schemaname, indtable); - if (strcmp(indisclustered, "t") == 0) - appendPQExpBufferStr(&tmpbuf, _(", clustered")); + if (strlen(indpred)) + appendPQExpBuffer(&tmpbuf, _(", predicate (%s)"), indpred); - if (strcmp(indisvalid, "t") != 0) - appendPQExpBufferStr(&tmpbuf, _(", invalid")); + if (strcmp(indisclustered, "t") == 0) + appendPQExpBufferStr(&tmpbuf, _(", clustered")); - if (strcmp(deferrable, "t") == 0) - appendPQExpBufferStr(&tmpbuf, _(", deferrable")); + if (strcmp(indisvalid, "t") != 0) + appendPQExpBufferStr(&tmpbuf, _(", invalid")); - if (strcmp(deferred, "t") == 0) - appendPQExpBufferStr(&tmpbuf, _(", initially deferred")); + if (strcmp(deferrable, "t") == 0) + appendPQExpBufferStr(&tmpbuf, _(", deferrable")); - if (strcmp(indisreplident, "t") == 0) - appendPQExpBufferStr(&tmpbuf, _(", replica identity")); + if (strcmp(deferred, "t") == 0) + appendPQExpBufferStr(&tmpbuf, _(", initially deferred")); - printTableAddFooter(&cont, tmpbuf.data); + if (strcmp(indisreplident, "t") == 0) + appendPQExpBufferStr(&tmpbuf, _(", replica identity")); - /* - * If it's a partitioned index, we'll print the tablespace below - */ - if (tableinfo.relkind == RELKIND_INDEX) - add_tablespace_footer(&cont, tableinfo.relkind, - tableinfo.tablespace, true); - } + printTableAddFooter(&cont, tmpbuf.data); - PQclear(result); - } - /* If you add relkinds here, see also "Finish printing..." stanza below */ - else if (tableinfo.relkind == RELKIND_RELATION || - tableinfo.relkind == RELKIND_MATVIEW || - tableinfo.relkind == RELKIND_FOREIGN_TABLE || - tableinfo.relkind == RELKIND_PARTITIONED_TABLE || - tableinfo.relkind == RELKIND_PARTITIONED_INDEX || - tableinfo.relkind == RELKIND_TOASTVALUE) - { - /* Footer information about a table */ - PGresult *result = NULL; - int tuples = 0; + /* + * If it's a partitioned index, we'll print the tablespace + * below + */ + if (tableinfo.relkind == RELKIND_INDEX) + add_tablespace_footer(&cont, tableinfo.relkind, + tableinfo.tablespace, true); + } - /* print indexes */ - if (tableinfo.hasindex) - { - printfPQExpBuffer(&buf, - "SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, "); - if (pset.sversion >= 80200) - appendPQExpBufferStr(&buf, "i.indisvalid, "); - else - appendPQExpBufferStr(&buf, "true as indisvalid, "); - appendPQExpBufferStr(&buf, "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n "); - if (pset.sversion >= 90000) - appendPQExpBufferStr(&buf, - "pg_catalog.pg_get_constraintdef(con.oid, true), " - "contype, condeferrable, condeferred"); - else - appendPQExpBufferStr(&buf, - "null AS constraintdef, null AS contype, " - "false AS condeferrable, false AS condeferred"); - if (pset.sversion >= 90400) - appendPQExpBufferStr(&buf, ", i.indisreplident"); - else - appendPQExpBufferStr(&buf, ", false AS indisreplident"); - if (pset.sversion >= 80000) - appendPQExpBufferStr(&buf, ", c2.reltablespace"); - appendPQExpBufferStr(&buf, - "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"); - if (pset.sversion >= 90000) - appendPQExpBufferStr(&buf, - " LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n"); - appendPQExpBuffer(&buf, - "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n" - "ORDER BY i.indisprimary DESC, c2.relname;", - oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); + PQclear(result); + } + break; - if (tuples > 0) + /* + * If you add relkinds here, see also "Finish printing..." stanza + * below + */ + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_TOASTVALUE: { - printTableAddFooter(&cont, _("Indexes:")); - for (i = 0; i < tuples; i++) - { - /* untranslated index name */ - printfPQExpBuffer(&buf, " \"%s\"", - PQgetvalue(result, i, 0)); + /* Footer information about a table */ + PGresult *result = NULL; + int tuples = 0; - /* If exclusion constraint, print the constraintdef */ - if (strcmp(PQgetvalue(result, i, 7), "x") == 0) - { - appendPQExpBuffer(&buf, " %s", - PQgetvalue(result, i, 6)); - } + /* print indexes */ + if (tableinfo.hasindex) + { + printfPQExpBuffer(&buf, + "SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, "); + if (pset.sversion >= 80200) + appendPQExpBufferStr(&buf, "i.indisvalid, "); else - { - const char *indexdef; - const char *usingpos; + appendPQExpBufferStr(&buf, "true as indisvalid, "); + appendPQExpBufferStr(&buf, "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n "); + if (pset.sversion >= 90000) + appendPQExpBufferStr(&buf, + "pg_catalog.pg_get_constraintdef(con.oid, true), " + "contype, condeferrable, condeferred"); + else + appendPQExpBufferStr(&buf, + "null AS constraintdef, null AS contype, " + "false AS condeferrable, false AS condeferred"); + if (pset.sversion >= 90400) + appendPQExpBufferStr(&buf, ", i.indisreplident"); + else + appendPQExpBufferStr(&buf, ", false AS indisreplident"); + if (pset.sversion >= 80000) + appendPQExpBufferStr(&buf, ", c2.reltablespace"); + appendPQExpBufferStr(&buf, + "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"); + if (pset.sversion >= 90000) + appendPQExpBufferStr(&buf, + " LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n"); + appendPQExpBuffer(&buf, + "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n" + "ORDER BY i.indisprimary DESC, c2.relname;", + oid); + result = PSQLexec(buf.data); + if (!result) + goto error_return; + else + tuples = PQntuples(result); - /* Label as primary key or unique (but not both) */ - if (strcmp(PQgetvalue(result, i, 1), "t") == 0) - appendPQExpBufferStr(&buf, " PRIMARY KEY,"); - else if (strcmp(PQgetvalue(result, i, 2), "t") == 0) + if (tuples > 0) + { + printTableAddFooter(&cont, _("Indexes:")); + for (i = 0; i < tuples; i++) { - if (strcmp(PQgetvalue(result, i, 7), "u") == 0) - appendPQExpBufferStr(&buf, " UNIQUE CONSTRAINT,"); + /* untranslated index name */ + printfPQExpBuffer(&buf, " \"%s\"", + PQgetvalue(result, i, 0)); + + /* + * If exclusion constraint, print the + * constraintdef + */ + if (strcmp(PQgetvalue(result, i, 7), "x") == 0) + { + appendPQExpBuffer(&buf, " %s", + PQgetvalue(result, i, 6)); + } else - appendPQExpBufferStr(&buf, " UNIQUE,"); + { + const char *indexdef; + const char *usingpos; + + /* + * Label as primary key or unique (but not + * both) + */ + if (strcmp(PQgetvalue(result, i, 1), "t") == 0) + appendPQExpBufferStr(&buf, " PRIMARY KEY,"); + else if (strcmp(PQgetvalue(result, i, 2), "t") == 0) + { + if (strcmp(PQgetvalue(result, i, 7), "u") == 0) + appendPQExpBufferStr(&buf, " UNIQUE CONSTRAINT,"); + else + appendPQExpBufferStr(&buf, " UNIQUE,"); + } + + /* Everything after "USING" is echoed verbatim */ + indexdef = PQgetvalue(result, i, 5); + usingpos = strstr(indexdef, " USING "); + if (usingpos) + indexdef = usingpos + 7; + appendPQExpBuffer(&buf, " %s", indexdef); + + /* Need these for deferrable PK/UNIQUE indexes */ + if (strcmp(PQgetvalue(result, i, 8), "t") == 0) + appendPQExpBufferStr(&buf, " DEFERRABLE"); + + if (strcmp(PQgetvalue(result, i, 9), "t") == 0) + appendPQExpBufferStr(&buf, " INITIALLY DEFERRED"); + } + + /* Add these for all cases */ + if (strcmp(PQgetvalue(result, i, 3), "t") == 0) + appendPQExpBufferStr(&buf, " CLUSTER"); + + if (strcmp(PQgetvalue(result, i, 4), "t") != 0) + appendPQExpBufferStr(&buf, " INVALID"); + + if (strcmp(PQgetvalue(result, i, 10), "t") == 0) + appendPQExpBufferStr(&buf, " REPLICA IDENTITY"); + + printTableAddFooter(&cont, buf.data); + + /* Print tablespace of the index on the same line */ + if (pset.sversion >= 80000) + add_tablespace_footer(&cont, RELKIND_INDEX, + atooid(PQgetvalue(result, i, 11)), + false); } + } + PQclear(result); + } - /* Everything after "USING" is echoed verbatim */ - indexdef = PQgetvalue(result, i, 5); - usingpos = strstr(indexdef, " USING "); - if (usingpos) - indexdef = usingpos + 7; - appendPQExpBuffer(&buf, " %s", indexdef); + /* print table (and column) check constraints */ + if (tableinfo.checks) + { + printfPQExpBuffer(&buf, + "SELECT r.conname, " + "pg_catalog.pg_get_constraintdef(r.oid, true)\n" + "FROM pg_catalog.pg_constraint r\n" + "WHERE r.conrelid = '%s' AND r.contype = 'c'\n" + "ORDER BY 1;", + oid); + result = PSQLexec(buf.data); + if (!result) + goto error_return; + else + tuples = PQntuples(result); - /* Need these for deferrable PK/UNIQUE indexes */ - if (strcmp(PQgetvalue(result, i, 8), "t") == 0) - appendPQExpBufferStr(&buf, " DEFERRABLE"); + if (tuples > 0) + { + printTableAddFooter(&cont, _("Check constraints:")); + for (i = 0; i < tuples; i++) + { + /* untranslated constraint name and def */ + printfPQExpBuffer(&buf, " \"%s\" %s", + PQgetvalue(result, i, 0), + PQgetvalue(result, i, 1)); - if (strcmp(PQgetvalue(result, i, 9), "t") == 0) - appendPQExpBufferStr(&buf, " INITIALLY DEFERRED"); + printTableAddFooter(&cont, buf.data); + } } + PQclear(result); + } - /* Add these for all cases */ - if (strcmp(PQgetvalue(result, i, 3), "t") == 0) - appendPQExpBufferStr(&buf, " CLUSTER"); + /* + * Print foreign-key constraints (there are none if no + * triggers, except if the table is partitioned, in which case + * the triggers appear in the partitions) + */ + if (tableinfo.hastriggers || + tableinfo.relkind == RELKIND_PARTITIONED_TABLE) + { + if (pset.sversion >= 120000 && + (tableinfo.ispartition || tableinfo.relkind == RELKIND_PARTITIONED_TABLE)) + { + /* + * Put the constraints defined in this table first, + * followed by the constraints defined in ancestor + * partitioned tables. + */ + printfPQExpBuffer(&buf, + "SELECT conrelid = '%s'::pg_catalog.regclass AS sametable,\n" + " conname,\n" + " pg_catalog.pg_get_constraintdef(oid, true) AS condef,\n" + " conrelid::pg_catalog.regclass AS ontable\n" + " FROM pg_catalog.pg_constraint,\n" + " pg_catalog.pg_partition_ancestors('%s')\n" + " WHERE conrelid = relid AND contype = 'f' AND conparentid = 0\n" + "ORDER BY sametable DESC, conname;", + oid, oid); + } + else + { + printfPQExpBuffer(&buf, + "SELECT true as sametable, conname,\n" + " pg_catalog.pg_get_constraintdef(r.oid, true) as condef,\n" + " conrelid::pg_catalog.regclass AS ontable\n" + "FROM pg_catalog.pg_constraint r\n" + "WHERE r.conrelid = '%s' AND r.contype = 'f'\n", + oid); + + if (pset.sversion >= 120000) + appendPQExpBufferStr(&buf, " AND conparentid = 0\n"); + appendPQExpBufferStr(&buf, "ORDER BY conname"); + } - if (strcmp(PQgetvalue(result, i, 4), "t") != 0) - appendPQExpBufferStr(&buf, " INVALID"); + result = PSQLexec(buf.data); + if (!result) + goto error_return; + else + tuples = PQntuples(result); - if (strcmp(PQgetvalue(result, i, 10), "t") == 0) - appendPQExpBufferStr(&buf, " REPLICA IDENTITY"); + if (tuples > 0) + { + int i_sametable = PQfnumber(result, "sametable"), + i_conname = PQfnumber(result, "conname"), + i_condef = PQfnumber(result, "condef"), + i_ontable = PQfnumber(result, "ontable"); - printTableAddFooter(&cont, buf.data); + printTableAddFooter(&cont, _("Foreign-key constraints:")); + for (i = 0; i < tuples; i++) + { + /* + * Print untranslated constraint name and + * definition. Use a "TABLE tab" prefix when the + * constraint is defined in a parent partitioned + * table. + */ + if (strcmp(PQgetvalue(result, i, i_sametable), "f") == 0) + printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s", + PQgetvalue(result, i, i_ontable), + PQgetvalue(result, i, i_conname), + PQgetvalue(result, i, i_condef)); + else + printfPQExpBuffer(&buf, " \"%s\" %s", + PQgetvalue(result, i, i_conname), + PQgetvalue(result, i, i_condef)); - /* Print tablespace of the index on the same line */ - if (pset.sversion >= 80000) - add_tablespace_footer(&cont, RELKIND_INDEX, - atooid(PQgetvalue(result, i, 11)), - false); + printTableAddFooter(&cont, buf.data); + } + } + PQclear(result); } - } - PQclear(result); - } - /* print table (and column) check constraints */ - if (tableinfo.checks) - { - printfPQExpBuffer(&buf, - "SELECT r.conname, " - "pg_catalog.pg_get_constraintdef(r.oid, true)\n" - "FROM pg_catalog.pg_constraint r\n" - "WHERE r.conrelid = '%s' AND r.contype = 'c'\n" - "ORDER BY 1;", - oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); - - if (tuples > 0) - { - printTableAddFooter(&cont, _("Check constraints:")); - for (i = 0; i < tuples; i++) + /* print incoming foreign-key references */ + if (tableinfo.hastriggers || + tableinfo.relkind == RELKIND_PARTITIONED_TABLE) { - /* untranslated constraint name and def */ - printfPQExpBuffer(&buf, " \"%s\" %s", - PQgetvalue(result, i, 0), - PQgetvalue(result, i, 1)); - - printTableAddFooter(&cont, buf.data); - } - } - PQclear(result); - } + if (pset.sversion >= 120000) + { + printfPQExpBuffer(&buf, + "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n" + " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n" + " FROM pg_catalog.pg_constraint c\n" + " WHERE confrelid IN (SELECT pg_catalog.pg_partition_ancestors('%s')\n" + " UNION ALL VALUES ('%s'::pg_catalog.regclass))\n" + " AND contype = 'f' AND conparentid = 0\n" + "ORDER BY conname;", + oid, oid); + } + else + { + printfPQExpBuffer(&buf, + "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n" + " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n" + " FROM pg_catalog.pg_constraint\n" + " WHERE confrelid = %s AND contype = 'f'\n" + "ORDER BY conname;", + oid); + } - /* - * Print foreign-key constraints (there are none if no triggers, - * except if the table is partitioned, in which case the triggers - * appear in the partitions) - */ - if (tableinfo.hastriggers || - tableinfo.relkind == RELKIND_PARTITIONED_TABLE) - { - if (pset.sversion >= 120000 && - (tableinfo.ispartition || tableinfo.relkind == RELKIND_PARTITIONED_TABLE)) - { - /* - * Put the constraints defined in this table first, followed - * by the constraints defined in ancestor partitioned tables. - */ - printfPQExpBuffer(&buf, - "SELECT conrelid = '%s'::pg_catalog.regclass AS sametable,\n" - " conname,\n" - " pg_catalog.pg_get_constraintdef(oid, true) AS condef,\n" - " conrelid::pg_catalog.regclass AS ontable\n" - " FROM pg_catalog.pg_constraint,\n" - " pg_catalog.pg_partition_ancestors('%s')\n" - " WHERE conrelid = relid AND contype = 'f' AND conparentid = 0\n" - "ORDER BY sametable DESC, conname;", - oid, oid); - } - else - { - printfPQExpBuffer(&buf, - "SELECT true as sametable, conname,\n" - " pg_catalog.pg_get_constraintdef(r.oid, true) as condef,\n" - " conrelid::pg_catalog.regclass AS ontable\n" - "FROM pg_catalog.pg_constraint r\n" - "WHERE r.conrelid = '%s' AND r.contype = 'f'\n", - oid); + result = PSQLexec(buf.data); + if (!result) + goto error_return; + else + tuples = PQntuples(result); - if (pset.sversion >= 120000) - appendPQExpBufferStr(&buf, " AND conparentid = 0\n"); - appendPQExpBufferStr(&buf, "ORDER BY conname"); - } + if (tuples > 0) + { + int i_conname = PQfnumber(result, "conname"), + i_ontable = PQfnumber(result, "ontable"), + i_condef = PQfnumber(result, "condef"); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); + printTableAddFooter(&cont, _("Referenced by:")); + for (i = 0; i < tuples; i++) + { + printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s", + PQgetvalue(result, i, i_ontable), + PQgetvalue(result, i, i_conname), + PQgetvalue(result, i, i_condef)); - if (tuples > 0) - { - int i_sametable = PQfnumber(result, "sametable"), - i_conname = PQfnumber(result, "conname"), - i_condef = PQfnumber(result, "condef"), - i_ontable = PQfnumber(result, "ontable"); + printTableAddFooter(&cont, buf.data); + } + } + PQclear(result); + } - printTableAddFooter(&cont, _("Foreign-key constraints:")); - for (i = 0; i < tuples; i++) + /* print any row-level policies */ + if (pset.sversion >= 90500) { - /* - * Print untranslated constraint name and definition. Use - * a "TABLE tab" prefix when the constraint is defined in - * a parent partitioned table. - */ - if (strcmp(PQgetvalue(result, i, i_sametable), "f") == 0) - printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s", - PQgetvalue(result, i, i_ontable), - PQgetvalue(result, i, i_conname), - PQgetvalue(result, i, i_condef)); + printfPQExpBuffer(&buf, "SELECT pol.polname,"); + if (pset.sversion >= 100000) + appendPQExpBufferStr(&buf, + " pol.polpermissive,\n"); else - printfPQExpBuffer(&buf, " \"%s\" %s", - PQgetvalue(result, i, i_conname), - PQgetvalue(result, i, i_condef)); - - printTableAddFooter(&cont, buf.data); - } - } - PQclear(result); - } - - /* print incoming foreign-key references */ - if (tableinfo.hastriggers || - tableinfo.relkind == RELKIND_PARTITIONED_TABLE) - { - if (pset.sversion >= 120000) - { - printfPQExpBuffer(&buf, - "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n" - " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n" - " FROM pg_catalog.pg_constraint c\n" - " WHERE confrelid IN (SELECT pg_catalog.pg_partition_ancestors('%s')\n" - " UNION ALL VALUES ('%s'::pg_catalog.regclass))\n" - " AND contype = 'f' AND conparentid = 0\n" - "ORDER BY conname;", - oid, oid); - } - else - { - printfPQExpBuffer(&buf, - "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n" - " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n" - " FROM pg_catalog.pg_constraint\n" - " WHERE confrelid = %s AND contype = 'f'\n" - "ORDER BY conname;", - oid); - } - - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); - - if (tuples > 0) - { - int i_conname = PQfnumber(result, "conname"), - i_ontable = PQfnumber(result, "ontable"), - i_condef = PQfnumber(result, "condef"); + appendPQExpBufferStr(&buf, + " 't' as polpermissive,\n"); + appendPQExpBuffer(&buf, + " CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n" + " pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n" + " pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n" + " CASE pol.polcmd\n" + " WHEN 'r' THEN 'SELECT'\n" + " WHEN 'a' THEN 'INSERT'\n" + " WHEN 'w' THEN 'UPDATE'\n" + " WHEN 'd' THEN 'DELETE'\n" + " END AS cmd\n" + "FROM pg_catalog.pg_policy pol\n" + "WHERE pol.polrelid = '%s' ORDER BY 1;", + oid); + + result = PSQLexec(buf.data); + if (!result) + goto error_return; + else + tuples = PQntuples(result); - printTableAddFooter(&cont, _("Referenced by:")); - for (i = 0; i < tuples; i++) - { - printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s", - PQgetvalue(result, i, i_ontable), - PQgetvalue(result, i, i_conname), - PQgetvalue(result, i, i_condef)); + /* + * Handle cases where RLS is enabled and there are + * policies, or there aren't policies, or RLS isn't + * enabled but there are policies + */ + if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0) + printTableAddFooter(&cont, _("Policies:")); - printTableAddFooter(&cont, buf.data); - } - } - PQclear(result); - } + if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0) + printTableAddFooter(&cont, _("Policies (forced row security enabled):")); - /* print any row-level policies */ - if (pset.sversion >= 90500) - { - printfPQExpBuffer(&buf, "SELECT pol.polname,"); - if (pset.sversion >= 100000) - appendPQExpBufferStr(&buf, - " pol.polpermissive,\n"); - else - appendPQExpBufferStr(&buf, - " 't' as polpermissive,\n"); - appendPQExpBuffer(&buf, - " CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n" - " pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n" - " pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n" - " CASE pol.polcmd\n" - " WHEN 'r' THEN 'SELECT'\n" - " WHEN 'a' THEN 'INSERT'\n" - " WHEN 'w' THEN 'UPDATE'\n" - " WHEN 'd' THEN 'DELETE'\n" - " END AS cmd\n" - "FROM pg_catalog.pg_policy pol\n" - "WHERE pol.polrelid = '%s' ORDER BY 1;", - oid); + if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0) + printTableAddFooter(&cont, _("Policies (row security enabled): (none)")); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); + if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0) + printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)")); - /* - * Handle cases where RLS is enabled and there are policies, or - * there aren't policies, or RLS isn't enabled but there are - * policies - */ - if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0) - printTableAddFooter(&cont, _("Policies:")); + if (!tableinfo.rowsecurity && tuples > 0) + printTableAddFooter(&cont, _("Policies (row security disabled):")); - if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0) - printTableAddFooter(&cont, _("Policies (forced row security enabled):")); + /* Might be an empty set - that's ok */ + for (i = 0; i < tuples; i++) + { + printfPQExpBuffer(&buf, " POLICY \"%s\"", + PQgetvalue(result, i, 0)); - if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0) - printTableAddFooter(&cont, _("Policies (row security enabled): (none)")); + if (*(PQgetvalue(result, i, 1)) == 'f') + appendPQExpBufferStr(&buf, " AS RESTRICTIVE"); - if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0) - printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)")); + if (!PQgetisnull(result, i, 5)) + appendPQExpBuffer(&buf, " FOR %s", + PQgetvalue(result, i, 5)); - if (!tableinfo.rowsecurity && tuples > 0) - printTableAddFooter(&cont, _("Policies (row security disabled):")); + if (!PQgetisnull(result, i, 2)) + { + appendPQExpBuffer(&buf, "\n TO %s", + PQgetvalue(result, i, 2)); + } - /* Might be an empty set - that's ok */ - for (i = 0; i < tuples; i++) - { - printfPQExpBuffer(&buf, " POLICY \"%s\"", - PQgetvalue(result, i, 0)); + if (!PQgetisnull(result, i, 3)) + appendPQExpBuffer(&buf, "\n USING (%s)", + PQgetvalue(result, i, 3)); - if (*(PQgetvalue(result, i, 1)) == 'f') - appendPQExpBufferStr(&buf, " AS RESTRICTIVE"); + if (!PQgetisnull(result, i, 4)) + appendPQExpBuffer(&buf, "\n WITH CHECK (%s)", + PQgetvalue(result, i, 4)); - if (!PQgetisnull(result, i, 5)) - appendPQExpBuffer(&buf, " FOR %s", - PQgetvalue(result, i, 5)); + printTableAddFooter(&cont, buf.data); - if (!PQgetisnull(result, i, 2)) - { - appendPQExpBuffer(&buf, "\n TO %s", - PQgetvalue(result, i, 2)); + } + PQclear(result); } - if (!PQgetisnull(result, i, 3)) - appendPQExpBuffer(&buf, "\n USING (%s)", - PQgetvalue(result, i, 3)); - - if (!PQgetisnull(result, i, 4)) - appendPQExpBuffer(&buf, "\n WITH CHECK (%s)", - PQgetvalue(result, i, 4)); + /* print any extended statistics */ + if (pset.sversion >= 100000) + { + printfPQExpBuffer(&buf, + "SELECT oid, " + "stxrelid::pg_catalog.regclass, " + "stxnamespace::pg_catalog.regnamespace AS nsp, " + "stxname,\n" + " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')\n" + " FROM pg_catalog.unnest(stxkeys) s(attnum)\n" + " JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND\n" + " a.attnum = s.attnum AND NOT attisdropped)) AS columns,\n" + " 'd' = any(stxkind) AS ndist_enabled,\n" + " 'f' = any(stxkind) AS deps_enabled,\n" + " 'm' = any(stxkind) AS mcv_enabled\n" + "FROM pg_catalog.pg_statistic_ext stat " + "WHERE stxrelid = '%s'\n" + "ORDER BY 1;", + oid); + + result = PSQLexec(buf.data); + if (!result) + goto error_return; + else + tuples = PQntuples(result); - printTableAddFooter(&cont, buf.data); + if (tuples > 0) + { + printTableAddFooter(&cont, _("Statistics objects:")); - } - PQclear(result); - } + for (i = 0; i < tuples; i++) + { + bool gotone = false; - /* print any extended statistics */ - if (pset.sversion >= 100000) - { - printfPQExpBuffer(&buf, - "SELECT oid, " - "stxrelid::pg_catalog.regclass, " - "stxnamespace::pg_catalog.regnamespace AS nsp, " - "stxname,\n" - " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')\n" - " FROM pg_catalog.unnest(stxkeys) s(attnum)\n" - " JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND\n" - " a.attnum = s.attnum AND NOT attisdropped)) AS columns,\n" - " 'd' = any(stxkind) AS ndist_enabled,\n" - " 'f' = any(stxkind) AS deps_enabled,\n" - " 'm' = any(stxkind) AS mcv_enabled\n" - "FROM pg_catalog.pg_statistic_ext stat " - "WHERE stxrelid = '%s'\n" - "ORDER BY 1;", - oid); + printfPQExpBuffer(&buf, " "); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); + /* + * statistics object name (qualified with + * namespace) + */ + appendPQExpBuffer(&buf, "\"%s\".\"%s\" (", + PQgetvalue(result, i, 2), + PQgetvalue(result, i, 3)); - if (tuples > 0) - { - printTableAddFooter(&cont, _("Statistics objects:")); + /* options */ + if (strcmp(PQgetvalue(result, i, 5), "t") == 0) + { + appendPQExpBufferStr(&buf, "ndistinct"); + gotone = true; + } - for (i = 0; i < tuples; i++) - { - bool gotone = false; + if (strcmp(PQgetvalue(result, i, 6), "t") == 0) + { + appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : ""); + gotone = true; + } - printfPQExpBuffer(&buf, " "); + if (strcmp(PQgetvalue(result, i, 7), "t") == 0) + { + appendPQExpBuffer(&buf, "%smcv", gotone ? ", " : ""); + } - /* statistics object name (qualified with namespace) */ - appendPQExpBuffer(&buf, "\"%s\".\"%s\" (", - PQgetvalue(result, i, 2), - PQgetvalue(result, i, 3)); + appendPQExpBuffer(&buf, ") ON %s FROM %s", + PQgetvalue(result, i, 4), + PQgetvalue(result, i, 1)); - /* options */ - if (strcmp(PQgetvalue(result, i, 5), "t") == 0) - { - appendPQExpBufferStr(&buf, "ndistinct"); - gotone = true; + printTableAddFooter(&cont, buf.data); + } } + PQclear(result); + } - if (strcmp(PQgetvalue(result, i, 6), "t") == 0) + /* print rules */ + if (tableinfo.hasrules && tableinfo.relkind != RELKIND_MATVIEW) + { + if (pset.sversion >= 80300) { - appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : ""); - gotone = true; + printfPQExpBuffer(&buf, + "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), " + "ev_enabled\n" + "FROM pg_catalog.pg_rewrite r\n" + "WHERE r.ev_class = '%s' ORDER BY 1;", + oid); } - - if (strcmp(PQgetvalue(result, i, 7), "t") == 0) + else { - appendPQExpBuffer(&buf, "%smcv", gotone ? ", " : ""); + printfPQExpBuffer(&buf, + "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), " + "'O' AS ev_enabled\n" + "FROM pg_catalog.pg_rewrite r\n" + "WHERE r.ev_class = '%s' ORDER BY 1;", + oid); } + result = PSQLexec(buf.data); + if (!result) + goto error_return; + else + tuples = PQntuples(result); - appendPQExpBuffer(&buf, ") ON %s FROM %s", - PQgetvalue(result, i, 4), - PQgetvalue(result, i, 1)); - - printTableAddFooter(&cont, buf.data); - } - } - PQclear(result); - } - - /* print rules */ - if (tableinfo.hasrules && tableinfo.relkind != RELKIND_MATVIEW) - { - if (pset.sversion >= 80300) - { - printfPQExpBuffer(&buf, - "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), " - "ev_enabled\n" - "FROM pg_catalog.pg_rewrite r\n" - "WHERE r.ev_class = '%s' ORDER BY 1;", - oid); - } - else - { - printfPQExpBuffer(&buf, - "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), " - "'O' AS ev_enabled\n" - "FROM pg_catalog.pg_rewrite r\n" - "WHERE r.ev_class = '%s' ORDER BY 1;", - oid); - } - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); - - if (tuples > 0) - { - bool have_heading; - int category; - - for (category = 0; category < 4; category++) - { - have_heading = false; - - for (i = 0; i < tuples; i++) + if (tuples > 0) { - const char *ruledef; - bool list_rule = false; + bool have_heading; + int category; - switch (category) + for (category = 0; category < 4; category++) { - case 0: - if (*PQgetvalue(result, i, 2) == 'O') - list_rule = true; - break; - case 1: - if (*PQgetvalue(result, i, 2) == 'D') - list_rule = true; - break; - case 2: - if (*PQgetvalue(result, i, 2) == 'A') - list_rule = true; - break; - case 3: - if (*PQgetvalue(result, i, 2) == 'R') - list_rule = true; - break; - } - if (!list_rule) - continue; + have_heading = false; - if (!have_heading) - { - switch (category) + for (i = 0; i < tuples; i++) { - case 0: - printfPQExpBuffer(&buf, _("Rules:")); - break; - case 1: - printfPQExpBuffer(&buf, _("Disabled rules:")); - break; - case 2: - printfPQExpBuffer(&buf, _("Rules firing always:")); - break; - case 3: - printfPQExpBuffer(&buf, _("Rules firing on replica only:")); - break; + const char *ruledef; + bool list_rule = false; + + switch (category) + { + case 0: + if (*PQgetvalue(result, i, 2) == 'O') + list_rule = true; + break; + case 1: + if (*PQgetvalue(result, i, 2) == 'D') + list_rule = true; + break; + case 2: + if (*PQgetvalue(result, i, 2) == 'A') + list_rule = true; + break; + case 3: + if (*PQgetvalue(result, i, 2) == 'R') + list_rule = true; + break; + } + if (!list_rule) + continue; + + if (!have_heading) + { + switch (category) + { + case 0: + printfPQExpBuffer(&buf, _("Rules:")); + break; + case 1: + printfPQExpBuffer(&buf, _("Disabled rules:")); + break; + case 2: + printfPQExpBuffer(&buf, _("Rules firing always:")); + break; + case 3: + printfPQExpBuffer(&buf, _("Rules firing on replica only:")); + break; + } + printTableAddFooter(&cont, buf.data); + have_heading = true; + } + + /* + * Everything after "CREATE RULE" is echoed + * verbatim + */ + ruledef = PQgetvalue(result, i, 1); + ruledef += 12; + printfPQExpBuffer(&buf, " %s", ruledef); + printTableAddFooter(&cont, buf.data); } - printTableAddFooter(&cont, buf.data); - have_heading = true; } - - /* Everything after "CREATE RULE" is echoed verbatim */ - ruledef = PQgetvalue(result, i, 1); - ruledef += 12; - printfPQExpBuffer(&buf, " %s", ruledef); - printTableAddFooter(&cont, buf.data); } + PQclear(result); } - } - PQclear(result); - } - - /* print any publications */ - if (pset.sversion >= 100000) - { - printfPQExpBuffer(&buf, - "SELECT pubname\n" - "FROM pg_catalog.pg_publication p\n" - "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n" - "WHERE pr.prrelid = '%s'\n" - "UNION ALL\n" - "SELECT pubname\n" - "FROM pg_catalog.pg_publication p\n" - "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n" - "ORDER BY 1;", - oid, oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); + /* print any publications */ + if (pset.sversion >= 100000) + { + printfPQExpBuffer(&buf, + "SELECT pubname\n" + "FROM pg_catalog.pg_publication p\n" + "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n" + "WHERE pr.prrelid = '%s'\n" + "UNION ALL\n" + "SELECT pubname\n" + "FROM pg_catalog.pg_publication p\n" + "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n" + "ORDER BY 1;", + oid, oid); + + result = PSQLexec(buf.data); + if (!result) + goto error_return; + else + tuples = PQntuples(result); - if (tuples > 0) - printTableAddFooter(&cont, _("Publications:")); + if (tuples > 0) + printTableAddFooter(&cont, _("Publications:")); - /* Might be an empty set - that's ok */ - for (i = 0; i < tuples; i++) - { - printfPQExpBuffer(&buf, " \"%s\"", - PQgetvalue(result, i, 0)); + /* Might be an empty set - that's ok */ + for (i = 0; i < tuples; i++) + { + printfPQExpBuffer(&buf, " \"%s\"", + PQgetvalue(result, i, 0)); - printTableAddFooter(&cont, buf.data); + printTableAddFooter(&cont, buf.data); + } + PQclear(result); + } } - PQclear(result); - } + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_VIEW: + break; } /* Get view_def if table is a view or materialized view */ - if ((tableinfo.relkind == RELKIND_VIEW || - tableinfo.relkind == RELKIND_MATVIEW) && verbose) + Assert(RELKIND_IS_VALID((RelKind) tableinfo.relkind)); + switch ((RelKind) tableinfo.relkind) { - PGresult *result; + case RELKIND_VIEW: + case RELKIND_MATVIEW: + if (verbose) + { + PGresult *result; - printfPQExpBuffer(&buf, - "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);", - oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);", + oid); + result = PSQLexec(buf.data); + if (!result) + goto error_return; - if (PQntuples(result) > 0) - view_def = pg_strdup(PQgetvalue(result, 0, 0)); + if (PQntuples(result) > 0) + view_def = pg_strdup(PQgetvalue(result, 0, 0)); - PQclear(result); + PQclear(result); + } + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + break; } if (view_def) @@ -3086,224 +3195,234 @@ describeOneTableDetails(const char *schemaname, /* * Finish printing the footer information about a table. */ - if (tableinfo.relkind == RELKIND_RELATION || - tableinfo.relkind == RELKIND_MATVIEW || - tableinfo.relkind == RELKIND_FOREIGN_TABLE || - tableinfo.relkind == RELKIND_PARTITIONED_TABLE || - tableinfo.relkind == RELKIND_PARTITIONED_INDEX || - tableinfo.relkind == RELKIND_TOASTVALUE) + Assert(RELKIND_IS_VALID((RelKind) tableinfo.relkind)); + switch ((RelKind) tableinfo.relkind) { - bool is_partitioned; - PGresult *result; - int tuples; - - /* simplify some repeated tests below */ - is_partitioned = (tableinfo.relkind == RELKIND_PARTITIONED_TABLE || - tableinfo.relkind == RELKIND_PARTITIONED_INDEX); - - /* print foreign server name */ - if (tableinfo.relkind == RELKIND_FOREIGN_TABLE) - { - char *ftoptions; - - /* Footer information about foreign table */ - printfPQExpBuffer(&buf, - "SELECT s.srvname,\n" - " pg_catalog.array_to_string(ARRAY(\n" - " SELECT pg_catalog.quote_ident(option_name)" - " || ' ' || pg_catalog.quote_literal(option_value)\n" - " FROM pg_catalog.pg_options_to_table(ftoptions)), ', ')\n" - "FROM pg_catalog.pg_foreign_table f,\n" - " pg_catalog.pg_foreign_server s\n" - "WHERE f.ftrelid = '%s' AND s.oid = f.ftserver;", - oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else if (PQntuples(result) != 1) + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_TOASTVALUE: { - PQclear(result); - goto error_return; - } + bool is_partitioned; + PGresult *result; + int tuples; - /* Print server name */ - printfPQExpBuffer(&buf, _("Server: %s"), - PQgetvalue(result, 0, 0)); - printTableAddFooter(&cont, buf.data); + /* simplify some repeated tests below */ + is_partitioned = (tableinfo.relkind == RELKIND_PARTITIONED_TABLE || + tableinfo.relkind == RELKIND_PARTITIONED_INDEX); - /* Print per-table FDW options, if any */ - ftoptions = PQgetvalue(result, 0, 1); - if (ftoptions && ftoptions[0] != '\0') - { - printfPQExpBuffer(&buf, _("FDW options: (%s)"), ftoptions); - printTableAddFooter(&cont, buf.data); - } - PQclear(result); - } + /* print foreign server name */ + if (tableinfo.relkind == RELKIND_FOREIGN_TABLE) + { + char *ftoptions; + + /* Footer information about foreign table */ + printfPQExpBuffer(&buf, + "SELECT s.srvname,\n" + " pg_catalog.array_to_string(ARRAY(\n" + " SELECT pg_catalog.quote_ident(option_name)" + " || ' ' || pg_catalog.quote_literal(option_value)\n" + " FROM pg_catalog.pg_options_to_table(ftoptions)), ', ')\n" + "FROM pg_catalog.pg_foreign_table f,\n" + " pg_catalog.pg_foreign_server s\n" + "WHERE f.ftrelid = '%s' AND s.oid = f.ftserver;", + oid); + result = PSQLexec(buf.data); + if (!result) + goto error_return; + else if (PQntuples(result) != 1) + { + PQclear(result); + goto error_return; + } - /* print tables inherited from (exclude partitioned parents) */ - printfPQExpBuffer(&buf, - "SELECT c.oid::pg_catalog.regclass\n" - "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" - "WHERE c.oid = i.inhparent AND i.inhrelid = '%s'\n" - " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_TABLE) - " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_INDEX) - "\nORDER BY inhseqno;", - oid); + /* Print server name */ + printfPQExpBuffer(&buf, _("Server: %s"), + PQgetvalue(result, 0, 0)); + printTableAddFooter(&cont, buf.data); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - { - const char *s = _("Inherits"); - int sw = pg_wcswidth(s, strlen(s), pset.encoding); + /* Print per-table FDW options, if any */ + ftoptions = PQgetvalue(result, 0, 1); + if (ftoptions && ftoptions[0] != '\0') + { + printfPQExpBuffer(&buf, _("FDW options: (%s)"), ftoptions); + printTableAddFooter(&cont, buf.data); + } + PQclear(result); + } - tuples = PQntuples(result); + /* print tables inherited from (exclude partitioned parents) */ + printfPQExpBuffer(&buf, + "SELECT c.oid::pg_catalog.regclass\n" + "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" + "WHERE c.oid = i.inhparent AND i.inhrelid = '%s'\n" + " AND c.relkind != " RelKindAsString(RELKIND_PARTITIONED_TABLE) + " AND c.relkind != " RelKindAsString(RELKIND_PARTITIONED_INDEX) + "\nORDER BY inhseqno;", + oid); - for (i = 0; i < tuples; i++) - { - if (i == 0) - printfPQExpBuffer(&buf, "%s: %s", - s, PQgetvalue(result, i, 0)); + result = PSQLexec(buf.data); + if (!result) + goto error_return; else - printfPQExpBuffer(&buf, "%*s %s", - sw, "", PQgetvalue(result, i, 0)); - if (i < tuples - 1) - appendPQExpBufferChar(&buf, ','); + { + const char *s = _("Inherits"); + int sw = pg_wcswidth(s, strlen(s), pset.encoding); - printTableAddFooter(&cont, buf.data); - } + tuples = PQntuples(result); - PQclear(result); - } + for (i = 0; i < tuples; i++) + { + if (i == 0) + printfPQExpBuffer(&buf, "%s: %s", + s, PQgetvalue(result, i, 0)); + else + printfPQExpBuffer(&buf, "%*s %s", + sw, "", PQgetvalue(result, i, 0)); + if (i < tuples - 1) + appendPQExpBufferChar(&buf, ','); - /* print child tables (with additional info if partitions) */ - if (pset.sversion >= 100000) - printfPQExpBuffer(&buf, - "SELECT c.oid::pg_catalog.regclass, c.relkind," - " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n" - "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" - "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n" - "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT'," - " c.oid::pg_catalog.regclass::pg_catalog.text;", - oid); - else if (pset.sversion >= 80300) - printfPQExpBuffer(&buf, - "SELECT c.oid::pg_catalog.regclass, c.relkind, NULL\n" - "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" - "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n" - "ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", - oid); - else - printfPQExpBuffer(&buf, - "SELECT c.oid::pg_catalog.regclass, c.relkind, NULL\n" - "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" - "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n" - "ORDER BY c.relname;", - oid); + printTableAddFooter(&cont, buf.data); + } - result = PSQLexec(buf.data); - if (!result) - goto error_return; - tuples = PQntuples(result); + PQclear(result); + } - /* - * For a partitioned table with no partitions, always print the number - * of partitions as zero, even when verbose output is expected. - * Otherwise, we will not print "Partitions" section for a partitioned - * table without any partitions. - */ - if (is_partitioned && tuples == 0) - { - printfPQExpBuffer(&buf, _("Number of partitions: %d"), tuples); - printTableAddFooter(&cont, buf.data); - } - else if (!verbose) - { - /* print the number of child tables, if any */ - if (tuples > 0) - { - if (is_partitioned) - printfPQExpBuffer(&buf, _("Number of partitions: %d (Use \\d+ to list them.)"), tuples); + /* print child tables (with additional info if partitions) */ + if (pset.sversion >= 100000) + printfPQExpBuffer(&buf, + "SELECT c.oid::pg_catalog.regclass, c.relkind," + " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n" + "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" + "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n" + "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT'," + " c.oid::pg_catalog.regclass::pg_catalog.text;", + oid); + else if (pset.sversion >= 80300) + printfPQExpBuffer(&buf, + "SELECT c.oid::pg_catalog.regclass, c.relkind, NULL\n" + "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" + "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n" + "ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", + oid); else - printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples); - printTableAddFooter(&cont, buf.data); - } - } - else - { - /* display the list of child tables */ - const char *ct = is_partitioned ? _("Partitions") : _("Child tables"); - int ctw = pg_wcswidth(ct, strlen(ct), pset.encoding); - - for (i = 0; i < tuples; i++) - { - char child_relkind = *PQgetvalue(result, i, 1); + printfPQExpBuffer(&buf, + "SELECT c.oid::pg_catalog.regclass, c.relkind, NULL\n" + "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" + "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n" + "ORDER BY c.relname;", + oid); + + result = PSQLexec(buf.data); + if (!result) + goto error_return; + tuples = PQntuples(result); - if (i == 0) - printfPQExpBuffer(&buf, "%s: %s", - ct, PQgetvalue(result, i, 0)); + /* + * For a partitioned table with no partitions, always print + * the number of partitions as zero, even when verbose output + * is expected. Otherwise, we will not print "Partitions" + * section for a partitioned table without any partitions. + */ + if (is_partitioned && tuples == 0) + { + printfPQExpBuffer(&buf, _("Number of partitions: %d"), tuples); + printTableAddFooter(&cont, buf.data); + } + else if (!verbose) + { + /* print the number of child tables, if any */ + if (tuples > 0) + { + if (is_partitioned) + printfPQExpBuffer(&buf, _("Number of partitions: %d (Use \\d+ to list them.)"), tuples); + else + printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples); + printTableAddFooter(&cont, buf.data); + } + } else - printfPQExpBuffer(&buf, "%*s %s", - ctw, "", PQgetvalue(result, i, 0)); - if (!PQgetisnull(result, i, 2)) - appendPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 2)); - if (child_relkind == RELKIND_PARTITIONED_TABLE || - child_relkind == RELKIND_PARTITIONED_INDEX) - appendPQExpBufferStr(&buf, ", PARTITIONED"); - if (i < tuples - 1) - appendPQExpBufferChar(&buf, ','); + { + /* display the list of child tables */ + const char *ct = is_partitioned ? _("Partitions") : _("Child tables"); + int ctw = pg_wcswidth(ct, strlen(ct), pset.encoding); - printTableAddFooter(&cont, buf.data); - } - } - PQclear(result); + for (i = 0; i < tuples; i++) + { + char child_relkind = *PQgetvalue(result, i, 1); + + if (i == 0) + printfPQExpBuffer(&buf, "%s: %s", + ct, PQgetvalue(result, i, 0)); + else + printfPQExpBuffer(&buf, "%*s %s", + ctw, "", PQgetvalue(result, i, 0)); + if (!PQgetisnull(result, i, 2)) + appendPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 2)); + if (child_relkind == RELKIND_PARTITIONED_TABLE || + child_relkind == RELKIND_PARTITIONED_INDEX) + appendPQExpBufferStr(&buf, ", PARTITIONED"); + if (i < tuples - 1) + appendPQExpBufferChar(&buf, ','); - /* Table type */ - if (tableinfo.reloftype) - { - printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype); - printTableAddFooter(&cont, buf.data); - } + printTableAddFooter(&cont, buf.data); + } + } + PQclear(result); - if (verbose && - (tableinfo.relkind == RELKIND_RELATION || - tableinfo.relkind == RELKIND_MATVIEW) && + /* Table type */ + if (tableinfo.reloftype) + { + printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype); + printTableAddFooter(&cont, buf.data); + } - /* - * No need to display default values; we already display a REPLICA - * IDENTITY marker on indexes. - */ - tableinfo.relreplident != 'i' && - ((strcmp(schemaname, "pg_catalog") != 0 && tableinfo.relreplident != 'd') || - (strcmp(schemaname, "pg_catalog") == 0 && tableinfo.relreplident != 'n'))) - { - const char *s = _("Replica Identity"); + if (verbose && + (tableinfo.relkind == RELKIND_RELATION || + tableinfo.relkind == RELKIND_MATVIEW) && - printfPQExpBuffer(&buf, "%s: %s", - s, - tableinfo.relreplident == 'f' ? "FULL" : - tableinfo.relreplident == 'n' ? "NOTHING" : - "???"); + /* + * No need to display default values; we already display a + * REPLICA IDENTITY marker on indexes. + */ + tableinfo.relreplident != 'i' && + ((strcmp(schemaname, "pg_catalog") != 0 && tableinfo.relreplident != 'd') || + (strcmp(schemaname, "pg_catalog") == 0 && tableinfo.relreplident != 'n'))) + { + const char *s = _("Replica Identity"); - printTableAddFooter(&cont, buf.data); - } + printfPQExpBuffer(&buf, "%s: %s", + s, + tableinfo.relreplident == 'f' ? "FULL" : + tableinfo.relreplident == 'n' ? "NOTHING" : + "???"); - /* OIDs, if verbose and not a materialized view */ - if (verbose && tableinfo.relkind != RELKIND_MATVIEW && tableinfo.hasoids) - printTableAddFooter(&cont, _("Has OIDs: yes")); + printTableAddFooter(&cont, buf.data); + } - /* Tablespace info */ - add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace, - true); + /* OIDs, if verbose and not a materialized view */ + if (verbose && tableinfo.relkind != RELKIND_MATVIEW && tableinfo.hasoids) + printTableAddFooter(&cont, _("Has OIDs: yes")); - /* Access method info */ - if (verbose && tableinfo.relam != NULL && !pset.hide_tableam) - { - printfPQExpBuffer(&buf, _("Access method: %s"), tableinfo.relam); - printTableAddFooter(&cont, buf.data); - } + /* Tablespace info */ + add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace, + true); + + /* Access method info */ + if (verbose && tableinfo.relam != NULL && !pset.hide_tableam) + { + printfPQExpBuffer(&buf, _("Access method: %s"), tableinfo.relam); + printTableAddFooter(&cont, buf.data); + } + } + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_INDEX: + case RELKIND_VIEW: + break; } /* reloptions, if verbose */ @@ -3348,59 +3467,70 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == RELKIND_RELATION || - relkind == RELKIND_MATVIEW || - relkind == RELKIND_INDEX || - relkind == RELKIND_PARTITIONED_TABLE || - relkind == RELKIND_PARTITIONED_INDEX || - relkind == RELKIND_TOASTVALUE) + Assert(RELKIND_IS_VALID((RelKind) relkind)); + switch ((RelKind) relkind) { - /* - * We ignore the database default tablespace so that users not using - * tablespaces don't need to know about them. This case also covers - * pre-8.0 servers, for which tablespace will always be 0. - */ - if (tablespace != 0) - { - PGresult *result = NULL; - PQExpBufferData buf; - - initPQExpBuffer(&buf); - printfPQExpBuffer(&buf, - "SELECT spcname FROM pg_catalog.pg_tablespace\n" - "WHERE oid = '%u';", tablespace); - result = PSQLexec(buf.data); - if (!result) - { - termPQExpBuffer(&buf); - return; - } - /* Should always be the case, but.... */ - if (PQntuples(result) > 0) + case RELKIND_RELATION: + case RELKIND_MATVIEW: + case RELKIND_INDEX: + case RELKIND_PARTITIONED_TABLE: + case RELKIND_PARTITIONED_INDEX: + case RELKIND_TOASTVALUE: { - if (newline) - { - /* Add the tablespace as a new footer */ - printfPQExpBuffer(&buf, _("Tablespace: \"%s\""), - PQgetvalue(result, 0, 0)); - printTableAddFooter(cont, buf.data); - } - else + /* + * We ignore the database default tablespace so that users not + * using tablespaces don't need to know about them. This case + * also covers pre-8.0 servers, for which tablespace will + * always be 0. + */ + if (tablespace != 0) { - /* Append the tablespace to the latest footer */ - printfPQExpBuffer(&buf, "%s", cont->footer->data); - - /*------- - translator: before this string there's an index description like - '"foo_pkey" PRIMARY KEY, btree (a)' */ - appendPQExpBuffer(&buf, _(", tablespace \"%s\""), - PQgetvalue(result, 0, 0)); - printTableSetFooter(cont, buf.data); + PGresult *result = NULL; + PQExpBufferData buf; + + initPQExpBuffer(&buf); + printfPQExpBuffer(&buf, + "SELECT spcname FROM pg_catalog.pg_tablespace\n" + "WHERE oid = '%u';", tablespace); + result = PSQLexec(buf.data); + if (!result) + { + termPQExpBuffer(&buf); + return; + } + /* Should always be the case, but.... */ + if (PQntuples(result) > 0) + { + if (newline) + { + /* Add the tablespace as a new footer */ + printfPQExpBuffer(&buf, _("Tablespace: \"%s\""), + PQgetvalue(result, 0, 0)); + printTableAddFooter(cont, buf.data); + } + else + { + /* Append the tablespace to the latest footer */ + printfPQExpBuffer(&buf, "%s", cont->footer->data); + + /*------- + translator: before this string there's an index description like + '"foo_pkey" PRIMARY KEY, btree (a)' */ + appendPQExpBuffer(&buf, _(", tablespace \"%s\""), + PQgetvalue(result, 0, 0)); + printTableSetFooter(cont, buf.data); + } + } + PQclear(result); + termPQExpBuffer(&buf); } } - PQclear(result); - termPQExpBuffer(&buf); - } + break; + case RELKIND_SEQUENCE: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_VIEW: + break; } } @@ -3694,15 +3824,15 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys "SELECT n.nspname as \"%s\",\n" " c.relname as \"%s\",\n" " CASE c.relkind" - " WHEN " CppAsString2(RELKIND_RELATION) " THEN '%s'" - " WHEN " CppAsString2(RELKIND_VIEW) " THEN '%s'" - " WHEN " CppAsString2(RELKIND_MATVIEW) " THEN '%s'" - " WHEN " CppAsString2(RELKIND_INDEX) " THEN '%s'" - " WHEN " CppAsString2(RELKIND_SEQUENCE) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_RELATION) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_VIEW) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_MATVIEW) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_INDEX) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_SEQUENCE) " THEN '%s'" " WHEN 's' THEN '%s'" - " WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'" - " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'" - " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_FOREIGN_TABLE) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_PARTITIONED_TABLE) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_PARTITIONED_INDEX) " THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3779,21 +3909,21 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, CppAsString2(RELKIND_RELATION) "," - CppAsString2(RELKIND_PARTITIONED_TABLE) ","); + appendPQExpBufferStr(&buf, RelKindAsString(RELKIND_RELATION) "," + RelKindAsString(RELKIND_PARTITIONED_TABLE) ","); if (showViews) - appendPQExpBufferStr(&buf, CppAsString2(RELKIND_VIEW) ","); + appendPQExpBufferStr(&buf, RelKindAsString(RELKIND_VIEW) ","); if (showMatViews) - appendPQExpBufferStr(&buf, CppAsString2(RELKIND_MATVIEW) ","); + appendPQExpBufferStr(&buf, RelKindAsString(RELKIND_MATVIEW) ","); if (showIndexes) - appendPQExpBufferStr(&buf, CppAsString2(RELKIND_INDEX) "," - CppAsString2(RELKIND_PARTITIONED_INDEX) ","); + appendPQExpBufferStr(&buf, RelKindAsString(RELKIND_INDEX) "," + RelKindAsString(RELKIND_PARTITIONED_INDEX) ","); if (showSeq) - appendPQExpBufferStr(&buf, CppAsString2(RELKIND_SEQUENCE) ","); + appendPQExpBufferStr(&buf, RelKindAsString(RELKIND_SEQUENCE) ","); if (showSystem || pattern) appendPQExpBufferStr(&buf, "'s',"); /* was RELKIND_SPECIAL */ if (showForeign) - appendPQExpBufferStr(&buf, CppAsString2(RELKIND_FOREIGN_TABLE) ","); + appendPQExpBufferStr(&buf, RelKindAsString(RELKIND_FOREIGN_TABLE) ","); appendPQExpBufferStr(&buf, "''"); /* dummy */ appendPQExpBufferStr(&buf, ")\n"); @@ -3921,8 +4051,8 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose) { appendPQExpBuffer(&buf, ",\n CASE c.relkind" - " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'" - " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_PARTITIONED_TABLE) " THEN '%s'" + " WHEN " RelKindAsString(RELKIND_PARTITIONED_INDEX) " THEN '%s'" " END as \"%s\"", gettext_noop("partitioned table"), gettext_noop("partitioned index"), @@ -4012,9 +4142,9 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose) appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_TABLE) ","); + appendPQExpBufferStr(&buf, RelKindAsString(RELKIND_PARTITIONED_TABLE) ","); if (showIndexes) - appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_INDEX) ","); + appendPQExpBufferStr(&buf, RelKindAsString(RELKIND_PARTITIONED_INDEX) ","); appendPQExpBufferStr(&buf, "''"); /* dummy */ appendPQExpBufferStr(&buf, ")\n"); diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index eb018854a5..c56beb579b 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -354,7 +354,7 @@ static const SchemaQuery Query_for_list_of_datatypes = { .catname = "pg_catalog.pg_type t", /* selcondition --- ignore table rowtypes and array types */ .selcondition = "(t.typrelid = 0 " - " OR (SELECT c.relkind = " CppAsString2(RELKIND_COMPOSITE_TYPE) + " OR (SELECT c.relkind = " RelKindAsString(RELKIND_COMPOSITE_TYPE) " FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) " "AND t.typname !~ '^_'", .viscondition = "pg_catalog.pg_type_is_visible(t.oid)", @@ -366,7 +366,7 @@ static const SchemaQuery Query_for_list_of_datatypes = { static const SchemaQuery Query_for_list_of_composite_datatypes = { .catname = "pg_catalog.pg_type t", /* selcondition --- only get composite types */ - .selcondition = "(SELECT c.relkind = " CppAsString2(RELKIND_COMPOSITE_TYPE) + .selcondition = "(SELECT c.relkind = " RelKindAsString(RELKIND_COMPOSITE_TYPE) " FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid) " "AND t.typname !~ '^_'", .viscondition = "pg_catalog.pg_type_is_visible(t.oid)", @@ -425,7 +425,7 @@ static const SchemaQuery Query_for_list_of_routines = { static const SchemaQuery Query_for_list_of_sequences = { .catname = "pg_catalog.pg_class c", - .selcondition = "c.relkind IN (" CppAsString2(RELKIND_SEQUENCE) ")", + .selcondition = "c.relkind IN (" RelKindAsString(RELKIND_SEQUENCE) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -433,7 +433,7 @@ static const SchemaQuery Query_for_list_of_sequences = { static const SchemaQuery Query_for_list_of_foreign_tables = { .catname = "pg_catalog.pg_class c", - .selcondition = "c.relkind IN (" CppAsString2(RELKIND_FOREIGN_TABLE) ")", + .selcondition = "c.relkind IN (" RelKindAsString(RELKIND_FOREIGN_TABLE) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -442,8 +442,8 @@ static const SchemaQuery Query_for_list_of_foreign_tables = { static const SchemaQuery Query_for_list_of_tables = { .catname = "pg_catalog.pg_class c", .selcondition = - "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_PARTITIONED_TABLE) ")", + "c.relkind IN (" RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_PARTITIONED_TABLE) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -451,7 +451,7 @@ static const SchemaQuery Query_for_list_of_tables = { static const SchemaQuery Query_for_list_of_partitioned_tables = { .catname = "pg_catalog.pg_class c", - .selcondition = "c.relkind IN (" CppAsString2(RELKIND_PARTITIONED_TABLE) ")", + .selcondition = "c.relkind IN (" RelKindAsString(RELKIND_PARTITIONED_TABLE) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -459,7 +459,7 @@ static const SchemaQuery Query_for_list_of_partitioned_tables = { static const SchemaQuery Query_for_list_of_views = { .catname = "pg_catalog.pg_class c", - .selcondition = "c.relkind IN (" CppAsString2(RELKIND_VIEW) ")", + .selcondition = "c.relkind IN (" RelKindAsString(RELKIND_VIEW) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -467,7 +467,7 @@ static const SchemaQuery Query_for_list_of_views = { static const SchemaQuery Query_for_list_of_matviews = { .catname = "pg_catalog.pg_class c", - .selcondition = "c.relkind IN (" CppAsString2(RELKIND_MATVIEW) ")", + .selcondition = "c.relkind IN (" RelKindAsString(RELKIND_MATVIEW) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -476,8 +476,8 @@ static const SchemaQuery Query_for_list_of_matviews = { static const SchemaQuery Query_for_list_of_indexes = { .catname = "pg_catalog.pg_class c", .selcondition = - "c.relkind IN (" CppAsString2(RELKIND_INDEX) ", " - CppAsString2(RELKIND_PARTITIONED_INDEX) ")", + "c.relkind IN (" RelKindAsString(RELKIND_INDEX) ", " + RelKindAsString(RELKIND_PARTITIONED_INDEX) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -485,7 +485,7 @@ static const SchemaQuery Query_for_list_of_indexes = { static const SchemaQuery Query_for_list_of_partitioned_indexes = { .catname = "pg_catalog.pg_class c", - .selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX), + .selcondition = "c.relkind = " RelKindAsString(RELKIND_PARTITIONED_INDEX), .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -503,8 +503,8 @@ static const SchemaQuery Query_for_list_of_relations = { /* partitioned relations */ static const SchemaQuery Query_for_list_of_partitioned_relations = { .catname = "pg_catalog.pg_class c", - .selcondition = "c.relkind IN (" CppAsString2(RELKIND_PARTITIONED_TABLE) - ", " CppAsString2(RELKIND_PARTITIONED_INDEX) ")", + .selcondition = "c.relkind IN (" RelKindAsString(RELKIND_PARTITIONED_TABLE) + ", " RelKindAsString(RELKIND_PARTITIONED_INDEX) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -521,10 +521,10 @@ static const SchemaQuery Query_for_list_of_operator_families = { static const SchemaQuery Query_for_list_of_updatables = { .catname = "pg_catalog.pg_class c", .selcondition = - "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_FOREIGN_TABLE) ", " - CppAsString2(RELKIND_VIEW) ", " - CppAsString2(RELKIND_PARTITIONED_TABLE) ")", + "c.relkind IN (" RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_FOREIGN_TABLE) ", " + RelKindAsString(RELKIND_VIEW) ", " + RelKindAsString(RELKIND_PARTITIONED_TABLE) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -534,12 +534,12 @@ static const SchemaQuery Query_for_list_of_updatables = { static const SchemaQuery Query_for_list_of_selectables = { .catname = "pg_catalog.pg_class c", .selcondition = - "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_SEQUENCE) ", " - CppAsString2(RELKIND_VIEW) ", " - CppAsString2(RELKIND_MATVIEW) ", " - CppAsString2(RELKIND_FOREIGN_TABLE) ", " - CppAsString2(RELKIND_PARTITIONED_TABLE) ")", + "c.relkind IN (" RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_SEQUENCE) ", " + RelKindAsString(RELKIND_VIEW) ", " + RelKindAsString(RELKIND_MATVIEW) ", " + RelKindAsString(RELKIND_FOREIGN_TABLE) ", " + RelKindAsString(RELKIND_PARTITIONED_TABLE) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -552,10 +552,10 @@ static const SchemaQuery Query_for_list_of_selectables = { static const SchemaQuery Query_for_list_of_analyzables = { .catname = "pg_catalog.pg_class c", .selcondition = - "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_PARTITIONED_TABLE) ", " - CppAsString2(RELKIND_MATVIEW) ", " - CppAsString2(RELKIND_FOREIGN_TABLE) ")", + "c.relkind IN (" RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_PARTITIONED_TABLE) ", " + RelKindAsString(RELKIND_MATVIEW) ", " + RelKindAsString(RELKIND_FOREIGN_TABLE) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -565,9 +565,9 @@ static const SchemaQuery Query_for_list_of_analyzables = { static const SchemaQuery Query_for_list_of_indexables = { .catname = "pg_catalog.pg_class c", .selcondition = - "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_PARTITIONED_TABLE) ", " - CppAsString2(RELKIND_MATVIEW) ")", + "c.relkind IN (" RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_PARTITIONED_TABLE) ", " + RelKindAsString(RELKIND_MATVIEW) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", @@ -577,8 +577,8 @@ static const SchemaQuery Query_for_list_of_indexables = { static const SchemaQuery Query_for_list_of_vacuumables = { .catname = "pg_catalog.pg_class c", .selcondition = - "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_MATVIEW) ")", + "c.relkind IN (" RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_MATVIEW) ")", .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", .namespace = "c.relnamespace", .result = "pg_catalog.quote_ident(c.relname)", diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c index b7b19ccc1c..1b921a1483 100644 --- a/src/bin/scripts/reindexdb.c +++ b/src/bin/scripts/reindexdb.c @@ -621,8 +621,8 @@ get_parallel_object_list(PGconn *conn, ReindexType type, " ON c.relnamespace = ns.oid\n" " WHERE ns.nspname != 'pg_catalog'\n" " AND c.relkind IN (" - CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_MATVIEW) ")\n" + RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_MATVIEW) ")\n" " ORDER BY c.relpages DESC;"); break; @@ -643,8 +643,8 @@ get_parallel_object_list(PGconn *conn, ReindexType type, " JOIN pg_catalog.pg_namespace ns" " ON c.relnamespace = ns.oid\n" " WHERE c.relkind IN (" - CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_MATVIEW) ")\n" + RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_MATVIEW) ")\n" " AND ns.nspname IN ("); for (cell = user_list->head; cell; cell = cell->next) diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c index 6a3c941158..9a90e820f4 100644 --- a/src/bin/scripts/vacuumdb.c +++ b/src/bin/scripts/vacuumdb.c @@ -573,8 +573,8 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts, if (!tables_listed) { appendPQExpBufferStr(&catalog_query, " WHERE c.relkind OPERATOR(pg_catalog.=) ANY (array[" - CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_MATVIEW) "])\n"); + RelKindAsString(RELKIND_RELATION) ", " + RelKindAsString(RELKIND_MATVIEW) "])\n"); has_where = true; } diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 78b33b2a7f..4bd86703af 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -154,16 +154,44 @@ typedef FormData_pg_class *Form_pg_class; #ifdef EXPOSE_TO_CLIENT_CODE -#define RELKIND_RELATION 'r' /* ordinary table */ -#define RELKIND_INDEX 'i' /* secondary index */ -#define RELKIND_SEQUENCE 'S' /* sequence object */ -#define RELKIND_TOASTVALUE 't' /* for out-of-line values */ -#define RELKIND_VIEW 'v' /* view */ -#define RELKIND_MATVIEW 'm' /* materialized view */ -#define RELKIND_COMPOSITE_TYPE 'c' /* composite type */ -#define RELKIND_FOREIGN_TABLE 'f' /* foreign table */ -#define RELKIND_PARTITIONED_TABLE 'p' /* partitioned table */ -#define RELKIND_PARTITIONED_INDEX 'I' /* partitioned index */ +typedef enum RelKind +{ + RELKIND_RELATION = 'r', /* ordinary table */ + RELKIND_INDEX = 'i', /* secondary index */ + RELKIND_SEQUENCE = 'S', /* sequence object */ + RELKIND_TOASTVALUE = 't', /* for out-of-line values */ + RELKIND_VIEW = 'v', /* view */ + RELKIND_MATVIEW = 'm', /* materialized view */ + RELKIND_COMPOSITE_TYPE = 'c', /* composite type */ + RELKIND_FOREIGN_TABLE = 'f', /* foreign table */ + RELKIND_PARTITIONED_TABLE = 'p', /* partitioned table */ + RELKIND_PARTITIONED_INDEX = 'I' /* partitioned index */ +} RelKind; + +#define chr_RELKIND_RELATION 'r' +#define chr_RELKIND_INDEX 'i' +#define chr_RELKIND_SEQUENCE 'S' +#define chr_RELKIND_TOASTVALUE 't' +#define chr_RELKIND_VIEW 'v' +#define chr_RELKIND_MATVIEW 'm' +#define chr_RELKIND_COMPOSITE_TYPE 'c' +#define chr_RELKIND_FOREIGN_TABLE 'f' +#define chr_RELKIND_PARTITIONED_TABLE 'p' +#define chr_RELKIND_PARTITIONED_INDEX 'I' + +#define RELKIND_IS_VALID(x) ( \ + (x) == chr_RELKIND_RELATION || \ + (x) == chr_RELKIND_INDEX || \ + (x) == chr_RELKIND_SEQUENCE || \ + (x) == chr_RELKIND_TOASTVALUE || \ + (x) == chr_RELKIND_VIEW || \ + (x) == chr_RELKIND_MATVIEW || \ + (x) == chr_RELKIND_COMPOSITE_TYPE || \ + (x) == chr_RELKIND_FOREIGN_TABLE || \ + (x) == chr_RELKIND_PARTITIONED_TABLE || \ + (x) == chr_RELKIND_PARTITIONED_INDEX ) + +#define RelKindAsString(x) CppAsString2(chr_##x) #define RELPERSISTENCE_PERMANENT 'p' /* regular table */ #define RELPERSISTENCE_UNLOGGED 'u' /* unlogged permanent table */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 6f96b31fb4..f72a241b7c 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -2110,7 +2110,7 @@ typedef struct AggregateInstrumentation Size hash_mem_peak; /* peak hash table memory usage */ uint64 hash_disk_used; /* kB of disk space used */ int hash_batches_used; /* batches used during entire execution */ -} AggregateInstrumentation; +} AggregateInstrumentation; /* ---------------- * Shared memory container for per-worker aggregate information @@ -2120,7 +2120,7 @@ typedef struct SharedAggInfo { int num_workers; AggregateInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]; -} SharedAggInfo; +} SharedAggInfo; /* --------------------- * AggState information diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index b20e2ad4f6..65602a8763 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -81,7 +81,7 @@ typedef enum PROC_WAIT_STATUS_OK, PROC_WAIT_STATUS_WAITING, PROC_WAIT_STATUS_ERROR, -} ProcWaitStatus; +} ProcWaitStatus; /* * Each backend has a PGPROC struct in shared memory. There is also a list of diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 3ca5e938f8..e5afbeddae 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -47,11 +47,11 @@ extern int namestrcmp(Name name, const char *str); extern int32 pg_atoi(const char *s, int size, int c); extern int16 pg_strtoint16(const char *s); extern int32 pg_strtoint32(const char *s); -extern int pg_itoa(int16 i, char *a); -extern int pg_ultoa_n(uint32 l, char *a); -extern int pg_ulltoa_n(uint64 l, char *a); -extern int pg_ltoa(int32 l, char *a); -extern int pg_lltoa(int64 ll, char *a); +extern int pg_itoa(int16 i, char *a); +extern int pg_ultoa_n(uint32 l, char *a); +extern int pg_ulltoa_n(uint64 l, char *a); +extern int pg_ltoa(int32 l, char *a); +extern int pg_lltoa(int64 ll, char *a); extern char *pg_ultostr_zeropad(char *str, uint32 value, int32 minwidth); extern char *pg_ultostr(char *str, uint32 value); extern uint64 pg_strtouint64(const char *str, char **endptr, int base); diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index e7f4a5f291..cefe979988 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -1723,14 +1723,22 @@ plpgsql_parse_cwordtype(List *idents) * It must be a relation, sequence, view, materialized view, composite * type, or foreign table */ - if (classStruct->relkind != RELKIND_RELATION && - classStruct->relkind != RELKIND_SEQUENCE && - classStruct->relkind != RELKIND_VIEW && - classStruct->relkind != RELKIND_MATVIEW && - classStruct->relkind != RELKIND_COMPOSITE_TYPE && - classStruct->relkind != RELKIND_FOREIGN_TABLE && - classStruct->relkind != RELKIND_PARTITIONED_TABLE) - goto done; + Assert(RELKIND_IS_VALID((RelKind) classStruct->relkind)); + switch ((RelKind) classStruct->relkind) + { + case RELKIND_RELATION: + case RELKIND_SEQUENCE: + case RELKIND_VIEW: + case RELKIND_MATVIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + break; + case RELKIND_PARTITIONED_INDEX: + case RELKIND_INDEX: + case RELKIND_TOASTVALUE: + goto done; + } /* * Fetch the named table field and its type diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c index 02397f2eb1..87453751b0 100644 --- a/src/test/regress/regress.c +++ b/src/test/regress/regress.c @@ -816,7 +816,7 @@ test_spinlock(void) char data_before[4]; slock_t lock; char data_after[4]; - } struct_w_lock; + } struct_w_lock; memcpy(struct_w_lock.data_before, "abcd", 4); memcpy(struct_w_lock.data_after, "ef12", 4); @@ -864,28 +864,28 @@ test_spinlock(void) } /* - * Ensure that allocating more than INT32_MAX emulated spinlocks - * works. That's interesting because the spinlock emulation uses a 32bit - * integer to map spinlocks onto semaphores. There've been bugs... + * Ensure that allocating more than INT32_MAX emulated spinlocks works. + * That's interesting because the spinlock emulation uses a 32bit integer + * to map spinlocks onto semaphores. There've been bugs... */ #ifndef HAVE_SPINLOCKS { /* - * Initialize enough spinlocks to advance counter close to - * wraparound. It's too expensive to perform acquire/release for each, - * as those may be syscalls when the spinlock emulation is used (and - * even just atomic TAS would be expensive). + * Initialize enough spinlocks to advance counter close to wraparound. + * It's too expensive to perform acquire/release for each, as those + * may be syscalls when the spinlock emulation is used (and even just + * atomic TAS would be expensive). */ for (uint32 i = 0; i < INT32_MAX - 100000; i++) { - slock_t lock; + slock_t lock; SpinLockInit(&lock); } for (uint32 i = 0; i < 200000; i++) { - slock_t lock; + slock_t lock; SpinLockInit(&lock); @@ -915,7 +915,7 @@ test_spinlock(void) static void test_atomic_spin_nest(void) { - slock_t lock; + slock_t lock; #define NUM_TEST_ATOMICS (NUM_SPINLOCK_SEMAPHORES + NUM_ATOMICS_SEMAPHORES + 27) pg_atomic_uint32 atomics32[NUM_TEST_ATOMICS]; pg_atomic_uint64 atomics64[NUM_TEST_ATOMICS]; diff --git a/src/tools/findoidjoins/findoidjoins.c b/src/tools/findoidjoins/findoidjoins.c index 5239332ea7..3961804b10 100644 --- a/src/tools/findoidjoins/findoidjoins.c +++ b/src/tools/findoidjoins/findoidjoins.c @@ -62,7 +62,7 @@ main(int argc, char **argv) "SELECT c.relname, (SELECT nspname FROM " "pg_catalog.pg_namespace n WHERE n.oid = c.relnamespace) AS nspname " "FROM pg_catalog.pg_class c " - "WHERE c.relkind = " CppAsString2(RELKIND_RELATION) + "WHERE c.relkind = " RelKindAsString(RELKIND_RELATION) " AND c.oid < '%u'" " AND EXISTS(SELECT * FROM pg_attribute a" " WHERE a.attrelid = c.oid AND a.attname = 'oid' " @@ -88,7 +88,7 @@ main(int argc, char **argv) "FROM pg_catalog.pg_class c, pg_catalog.pg_attribute a " "WHERE a.attnum > 0" " AND a.attname != 'oid'" - " AND c.relkind = " CppAsString2(RELKIND_RELATION) + " AND c.relkind = " RelKindAsString(RELKIND_RELATION) " AND c.oid < '%u'" " AND a.attrelid = c.oid" " AND a.atttypid IN ('pg_catalog.oid'::regtype, " @@ -166,7 +166,7 @@ main(int argc, char **argv) "a.attname " "FROM pg_catalog.pg_class c, pg_catalog.pg_attribute a " "WHERE a.attnum > 0" - " AND c.relkind = " CppAsString2(RELKIND_RELATION) + " AND c.relkind = " RelKindAsString(RELKIND_RELATION) " AND a.attrelid = c.oid" " AND a.atttypid IN ('pg_catalog.oid[]'::regtype, " " 'pg_catalog.oidvector'::regtype, " -- 2.21.1 (Apple Git-122.3)