diff options
69 files changed, 478 insertions, 176 deletions
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index fb63471a0e0..da2a8f34c20 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -4503,7 +4503,8 @@ ProjIndexIsUnchanged(Relation relation, HeapTuple oldtup, HeapTuple newtup) List *indexoidlist = RelationGetIndexList(relation); EState *estate = CreateExecutorState(); ExprContext *econtext = GetPerTupleExprContext(estate); - TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(relation)); + TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(relation), + &TTSOpsHeapTuple); bool equals = true; Datum old_values[INDEX_MAX_KEYS]; bool old_isnull[INDEX_MAX_KEYS]; diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 4088286151a..21bdf794da6 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -2510,7 +2510,8 @@ IndexBuildHeapRangeScan(Relation heapRelation, */ estate = CreateExecutorState(); econtext = GetPerTupleExprContext(estate); - slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation)); + slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation), + &TTSOpsHeapTuple); /* Arrange for econtext's scan tuple to be the tuple under test */ econtext->ecxt_scantuple = slot; @@ -2997,7 +2998,8 @@ IndexCheckExclusion(Relation heapRelation, */ estate = CreateExecutorState(); econtext = GetPerTupleExprContext(estate); - slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation)); + slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation), + &TTSOpsHeapTuple); /* Arrange for econtext's scan tuple to be the tuple under test */ econtext->ecxt_scantuple = slot; @@ -3315,7 +3317,8 @@ validate_index_heapscan(Relation heapRelation, */ estate = CreateExecutorState(); econtext = GetPerTupleExprContext(estate); - slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation)); + slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation), + &TTSOpsHeapTuple); /* Arrange for econtext's scan tuple to be the tuple under test */ econtext->ecxt_scantuple = slot; diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index daf7ae2eb2b..c5f6efba2b6 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -95,7 +95,8 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) heapRelation = indstate->ri_RelationDesc; /* Need a slot to hold the tuple being examined */ - slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation)); + slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation), + &TTSOpsHeapTuple); ExecStoreHeapTuple(heapTuple, slot, false); /* diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 8ac868ad733..b8445dc3728 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -730,7 +730,8 @@ compute_index_stats(Relation onerel, double totalrows, estate = CreateExecutorState(); econtext = GetPerTupleExprContext(estate); /* Need a slot to hold the current heap tuple, too */ - slot = MakeSingleTupleTableSlot(RelationGetDescr(onerel)); + slot = MakeSingleTupleTableSlot(RelationGetDescr(onerel), + &TTSOpsHeapTuple); /* Arrange for econtext's scan tuple to be the tuple under test */ econtext->ecxt_scantuple = slot; diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c index f472355b48f..b0b2cb2a146 100644 --- a/src/backend/commands/constraint.c +++ b/src/backend/commands/constraint.c @@ -122,7 +122,8 @@ unique_key_recheck(PG_FUNCTION_ARGS) /* * The heap tuple must be put into a slot for FormIndexDatum. */ - slot = MakeSingleTupleTableSlot(RelationGetDescr(trigdata->tg_relation)); + slot = MakeSingleTupleTableSlot(RelationGetDescr(trigdata->tg_relation), + &TTSOpsHeapTuple); ExecStoreHeapTuple(new_row, slot, false); diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index a9471c5ef6a..e62e3d8fba2 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2488,9 +2488,11 @@ CopyFrom(CopyState cstate) ExecInitRangeTable(estate, cstate->range_table); /* Set up a tuple slot too */ - myslot = ExecInitExtraTupleSlot(estate, tupDesc); + myslot = ExecInitExtraTupleSlot(estate, tupDesc, + &TTSOpsHeapTuple); /* Triggers might need a slot as well */ - estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL); + estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL, + &TTSOpsHeapTuple); /* * Set up a ModifyTableState so we can let FDW(s) init themselves for diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 799a22e9d55..ab2c84ff98a 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -266,7 +266,8 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString, Assert(es->indent == 0); /* output tuples */ - tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt)); + tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt), + &TTSOpsVirtual); if (es->format == EXPLAIN_FORMAT_TEXT) do_text_output_multiline(tstate, es->str->data); else diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 3925fb83a54..f6e12a33532 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -2347,7 +2347,8 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver tupTypmod = HeapTupleHeaderGetTypMod(td); retdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - tstate = begin_tup_output_tupdesc(dest, retdesc); + tstate = begin_tup_output_tupdesc(dest, retdesc, + &TTSOpsHeapTuple); rettupdata.t_len = HeapTupleHeaderGetDatumLength(td); ItemPointerSetInvalid(&(rettupdata.t_self)); diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index f138e61a8d3..0efbfec4751 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -1148,7 +1148,7 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) res->err))); /* Process tables. */ - slot = MakeSingleTupleTableSlot(res->tupledesc); + slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple); while (tuplestore_gettupleslot(res->tuplestore, true, false, slot)) { char *nspname; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 73da6c39c22..a15e6045075 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -4736,8 +4736,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) * tuples are the same, the tupDescs might not be (consider ADD COLUMN * without a default). */ - oldslot = MakeSingleTupleTableSlot(oldTupDesc); - newslot = MakeSingleTupleTableSlot(newTupDesc); + oldslot = MakeSingleTupleTableSlot(oldTupDesc, &TTSOpsHeapTuple); + newslot = MakeSingleTupleTableSlot(newTupDesc, &TTSOpsHeapTuple); /* Preallocate values/isnull arrays */ i = Max(newTupDesc->natts, oldTupDesc->natts); @@ -8527,7 +8527,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); - slot = MakeSingleTupleTableSlot(tupdesc); + slot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsHeapTuple); econtext->ecxt_scantuple = slot; snapshot = RegisterSnapshot(GetLatestSnapshot()); diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index d6f33ecbd04..b91ebdb3d04 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -3525,7 +3525,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); estate->es_trig_oldtup_slot = - ExecInitExtraTupleSlot(estate, NULL); + ExecInitExtraTupleSlot(estate, NULL, &TTSOpsHeapTuple); MemoryContextSwitchTo(oldContext); } oldslot = estate->es_trig_oldtup_slot; @@ -3539,7 +3539,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); estate->es_trig_newtup_slot = - ExecInitExtraTupleSlot(estate, NULL); + ExecInitExtraTupleSlot(estate, NULL, &TTSOpsHeapTuple); MemoryContextSwitchTo(oldContext); } newslot = estate->es_trig_newtup_slot; @@ -4546,8 +4546,10 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events, ExecDropSingleTupleTableSlot(slot1); ExecDropSingleTupleTableSlot(slot2); } - slot1 = MakeSingleTupleTableSlot(rel->rd_att); - slot2 = MakeSingleTupleTableSlot(rel->rd_att); + slot1 = MakeSingleTupleTableSlot(rel->rd_att, + &TTSOpsMinimalTuple); + slot2 = MakeSingleTupleTableSlot(rel->rd_att, + &TTSOpsMinimalTuple); } if (trigdesc == NULL) /* should not happen */ elog(ERROR, "relation %u has no triggers", diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 885da18306a..cd5ee91cd43 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -2423,7 +2423,8 @@ ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, ExprState *state) scratch->d.wholerow.junkFilter = ExecInitJunkFilter(subplan->plan->targetlist, ExecGetResultType(subplan)->tdhasoid, - ExecInitExtraTupleSlot(parent->state, NULL)); + ExecInitExtraTupleSlot(parent->state, NULL, + &TTSOpsVirtual)); } } } @@ -3214,6 +3215,7 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate, */ ExprState * ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc, + const TupleTableSlotOps *lops, const TupleTableSlotOps *rops, int numCols, AttrNumber *keyColIdx, Oid *eqfunctions, diff --git a/src/backend/executor/execGrouping.c b/src/backend/executor/execGrouping.c index c4d0e040587..abce1e95cb6 100644 --- a/src/backend/executor/execGrouping.c +++ b/src/backend/executor/execGrouping.c @@ -75,7 +75,8 @@ execTuplesMatchPrepare(TupleDesc desc, eqFunctions[i] = get_opcode(eqOperators[i]); /* build actual expression */ - expr = ExecBuildGroupingEqual(desc, desc, numCols, keyColIdx, eqFunctions, + expr = ExecBuildGroupingEqual(desc, desc, NULL, NULL, + numCols, keyColIdx, eqFunctions, parent); return expr; @@ -202,10 +203,13 @@ BuildTupleHashTable(PlanState *parent, * We copy the input tuple descriptor just for safety --- we assume all * input tuples will have equivalent descriptors. */ - hashtable->tableslot = MakeSingleTupleTableSlot(CreateTupleDescCopy(inputDesc)); + hashtable->tableslot = MakeSingleTupleTableSlot(CreateTupleDescCopy(inputDesc), + &TTSOpsMinimalTuple); /* build comparator for all columns */ + /* XXX: should we support non-minimal tuples for the inputslot? */ hashtable->tab_eq_func = ExecBuildGroupingEqual(inputDesc, inputDesc, + &TTSOpsMinimalTuple, &TTSOpsMinimalTuple, numCols, keyColIdx, eqfuncoids, parent); diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 9927ad70e66..8b35bb458de 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -706,7 +706,8 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index, * to this slot. Be sure to save and restore caller's value for * scantuple. */ - existing_slot = MakeSingleTupleTableSlot(RelationGetDescr(heap)); + existing_slot = MakeSingleTupleTableSlot(RelationGetDescr(heap), + &TTSOpsHeapTuple); econtext = GetPerTupleExprContext(estate); save_scantuple = econtext->ecxt_scantuple; diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c index 57d74e57c1a..26558282e9c 100644 --- a/src/backend/executor/execJunk.c +++ b/src/backend/executor/execJunk.c @@ -78,7 +78,7 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) if (slot) ExecSetSlotDescriptor(slot, cleanTupType); else - slot = MakeSingleTupleTableSlot(cleanTupType); + slot = MakeSingleTupleTableSlot(cleanTupType, &TTSOpsVirtual); /* * Now calculate the mapping between the original tuple's attributes and @@ -149,7 +149,7 @@ ExecInitJunkFilterConversion(List *targetList, if (slot) ExecSetSlotDescriptor(slot, cleanTupType); else - slot = MakeSingleTupleTableSlot(cleanTupType); + slot = MakeSingleTupleTableSlot(cleanTupType, &TTSOpsVirtual); /* * Calculate the mapping between the original tuple's attributes and the diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index d10e533fd16..74398eb4643 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1052,10 +1052,12 @@ InitPlan(QueryDesc *queryDesc, int eflags) if (junk_filter_needed) { JunkFilter *j; + TupleTableSlot *slot; + slot = ExecInitExtraTupleSlot(estate, NULL, &TTSOpsVirtual); j = ExecInitJunkFilter(planstate->plan->targetlist, tupType->tdhasoid, - ExecInitExtraTupleSlot(estate, NULL)); + slot); estate->es_junkFilter = j; /* Want to return the cleaned tuple type */ @@ -1928,7 +1930,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, */ if (map != NULL) slot = execute_attr_map_slot(map, slot, - MakeTupleTableSlot(tupdesc)); + MakeTupleTableSlot(tupdesc, &TTSOpsVirtual)); } insertedCols = GetInsertedColumns(resultRelInfo, estate); @@ -2009,7 +2011,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo, */ if (map != NULL) slot = execute_attr_map_slot(map, slot, - MakeTupleTableSlot(tupdesc)); + MakeTupleTableSlot(tupdesc, &TTSOpsVirtual)); } insertedCols = GetInsertedColumns(resultRelInfo, estate); @@ -2059,7 +2061,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo, */ if (map != NULL) slot = execute_attr_map_slot(map, slot, - MakeTupleTableSlot(tupdesc)); + MakeTupleTableSlot(tupdesc, &TTSOpsVirtual)); } insertedCols = GetInsertedColumns(resultRelInfo, estate); @@ -2167,7 +2169,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, */ if (map != NULL) slot = execute_attr_map_slot(map, slot, - MakeTupleTableSlot(tupdesc)); + MakeTupleTableSlot(tupdesc, &TTSOpsVirtual)); } insertedCols = GetInsertedColumns(resultRelInfo, estate); diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 45fc75937ab..e11fe687121 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -144,7 +144,8 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel) * We need an additional tuple slot for storing transient tuples that * are converted to the root table descriptor. */ - proute->root_tuple_slot = MakeTupleTableSlot(RelationGetDescr(rel)); + proute->root_tuple_slot = MakeTupleTableSlot(RelationGetDescr(rel), + &TTSOpsHeapTuple); } i = 0; @@ -740,7 +741,8 @@ ExecInitRoutingInfo(ModifyTableState *mtstate, */ proute->partition_tuple_slots[partidx] = ExecInitExtraTupleSlot(estate, - RelationGetDescr(partrel)); + RelationGetDescr(partrel), + &TTSOpsHeapTuple); } /* @@ -974,7 +976,7 @@ get_partition_dispatch_recurse(Relation rel, Relation parent, * using the correct tuple descriptor when computing its partition key * for tuple routing. */ - pd->tupslot = MakeSingleTupleTableSlot(tupdesc); + pd->tupslot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsHeapTuple); pd->tupmap = convert_tuples_by_name_map_if_req(RelationGetDescr(parent), tupdesc, gettext_noop("could not convert row type")); diff --git a/src/backend/executor/execSRF.c b/src/backend/executor/execSRF.c index 3bffb0ea71f..248283f2543 100644 --- a/src/backend/executor/execSRF.c +++ b/src/backend/executor/execSRF.c @@ -873,7 +873,8 @@ ExecPrepareTuplestoreResult(SetExprState *sexpr, slotDesc = NULL; /* keep compiler quiet */ } - sexpr->funcResultSlot = MakeSingleTupleTableSlot(slotDesc); + sexpr->funcResultSlot = MakeSingleTupleTableSlot(slotDesc, + &TTSOpsMinimalTuple); MemoryContextSwitchTo(oldcontext); } diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 391db672d1d..2cd7e5c8669 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -73,6 +73,12 @@ static TupleDesc ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk); +const TupleTableSlotOps TTSOpsVirtual; +const TupleTableSlotOps TTSOpsHeapTuple; +const TupleTableSlotOps TTSOpsMinimalTuple; +const TupleTableSlotOps TTSOpsBufferTuple; + + /* ---------------------------------------------------------------- * tuple table create/delete functions * ---------------------------------------------------------------- @@ -87,7 +93,8 @@ static TupleDesc ExecTypeFromTLInternal(List *targetList, * -------------------------------- */ TupleTableSlot * -MakeTupleTableSlot(TupleDesc tupleDesc) +MakeTupleTableSlot(TupleDesc tupleDesc, + const TupleTableSlotOps *tts_ops) { Size sz; TupleTableSlot *slot; @@ -104,6 +111,8 @@ MakeTupleTableSlot(TupleDesc tupleDesc) sz = sizeof(TupleTableSlot); slot = palloc0(sz); + /* const for optimization purposes, OK to modify at allocation time */ + *((const TupleTableSlotOps **) &slot->tts_ops) = tts_ops; slot->type = T_TupleTableSlot; slot->tts_flags |= TTS_FLAG_EMPTY; if (tupleDesc != NULL) @@ -140,9 +149,10 @@ MakeTupleTableSlot(TupleDesc tupleDesc) * -------------------------------- */ TupleTableSlot * -ExecAllocTableSlot(List **tupleTable, TupleDesc desc) +ExecAllocTableSlot(List **tupleTable, TupleDesc desc, + const TupleTableSlotOps *tts_ops) { - TupleTableSlot *slot = MakeTupleTableSlot(desc); + TupleTableSlot *slot = MakeTupleTableSlot(desc, tts_ops); *tupleTable = lappend(*tupleTable, slot); @@ -198,16 +208,17 @@ ExecResetTupleTable(List *tupleTable, /* tuple table */ /* -------------------------------- * MakeSingleTupleTableSlot * - * This is a convenience routine for operations that need a - * standalone TupleTableSlot not gotten from the main executor - * tuple table. It makes a single slot and initializes it - * to use the given tuple descriptor. + * This is a convenience routine for operations that need a standalone + * TupleTableSlot not gotten from the main executor tuple table. It makes + * a single slot of given TupleTableSlotType and initializes it to use the + * given tuple descriptor. * -------------------------------- */ TupleTableSlot * -MakeSingleTupleTableSlot(TupleDesc tupdesc) +MakeSingleTupleTableSlot(TupleDesc tupdesc, + const TupleTableSlotOps *tts_ops) { - TupleTableSlot *slot = MakeTupleTableSlot(tupdesc); + TupleTableSlot *slot = MakeTupleTableSlot(tupdesc, tts_ops); return slot; } @@ -964,13 +975,17 @@ ExecInitResultTypeTL(PlanState *planstate) * ---------------- */ void -ExecInitResultSlot(PlanState *planstate) +ExecInitResultSlot(PlanState *planstate, const TupleTableSlotOps *tts_ops) { TupleTableSlot *slot; slot = ExecAllocTableSlot(&planstate->state->es_tupleTable, - planstate->ps_ResultTupleDesc); + planstate->ps_ResultTupleDesc, tts_ops); planstate->ps_ResultTupleSlot = slot; + + planstate->resultopsfixed = planstate->ps_ResultTupleDesc != NULL; + planstate->resultops = tts_ops; + planstate->resultopsset = true; } /* ---------------- @@ -980,10 +995,11 @@ ExecInitResultSlot(PlanState *planstate) * ---------------- */ void -ExecInitResultTupleSlotTL(PlanState *planstate) +ExecInitResultTupleSlotTL(PlanState *planstate, + const TupleTableSlotOps *tts_ops) { ExecInitResultTypeTL(planstate); - ExecInitResultSlot(planstate); + ExecInitResultSlot(planstate, tts_ops); } /* ---------------- @@ -991,11 +1007,15 @@ ExecInitResultTupleSlotTL(PlanState *planstate) * ---------------- */ void -ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupledesc) +ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, + TupleDesc tupledesc, const TupleTableSlotOps *tts_ops) { scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable, - tupledesc); + tupledesc, tts_ops); scanstate->ps.scandesc = tupledesc; + scanstate->ps.scanopsfixed = tupledesc != NULL; + scanstate->ps.scanops = tts_ops; + scanstate->ps.scanopsset = true; } /* ---------------- @@ -1007,9 +1027,11 @@ ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupledesc) * ---------------- */ TupleTableSlot * -ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc) +ExecInitExtraTupleSlot(EState *estate, + TupleDesc tupledesc, + const TupleTableSlotOps *tts_ops) { - return ExecAllocTableSlot(&estate->es_tupleTable, tupledesc); + return ExecAllocTableSlot(&estate->es_tupleTable, tupledesc, tts_ops); } /* ---------------- @@ -1021,9 +1043,10 @@ ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc) * ---------------- */ TupleTableSlot * -ExecInitNullTupleSlot(EState *estate, TupleDesc tupType) +ExecInitNullTupleSlot(EState *estate, TupleDesc tupType, + const TupleTableSlotOps *tts_ops) { - TupleTableSlot *slot = ExecInitExtraTupleSlot(estate, tupType); + TupleTableSlot *slot = ExecInitExtraTupleSlot(estate, tupType, tts_ops); return ExecStoreAllNullTuple(slot); } @@ -1547,13 +1570,15 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) * table function capability. Currently used by EXPLAIN and SHOW ALL. */ TupOutputState * -begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc) +begin_tup_output_tupdesc(DestReceiver *dest, + TupleDesc tupdesc, + const TupleTableSlotOps *tts_ops) { TupOutputState *tstate; tstate = (TupOutputState *) palloc(sizeof(TupOutputState)); - tstate->slot = MakeSingleTupleTableSlot(tupdesc); + tstate->slot = MakeSingleTupleTableSlot(tupdesc, tts_ops); tstate->dest = dest; tstate->dest->rStartup(tstate->dest, (int) CMD_SELECT, tupdesc); diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index f9e7bb479f1..f39be12c54d 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -454,6 +454,35 @@ ExecGetResultType(PlanState *planstate) return planstate->ps_ResultTupleDesc; } +/* + * ExecGetResultSlotOps - information about node's type of result slot + */ +const TupleTableSlotOps * +ExecGetResultSlotOps(PlanState *planstate, bool *isfixed) +{ + if (planstate->resultopsset && planstate->resultops) + { + if (isfixed) + *isfixed = planstate->resultopsfixed; + return planstate->resultops; + } + + if (isfixed) + { + if (planstate->resultopsset) + *isfixed = planstate->resultopsfixed; + else if (planstate->ps_ResultTupleSlot) + *isfixed = TTS_FIXED(planstate->ps_ResultTupleSlot); + else + *isfixed = false; + } + + if (!planstate->ps_ResultTupleSlot) + return &TTSOpsVirtual; + + return planstate->ps_ResultTupleSlot->tts_ops; +} + /* ---------------- * ExecAssignProjectionInfo @@ -492,11 +521,21 @@ ExecConditionalAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc, planstate->plan->targetlist, varno, inputDesc)) + { planstate->ps_ProjInfo = NULL; + planstate->resultopsset = planstate->scanopsset; + planstate->resultopsfixed = planstate->scanopsfixed; + planstate->resultops = planstate->scanops; + } else { if (!planstate->ps_ResultTupleSlot) - ExecInitResultSlot(planstate); + { + ExecInitResultSlot(planstate, &TTSOpsVirtual); + planstate->resultops = &TTSOpsVirtual; + planstate->resultopsfixed = true; + planstate->resultopsset = true; + } ExecAssignProjectionInfo(planstate, inputDesc); } } @@ -611,7 +650,9 @@ ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc) * ---------------- */ void -ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate) +ExecCreateScanSlotFromOuterPlan(EState *estate, + ScanState *scanstate, + const TupleTableSlotOps *tts_ops) { PlanState *outerPlan; TupleDesc tupDesc; @@ -619,7 +660,7 @@ ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate) outerPlan = outerPlanState(scanstate); tupDesc = ExecGetResultType(outerPlan); - ExecInitScanTupleSlot(estate, scanstate, tupDesc); + ExecInitScanTupleSlot(estate, scanstate, tupDesc, tts_ops); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index f4dd5732198..ae5c7c5490b 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -1717,7 +1717,8 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, /* Set up junk filter if needed */ if (junkFilter) - *junkFilter = ExecInitJunkFilter(tlist, false, NULL); + *junkFilter = ExecInitJunkFilter(tlist, false, + MakeSingleTupleTableSlot(NULL, &TTSOpsMinimalTuple)); } else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) { @@ -1770,7 +1771,12 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, } /* Set up junk filter if needed */ if (junkFilter) - *junkFilter = ExecInitJunkFilter(tlist, false, NULL); + { + TupleTableSlot *slot = + MakeSingleTupleTableSlot(NULL, &TTSOpsMinimalTuple); + + *junkFilter = ExecInitJunkFilter(tlist, false, slot); + } return false; /* NOT returning whole tuple */ } } @@ -1786,7 +1792,12 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, * what the caller expects will happen at runtime. */ if (junkFilter) - *junkFilter = ExecInitJunkFilter(tlist, false, NULL); + { + TupleTableSlot *slot; + + slot = MakeSingleTupleTableSlot(NULL, &TTSOpsMinimalTuple); + *junkFilter = ExecInitJunkFilter(tlist, false, slot); + } return true; } Assert(tupdesc); @@ -1927,9 +1938,14 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, /* Set up junk filter if needed */ if (junkFilter) + { + TupleTableSlot *slot = + MakeSingleTupleTableSlot(NULL, &TTSOpsMinimalTuple); + *junkFilter = ExecInitJunkFilterConversion(tlist, CreateTupleDescCopy(tupdesc), - NULL); + slot); + } /* Report that we are returning entire tuple result */ return true; diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 85f1ec7140f..20d6d8e9cbb 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1403,7 +1403,8 @@ find_hash_columns(AggState *aggstate) &perhash->eqfuncoids, &perhash->hashfunctions); perhash->hashslot = - ExecAllocTableSlot(&estate->es_tupleTable, hashDesc); + ExecAllocTableSlot(&estate->es_tupleTable, hashDesc, + &TTSOpsMinimalTuple); list_free(hashTlist); bms_free(colnos); @@ -2211,15 +2212,17 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) /* * initialize source tuple type. */ - ExecCreateScanSlotFromOuterPlan(estate, &aggstate->ss); + ExecCreateScanSlotFromOuterPlan(estate, &aggstate->ss, + ExecGetResultSlotOps(outerPlanState(&aggstate->ss), NULL)); scanDesc = aggstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor; if (node->chain) - aggstate->sort_slot = ExecInitExtraTupleSlot(estate, scanDesc); + aggstate->sort_slot = ExecInitExtraTupleSlot(estate, scanDesc, + &TTSOpsMinimalTuple); /* * Initialize result type, slot and projection. */ - ExecInitResultTupleSlotTL(&aggstate->ss.ps); + ExecInitResultTupleSlotTL(&aggstate->ss.ps, &TTSOpsVirtual); ExecAssignProjectionInfo(&aggstate->ss.ps, NULL); /* @@ -3062,7 +3065,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, { pertrans->sortdesc = ExecTypeFromTL(aggref->args, false); pertrans->sortslot = - ExecInitExtraTupleSlot(estate, pertrans->sortdesc); + ExecInitExtraTupleSlot(estate, pertrans->sortdesc, + &TTSOpsMinimalTuple); } if (numSortCols > 0) @@ -3084,7 +3088,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, { /* we will need an extra slot to store prior values */ pertrans->uniqslot = - ExecInitExtraTupleSlot(estate, pertrans->sortdesc); + ExecInitExtraTupleSlot(estate, pertrans->sortdesc, + &TTSOpsMinimalTuple); } /* Extract the sort information for use later */ diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 94a17c7c67c..8c4abe12882 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -196,7 +196,11 @@ ExecInitAppend(Append *node, EState *estate, int eflags) /* * Initialize result tuple type and slot. */ - ExecInitResultTupleSlotTL(&appendstate->ps); + ExecInitResultTupleSlotTL(&appendstate->ps, &TTSOpsVirtual); + + /* node returns slots from each of its subnodes, therefore not fixed */ + appendstate->ps.resultopsset = true; + appendstate->ps.resultopsfixed = false; appendplanstates = (PlanState **) palloc(nplans * sizeof(PlanState *)); diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index c153d74f411..1c27bfc412c 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -913,7 +913,8 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) * get the scan type from the relation descriptor. */ ExecInitScanTupleSlot(estate, &scanstate->ss, - RelationGetDescr(currentRelation)); + RelationGetDescr(currentRelation), + &TTSOpsBufferTuple); /* diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 017b8772775..133aaf2d3b7 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -260,7 +260,8 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) * table) is the same as the result rowtype of the CTE query. */ ExecInitScanTupleSlot(estate, &scanstate->ss, - ExecGetResultType(scanstate->cteplanstate)); + ExecGetResultType(scanstate->cteplanstate), + &TTSOpsMinimalTuple); /* * Initialize result type and projection. diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index ab3e34790e8..a2e67074a87 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -73,13 +73,14 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) TupleDesc scan_tupdesc; scan_tupdesc = ExecTypeFromTL(cscan->custom_scan_tlist, false); - ExecInitScanTupleSlot(estate, &css->ss, scan_tupdesc); + ExecInitScanTupleSlot(estate, &css->ss, scan_tupdesc, &TTSOpsVirtual); /* Node's targetlist will contain Vars with varno = INDEX_VAR */ tlistvarno = INDEX_VAR; } else { - ExecInitScanTupleSlot(estate, &css->ss, RelationGetDescr(scan_rel)); + ExecInitScanTupleSlot(estate, &css->ss, RelationGetDescr(scan_rel), + &TTSOpsVirtual); /* Node's targetlist will contain Vars with varno = scanrelid */ tlistvarno = scanrelid; } @@ -87,7 +88,7 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) /* * Initialize result slot, type and projection. */ - ExecInitResultTupleSlotTL(&css->ss.ps); + ExecInitResultTupleSlotTL(&css->ss.ps, &TTSOpsVirtual); ExecAssignScanProjectionInfoWithVarno(&css->ss, tlistvarno); /* initialize child expressions */ diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index f7eef32f6fe..a2ab2d265b3 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -180,7 +180,8 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) TupleDesc scan_tupdesc; scan_tupdesc = ExecTypeFromTL(node->fdw_scan_tlist, false); - ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc); + ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc, + &TTSOpsHeapTuple); /* Node's targetlist will contain Vars with varno = INDEX_VAR */ tlistvarno = INDEX_VAR; } @@ -190,11 +191,16 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) /* don't trust FDWs to return tuples fulfilling NOT NULL constraints */ scan_tupdesc = CreateTupleDescCopy(RelationGetDescr(currentRelation)); - ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc); + ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc, + &TTSOpsHeapTuple); /* Node's targetlist will contain Vars with varno = scanrelid */ tlistvarno = scanrelid; } + /* Don't know what an FDW might return */ + scanstate->ss.ps.scanopsfixed = false; + scanstate->ss.ps.scanopsset = true; + /* * Initialize result slot, type and projection. */ diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index 0596adbb2f1..b6a1fa14560 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -424,7 +424,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) */ if (!scanstate->simple) { - fs->func_slot = ExecInitExtraTupleSlot(estate, fs->tupdesc); + fs->func_slot = ExecInitExtraTupleSlot(estate, fs->tupdesc, + &TTSOpsMinimalTuple); } else fs->func_slot = NULL; @@ -482,7 +483,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) /* * Initialize scan slot and type. */ - ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc); + ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc, + &TTSOpsMinimalTuple); /* * Initialize result slot, type and projection. diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index afddb0a0394..e45e07f0a1a 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -91,6 +91,11 @@ ExecInitGather(Gather *node, EState *estate, int eflags) outerPlanState(gatherstate) = ExecInitNode(outerNode, estate, eflags); tupDesc = ExecGetResultType(outerPlanState(gatherstate)); + /* this node uses tuples from the tuple queue as scan slot */ + gatherstate->ps.scanops = &TTSOpsHeapTuple; + gatherstate->ps.scanopsfixed = true; + gatherstate->ps.scanopsset = true; + /* * Initialize result type and projection. */ @@ -100,7 +105,8 @@ ExecInitGather(Gather *node, EState *estate, int eflags) /* * Initialize funnel slot to same tuple descriptor as outer plan. */ - gatherstate->funnel_slot = ExecInitExtraTupleSlot(estate, tupDesc); + gatherstate->funnel_slot = ExecInitExtraTupleSlot(estate, tupDesc, + &TTSOpsHeapTuple); /* * Gather doesn't support checking a qual (it's always more efficient to diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c index 7ae067f9ebf..651565123be 100644 --- a/src/backend/executor/nodeGatherMerge.c +++ b/src/backend/executor/nodeGatherMerge.c @@ -122,6 +122,13 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags) ExecInitResultTypeTL(&gm_state->ps); ExecConditionalAssignProjectionInfo(&gm_state->ps, tupDesc, OUTER_VAR); + /* leader accesses ExecProcNode result directly, others go through tuple queue */ + if (gm_state->ps.ps_ProjInfo == NULL) + { + gm_state->ps.resultopsset = true; + gm_state->ps.resultopsfixed = false; + } + /* * initialize sort-key information */ @@ -404,7 +411,8 @@ gather_merge_setup(GatherMergeState *gm_state) /* Initialize tuple slot for worker */ gm_state->gm_slots[i + 1] = - ExecInitExtraTupleSlot(gm_state->ps.state, gm_state->tupDesc); + ExecInitExtraTupleSlot(gm_state->ps.state, gm_state->tupDesc, + &TTSOpsHeapTuple); } /* Allocate the resources for the merge */ diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index 9c1e51bc954..58e0b10cd16 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -162,6 +162,7 @@ GroupState * ExecInitGroup(Group *node, EState *estate, int eflags) { GroupState *grpstate; + const TupleTableSlotOps *tts_ops; /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); @@ -188,12 +189,13 @@ ExecInitGroup(Group *node, EState *estate, int eflags) /* * Initialize scan slot and type. */ - ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss); + tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL); + ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops); /* * Initialize result slot, type and projection. */ - ExecInitResultTupleSlotTL(&grpstate->ss.ps); + ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual); ExecAssignProjectionInfo(&grpstate->ss.ps, NULL); /* diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 5a9f1ea3c55..ba2f6686cf6 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -382,7 +382,7 @@ ExecInitHash(Hash *node, EState *estate, int eflags) * initialize our result slot and type. No need to build projection * because this node doesn't do projections. */ - ExecInitResultTupleSlotTL(&hashstate->ps); + ExecInitResultTupleSlotTL(&hashstate->ps, &TTSOpsMinimalTuple); hashstate->ps.ps_ProjInfo = NULL; /* diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index d6a6ef770dd..c78b92d8a60 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -606,6 +606,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) TupleDesc outerDesc, innerDesc; ListCell *l; + const TupleTableSlotOps *ops; /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); @@ -650,13 +651,15 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) /* * Initialize result slot, type and projection. */ - ExecInitResultTupleSlotTL(&hjstate->js.ps); + ExecInitResultTupleSlotTL(&hjstate->js.ps, &TTSOpsVirtual); ExecAssignProjectionInfo(&hjstate->js.ps, NULL); /* * tuple table initialization */ - hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate, outerDesc); + ops = ExecGetResultSlotOps(outerPlanState(hjstate), NULL); + hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate, outerDesc, + ops); /* * detect whether we need only consider the first matching inner tuple @@ -673,17 +676,17 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) case JOIN_LEFT: case JOIN_ANTI: hjstate->hj_NullInnerTupleSlot = - ExecInitNullTupleSlot(estate, innerDesc); + ExecInitNullTupleSlot(estate, innerDesc, &TTSOpsVirtual); break; case JOIN_RIGHT: hjstate->hj_NullOuterTupleSlot = - ExecInitNullTupleSlot(estate, outerDesc); + ExecInitNullTupleSlot(estate, outerDesc, &TTSOpsVirtual); break; case JOIN_FULL: hjstate->hj_NullOuterTupleSlot = - ExecInitNullTupleSlot(estate, outerDesc); + ExecInitNullTupleSlot(estate, outerDesc, &TTSOpsVirtual); hjstate->hj_NullInnerTupleSlot = - ExecInitNullTupleSlot(estate, innerDesc); + ExecInitNullTupleSlot(estate, innerDesc, &TTSOpsVirtual); break; default: elog(ERROR, "unrecognized join type: %d", diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 865a056c027..4e5e52cec3b 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -527,7 +527,7 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) * suitable data anyway.) */ tupDesc = ExecTypeFromTL(node->indextlist, false); - ExecInitScanTupleSlot(estate, &indexstate->ss, tupDesc); + ExecInitScanTupleSlot(estate, &indexstate->ss, tupDesc, &TTSOpsHeapTuple); /* * Initialize result type and projection info. The node's targetlist will diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 8593c0e3050..479cbf9df4f 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -208,8 +208,6 @@ IndexNextWithReorder(IndexScanState *node) scandesc = node->iss_ScanDesc; econtext = node->ss.ps.ps_ExprContext; - slot = node->ss.ss_ScanTupleSlot; - if (scandesc == NULL) { /* @@ -245,6 +243,7 @@ IndexNextWithReorder(IndexScanState *node) */ if (!pairingheap_is_empty(node->iss_ReorderQueue)) { + slot = node->iss_ReorderQueueSlot; topmost = (ReorderTuple *) pairingheap_first(node->iss_ReorderQueue); if (node->iss_ReachedEnd || @@ -264,13 +263,15 @@ IndexNextWithReorder(IndexScanState *node) else if (node->iss_ReachedEnd) { /* Queue is empty, and no more tuples from index. We're done. */ - return ExecClearTuple(slot); + ExecClearTuple(node->iss_ReorderQueueSlot); + return ExecClearTuple(node->ss.ss_ScanTupleSlot); } /* * Fetch next tuple from the index. */ next_indextuple: + slot = node->ss.ss_ScanTupleSlot; tuple = index_getnext(scandesc, ForwardScanDirection); if (!tuple) { @@ -372,7 +373,8 @@ next_indextuple: * if we get here it means the index scan failed so we are at the end of * the scan.. */ - return ExecClearTuple(slot); + ExecClearTuple(node->iss_ReorderQueueSlot); + return ExecClearTuple(node->ss.ss_ScanTupleSlot); } /* @@ -824,6 +826,8 @@ ExecEndIndexScan(IndexScanState *node) */ if (node->ss.ps.ps_ResultTupleSlot) ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); + if (node->iss_ReorderQueueSlot) + ExecClearTuple(node->iss_ReorderQueueSlot); ExecClearTuple(node->ss.ss_ScanTupleSlot); /* @@ -945,7 +949,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) * get the scan type from the relation descriptor. */ ExecInitScanTupleSlot(estate, &indexstate->ss, - RelationGetDescr(currentRelation)); + RelationGetDescr(currentRelation), + &TTSOpsBufferTuple); /* * Initialize result type and projection. @@ -1076,9 +1081,13 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) indexstate->iss_OrderByNulls = (bool *) palloc(numOrderByKeys * sizeof(bool)); - /* and initialize the reorder queue */ + /* and initialize the reorder queue and the corresponding slot */ indexstate->iss_ReorderQueue = pairingheap_allocate(reorderqueue_cmp, indexstate); + indexstate->iss_ReorderQueueSlot = + ExecAllocTableSlot(&estate->es_tupleTable, + RelationGetDescr(currentRelation), + &TTSOpsHeapTuple); } /* diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c index f0b68191400..6792f9e86c7 100644 --- a/src/backend/executor/nodeLimit.c +++ b/src/backend/executor/nodeLimit.c @@ -380,6 +380,10 @@ ExecInitLimit(Limit *node, EState *estate, int eflags) */ ExecInitResultTypeTL(&limitstate->ps); + limitstate->ps.resultopsset = true; + limitstate->ps.resultops = ExecGetResultSlotOps(outerPlanState(limitstate), + &limitstate->ps.resultopsfixed); + /* * limit nodes do no projections, so initialize projection info for this * node appropriately diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c index 961798cecb3..7887388b9e9 100644 --- a/src/backend/executor/nodeLockRows.c +++ b/src/backend/executor/nodeLockRows.c @@ -390,6 +390,11 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags) */ outerPlanState(lrstate) = ExecInitNode(outerPlan, estate, eflags); + /* node returns unmodified slots from the outer plan */ + lrstate->ps.resultopsset = true; + lrstate->ps.resultops = ExecGetResultSlotOps(outerPlanState(lrstate), + &lrstate->ps.resultopsfixed); + /* * LockRows nodes do no projections, so initialize projection info for * this node appropriately diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c index 4ede428f908..657814cd0db 100644 --- a/src/backend/executor/nodeMaterial.c +++ b/src/backend/executor/nodeMaterial.c @@ -146,10 +146,8 @@ ExecMaterial(PlanState *pstate) if (tuplestorestate) tuplestore_puttupleslot(tuplestorestate, outerslot); - /* - * We can just return the subplan's returned tuple, without copying. - */ - return outerslot; + ExecCopySlot(slot, outerslot); + return slot; } /* @@ -223,13 +221,13 @@ ExecInitMaterial(Material *node, EState *estate, int eflags) * * material nodes only return tuples from their materialized relation. */ - ExecInitResultTupleSlotTL(&matstate->ss.ps); + ExecInitResultTupleSlotTL(&matstate->ss.ps, &TTSOpsMinimalTuple); matstate->ss.ps.ps_ProjInfo = NULL; /* * initialize tuple type. */ - ExecCreateScanSlotFromOuterPlan(estate, &matstate->ss); + ExecCreateScanSlotFromOuterPlan(estate, &matstate->ss, &TTSOpsMinimalTuple); return matstate; } diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c index dbed667d164..188e76b60cd 100644 --- a/src/backend/executor/nodeMergeAppend.c +++ b/src/backend/executor/nodeMergeAppend.c @@ -167,7 +167,11 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags) * MergeAppend nodes do have Result slots, which hold pointers to tuples, * so we have to initialize them. FIXME */ - ExecInitResultTupleSlotTL(&mergestate->ps); + ExecInitResultTupleSlotTL(&mergestate->ps, &TTSOpsVirtual); + + /* node returns slots from each of its subnodes, therefore not fixed */ + mergestate->ps.resultopsset = true; + mergestate->ps.resultopsfixed = false; /* * call ExecInitNode on each of the valid plans to be executed and save diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 9c978313318..1c90291d127 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -1438,6 +1438,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) MergeJoinState *mergestate; TupleDesc outerDesc, innerDesc; + const TupleTableSlotOps *innerOps; /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); @@ -1512,13 +1513,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) /* * Initialize result slot, type and projection. */ - ExecInitResultTupleSlotTL(&mergestate->js.ps); + ExecInitResultTupleSlotTL(&mergestate->js.ps, &TTSOpsVirtual); ExecAssignProjectionInfo(&mergestate->js.ps, NULL); /* * tuple table initialization */ - mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate, innerDesc); + innerOps = ExecGetResultSlotOps(innerPlanState(mergestate), NULL); + mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate, innerDesc, + innerOps); /* * initialize child expressions @@ -1548,13 +1551,13 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) mergestate->mj_FillOuter = true; mergestate->mj_FillInner = false; mergestate->mj_NullInnerTupleSlot = - ExecInitNullTupleSlot(estate, innerDesc); + ExecInitNullTupleSlot(estate, innerDesc, &TTSOpsVirtual); break; case JOIN_RIGHT: mergestate->mj_FillOuter = false; mergestate->mj_FillInner = true; mergestate->mj_NullOuterTupleSlot = - ExecInitNullTupleSlot(estate, outerDesc); + ExecInitNullTupleSlot(estate, outerDesc, &TTSOpsVirtual); /* * Can't handle right or full join with non-constant extra @@ -1570,9 +1573,9 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) mergestate->mj_FillOuter = true; mergestate->mj_FillInner = true; mergestate->mj_NullOuterTupleSlot = - ExecInitNullTupleSlot(estate, outerDesc); + ExecInitNullTupleSlot(estate, outerDesc, &TTSOpsVirtual); mergestate->mj_NullInnerTupleSlot = - ExecInitNullTupleSlot(estate, innerDesc); + ExecInitNullTupleSlot(estate, innerDesc, &TTSOpsVirtual); /* * Can't handle right or full join with non-constant extra diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index a3ca336f2ac..bb344a7070a 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -2056,6 +2056,15 @@ ExecModifyTable(PlanState *pstate) } /* + * Ensure input tuple is the right format for the target relation. + */ + if (node->mt_scans[node->mt_whichplan]->tts_ops != planSlot->tts_ops) + { + ExecCopySlot(node->mt_scans[node->mt_whichplan], planSlot); + planSlot = node->mt_scans[node->mt_whichplan]; + } + + /* * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do * here is compute the RETURNING expressions. */ @@ -2238,6 +2247,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans); mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex; + mtstate->mt_scans = (TupleTableSlot **) palloc0(sizeof(TupleTableSlot *) * nplans); /* If modifying a partitioned table, initialize the root table info */ if (node->rootResultRelIndex >= 0) @@ -2304,6 +2314,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) /* Now init the plan for this result rel */ estate->es_result_relation_info = resultRelInfo; mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags); + mtstate->mt_scans[i] = + ExecInitExtraTupleSlot(mtstate->ps.state, ExecGetResultType(mtstate->mt_plans[i]), + &TTSOpsHeapTuple); /* Also let FDWs init themselves for foreign-table result rels */ if (!resultRelInfo->ri_usesFdwDirectModify && @@ -2403,7 +2416,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists); /* Set up a slot for the output of the RETURNING projection(s) */ - ExecInitResultTupleSlotTL(&mtstate->ps); + ExecInitResultTupleSlotTL(&mtstate->ps, &TTSOpsVirtual); slot = mtstate->ps.ps_ResultTupleSlot; /* Need an econtext too */ @@ -2472,7 +2485,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) mtstate->mt_existing = ExecInitExtraTupleSlot(mtstate->ps.state, mtstate->mt_partition_tuple_routing ? - NULL : relationDesc); + NULL : relationDesc, &TTSOpsBufferTuple); /* carried forward solely for the benefit of explain */ mtstate->mt_excludedtlist = node->exclRelTlist; @@ -2494,7 +2507,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state, mtstate->mt_partition_tuple_routing ? - NULL : tupDesc); + NULL : tupDesc, &TTSOpsHeapTuple); resultRelInfo->ri_onConflict->oc_ProjTupdesc = tupDesc; /* build UPDATE SET projection state */ @@ -2605,7 +2618,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) j = ExecInitJunkFilter(subplan->targetlist, resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, - ExecInitExtraTupleSlot(estate, NULL)); + ExecInitExtraTupleSlot(estate, NULL, + &TTSOpsHeapTuple)); if (operation == CMD_UPDATE || operation == CMD_DELETE) { @@ -2652,10 +2666,12 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) /* * Set up a tuple table slot for use for trigger output tuples. In a plan * containing multiple ModifyTable nodes, all can share one such slot, so - * we keep it in the estate. + * we keep it in the estate. The tuple being inserted doesn't come from a + * buffer. */ if (estate->es_trig_tuple_slot == NULL) - estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL); + estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL, + &TTSOpsHeapTuple); /* * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it diff --git a/src/backend/executor/nodeNamedtuplestorescan.c b/src/backend/executor/nodeNamedtuplestorescan.c index cf1b7b4f872..cd8a1fceac5 100644 --- a/src/backend/executor/nodeNamedtuplestorescan.c +++ b/src/backend/executor/nodeNamedtuplestorescan.c @@ -137,7 +137,8 @@ ExecInitNamedTuplestoreScan(NamedTuplestoreScan *node, EState *estate, int eflag /* * The scan tuple type is specified for the tuplestore. */ - ExecInitScanTupleSlot(estate, &scanstate->ss, scanstate->tupdesc); + ExecInitScanTupleSlot(estate, &scanstate->ss, scanstate->tupdesc, + &TTSOpsMinimalTuple); /* * Initialize result type and projection. diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 8dbec685eb1..0cb6f9dd4cd 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -304,7 +304,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) /* * Initialize result slot, type and projection. */ - ExecInitResultTupleSlotTL(&nlstate->js.ps); + ExecInitResultTupleSlotTL(&nlstate->js.ps, &TTSOpsVirtual); ExecAssignProjectionInfo(&nlstate->js.ps, NULL); /* @@ -332,7 +332,8 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) case JOIN_ANTI: nlstate->nl_NullInnerTupleSlot = ExecInitNullTupleSlot(estate, - ExecGetResultType(innerPlanState(nlstate))); + ExecGetResultType(innerPlanState(nlstate)), + &TTSOpsVirtual); break; default: elog(ERROR, "unrecognized join type: %d", diff --git a/src/backend/executor/nodeProjectSet.c b/src/backend/executor/nodeProjectSet.c index e4dd4142177..06a7da8f77e 100644 --- a/src/backend/executor/nodeProjectSet.c +++ b/src/backend/executor/nodeProjectSet.c @@ -256,7 +256,7 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags) /* * tuple table and result type initialization */ - ExecInitResultTupleSlotTL(&state->ps); + ExecInitResultTupleSlotTL(&state->ps, &TTSOpsVirtual); /* Create workspace for per-tlist-entry expr state & SRF-is-done state */ state->nelems = list_length(node->plan.targetlist); diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 2bbb2e78848..63428c7ffe0 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -217,7 +217,7 @@ ExecInitResult(Result *node, EState *estate, int eflags) /* * Initialize result slot, type and projection. */ - ExecInitResultTupleSlotTL(&resstate->ps); + ExecInitResultTupleSlotTL(&resstate->ps, &TTSOpsVirtual); ExecAssignProjectionInfo(&resstate->ps, NULL); /* diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index cfa26535d7b..55e7bd2f6cf 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -146,7 +146,8 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) /* and create slot with appropriate rowtype */ ExecInitScanTupleSlot(estate, &scanstate->ss, - RelationGetDescr(scanstate->ss.ss_currentRelation)); + RelationGetDescr(scanstate->ss.ss_currentRelation), + &TTSOpsBufferTuple); /* * Initialize result type and projection. diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index b4bea67610f..307ad9ccd53 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -172,7 +172,8 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) /* and create slot with the appropriate rowtype */ ExecInitScanTupleSlot(estate, &scanstate->ss, - RelationGetDescr(scanstate->ss.ss_currentRelation)); + RelationGetDescr(scanstate->ss.ss_currentRelation), + &TTSOpsBufferTuple); /* * Initialize result type and projection. diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c index 46bf77775c4..44c43e99a02 100644 --- a/src/backend/executor/nodeSetOp.c +++ b/src/backend/executor/nodeSetOp.c @@ -532,7 +532,9 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags) * Initialize result slot and type. Setop nodes do no projections, so * initialize projection info for this node appropriately. */ - ExecInitResultTupleSlotTL(&setopstate->ps); + ExecInitResultTupleSlotTL(&setopstate->ps, + node->strategy == SETOP_HASHED ? + &TTSOpsMinimalTuple : &TTSOpsHeapTuple); setopstate->ps.ps_ProjInfo = NULL; /* diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c index 5492cd45579..750292b1014 100644 --- a/src/backend/executor/nodeSort.c +++ b/src/backend/executor/nodeSort.c @@ -211,13 +211,13 @@ ExecInitSort(Sort *node, EState *estate, int eflags) /* * Initialize scan slot and type. */ - ExecCreateScanSlotFromOuterPlan(estate, &sortstate->ss); + ExecCreateScanSlotFromOuterPlan(estate, &sortstate->ss, &TTSOpsVirtual); /* * Initialize return slot and type. No need to initialize projection info * because this node doesn't do projections. */ - ExecInitResultTupleSlotTL(&sortstate->ss.ps); + ExecInitResultTupleSlotTL(&sortstate->ss.ps, &TTSOpsMinimalTuple); sortstate->ss.ps.ps_ProjInfo = NULL; SO1_printf("ExecInitSort: %s\n", diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 63de981034d..b42e60576e3 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -968,7 +968,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * own innerecontext. */ tupDescLeft = ExecTypeFromTL(lefttlist, false); - slot = ExecInitExtraTupleSlot(estate, tupDescLeft); + slot = ExecInitExtraTupleSlot(estate, tupDescLeft, &TTSOpsVirtual); sstate->projLeft = ExecBuildProjectionInfo(lefttlist, NULL, slot, @@ -976,7 +976,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) NULL); sstate->descRight = tupDescRight = ExecTypeFromTL(righttlist, false); - slot = ExecInitExtraTupleSlot(estate, tupDescRight); + slot = ExecInitExtraTupleSlot(estate, tupDescRight, &TTSOpsVirtual); sstate->projRight = ExecBuildProjectionInfo(righttlist, sstate->innerecontext, slot, @@ -988,6 +988,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * across-type comparison). */ sstate->cur_eq_comp = ExecBuildGroupingEqual(tupDescLeft, tupDescRight, + &TTSOpsVirtual, &TTSOpsMinimalTuple, ncols, sstate->keyColIdx, sstate->tab_eq_funcoids, diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index b84c6892d50..c9058ab0e94 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -129,7 +129,18 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) * Initialize scan slot and type (needed by ExecAssignScanProjectionInfo) */ ExecInitScanTupleSlot(estate, &subquerystate->ss, - ExecGetResultType(subquerystate->subplan)); + ExecGetResultType(subquerystate->subplan), + ExecGetResultSlotOps(subquerystate->subplan, NULL)); + /* + * The slot used as the scantuple isn't the slot above (outside of EPQ), + * but the one from the node below. + */ + subquerystate->ss.ps.scanopsset = true; + subquerystate->ss.ps.scanops = ExecGetResultSlotOps(subquerystate->subplan, + &subquerystate->ss.ps.scanopsfixed); + subquerystate->ss.ps.resultopsset = true; + subquerystate->ss.ps.resultops = subquerystate->ss.ps.scanops; + subquerystate->ss.ps.resultopsfixed = subquerystate->ss.ps.scanopsfixed; /* * Initialize result type and projection. diff --git a/src/backend/executor/nodeTableFuncscan.c b/src/backend/executor/nodeTableFuncscan.c index b0c94d7e063..a79e4cb2cf5 100644 --- a/src/backend/executor/nodeTableFuncscan.c +++ b/src/backend/executor/nodeTableFuncscan.c @@ -147,7 +147,8 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags) tf->coltypmods, tf->colcollations); /* and the corresponding scan slot */ - ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc); + ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc, + &TTSOpsMinimalTuple); /* * Initialize result type and projection. diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index bc859e3d516..939ece2faa1 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -543,7 +543,8 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) * get the scan type from the relation descriptor. */ ExecInitScanTupleSlot(estate, &tidstate->ss, - RelationGetDescr(currentRelation)); + RelationGetDescr(currentRelation), + &TTSOpsBufferTuple); /* * Initialize result type and projection. diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c index c791f89b48c..c5e4232e68c 100644 --- a/src/backend/executor/nodeUnique.c +++ b/src/backend/executor/nodeUnique.c @@ -141,7 +141,7 @@ ExecInitUnique(Unique *node, EState *estate, int eflags) * Initialize result slot and type. Unique nodes do no projections, so * initialize projection info for this node appropriately. */ - ExecInitResultTupleSlotTL(&uniquestate->ps); + ExecInitResultTupleSlotTL(&uniquestate->ps, &TTSOpsMinimalTuple); uniquestate->ps.ps_ProjInfo = NULL; /* diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index fa49d0470fc..6351cdac27a 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -261,7 +261,7 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) * Get info about values list, initialize scan slot with it. */ tupdesc = ExecTypeFromExprList((List *) linitial(node->values_lists)); - ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc); + ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc, &TTSOpsVirtual); /* * Initialize result type and projection. diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index 6e597e82856..298e3707450 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -2316,16 +2316,25 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * initialize source tuple type (which is also the tuple type that we'll * store in the tuplestore and use in all our working slots). */ - ExecCreateScanSlotFromOuterPlan(estate, &winstate->ss); + ExecCreateScanSlotFromOuterPlan(estate, &winstate->ss, &TTSOpsMinimalTuple); scanDesc = winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor; + /* the outer tuple isn't the child's tuple, but always a minimal tuple */ + winstate->ss.ps.outeropsset = true; + winstate->ss.ps.outerops = &TTSOpsMinimalTuple; + winstate->ss.ps.outeropsfixed = true; + /* * tuple table initialization */ - winstate->first_part_slot = ExecInitExtraTupleSlot(estate, scanDesc); - winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc); - winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc); - winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc); + winstate->first_part_slot = ExecInitExtraTupleSlot(estate, scanDesc, + &TTSOpsMinimalTuple); + winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc, + &TTSOpsMinimalTuple); + winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc, + &TTSOpsMinimalTuple); + winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc, + &TTSOpsMinimalTuple); /* * create frame head and tail slots only if needed (must create slots in @@ -2339,17 +2348,19 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) && node->ordNumCols != 0) || (frameOptions & FRAMEOPTION_START_OFFSET)) - winstate->framehead_slot = ExecInitExtraTupleSlot(estate, scanDesc); + winstate->framehead_slot = ExecInitExtraTupleSlot(estate, scanDesc, + &TTSOpsMinimalTuple); if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) && node->ordNumCols != 0) || (frameOptions & FRAMEOPTION_END_OFFSET)) - winstate->frametail_slot = ExecInitExtraTupleSlot(estate, scanDesc); + winstate->frametail_slot = ExecInitExtraTupleSlot(estate, scanDesc, + &TTSOpsMinimalTuple); } /* * Initialize result slot, type and projection. */ - ExecInitResultTupleSlotTL(&winstate->ss.ps); + ExecInitResultTupleSlotTL(&winstate->ss.ps, &TTSOpsVirtual); ExecAssignProjectionInfo(&winstate->ss.ps, NULL); /* Set up data for comparing tuples */ diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index 1ce8ae9f026..a432926fe15 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -160,7 +160,12 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) * tuple table initialization */ ExecInitResultTypeTL(&scanstate->ss.ps); - ExecInitScanTupleSlot(estate, &scanstate->ss, NULL); + + /* signal that return type is not yet known */ + scanstate->ss.ps.resultopsset = true; + scanstate->ss.ps.resultopsfixed = false; + + ExecInitScanTupleSlot(estate, &scanstate->ss, NULL, &TTSOpsMinimalTuple); /* * initialize child expressions diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c index 0b5e0dd89f5..be9fd49cd2e 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -1275,7 +1275,7 @@ check_default_partition_contents(Relation parent, Relation default_rel, econtext = GetPerTupleExprContext(estate); snapshot = RegisterSnapshot(GetLatestSnapshot()); scan = heap_beginscan(part_rel, snapshot, 0, NULL); - tupslot = MakeSingleTupleTableSlot(tupdesc); + tupslot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsHeapTuple); /* * Switch to per-tuple memory context and reset it for each tuple diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c index 6e420d893cf..9e682331d2f 100644 --- a/src/backend/replication/logical/tablesync.c +++ b/src/backend/replication/logical/tablesync.c @@ -685,7 +685,7 @@ fetch_remote_table_info(char *nspname, char *relname, (errmsg("could not fetch table info for table \"%s.%s\" from publisher: %s", nspname, relname, res->err))); - slot = MakeSingleTupleTableSlot(res->tupledesc); + slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple); if (!tuplestore_gettupleslot(res->tuplestore, true, false, slot)) ereport(ERROR, (errmsg("table \"%s.%s\" not found on publisher", @@ -727,7 +727,7 @@ fetch_remote_table_info(char *nspname, char *relname, lrel->attkeys = NULL; natt = 0; - slot = MakeSingleTupleTableSlot(res->tupledesc); + slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple); while (tuplestore_gettupleslot(res->tuplestore, true, false, slot)) { lrel->attnames[natt] = diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 277da69fa6c..3cd1e0d728e 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -213,7 +213,8 @@ create_estate_for_relation(LogicalRepRelMapEntry *rel) /* Triggers might need a slot */ if (resultRelInfo->ri_TrigDesc) - estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL); + estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL, + &TTSOpsVirtual); /* Prepare to catch AFTER triggers. */ AfterTriggerBeginQuery(); @@ -609,7 +610,8 @@ apply_handle_insert(StringInfo s) /* Initialize the executor state. */ estate = create_estate_for_relation(rel); remoteslot = ExecInitExtraTupleSlot(estate, - RelationGetDescr(rel->localrel)); + RelationGetDescr(rel->localrel), + &TTSOpsHeapTuple); /* Input functions may need an active snapshot, so get one */ PushActiveSnapshot(GetTransactionSnapshot()); @@ -715,9 +717,11 @@ apply_handle_update(StringInfo s) /* Initialize the executor state. */ estate = create_estate_for_relation(rel); remoteslot = ExecInitExtraTupleSlot(estate, - RelationGetDescr(rel->localrel)); + RelationGetDescr(rel->localrel), + &TTSOpsHeapTuple); localslot = ExecInitExtraTupleSlot(estate, - RelationGetDescr(rel->localrel)); + RelationGetDescr(rel->localrel), + &TTSOpsHeapTuple); EvalPlanQualInit(&epqstate, estate, NULL, NIL, -1); PushActiveSnapshot(GetTransactionSnapshot()); @@ -756,7 +760,7 @@ apply_handle_update(StringInfo s) { /* Process and store remote tuple in the slot */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); - ExecStoreHeapTuple(localslot->tts_tuple, remoteslot, false); + ExecCopySlot(remoteslot, localslot); slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed); MemoryContextSwitchTo(oldctx); @@ -833,9 +837,11 @@ apply_handle_delete(StringInfo s) /* Initialize the executor state. */ estate = create_estate_for_relation(rel); remoteslot = ExecInitExtraTupleSlot(estate, - RelationGetDescr(rel->localrel)); + RelationGetDescr(rel->localrel), + &TTSOpsVirtual); localslot = ExecInitExtraTupleSlot(estate, - RelationGetDescr(rel->localrel)); + RelationGetDescr(rel->localrel), + &TTSOpsHeapTuple); EvalPlanQualInit(&epqstate, estate, NULL, NIL, -1); PushActiveSnapshot(GetTransactionSnapshot()); diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 2683385ca6e..39337d2f1f8 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -403,7 +403,7 @@ IdentifySystem(void) TEXTOID, -1, 0); /* prepare for projection of tuples */ - tstate = begin_tup_output_tupdesc(dest, tupdesc); + tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual); /* column 1: system identifier */ values[0] = CStringGetTextDatum(sysid); @@ -735,7 +735,7 @@ StartReplication(StartReplicationCmd *cmd) TEXTOID, -1, 0); /* prepare for projection of tuple */ - tstate = begin_tup_output_tupdesc(dest, tupdesc); + tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual); values[0] = Int64GetDatum((int64) sendTimeLineNextTLI); values[1] = CStringGetTextDatum(startpos_str); @@ -1007,7 +1007,7 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd) TEXTOID, -1, 0); /* prepare for projection of tuples */ - tstate = begin_tup_output_tupdesc(dest, tupdesc); + tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual); /* slot_name */ slot_name = NameStr(MyReplicationSlot->data.name); diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 66cc5c35c68..b44438bf088 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -1071,7 +1071,7 @@ RunFromStore(Portal portal, ScanDirection direction, uint64 count, uint64 current_tuple_count = 0; TupleTableSlot *slot; - slot = MakeSingleTupleTableSlot(portal->tupDesc); + slot = MakeSingleTupleTableSlot(portal->tupDesc, &TTSOpsMinimalTuple); dest->rStartup(dest, CMD_SELECT, portal->tupDesc); diff --git a/src/backend/utils/adt/orderedsetaggs.c b/src/backend/utils/adt/orderedsetaggs.c index be9422dcfb6..8871aed9045 100644 --- a/src/backend/utils/adt/orderedsetaggs.c +++ b/src/backend/utils/adt/orderedsetaggs.c @@ -239,7 +239,8 @@ ordered_set_startup(FunctionCallInfo fcinfo, bool use_tuples) } /* Create slot we'll use to store/retrieve rows */ - qstate->tupslot = MakeSingleTupleTableSlot(qstate->tupdesc); + qstate->tupslot = MakeSingleTupleTableSlot(qstate->tupdesc, + &TTSOpsMinimalTuple); } else { @@ -1375,7 +1376,8 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS) * previous row available for comparisons. This is accomplished by * swapping the slot pointer variables after each row. */ - extraslot = MakeSingleTupleTableSlot(osastate->qstate->tupdesc); + extraslot = MakeSingleTupleTableSlot(osastate->qstate->tupdesc, + &TTSOpsMinimalTuple); slot2 = extraslot; /* iterate till we find the hypothetical row */ diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index e0ece74bb92..dbbbcc979b4 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5534,7 +5534,8 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, indexInfo = BuildIndexInfo(indexRel); /* some other stuff */ - slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRel)); + slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRel), + &TTSOpsHeapTuple); econtext->ecxt_scantuple = slot; get_typlenbyval(vardata->atttype, &typLen, &typByVal); InitNonVacuumableSnapshot(SnapshotNonVacuumable, RecentGlobalXmin); diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 0327b295da8..f9074215a2d 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -8262,7 +8262,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest) TEXTOID, -1, 0); /* prepare for projection of tuples */ - tstate = begin_tup_output_tupdesc(dest, tupdesc); + tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual); /* Send it */ do_text_output_oneline(tstate, value); @@ -8292,7 +8292,7 @@ ShowAllGUCConfig(DestReceiver *dest) TEXTOID, -1, 0); /* prepare for projection of tuples */ - tstate = begin_tup_output_tupdesc(dest, tupdesc); + tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual); for (i = 0; i < num_guc_variables; i++) { diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index 5909404e1e5..ee7fd83c02c 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -933,7 +933,7 @@ tuplesort_begin_cluster(TupleDesc tupDesc, * scantuple has to point to that slot, too. */ state->estate = CreateExecutorState(); - slot = MakeSingleTupleTableSlot(tupDesc); + slot = MakeSingleTupleTableSlot(tupDesc, &TTSOpsVirtual); econtext = GetPerTupleExprContext(state->estate); econtext->ecxt_scantuple = slot; } diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 84412657845..14c4e3ae2fa 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -249,6 +249,7 @@ extern List *ExecInitExprList(List *nodes, PlanState *parent); extern ExprState *ExecBuildAggTrans(AggState *aggstate, struct AggStatePerPhaseData *phase, bool doSort, bool doHash); extern ExprState *ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc, + const TupleTableSlotOps *lops, const TupleTableSlotOps *rops, int numCols, AttrNumber *keyColIdx, Oid *eqfunctions, @@ -430,13 +431,18 @@ extern void ExecScanReScan(ScanState *node); * prototypes from functions in execTuples.c */ extern void ExecInitResultTypeTL(PlanState *planstate); -extern void ExecInitResultSlot(PlanState *planstate); -extern void ExecInitResultTupleSlotTL(PlanState *planstate); -extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupleDesc); +extern void ExecInitResultSlot(PlanState *planstate, + const TupleTableSlotOps *tts_ops); +extern void ExecInitResultTupleSlotTL(PlanState *planstate, + const TupleTableSlotOps *tts_ops); +extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, + TupleDesc tupleDesc, + const TupleTableSlotOps *tts_ops); extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate, - TupleDesc tupleDesc); -extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, - TupleDesc tupType); + TupleDesc tupledesc, + const TupleTableSlotOps *tts_ops); +extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, TupleDesc tupType, + const TupleTableSlotOps *tts_ops); extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); extern TupleDesc ExecTypeFromExprList(List *exprList); @@ -450,7 +456,8 @@ typedef struct TupOutputState } TupOutputState; extern TupOutputState *begin_tup_output_tupdesc(DestReceiver *dest, - TupleDesc tupdesc); + TupleDesc tupdesc, + const TupleTableSlotOps *tts_ops); extern void do_tup_output(TupOutputState *tstate, Datum *values, bool *isnull); extern void do_text_output_multiline(TupOutputState *tstate, const char *txt); extern void end_tup_output(TupOutputState *tstate); @@ -504,13 +511,18 @@ extern ExprContext *MakePerTupleExprContext(EState *estate); extern void ExecAssignExprContext(EState *estate, PlanState *planstate); extern TupleDesc ExecGetResultType(PlanState *planstate); +extern TupleTableSlot ExecGetResultSlot(PlanState *planstate); +extern const TupleTableSlotOps *ExecGetResultSlotOps(PlanState *planstate, + bool *isfixed); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); extern void ExecConditionalAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc, Index varno); extern void ExecFreeExprContext(PlanState *planstate); extern void ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc); -extern void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate); +extern void ExecCreateScanSlotFromOuterPlan(EState *estate, + ScanState *scanstate, + const TupleTableSlotOps *tts_ops); extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid); diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index 8bfa73c30ea..86e47c56af2 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -132,6 +132,9 @@ #define TTS_FLAG_FIXED (1 << 5) #define TTS_FIXED(slot) (((slot)->tts_flags & TTS_FLAG_FIXED) != 0) +struct TupleTableSlotOps; +typedef struct TupleTableSlotOps TupleTableSlotOps; + typedef struct TupleTableSlot { NodeTag type; @@ -141,20 +144,35 @@ typedef struct TupleTableSlot AttrNumber tts_nvalid; /* # of valid values in tts_values */ #define FIELDNO_TUPLETABLESLOT_TUPLE 3 HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */ -#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 4 + const TupleTableSlotOps *const tts_ops; /* implementation of slot */ +#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 5 TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */ MemoryContext tts_mcxt; /* slot itself is in this context */ Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */ -#define FIELDNO_TUPLETABLESLOT_OFF 7 +#define FIELDNO_TUPLETABLESLOT_OFF 8 uint32 tts_off; /* saved state for slot_deform_tuple */ -#define FIELDNO_TUPLETABLESLOT_VALUES 8 +#define FIELDNO_TUPLETABLESLOT_VALUES 9 Datum *tts_values; /* current per-attribute values */ -#define FIELDNO_TUPLETABLESLOT_ISNULL 9 +#define FIELDNO_TUPLETABLESLOT_ISNULL 10 bool *tts_isnull; /* current per-attribute isnull flags */ MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */ HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */ } TupleTableSlot; +/* routines for a TupleTableSlot implementation */ +struct TupleTableSlotOps +{ +}; + +/* + * Predefined TupleTableSlotOps for various types of TupleTableSlotOps. The + * same are used to identify the type of a given slot. + */ +extern PGDLLIMPORT const TupleTableSlotOps TTSOpsVirtual; +extern PGDLLIMPORT const TupleTableSlotOps TTSOpsHeapTuple; +extern PGDLLIMPORT const TupleTableSlotOps TTSOpsMinimalTuple; +extern PGDLLIMPORT const TupleTableSlotOps TTSOpsBufferTuple; + #define TTS_HAS_PHYSICAL_TUPLE(slot) \ ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr)) @@ -165,10 +183,13 @@ typedef struct TupleTableSlot ((slot) == NULL || TTS_EMPTY(slot)) /* in executor/execTuples.c */ -extern TupleTableSlot *MakeTupleTableSlot(TupleDesc desc); -extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable, TupleDesc desc); +extern TupleTableSlot *MakeTupleTableSlot(TupleDesc tupleDesc, + const TupleTableSlotOps *tts_ops); +extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable, TupleDesc desc, + const TupleTableSlotOps *tts_ops); extern void ExecResetTupleTable(List *tupleTable, bool shouldFree); -extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc); +extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc, + const TupleTableSlotOps *tts_ops); extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot); extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc); extern TupleTableSlot *ExecStoreHeapTuple(HeapTuple tuple, diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 18544566f70..63c871e6d03 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -977,6 +977,42 @@ typedef struct PlanState * descriptor, without encoding knowledge about all executor nodes. */ TupleDesc scandesc; + + /* + * Define the slot types for inner, outer and scanslots for expression + * contexts with this state as a parent. If *opsset is set, then + * *opsfixed indicates whether *ops is guaranteed to be the type of slot + * used. That means that every slot in the corresponding + * ExprContext.ecxt_*tuple will point to a slot of that type, while + * evaluating the expression. If *opsfixed is false, but *ops is set, + * that indicates the most likely type of slot. + * + * The scan* fields are set by ExecInitScanTupleSlot(). If that's not + * called, nodes can initialize the fields themselves. + * + * If outer/inneropsset is false, the information is inferred on-demand + * using ExecGetResultSlotOps() on ->righttree/lefttree, using the + * corresponding node's resultops* fields. + * + * The result* fields are automatically set when ExecInitResultSlot is + * used (be it directly or when the slot is created by + * ExecAssignScanProjectionInfo() / + * ExecConditionalAssignProjectionInfo()). If no projection is necessary + * ExecConditionalAssignProjectionInfo() defaults those fields to the scan + * operations. + */ + const TupleTableSlotOps *scanops; + const TupleTableSlotOps *outerops; + const TupleTableSlotOps *innerops; + const TupleTableSlotOps *resultops; + bool scanopsfixed; + bool outeropsfixed; + bool inneropsfixed; + bool resultopsfixed; + bool scanopsset; + bool outeropsset; + bool inneropsset; + bool resultopsset; } PlanState; /* ---------------- @@ -1064,6 +1100,8 @@ typedef struct ModifyTableState PlanState **mt_plans; /* subplans (one per target rel) */ int mt_nplans; /* number of plans in the array */ int mt_whichplan; /* which one is being executed (0..n-1) */ + TupleTableSlot** mt_scans; /* input tuple corresponding to underlying + plans */ ResultRelInfo *resultRelInfo; /* per-subplan target relations */ ResultRelInfo *rootResultRelInfo; /* root target relation (partitioned * table root) */ @@ -1329,6 +1367,7 @@ typedef struct IndexScanState bool *iss_OrderByTypByVals; int16 *iss_OrderByTypLens; Size iss_PscanLen; + TupleTableSlot *iss_ReorderQueueSlot; } IndexScanState; /* ---------------- |