Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lNFdL-0006DF-AD for pgsql-hackers@arkaria.postgresql.org; Fri, 19 Mar 2021 13:57:47 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1lNFdK-0001ru-87 for pgsql-hackers@arkaria.postgresql.org; Fri, 19 Mar 2021 13:57:46 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lNFdJ-0001oV-ST for pgsql-hackers@lists.postgresql.org; Fri, 19 Mar 2021 13:57:46 +0000 Received: from wout4-smtp.messagingengine.com ([64.147.123.20]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lNFdH-0002We-GT for pgsql-hackers@lists.postgresql.org; Fri, 19 Mar 2021 13:57:44 +0000 Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) by mailout.west.internal (Postfix) with ESMTP id 635921364; Fri, 19 Mar 2021 09:57:41 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute2.internal (MEProxy); Fri, 19 Mar 2021 09:57:41 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :date:from:in-reply-to:message-id:mime-version:subject:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; bh=wU/8hNmDt1rDHknv6yy8P/Q1uR0cEmdhX3NY80da4oI=; b=IgqGvCIn SkcJL/Takdit7EUrJu36Dz+bFod2QIHn9/yJCaVCZyt8LYYpDsrp6SLH5HhPHWN0 nIUAqtwZ15G9OppjcEhfz9cGi+Qf0TRgoEH38AwiCqT+nC66USq8zH3iUcJ908oM Fd51TGE+EJSZHHZ0DKGcaTvAV+YPetl1tJJnXcas3iIQThOcSOqAi0FVkstkZZ/S RZGUz8gvBy3HHcU52WNaArBstRhyZz+fSkgALwb2Sw3Rmh8B8LjbsPwydkw/Atua t3MLvpkzlksd4NqYQXwvUsD4Q3RYd8zcDgh8dRBMLxXb20poaqzIZjE3kWKYFV1L fs2aOeHe1zrcog== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduledrudefkedgheelucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepfffhvffukfggtggugfgjfgesmhekreertderudenucfhrhhomheptehlvhgr rhhoucfjvghrrhgvrhgruceorghlvhhhvghrrhgvsegrlhhvhhdrnhhoqdhiphdrohhrgh eqnecuggftrfgrthhtvghrnhephfefgeelkefgjeeuhefgfeevleeftedvhefftdeutefg hfekhfduheffkeduteeknecukfhppeduledtrdelhedrudelrdelheenucevlhhushhtvg hrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpegrlhhvhhgvrhhrvgesrghl vhhhrdhnohdqihhprdhorhhg X-ME-Proxy: Received: from perhan.alvh.no-ip.org (unknown [190.95.19.95]) by mail.messagingengine.com (Postfix) with ESMTPA id 54FEE24005C; Fri, 19 Mar 2021 09:57:40 -0400 (EDT) Received: by perhan.alvh.no-ip.org (Postfix, from userid 1000) id AA6932A0CAD; Fri, 19 Mar 2021 10:57:37 -0300 (-03) Date: Fri, 19 Mar 2021 10:57:37 -0300 From: Alvaro Herrera To: Justin Pryzby Cc: Amit Langote , pgsql-hackers@lists.postgresql.org Subject: Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY Message-ID: <20210319135737.GA28771@alvherre.pgsql> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="envbJBWh7q8WU6mo" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <20210317184534.GB11765@telsasoft.com> User-Agent: Mutt/1.10.1 (2018-07-13) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --envbJBWh7q8WU6mo Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit On 2021-Mar-17, Justin Pryzby wrote: > The v8 patch has the "broken constraint" problem. Yeah, I had misunderstood what the problem was. I think a good solution to this is to have get_partition_parent return the true parent even when a detach is pending, for one particular callsite. (This means adjusting all other callsites.) Notpatch attached (applies on top of v8). > Also, it "fails to avoid" adding duplicate constraints: > > Check constraints: > "c" CHECK (i IS NOT NULL AND i > 1 AND i < 2) > "cc" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) > "p1_check" CHECK (true) > "p1_i_check" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) Do you mean the "cc" and "p1_i_check" one? I mean, if you already have a constraint in the partition that duplicates the partition constraint, then during attach we still create our new constraint? I guess a solution to this would be to scan all constraints and see if any equals the expression that the new one would have. Sounds easy enough now that write it out loud. Thanks -- Álvaro Herrera 39°49'30"S 73°17'W "Java is clearly an example of money oriented programming" (A. Stepanov) --envbJBWh7q8WU6mo Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="0001-Fix-for-generation-of-invalid-constraints.notpatch" From ecec91fc4744ddd9a231754afdf10ec09742f8d9 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Fri, 19 Mar 2021 10:52:26 -0300 Subject: [PATCH] Fix for generation of invalid constraints per report from Justin Pryzby --- src/backend/catalog/heap.c | 2 +- src/backend/catalog/partition.c | 32 ++++++++++++++--------------- src/backend/commands/tablecmds.c | 12 +++++------ src/backend/utils/cache/partcache.c | 11 ++-------- src/include/catalog/partition.h | 2 +- 5 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 1a05f2a2fe..c6c5e40a39 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -1919,7 +1919,7 @@ heap_drop_with_catalog(Oid relid) elog(ERROR, "cache lookup failed for relation %u", relid); if (((Form_pg_class) GETSTRUCT(tuple))->relispartition) { - parentOid = get_partition_parent(relid); + parentOid = get_partition_parent(relid, true); LockRelationOid(parentOid, AccessExclusiveLock); /* diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 797a7384ad..a7cf3d9e86 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -42,15 +42,15 @@ static void get_partition_ancestors_worker(Relation inhRel, Oid relid, * * Returns inheritance parent of a partition by scanning pg_inherits * - * If the partition is in the process of being detached, InvalidOid is - * returned and no error is thrown. + * If the partition is in the process of being detached, we raise the same + * error unless even_if_detached is passed. * * 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. */ Oid -get_partition_parent(Oid relid) +get_partition_parent(Oid relid, bool even_if_detached) { Relation catalogRelation; Oid result; @@ -60,10 +60,10 @@ get_partition_parent(Oid relid) result = get_partition_parent_worker(catalogRelation, relid, &detached); - if (detached) - result = InvalidOid; - else if (!OidIsValid(result)) + if (!OidIsValid(result)) elog(ERROR, "could not find tuple for parent of relation %u", relid); + if (detached && !even_if_detached) + elog(ERROR, "relation %u has no parent because it's being detached", relid); table_close(catalogRelation, AccessShareLock); @@ -75,8 +75,8 @@ get_partition_parent(Oid relid) * Scan the pg_inherits relation to return the OID of the parent of the * given relation * - * If the partition is being detached, InvalidOid is returned, and also - * *detached is set true. + * If the partition is being detached, *detached is set true (but the original + * parent is still returned.) */ static Oid get_partition_parent_worker(Relation inhRel, Oid relid, bool *detached) @@ -106,12 +106,9 @@ get_partition_parent_worker(Relation inhRel, Oid relid, bool *detached) /* A partition being detached has no parent */ if (form->inhdetachpending) - { *detached = true; - result = InvalidOid; - } - else - result = form->inhparent; + + result = form->inhparent; } systable_endscan(scan); @@ -154,9 +151,12 @@ get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors) Oid parentOid; bool detached; - /* Recursion ends at the topmost level, ie., when there's no parent */ + /* + * Recursion ends at the topmost level, ie., when there's no parent; also + * when the partition is being detached. + */ parentOid = get_partition_parent_worker(inhRel, relid, &detached); - if (parentOid == InvalidOid) + if (parentOid == InvalidOid || detached) return; *ancestors = lappend_oid(*ancestors, parentOid); @@ -189,7 +189,7 @@ index_get_partition(Relation partition, Oid indexId) ReleaseSysCache(tup); if (!ispartition) continue; - if (get_partition_parent(partIdx) == indexId) + if (get_partition_parent(partIdx, false) == indexId) { list_free(idxlist); return partIdx; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 21dc869b0b..853dc8d257 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1547,7 +1547,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, */ if (is_partition && relOid != oldRelOid) { - state->partParentOid = get_partition_parent(relOid); + state->partParentOid = get_partition_parent(relOid, false); if (OidIsValid(state->partParentOid)) LockRelationOid(state->partParentOid, AccessExclusiveLock); } @@ -6816,7 +6816,7 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode) /* If rel is partition, shouldn't drop NOT NULL if parent has the same */ if (rel->rd_rel->relispartition) { - Oid parentId = get_partition_parent(RelationGetRelid(rel)); + Oid parentId = get_partition_parent(RelationGetRelid(rel), false); Relation parent = table_open(parentId, AccessShareLock); TupleDesc tupDesc = RelationGetDescr(parent); AttrNumber parent_attnum; @@ -17361,7 +17361,7 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, if (!has_superclass(idxid)) continue; - Assert((IndexGetRelation(get_partition_parent(idxid), false) == + Assert((IndexGetRelation(get_partition_parent(idxid, false), false) == RelationGetRelid(rel))); idx = index_open(idxid, AccessExclusiveLock); @@ -17660,7 +17660,7 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) /* Silently do nothing if already in the right state */ currParent = partIdx->rd_rel->relispartition ? - get_partition_parent(partIdxId) : InvalidOid; + get_partition_parent(partIdxId, false) : InvalidOid; if (currParent != RelationGetRelid(parentIdx)) { IndexInfo *childInfo; @@ -17872,8 +17872,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 = get_partition_parent(RelationGetRelid(partedIdx), false); + parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false); parentIdx = relation_open(parentIdxId, AccessExclusiveLock); parentTbl = relation_open(parentTblId, AccessExclusiveLock); Assert(!parentIdx->rd_index->indisvalid); diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c index 0fe4f55b04..6dfa3fb4a8 100644 --- a/src/backend/utils/cache/partcache.c +++ b/src/backend/utils/cache/partcache.c @@ -352,16 +352,9 @@ generate_partition_qual(Relation rel) return copyObject(rel->rd_partcheck); /* - * Obtain parent relid; if it's invalid, then the partition is being - * detached. The constraint is NIL in that case, and let's cache that. + * Obtain parent relid. XXX explain why we need this */ - parentrelid = get_partition_parent(RelationGetRelid(rel)); - if (parentrelid == InvalidOid) - { - rel->rd_partcheckvalid = true; - rel->rd_partcheck = NIL; - return NIL; - } + parentrelid = get_partition_parent(RelationGetRelid(rel), true); /* Grab at least an AccessShareLock on the parent table */ parent = relation_open(parentrelid, AccessShareLock); diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index fe3f66befa..c8c7bc1d99 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -19,7 +19,7 @@ /* Seed for the extended hash function */ #define HASH_PARTITION_SEED UINT64CONST(0x7A5B22367996DCFD) -extern Oid get_partition_parent(Oid relid); +extern Oid get_partition_parent(Oid relid, bool even_if_detached); extern List *get_partition_ancestors(Oid relid); extern Oid index_get_partition(Relation partition, Oid indexId); extern List *map_partition_varattnos(List *expr, int fromrel_varno, -- 2.20.1 --envbJBWh7q8WU6mo--