|
64 | 64 | #include "nodes/nodeFuncs.h"
|
65 | 65 | #include "optimizer/optimizer.h"
|
66 | 66 | #include "rewrite/rewriteHandler.h"
|
| 67 | +#include "rewrite/rewriteManip.h" |
67 | 68 | #include "storage/lmgr.h"
|
68 | 69 | #include "utils/builtins.h"
|
69 | 70 | #include "utils/datum.h"
|
@@ -3551,6 +3552,7 @@ ExecInitMerge(ModifyTableState *mtstate, EState *estate)
|
3551 | 3552 | switch (action->commandType)
|
3552 | 3553 | {
|
3553 | 3554 | case CMD_INSERT:
|
| 3555 | + /* INSERT actions always use rootRelInfo */ |
3554 | 3556 | ExecCheckPlanOutput(rootRelInfo->ri_RelationDesc,
|
3555 | 3557 | action->targetList);
|
3556 | 3558 |
|
@@ -3590,9 +3592,23 @@ ExecInitMerge(ModifyTableState *mtstate, EState *estate)
|
3590 | 3592 | }
|
3591 | 3593 | else
|
3592 | 3594 | {
|
3593 |
| - /* not partitioned? use the stock relation and slot */ |
3594 |
| - tgtslot = resultRelInfo->ri_newTupleSlot; |
3595 |
| - tgtdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); |
| 3595 | + /* |
| 3596 | + * If the MERGE targets an inherited table, we insert |
| 3597 | + * into the root table, so we must initialize its |
| 3598 | + * "new" tuple slot, if not already done, and use its |
| 3599 | + * relation descriptor for the projection. |
| 3600 | + * |
| 3601 | + * For non-inherited tables, rootRelInfo and |
| 3602 | + * resultRelInfo are the same, and the "new" tuple |
| 3603 | + * slot will already have been initialized. |
| 3604 | + */ |
| 3605 | + if (rootRelInfo->ri_newTupleSlot == NULL) |
| 3606 | + rootRelInfo->ri_newTupleSlot = |
| 3607 | + table_slot_create(rootRelInfo->ri_RelationDesc, |
| 3608 | + &estate->es_tupleTable); |
| 3609 | + |
| 3610 | + tgtslot = rootRelInfo->ri_newTupleSlot; |
| 3611 | + tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc); |
3596 | 3612 | }
|
3597 | 3613 |
|
3598 | 3614 | action_state->mas_proj =
|
@@ -3625,6 +3641,113 @@ ExecInitMerge(ModifyTableState *mtstate, EState *estate)
|
3625 | 3641 | }
|
3626 | 3642 | }
|
3627 | 3643 | }
|
| 3644 | + |
| 3645 | + /* |
| 3646 | + * If the MERGE targets an inherited table, any INSERT actions will use |
| 3647 | + * rootRelInfo, and rootRelInfo will not be in the resultRelInfo array. |
| 3648 | + * Therefore we must initialize its WITH CHECK OPTION constraints and |
| 3649 | + * RETURNING projection, as ExecInitModifyTable did for the resultRelInfo |
| 3650 | + * entries. |
| 3651 | + * |
| 3652 | + * Note that the planner does not build a withCheckOptionList or |
| 3653 | + * returningList for the root relation, but as in ExecInitPartitionInfo, |
| 3654 | + * we can use the first resultRelInfo entry as a reference to calculate |
| 3655 | + * the attno's for the root table. |
| 3656 | + */ |
| 3657 | + if (rootRelInfo != mtstate->resultRelInfo && |
| 3658 | + rootRelInfo->ri_RelationDesc->rd_rel->relkind != RELKIND_PARTITIONED_TABLE && |
| 3659 | + (mtstate->mt_merge_subcommands & MERGE_INSERT) != 0) |
| 3660 | + { |
| 3661 | + Relation rootRelation = rootRelInfo->ri_RelationDesc; |
| 3662 | + Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc; |
| 3663 | + int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; |
| 3664 | + AttrMap *part_attmap = NULL; |
| 3665 | + bool found_whole_row; |
| 3666 | + |
| 3667 | + if (node->withCheckOptionLists != NIL) |
| 3668 | + { |
| 3669 | + List *wcoList; |
| 3670 | + List *wcoExprs = NIL; |
| 3671 | + |
| 3672 | + /* There should be as many WCO lists as result rels */ |
| 3673 | + Assert(list_length(node->withCheckOptionLists) == |
| 3674 | + list_length(node->resultRelations)); |
| 3675 | + |
| 3676 | + /* |
| 3677 | + * Use the first WCO list as a reference. In the most common case, |
| 3678 | + * this will be for the same relation as rootRelInfo, and so there |
| 3679 | + * will be no need to adjust its attno's. |
| 3680 | + */ |
| 3681 | + wcoList = linitial(node->withCheckOptionLists); |
| 3682 | + if (rootRelation != firstResultRel) |
| 3683 | + { |
| 3684 | + /* Convert any Vars in it to contain the root's attno's */ |
| 3685 | + part_attmap = |
| 3686 | + build_attrmap_by_name(RelationGetDescr(rootRelation), |
| 3687 | + RelationGetDescr(firstResultRel), |
| 3688 | + false); |
| 3689 | + |
| 3690 | + wcoList = (List *) |
| 3691 | + map_variable_attnos((Node *) wcoList, |
| 3692 | + firstVarno, 0, |
| 3693 | + part_attmap, |
| 3694 | + RelationGetForm(rootRelation)->reltype, |
| 3695 | + &found_whole_row); |
| 3696 | + } |
| 3697 | + |
| 3698 | + foreach(lc, wcoList) |
| 3699 | + { |
| 3700 | + WithCheckOption *wco = lfirst_node(WithCheckOption, lc); |
| 3701 | + ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual), |
| 3702 | + &mtstate->ps); |
| 3703 | + |
| 3704 | + wcoExprs = lappend(wcoExprs, wcoExpr); |
| 3705 | + } |
| 3706 | + |
| 3707 | + rootRelInfo->ri_WithCheckOptions = wcoList; |
| 3708 | + rootRelInfo->ri_WithCheckOptionExprs = wcoExprs; |
| 3709 | + } |
| 3710 | + |
| 3711 | + if (node->returningLists != NIL) |
| 3712 | + { |
| 3713 | + List *returningList; |
| 3714 | + |
| 3715 | + /* There should be as many returning lists as result rels */ |
| 3716 | + Assert(list_length(node->returningLists) == |
| 3717 | + list_length(node->resultRelations)); |
| 3718 | + |
| 3719 | + /* |
| 3720 | + * Use the first returning list as a reference. In the most common |
| 3721 | + * case, this will be for the same relation as rootRelInfo, and so |
| 3722 | + * there will be no need to adjust its attno's. |
| 3723 | + */ |
| 3724 | + returningList = linitial(node->returningLists); |
| 3725 | + if (rootRelation != firstResultRel) |
| 3726 | + { |
| 3727 | + /* Convert any Vars in it to contain the root's attno's */ |
| 3728 | + if (part_attmap == NULL) |
| 3729 | + part_attmap = |
| 3730 | + build_attrmap_by_name(RelationGetDescr(rootRelation), |
| 3731 | + RelationGetDescr(firstResultRel), |
| 3732 | + false); |
| 3733 | + |
| 3734 | + returningList = (List *) |
| 3735 | + map_variable_attnos((Node *) returningList, |
| 3736 | + firstVarno, 0, |
| 3737 | + part_attmap, |
| 3738 | + RelationGetForm(rootRelation)->reltype, |
| 3739 | + &found_whole_row); |
| 3740 | + } |
| 3741 | + rootRelInfo->ri_returningList = returningList; |
| 3742 | + |
| 3743 | + /* Initialize the RETURNING projection */ |
| 3744 | + rootRelInfo->ri_projectReturning = |
| 3745 | + ExecBuildProjectionInfo(returningList, econtext, |
| 3746 | + mtstate->ps.ps_ResultTupleSlot, |
| 3747 | + &mtstate->ps, |
| 3748 | + RelationGetDescr(rootRelation)); |
| 3749 | + } |
| 3750 | + } |
3628 | 3751 | }
|
3629 | 3752 |
|
3630 | 3753 | /*
|
|
0 commit comments