From f0b900086a370e45f730138d55da4f260d24809c Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 19 Dec 2024 17:07:14 -0500 Subject: [PATCH] Use ExecGetCommonSlotOps infrastructure in more places. Append, MergeAppend, and RecursiveUnion can all use the support functions added in commit 276279295. The first two can report a fixed result slot type if all their children return the same fixed slot type. That does nothing for the append step itself, but might allow optimizations in the parent plan node. RecursiveUnion can optimize tuple hash table operations in the same way as SetOp now does. Patch by me; thanks to Richard Guo and David Rowley for review. Discussion: https://postgr.es/m/1850138.1731549611@sss.pgh.pa.us --- src/backend/executor/nodeAppend.c | 32 +++++++++++++------ src/backend/executor/nodeMergeAppend.c | 38 ++++++++++++++++------- src/backend/executor/nodeRecursiveunion.c | 8 +++-- 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index ca0f54d676f..b5d56569f7f 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -110,6 +110,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + const TupleTableSlotOps *appendops; Bitmapset *validsubplans; Bitmapset *asyncplans; int nplans; @@ -176,15 +177,6 @@ ExecInitAppend(Append *node, EState *estate, int eflags) appendstate->as_prune_state = NULL; } - /* - * Initialize result tuple type and slot. - */ - 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 *)); @@ -227,6 +219,28 @@ ExecInitAppend(Append *node, EState *estate, int eflags) appendstate->appendplans = appendplanstates; appendstate->as_nplans = nplans; + /* + * Initialize Append's result tuple type and slot. If the child plans all + * produce the same fixed slot type, we can use that slot type; otherwise + * make a virtual slot. (Note that the result slot itself is used only to + * return a null tuple at end of execution; real tuples are returned to + * the caller in the children's own result slots. What we are doing here + * is allowing the parent plan node to optimize if the Append will return + * only one kind of slot.) + */ + appendops = ExecGetCommonSlotOps(appendplanstates, j); + if (appendops != NULL) + { + ExecInitResultTupleSlotTL(&appendstate->ps, appendops); + } + else + { + ExecInitResultTupleSlotTL(&appendstate->ps, &TTSOpsVirtual); + /* show that the output slot type is not fixed */ + appendstate->ps.resultopsset = true; + appendstate->ps.resultopsfixed = false; + } + /* Initialize async state */ appendstate->as_asyncplans = asyncplans; appendstate->as_nasyncplans = nasyncplans; diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c index e1b9b984a7a..081734e8172 100644 --- a/src/backend/executor/nodeMergeAppend.c +++ b/src/backend/executor/nodeMergeAppend.c @@ -66,6 +66,7 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags) { MergeAppendState *mergestate = makeNode(MergeAppendState); PlanState **mergeplanstates; + const TupleTableSlotOps *mergeops; Bitmapset *validsubplans; int nplans; int i, @@ -128,18 +129,6 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags) mergestate->ms_heap = binaryheap_allocate(nplans, heap_compare_slots, mergestate); - /* - * Miscellaneous initialization - * - * MergeAppend nodes do have Result slots, which hold pointers to tuples, - * so we have to initialize them. FIXME - */ - 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 * the results into the mergeplanstates array. @@ -153,6 +142,31 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags) mergeplanstates[j++] = ExecInitNode(initNode, estate, eflags); } + /* + * Initialize MergeAppend's result tuple type and slot. If the child + * plans all produce the same fixed slot type, we can use that slot type; + * otherwise make a virtual slot. (Note that the result slot itself is + * used only to return a null tuple at end of execution; real tuples are + * returned to the caller in the children's own result slots. What we are + * doing here is allowing the parent plan node to optimize if the + * MergeAppend will return only one kind of slot.) + */ + mergeops = ExecGetCommonSlotOps(mergeplanstates, j); + if (mergeops != NULL) + { + ExecInitResultTupleSlotTL(&mergestate->ps, mergeops); + } + else + { + ExecInitResultTupleSlotTL(&mergestate->ps, &TTSOpsVirtual); + /* show that the output slot type is not fixed */ + mergestate->ps.resultopsset = true; + mergestate->ps.resultopsfixed = false; + } + + /* + * Miscellaneous initialization + */ mergestate->ps.ps_ProjInfo = NULL; /* diff --git a/src/backend/executor/nodeRecursiveunion.c b/src/backend/executor/nodeRecursiveunion.c index 39be4cdc3b1..b577b72b50c 100644 --- a/src/backend/executor/nodeRecursiveunion.c +++ b/src/backend/executor/nodeRecursiveunion.c @@ -37,10 +37,14 @@ build_hash_table(RecursiveUnionState *rustate) Assert(node->numCols > 0); Assert(node->numGroups > 0); - /* XXX is it worth working a bit harder to determine the inputOps here? */ + /* + * If both child plans deliver the same fixed tuple slot type, we can tell + * BuildTupleHashTableExt to expect that slot type as input. Otherwise, + * we'll pass NULL denoting that any slot type is possible. + */ rustate->hashtable = BuildTupleHashTableExt(&rustate->ps, desc, - NULL, + ExecGetCommonChildSlotOps(&rustate->ps), node->numCols, node->dupColIdx, rustate->eqfuncoids, -- 2.30.2