51
51
* PartitionDispatchData->indexes for details on how this array is
52
52
* indexed.
53
53
*
54
+ * nonleaf_partitions
55
+ * Array of 'max_dispatch' elements containing pointers to fake
56
+ * ResultRelInfo objects for nonleaf partitions, useful for checking
57
+ * the partition constraint.
58
+ *
54
59
* num_dispatch
55
60
* The current number of items stored in the 'partition_dispatch_info'
56
61
* array. Also serves as the index of the next free array element for
@@ -89,6 +94,7 @@ struct PartitionTupleRouting
89
94
{
90
95
Relation partition_root ;
91
96
PartitionDispatch * partition_dispatch_info ;
97
+ ResultRelInfo * * nonleaf_partitions ;
92
98
int num_dispatch ;
93
99
int max_dispatch ;
94
100
ResultRelInfo * * partitions ;
@@ -280,9 +286,11 @@ ExecFindPartition(ModifyTableState *mtstate,
280
286
PartitionDispatch dispatch ;
281
287
PartitionDesc partdesc ;
282
288
ExprContext * ecxt = GetPerTupleExprContext (estate );
283
- TupleTableSlot * ecxt_scantuple_old = ecxt -> ecxt_scantuple ;
289
+ TupleTableSlot * ecxt_scantuple_saved = ecxt -> ecxt_scantuple ;
290
+ TupleTableSlot * rootslot = slot ;
284
291
TupleTableSlot * myslot = NULL ;
285
292
MemoryContext oldcxt ;
293
+ ResultRelInfo * rri = NULL ;
286
294
287
295
/* use per-tuple context here to avoid leaking memory */
288
296
oldcxt = MemoryContextSwitchTo (GetPerTupleMemoryContext (estate ));
@@ -296,27 +304,15 @@ ExecFindPartition(ModifyTableState *mtstate,
296
304
297
305
/* start with the root partitioned table */
298
306
dispatch = pd [0 ];
299
- while (true )
307
+ while (dispatch != NULL )
300
308
{
301
- AttrNumber * map = dispatch -> tupmap ;
302
309
int partidx = -1 ;
303
310
304
311
CHECK_FOR_INTERRUPTS ();
305
312
306
313
rel = dispatch -> reldesc ;
307
314
partdesc = dispatch -> partdesc ;
308
315
309
- /*
310
- * Convert the tuple to this parent's layout, if different from the
311
- * current relation.
312
- */
313
- myslot = dispatch -> tupslot ;
314
- if (myslot != NULL )
315
- {
316
- Assert (map != NULL );
317
- slot = execute_attr_map_slot (map , slot , myslot );
318
- }
319
-
320
316
/*
321
317
* Extract partition key from tuple. Expression evaluation machinery
322
318
* that FormPartitionKeyDatum() invokes expects ecxt_scantuple to
@@ -351,11 +347,9 @@ ExecFindPartition(ModifyTableState *mtstate,
351
347
352
348
if (partdesc -> is_leaf [partidx ])
353
349
{
354
- ResultRelInfo * rri ;
355
-
356
350
/*
357
- * Look to see if we've already got a ResultRelInfo for this
358
- * partition.
351
+ * We've reached the leaf -- hurray, we're done. Look to see if
352
+ * we've already got a ResultRelInfo for this partition.
359
353
*/
360
354
if (likely (dispatch -> indexes [partidx ] >= 0 ))
361
355
{
@@ -399,14 +393,10 @@ ExecFindPartition(ModifyTableState *mtstate,
399
393
dispatch ,
400
394
rootResultRelInfo , partidx );
401
395
}
396
+ Assert (rri != NULL );
402
397
403
- /* Release the tuple in the lowest parent's dedicated slot. */
404
- if (slot == myslot )
405
- ExecClearTuple (myslot );
406
-
407
- MemoryContextSwitchTo (oldcxt );
408
- ecxt -> ecxt_scantuple = ecxt_scantuple_old ;
409
- return rri ;
398
+ /* Signal to terminate the loop */
399
+ dispatch = NULL ;
410
400
}
411
401
else
412
402
{
@@ -418,6 +408,8 @@ ExecFindPartition(ModifyTableState *mtstate,
418
408
/* Already built. */
419
409
Assert (dispatch -> indexes [partidx ] < proute -> num_dispatch );
420
410
411
+ rri = proute -> nonleaf_partitions [dispatch -> indexes [partidx ]];
412
+
421
413
/*
422
414
* Move down to the next partition level and search again
423
415
* until we find a leaf partition that matches this tuple
@@ -439,10 +431,75 @@ ExecFindPartition(ModifyTableState *mtstate,
439
431
dispatch , partidx );
440
432
Assert (dispatch -> indexes [partidx ] >= 0 &&
441
433
dispatch -> indexes [partidx ] < proute -> num_dispatch );
434
+
435
+ rri = proute -> nonleaf_partitions [dispatch -> indexes [partidx ]];
442
436
dispatch = subdispatch ;
443
437
}
438
+
439
+ /*
440
+ * Convert the tuple to the new parent's layout, if different from
441
+ * the previous parent.
442
+ */
443
+ if (dispatch -> tupslot )
444
+ {
445
+ AttrNumber * map = dispatch -> tupmap ;
446
+ TupleTableSlot * tempslot = myslot ;
447
+
448
+ myslot = dispatch -> tupslot ;
449
+ slot = execute_attr_map_slot (map , slot , myslot );
450
+
451
+ if (tempslot != NULL )
452
+ ExecClearTuple (tempslot );
453
+ }
454
+ }
455
+
456
+ /*
457
+ * If this partition is the default one, we must check its partition
458
+ * constraint now, which may have changed concurrently due to
459
+ * partitions being added to the parent.
460
+ *
461
+ * (We do this here, and do not rely on ExecInsert doing it, because
462
+ * we don't want to miss doing it for non-leaf partitions.)
463
+ */
464
+ if (partidx == partdesc -> boundinfo -> default_index )
465
+ {
466
+ PartitionRoutingInfo * partrouteinfo = rri -> ri_PartitionInfo ;
467
+
468
+ /*
469
+ * The tuple must match the partition's layout for the constraint
470
+ * expression to be evaluated successfully. If the partition is
471
+ * sub-partitioned, that would already be the case due to the code
472
+ * above, but for a leaf partition the tuple still matches the
473
+ * parent's layout.
474
+ *
475
+ * Note that we have a map to convert from root to current
476
+ * partition, but not from immediate parent to current partition.
477
+ * So if we have to convert, do it from the root slot; if not, use
478
+ * the root slot as-is.
479
+ */
480
+ if (partrouteinfo )
481
+ {
482
+ TupleConversionMap * map = partrouteinfo -> pi_RootToPartitionMap ;
483
+
484
+ if (map )
485
+ slot = execute_attr_map_slot (map -> attrMap , rootslot ,
486
+ partrouteinfo -> pi_PartitionTupleSlot );
487
+ else
488
+ slot = rootslot ;
489
+ }
490
+
491
+ ExecPartitionCheck (rri , slot , estate , true);
444
492
}
445
493
}
494
+
495
+ /* Release the tuple in the lowest parent's dedicated slot. */
496
+ if (myslot != NULL )
497
+ ExecClearTuple (myslot );
498
+ /* and restore ecxt's scantuple */
499
+ ecxt -> ecxt_scantuple = ecxt_scantuple_saved ;
500
+ MemoryContextSwitchTo (oldcxt );
501
+
502
+ return rri ;
446
503
}
447
504
448
505
/*
@@ -1071,17 +1128,37 @@ ExecInitPartitionDispatchInfo(EState *estate,
1071
1128
proute -> max_dispatch = 4 ;
1072
1129
proute -> partition_dispatch_info = (PartitionDispatch * )
1073
1130
palloc (sizeof (PartitionDispatch ) * proute -> max_dispatch );
1131
+ proute -> nonleaf_partitions = (ResultRelInfo * * )
1132
+ palloc (sizeof (ResultRelInfo * ) * proute -> max_dispatch );
1074
1133
}
1075
1134
else
1076
1135
{
1077
1136
proute -> max_dispatch *= 2 ;
1078
1137
proute -> partition_dispatch_info = (PartitionDispatch * )
1079
1138
repalloc (proute -> partition_dispatch_info ,
1080
1139
sizeof (PartitionDispatch ) * proute -> max_dispatch );
1140
+ proute -> nonleaf_partitions = (ResultRelInfo * * )
1141
+ repalloc (proute -> nonleaf_partitions ,
1142
+ sizeof (ResultRelInfo * ) * proute -> max_dispatch );
1081
1143
}
1082
1144
}
1083
1145
proute -> partition_dispatch_info [dispatchidx ] = pd ;
1084
1146
1147
+ /*
1148
+ * If setting up a PartitionDispatch for a sub-partitioned table, we may
1149
+ * also need a minimally valid ResultRelInfo for checking the partition
1150
+ * constraint later; set that up now.
1151
+ */
1152
+ if (parent_pd )
1153
+ {
1154
+ ResultRelInfo * rri = makeNode (ResultRelInfo );
1155
+
1156
+ InitResultRelInfo (rri , rel , 1 , proute -> partition_root , 0 );
1157
+ proute -> nonleaf_partitions [dispatchidx ] = rri ;
1158
+ }
1159
+ else
1160
+ proute -> nonleaf_partitions [dispatchidx ] = NULL ;
1161
+
1085
1162
/*
1086
1163
* Finally, if setting up a PartitionDispatch for a sub-partitioned table,
1087
1164
* install a downlink in the parent to allow quick descent.
0 commit comments