@@ -69,7 +69,9 @@ CustomScanMethods partition_filter_plan_methods;
69
69
CustomExecMethods partition_filter_exec_methods ;
70
70
71
71
static ExprState * prepare_expr_state (const PartRelationInfo * prel ,
72
- EState * estate );
72
+ Relation source_rel ,
73
+ EState * estate ,
74
+ bool try_map );
73
75
static void prepare_rri_for_insert (EState * estate ,
74
76
ResultRelInfoHolder * rri_holder ,
75
77
const ResultPartsStorage * rps_storage ,
@@ -261,7 +263,6 @@ scan_result_parts_storage(Oid partid, ResultPartsStorage *parts_storage)
261
263
262
264
/* Open child relation and check if it is a valid target */
263
265
child_rel = heap_open (partid , NoLock );
264
- CheckValidResultRel (child_rel , parts_storage -> command_type );
265
266
266
267
/* Build Var translation list for 'inserted_cols' */
267
268
make_inh_translation_list (base_rel , child_rel , 0 , & translated_vars );
@@ -311,6 +312,10 @@ scan_result_parts_storage(Oid partid, ResultPartsStorage *parts_storage)
311
312
/* ri_ConstraintExprs will be initialized by ExecRelCheck() */
312
313
child_result_rel_info -> ri_ConstraintExprs = NULL ;
313
314
315
+ /* Check that this partition is a valid result relation */
316
+ CheckValidResultRelCompat (child_result_rel_info ,
317
+ parts_storage -> command_type );
318
+
314
319
/* Fill the ResultRelInfo holder */
315
320
rri_holder -> partid = partid ;
316
321
rri_holder -> result_rel_info = child_result_rel_info ;
@@ -446,11 +451,11 @@ select_partition_for_insert(ExprState *expr_state,
446
451
elog (ERROR , ERR_PART_ATTR_MULTIPLE );
447
452
else if (nparts == 0 )
448
453
{
449
- partition_relid = create_partitions_for_value (parent_relid ,
450
- value , prel -> ev_type );
454
+ partition_relid = create_partitions_for_value (parent_relid ,
455
+ value , prel -> ev_type );
451
456
452
- /* get_pathman_relation_info() will refresh this entry */
453
- invalidate_pathman_relation_info (parent_relid , NULL );
457
+ /* get_pathman_relation_info() will refresh this entry */
458
+ invalidate_pathman_relation_info (parent_relid , NULL );
454
459
}
455
460
else partition_relid = parts [0 ];
456
461
@@ -475,23 +480,32 @@ select_partition_for_insert(ExprState *expr_state,
475
480
/* This partition might have sub-partitions */
476
481
else if (rri_holder -> has_children )
477
482
{
478
- const PartRelationInfo * sub_prel ;
483
+ const PartRelationInfo * child_prel ;
479
484
480
485
/* Fetch PartRelationInfo for this partitioned relation */
481
- sub_prel = get_pathman_relation_info (rri_holder -> partid );
486
+ child_prel = get_pathman_relation_info (rri_holder -> partid );
482
487
483
488
/* Might be a false alarm */
484
- if (!sub_prel )
489
+ if (!child_prel )
485
490
return rri_holder ;
486
491
487
- /* Build an expression state if not yet */
492
+ /* Build an expression state if it's not ready yet */
488
493
if (!rri_holder -> expr_state )
489
- rri_holder -> expr_state = prepare_expr_state (sub_prel , estate );
494
+ {
495
+ /* Fetch original topmost parent */
496
+ Relation source_rel = parts_storage -> base_rri -> ri_RelationDesc ;
497
+
498
+ /* Build a partitioning expression state */
499
+ rri_holder -> expr_state = prepare_expr_state (child_prel ,
500
+ source_rel ,
501
+ estate ,
502
+ true);
503
+ }
490
504
491
505
/* Recursively search for subpartitions */
492
506
rri_holder = select_partition_for_insert (rri_holder -> expr_state ,
493
507
econtext , estate ,
494
- sub_prel , parts_storage );
508
+ child_prel , parts_storage );
495
509
}
496
510
}
497
511
/* Loop until we get some result */
@@ -501,15 +515,45 @@ select_partition_for_insert(ExprState *expr_state,
501
515
}
502
516
503
517
static ExprState *
504
- prepare_expr_state (const PartRelationInfo * prel , EState * estate )
518
+ prepare_expr_state (const PartRelationInfo * prel ,
519
+ Relation source_rel ,
520
+ EState * estate ,
521
+ bool try_map )
505
522
{
506
523
ExprState * expr_state ;
507
524
MemoryContext old_mcxt ;
508
525
Node * expr ;
509
526
510
- /* Change varno in Vars according to range table */
527
+ /* Fetch partitioning expression (we don't care about varno) */
511
528
expr = PrelExpressionForRelid (prel , PART_EXPR_VARNO );
512
529
530
+ /* Should we try using map? */
531
+ if (try_map )
532
+ {
533
+
534
+ AttrNumber * map ;
535
+ int map_length ;
536
+ TupleDesc source_tupdesc = RelationGetDescr (source_rel );
537
+
538
+ /* Remap expression attributes for source relation */
539
+ map = PrelExpressionAttributesMap (prel , source_tupdesc , & map_length );
540
+
541
+ if (map )
542
+ {
543
+ bool found_whole_row ;
544
+
545
+ expr = map_variable_attnos_compat (expr , PART_EXPR_VARNO , 0 , map ,
546
+ map_length , InvalidOid ,
547
+ & found_whole_row );
548
+
549
+ if (found_whole_row )
550
+ elog (ERROR , "unexpected whole-row reference"
551
+ " found in partition key" );
552
+
553
+ pfree (map );
554
+ }
555
+ }
556
+
513
557
/* Prepare state for expression execution */
514
558
old_mcxt = MemoryContextSwitchTo (estate -> es_query_cxt );
515
559
expr_state = ExecInitExpr ((Expr * ) expr , NULL );
@@ -595,8 +639,6 @@ partition_filter_create_scan_state(CustomScan *node)
595
639
Assert (state -> on_conflict_action >= ONCONFLICT_NONE ||
596
640
state -> on_conflict_action <= ONCONFLICT_UPDATE );
597
641
598
- state -> expr_state = NULL ;
599
-
600
642
/* There should be exactly one subplan */
601
643
Assert (list_length (node -> custom_plans ) == 1 );
602
644
@@ -607,55 +649,35 @@ void
607
649
partition_filter_begin (CustomScanState * node , EState * estate , int eflags )
608
650
{
609
651
PartitionFilterState * state = (PartitionFilterState * ) node ;
610
- const PartRelationInfo * prel ;
611
652
PlanState * child_state ;
653
+ ResultRelInfo * current_rri ;
654
+ Relation current_rel ;
655
+ const PartRelationInfo * prel ;
656
+ bool try_map ;
612
657
613
658
/* It's convenient to store PlanState in 'custom_ps' */
614
659
child_state = ExecInitNode (state -> subplan , estate , eflags );
615
660
node -> custom_ps = list_make1 (child_state );
616
661
617
- if (state -> expr_state == NULL )
618
- {
619
- /* Fetch PartRelationInfo for this partitioned relation */
620
- prel = get_pathman_relation_info (state -> partitioned_table );
662
+ /* Fetch current result relation (rri + rel) */
663
+ current_rri = estate -> es_result_relation_info ;
664
+ current_rel = current_rri -> ri_RelationDesc ;
621
665
622
- /* Prepare state for expression execution */
623
- if (state -> command_type == CMD_UPDATE )
624
- {
625
- /*
626
- * In UPDATE queries we would work with child relation, but
627
- * expression contains varattnos of base relation, so we map
628
- * parent varattnos to child varattnos.
629
- */
630
- bool found_whole_row ;
631
-
632
- AttrNumber * map ;
633
- Node * expr ;
634
- ResultRelInfo * child_rri = estate -> es_result_relation_info ;
635
- Relation child_rel = child_rri -> ri_RelationDesc ;
636
-
637
- MemoryContext old_mcxt ;
638
-
639
- map = build_attributes_map (prel , RelationGetDescr (child_rel ));
640
- expr = map_variable_attnos (PrelExpressionForRelid (prel , PART_EXPR_VARNO ),
641
- PART_EXPR_VARNO , 0 , map ,
642
- RelationGetDescr (child_rel )-> natts ,
643
- & found_whole_row );
666
+ /* Fetch PartRelationInfo for this partitioned relation */
667
+ prel = get_pathman_relation_info (state -> partitioned_table );
644
668
645
- if (found_whole_row )
646
- elog (ERROR , "unexpected whole-row reference found in partition key" );
669
+ /*
670
+ * In UPDATE queries we have to work with child relation tlist,
671
+ * but expression contains varattnos of base relation, so we
672
+ * map parent varattnos to child varattnos.
673
+ *
674
+ * We don't need map if current relation == base relation.
675
+ */
676
+ try_map = state -> command_type == CMD_UPDATE &&
677
+ RelationGetRelid (current_rel ) != state -> partitioned_table ;
647
678
648
- /* Prepare state for expression execution */
649
- old_mcxt = MemoryContextSwitchTo (estate -> es_query_cxt );
650
- state -> expr_state = ExecInitExpr ((Expr * ) expr , NULL );
651
- MemoryContextSwitchTo (old_mcxt );
652
- }
653
- else
654
- {
655
- /* Simple INSERT, expression based on parent attribute numbers */
656
- state -> expr_state = prepare_expr_state (prel , estate );
657
- }
658
- }
679
+ /* Build a partitioning expression state */
680
+ state -> expr_state = prepare_expr_state (prel , current_rel , estate , try_map );
659
681
660
682
/* Init ResultRelInfo cache */
661
683
init_result_parts_storage (& state -> result_parts , estate ,
@@ -665,6 +687,10 @@ partition_filter_begin(CustomScanState *node, EState *estate, int eflags)
665
687
(void * ) state ,
666
688
state -> command_type );
667
689
690
+ /* Don't forget to initialize 'base_rri'! */
691
+ state -> result_parts .base_rri = current_rri ;
692
+
693
+ /* No warnings yet */
668
694
state -> warning_triggered = false;
669
695
}
670
696
@@ -681,10 +707,6 @@ partition_filter_exec(CustomScanState *node)
681
707
slot = ExecProcNode (child_ps );
682
708
state -> subplan_slot = slot ;
683
709
684
- /* Don't forget to initialize 'base_rri'! */
685
- if (!state -> result_parts .base_rri )
686
- state -> result_parts .base_rri = estate -> es_result_relation_info ;
687
-
688
710
if (state -> tup_convert_slot )
689
711
ExecClearTuple (state -> tup_convert_slot );
690
712
0 commit comments