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
- AttrMap * 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
@@ -352,11 +348,9 @@ ExecFindPartition(ModifyTableState *mtstate,
352
348
353
349
if (partdesc -> is_leaf [partidx ])
354
350
{
355
- ResultRelInfo * rri ;
356
-
357
351
/*
358
- * Look to see if we've already got a ResultRelInfo for this
359
- * partition.
352
+ * We've reached the leaf -- hurray, we're done. Look to see if
353
+ * we've already got a ResultRelInfo for this partition.
360
354
*/
361
355
if (likely (dispatch -> indexes [partidx ] >= 0 ))
362
356
{
@@ -400,14 +394,10 @@ ExecFindPartition(ModifyTableState *mtstate,
400
394
dispatch ,
401
395
rootResultRelInfo , partidx );
402
396
}
397
+ Assert (rri != NULL );
403
398
404
- /* Release the tuple in the lowest parent's dedicated slot. */
405
- if (slot == myslot )
406
- ExecClearTuple (myslot );
407
-
408
- MemoryContextSwitchTo (oldcxt );
409
- ecxt -> ecxt_scantuple = ecxt_scantuple_old ;
410
- return rri ;
399
+ /* Signal to terminate the loop */
400
+ dispatch = NULL ;
411
401
}
412
402
else
413
403
{
@@ -419,6 +409,8 @@ ExecFindPartition(ModifyTableState *mtstate,
419
409
/* Already built. */
420
410
Assert (dispatch -> indexes [partidx ] < proute -> num_dispatch );
421
411
412
+ rri = proute -> nonleaf_partitions [dispatch -> indexes [partidx ]];
413
+
422
414
/*
423
415
* Move down to the next partition level and search again
424
416
* until we find a leaf partition that matches this tuple
@@ -440,10 +432,75 @@ ExecFindPartition(ModifyTableState *mtstate,
440
432
dispatch , partidx );
441
433
Assert (dispatch -> indexes [partidx ] >= 0 &&
442
434
dispatch -> indexes [partidx ] < proute -> num_dispatch );
435
+
436
+ rri = proute -> nonleaf_partitions [dispatch -> indexes [partidx ]];
443
437
dispatch = subdispatch ;
444
438
}
439
+
440
+ /*
441
+ * Convert the tuple to the new parent's layout, if different from
442
+ * the previous parent.
443
+ */
444
+ if (dispatch -> tupslot )
445
+ {
446
+ AttrMap * map = dispatch -> tupmap ;
447
+ TupleTableSlot * tempslot = myslot ;
448
+
449
+ myslot = dispatch -> tupslot ;
450
+ slot = execute_attr_map_slot (map , slot , myslot );
451
+
452
+ if (tempslot != NULL )
453
+ ExecClearTuple (tempslot );
454
+ }
455
+ }
456
+
457
+ /*
458
+ * If this partition is the default one, we must check its partition
459
+ * constraint now, which may have changed concurrently due to
460
+ * partitions being added to the parent.
461
+ *
462
+ * (We do this here, and do not rely on ExecInsert doing it, because
463
+ * we don't want to miss doing it for non-leaf partitions.)
464
+ */
465
+ if (partidx == partdesc -> boundinfo -> default_index )
466
+ {
467
+ PartitionRoutingInfo * partrouteinfo = rri -> ri_PartitionInfo ;
468
+
469
+ /*
470
+ * The tuple must match the partition's layout for the constraint
471
+ * expression to be evaluated successfully. If the partition is
472
+ * sub-partitioned, that would already be the case due to the code
473
+ * above, but for a leaf partition the tuple still matches the
474
+ * parent's layout.
475
+ *
476
+ * Note that we have a map to convert from root to current
477
+ * partition, but not from immediate parent to current partition.
478
+ * So if we have to convert, do it from the root slot; if not, use
479
+ * the root slot as-is.
480
+ */
481
+ if (partrouteinfo )
482
+ {
483
+ TupleConversionMap * map = partrouteinfo -> pi_RootToPartitionMap ;
484
+
485
+ if (map )
486
+ slot = execute_attr_map_slot (map -> attrMap , rootslot ,
487
+ partrouteinfo -> pi_PartitionTupleSlot );
488
+ else
489
+ slot = rootslot ;
490
+ }
491
+
492
+ ExecPartitionCheck (rri , slot , estate , true);
445
493
}
446
494
}
495
+
496
+ /* Release the tuple in the lowest parent's dedicated slot. */
497
+ if (myslot != NULL )
498
+ ExecClearTuple (myslot );
499
+ /* and restore ecxt's scantuple */
500
+ ecxt -> ecxt_scantuple = ecxt_scantuple_saved ;
501
+ MemoryContextSwitchTo (oldcxt );
502
+
503
+ return rri ;
447
504
}
448
505
449
506
/*
@@ -1060,17 +1117,37 @@ ExecInitPartitionDispatchInfo(EState *estate,
1060
1117
proute -> max_dispatch = 4 ;
1061
1118
proute -> partition_dispatch_info = (PartitionDispatch * )
1062
1119
palloc (sizeof (PartitionDispatch ) * proute -> max_dispatch );
1120
+ proute -> nonleaf_partitions = (ResultRelInfo * * )
1121
+ palloc (sizeof (ResultRelInfo * ) * proute -> max_dispatch );
1063
1122
}
1064
1123
else
1065
1124
{
1066
1125
proute -> max_dispatch *= 2 ;
1067
1126
proute -> partition_dispatch_info = (PartitionDispatch * )
1068
1127
repalloc (proute -> partition_dispatch_info ,
1069
1128
sizeof (PartitionDispatch ) * proute -> max_dispatch );
1129
+ proute -> nonleaf_partitions = (ResultRelInfo * * )
1130
+ repalloc (proute -> nonleaf_partitions ,
1131
+ sizeof (ResultRelInfo * ) * proute -> max_dispatch );
1070
1132
}
1071
1133
}
1072
1134
proute -> partition_dispatch_info [dispatchidx ] = pd ;
1073
1135
1136
+ /*
1137
+ * If setting up a PartitionDispatch for a sub-partitioned table, we may
1138
+ * also need a minimally valid ResultRelInfo for checking the partition
1139
+ * constraint later; set that up now.
1140
+ */
1141
+ if (parent_pd )
1142
+ {
1143
+ ResultRelInfo * rri = makeNode (ResultRelInfo );
1144
+
1145
+ InitResultRelInfo (rri , rel , 1 , proute -> partition_root , 0 );
1146
+ proute -> nonleaf_partitions [dispatchidx ] = rri ;
1147
+ }
1148
+ else
1149
+ proute -> nonleaf_partitions [dispatchidx ] = NULL ;
1150
+
1074
1151
/*
1075
1152
* Finally, if setting up a PartitionDispatch for a sub-partitioned table,
1076
1153
* install a downlink in the parent to allow quick descent.
0 commit comments