diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index feca620cff..a25ead01d4 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -1202,6 +1202,9 @@ RelationBuildDesc(Oid targetRelId, bool insertIt) relation->rd_pdcxt = NULL; } + relation->rd_partdesc_orig = NULL; + relation->rd_pdcxt_orig = NULL; + /* * if it's an index, initialize index-related information */ @@ -2012,6 +2015,20 @@ RelationClose(Relation relation) /* Note: no locking manipulations needed */ RelationDecrementReferenceCount(relation); + /* + * If the partdesc has been changed while the relation had a non-zero + * ref count we'll have stored the original partdesc. When the ref + * count reaches 0 we must get rid of the original partdesc and destroy + * the memory context it was stored in. + */ + if (relation->rd_partdesc_orig && + RelationHasReferenceCountZero(relation)) + { + MemoryContextDelete(relation->rd_pdcxt_orig); + relation->rd_partdesc_orig = NULL; + relation->rd_pdcxt_orig = NULL; + } + #ifdef RELCACHE_FORCE_RELEASE if (RelationHasReferenceCountZero(relation) && relation->rd_createSubid == InvalidSubTransactionId && @@ -2288,7 +2305,7 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc) MemoryContextDelete(relation->rd_rsdesc->rscxt); if (relation->rd_partkeycxt) MemoryContextDelete(relation->rd_partkeycxt); - if (relation->rd_pdcxt) + if (relation->rd_pdcxt && !relation->rd_pdcxt_orig) MemoryContextDelete(relation->rd_pdcxt); if (relation->rd_partcheck) pfree(relation->rd_partcheck); @@ -2546,6 +2563,27 @@ RelationClearRelation(Relation relation, bool rebuild) SWAPFIELD(MemoryContext, rd_pdcxt); } + /* + * When the partdesc changes while the refcount is > 0 then we must + * keep the original partdesc intact as it might currently be getting + * used by some code. We store the original partdesc in + * rd_partdesc_orig. If it changes multiple times we don't need to + * keep the intermediate ones, just the original. This gets cleared + * up and set to NULL again when the ref count reaches 0. + */ + else if (relation->rd_partdesc != NULL && newrel->rd_partdesc != NULL && + relation->rd_partdesc_orig == NULL) + { + relation->rd_partdesc_orig = newrel->rd_partdesc; + relation->rd_pdcxt_orig = newrel->rd_pdcxt; + + /* + * Set this so that RelationDestroyRelation does not destroy the + * memory context of the original partdesc. + */ + newrel->rd_pdcxt_orig = newrel->rd_pdcxt; + } + #undef SWAPFIELD /* And now we can throw away the temporary entry */ @@ -5652,6 +5690,8 @@ load_relcache_init_file(bool shared) rel->rd_partkey = NULL; rel->rd_pdcxt = NULL; rel->rd_partdesc = NULL; + rel->rd_pdcxt_orig = NULL; + rel->rd_partdesc_orig = NULL; rel->rd_partcheck = NIL; rel->rd_indexprs = NIL; rel->rd_indpred = NIL; diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index eb1858aa92..7724d8da15 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -99,6 +99,12 @@ typedef struct RelationData struct PartitionKeyData *rd_partkey; /* partition key, or NULL */ MemoryContext rd_pdcxt; /* private context for partdesc */ struct PartitionDescData *rd_partdesc; /* partitions, or NULL */ + MemoryContext rd_pdcxt_orig; /* private context for rd_partdesc_orig */ + struct PartitionDescData *rd_partdesc_orig; /* original partitions, or + * NULL if not partitioned or + * the rd_partdesc has not + * changed while the ref count + * was non-zero */ List *rd_partcheck; /* partition CHECK quals */ /* data managed by RelationGetIndexList: */