|
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"
|
@@ -3735,6 +3736,7 @@ ExecInitMerge(ModifyTableState *mtstate, EState *estate)
|
3735 | 3736 | switch (action->commandType)
|
3736 | 3737 | {
|
3737 | 3738 | case CMD_INSERT:
|
| 3739 | + /* INSERT actions always use rootRelInfo */ |
3738 | 3740 | ExecCheckPlanOutput(rootRelInfo->ri_RelationDesc,
|
3739 | 3741 | action->targetList);
|
3740 | 3742 |
|
@@ -3774,9 +3776,23 @@ ExecInitMerge(ModifyTableState *mtstate, EState *estate)
|
3774 | 3776 | }
|
3775 | 3777 | else
|
3776 | 3778 | {
|
3777 |
| - /* not partitioned? use the stock relation and slot */ |
3778 |
| - tgtslot = resultRelInfo->ri_newTupleSlot; |
3779 |
| - tgtdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); |
| 3779 | + /* |
| 3780 | + * If the MERGE targets an inherited table, we insert |
| 3781 | + * into the root table, so we must initialize its |
| 3782 | + * "new" tuple slot, if not already done, and use its |
| 3783 | + * relation descriptor for the projection. |
| 3784 | + * |
| 3785 | + * For non-inherited tables, rootRelInfo and |
| 3786 | + * resultRelInfo are the same, and the "new" tuple |
| 3787 | + * slot will already have been initialized. |
| 3788 | + */ |
| 3789 | + if (rootRelInfo->ri_newTupleSlot == NULL) |
| 3790 | + rootRelInfo->ri_newTupleSlot = |
| 3791 | + table_slot_create(rootRelInfo->ri_RelationDesc, |
| 3792 | + &estate->es_tupleTable); |
| 3793 | + |
| 3794 | + tgtslot = rootRelInfo->ri_newTupleSlot; |
| 3795 | + tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc); |
3780 | 3796 | }
|
3781 | 3797 |
|
3782 | 3798 | action_state->mas_proj =
|
@@ -3809,6 +3825,114 @@ ExecInitMerge(ModifyTableState *mtstate, EState *estate)
|
3809 | 3825 | }
|
3810 | 3826 | }
|
3811 | 3827 | }
|
| 3828 | + |
| 3829 | + /* |
| 3830 | + * If the MERGE targets an inherited table, any INSERT actions will use |
| 3831 | + * rootRelInfo, and rootRelInfo will not be in the resultRelInfo array. |
| 3832 | + * Therefore we must initialize its WITH CHECK OPTION constraints and |
| 3833 | + * RETURNING projection, as ExecInitModifyTable did for the resultRelInfo |
| 3834 | + * entries. |
| 3835 | + * |
| 3836 | + * Note that the planner does not build a withCheckOptionList or |
| 3837 | + * returningList for the root relation, but as in ExecInitPartitionInfo, |
| 3838 | + * we can use the first resultRelInfo entry as a reference to calculate |
| 3839 | + * the attno's for the root table. |
| 3840 | + */ |
| 3841 | + if (rootRelInfo != mtstate->resultRelInfo && |
| 3842 | + rootRelInfo->ri_RelationDesc->rd_rel->relkind != RELKIND_PARTITIONED_TABLE && |
| 3843 | + (mtstate->mt_merge_subcommands & MERGE_INSERT) != 0) |
| 3844 | + { |
| 3845 | + ModifyTable *node = (ModifyTable *) mtstate->ps.plan; |
| 3846 | + Relation rootRelation = rootRelInfo->ri_RelationDesc; |
| 3847 | + Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc; |
| 3848 | + int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; |
| 3849 | + AttrMap *part_attmap = NULL; |
| 3850 | + bool found_whole_row; |
| 3851 | + |
| 3852 | + if (node->withCheckOptionLists != NIL) |
| 3853 | + { |
| 3854 | + List *wcoList; |
| 3855 | + List *wcoExprs = NIL; |
| 3856 | + |
| 3857 | + /* There should be as many WCO lists as result rels */ |
| 3858 | + Assert(list_length(node->withCheckOptionLists) == |
| 3859 | + list_length(node->resultRelations)); |
| 3860 | + |
| 3861 | + /* |
| 3862 | + * Use the first WCO list as a reference. In the most common case, |
| 3863 | + * this will be for the same relation as rootRelInfo, and so there |
| 3864 | + * will be no need to adjust its attno's. |
| 3865 | + */ |
| 3866 | + wcoList = linitial(node->withCheckOptionLists); |
| 3867 | + if (rootRelation != firstResultRel) |
| 3868 | + { |
| 3869 | + /* Convert any Vars in it to contain the root's attno's */ |
| 3870 | + part_attmap = |
| 3871 | + build_attrmap_by_name(RelationGetDescr(rootRelation), |
| 3872 | + RelationGetDescr(firstResultRel), |
| 3873 | + false); |
| 3874 | + |
| 3875 | + wcoList = (List *) |
| 3876 | + map_variable_attnos((Node *) wcoList, |
| 3877 | + firstVarno, 0, |
| 3878 | + part_attmap, |
| 3879 | + RelationGetForm(rootRelation)->reltype, |
| 3880 | + &found_whole_row); |
| 3881 | + } |
| 3882 | + |
| 3883 | + foreach(lc, wcoList) |
| 3884 | + { |
| 3885 | + WithCheckOption *wco = lfirst_node(WithCheckOption, lc); |
| 3886 | + ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual), |
| 3887 | + &mtstate->ps); |
| 3888 | + |
| 3889 | + wcoExprs = lappend(wcoExprs, wcoExpr); |
| 3890 | + } |
| 3891 | + |
| 3892 | + rootRelInfo->ri_WithCheckOptions = wcoList; |
| 3893 | + rootRelInfo->ri_WithCheckOptionExprs = wcoExprs; |
| 3894 | + } |
| 3895 | + |
| 3896 | + if (node->returningLists != NIL) |
| 3897 | + { |
| 3898 | + List *returningList; |
| 3899 | + |
| 3900 | + /* There should be as many returning lists as result rels */ |
| 3901 | + Assert(list_length(node->returningLists) == |
| 3902 | + list_length(node->resultRelations)); |
| 3903 | + |
| 3904 | + /* |
| 3905 | + * Use the first returning list as a reference. In the most common |
| 3906 | + * case, this will be for the same relation as rootRelInfo, and so |
| 3907 | + * there will be no need to adjust its attno's. |
| 3908 | + */ |
| 3909 | + returningList = linitial(node->returningLists); |
| 3910 | + if (rootRelation != firstResultRel) |
| 3911 | + { |
| 3912 | + /* Convert any Vars in it to contain the root's attno's */ |
| 3913 | + if (part_attmap == NULL) |
| 3914 | + part_attmap = |
| 3915 | + build_attrmap_by_name(RelationGetDescr(rootRelation), |
| 3916 | + RelationGetDescr(firstResultRel), |
| 3917 | + false); |
| 3918 | + |
| 3919 | + returningList = (List *) |
| 3920 | + map_variable_attnos((Node *) returningList, |
| 3921 | + firstVarno, 0, |
| 3922 | + part_attmap, |
| 3923 | + RelationGetForm(rootRelation)->reltype, |
| 3924 | + &found_whole_row); |
| 3925 | + } |
| 3926 | + rootRelInfo->ri_returningList = returningList; |
| 3927 | + |
| 3928 | + /* Initialize the RETURNING projection */ |
| 3929 | + rootRelInfo->ri_projectReturning = |
| 3930 | + ExecBuildProjectionInfo(returningList, econtext, |
| 3931 | + mtstate->ps.ps_ResultTupleSlot, |
| 3932 | + &mtstate->ps, |
| 3933 | + RelationGetDescr(rootRelation)); |
| 3934 | + } |
| 3935 | + } |
3812 | 3936 | }
|
3813 | 3937 |
|
3814 | 3938 | /*
|
|
0 commit comments