From 99c32c8bb1d424aecdf24fcb0e96a470c91d83a0 Mon Sep 17 00:00:00 2001 From: "dgrowley@gmail.com" Date: Thu, 2 Aug 2018 16:02:42 +1200 Subject: [PATCH v1 2/4] Store partition details in pg_partition instead of pg_inherits --- contrib/postgres_fdw/postgres_fdw.c | 4 +- src/backend/catalog/Makefile | 2 +- src/backend/catalog/heap.c | 72 ++--- src/backend/catalog/index.c | 2 +- src/backend/catalog/partition.c | 140 +++++---- src/backend/catalog/pg_inherits.c | 6 + src/backend/commands/analyze.c | 12 +- src/backend/commands/lockcmds.c | 127 +++++--- src/backend/commands/publicationcmds.c | 10 +- src/backend/commands/tablecmds.c | 477 +++++++++++++++++++++-------- src/backend/commands/trigger.c | 10 +- src/backend/commands/vacuum.c | 3 +- src/backend/executor/execPartition.c | 3 +- src/backend/optimizer/prep/prepunion.c | 215 ++++++++----- src/backend/partitioning/partbounds.c | 11 +- src/backend/rewrite/rewriteDefine.c | 2 +- src/backend/tcop/utility.c | 9 +- src/backend/utils/cache/partcache.c | 70 +++-- src/backend/utils/cache/syscache.c | 14 +- src/bin/pg_dump/common.c | 22 ++ src/bin/pg_dump/pg_dump.c | 30 +- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 60 +++- src/bin/psql/tab-complete.c | 1 + src/include/catalog/heap.h | 4 +- src/include/catalog/indexing.h | 6 + src/include/catalog/partition.h | 2 + src/include/catalog/pg_class.dat | 18 +- src/include/catalog/pg_class.h | 3 +- src/include/catalog/toasting.h | 1 + src/include/nodes/parsenodes.h | 2 +- src/include/utils/rel.h | 6 + src/include/utils/syscache.h | 1 + src/test/regress/expected/alter_table.out | 4 +- src/test/regress/expected/misc_sanity.out | 3 +- src/test/regress/expected/sanity_check.out | 1 + 36 files changed, 907 insertions(+), 447 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 0803c30a48..beb867d613 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -4522,7 +4522,9 @@ postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid) deparseStringLiteral(&buf, stmt->remote_schema); /* Partitions are supported since Postgres 10 */ - if (PQserverVersion(conn) >= 100000) + if (PQserverVersion(conn) >= 120000) + appendStringInfoString(&buf, " AND c.relpartitionparent <> 0 "); + else if (PQserverVersion(conn) >= 100000) appendStringInfoString(&buf, " AND NOT c.relispartition "); /* Apply restrictions for LIMIT TO and EXCEPT */ diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index 0865240f11..43d9e2eaaa 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -46,7 +46,7 @@ CATALOG_HEADERS := \ pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \ pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \ pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \ - pg_subscription_rel.h + pg_subscription_rel.h pg_partition.h GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 4cfc0c8911..c6429a3785 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -49,6 +49,7 @@ #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" +#include "catalog/pg_partition.h" #include "catalog/pg_partitioned_table.h" #include "catalog/pg_statistic.h" #include "catalog/pg_subscription_rel.h" @@ -810,7 +811,7 @@ InsertPgClassTuple(Relation pg_class_desc, values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass); values[Anum_pg_class_relispopulated - 1] = BoolGetDatum(rd_rel->relispopulated); values[Anum_pg_class_relreplident - 1] = CharGetDatum(rd_rel->relreplident); - values[Anum_pg_class_relispartition - 1] = BoolGetDatum(rd_rel->relispartition); + values[Anum_pg_class_relpartitionparent - 1] = ObjectIdGetDatum(rd_rel->relpartitionparent); values[Anum_pg_class_relrewrite - 1] = ObjectIdGetDatum(rd_rel->relrewrite); values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid); values[Anum_pg_class_relminmxid - 1] = MultiXactIdGetDatum(rd_rel->relminmxid); @@ -823,9 +824,6 @@ InsertPgClassTuple(Relation pg_class_desc, else nulls[Anum_pg_class_reloptions - 1] = true; - /* relpartbound is set by updating this tuple, if necessary */ - nulls[Anum_pg_class_relpartbound - 1] = true; - tup = heap_form_tuple(RelationGetDescr(pg_class_desc), values, nulls); /* @@ -929,8 +927,8 @@ AddNewRelationTuple(Relation pg_class_desc, new_rel_reltup->reltype = new_type_oid; new_rel_reltup->reloftype = reloftype; - /* relispartition is always set by updating this tuple later */ - new_rel_reltup->relispartition = false; + /* relpartitionparent is always set by updating this tuple later */ + new_rel_reltup->relpartitionparent = InvalidOid; new_rel_desc->rd_att->tdtypeid = new_type_oid; @@ -1807,11 +1805,17 @@ heap_drop_with_catalog(Oid relid) tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", relid); - if (((Form_pg_class) GETSTRUCT(tuple))->relispartition) + + parentOid = ((Form_pg_class) GETSTRUCT(tuple))->relpartitionparent; + if (OidIsValid(parentOid)) { - parentOid = get_partition_parent(relid); + HeapTuple parttup; + Relation pgpart; + LockRelationOid(parentOid, AccessExclusiveLock); + pgpart = heap_open(PartitionRelationId, RowExclusiveLock); + /* * If this is not the default partition, dropping it will change the * default partition's partition constraint, so we must lock it. @@ -1819,6 +1823,14 @@ heap_drop_with_catalog(Oid relid) defaultPartOid = get_default_partition_oid(parentOid); if (OidIsValid(defaultPartOid) && relid != defaultPartOid) LockRelationOid(defaultPartOid, AccessExclusiveLock); + + parttup = SearchSysCacheCopy1(PARTSRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(parttup)) + elog(ERROR, "cache lookup failed for relation %u", relid); + + CatalogTupleDelete(pgpart, &parttup->t_self); + + heap_close(pgpart, RowExclusiveLock); } ReleaseSysCache(tuple); @@ -2759,7 +2771,8 @@ MergeWithExistingConstraint(Relation rel, const char *ccname, Node *expr, * constraints are always non-local, including those that were * merged. */ - if (is_local && !con->conislocal && !rel->rd_rel->relispartition) + if (is_local && !con->conislocal && + !OidIsValid(rel->rd_rel->relpartitionparent)) allow_merge = true; if (!found || !allow_merge) @@ -2809,7 +2822,7 @@ MergeWithExistingConstraint(Relation rel, const char *ccname, Node *expr, * inherited only once since it cannot have multiple parents and * it is never considered local. */ - if (rel->rd_rel->relispartition) + if (OidIsValid(rel->rd_rel->relpartitionparent)) { con->coninhcount = 1; con->conislocal = false; @@ -3481,9 +3494,9 @@ RemovePartitionKeyByRelId(Oid relid) } /* - * StorePartitionBound - * Update pg_class tuple of rel to store the partition bound and set - * relispartition to true + * MarkRelationPartitioned + * Update pg_class tuple of rel to set relpartitionparent to the parent's + * Oid. * * If this is the default partition, also update the default partition OID in * pg_partitioned_table. @@ -3493,14 +3506,10 @@ RemovePartitionKeyByRelId(Oid relid) * default partition, we must invalidate its relcache entry as well. */ void -StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound) +MarkRelationPartitioned(Relation rel, Relation parent, bool is_default) { Relation classRel; - HeapTuple tuple, - newtuple; - Datum new_val[Natts_pg_class]; - bool new_null[Natts_pg_class], - new_repl[Natts_pg_class]; + HeapTuple tuple; Oid defaultPartOid; /* Update pg_class tuple */ @@ -3514,36 +3523,23 @@ StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound) #ifdef USE_ASSERT_CHECKING { Form_pg_class classForm; - bool isnull; classForm = (Form_pg_class) GETSTRUCT(tuple); - Assert(!classForm->relispartition); - (void) SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relpartbound, - &isnull); - Assert(isnull); + Assert(!OidIsValid(classForm->relpartitionparent)); } #endif - /* Fill in relpartbound value */ - memset(new_val, 0, sizeof(new_val)); - memset(new_null, false, sizeof(new_null)); - memset(new_repl, false, sizeof(new_repl)); - new_val[Anum_pg_class_relpartbound - 1] = CStringGetTextDatum(nodeToString(bound)); - new_null[Anum_pg_class_relpartbound - 1] = false; - new_repl[Anum_pg_class_relpartbound - 1] = true; - newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel), - new_val, new_null, new_repl); - /* Also set the flag */ - ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = true; - CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple); - heap_freetuple(newtuple); + /* Set the relpartitionparent */ + ((Form_pg_class) GETSTRUCT(tuple))->relpartitionparent = RelationGetRelid(parent); + CatalogTupleUpdate(classRel, &tuple->t_self, tuple); + heap_freetuple(tuple); heap_close(classRel, RowExclusiveLock); /* * If we're storing bounds for the default partition, update * pg_partitioned_table too. */ - if (bound->is_default) + if (is_default) update_default_partition_oid(RelationGetRelid(parent), RelationGetRelid(rel)); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 8b276bc430..eda850edef 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -936,7 +936,7 @@ index_create(Relation heapRelation, indexRelation->rd_rel->relowner = heapRelation->rd_rel->relowner; indexRelation->rd_rel->relam = accessMethodObjectId; indexRelation->rd_rel->relhasoids = false; - indexRelation->rd_rel->relispartition = OidIsValid(parentIndexRelid); + indexRelation->rd_rel->relpartitionparent = parentIndexRelid; /* * store index's pg_class entry diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 558022647c..d3e6787885 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -22,6 +22,7 @@ #include "catalog/indexing.h" #include "catalog/partition.h" #include "catalog/pg_inherits.h" +#include "catalog/pg_partition.h" #include "catalog/pg_partitioned_table.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" @@ -29,124 +30,121 @@ #include "optimizer/var.h" #include "partitioning/partbounds.h" #include "rewrite/rewriteManip.h" +#include "storage/lmgr.h" #include "utils/fmgroids.h" #include "utils/partcache.h" #include "utils/rel.h" #include "utils/syscache.h" - -static Oid get_partition_parent_worker(Relation inhRel, Oid relid); -static void get_partition_ancestors_worker(Relation inhRel, Oid relid, - List **ancestors); +static void get_partition_descendants_worker(Oid relid, LOCKMODE lockmode, + List **reloids); /* * get_partition_parent * Obtain direct parent of given relation * - * Returns inheritance parent of a partition by scanning pg_inherits - * - * Note: Because this function assumes that the relation whose OID is passed - * as an argument will have precisely one parent, it should only be called - * when it is known that the relation is a partition. + * Returns partition parent of a partition or InvalidOid if there is no parent */ Oid get_partition_parent(Oid relid) { - Relation catalogRelation; Oid result; + HeapTuple tuple; - catalogRelation = heap_open(InheritsRelationId, AccessShareLock); - - result = get_partition_parent_worker(catalogRelation, relid); - - if (!OidIsValid(result)) - elog(ERROR, "could not find tuple for parent of relation %u", relid); + tuple = SearchSysCache1(RELOID, relid); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relid); - heap_close(catalogRelation, AccessShareLock); + result = ((Form_pg_class) GETSTRUCT(tuple))->relpartitionparent; + ReleaseSysCache(tuple); return result; } /* - * get_partition_parent_worker - * Scan the pg_inherits relation to return the OID of the parent of the - * given relation + * get_partition_ancestors + * Obtain ancestors of a given partition or partitioned index. + * + * Follows pg_class.relpartitionparent links and returns a list of ancestors + * of the given partition or partitioned index starting with the parent and + * ending with the top-level partitioned table or index. */ -static Oid -get_partition_parent_worker(Relation inhRel, Oid relid) +List * +get_partition_ancestors(Oid relid) { - SysScanDesc scan; - ScanKeyData key[2]; - Oid result = InvalidOid; + List *result = NIL; HeapTuple tuple; - ScanKeyInit(&key[0], - Anum_pg_inherits_inhrelid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relid)); - ScanKeyInit(&key[1], - Anum_pg_inherits_inhseqno, - BTEqualStrategyNumber, F_INT4EQ, - Int32GetDatum(1)); - - scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, true, - NULL, 2, key); - tuple = systable_getnext(scan); - if (HeapTupleIsValid(tuple)) + for (;;) { - Form_pg_inherits form = (Form_pg_inherits) GETSTRUCT(tuple); + Oid parentOid; - result = form->inhparent; - } + tuple = SearchSysCache1(RELOID, relid); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relid); + + parentOid = ((Form_pg_class) GETSTRUCT(tuple))->relpartitionparent; + ReleaseSysCache(tuple); - systable_endscan(scan); + if (!OidIsValid(parentOid)) + break; + + result = lappend_oid(result, parentOid); + + relid = parentOid; + } return result; } /* - * get_partition_ancestors - * Obtain ancestors of given relation - * - * Returns a list of ancestors of the given relation. - * - * Note: Because this function assumes that the relation whose OID is passed - * as an argument and each ancestor will have precisely one parent, it should - * only be called when it is known that the relation is a partition. + * get_partition_descendants + * Returns a list of Oids of all partitions which descend from 'relid' + * including 'relid' itself. Obtains a 'lockmode' level lock on each + * item in the list. */ List * -get_partition_ancestors(Oid relid) +get_partition_descendants(Oid relid, LOCKMODE lockmode) { - List *result = NIL; - Relation inhRel; - - inhRel = heap_open(InheritsRelationId, AccessShareLock); - - get_partition_ancestors_worker(inhRel, relid, &result); + List *reloids = NIL; - heap_close(inhRel, AccessShareLock); + get_partition_descendants_worker(relid, lockmode, &reloids); - return result; + return reloids; } -/* - * get_partition_ancestors_worker - * recursive worker for get_partition_ancestors - */ static void -get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors) +get_partition_descendants_worker(Oid relid, LOCKMODE lockmode, List **reloids) { - Oid parentOid; + Relation rel = relation_open(relid, lockmode); + PartitionDesc partdesc; + int i; + + partdesc = RelationGetPartitionDesc(rel); + + Assert(partdesc); - /* Recursion ends at the topmost level, ie., when there's no parent */ - parentOid = get_partition_parent_worker(inhRel, relid); - if (parentOid == InvalidOid) - return; + *reloids = lappend_oid(*reloids, relid); - *ancestors = lappend_oid(*ancestors, parentOid); - get_partition_ancestors_worker(inhRel, parentOid, ancestors); + for (i = 0; i < partdesc->nparts; i++) + { + Oid partoid = partdesc->oids[i]; + + if (!partdesc->is_leaf[i]) + get_partition_descendants_worker(partoid, lockmode, reloids); + else + { + if (lockmode != NoLock) + LockRelationOid(partoid, lockmode); + + *reloids = lappend_oid(*reloids, partoid); + } + } + + relation_close(rel, NoLock); } + /* * map_partition_varattnos - maps varattno of any Vars in expr from the * attno's of 'from_rel' to the attno's of 'to_rel' partition, each of which diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c index 85baca54cc..87c8fd3266 100644 --- a/src/backend/catalog/pg_inherits.c +++ b/src/backend/catalog/pg_inherits.c @@ -28,6 +28,7 @@ #include "storage/lmgr.h" #include "utils/builtins.h" #include "utils/fmgroids.h" +#include "utils/lsyscache.h" // get_rel_relkind #include "utils/memutils.h" #include "utils/syscache.h" #include "utils/tqual.h" @@ -172,6 +173,8 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents) *rel_numparents; ListCell *l; + Assert(get_rel_relkind(parentrelId) != RELKIND_PARTITIONED_TABLE); + memset(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(SeenRelsEntry); @@ -267,6 +270,9 @@ has_subclass(Oid relationId) if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", relationId); + Assert(!OidIsValid(((Form_pg_class) GETSTRUCT(tuple))->relpartitionparent)); + Assert(((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_PARTITIONED_TABLE); + result = ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass; ReleaseSysCache(tuple); return result; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 3e148f03d0..83f70fb165 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -26,6 +26,7 @@ #include "catalog/catalog.h" #include "catalog/index.h" #include "catalog/indexing.h" +#include "catalog/partition.h" #include "catalog/pg_collation.h" #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" @@ -311,9 +312,10 @@ analyze_rel(Oid relid, RangeVar *relation, int options, relpages, false, in_outer_xact, elevel); /* - * If there are child tables, do recursive ANALYZE. + * If there are child tables or it's a partitioned table, do recursive + * ANALYZE. */ - if (onerel->rd_rel->relhassubclass) + if (onerel->rd_rel->relhassubclass || onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages, true, in_outer_xact, elevel); @@ -1334,8 +1336,10 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, * Find all members of inheritance set. We only need AccessShareLock on * the children. */ - tableOIDs = - find_all_inheritors(RelationGetRelid(onerel), AccessShareLock, NULL); + if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + tableOIDs = get_partition_descendants(RelationGetRelid(onerel), AccessShareLock); + else + tableOIDs = find_all_inheritors(RelationGetRelid(onerel), AccessShareLock, NULL); /* * Check that there's at least one descendant, else fail. This could diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index 71278b38cf..4a583c1c8b 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -15,6 +15,7 @@ #include "postgres.h" #include "catalog/namespace.h" +#include "catalog/partition.h" #include "catalog/pg_inherits.h" #include "commands/lockcmds.h" #include "miscadmin.h" @@ -29,6 +30,8 @@ static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, Oid userid); static AclResult LockTableAclCheck(Oid relid, LOCKMODE lockmode, Oid userid); +static bool LockSingleTable(Oid relid, LOCKMODE lockmode, bool nowait, + Oid userid); static void RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg); static void LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, List *ancestor_views); @@ -107,65 +110,109 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid, } /* - * Apply LOCK TABLE recursively over an inheritance tree + * LockSingleTable + * Apply LOCK TABLE to a single table. + */ +static bool +LockSingleTable(Oid relid, LOCKMODE lockmode, bool nowait, Oid userid) +{ + AclResult aclresult; + + /* Check permissions before acquiring the lock. */ + aclresult = LockTableAclCheck(relid, lockmode, userid); + if (aclresult != ACLCHECK_OK) + { + char *relname = get_rel_name(relid); + + if (!relname) + return false; /* table concurrently dropped, just skip it */ + aclcheck_error(aclresult, get_relkind_objtype(get_rel_relkind(relid)), relname); + } + + /* We have enough rights to lock the relation; do so. */ + if (!nowait) + LockRelationOid(relid, lockmode); + else if (!ConditionalLockRelationOid(relid, lockmode)) + { + /* try to throw error by name; relation could be deleted... */ + char *relname = get_rel_name(relid); + + if (!relname) + return false; /* table concurrently dropped, just skip it */ + ereport(ERROR, + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on relation \"%s\"", + relname))); + } + + /* + * Even if we got the lock, child might have been concurrently + * dropped. If so, we can skip it. + */ + if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid))) + { + /* Release useless lock */ + UnlockRelationOid(relid, lockmode); + return false; + } + return true; +} + +/* + * Apply LOCK TABLE recursively over an inheritance tree or partitioned table * * We use find_inheritance_children not find_all_inheritors to avoid taking * locks far in advance of checking privileges. This means we'll visit - * multiply-inheriting children more than once, but that's no problem. + * multiply-inheriting children more than once, but that's no problem. For + * partitions, we simply loop through each partition checking if it's a + * sub-partitioned table or leaf partition, for the latter we needn't + * recurse, but for the former we must. */ static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, Oid userid) { - List *children; - ListCell *lc; + Relation rel; + + if (!LockSingleTable(reloid, lockmode, nowait, userid)) + return; - children = find_inheritance_children(reloid, NoLock); + /* Lock taken above */ + rel = relation_open(reloid, NoLock); - foreach(lc, children) + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { - Oid childreloid = lfirst_oid(lc); - AclResult aclresult; + PartitionDesc partdesc = RelationGetPartitionDesc(rel); + int nparts = partdesc->nparts; + int i; - /* Check permissions before acquiring the lock. */ - aclresult = LockTableAclCheck(childreloid, lockmode, userid); - if (aclresult != ACLCHECK_OK) + for (i = 0; i < nparts; i++) { - char *relname = get_rel_name(childreloid); + Oid partoid = partdesc->oids[i]; - if (!relname) - continue; /* child concurrently dropped, just skip it */ - aclcheck_error(aclresult, get_relkind_objtype(get_rel_relkind(childreloid)), relname); + if (partdesc->is_leaf[i]) + LockSingleTable(partoid, lockmode, nowait, userid); + else + LockTableRecurse(partoid, lockmode, nowait, userid); } + } - /* We have enough rights to lock the relation; do so. */ - if (!nowait) - LockRelationOid(childreloid, lockmode); - else if (!ConditionalLockRelationOid(childreloid, lockmode)) - { - /* try to throw error by name; relation could be deleted... */ - char *relname = get_rel_name(childreloid); - - if (!relname) - continue; /* child concurrently dropped, just skip it */ - ereport(ERROR, - (errcode(ERRCODE_LOCK_NOT_AVAILABLE), - errmsg("could not obtain lock on relation \"%s\"", - relname))); - } + /* leaf partitions won't have inheritance children, so skip those */ + else if (!OidIsValid(rel->rd_rel->relpartitionparent)) + { + List *children; + ListCell *lc; - /* - * Even if we got the lock, child might have been concurrently - * dropped. If so, we can skip it. - */ - if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(childreloid))) + children = find_inheritance_children(RelationGetRelid(rel), lockmode); + + foreach(lc, children) { - /* Release useless lock */ - UnlockRelationOid(childreloid, lockmode); - continue; - } + Oid childreloid = lfirst_oid(lc); - LockTableRecurse(childreloid, lockmode, nowait, userid); + LockTableRecurse(childreloid, lockmode, nowait, userid); + } } + + relation_close(rel, NoLock); } /* diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index 6f7762a906..4622173e8a 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -28,6 +28,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/objectaddress.h" +#include "catalog/partition.h" #include "catalog/pg_inherits.h" #include "catalog/pg_type.h" #include "catalog/pg_publication.h" @@ -529,8 +530,13 @@ OpenTableList(List *tables) ListCell *child; List *children; - children = find_all_inheritors(myrelid, ShareUpdateExclusiveLock, - NULL); + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + children = get_partition_descendants(myrelid, + ShareUpdateExclusiveLock); + else + children = find_all_inheritors(myrelid, + ShareUpdateExclusiveLock, + NULL); foreach(child, children) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index eb2d33dd86..8a0fcd7ece 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -40,6 +40,7 @@ #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" +#include "catalog/pg_partition.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -477,6 +478,8 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy); static void ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, char strategy); +static void AttachPartition(Relation attachrel, Relation rel, + PartitionBoundSpec *bound); static void CreateInheritance(Relation child_rel, Relation parent_rel); static void RemoveInheritance(Relation child_rel, Relation parent_rel); static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, @@ -492,8 +495,8 @@ static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation rel, static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl); static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl); -static void update_relispartition(Relation classRel, Oid relationId, - bool newval); +static void update_relpartitionparent(Relation classRel, Oid relationId, + Oid newparent); /* ---------------------------------------------------------------- @@ -771,7 +774,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, typaddress); /* Store inheritance information for new rel. */ - StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL); + if (stmt->partbound == NULL) + StoreCatalogInheritance(relationId, inheritOids, false); /* * We must bump the command counter to make the newly-created relation @@ -860,8 +864,11 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, heap_close(defaultRel, NoLock); } + /* Add the pg_partition record */ + AttachPartition(rel, parent, bound); + /* Update the pg_class entry. */ - StorePartitionBound(rel, parent, bound); + MarkRelationPartitioned(rel, parent, bound->is_default); heap_close(parent, NoLock); } @@ -1204,7 +1211,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, struct DropRelationCallbackState *state; char relkind; char expected_relkind; - bool is_partition; + Oid parentoid; Form_pg_class classform; LOCKMODE heap_lockmode; @@ -1243,7 +1250,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, if (!HeapTupleIsValid(tuple)) return; /* concurrently dropped, so nothing to do */ classform = (Form_pg_class) GETSTRUCT(tuple); - is_partition = classform->relispartition; + parentoid = classform->relpartitionparent; /* * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE, @@ -1298,11 +1305,10 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, * parent before its partitions, so we risk deadlock it we do it the other * way around. */ - if (is_partition && relOid != oldRelOid) + if (OidIsValid(parentoid) && relOid != oldRelOid) { - state->partParentOid = get_partition_parent(relOid); - if (OidIsValid(state->partParentOid)) - LockRelationOid(state->partParentOid, AccessExclusiveLock); + state->partParentOid = parentoid; + LockRelationOid(parentoid, AccessExclusiveLock); } } @@ -1356,7 +1362,11 @@ ExecuteTruncate(TruncateStmt *stmt) ListCell *child; List *children; - children = find_all_inheritors(myrelid, AccessExclusiveLock, NULL); + /* partitioned tables cannot have any inheritors */ + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + children = get_partition_descendants(myrelid, AccessExclusiveLock); + else + children = find_all_inheritors(myrelid, AccessExclusiveLock, NULL); foreach(child, children) { @@ -1972,7 +1982,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot inherit from partitioned table \"%s\"", parent->relname))); - if (relation->rd_rel->relispartition && !is_partition) + if (OidIsValid(relation->rd_rel->relpartitionparent) && !is_partition) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot inherit from partition \"%s\"", @@ -2761,28 +2771,45 @@ renameatt_internal(Oid myrelid, ListCell *lo, *li; - /* - * we need the number of parents for each child so that the recursive - * calls to renameatt() can determine whether there are any parents - * outside the inheritance hierarchy being processed. - */ - child_oids = find_all_inheritors(myrelid, AccessExclusiveLock, - &child_numparents); + if (targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + child_oids = get_partition_descendants(myrelid, AccessExclusiveLock); - /* - * 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. - */ - forboth(lo, child_oids, li, child_numparents) + foreach(lo, child_oids) + { + Oid childrelid = lfirst_oid(lo); + + if (childrelid == myrelid) + continue; + /* note we need not recurse again */ + renameatt_internal(childrelid, oldattname, newattname, false, true, 1, behavior); + } + } + else if (!OidIsValid(targetrelation->rd_rel->relpartitionparent)) { - Oid childrelid = lfirst_oid(lo); - int numparents = lfirst_int(li); + /* + * we need the number of parents for each child so that the recursive + * calls to renameatt() can determine whether there are any parents + * outside the inheritance hierarchy being processed. + */ + child_oids = find_all_inheritors(myrelid, AccessExclusiveLock, + &child_numparents); - if (childrelid == myrelid) - continue; - /* note we need not recurse again */ - renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior); + /* + * 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. + */ + forboth(lo, child_oids, li, child_numparents) + { + Oid childrelid = lfirst_oid(lo); + int numparents = lfirst_int(li); + + if (childrelid == myrelid) + continue; + /* note we need not recurse again */ + renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior); + } } } else @@ -5043,12 +5070,17 @@ ATSimpleRecursion(List **wqueue, Relation rel, ListCell *child; List *children; - children = find_all_inheritors(relid, lockmode, NULL); + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + children = get_partition_descendants(relid, lockmode); + else if (!OidIsValid(rel->rd_rel->relpartitionparent)) + children = find_all_inheritors(relid, lockmode, NULL); + else + children = NIL; /* - * 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. + * find_all_inheritors and get_partition_descendants performs the + * recursive search for all descendant tables, so all we have to do is + * process all of the relids in the list that it returns. */ foreach(child, children) { @@ -5057,7 +5089,7 @@ ATSimpleRecursion(List **wqueue, Relation rel, if (childrelid == relid) continue; - /* find_all_inheritors already got lock */ + /* lock already obtained above */ childrel = relation_open(childrelid, NoLock); CheckTableNotInUse(childrel, "ALTER TABLE"); ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode); @@ -5374,7 +5406,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, if (recursing) ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); - if (rel->rd_rel->relispartition && !recursing) + if (OidIsValid(rel->rd_rel->relpartitionparent) && !recursing) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot add column to a partition"))); @@ -5697,7 +5729,18 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, * routines, we have to do this one level of recursion at a time; we can't * use find_all_inheritors to do it in one pass. */ - children = find_inheritance_children(RelationGetRelid(rel), lockmode); + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + PartitionDesc partdesc = RelationGetPartitionDesc(rel); + int i; + children = NIL; + for (i = 0; i < partdesc->nparts; i++) + children = lappend_oid(children, partdesc->oids[i]); + } + else if (OidIsValid(rel->rd_rel->relpartitionparent)) + children = NIL; + else + children = find_inheritance_children(RelationGetRelid(rel), lockmode); /* * If we are told not to recurse, there had better not be any child @@ -5971,10 +6014,13 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode) list_free(indexoidlist); - /* If rel is partition, shouldn't drop NOT NULL if parent has the same */ - if (rel->rd_rel->relispartition) + /* + * Disallow dropping of partitions NOT NULL constraints when the + * constraint is present on the parent. + */ + if (OidIsValid(RelationGetParentRelid(rel))) { - Oid parentId = get_partition_parent(RelationGetRelid(rel)); + Oid parentId = RelationGetParentRelid(rel); Relation parent = heap_open(parentId, AccessShareLock); TupleDesc tupDesc = RelationGetDescr(parent); AttrNumber parent_attnum; @@ -6800,7 +6846,21 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, * routines, we have to do this one level of recursion at a time; we can't * use find_all_inheritors to do it in one pass. */ - children = find_inheritance_children(RelationGetRelid(rel), lockmode); + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + PartitionDesc partdesc = RelationGetPartitionDesc(rel); + int i; + children = NIL; + for (i = 0; i < partdesc->nparts; i++) + { + LockRelationOid(partdesc->oids[i], lockmode); + children = lappend_int(children, partdesc->oids[i]); + } + } + else if (!OidIsValid(rel->rd_rel->relpartitionparent)) + children = find_inheritance_children(RelationGetRelid(rel), lockmode); + else + children = NIL; if (children) { @@ -7251,7 +7311,21 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, * routines, we have to do this one level of recursion at a time; we can't * use find_all_inheritors to do it in one pass. */ - children = find_inheritance_children(RelationGetRelid(rel), lockmode); + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + PartitionDesc partdesc = RelationGetPartitionDesc(rel); + int i; + children = NIL; + for (i = 0; i < partdesc->nparts; i++) + { + LockRelationOid(partdesc->oids[i], lockmode); + children = lappend_int(children, partdesc->oids[i]); + } + } + else if (!OidIsValid(rel->rd_rel->relpartitionparent)) + children = find_inheritance_children(RelationGetRelid(rel), lockmode); + else + children = NIL; /* * Check if ONLY was specified with ALTER TABLE. If so, allow the @@ -8025,8 +8099,14 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse, * shouldn't try to look for it in the children. */ if (!recursing && !con->connoinherit) - children = find_all_inheritors(RelationGetRelid(rel), + { + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + children = get_partition_descendants(RelationGetRelid(rel), + lockmode); + else if (!OidIsValid(rel->rd_rel->relpartitionparent)) + children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL); + } /* * For CHECK constraints, we must ensure that we only mark the @@ -8939,7 +9019,23 @@ ATExecDropConstraint(Relation rel, const char *constrName, * use find_all_inheritors to do it in one pass. */ if (!is_no_inherit_constraint) - children = find_inheritance_children(RelationGetRelid(rel), lockmode); + { + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + PartitionDesc partdesc = RelationGetPartitionDesc(rel); + int i; + children = NIL; + for (i = 0; i < partdesc->nparts; i++) + { + LockRelationOid(partdesc->oids[i], lockmode); + children = lappend_int(children, partdesc->oids[i]); + } + } + else if (!OidIsValid(rel->rd_rel->relpartitionparent)) + children = find_inheritance_children(RelationGetRelid(rel), lockmode); + else + children = NIL; + } else children = NIL; @@ -9230,12 +9326,15 @@ ATPrepAlterColumnType(List **wqueue, ListCell *child; List *children; - children = find_all_inheritors(relid, lockmode, NULL); + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + children = get_partition_descendants(relid, lockmode); + else + children = find_all_inheritors(relid, lockmode, NULL); /* - * 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. + * find_all_inheritors and get_partition_descendants does the + * recursive search of all descendants tables, so all we have to do is + * process all of the relids in the list that it returns. */ foreach(child, children) { @@ -9245,7 +9344,7 @@ ATPrepAlterColumnType(List **wqueue, if (childrelid == relid) continue; - /* find_all_inheritors already got lock */ + /* lock already obtained above */ childrel = relation_open(childrelid, NoLock); CheckTableNotInUse(childrel, "ALTER TABLE"); @@ -11379,7 +11478,7 @@ ATPrepAddInherit(Relation child_rel) (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot change inheritance of typed table"))); - if (child_rel->rd_rel->relispartition) + if (OidIsValid(child_rel->rd_rel->relpartitionparent)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot change inheritance of a partition"))); @@ -11443,7 +11542,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode) parent->relname))); /* Likewise for partitions */ - if (parent_rel->rd_rel->relispartition) + if (OidIsValid(parent_rel->rd_rel->relpartitionparent)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot inherit from a partition"))); @@ -11506,6 +11605,59 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode) return address; } +static void +AttachPartition(Relation attachrel, Relation rel, PartitionBoundSpec *bound) +{ + Datum values[Natts_pg_partition]; + bool nulls[Natts_pg_partition]; + HeapTuple tuple; + Relation partRelation; + Oid attachrelid = RelationGetRelid(attachrel); + Oid partedrelid = RelationGetRelid(rel); + ObjectAddress childobject, + parentobject; + + partRelation = heap_open(PartitionRelationId, RowExclusiveLock); + + /* + * Make the pg_partition entry + */ + values[Anum_pg_partition_partrelid - 1] = ObjectIdGetDatum(attachrelid); + values[Anum_pg_partition_parentrelid - 1] = ObjectIdGetDatum(partedrelid); + values[Anum_pg_partition_partbound - 1] = CStringGetTextDatum(nodeToString(bound)); + + memset(nulls, 0, sizeof(nulls)); + + tuple = heap_form_tuple(RelationGetDescr(partRelation), values, nulls); + + CatalogTupleInsert(partRelation, tuple); + + heap_freetuple(tuple); + + heap_close(partRelation, RowExclusiveLock); + + /* + * Store a dependency too + */ + parentobject.classId = RelationRelationId; + parentobject.objectId = partedrelid; + parentobject.objectSubId = 0; + childobject.classId = RelationRelationId; + childobject.objectId = attachrelid; + childobject.objectSubId = 0; + + recordDependencyOn(&childobject, &parentobject, DEPENDENCY_AUTO); + + /* + * Post creation hook of this partition. Since object_access_hook + * doesn't take multiple object identifiers, we relay oid of parent + * relation using auxiliary_id argument. + */ + InvokeObjectPostAlterHookArg(PartitionRelationId, + attachrelid, 0, + partedrelid, false); +} + /* * CreateInheritance * Catalog manipulation portion of creating inheritance between a child @@ -11922,7 +12074,7 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode) ObjectAddress address; Relation parent_rel; - if (rel->rd_rel->relispartition) + if (OidIsValid(rel->rd_rel->relpartitionparent)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot change inheritance of a partition"))); @@ -11967,7 +12119,7 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode) * coninhcount and conislocal for inherited constraints are adjusted in * exactly the same way. * - * Common to ATExecDropInherit() and ATExecDetachPartition(). + * Used in ATExecDropInherit() */ static void RemoveInheritance(Relation child_rel, Relation parent_rel) @@ -11979,28 +12131,16 @@ RemoveInheritance(Relation child_rel, Relation parent_rel) constraintTuple; List *connames; bool found; - 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; found = DeleteInheritsTuple(RelationGetRelid(child_rel), RelationGetRelid(parent_rel)); if (!found) { - if (child_is_partition) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("relation \"%s\" is not a partition of relation \"%s\"", - RelationGetRelationName(child_rel), - RelationGetRelationName(parent_rel)))); - else - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("relation \"%s\" is not a parent of relation \"%s\"", - RelationGetRelationName(parent_rel), - RelationGetRelationName(child_rel)))); + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + errmsg("relation \"%s\" is not a parent of relation \"%s\"", + RelationGetRelationName(parent_rel), + RelationGetRelationName(child_rel)))); } /* @@ -12119,7 +12259,7 @@ RemoveInheritance(Relation child_rel, Relation parent_rel) drop_parent_dependency(RelationGetRelid(child_rel), RelationRelationId, RelationGetRelid(parent_rel), - child_dependency_type(child_is_partition)); + child_dependency_type(false)); /* * Post alter hook of this inherits. Since object_access_hook doesn't take @@ -14039,7 +14179,6 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) { Relation attachrel, catalog; - List *attachrel_children; List *partConstraint; SysScanDesc scan; ScanKeyData skey; @@ -14072,7 +14211,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) ATSimplePermissions(attachrel, ATT_TABLE | ATT_FOREIGN_TABLE); /* A partition can only have one parent */ - if (attachrel->rd_rel->relispartition) + if (OidIsValid(attachrel->rd_rel->relpartitionparent)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is already a partition", @@ -14084,8 +14223,8 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) errmsg("cannot attach a typed table as partition"))); /* - * Table being attached should not already be part of inheritance; either - * as a child table... + * The table being attached should not be part of any inheritance + * hierarchy as a child or as a parent. */ catalog = heap_open(InheritsRelationId, AccessShareLock); ScanKeyInit(&skey, @@ -14100,15 +14239,13 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) errmsg("cannot attach inheritance child as partition"))); systable_endscan(scan); - /* ...or as a parent table (except the case when it is partitioned) */ ScanKeyInit(&skey, Anum_pg_inherits_inhparent, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(attachrel))); scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL, 1, &skey); - if (HeapTupleIsValid(systable_getnext(scan)) && - attachrel->rd_rel->relkind == RELKIND_RELATION) + if (HeapTupleIsValid(systable_getnext(scan))) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot attach inheritance parent as partition"))); @@ -14129,16 +14266,24 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) * definition is present in all the partitions, we need not scan the * table, nor its partitions. But we cannot risk a deadlock by taking a * weaker lock now and the stronger one only when needed. + * + * The the attachrel is a leaf partition, then it can have no partitions + * so we needn't bother checking this. */ - attachrel_children = find_all_inheritors(RelationGetRelid(attachrel), - AccessExclusiveLock, NULL); - if (list_member_oid(attachrel_children, RelationGetRelid(rel))) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_TABLE), - errmsg("circular inheritance not allowed"), - errdetail("\"%s\" is already a child of \"%s\".", - RelationGetRelationName(rel), - RelationGetRelationName(attachrel)))); + if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + List *attachrel_children; + + attachrel_children = get_partition_descendants(RelationGetRelid(attachrel), + AccessExclusiveLock); + if (list_member_oid(attachrel_children, RelationGetRelid(rel))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_TABLE), + errmsg("circular partitioning is not allowed"), + errdetail("\"%s\" is already a child of \"%s\".", + RelationGetRelationName(rel), + RelationGetRelationName(attachrel)))); + } /* If the parent is permanent, so must be all of its partitions. */ if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP && @@ -14223,8 +14368,11 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) trigger_name, RelationGetRelationName(attachrel)), errdetail("ROW triggers with transition tables are not supported on partitions"))); - /* OK to create inheritance. Rest of the checks performed there */ - CreateInheritance(attachrel, rel); + /* Match up the columns and bump attinhcount as needed */ + MergeAttributesIntoExisting(attachrel, rel); + + /* Match up the constraints and bump coninhcount as needed */ + MergeConstraintsIntoExisting(attachrel, rel); /* * Check that the new partition's bound is valid and does not overlap any @@ -14234,8 +14382,10 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) check_new_partition_bound(RelationGetRelationName(attachrel), rel, cmd->bound); + AttachPartition(attachrel, rel, cmd->bound); + /* Update the pg_class entry. */ - StorePartitionBound(attachrel, rel, cmd->bound); + MarkRelationPartitioned(attachrel, rel, cmd->bound->is_default); /* Ensure there exists a correct set of indexes in the partition. */ AttachPartitionEnsureIndexes(rel, attachrel); @@ -14427,7 +14577,7 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) Oid cldConstrOid = InvalidOid; /* does this index have a parent? if so, can't use it */ - if (attachrelIdxRels[i]->rd_rel->relispartition) + if (OidIsValid(attachrelIdxRels[i]->rd_rel->relpartitionparent)) continue; if (CompareIndexInfo(attachInfos[i], info, @@ -14458,7 +14608,7 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) IndexSetParentIndex(attachrelIdxRels[i], idx); if (OidIsValid(constraintOid)) ConstraintSetParentConstraint(cldConstrOid, constraintOid); - update_relispartition(NULL, cldIdxId, true); + update_relpartitionparent(NULL, cldIdxId, idx); found = true; break; } @@ -14628,18 +14778,20 @@ static ObjectAddress ATExecDetachPartition(Relation rel, RangeVar *name) { Relation partRel, - classRel; - HeapTuple tuple, - newtuple; - Datum new_val[Natts_pg_class]; - bool isnull, - new_null[Natts_pg_class], - new_repl[Natts_pg_class]; + pgclass, + pgattr, + pgpart; + HeapTuple tuple; + HeapTuple parttup; + bool isnull; + SysScanDesc scan; + ScanKeyData key[3]; ObjectAddress address; Oid defaultPartOid; List *indexes; ListCell *cell; + /* * We must lock the default partition, because detaching this partition * will change its partition constraint. @@ -14651,35 +14803,85 @@ ATExecDetachPartition(Relation rel, RangeVar *name) partRel = heap_openrv(name, AccessShareLock); - /* All inheritance related checks are performed within the function */ - RemoveInheritance(partRel, rel); - /* Update pg_class tuple */ - classRel = heap_open(RelationRelationId, RowExclusiveLock); + pgclass = heap_open(RelationRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(RelationGetRelid(partRel))); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", RelationGetRelid(partRel)); - Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition); - (void) SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relpartbound, + if (!OidIsValid(((Form_pg_class) GETSTRUCT(tuple))->relpartitionparent)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + errmsg("relation \"%s\" is not a partition of relation \"%s\"", + RelationGetRelationName(partRel), + RelationGetRelationName(rel)))); + + pgpart = heap_open(PartitionRelationId, RowExclusiveLock); + + parttup = SearchSysCacheCopy1(PARTSRELID, + ObjectIdGetDatum(RelationGetRelid(partRel))); + if (!HeapTupleIsValid(parttup)) + elog(ERROR, "cache lookup failed for relation %u", + RelationGetRelid(partRel)); + /* XXX or use relpartitionparent to check? */ + if (((Form_pg_partition) GETSTRUCT(parttup))->parentrelid != RelationGetRelid(rel)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + errmsg("relation \"%s\" is not a partition of relation \"%s\"", + RelationGetRelationName(partRel), + RelationGetRelationName(rel)))); + + (void) SysCacheGetAttr(PARTSRELID, parttup, Anum_pg_partition_partbound, &isnull); Assert(!isnull); - /* Clear relpartbound and reset relispartition */ - memset(new_val, 0, sizeof(new_val)); - memset(new_null, false, sizeof(new_null)); - memset(new_repl, false, sizeof(new_repl)); - new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0; - new_null[Anum_pg_class_relpartbound - 1] = true; - new_repl[Anum_pg_class_relpartbound - 1] = true; - newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel), - new_val, new_null, new_repl); - - ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false; - CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple); - heap_freetuple(newtuple); + /* unset relpartitionparent */ + ((Form_pg_class) GETSTRUCT(tuple))->relpartitionparent = InvalidOid; + CatalogTupleUpdate(pgclass, &tuple->t_self, tuple); + heap_freetuple(tuple); + + CatalogTupleDelete(pgpart, &parttup->t_self); + heap_freetuple(parttup); + + /* + * Search through child columns looking for ones matching parent partition + */ + pgattr = heap_open(AttributeRelationId, RowExclusiveLock); + ScanKeyInit(&key[0], + Anum_pg_attribute_attrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationGetRelid(partRel))); + scan = systable_beginscan(pgattr, AttributeRelidNumIndexId, + true, NULL, 1, key); + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(tuple); + + /* Ignore if dropped or not inherited */ + if (att->attisdropped) + continue; + if (att->attinhcount <= 0) + continue; + + if (SearchSysCacheExistsAttName(RelationGetRelid(rel), + NameStr(att->attname))) + { + /* Decrement inhcount and possibly set islocal to true */ + HeapTuple copyTuple = heap_copytuple(tuple); + Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple); + + copy_att->attinhcount--; + if (copy_att->attinhcount == 0) + copy_att->attislocal = true; + + CatalogTupleUpdate(pgattr, ©Tuple->t_self, copyTuple); + heap_freetuple(copyTuple); + } + } + systable_endscan(scan); + heap_close(pgattr, RowExclusiveLock); if (OidIsValid(defaultPartOid)) { @@ -14712,10 +14914,11 @@ ATExecDetachPartition(Relation rel, RangeVar *name) idx = index_open(idxid, AccessExclusiveLock); IndexSetParentIndex(idx, InvalidOid); - update_relispartition(classRel, idxid, false); + update_relpartitionparent(pgclass, idxid, InvalidOid); relation_close(idx, AccessExclusiveLock); } - heap_close(classRel, RowExclusiveLock); + heap_close(pgclass, RowExclusiveLock); + heap_close(pgpart, RowExclusiveLock); /* * Invalidate the parent's relcache so that the partition is no longer @@ -14728,6 +14931,20 @@ ATExecDetachPartition(Relation rel, RangeVar *name) /* keep our lock until commit */ heap_close(partRel, NoLock); + drop_parent_dependency(RelationGetRelid(partRel), + RelationRelationId, + RelationGetRelid(rel), + child_dependency_type(true)); + + /* + * Post alter hook of this inherits. Since object_access_hook doesn't take + * multiple object identifiers, we relay oid of parent relation using + * auxiliary_id argument. + */ + InvokeObjectPostAlterHookArg(PartitionRelationId, + RelationGetRelid(partRel), 0, + RelationGetRelid(rel), false); + return address; } @@ -14837,8 +15054,7 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx)); /* Silently do nothing if already in the right state */ - currParent = partIdx->rd_rel->relispartition ? - get_partition_parent(partIdxId) : InvalidOid; + currParent = partIdx->rd_rel->relpartitionparent; if (currParent != RelationGetRelid(parentIdx)) { IndexInfo *childInfo; @@ -14932,7 +15148,7 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx)); if (OidIsValid(constraintOid)) ConstraintSetParentConstraint(cldConstrId, constraintOid); - update_relispartition(NULL, partIdxId, true); + update_relpartitionparent(NULL, partIdxId, RelationGetRelid(parentIdx)); pfree(attmap); @@ -15060,7 +15276,7 @@ validatePartitionedIndex(Relation partedIdx, Relation partedTbl) * If this index is in turn a partition of a larger index, validating it * might cause the parent to become valid also. Try that. */ - if (updated && partedIdx->rd_rel->relispartition) + if (updated && OidIsValid(RelationGetParentRelid(partedIdx))) { Oid parentIdxId, parentTblId; @@ -15070,8 +15286,8 @@ validatePartitionedIndex(Relation partedIdx, Relation partedTbl) /* make sure we see the validation we just did */ CommandCounterIncrement(); - parentIdxId = get_partition_parent(RelationGetRelid(partedIdx)); - parentTblId = get_partition_parent(RelationGetRelid(partedTbl)); + parentIdxId = RelationGetParentRelid(partedIdx); + parentTblId = RelationGetParentRelid(partedTbl); parentIdx = relation_open(parentIdxId, AccessExclusiveLock); parentTbl = relation_open(parentTblId, AccessExclusiveLock); Assert(!parentIdx->rd_index->indisvalid); @@ -15084,13 +15300,14 @@ validatePartitionedIndex(Relation partedIdx, Relation partedTbl) } /* - * Update the relispartition flag of the given relation to the given value. + * Update the relpartitionparent value of the given relation to the given + * value. * * classRel is the pg_class relation, already open and suitably locked. * It can be passed as NULL, in which case it's opened and closed locally. */ static void -update_relispartition(Relation classRel, Oid relationId, bool newval) +update_relpartitionparent(Relation classRel, Oid relationId, Oid newparent) { HeapTuple tup; HeapTuple newtup; @@ -15106,7 +15323,7 @@ update_relispartition(Relation classRel, Oid relationId, bool newval) tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId)); newtup = heap_copytuple(tup); classForm = (Form_pg_class) GETSTRUCT(newtup); - classForm->relispartition = newval; + classForm->relpartitionparent = newparent; CatalogTupleUpdate(classRel, &tup->t_self, newtup); heap_freetuple(newtup); ReleaseSysCache(tup); diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 2436692eb8..8c712b97cc 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -363,8 +363,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, partition_recurse = !isInternal && stmt->row && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE; if (partition_recurse) - list_free(find_all_inheritors(RelationGetRelid(rel), - ShareRowExclusiveLock, NULL)); + list_free(get_partition_descendants(RelationGetRelid(rel), + ShareRowExclusiveLock)); /* Compute tgtype */ TRIGGER_CLEAR_TYPE(tgtype); @@ -453,14 +453,14 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, * tuples each child should see. See also the prohibitions in * ATExecAttachPartition() and ATExecAddInherit(). */ - if (TRIGGER_FOR_ROW(tgtype) && has_superclass(rel->rd_id)) + if (TRIGGER_FOR_ROW(tgtype)) { /* Use appropriate error message. */ - if (rel->rd_rel->relispartition) + if (OidIsValid(rel->rd_rel->relpartitionparent)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("ROW triggers with transition tables are not supported on partitions"))); - else + else if (has_superclass(rel->rd_id)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("ROW triggers with transition tables are not supported on inheritance children"))); diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index ee32fe8871..6ec8cee33f 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -31,6 +31,7 @@ #include "access/transam.h" #include "access/xact.h" #include "catalog/namespace.h" +#include "catalog/partition.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" @@ -485,7 +486,7 @@ expand_vacuum_rel(VacuumRelation *vrel) */ if (include_parts) { - List *part_oids = find_all_inheritors(relid, NoLock, NULL); + List *part_oids = get_partition_descendants(relid, NoLock); ListCell *part_lc; foreach(part_lc, part_oids) diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 7849e04bdb..448526d9f2 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -14,7 +14,6 @@ #include "postgres.h" #include "catalog/partition.h" -#include "catalog/pg_inherits.h" #include "catalog/pg_type.h" #include "executor/execPartition.h" #include "executor/executor.h" @@ -80,7 +79,7 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel) ModifyTable *node = mtstate ? (ModifyTable *) mtstate->ps.plan : NULL; /* Lock all the partitions. */ - (void) find_all_inheritors(RelationGetRelid(rel), RowExclusiveLock, NULL); + (void) get_partition_descendants(RelationGetRelid(rel), RowExclusiveLock); /* * Here we attempt to expend as little effort as possible in setting up diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 690b6bbab7..3896617760 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -99,9 +99,11 @@ static List *generate_append_tlist(List *colTypes, List *colCollations, List *input_tlists, List *refnames_tlist); static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist); +static void expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *rte, + Index rti); static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti); -static void expand_partitioned_rtentry(PlannerInfo *root, +static void expand_partitioned_rtentry_recurse(PlannerInfo *root, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, PlanRowMark *top_parentrc, LOCKMODE lockmode, @@ -1487,7 +1489,20 @@ expand_inherited_tables(PlannerInfo *root) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl); - expand_inherited_rtentry(root, rte, rti); + if (rte->relkind == RELKIND_PARTITIONED_TABLE) + expand_partitioned_rtentry(root, rte, rti); + else if (OidIsValid(rte->relid)) + { + /* + * Partitioned tables cannot be part of an inheritance hierarchy, + * so we needn't bother trying to expand those. + */ + if (OidIsValid(get_partition_parent(rte->relid))) + rte->inh = false; + else + expand_inherited_rtentry(root, rte, rti); + } + /* no need to attempt to expand anything else */ rl = lnext(rl); } } @@ -1522,6 +1537,11 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) LOCKMODE lockmode; List *inhOIDs; ListCell *l; + List *appinfos = NIL; + RangeTblEntry *childrte; + Index childRTindex; + + Assert(rte->relkind != RELKIND_PARTITIONED_TABLE); /* Does RT entry allow inheritance? */ if (!rte->inh) @@ -1534,7 +1554,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) } /* Fast path for common case of childless table */ parentOID = rte->relid; - if (!has_subclass(parentOID)) + if (rte->relkind != RELKIND_PARTITIONED_TABLE && !has_subclass(parentOID)) { /* Clear flag before returning */ rte->inh = false; @@ -1591,93 +1611,137 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) */ oldrelation = heap_open(parentOID, NoLock); - /* Scan the inheritance set and expand it */ - if (RelationGetPartitionDesc(oldrelation) != NULL) + Assert(!RelationGetPartitionDesc(oldrelation)); + + /* + * This table has no partitions. Expand any plain inheritance + * children in the order the OIDs were returned by + * find_all_inheritors. + */ + foreach(l, inhOIDs) { - Assert(rte->relkind == RELKIND_PARTITIONED_TABLE); + Oid childOID = lfirst_oid(l); + Relation newrelation; + + /* Open rel if needed; we already have required locks */ + if (childOID != parentOID) + newrelation = heap_open(childOID, NoLock); + else + newrelation = oldrelation; /* - * If this table has partitions, recursively expand them in the order - * in which they appear in the PartitionDesc. While at it, also - * extract the partition key columns of all the partitioned tables. - */ - expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc, - lockmode, &root->append_rel_list); + * 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)) + { + heap_close(newrelation, lockmode); + continue; + } + + expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc, + newrelation, + &appinfos, &childrte, + &childRTindex); + + /* Close child relations, but keep locks */ + if (childOID != parentOID) + heap_close(newrelation, NoLock); } + + /* + * If all the children were temp tables, pretend it's a + * non-inheritance situation; we don't need Append node in that case. + * The duplicate RTE we added for the parent table is harmless, so we + * don't bother to get rid of it; ditto for the useless PlanRowMark + * node. + */ + if (list_length(appinfos) < 2) + rte->inh = false; else - { - List *appinfos = NIL; - RangeTblEntry *childrte; - Index childRTindex; + root->append_rel_list = list_concat(root->append_rel_list, + appinfos); - /* - * This table has no partitions. Expand any plain inheritance - * children in the order the OIDs were returned by - * find_all_inheritors. - */ - foreach(l, inhOIDs) - { - Oid childOID = lfirst_oid(l); - Relation newrelation; + heap_close(oldrelation, NoLock); +} - /* Open rel if needed; we already have required locks */ - if (childOID != parentOID) - newrelation = heap_open(childOID, NoLock); - else - newrelation = oldrelation; +static void +expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) +{ + Query *parse = root->parse; + PlanRowMark *partrc; + Relation partrel; + LOCKMODE lockmode; + PartitionDesc partdesc; - /* - * 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)) - { - heap_close(newrelation, lockmode); - continue; - } + Assert(rte->relkind == RELKIND_PARTITIONED_TABLE); + + /* Support legacy ONLY syntax for partitions */ + if (!rte->inh) + return; - expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc, - newrelation, - &appinfos, &childrte, - &childRTindex); + /* + * The rewriter should already have obtained an appropriate lock on each + * relation named in the query. However, for each child relation we add + * to the query, we must obtain an appropriate lock, because this will be + * the first use of those relations in the parse/rewrite/plan pipeline. + * + * If the parent relation is the query's result relation, then we need + * RowExclusiveLock. Otherwise, if it's accessed FOR UPDATE/SHARE, we + * need RowShareLock; otherwise AccessShareLock. We can't just grab + * AccessShareLock because then the executor would be trying to upgrade + * the lock, leading to possible deadlocks. (This code should match the + * parser and rewriter.) + */ + partrc = get_plan_rowmark(root->rowMarks, rti); + if (rti == parse->resultRelation) + lockmode = RowExclusiveLock; + else if (partrc && RowMarkRequiresRowShareLock(partrc->markType)) + lockmode = RowShareLock; + else + lockmode = AccessShareLock; - /* Close child relations, but keep locks */ - if (childOID != parentOID) - heap_close(newrelation, NoLock); - } + /* + * If parent relation is selected FOR UPDATE/SHARE, we need to mark its + * PlanRowMark as isParent = true, and generate a new PlanRowMark for each + * child. + */ + if (partrc) + partrc->isParent = true; - /* - * If all the children were temp tables, pretend it's a - * non-inheritance situation; we don't need Append node in that case. - * The duplicate RTE we added for the parent table is harmless, so we - * don't bother to get rid of it; ditto for the useless PlanRowMark - * node. - */ - if (list_length(appinfos) < 2) - rte->inh = false; - else - root->append_rel_list = list_concat(root->append_rel_list, - appinfos); + /* + * Must open the partitioned relation to examine its tupdesc. We need not + * lock it; we assume the rewriter already did. + */ + partrel = heap_open(rte->relid, NoLock); - } + partdesc = RelationGetPartitionDesc(partrel); - heap_close(oldrelation, NoLock); + /* + * If this table has partitions, recursively expand them in the order in + * which they appear in the PartitionDesc. While at it, also extract the + * partition key columns of all the partitioned tables. + */ + expand_partitioned_rtentry_recurse(root, rte, rti, partrel, partrc, + lockmode, &root->append_rel_list); + + heap_close(partrel, NoLock); } /* - * expand_partitioned_rtentry + * expand_partitioned_rtentry_recurse * Recursively expand an RTE for a partitioned table. * * Note that RelationGetPartitionDispatchInfo will expand partitions in the * same order as this code. */ static void -expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, - Index parentRTindex, Relation parentrel, - PlanRowMark *top_parentrc, LOCKMODE lockmode, - List **appinfos) +expand_partitioned_rtentry_recurse(PlannerInfo *root, RangeTblEntry *parentrte, + Index parentRTindex, Relation parentrel, + PlanRowMark *top_parentrc, LOCKMODE lockmode, + List **appinfos) { int i; RangeTblEntry *childrte; @@ -1689,8 +1753,6 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned table should always have a partition descriptor. */ Assert(partdesc); - Assert(parentrte->inh); - /* * Note down whether any partition key cols are being updated. Though it's * the root partitioned table's updatedCols we are interested in, we @@ -1722,16 +1784,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, Oid childOID = partdesc->oids[i]; Relation childrel; - /* Open rel; we already have required locks */ - childrel = heap_open(childOID, NoLock); - - /* - * Temporary partitions belonging to other sessions should have been - * disallowed at definition, but for paranoia's sake, let's double - * check. - */ - if (RELATION_IS_OTHER_TEMP(childrel)) - elog(ERROR, "temporary relation from another session found as partition"); + childrel = heap_open(childOID, lockmode); expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel, top_parentrc, childrel, @@ -1739,7 +1792,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - expand_partitioned_rtentry(root, childrte, childRTindex, + expand_partitioned_rtentry_recurse(root, childrte, childRTindex, childrel, top_parentrc, lockmode, appinfos); diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c index 9015a05d32..360d4e08cd 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -15,6 +15,7 @@ #include "catalog/partition.h" #include "catalog/pg_inherits.h" +#include "catalog/pg_partition.h" #include "catalog/pg_type.h" #include "commands/tablecmds.h" #include "executor/executor.h" @@ -628,8 +629,8 @@ check_default_partition_contents(Relation parent, Relation default_rel, * 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); + all_parts = get_partition_descendants(RelationGetRelid(default_rel), + AccessExclusiveLock); else all_parts = list_make1_oid(RelationGetRelid(default_rel)); @@ -1634,12 +1635,12 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool isnull; PartitionBoundSpec *bspec; - tuple = SearchSysCache1(RELOID, inhrelid); + tuple = SearchSysCache1(PARTSRELID, inhrelid); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", inhrelid); - datum = SysCacheGetAttr(RELOID, tuple, - Anum_pg_class_relpartbound, + datum = SysCacheGetAttr(PARTSRELID, tuple, + Anum_pg_partition_partbound, &isnull); Assert(!isnull); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index d81a2ea342..3c5e8e75d6 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -428,7 +428,7 @@ DefineQueryRewrite(const char *rulename, errmsg("cannot convert partitioned table \"%s\" to a view", RelationGetRelationName(event_relation)))); - if (event_relation->rd_rel->relispartition) + if (OidIsValid(event_relation->rd_rel->relpartitionparent)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot convert partition \"%s\" to a view", diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index b5804f64ad..51e0e789f3 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -25,6 +25,7 @@ #include "catalog/namespace.h" #include "catalog/pg_inherits.h" #include "catalog/toasting.h" +#include "catalog/partition.h" #include "commands/alter.h" #include "commands/async.h" #include "commands/cluster.h" @@ -1323,10 +1324,10 @@ ProcessUtilitySlow(ParseState *pstate, get_rel_relkind(relid) == RELKIND_PARTITIONED_TABLE) { ListCell *lc; - List *inheritors = NIL; + List *partoids = NIL; - inheritors = find_all_inheritors(relid, lockmode, NULL); - foreach(lc, inheritors) + partoids = get_partition_descendants(relid, lockmode); + foreach(lc, partoids) { char relkind = get_rel_relkind(lfirst_oid(lc)); @@ -1340,7 +1341,7 @@ ProcessUtilitySlow(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", stmt->relation->relname))); } - list_free(inheritors); + list_free(partoids); } /* Run parse analysis ... */ diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c index 82acfeb460..51a21c4793 100644 --- a/src/backend/utils/cache/partcache.c +++ b/src/backend/utils/cache/partcache.c @@ -18,9 +18,10 @@ #include "access/heapam.h" #include "access/htup_details.h" #include "access/nbtree.h" +#include "catalog/indexing.h" #include "catalog/partition.h" -#include "catalog/pg_inherits.h" #include "catalog/pg_opclass.h" +#include "catalog/pg_partition.h" #include "catalog/pg_partitioned_table.h" #include "miscadmin.h" #include "nodes/makefuncs.h" @@ -30,6 +31,7 @@ #include "partitioning/partbounds.h" #include "utils/builtins.h" #include "utils/datum.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/partcache.h" @@ -260,8 +262,7 @@ RelationBuildPartitionKey(Relation relation) void RelationBuildPartitionDesc(Relation rel) { - List *inhoids, - *partoids; + List *partoids; Oid *oids = NULL; List *boundspecs = NIL; ListCell *cell; @@ -270,6 +271,10 @@ RelationBuildPartitionDesc(Relation rel) PartitionKey key = RelationGetPartitionKey(rel); PartitionDesc result; MemoryContext oldcxt; + SysScanDesc scan; + ScanKeyData scankey[1]; + Relation pgpart; + HeapTuple partTuple; int ndatums = 0; int default_index = -1; @@ -285,39 +290,47 @@ RelationBuildPartitionDesc(Relation rel) PartitionRangeBound **rbounds = NULL; /* Get partition oids from pg_inherits */ - inhoids = find_inheritance_children(RelationGetRelid(rel), NoLock); + pgpart = heap_open(PartitionRelationId, AccessShareLock); + + ScanKeyInit(&scankey[0], + Anum_pg_partition_parentrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationGetRelid(rel))); + + scan = systable_beginscan(pgpart, PartitionParentrelidIndexId, true, + NULL, 1, scankey); /* Collect bound spec nodes in a list */ - i = 0; partoids = NIL; - foreach(cell, inhoids) + while ((partTuple = systable_getnext(scan)) != NULL) { - Oid inhrelid = lfirst_oid(cell); + Oid partrelid = ((Form_pg_partition) GETSTRUCT(partTuple))->partrelid; HeapTuple tuple; Datum datum; bool isnull; Node *boundspec; - tuple = SearchSysCache1(RELOID, inhrelid); + tuple = SearchSysCache1(RELOID, partrelid); if (!HeapTupleIsValid(tuple)) - elog(ERROR, "cache lookup failed for relation %u", inhrelid); + elog(ERROR, "cache lookup failed for relation %u", partrelid); /* * It is possible that the pg_class tuple of a partition has not been * updated yet to set its relpartbound field. The only case where * this happens is when we open the parent relation to check using its * partition descriptor that a new partition's bound does not overlap - * some existing partition. + * some existing partition. XXX needs updated! Figure out if this is + * still required. */ - if (!((Form_pg_class) GETSTRUCT(tuple))->relispartition) + if (!OidIsValid(((Form_pg_class) GETSTRUCT(tuple))->relpartitionparent)) { ReleaseSysCache(tuple); continue; } - datum = SysCacheGetAttr(RELOID, tuple, - Anum_pg_class_relpartbound, - &isnull); + datum = fastgetattr(partTuple, Anum_pg_partition_partbound, + pgpart->rd_att, &isnull); + Assert(!isnull); boundspec = (Node *) stringToNode(TextDatumGetCString(datum)); @@ -331,16 +344,20 @@ RelationBuildPartitionDesc(Relation rel) Oid partdefid; partdefid = get_default_partition_oid(RelationGetRelid(rel)); - if (partdefid != inhrelid) + if (partdefid != partrelid) elog(ERROR, "expected partdefid %u, but got %u", - inhrelid, partdefid); + partrelid, partdefid); } boundspecs = lappend(boundspecs, boundspec); - partoids = lappend_oid(partoids, inhrelid); + partoids = lappend_oid(partoids, partrelid); ReleaseSysCache(tuple); } + systable_endscan(scan); + + heap_close(pgpart, AccessShareLock); + nparts = list_length(partoids); if (nparts > 0) @@ -808,7 +825,7 @@ List * RelationGetPartitionQual(Relation rel) { /* Quick exit */ - if (!rel->rd_rel->relispartition) + if (!OidIsValid(rel->rd_rel->relpartitionparent)) return NIL; return generate_partition_qual(rel); @@ -829,7 +846,7 @@ get_partition_qual_relid(Oid relid) List *and_args; /* Do the work only if this relation is a partition. */ - if (rel->rd_rel->relispartition) + if (OidIsValid(rel->rd_rel->relpartitionparent)) { and_args = generate_partition_qual(rel); @@ -880,20 +897,19 @@ generate_partition_qual(Relation rel) return copyObject(rel->rd_partcheck); /* Grab at least an AccessShareLock on the parent table */ - parent = heap_open(get_partition_parent(RelationGetRelid(rel)), - AccessShareLock); + parent = heap_open(RelationGetParentRelid(rel), AccessShareLock); - /* Get pg_class.relpartbound */ - tuple = SearchSysCache1(RELOID, RelationGetRelid(rel)); + /* Get pg_partition.partbound */ + tuple = SearchSysCache1(PARTSRELID, RelationGetRelid(rel)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", RelationGetRelid(rel)); - boundDatum = SysCacheGetAttr(RELOID, tuple, - Anum_pg_class_relpartbound, + boundDatum = SysCacheGetAttr(PARTSRELID, tuple, + Anum_pg_partition_partbound, &isnull); if (isnull) /* should not happen */ - elog(ERROR, "relation \"%s\" has relpartbound = null", + elog(ERROR, "relation \"%s\" has partbound = null", RelationGetRelationName(rel)); bound = castNode(PartitionBoundSpec, stringToNode(TextDatumGetCString(boundDatum))); @@ -902,7 +918,7 @@ generate_partition_qual(Relation rel) my_qual = get_qual_from_partbound(rel, parent, bound); /* Add the parent's quals to the list (if any) */ - if (parent->rd_rel->relispartition) + if (OidIsValid(parent->rd_rel->relpartitionparent)) result = list_concat(generate_partition_qual(parent), my_qual); else result = my_qual; diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index 2b381782a3..8ad9d2fa3b 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -48,6 +48,7 @@ #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" #include "catalog/pg_opfamily.h" +#include "catalog/pg_partition.h" #include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_publication.h" @@ -584,6 +585,17 @@ static const struct cachedesc cacheinfo[] = { }, 32 }, + {PartitionRelationId, /* PARTSRELID */ + PartitionRelidIndexId, + 1, + { + Anum_pg_partition_partrelid, + 0, + 0, + 0 + }, + 32 + }, {ProcedureRelationId, /* PROCNAMEARGSNSP */ ProcedureNameArgsNspIndexId, 3, @@ -1238,7 +1250,7 @@ GetSysCacheOid(int cacheId, /* * SearchSysCacheAttName - * + *subqu * This routine is equivalent to SearchSysCache on the ATTNAME cache, * except that it will return NULL if the found attribute is marked * attisdropped. This is convenient for callers that want to act as diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 0d147cb08d..4f6c68e844 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -1022,6 +1022,28 @@ findParentsByOid(TableInfo *self, j; int numParents; + /* + * For PG12 and above pg_class has a relpartitionparent column that allows + * us to determine the parent without looking at the inheritance data. + */ + if (self->partitionparent != 0) + { + self->parents = (TableInfo **) pg_malloc(sizeof(TableInfo *)); + + self->parents[0] = findTableByOid(self->partitionparent); + + if (self->parents[0] == NULL) + { + write_msg(NULL, "failed sanity check, parent OID %u of table \"%s\" (OID %u) not found\n", + self->partitionparent, + self->dobj.name, + oid); + exit_nicely(1); + } + self->numParents = 1; + return; + } + numParents = 0; for (i = 0; i < numInherits; i++) { diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 20e8aedb19..af7e2bd813 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -5878,6 +5878,7 @@ getTables(Archive *fout, int *numTables) int i_changed_acl; int i_partkeydef; int i_ispartition; + int i_partitionparent; int i_partbound; /* @@ -5904,7 +5905,9 @@ getTables(Archive *fout, int *numTables) { char *partkeydef = "NULL"; char *ispartition = "false"; + char *partitionparent = "0"; char *partbound = "NULL"; + char *partjoin = ""; PQExpBuffer acl_subquery = createPQExpBuffer(); PQExpBuffer racl_subquery = createPQExpBuffer(); @@ -5920,14 +5923,23 @@ getTables(Archive *fout, int *numTables) * Collect the information about any partitioned tables, which were * added in PG10. */ - - if (fout->remoteVersion >= 100000) + /* XXX how much do we add here before we split this out completely? */ + if (fout->remoteVersion >= 120000) + { + partjoin = "LEFT JOIN pg_partition p ON (c.oid = p.partrelid) "; + partkeydef = "pg_get_partkeydef(c.oid)"; + ispartition = "c.relpartitionparent <> 0"; + partitionparent = "c.relpartitionparent"; + partbound = "pg_get_expr(p.partbound, c.oid)"; + } + else if (fout->remoteVersion >= 100000) { partkeydef = "pg_get_partkeydef(c.oid)"; ispartition = "c.relispartition"; partbound = "pg_get_expr(c.relpartbound, c.oid)"; } + /* * Left join to pick up dependency info linking sequences to their * owning column, if any (note this dependency is AUTO as of 8.2) @@ -5982,6 +5994,7 @@ getTables(Archive *fout, int *numTables) "AS changed_acl, " "%s AS partkeydef, " "%s AS ispartition, " + "%s AS partitionparent, " "%s AS partbound " "FROM pg_class c " "LEFT JOIN pg_depend d ON " @@ -5994,6 +6007,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " + "%s" "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, @@ -6008,8 +6022,10 @@ getTables(Archive *fout, int *numTables) attinitracl_subquery->data, partkeydef, ispartition, + partitionparent, partbound, RELKIND_SEQUENCE, + partjoin, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, @@ -6057,6 +6073,7 @@ getTables(Archive *fout, int *numTables) "NULL AS changed_acl, " "NULL AS partkeydef, " "false AS ispartition, " + "0 AS partitionparent, " "NULL AS partbound " "FROM pg_class c " "LEFT JOIN pg_depend d ON " @@ -6106,6 +6123,7 @@ getTables(Archive *fout, int *numTables) "NULL AS changed_acl, " "NULL AS partkeydef, " "false AS ispartition, " + "0 AS partitionparent, " "NULL AS partbound " "FROM pg_class c " "LEFT JOIN pg_depend d ON " @@ -6155,6 +6173,7 @@ getTables(Archive *fout, int *numTables) "NULL AS changed_acl, " "NULL AS partkeydef, " "false AS ispartition, " + "0 AS partitionparent, " "NULL AS partbound " "FROM pg_class c " "LEFT JOIN pg_depend d ON " @@ -6202,6 +6221,7 @@ getTables(Archive *fout, int *numTables) "NULL AS changed_acl, " "NULL AS partkeydef, " "false AS ispartition, " + "0 AS partitionparent, " "NULL AS partbound " "FROM pg_class c " "LEFT JOIN pg_depend d ON " @@ -6249,6 +6269,7 @@ getTables(Archive *fout, int *numTables) "NULL AS changed_acl, " "NULL AS partkeydef, " "false AS ispartition, " + "0 AS partitionparent, " "NULL AS partbound " "FROM pg_class c " "LEFT JOIN pg_depend d ON " @@ -6295,6 +6316,7 @@ getTables(Archive *fout, int *numTables) "NULL AS changed_acl, " "NULL AS partkeydef, " "false AS ispartition, " + "0 AS partitionparent, " "NULL AS partbound " "FROM pg_class c " "LEFT JOIN pg_depend d ON " @@ -6341,6 +6363,7 @@ getTables(Archive *fout, int *numTables) "NULL AS changed_acl, " "NULL AS partkeydef, " "false AS ispartition, " + "0 AS partitionparent, " "NULL AS partbound " "FROM pg_class c " "LEFT JOIN pg_depend d ON " @@ -6386,6 +6409,7 @@ getTables(Archive *fout, int *numTables) "NULL AS changed_acl, " "NULL AS partkeydef, " "false AS ispartition, " + "0 AS partitionparent, " "NULL AS partbound " "FROM pg_class c " "LEFT JOIN pg_depend d ON " @@ -6455,6 +6479,7 @@ getTables(Archive *fout, int *numTables) i_changed_acl = PQfnumber(res, "changed_acl"); i_partkeydef = PQfnumber(res, "partkeydef"); i_ispartition = PQfnumber(res, "ispartition"); + i_partitionparent = PQfnumber(res, "partitionparent"); i_partbound = PQfnumber(res, "partbound"); if (dopt->lockWaitTimeout) @@ -6561,6 +6586,7 @@ getTables(Archive *fout, int *numTables) /* Partition key string or NULL */ tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0); + tblinfo[i].partitionparent = atooid(PQgetvalue(res, i, i_partitionparent)); tblinfo[i].partbound = pg_strdup(PQgetvalue(res, i, i_partbound)); /* diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 1448005f30..e5155f760e 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -296,6 +296,7 @@ typedef struct _tableInfo bool dummy_view; /* view's real definition must be postponed */ bool postponed_def; /* matview must be postponed into post-data */ bool ispartition; /* is table a partition? */ + Oid partitionparent; /* owning partitioned table, or 0 */ /* * These fields are computed only if we decide the table is interesting diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 80d8338b96..7fde1114a0 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -2044,18 +2044,36 @@ describeOneTableDetails(const char *schemaname, char *partdef; char *partconstraintdef = NULL; - printfPQExpBuffer(&buf, - "SELECT inhparent::pg_catalog.regclass,\n" - " pg_catalog.pg_get_expr(c.relpartbound, inhrelid)"); - /* If verbose, also request the partition constraint definition */ - if (verbose) + if (pset.sversion >= 120000) + { + printfPQExpBuffer(&buf, + "SELECT p.parentrelid::pg_catalog.regclass,\n" + " pg_catalog.pg_get_expr(p.partbound, p.partrelid)"); + /* If verbose, also request the partition constraint definition */ + if (verbose) + appendPQExpBuffer(&buf, + ",\n pg_catalog.pg_get_partition_constraintdef(p.partrelid)"); appendPQExpBuffer(&buf, - ",\n pg_catalog.pg_get_partition_constraintdef(inhrelid)"); - appendPQExpBuffer(&buf, - "\nFROM pg_catalog.pg_class c" - " JOIN pg_catalog.pg_inherits i" - " ON c.oid = inhrelid" - "\nWHERE c.oid = '%s' AND c.relispartition;", oid); + "\nFROM pg_catalog.pg_class c" + " JOIN pg_catalog.pg_partition p" + " ON c.oid = p.partrelid" + "\nWHERE c.oid = '%s' AND c.relpartitionparent <> 0;", oid); + } + else + { + printfPQExpBuffer(&buf, + "SELECT inhparent::pg_catalog.regclass,\n" + " pg_catalog.pg_get_expr(c.relpartbound, inhrelid)"); + /* If verbose, also request the partition constraint definition */ + if (verbose) + appendPQExpBuffer(&buf, + ",\n pg_catalog.pg_get_partition_constraintdef(inhrelid)"); + appendPQExpBuffer(&buf, + "\nFROM pg_catalog.pg_class c" + " JOIN pg_catalog.pg_inherits i" + " ON c.oid = inhrelid" + "\nWHERE c.oid = '%s' AND c.relispartition;", oid); + } result = PSQLexec(buf.data); if (!result) goto error_return; @@ -2984,7 +3002,25 @@ describeOneTableDetails(const char *schemaname, } /* print child tables (with additional info if partitions) */ - if (pset.sversion >= 100000) + if (pset.sversion >= 120000) + { + if (tableinfo.relkind == RELKIND_PARTITIONED_TABLE) + printfPQExpBuffer(&buf, + "SELECT c.oid::pg_catalog.regclass," + " pg_catalog.pg_get_expr(p.partbound, p.partrelid)," + " c.relkind" + " FROM pg_catalog.pg_class c, pg_catalog.pg_partition p" + " WHERE c.oid=p.partrelid AND p.parentrelid = '%s'" + " ORDER BY pg_catalog.pg_get_expr(p.partbound, p.partrelid) = 'DEFAULT'," + " c.oid::pg_catalog.regclass::pg_catalog.text;", oid); + else + printfPQExpBuffer(&buf, + "SELECT c.oid::pg_catalog.regclass,NULL,c.relkind" + " FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i" + " WHERE c.oid=i.inhrelid AND i.inhparent = '%s'" + " ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", oid); + } + else if (pset.sversion >= 100000) printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass," " pg_catalog.pg_get_expr(c.relpartbound, c.oid)," diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index bb696f8ee9..b8dbc5b539 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -1134,6 +1134,7 @@ static const SchemaQuery Query_for_list_of_statistics = { " SELECT 'DEFAULT' ) ss "\ " WHERE pg_catalog.substring(name,1,%%d)='%%s'" +/* XXX fix this */ /* the silly-looking length condition is just to eat up the current word */ #define Query_for_partition_of_table \ "SELECT pg_catalog.quote_ident(c2.relname) "\ diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index c5e40ff017..d66a2f0b0d 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -149,7 +149,7 @@ extern void StorePartitionKey(Relation rel, Oid *partopclass, Oid *partcollation); extern void RemovePartitionKeyByRelId(Oid relid); -extern void StorePartitionBound(Relation rel, Relation parent, - PartitionBoundSpec *bound); +extern void MarkRelationPartitioned(Relation rel, Relation parent, + bool is_default); #endif /* HEAP_H */ diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 24915824ca..a42c7041d5 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -339,6 +339,12 @@ DECLARE_UNIQUE_INDEX(pg_replication_origin_roname_index, 6002, on pg_replication DECLARE_UNIQUE_INDEX(pg_partitioned_table_partrelid_index, 3351, on pg_partitioned_table using btree(partrelid oid_ops)); #define PartitionedRelidIndexId 3351 +DECLARE_UNIQUE_INDEX(pg_partition_partrelid_index, 3998, on pg_partition using btree(partrelid oid_ops)); +#define PartitionRelidIndexId 3998 + +DECLARE_INDEX(pg_partition_parentrelid_index, 4001, on pg_partition using btree(parentrelid oid_ops)); +#define PartitionParentrelidIndexId 4001 + DECLARE_UNIQUE_INDEX(pg_publication_oid_index, 6110, on pg_publication using btree(oid oid_ops)); #define PublicationObjectIndexId 6110 diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index 4b3b5ae770..8cbbd227f4 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -15,6 +15,7 @@ #include "fmgr.h" #include "partitioning/partdefs.h" +#include "storage/lock.h" #include "utils/relcache.h" /* Seed for the extended hash function */ @@ -35,6 +36,7 @@ typedef struct PartitionDescData extern Oid get_partition_parent(Oid relid); extern List *get_partition_ancestors(Oid relid); +extern List *get_partition_descendants(Oid relid, LOCKMODE lockmode); extern List *map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel, bool *found_whole_row); diff --git a/src/include/catalog/pg_class.dat b/src/include/catalog/pg_class.dat index 9fffdef379..d0c5ab466e 100644 --- a/src/include/catalog/pg_class.dat +++ b/src/include/catalog/pg_class.dat @@ -28,9 +28,9 @@ relpersistence => 'p', relkind => 'r', relnatts => '30', relchecks => '0', relhasoids => 't', relhasrules => 'f', relhastriggers => 'f', relhassubclass => 'f', relrowsecurity => 'f', relforcerowsecurity => 'f', - relispopulated => 't', relreplident => 'n', relispartition => 'f', + relispopulated => 't', relreplident => 'n', relpartitionparent => '0', relrewrite => '0', relfrozenxid => '3', relminmxid => '1', relacl => '_null_', - reloptions => '_null_', relpartbound => '_null_' }, + reloptions => '_null_' }, { oid => '1249', relname => 'pg_attribute', relnamespace => 'PGNSP', reltype => '75', reloftype => '0', relowner => 'PGUID', relam => '0', relfilenode => '0', @@ -39,9 +39,9 @@ relpersistence => 'p', relkind => 'r', relnatts => '24', relchecks => '0', relhasoids => 'f', relhasrules => 'f', relhastriggers => 'f', relhassubclass => 'f', relrowsecurity => 'f', relforcerowsecurity => 'f', - relispopulated => 't', relreplident => 'n', relispartition => 'f', + relispopulated => 't', relreplident => 'n', relpartitionparent => '0', relrewrite => '0', relfrozenxid => '3', relminmxid => '1', relacl => '_null_', - reloptions => '_null_', relpartbound => '_null_' }, + reloptions => '_null_' }, { oid => '1255', relname => 'pg_proc', relnamespace => 'PGNSP', reltype => '81', reloftype => '0', relowner => 'PGUID', relam => '0', relfilenode => '0', @@ -50,19 +50,19 @@ relpersistence => 'p', relkind => 'r', relnatts => '28', relchecks => '0', relhasoids => 't', relhasrules => 'f', relhastriggers => 'f', relhassubclass => 'f', relrowsecurity => 'f', relforcerowsecurity => 'f', - relispopulated => 't', relreplident => 'n', relispartition => 'f', + relispopulated => 't', relreplident => 'n', relpartitionparent => '0', relrewrite => '0', relfrozenxid => '3', relminmxid => '1', relacl => '_null_', - reloptions => '_null_', relpartbound => '_null_' }, + reloptions => '_null_' }, { oid => '1259', relname => 'pg_class', relnamespace => 'PGNSP', reltype => '83', reloftype => '0', relowner => 'PGUID', relam => '0', relfilenode => '0', reltablespace => '0', relpages => '0', reltuples => '0', relallvisible => '0', reltoastrelid => '0', relhasindex => 'f', relisshared => 'f', - relpersistence => 'p', relkind => 'r', relnatts => '33', relchecks => '0', + relpersistence => 'p', relkind => 'r', relnatts => '32', relchecks => '0', relhasoids => 't', relhasrules => 'f', relhastriggers => 'f', relhassubclass => 'f', relrowsecurity => 'f', relforcerowsecurity => 'f', - relispopulated => 't', relreplident => 'n', relispartition => 'f', + relispopulated => 't', relreplident => 'n', relpartitionparent => '0', relrewrite => '0', relfrozenxid => '3', relminmxid => '1', relacl => '_null_', - reloptions => '_null_', relpartbound => '_null_' }, + reloptions => '_null_' }, ] diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index dc6c415c58..a8f2cadcf6 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -66,7 +66,7 @@ CATALOG(pg_class,1259,RelationRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83,Relat * not */ bool relispopulated; /* matview currently holds query results */ char relreplident; /* see REPLICA_IDENTITY_xxx constants */ - bool relispartition; /* is relation a partition? */ + Oid relpartitionparent; /* Oid of parent if partition, or 0 */ Oid relrewrite; /* heap for rewrite during DDL, link to * original rel */ TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */ @@ -77,7 +77,6 @@ CATALOG(pg_class,1259,RelationRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83,Relat /* NOTE: These fields are not present in a relcache entry's rd_rel field. */ aclitem relacl[1]; /* access permissions */ text reloptions[1]; /* access-method-specific options */ - pg_node_tree relpartbound; /* partition bound node tree */ #endif } FormData_pg_class; diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h index f259890e43..7d3934865c 100644 --- a/src/include/catalog/toasting.h +++ b/src/include/catalog/toasting.h @@ -60,6 +60,7 @@ DECLARE_TOAST(pg_foreign_table, 4153, 4154); DECLARE_TOAST(pg_init_privs, 4155, 4156); DECLARE_TOAST(pg_language, 4157, 4158); DECLARE_TOAST(pg_namespace, 4163, 4164); +DECLARE_TOAST(pg_partition, 3424, 3425); DECLARE_TOAST(pg_partitioned_table, 4165, 4166); DECLARE_TOAST(pg_policy, 4167, 4168); DECLARE_TOAST(pg_proc, 2836, 2837); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 07ab1a3dde..61b31fb2bb 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -803,7 +803,7 @@ typedef struct PartitionSpec * PartitionBoundSpec - a partition bound specification * * This represents the portion of the partition key space assigned to a - * particular partition. These are stored on disk in pg_class.relpartbound. + * particular partition. These are stored on disk in pg_partition.partbound. */ struct PartitionBoundSpec { diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 6ecbdb6294..eb1858aa92 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -406,6 +406,12 @@ typedef struct ViewOptions */ #define RelationGetRelid(relation) ((relation)->rd_id) +/* + * RelationGetParentRelid + * Returns the OID of the relations parent, or InvalidOid if the + * relation has no parent + */ +#define RelationGetParentRelid(relation) ((relation)->rd_rel->relpartitionparent) /* * RelationGetNumberOfAttributes * Returns the total number of attributes in a relation. diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h index 4f333586ee..44d94f001a 100644 --- a/src/include/utils/syscache.h +++ b/src/include/utils/syscache.h @@ -73,6 +73,7 @@ enum SysCacheIdentifier OPFAMILYAMNAMENSP, OPFAMILYOID, PARTRELID, + PARTSRELID, PROCNAMEARGSNSP, PROCOID, PUBLICATIONNAME, diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 0218c2c362..bf62b042ac 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -3654,10 +3654,10 @@ ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2); ERROR: "part_2" is already a partition -- check that circular inheritance is not allowed ALTER TABLE part_5 ATTACH PARTITION list_parted2 FOR VALUES IN ('b'); -ERROR: circular inheritance not allowed +ERROR: circular partitioning is not allowed DETAIL: "part_5" is already a child of "list_parted2". ALTER TABLE list_parted2 ATTACH PARTITION list_parted2 FOR VALUES IN (0); -ERROR: circular inheritance not allowed +ERROR: circular partitioning is not allowed DETAIL: "list_parted2" is already a child of "list_parted2". -- If a partitioned table being created or an existing table being attached -- as a partition does not have a constraint that would allow validation scan diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out index 2d3522b500..6285da1ee1 100644 --- a/src/test/regress/expected/misc_sanity.out +++ b/src/test/regress/expected/misc_sanity.out @@ -100,10 +100,9 @@ ORDER BY 1, 2; pg_attribute | attoptions | text[] pg_class | relacl | aclitem[] pg_class | reloptions | text[] - pg_class | relpartbound | pg_node_tree pg_index | indexprs | pg_node_tree pg_index | indpred | pg_node_tree pg_largeobject | data | bytea pg_largeobject_metadata | lomacl | aclitem[] -(11 rows) +(10 rows) diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 0aa5357917..44ad08e6e5 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -135,6 +135,7 @@ pg_namespace|t pg_opclass|t pg_operator|t pg_opfamily|t +pg_partition|t pg_partitioned_table|t pg_pltemplate|t pg_policy|t -- 2.16.2.windows.1