diff options
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/execExpr.c | 112 | ||||
-rw-r--r-- | src/backend/executor/execExprInterp.c | 10 | ||||
-rw-r--r-- | src/backend/executor/execPartition.c | 123 | ||||
-rw-r--r-- | src/backend/executor/nodeModifyTable.c | 29 |
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; } } |