aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execExpr.c112
-rw-r--r--src/backend/executor/execExprInterp.c10
-rw-r--r--src/backend/executor/execPartition.c123
-rw-r--r--src/backend/executor/nodeModifyTable.c29
4 files changed, 154 insertions, 120 deletions
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 77c9d785d99..8c9f8a6aeb6 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -485,14 +485,21 @@ ExecBuildProjectionInfo(List *targetList,
* be stored into the given tuple slot. (Caller must have ensured that tuple
* slot has a descriptor matching the target rel!)
*
- * subTargetList is the tlist of the subplan node feeding ModifyTable.
- * We use this mainly to cross-check that the expressions being assigned
- * are of the correct types. The values from this tlist are assumed to be
- * available from the "outer" tuple slot. They are assigned to target columns
- * listed in the corresponding targetColnos elements. (Only non-resjunk tlist
- * entries are assigned.) Columns not listed in targetColnos are filled from
- * the UPDATE's old tuple, which is assumed to be available in the "scan"
- * tuple slot.
+ * When evalTargetList is false, targetList contains the UPDATE ... SET
+ * expressions that have already been computed by a subplan node; the values
+ * from this tlist are assumed to be available in the "outer" tuple slot.
+ * When evalTargetList is true, targetList contains the UPDATE ... SET
+ * expressions that must be computed (which could contain references to
+ * the outer, inner, or scan tuple slots).
+ *
+ * In either case, targetColnos contains a list of the target column numbers
+ * corresponding to the non-resjunk entries of targetList. The tlist values
+ * are assigned into these columns of the result tuple slot. Target columns
+ * not listed in targetColnos are filled from the UPDATE's old tuple, which
+ * is assumed to be available in the "scan" tuple slot.
+ *
+ * targetList can also contain resjunk columns. These must be evaluated
+ * if evalTargetList is true, but their values are discarded.
*
* relDesc must describe the relation we intend to update.
*
@@ -503,7 +510,8 @@ ExecBuildProjectionInfo(List *targetList,
* ExecCheckPlanOutput, so we must do our safety checks here.
*/
ProjectionInfo *
-ExecBuildUpdateProjection(List *subTargetList,
+ExecBuildUpdateProjection(List *targetList,
+ bool evalTargetList,
List *targetColnos,
TupleDesc relDesc,
ExprContext *econtext,
@@ -525,19 +533,22 @@ ExecBuildUpdateProjection(List *subTargetList,
/* We embed ExprState into ProjectionInfo instead of doing extra palloc */
projInfo->pi_state.tag = T_ExprState;
state = &projInfo->pi_state;
- state->expr = NULL; /* not used */
+ if (evalTargetList)
+ state->expr = (Expr *) targetList;
+ else
+ state->expr = NULL; /* not used */
state->parent = parent;
state->ext_params = NULL;
state->resultslot = slot;
/*
- * Examine the subplan tlist to see how many non-junk columns there are,
- * and to verify that the non-junk columns come before the junk ones.
+ * Examine the targetList to see how many non-junk columns there are, and
+ * to verify that the non-junk columns come before the junk ones.
*/
nAssignableCols = 0;
sawJunk = false;
- foreach(lc, subTargetList)
+ foreach(lc, targetList)
{
TargetEntry *tle = lfirst_node(TargetEntry, lc);
@@ -569,12 +580,10 @@ ExecBuildUpdateProjection(List *subTargetList,
}
/*
- * We want to insert EEOP_*_FETCHSOME steps to ensure the outer and scan
- * tuples are sufficiently deconstructed. Outer tuple is easy, but for
- * scan tuple we must find out the last old column we need.
+ * We need to insert EEOP_*_FETCHSOME steps to ensure the input tuples are
+ * sufficiently deconstructed. The scan tuple must be deconstructed at
+ * least as far as the last old column we need.
*/
- deform.last_outer = nAssignableCols;
-
for (int attnum = relDesc->natts; attnum > 0; attnum--)
{
Form_pg_attribute attr = TupleDescAttr(relDesc, attnum - 1);
@@ -587,15 +596,26 @@ ExecBuildUpdateProjection(List *subTargetList,
break;
}
+ /*
+ * If we're actually evaluating the tlist, incorporate its input
+ * requirements too; otherwise, we'll just need to fetch the appropriate
+ * number of columns of the "outer" tuple.
+ */
+ if (evalTargetList)
+ get_last_attnums_walker((Node *) targetList, &deform);
+ else
+ deform.last_outer = nAssignableCols;
+
ExecPushExprSlots(state, &deform);
/*
- * Now generate code to fetch data from the outer tuple, incidentally
- * validating that it'll be of the right type. The checks above ensure
- * that the forboth() will iterate over exactly the non-junk columns.
+ * Now generate code to evaluate the tlist's assignable expressions or
+ * fetch them from the outer tuple, incidentally validating that they'll
+ * be of the right data type. The checks above ensure that the forboth()
+ * will iterate over exactly the non-junk columns.
*/
outerattnum = 0;
- forboth(lc, subTargetList, lc2, targetColnos)
+ forboth(lc, targetList, lc2, targetColnos)
{
TargetEntry *tle = lfirst_node(TargetEntry, lc);
AttrNumber targetattnum = lfirst_int(lc2);
@@ -628,13 +648,47 @@ ExecBuildUpdateProjection(List *subTargetList,
targetattnum,
format_type_be(exprType((Node *) tle->expr)))));
- /*
- * OK, build an outer-tuple reference.
- */
- scratch.opcode = EEOP_ASSIGN_OUTER_VAR;
- scratch.d.assign_var.attnum = outerattnum++;
- scratch.d.assign_var.resultnum = targetattnum - 1;
- ExprEvalPushStep(state, &scratch);
+ /* OK, generate code to perform the assignment. */
+ if (evalTargetList)
+ {
+ /*
+ * We must evaluate the TLE's expression and assign it. We do not
+ * bother jumping through hoops for "safe" Vars like
+ * ExecBuildProjectionInfo does; this is a relatively less-used
+ * path and it doesn't seem worth expending code for that.
+ */
+ ExecInitExprRec(tle->expr, state,
+ &state->resvalue, &state->resnull);
+ /* Needn't worry about read-only-ness here, either. */
+ scratch.opcode = EEOP_ASSIGN_TMP;
+ scratch.d.assign_tmp.resultnum = targetattnum - 1;
+ ExprEvalPushStep(state, &scratch);
+ }
+ else
+ {
+ /* Just assign from the outer tuple. */
+ scratch.opcode = EEOP_ASSIGN_OUTER_VAR;
+ scratch.d.assign_var.attnum = outerattnum;
+ scratch.d.assign_var.resultnum = targetattnum - 1;
+ ExprEvalPushStep(state, &scratch);
+ }
+ outerattnum++;
+ }
+
+ /*
+ * If we're evaluating the tlist, must evaluate any resjunk columns too.
+ * (This matters for things like MULTIEXPR_SUBLINK SubPlans.)
+ */
+ if (evalTargetList)
+ {
+ for_each_cell(lc, targetList, lc)
+ {
+ TargetEntry *tle = lfirst_node(TargetEntry, lc);
+
+ Assert(tle->resjunk);
+ ExecInitExprRec(tle->expr, state,
+ &state->resvalue, &state->resnull);
+ }
}
/*
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index a9ed98ae485..5483dee6507 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -626,6 +626,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];
@@ -642,6 +643,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];
@@ -658,6 +660,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];
@@ -668,6 +671,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;
@@ -678,6 +682,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] =
@@ -2091,8 +2096,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;
@@ -2224,6 +2231,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 8afddca73a0..8e2feafd28c 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -181,7 +181,7 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
Datum *values,
bool *isnull,
int maxfieldlen);
-static List *adjust_partition_tlist(List *tlist, TupleConversionMap *map);
+static List *adjust_partition_colnos(List *colnos, ResultRelInfo *leaf_part_rri);
static void ExecInitPruningContext(PartitionPruneContext *context,
List *pruning_steps,
PartitionDesc partdesc,
@@ -714,6 +714,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
*/
if (node->onConflictAction == ONCONFLICT_UPDATE)
{
+ OnConflictSetState *onconfl = makeNode(OnConflictSetState);
TupleConversionMap *map;
map = leaf_part_rri->ri_RootToPartitionMap;
@@ -721,14 +722,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);
@@ -748,17 +749,17 @@ 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;
+ List *onconflcols;
bool found_whole_row;
/*
@@ -768,7 +769,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),
@@ -788,20 +789,24 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
&found_whole_row);
/* We ignore the value of found_whole_row. */
- /* Finally, adjust this tlist to match the partition. */
- onconflset = adjust_partition_tlist(onconflset, map);
+ /* Finally, adjust the target colnos to match the partition. */
+ onconflcols = adjust_partition_colnos(node->onConflictCols,
+ leaf_part_rri);
/* 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 =
+ ExecBuildUpdateProjection(onconflset,
+ true,
+ onconflcols,
+ partrelDesc,
+ econtext,
+ onconfl->oc_ProjSlot,
+ &mtstate->ps);
/*
* If there is a WHERE clause, initialize state where it will
@@ -828,7 +833,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);
}
}
@@ -1421,71 +1426,35 @@ ExecBuildSlotPartitionKeyDescription(Relation rel,
}
/*
- * adjust_partition_tlist
- * Adjust the targetlist entries for a given partition to account for
- * attribute differences between 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.
- *
- * Scribbles on the input tlist, so callers must make sure to make a copy
- * before passing it to us.
+ * adjust_partition_colnos
+ * Adjust the list of UPDATE target column numbers to account for
+ * attribute differences between the parent and the partition.
*/
static List *
-adjust_partition_tlist(List *tlist, TupleConversionMap *map)
+adjust_partition_colnos(List *colnos, ResultRelInfo *leaf_part_rri)
{
- List *new_tlist = NIL;
- TupleDesc tupdesc = map->outdesc;
- AttrMap *attrMap = map->attrMap;
- AttrNumber attrno;
-
- Assert(tupdesc->natts == attrMap->maplen);
- for (attrno = 1; attrno <= tupdesc->natts; attrno++)
- {
- Form_pg_attribute att_tup = TupleDescAttr(tupdesc, attrno - 1);
- TargetEntry *tle;
-
- if (attrMap->attnums[attrno - 1] != InvalidAttrNumber)
- {
- Assert(!att_tup->attisdropped);
-
- /*
- * Use the corresponding entry from the parent's tlist, adjusting
- * the resno the match the partition's attno.
- */
- tle = (TargetEntry *) list_nth(tlist, attrMap->attnums[attrno - 1] - 1);
- tle->resno = attrno;
- }
- else
- {
- Const *expr;
+ List *new_colnos = NIL;
+ TupleConversionMap *map = ExecGetChildToRootMap(leaf_part_rri);
+ AttrMap *attrMap;
+ ListCell *lc;
- /*
- * For a dropped attribute in the partition, generate a dummy
- * entry with resno matching the partition's attno.
- */
- Assert(att_tup->attisdropped);
- expr = makeConst(INT4OID,
- -1,
- InvalidOid,
- sizeof(int32),
- (Datum) 0,
- true, /* isnull */
- true /* byval */ );
- tle = makeTargetEntry((Expr *) expr,
- attrno,
- pstrdup(NameStr(att_tup->attname)),
- false);
- }
+ Assert(map != NULL); /* else we shouldn't be here */
+ attrMap = map->attrMap;
- new_tlist = lappend(new_tlist, tle);
+ foreach(lc, colnos)
+ {
+ AttrNumber parentattrno = lfirst_int(lc);
+
+ if (parentattrno <= 0 ||
+ parentattrno > attrMap->maplen ||
+ attrMap->attnums[parentattrno - 1] == 0)
+ elog(ERROR, "unexpected attno %d in target column list",
+ parentattrno);
+ new_colnos = lappend_int(new_colnos,
+ attrMap->attnums[parentattrno - 1]);
}
- return new_tlist;
+ return new_colnos;
}
/*-------------------------------------------------------------------------
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index c5a2a9a054b..a62928ae7ce 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -492,6 +492,7 @@ ExecInitUpdateProjection(ModifyTableState *mtstate,
resultRelInfo->ri_projectNew =
ExecBuildUpdateProjection(subplan->targetlist,
+ false, /* subplan did the evaluation */
updateColnos,
relDesc,
mtstate->ps.ps_ExprContext,
@@ -2972,9 +2973,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
*/
if (node->onConflictAction == ONCONFLICT_UPDATE)
{
+ OnConflictSetState *onconfl = makeNode(OnConflictSetState);
ExprContext *econtext;
TupleDesc relationDesc;
- TupleDesc tupDesc;
/* already exists if created by RETURNING processing above */
if (mtstate->ps.ps_ExprContext == NULL)
@@ -2984,10 +2985,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);
@@ -2997,17 +2998,19 @@ 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);
/* 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 =
+ ExecBuildUpdateProjection(node->onConflictSet,
+ true,
+ node->onConflictCols,
+ relationDesc,
+ econtext,
+ onconfl->oc_ProjSlot,
+ &mtstate->ps);
/* initialize state to evaluate the WHERE clause, if any */
if (node->onConflictWhere)
@@ -3016,7 +3019,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;
}
}