2010-10-14 16:56:39 -04:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* nodeMergeAppend.c
|
|
|
|
|
* routines to handle MergeAppend nodes.
|
|
|
|
|
*
|
2025-01-01 11:21:55 -05:00
|
|
|
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
|
2010-10-14 16:56:39 -04:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* IDENTIFICATION
|
|
|
|
|
* src/backend/executor/nodeMergeAppend.c
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
/* INTERFACE ROUTINES
|
|
|
|
|
* ExecInitMergeAppend - initialize the MergeAppend node
|
|
|
|
|
* ExecMergeAppend - retrieve the next tuple from the node
|
|
|
|
|
* ExecEndMergeAppend - shut down the MergeAppend node
|
|
|
|
|
* ExecReScanMergeAppend - rescan the MergeAppend node
|
|
|
|
|
*
|
|
|
|
|
* NOTES
|
|
|
|
|
* A MergeAppend node contains a list of one or more subplans.
|
|
|
|
|
* These are each expected to deliver tuples that are sorted according
|
|
|
|
|
* to a common sort key. The MergeAppend node merges these streams
|
|
|
|
|
* to produce output sorted the same way.
|
|
|
|
|
*
|
|
|
|
|
* MergeAppend nodes don't make use of their left and right
|
|
|
|
|
* subtrees, rather they maintain a list of subplans so
|
|
|
|
|
* a typical MergeAppend node looks like this in the plan tree:
|
|
|
|
|
*
|
|
|
|
|
* ...
|
|
|
|
|
* /
|
|
|
|
|
* MergeAppend---+------+------+--- nil
|
|
|
|
|
* / \ | | |
|
|
|
|
|
* nil nil ... ... ...
|
|
|
|
|
* subplans
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
2024-03-04 06:00:11 -05:00
|
|
|
#include "executor/executor.h"
|
2018-07-19 06:49:43 -04:00
|
|
|
#include "executor/execPartition.h"
|
2010-10-14 16:56:39 -04:00
|
|
|
#include "executor/nodeMergeAppend.h"
|
2012-11-29 11:13:08 -05:00
|
|
|
#include "lib/binaryheap.h"
|
2017-07-25 20:37:17 -04:00
|
|
|
#include "miscadmin.h"
|
2012-11-29 11:13:08 -05:00
|
|
|
|
2010-10-14 16:56:39 -04:00
|
|
|
/*
|
2012-11-29 11:13:08 -05:00
|
|
|
* We have one slot for each item in the heap array. We use SlotNumber
|
|
|
|
|
* to store slot indexes. This doesn't actually provide any formal
|
|
|
|
|
* type-safety, but it makes the code more self-documenting.
|
2010-10-14 16:56:39 -04:00
|
|
|
*/
|
2012-11-29 11:13:08 -05:00
|
|
|
typedef int32 SlotNumber;
|
2010-10-14 16:56:39 -04:00
|
|
|
|
2017-07-17 03:33:49 -04:00
|
|
|
static TupleTableSlot *ExecMergeAppend(PlanState *pstate);
|
2012-11-29 11:13:08 -05:00
|
|
|
static int heap_compare_slots(Datum a, Datum b, void *arg);
|
2010-10-14 16:56:39 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
|
* ExecInitMergeAppend
|
|
|
|
|
*
|
|
|
|
|
* Begin all of the subscans of the MergeAppend node.
|
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
MergeAppendState *
|
|
|
|
|
ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
|
|
|
|
|
{
|
|
|
|
|
MergeAppendState *mergestate = makeNode(MergeAppendState);
|
|
|
|
|
PlanState **mergeplanstates;
|
2024-12-19 17:07:14 -05:00
|
|
|
const TupleTableSlotOps *mergeops;
|
2018-07-19 06:49:43 -04:00
|
|
|
Bitmapset *validsubplans;
|
2010-10-14 16:56:39 -04:00
|
|
|
int nplans;
|
2018-07-19 06:49:43 -04:00
|
|
|
int i,
|
|
|
|
|
j;
|
2010-10-14 16:56:39 -04:00
|
|
|
|
|
|
|
|
/* check for unsupported flags */
|
|
|
|
|
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* create new MergeAppendState for our node
|
|
|
|
|
*/
|
|
|
|
|
mergestate->ps.plan = (Plan *) node;
|
|
|
|
|
mergestate->ps.state = estate;
|
2017-07-17 03:33:49 -04:00
|
|
|
mergestate->ps.ExecProcNode = ExecMergeAppend;
|
2018-07-19 06:49:43 -04:00
|
|
|
|
|
|
|
|
/* If run-time partition pruning is enabled, then set that up now */
|
2025-01-29 21:57:32 -05:00
|
|
|
if (node->part_prune_index >= 0)
|
2018-07-19 06:49:43 -04:00
|
|
|
{
|
|
|
|
|
PartitionPruneState *prunestate;
|
|
|
|
|
|
2022-04-05 05:46:48 -04:00
|
|
|
/*
|
|
|
|
|
* Set up pruning data structure. This also initializes the set of
|
|
|
|
|
* subplans to initialize (validsubplans) by taking into account the
|
|
|
|
|
* result of performing initial pruning if any.
|
|
|
|
|
*/
|
|
|
|
|
prunestate = ExecInitPartitionPruning(&mergestate->ps,
|
|
|
|
|
list_length(node->mergeplans),
|
2025-01-29 21:57:32 -05:00
|
|
|
node->part_prune_index,
|
|
|
|
|
node->apprelids,
|
2022-04-05 05:46:48 -04:00
|
|
|
&validsubplans);
|
2018-07-19 06:49:43 -04:00
|
|
|
mergestate->ms_prune_state = prunestate;
|
2022-04-05 05:46:48 -04:00
|
|
|
nplans = bms_num_members(validsubplans);
|
2018-07-19 06:49:43 -04:00
|
|
|
|
|
|
|
|
/*
|
2019-12-11 17:05:30 -05:00
|
|
|
* When no run-time pruning is required and there's at least one
|
2022-04-11 04:49:41 -04:00
|
|
|
* subplan, we can fill ms_valid_subplans immediately, preventing
|
2019-12-11 17:05:30 -05:00
|
|
|
* later calls to ExecFindMatchingSubPlans.
|
2018-07-19 06:49:43 -04:00
|
|
|
*/
|
2019-12-11 17:05:30 -05:00
|
|
|
if (!prunestate->do_exec_prune && nplans > 0)
|
2018-07-19 06:49:43 -04:00
|
|
|
mergestate->ms_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
nplans = list_length(node->mergeplans);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* When run-time partition pruning is not enabled we can just mark all
|
|
|
|
|
* subplans as valid; they must also all be initialized.
|
|
|
|
|
*/
|
2018-07-30 17:03:19 -04:00
|
|
|
Assert(nplans > 0);
|
2018-07-19 06:49:43 -04:00
|
|
|
mergestate->ms_valid_subplans = validsubplans =
|
|
|
|
|
bms_add_range(NULL, 0, nplans - 1);
|
|
|
|
|
mergestate->ms_prune_state = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mergeplanstates = (PlanState **) palloc(nplans * sizeof(PlanState *));
|
2010-10-14 16:56:39 -04:00
|
|
|
mergestate->mergeplans = mergeplanstates;
|
|
|
|
|
mergestate->ms_nplans = nplans;
|
|
|
|
|
|
|
|
|
|
mergestate->ms_slots = (TupleTableSlot **) palloc0(sizeof(TupleTableSlot *) * nplans);
|
2024-04-11 04:18:05 -04:00
|
|
|
mergestate->ms_heap = binaryheap_allocate(nplans, heap_compare_slots,
|
2012-11-29 11:13:08 -05:00
|
|
|
mergestate);
|
2010-10-14 16:56:39 -04:00
|
|
|
|
|
|
|
|
/*
|
2018-07-19 06:49:43 -04:00
|
|
|
* call ExecInitNode on each of the valid plans to be executed and save
|
|
|
|
|
* the results into the mergeplanstates array.
|
2010-10-14 16:56:39 -04:00
|
|
|
*/
|
2019-07-22 03:03:12 -04:00
|
|
|
j = 0;
|
|
|
|
|
i = -1;
|
|
|
|
|
while ((i = bms_next_member(validsubplans, i)) >= 0)
|
2010-10-14 16:56:39 -04:00
|
|
|
{
|
2019-07-22 03:03:12 -04:00
|
|
|
Plan *initNode = (Plan *) list_nth(node->mergeplans, i);
|
2010-10-14 16:56:39 -04:00
|
|
|
|
2019-07-22 03:03:12 -04:00
|
|
|
mergeplanstates[j++] = ExecInitNode(initNode, estate, eflags);
|
2010-10-14 16:56:39 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-19 17:07:14 -05:00
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2010-10-14 16:56:39 -04:00
|
|
|
mergestate->ps.ps_ProjInfo = NULL;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* initialize sort-key information
|
|
|
|
|
*/
|
|
|
|
|
mergestate->ms_nkeys = node->numCols;
|
2011-12-07 00:18:38 -05:00
|
|
|
mergestate->ms_sortkeys = palloc0(sizeof(SortSupportData) * node->numCols);
|
2010-10-14 16:56:39 -04:00
|
|
|
|
|
|
|
|
for (i = 0; i < node->numCols; i++)
|
|
|
|
|
{
|
2011-12-07 00:18:38 -05:00
|
|
|
SortSupport sortKey = mergestate->ms_sortkeys + i;
|
2011-03-26 18:28:40 -04:00
|
|
|
|
2011-12-07 00:18:38 -05:00
|
|
|
sortKey->ssup_cxt = CurrentMemoryContext;
|
|
|
|
|
sortKey->ssup_collation = node->collations[i];
|
|
|
|
|
sortKey->ssup_nulls_first = node->nullsFirst[i];
|
|
|
|
|
sortKey->ssup_attno = node->sortColIdx[i];
|
|
|
|
|
|
2015-01-19 15:20:31 -05:00
|
|
|
/*
|
|
|
|
|
* It isn't feasible to perform abbreviated key conversion, since
|
|
|
|
|
* tuples are pulled into mergestate's binary heap as needed. It
|
|
|
|
|
* would likely be counter-productive to convert tuples into an
|
|
|
|
|
* abbreviated representation as they're pulled up, so opt out of that
|
|
|
|
|
* additional optimization entirely.
|
|
|
|
|
*/
|
|
|
|
|
sortKey->abbreviate = false;
|
|
|
|
|
|
2011-12-07 00:18:38 -05:00
|
|
|
PrepareSortSupportFromOrderingOp(node->sortOperators[i], sortKey);
|
2010-10-14 16:56:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* initialize to show we have not run the subplans yet
|
|
|
|
|
*/
|
|
|
|
|
mergestate->ms_initialized = false;
|
|
|
|
|
|
|
|
|
|
return mergestate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
|
* ExecMergeAppend
|
|
|
|
|
*
|
|
|
|
|
* Handles iteration over multiple subplans.
|
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
|
*/
|
2017-07-17 03:33:49 -04:00
|
|
|
static TupleTableSlot *
|
|
|
|
|
ExecMergeAppend(PlanState *pstate)
|
2010-10-14 16:56:39 -04:00
|
|
|
{
|
2017-07-17 03:33:49 -04:00
|
|
|
MergeAppendState *node = castNode(MergeAppendState, pstate);
|
2010-10-14 16:56:39 -04:00
|
|
|
TupleTableSlot *result;
|
|
|
|
|
SlotNumber i;
|
|
|
|
|
|
2017-07-25 20:37:17 -04:00
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
|
|
2010-10-14 16:56:39 -04:00
|
|
|
if (!node->ms_initialized)
|
|
|
|
|
{
|
2018-07-19 06:49:43 -04:00
|
|
|
/* Nothing to do if all subplans were pruned */
|
2019-12-11 17:05:30 -05:00
|
|
|
if (node->ms_nplans == 0)
|
2018-07-19 06:49:43 -04:00
|
|
|
return ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
|
|
|
|
|
2010-10-14 16:56:39 -04:00
|
|
|
/*
|
2018-07-19 06:49:43 -04:00
|
|
|
* If we've yet to determine the valid subplans then do so now. If
|
|
|
|
|
* run-time pruning is disabled then the valid subplans will always be
|
|
|
|
|
* set to all subplans.
|
2010-10-14 16:56:39 -04:00
|
|
|
*/
|
2018-07-19 06:49:43 -04:00
|
|
|
if (node->ms_valid_subplans == NULL)
|
|
|
|
|
node->ms_valid_subplans =
|
2022-04-05 05:46:48 -04:00
|
|
|
ExecFindMatchingSubPlans(node->ms_prune_state, false);
|
2018-07-19 06:49:43 -04:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* First time through: pull the first tuple from each valid subplan,
|
|
|
|
|
* and set up the heap.
|
|
|
|
|
*/
|
|
|
|
|
i = -1;
|
|
|
|
|
while ((i = bms_next_member(node->ms_valid_subplans, i)) >= 0)
|
2010-10-14 16:56:39 -04:00
|
|
|
{
|
|
|
|
|
node->ms_slots[i] = ExecProcNode(node->mergeplans[i]);
|
|
|
|
|
if (!TupIsNull(node->ms_slots[i]))
|
2012-11-29 11:13:08 -05:00
|
|
|
binaryheap_add_unordered(node->ms_heap, Int32GetDatum(i));
|
2010-10-14 16:56:39 -04:00
|
|
|
}
|
2012-11-29 11:13:08 -05:00
|
|
|
binaryheap_build(node->ms_heap);
|
2010-10-14 16:56:39 -04:00
|
|
|
node->ms_initialized = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Otherwise, pull the next tuple from whichever subplan we returned
|
2012-11-29 11:13:08 -05:00
|
|
|
* from last time, and reinsert the subplan index into the heap,
|
|
|
|
|
* because it might now compare differently against the existing
|
|
|
|
|
* elements of the heap. (We could perhaps simplify the logic a bit
|
|
|
|
|
* by doing this before returning from the prior call, but it's better
|
|
|
|
|
* to not pull tuples until necessary.)
|
2010-10-14 16:56:39 -04:00
|
|
|
*/
|
2012-11-29 11:13:08 -05:00
|
|
|
i = DatumGetInt32(binaryheap_first(node->ms_heap));
|
2010-10-14 16:56:39 -04:00
|
|
|
node->ms_slots[i] = ExecProcNode(node->mergeplans[i]);
|
|
|
|
|
if (!TupIsNull(node->ms_slots[i]))
|
2012-11-29 11:13:08 -05:00
|
|
|
binaryheap_replace_first(node->ms_heap, Int32GetDatum(i));
|
|
|
|
|
else
|
|
|
|
|
(void) binaryheap_remove_first(node->ms_heap);
|
2010-10-14 16:56:39 -04:00
|
|
|
}
|
|
|
|
|
|
2012-11-29 11:13:08 -05:00
|
|
|
if (binaryheap_empty(node->ms_heap))
|
2010-10-14 16:56:39 -04:00
|
|
|
{
|
|
|
|
|
/* All the subplans are exhausted, and so is the heap */
|
|
|
|
|
result = ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
|
|
|
|
}
|
2012-11-29 11:13:08 -05:00
|
|
|
else
|
2010-10-14 16:56:39 -04:00
|
|
|
{
|
2012-11-29 11:13:08 -05:00
|
|
|
i = DatumGetInt32(binaryheap_first(node->ms_heap));
|
|
|
|
|
result = node->ms_slots[i];
|
2010-10-14 16:56:39 -04:00
|
|
|
}
|
|
|
|
|
|
2012-11-29 11:13:08 -05:00
|
|
|
return result;
|
2010-10-14 16:56:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Compare the tuples in the two given slots.
|
|
|
|
|
*/
|
|
|
|
|
static int32
|
2012-11-29 11:13:08 -05:00
|
|
|
heap_compare_slots(Datum a, Datum b, void *arg)
|
2010-10-14 16:56:39 -04:00
|
|
|
{
|
2012-11-29 11:13:08 -05:00
|
|
|
MergeAppendState *node = (MergeAppendState *) arg;
|
|
|
|
|
SlotNumber slot1 = DatumGetInt32(a);
|
|
|
|
|
SlotNumber slot2 = DatumGetInt32(b);
|
|
|
|
|
|
2010-10-14 16:56:39 -04:00
|
|
|
TupleTableSlot *s1 = node->ms_slots[slot1];
|
|
|
|
|
TupleTableSlot *s2 = node->ms_slots[slot2];
|
|
|
|
|
int nkey;
|
|
|
|
|
|
|
|
|
|
Assert(!TupIsNull(s1));
|
|
|
|
|
Assert(!TupIsNull(s2));
|
|
|
|
|
|
|
|
|
|
for (nkey = 0; nkey < node->ms_nkeys; nkey++)
|
|
|
|
|
{
|
2011-12-07 00:18:38 -05:00
|
|
|
SortSupport sortKey = node->ms_sortkeys + nkey;
|
|
|
|
|
AttrNumber attno = sortKey->ssup_attno;
|
2010-10-14 16:56:39 -04:00
|
|
|
Datum datum1,
|
|
|
|
|
datum2;
|
|
|
|
|
bool isNull1,
|
|
|
|
|
isNull2;
|
2011-12-07 00:18:38 -05:00
|
|
|
int compare;
|
2010-10-14 16:56:39 -04:00
|
|
|
|
|
|
|
|
datum1 = slot_getattr(s1, attno, &isNull1);
|
|
|
|
|
datum2 = slot_getattr(s2, attno, &isNull2);
|
|
|
|
|
|
2011-12-07 00:18:38 -05:00
|
|
|
compare = ApplySortComparator(datum1, isNull1,
|
|
|
|
|
datum2, isNull2,
|
|
|
|
|
sortKey);
|
|
|
|
|
if (compare != 0)
|
Allow btree comparison functions to return INT_MIN.
Historically we forbade datatype-specific comparison functions from
returning INT_MIN, so that it would be safe to invert the sort order
just by negating the comparison result. However, this was never
really safe for comparison functions that directly return the result
of memcmp(), strcmp(), etc, as POSIX doesn't place any such restriction
on those library functions. Buildfarm results show that at least on
recent Linux on s390x, memcmp() actually does return INT_MIN sometimes,
causing sort failures.
The agreed-on answer is to remove this restriction and fix relevant
call sites to not make such an assumption; code such as "res = -res"
should be replaced by "INVERT_COMPARE_RESULT(res)". The same is needed
in a few places that just directly negated the result of memcmp or
strcmp.
To help find places having this problem, I've also added a compile option
to nbtcompare.c that causes some of the commonly used comparators to
return INT_MIN/INT_MAX instead of their usual -1/+1. It'd likely be
a good idea to have at least one buildfarm member running with
"-DSTRESS_SORT_INT_MIN". That's far from a complete test of course,
but it should help to prevent fresh introductions of such bugs.
This is a longstanding portability hazard, so back-patch to all supported
branches.
Discussion: https://postgr.es/m/20180928185215.ffoq2xrq5d3pafna@alap3.anarazel.de
2018-10-05 16:01:29 -04:00
|
|
|
{
|
|
|
|
|
INVERT_COMPARE_RESULT(compare);
|
|
|
|
|
return compare;
|
|
|
|
|
}
|
2010-10-14 16:56:39 -04:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
|
* ExecEndMergeAppend
|
|
|
|
|
*
|
|
|
|
|
* Shuts down the subscans of the MergeAppend node.
|
|
|
|
|
*
|
|
|
|
|
* Returns nothing of interest.
|
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
ExecEndMergeAppend(MergeAppendState *node)
|
|
|
|
|
{
|
|
|
|
|
PlanState **mergeplans;
|
|
|
|
|
int nplans;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* get information from the node
|
|
|
|
|
*/
|
|
|
|
|
mergeplans = node->mergeplans;
|
|
|
|
|
nplans = node->ms_nplans;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* shut down each of the subscans
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0; i < nplans; i++)
|
|
|
|
|
ExecEndNode(mergeplans[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExecReScanMergeAppend(MergeAppendState *node)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
2018-07-19 06:49:43 -04:00
|
|
|
/*
|
|
|
|
|
* If any PARAM_EXEC Params used in pruning expressions have changed, then
|
|
|
|
|
* we'd better unset the valid subplans so that they are reselected for
|
|
|
|
|
* the new parameter values.
|
|
|
|
|
*/
|
|
|
|
|
if (node->ms_prune_state &&
|
|
|
|
|
bms_overlap(node->ps.chgParam,
|
|
|
|
|
node->ms_prune_state->execparamids))
|
|
|
|
|
{
|
|
|
|
|
bms_free(node->ms_valid_subplans);
|
|
|
|
|
node->ms_valid_subplans = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-14 16:56:39 -04:00
|
|
|
for (i = 0; i < node->ms_nplans; i++)
|
|
|
|
|
{
|
|
|
|
|
PlanState *subnode = node->mergeplans[i];
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ExecReScan doesn't know about my subplans, so I have to do
|
|
|
|
|
* changed-parameter signaling myself.
|
|
|
|
|
*/
|
|
|
|
|
if (node->ps.chgParam != NULL)
|
|
|
|
|
UpdateChangedParamSet(subnode, node->ps.chgParam);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If chgParam of subnode is not null then plan will be re-scanned by
|
|
|
|
|
* first ExecProcNode.
|
|
|
|
|
*/
|
|
|
|
|
if (subnode->chgParam == NULL)
|
|
|
|
|
ExecReScan(subnode);
|
|
|
|
|
}
|
2013-08-30 19:15:21 -04:00
|
|
|
binaryheap_reset(node->ms_heap);
|
2010-10-14 16:56:39 -04:00
|
|
|
node->ms_initialized = false;
|
|
|
|
|
}
|