@@ -47,11 +47,11 @@ static int32 qsort_partition_rbound_cmp(const void *a, const void *b,
47
47
48
48
/*
49
49
* RelationBuildPartitionKey
50
- * Build and attach to relcache partition key data of relation
50
+ * Build partition key data of relation, and attach to relcache
51
51
*
52
52
* Partitioning key data is a complex structure; to avoid complicated logic to
53
53
* free individual elements whenever the relcache entry is flushed, we give it
54
- * its own memory context, child of CacheMemoryContext, which can easily be
54
+ * its own memory context, a child of CacheMemoryContext, which can easily be
55
55
* deleted on its own. To avoid leaking memory in that context in case of an
56
56
* error partway through this function, the context is initially created as a
57
57
* child of CurTransactionContext and only re-parented to CacheMemoryContext
@@ -150,15 +150,14 @@ RelationBuildPartitionKey(Relation relation)
150
150
MemoryContextSwitchTo (oldcxt );
151
151
}
152
152
153
+ /* Allocate assorted arrays in the partkeycxt, which we'll fill below */
153
154
oldcxt = MemoryContextSwitchTo (partkeycxt );
154
155
key -> partattrs = (AttrNumber * ) palloc0 (key -> partnatts * sizeof (AttrNumber ));
155
156
key -> partopfamily = (Oid * ) palloc0 (key -> partnatts * sizeof (Oid ));
156
157
key -> partopcintype = (Oid * ) palloc0 (key -> partnatts * sizeof (Oid ));
157
158
key -> partsupfunc = (FmgrInfo * ) palloc0 (key -> partnatts * sizeof (FmgrInfo ));
158
159
159
160
key -> partcollation = (Oid * ) palloc0 (key -> partnatts * sizeof (Oid ));
160
-
161
- /* Gather type and collation info as well */
162
161
key -> parttypid = (Oid * ) palloc0 (key -> partnatts * sizeof (Oid ));
163
162
key -> parttypmod = (int32 * ) palloc0 (key -> partnatts * sizeof (int32 ));
164
163
key -> parttyplen = (int16 * ) palloc0 (key -> partnatts * sizeof (int16 ));
@@ -241,6 +240,10 @@ RelationBuildPartitionKey(Relation relation)
241
240
242
241
ReleaseSysCache (tuple );
243
242
243
+ /* Assert that we're not leaking any old data during assignments below */
244
+ Assert (relation -> rd_partkeycxt == NULL );
245
+ Assert (relation -> rd_partkey == NULL );
246
+
244
247
/*
245
248
* Success --- reparent our context and make the relcache point to the
246
249
* newly constructed key
@@ -252,10 +255,13 @@ RelationBuildPartitionKey(Relation relation)
252
255
253
256
/*
254
257
* RelationBuildPartitionDesc
255
- * Form rel's partition descriptor
258
+ * Form rel's partition descriptor, and store in relcache entry
256
259
*
257
- * Not flushed from the cache by RelationClearRelation() unless changed because
258
- * of addition or removal of partition.
260
+ * Note: the descriptor won't be flushed from the cache by
261
+ * RelationClearRelation() unless it's changed because of
262
+ * addition or removal of a partition. Hence, code holding a lock
263
+ * that's sufficient to prevent that can assume that rd_partdesc
264
+ * won't change underneath it.
259
265
*/
260
266
void
261
267
RelationBuildPartitionDesc (Relation rel )
@@ -565,11 +571,24 @@ RelationBuildPartitionDesc(Relation rel)
565
571
(int ) key -> strategy );
566
572
}
567
573
568
- /* Now build the actual relcache partition descriptor */
574
+ /* Assert we aren't about to leak any old data structure */
575
+ Assert (rel -> rd_pdcxt == NULL );
576
+ Assert (rel -> rd_partdesc == NULL );
577
+
578
+ /*
579
+ * Now build the actual relcache partition descriptor. Note that the
580
+ * order of operations here is fairly critical. If we fail partway
581
+ * through this code, we won't have leaked memory because the rd_pdcxt is
582
+ * attached to the relcache entry immediately, so it'll be freed whenever
583
+ * the entry is rebuilt or destroyed. However, we don't assign to
584
+ * rd_partdesc until the cached data structure is fully complete and
585
+ * valid, so that no other code might try to use it.
586
+ */
569
587
rel -> rd_pdcxt = AllocSetContextCreate (CacheMemoryContext ,
570
588
"partition descriptor" ,
571
- ALLOCSET_DEFAULT_SIZES );
572
- MemoryContextCopyAndSetIdentifier (rel -> rd_pdcxt , RelationGetRelationName (rel ));
589
+ ALLOCSET_SMALL_SIZES );
590
+ MemoryContextCopyAndSetIdentifier (rel -> rd_pdcxt ,
591
+ RelationGetRelationName (rel ));
573
592
574
593
oldcxt = MemoryContextSwitchTo (rel -> rd_pdcxt );
575
594
@@ -839,11 +858,9 @@ get_partition_qual_relid(Oid relid)
839
858
* Generate partition predicate from rel's partition bound expression. The
840
859
* function returns a NIL list if there is no predicate.
841
860
*
842
- * Result expression tree is stored CacheMemoryContext to ensure it survives
843
- * as long as the relcache entry. But we should be running in a less long-lived
844
- * working context. To avoid leaking cache memory if this routine fails partway
845
- * through, we build in working memory and then copy the completed structure
846
- * into cache memory.
861
+ * We cache a copy of the result in the relcache entry, after constructing
862
+ * it using the caller's context. This approach avoids leaking any data
863
+ * into long-lived cache contexts, especially if we fail partway through.
847
864
*/
848
865
static List *
849
866
generate_partition_qual (Relation rel )
@@ -860,8 +877,8 @@ generate_partition_qual(Relation rel)
860
877
/* Guard against stack overflow due to overly deep partition tree */
861
878
check_stack_depth ();
862
879
863
- /* Quick copy */
864
- if (rel -> rd_partcheck != NIL )
880
+ /* If we already cached the result, just return a copy */
881
+ if (rel -> rd_partcheckvalid )
865
882
return copyObject (rel -> rd_partcheck );
866
883
867
884
/* Grab at least an AccessShareLock on the parent table */
@@ -907,14 +924,37 @@ generate_partition_qual(Relation rel)
907
924
if (found_whole_row )
908
925
elog (ERROR , "unexpected whole-row reference found in partition key" );
909
926
910
- /* Save a copy in the relcache */
911
- oldcxt = MemoryContextSwitchTo (CacheMemoryContext );
912
- rel -> rd_partcheck = copyObject (result );
913
- MemoryContextSwitchTo (oldcxt );
927
+ /* Assert that we're not leaking any old data during assignments below */
928
+ Assert (rel -> rd_partcheckcxt == NULL );
929
+ Assert (rel -> rd_partcheck == NIL );
930
+
931
+ /*
932
+ * Save a copy in the relcache. The order of these operations is fairly
933
+ * critical to avoid memory leaks and ensure that we don't leave a corrupt
934
+ * relcache entry if we fail partway through copyObject.
935
+ *
936
+ * If, as is definitely possible, the partcheck list is NIL, then we do
937
+ * not need to make a context to hold it.
938
+ */
939
+ if (result != NIL )
940
+ {
941
+ rel -> rd_partcheckcxt = AllocSetContextCreate (CacheMemoryContext ,
942
+ "partition constraint" ,
943
+ ALLOCSET_SMALL_SIZES );
944
+ MemoryContextCopyAndSetIdentifier (rel -> rd_partcheckcxt ,
945
+ RelationGetRelationName (rel ));
946
+ oldcxt = MemoryContextSwitchTo (rel -> rd_partcheckcxt );
947
+ rel -> rd_partcheck = copyObject (result );
948
+ MemoryContextSwitchTo (oldcxt );
949
+ }
950
+ else
951
+ rel -> rd_partcheck = NIL ;
952
+ rel -> rd_partcheckvalid = true;
914
953
915
954
/* Keep the parent locked until commit */
916
955
relation_close (parent , NoLock );
917
956
957
+ /* Return the working copy to the caller */
918
958
return result ;
919
959
}
920
960
0 commit comments