aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execExpr.c33
-rw-r--r--src/backend/executor/execExprInterp.c10
-rw-r--r--src/backend/executor/execPartition.c82
-rw-r--r--src/backend/executor/nodeModifyTable.c34
4 files changed, 109 insertions, 50 deletions
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 382ccc38d1e..5ee59ef6692 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -356,6 +356,32 @@ ExecBuildProjectionInfo(List *targetList,
PlanState *parent,
TupleDesc inputDesc)
{
+ return ExecBuildProjectionInfoExt(targetList,
+ econtext,
+ slot,
+ true,
+ parent,
+ inputDesc);
+}
+
+/*
+ * ExecBuildProjectionInfoExt
+ *
+ * As above, with one additional option.
+ *
+ * If assignJunkEntries is true (the usual case), resjunk entries in the tlist
+ * are not handled specially: they are evaluated and assigned to the proper
+ * column of the result slot. If assignJunkEntries is false, resjunk entries
+ * are evaluated, but their result is discarded without assignment.
+ */
+ProjectionInfo *
+ExecBuildProjectionInfoExt(List *targetList,
+ ExprContext *econtext,
+ TupleTableSlot *slot,
+ bool assignJunkEntries,
+ PlanState *parent,
+ TupleDesc inputDesc)
+{
ProjectionInfo *projInfo = makeNode(ProjectionInfo);
ExprState *state;
ExprEvalStep scratch = {0};
@@ -392,7 +418,8 @@ ExecBuildProjectionInfo(List *targetList,
*/
if (tle->expr != NULL &&
IsA(tle->expr, Var) &&
- ((Var *) tle->expr)->varattno > 0)
+ ((Var *) tle->expr)->varattno > 0 &&
+ (assignJunkEntries || !tle->resjunk))
{
/* Non-system Var, but how safe is it? */
variable = (Var *) tle->expr;
@@ -456,6 +483,10 @@ ExecBuildProjectionInfo(List *targetList,
ExecInitExprRec(tle->expr, state,
&state->resvalue, &state->resnull);
+ /* This makes it easy to discard resjunk results when told to. */
+ if (!assignJunkEntries && tle->resjunk)
+ continue;
+
/*
* Column might be referenced multiple times in upper nodes, so
* force value to R/O - but only if it could be an expanded datum.
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index f97ba96c125..4524034d5d0 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -581,6 +581,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
* care of at compilation time. But see EEOP_INNER_VAR comments.
*/
Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
+ Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts);
resultslot->tts_values[resultnum] = innerslot->tts_values[attnum];
resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum];
@@ -597,6 +598,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
* care of at compilation time. But see EEOP_INNER_VAR comments.
*/
Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
+ Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts);
resultslot->tts_values[resultnum] = outerslot->tts_values[attnum];
resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum];
@@ -613,6 +615,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
* care of at compilation time. But see EEOP_INNER_VAR comments.
*/
Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
+ Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts);
resultslot->tts_values[resultnum] = scanslot->tts_values[attnum];
resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum];
@@ -623,6 +626,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
{
int resultnum = op->d.assign_tmp.resultnum;
+ Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts);
resultslot->tts_values[resultnum] = state->resvalue;
resultslot->tts_isnull[resultnum] = state->resnull;
@@ -633,6 +637,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
{
int resultnum = op->d.assign_tmp.resultnum;
+ Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts);
resultslot->tts_isnull[resultnum] = state->resnull;
if (!resultslot->tts_isnull[resultnum])
resultslot->tts_values[resultnum] =
@@ -2074,8 +2079,10 @@ ExecJustAssignVarImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull)
*
* Since we use slot_getattr(), we don't need to implement the FETCHSOME
* step explicitly, and we also needn't Assert that the attnum is in range
- * --- slot_getattr() will take care of any problems.
+ * --- slot_getattr() will take care of any problems. Nonetheless, check
+ * that resultnum is in range.
*/
+ Assert(resultnum >= 0 && resultnum < outslot->tts_tupleDescriptor->natts);
outslot->tts_values[resultnum] =
slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
return 0;
@@ -2207,6 +2214,7 @@ ExecJustAssignVarVirtImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull
Assert(TTS_IS_VIRTUAL(inslot));
Assert(TTS_FIXED(inslot));
Assert(attnum >= 0 && attnum < inslot->tts_nvalid);
+ Assert(resultnum >= 0 && resultnum < outslot->tts_tupleDescriptor->natts);
outslot->tts_values[resultnum] = inslot->tts_values[attnum];
outslot->tts_isnull[resultnum] = inslot->tts_isnull[attnum];
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index e64bb2b6052..e3e6676a0e3 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -785,6 +785,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
*/
if (node->onConflictAction == ONCONFLICT_UPDATE)
{
+ OnConflictSetState *onconfl = makeNode(OnConflictSetState);
TupleConversionMap *map;
map = leaf_part_rri->ri_PartitionInfo->pi_RootToPartitionMap;
@@ -792,14 +793,14 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
Assert(node->onConflictSet != NIL);
Assert(rootResultRelInfo->ri_onConflict != NULL);
- leaf_part_rri->ri_onConflict = makeNode(OnConflictSetState);
+ leaf_part_rri->ri_onConflict = onconfl;
/*
* Need a separate existing slot for each partition, as the
* partition could be of a different AM, even if the tuple
* descriptors match.
*/
- leaf_part_rri->ri_onConflict->oc_Existing =
+ onconfl->oc_Existing =
table_slot_create(leaf_part_rri->ri_RelationDesc,
&mtstate->ps.state->es_tupleTable);
@@ -819,17 +820,16 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
* Projections and where clauses themselves don't store state
* / are independent of the underlying storage.
*/
- leaf_part_rri->ri_onConflict->oc_ProjSlot =
+ onconfl->oc_ProjSlot =
rootResultRelInfo->ri_onConflict->oc_ProjSlot;
- leaf_part_rri->ri_onConflict->oc_ProjInfo =
+ onconfl->oc_ProjInfo =
rootResultRelInfo->ri_onConflict->oc_ProjInfo;
- leaf_part_rri->ri_onConflict->oc_WhereClause =
+ onconfl->oc_WhereClause =
rootResultRelInfo->ri_onConflict->oc_WhereClause;
}
else
{
List *onconflset;
- TupleDesc tupDesc;
bool found_whole_row;
/*
@@ -839,7 +839,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
* pseudo-relation (INNER_VAR), and second to handle the main
* target relation (firstVarno).
*/
- onconflset = (List *) copyObject((Node *) node->onConflictSet);
+ onconflset = copyObject(node->onConflictSet);
if (part_attmap == NULL)
part_attmap =
build_attrmap_by_name(RelationGetDescr(partrel),
@@ -859,20 +859,19 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
&found_whole_row);
/* We ignore the value of found_whole_row. */
- /* Finally, adjust this tlist to match the partition. */
+ /* Finally, reorder the tlist to match the partition. */
onconflset = adjust_partition_tlist(onconflset, map);
/* create the tuple slot for the UPDATE SET projection */
- tupDesc = ExecTypeFromTL(onconflset);
- leaf_part_rri->ri_onConflict->oc_ProjSlot =
- ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc,
- &TTSOpsVirtual);
+ onconfl->oc_ProjSlot =
+ table_slot_create(partrel,
+ &mtstate->ps.state->es_tupleTable);
/* build UPDATE SET projection state */
- leaf_part_rri->ri_onConflict->oc_ProjInfo =
- ExecBuildProjectionInfo(onconflset, econtext,
- leaf_part_rri->ri_onConflict->oc_ProjSlot,
- &mtstate->ps, partrelDesc);
+ onconfl->oc_ProjInfo =
+ ExecBuildProjectionInfoExt(onconflset, econtext,
+ onconfl->oc_ProjSlot, false,
+ &mtstate->ps, partrelDesc);
/*
* If there is a WHERE clause, initialize state where it will
@@ -899,7 +898,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
RelationGetForm(partrel)->reltype,
&found_whole_row);
/* We ignore the value of found_whole_row. */
- leaf_part_rri->ri_onConflict->oc_WhereClause =
+ onconfl->oc_WhereClause =
ExecInitQual((List *) clause, &mtstate->ps);
}
}
@@ -1487,18 +1486,15 @@ ExecBuildSlotPartitionKeyDescription(Relation rel,
/*
* adjust_partition_tlist
- * Adjust the targetlist entries for a given partition to account for
- * attribute differences between parent and the partition
+ * Re-order the targetlist entries for a given partition to account for
+ * column position differences between the parent and the partition.
*
- * The expressions have already been fixed, but here we fix the list to make
- * target resnos match the partition's attribute numbers. This results in a
- * copy of the original target list in which the entries appear in resno
- * order, including both the existing entries (that may have their resno
- * changed in-place) and the newly added entries for columns that don't exist
- * in the parent.
+ * The expressions have already been fixed, but we must now re-order the
+ * entries in case the partition has different column order, and possibly
+ * add or remove dummy entries for dropped columns.
*
- * Scribbles on the input tlist, so callers must make sure to make a copy
- * before passing it to us.
+ * Although a new List is returned, this feels free to scribble on resno
+ * fields of the given tlist, so that should be a working copy.
*/
static List *
adjust_partition_tlist(List *tlist, TupleConversionMap *map)
@@ -1507,32 +1503,36 @@ adjust_partition_tlist(List *tlist, TupleConversionMap *map)
TupleDesc tupdesc = map->outdesc;
AttrMap *attrMap = map->attrMap;
AttrNumber attrno;
+ ListCell *lc;
Assert(tupdesc->natts == attrMap->maplen);
for (attrno = 1; attrno <= tupdesc->natts; attrno++)
{
Form_pg_attribute att_tup = TupleDescAttr(tupdesc, attrno - 1);
+ AttrNumber parentattrno = attrMap->attnums[attrno - 1];
TargetEntry *tle;
- if (attrMap->attnums[attrno - 1] != InvalidAttrNumber)
+ if (parentattrno != InvalidAttrNumber)
{
- Assert(!att_tup->attisdropped);
-
/*
* Use the corresponding entry from the parent's tlist, adjusting
- * the resno the match the partition's attno.
+ * the resno to match the partition's attno.
*/
- tle = (TargetEntry *) list_nth(tlist, attrMap->attnums[attrno - 1] - 1);
+ Assert(!att_tup->attisdropped);
+ tle = (TargetEntry *) list_nth(tlist, parentattrno - 1);
+ Assert(!tle->resjunk);
+ Assert(tle->resno == parentattrno);
tle->resno = attrno;
}
else
{
- Const *expr;
-
/*
* For a dropped attribute in the partition, generate a dummy
- * entry with resno matching the partition's attno.
+ * entry with resno matching the partition's attno. This should
+ * match what expand_targetlist() does.
*/
+ Const *expr;
+
Assert(att_tup->attisdropped);
expr = makeConst(INT4OID,
-1,
@@ -1550,6 +1550,18 @@ adjust_partition_tlist(List *tlist, TupleConversionMap *map)
new_tlist = lappend(new_tlist, tle);
}
+ /* Finally, attach any resjunk entries to the end of the new tlist */
+ foreach(lc, tlist)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (tle->resjunk)
+ {
+ tle->resno = list_length(new_tlist) + 1;
+ new_tlist = lappend(new_tlist, tle);
+ }
+ }
+
return new_tlist;
}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index cea3c13af1b..2a01449f25a 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -2608,9 +2608,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
*/
if (node->onConflictAction == ONCONFLICT_UPDATE)
{
+ OnConflictSetState *onconfl = makeNode(OnConflictSetState);
ExprContext *econtext;
TupleDesc relationDesc;
- TupleDesc tupDesc;
/* insert may only have one plan, inheritance is not expanded */
Assert(nplans == 1);
@@ -2623,10 +2623,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
/* create state for DO UPDATE SET operation */
- resultRelInfo->ri_onConflict = makeNode(OnConflictSetState);
+ resultRelInfo->ri_onConflict = onconfl;
/* initialize slot for the existing tuple */
- resultRelInfo->ri_onConflict->oc_Existing =
+ onconfl->oc_Existing =
table_slot_create(resultRelInfo->ri_RelationDesc,
&mtstate->ps.state->es_tupleTable);
@@ -2636,17 +2636,25 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* into the table, and for RETURNING processing - which may access
* system attributes.
*/
- tupDesc = ExecTypeFromTL((List *) node->onConflictSet);
- resultRelInfo->ri_onConflict->oc_ProjSlot =
- ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc,
- table_slot_callbacks(resultRelInfo->ri_RelationDesc));
+ onconfl->oc_ProjSlot =
+ table_slot_create(resultRelInfo->ri_RelationDesc,
+ &mtstate->ps.state->es_tupleTable);
+
+ /*
+ * The onConflictSet tlist should already have been adjusted to emit
+ * the table's exact column list. It could also contain resjunk
+ * columns, which should be evaluated but not included in the
+ * projection result.
+ */
+ ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc,
+ node->onConflictSet);
/* build UPDATE SET projection state */
- resultRelInfo->ri_onConflict->oc_ProjInfo =
- ExecBuildProjectionInfo(node->onConflictSet, econtext,
- resultRelInfo->ri_onConflict->oc_ProjSlot,
- &mtstate->ps,
- relationDesc);
+ onconfl->oc_ProjInfo =
+ ExecBuildProjectionInfoExt(node->onConflictSet, econtext,
+ onconfl->oc_ProjSlot, false,
+ &mtstate->ps,
+ relationDesc);
/* initialize state to evaluate the WHERE clause, if any */
if (node->onConflictWhere)
@@ -2655,7 +2663,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
qualexpr = ExecInitQual((List *) node->onConflictWhere,
&mtstate->ps);
- resultRelInfo->ri_onConflict->oc_WhereClause = qualexpr;
+ onconfl->oc_WhereClause = qualexpr;
}
}