aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/access/heap/heapam.c3
-rw-r--r--src/backend/catalog/index.c9
-rw-r--r--src/backend/catalog/indexing.c3
-rw-r--r--src/backend/commands/analyze.c3
-rw-r--r--src/backend/commands/constraint.c3
-rw-r--r--src/backend/commands/copy.c6
-rw-r--r--src/backend/commands/explain.c3
-rw-r--r--src/backend/commands/functioncmds.c3
-rw-r--r--src/backend/commands/subscriptioncmds.c2
-rw-r--r--src/backend/commands/tablecmds.c6
-rw-r--r--src/backend/commands/trigger.c10
-rw-r--r--src/backend/executor/execExpr.c4
-rw-r--r--src/backend/executor/execGrouping.c8
-rw-r--r--src/backend/executor/execIndexing.c3
-rw-r--r--src/backend/executor/execJunk.c4
-rw-r--r--src/backend/executor/execMain.c12
-rw-r--r--src/backend/executor/execPartition.c8
-rw-r--r--src/backend/executor/execSRF.c3
-rw-r--r--src/backend/executor/execTuples.c67
-rw-r--r--src/backend/executor/execUtils.c47
-rw-r--r--src/backend/executor/functions.c24
-rw-r--r--src/backend/executor/nodeAgg.c17
-rw-r--r--src/backend/executor/nodeAppend.c6
-rw-r--r--src/backend/executor/nodeBitmapHeapscan.c3
-rw-r--r--src/backend/executor/nodeCtescan.c3
-rw-r--r--src/backend/executor/nodeCustom.c7
-rw-r--r--src/backend/executor/nodeForeignscan.c10
-rw-r--r--src/backend/executor/nodeFunctionscan.c6
-rw-r--r--src/backend/executor/nodeGather.c8
-rw-r--r--src/backend/executor/nodeGatherMerge.c10
-rw-r--r--src/backend/executor/nodeGroup.c6
-rw-r--r--src/backend/executor/nodeHash.c2
-rw-r--r--src/backend/executor/nodeHashjoin.c15
-rw-r--r--src/backend/executor/nodeIndexonlyscan.c2
-rw-r--r--src/backend/executor/nodeIndexscan.c21
-rw-r--r--src/backend/executor/nodeLimit.c4
-rw-r--r--src/backend/executor/nodeLockRows.c5
-rw-r--r--src/backend/executor/nodeMaterial.c10
-rw-r--r--src/backend/executor/nodeMergeAppend.c6
-rw-r--r--src/backend/executor/nodeMergejoin.c15
-rw-r--r--src/backend/executor/nodeModifyTable.c28
-rw-r--r--src/backend/executor/nodeNamedtuplestorescan.c3
-rw-r--r--src/backend/executor/nodeNestloop.c5
-rw-r--r--src/backend/executor/nodeProjectSet.c2
-rw-r--r--src/backend/executor/nodeResult.c2
-rw-r--r--src/backend/executor/nodeSamplescan.c3
-rw-r--r--src/backend/executor/nodeSeqscan.c3
-rw-r--r--src/backend/executor/nodeSetOp.c4
-rw-r--r--src/backend/executor/nodeSort.c4
-rw-r--r--src/backend/executor/nodeSubplan.c5
-rw-r--r--src/backend/executor/nodeSubqueryscan.c13
-rw-r--r--src/backend/executor/nodeTableFuncscan.c3
-rw-r--r--src/backend/executor/nodeTidscan.c3
-rw-r--r--src/backend/executor/nodeUnique.c2
-rw-r--r--src/backend/executor/nodeValuesscan.c2
-rw-r--r--src/backend/executor/nodeWindowAgg.c27
-rw-r--r--src/backend/executor/nodeWorktablescan.c7
-rw-r--r--src/backend/partitioning/partbounds.c2
-rw-r--r--src/backend/replication/logical/tablesync.c4
-rw-r--r--src/backend/replication/logical/worker.c20
-rw-r--r--src/backend/replication/walsender.c6
-rw-r--r--src/backend/tcop/pquery.c2
-rw-r--r--src/backend/utils/adt/orderedsetaggs.c6
-rw-r--r--src/backend/utils/adt/selfuncs.c3
-rw-r--r--src/backend/utils/misc/guc.c4
-rw-r--r--src/backend/utils/sort/tuplesort.c2
-rw-r--r--src/include/executor/executor.h28
-rw-r--r--src/include/executor/tuptable.h35
-rw-r--r--src/include/nodes/execnodes.h39
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;
/* ----------------