Hi, If we detach a partition and drop the corresponding partitioned table, it drops the once-partition now-standalone table as well.
create table prt1 (a int, b int) partition by range(a); create table prt1_p1 partition of prt1 for values from (0) to (100); select oid, relname, relpartbound, relispartition, relkind from pg_class where relname like 'prt%'; oid | relname -------+--------- 16453 | prt1 16456 | prt1_p1 (2 rows) select * from pg_depend where refobjid = 'prt1'::regclass and objid = 'prt1_p1'::regclass; classid | objid | objsubid | refclassid | refobjid | refobjsubid | deptype ---------+-------+----------+------------+----------+-------------+--------- 1259 | 16456 | 0 | 1259 | 16453 | 0 | a (1 row) alter table prt1 detach partition prt1_p1; -- so the dependency exists even after detach select * from pg_depend where refobjid = 'prt1'::regclass and objid = 'prt1_p1'::regclass; classid | objid | objsubid | refclassid | refobjid | refobjsubid | deptype ---------+-------+----------+------------+----------+-------------+--------- 1259 | 16456 | 0 | 1259 | 16453 | 0 | a (1 row) drop table prt1; -- Oops, we deleted the once-partition now-standalone table as well select oid, relname, relpartbound, relispartition, relkind from pg_class where relname like 'prt%'; oid | relname | relpartbound | relispartition | relkind -----+---------+--------------+----------------+--------- (0 rows) The reason for this is as follows An AUTO dependency is recorded between a partitioned table and partition. In case of inheritance we record a NORMAL dependency between parent and child. While detaching a partition, we call RemoveInheritance(), which should have taken care of removing this dependency. But it removes the dependencies which are marked as NORMAL. When we changed the dependency for partitioned case from NORMAL to AUTO by updating StoreCatalogInheritance1(), this function was not updated. This caused the dependency to linger behind even after detaching the partition, thus causing the now standalone table (which was once a partition) to be dropped when the parent partitioned table is dropped. This patch fixes RemoveInheritance() to choose appropriate dependency. Attached patch 0001 to fix this. I see a similar issue-in-baking in case we change the type of dependency recorded between a table and the composite type associated with using CREATE TABLE ... OF command. 0002 patch addresses the potential issue by using a macro while creating and dropping the dependency in corresponding functions. There might be more such places, but I haven't searched for those. -- Best Wishes, Ashutosh Bapat EnterpriseDB Corporation The Postgres Database Company
From 97a647c487761782340d7ebca82b6d3200d79142 Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat <ashutosh.ba...@enterprisedb.com> Date: Mon, 12 Jun 2017 16:32:53 +0530 Subject: [PATCH 1/2] Dependency between partitioned table and partition. An AUTO dependency is recorded between a partitioned table and partition. In case of inheritance we record a NORMAL dependency between parent and child. While detaching a partition, we call RemoveInheritance(), which should have taken care of removing this dependency. But it removes the dependencies which are marked as NORMAL. When we changed the dependency for partitioned case from NORMAL to AUTO by updating StoreCatalogInheritance1(), this function was not updated. This caused the dependency to linger behind even after detaching the partition, thus causing the now standalone table (which was once a partition) to be dropped when the parent partitioned table is dropped. This patch fixes RemoveInheritance() to choose appropriate dependency. Ashutosh Bapat. --- src/backend/commands/tablecmds.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index b61fda9..9aef67b 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -283,6 +283,14 @@ struct DropRelationCallbackState #define ATT_COMPOSITE_TYPE 0x0010 #define ATT_FOREIGN_TABLE 0x0020 +/* + * Partition tables are expected to be dropped when the parent partitioned + * table gets dropped. Hence for partitioning we use AUTO dependency. + * Otherwise, for regular inheritance use NORMAL dependency. + */ +#define child_dependency_type(child_is_partition) \ + ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL) + static void truncate_check_rel(Relation rel); static List *MergeAttributes(List *schema, List *supers, char relpersistence, bool is_partition, List **supOids, List **supconstr, @@ -439,7 +447,8 @@ static void ATExecEnableDisableRule(Relation rel, char *rulename, static void ATPrepAddInherit(Relation child_rel); static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode); static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode); -static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid); +static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid, + DependencyType deptype); static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode); static void ATExecDropOf(Relation rel, LOCKMODE lockmode); static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode); @@ -2367,14 +2376,8 @@ StoreCatalogInheritance1(Oid relationId, Oid parentOid, childobject.objectId = relationId; childobject.objectSubId = 0; - /* - * Partition tables are expected to be dropped when the parent partitioned - * table gets dropped. - */ - if (child_is_partition) - recordDependencyOn(&childobject, &parentobject, DEPENDENCY_AUTO); - else - recordDependencyOn(&childobject, &parentobject, DEPENDENCY_NORMAL); + recordDependencyOn(&childobject, &parentobject, + child_dependency_type(child_is_partition)); /* * Post creation hook of this inheritance. Since object_access_hook @@ -11666,7 +11669,8 @@ RemoveInheritance(Relation child_rel, Relation parent_rel) drop_parent_dependency(RelationGetRelid(child_rel), RelationRelationId, - RelationGetRelid(parent_rel)); + RelationGetRelid(parent_rel), + child_dependency_type(child_is_partition)); /* * Post alter hook of this inherits. Since object_access_hook doesn't take @@ -11686,7 +11690,8 @@ RemoveInheritance(Relation child_rel, Relation parent_rel) * through pg_depend. */ static void -drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid) +drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid, + DependencyType deptype) { Relation catalogRelation; SysScanDesc scan; @@ -11718,7 +11723,7 @@ drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid) if (dep->refclassid == refclassid && dep->refobjid == refobjid && dep->refobjsubid == 0 && - dep->deptype == DEPENDENCY_NORMAL) + dep->deptype == deptype) CatalogTupleDelete(catalogRelation, &depTuple->t_self); } @@ -11839,7 +11844,8 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode) /* If the table was already typed, drop the existing dependency. */ if (rel->rd_rel->reloftype) - drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype); + drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype, + DEPENDENCY_NORMAL); /* Record a dependency on the new type. */ tableobj.classId = RelationRelationId; @@ -11892,7 +11898,8 @@ ATExecDropOf(Relation rel, LOCKMODE lockmode) * table is presumed enough rights. No lock required on the type, either. */ - drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype); + drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype, + DEPENDENCY_NORMAL); /* Clear pg_class.reloftype */ relationRelation = heap_open(RelationRelationId, RowExclusiveLock); -- 1.7.9.5
From 35943c90783d17b61a2f2ea39230e0242a251e3e Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat <ashutosh.ba...@enterprisedb.com> Date: Mon, 12 Jun 2017 16:49:07 +0530 Subject: [PATCH 2/2] Macro for dendency between table and its composite type. Create a macro for type dependency between a table and the composite type associated with it using CREATE/ALTER TABLE ... OF ... command, so as to use the same dependency type while creating and dropping the dependency in different functions. Ashutosh Bapat. --- src/backend/commands/tablecmds.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 9aef67b..2ac3646 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -291,6 +291,9 @@ struct DropRelationCallbackState #define child_dependency_type(child_is_partition) \ ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL) +/* Dependency between a table and its associated composite type. */ +#define TABLE_COMPOSITE_TYPE_DEPENDENCY DEPENDENCY_NORMAL + static void truncate_check_rel(Relation rel); static List *MergeAttributes(List *schema, List *supers, char relpersistence, bool is_partition, List **supOids, List **supconstr, @@ -11845,7 +11848,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode) /* If the table was already typed, drop the existing dependency. */ if (rel->rd_rel->reloftype) drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype, - DEPENDENCY_NORMAL); + TABLE_COMPOSITE_TYPE_DEPENDENCY); /* Record a dependency on the new type. */ tableobj.classId = RelationRelationId; @@ -11899,7 +11902,7 @@ ATExecDropOf(Relation rel, LOCKMODE lockmode) */ drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype, - DEPENDENCY_NORMAL); + TABLE_COMPOSITE_TYPE_DEPENDENCY); /* Clear pg_class.reloftype */ relationRelation = heap_open(RelationRelationId, RowExclusiveLock); -- 1.7.9.5
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers