diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:15:05 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:15:05 +0000 |
commit | 46651ce6fe013220ed397add242004d764fc0153 (patch) | |
tree | 6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/backend/executor/nodeMaterial.c | |
parent | Initial commit. (diff) | |
download | postgresql-14-46651ce6fe013220ed397add242004d764fc0153.tar.xz postgresql-14-46651ce6fe013220ed397add242004d764fc0153.zip |
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/executor/nodeMaterial.c')
-rw-r--r-- | src/backend/executor/nodeMaterial.c | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c new file mode 100644 index 0000000..7c53f8e --- /dev/null +++ b/src/backend/executor/nodeMaterial.c @@ -0,0 +1,368 @@ +/*------------------------------------------------------------------------- + * + * nodeMaterial.c + * Routines to handle materialization nodes. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/executor/nodeMaterial.c + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * ExecMaterial - materialize the result of a subplan + * ExecInitMaterial - initialize node and subnodes + * ExecEndMaterial - shutdown node and subnodes + * + */ +#include "postgres.h" + +#include "executor/executor.h" +#include "executor/nodeMaterial.h" +#include "miscadmin.h" + +/* ---------------------------------------------------------------- + * ExecMaterial + * + * As long as we are at the end of the data collected in the tuplestore, + * we collect one new row from the subplan on each call, and stash it + * aside in the tuplestore before returning it. The tuplestore is + * only read if we are asked to scan backwards, rescan, or mark/restore. + * + * ---------------------------------------------------------------- + */ +static TupleTableSlot * /* result tuple from subplan */ +ExecMaterial(PlanState *pstate) +{ + MaterialState *node = castNode(MaterialState, pstate); + EState *estate; + ScanDirection dir; + bool forward; + Tuplestorestate *tuplestorestate; + bool eof_tuplestore; + TupleTableSlot *slot; + + CHECK_FOR_INTERRUPTS(); + + /* + * get state info from node + */ + estate = node->ss.ps.state; + dir = estate->es_direction; + forward = ScanDirectionIsForward(dir); + tuplestorestate = node->tuplestorestate; + + /* + * If first time through, and we need a tuplestore, initialize it. + */ + if (tuplestorestate == NULL && node->eflags != 0) + { + tuplestorestate = tuplestore_begin_heap(true, false, work_mem); + tuplestore_set_eflags(tuplestorestate, node->eflags); + if (node->eflags & EXEC_FLAG_MARK) + { + /* + * Allocate a second read pointer to serve as the mark. We know it + * must have index 1, so needn't store that. + */ + int ptrno PG_USED_FOR_ASSERTS_ONLY; + + ptrno = tuplestore_alloc_read_pointer(tuplestorestate, + node->eflags); + Assert(ptrno == 1); + } + node->tuplestorestate = tuplestorestate; + } + + /* + * If we are not at the end of the tuplestore, or are going backwards, try + * to fetch a tuple from tuplestore. + */ + eof_tuplestore = (tuplestorestate == NULL) || + tuplestore_ateof(tuplestorestate); + + if (!forward && eof_tuplestore) + { + if (!node->eof_underlying) + { + /* + * When reversing direction at tuplestore EOF, the first + * gettupleslot call will fetch the last-added tuple; but we want + * to return the one before that, if possible. So do an extra + * fetch. + */ + if (!tuplestore_advance(tuplestorestate, forward)) + return NULL; /* the tuplestore must be empty */ + } + eof_tuplestore = false; + } + + /* + * If we can fetch another tuple from the tuplestore, return it. + */ + slot = node->ss.ps.ps_ResultTupleSlot; + if (!eof_tuplestore) + { + if (tuplestore_gettupleslot(tuplestorestate, forward, false, slot)) + return slot; + if (forward) + eof_tuplestore = true; + } + + /* + * If necessary, try to fetch another row from the subplan. + * + * Note: the eof_underlying state variable exists to short-circuit further + * subplan calls. It's not optional, unfortunately, because some plan + * node types are not robust about being called again when they've already + * returned NULL. + */ + if (eof_tuplestore && !node->eof_underlying) + { + PlanState *outerNode; + TupleTableSlot *outerslot; + + /* + * We can only get here with forward==true, so no need to worry about + * which direction the subplan will go. + */ + outerNode = outerPlanState(node); + outerslot = ExecProcNode(outerNode); + if (TupIsNull(outerslot)) + { + node->eof_underlying = true; + return NULL; + } + + /* + * Append a copy of the returned tuple to tuplestore. NOTE: because + * the tuplestore is certainly in EOF state, its read position will + * move forward over the added tuple. This is what we want. + */ + if (tuplestorestate) + tuplestore_puttupleslot(tuplestorestate, outerslot); + + ExecCopySlot(slot, outerslot); + return slot; + } + + /* + * Nothing left ... + */ + return ExecClearTuple(slot); +} + +/* ---------------------------------------------------------------- + * ExecInitMaterial + * ---------------------------------------------------------------- + */ +MaterialState * +ExecInitMaterial(Material *node, EState *estate, int eflags) +{ + MaterialState *matstate; + Plan *outerPlan; + + /* + * create state structure + */ + matstate = makeNode(MaterialState); + matstate->ss.ps.plan = (Plan *) node; + matstate->ss.ps.state = estate; + matstate->ss.ps.ExecProcNode = ExecMaterial; + + /* + * We must have a tuplestore buffering the subplan output to do backward + * scan or mark/restore. We also prefer to materialize the subplan output + * if we might be called on to rewind and replay it many times. However, + * if none of these cases apply, we can skip storing the data. + */ + matstate->eflags = (eflags & (EXEC_FLAG_REWIND | + EXEC_FLAG_BACKWARD | + EXEC_FLAG_MARK)); + + /* + * Tuplestore's interpretation of the flag bits is subtly different from + * the general executor meaning: it doesn't think BACKWARD necessarily + * means "backwards all the way to start". If told to support BACKWARD we + * must include REWIND in the tuplestore eflags, else tuplestore_trim + * might throw away too much. + */ + if (eflags & EXEC_FLAG_BACKWARD) + matstate->eflags |= EXEC_FLAG_REWIND; + + matstate->eof_underlying = false; + matstate->tuplestorestate = NULL; + + /* + * Miscellaneous initialization + * + * Materialization nodes don't need ExprContexts because they never call + * ExecQual or ExecProject. + */ + + /* + * initialize child nodes + * + * We shield the child node from the need to support REWIND, BACKWARD, or + * MARK/RESTORE. + */ + eflags &= ~(EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK); + + outerPlan = outerPlan(node); + outerPlanState(matstate) = ExecInitNode(outerPlan, estate, eflags); + + /* + * Initialize result type and slot. No need to initialize projection info + * because this node doesn't do projections. + * + * material nodes only return tuples from their materialized relation. + */ + ExecInitResultTupleSlotTL(&matstate->ss.ps, &TTSOpsMinimalTuple); + matstate->ss.ps.ps_ProjInfo = NULL; + + /* + * initialize tuple type. + */ + ExecCreateScanSlotFromOuterPlan(estate, &matstate->ss, &TTSOpsMinimalTuple); + + return matstate; +} + +/* ---------------------------------------------------------------- + * ExecEndMaterial + * ---------------------------------------------------------------- + */ +void +ExecEndMaterial(MaterialState *node) +{ + /* + * clean out the tuple table + */ + ExecClearTuple(node->ss.ss_ScanTupleSlot); + + /* + * Release tuplestore resources + */ + if (node->tuplestorestate != NULL) + tuplestore_end(node->tuplestorestate); + node->tuplestorestate = NULL; + + /* + * shut down the subplan + */ + ExecEndNode(outerPlanState(node)); +} + +/* ---------------------------------------------------------------- + * ExecMaterialMarkPos + * + * Calls tuplestore to save the current position in the stored file. + * ---------------------------------------------------------------- + */ +void +ExecMaterialMarkPos(MaterialState *node) +{ + Assert(node->eflags & EXEC_FLAG_MARK); + + /* + * if we haven't materialized yet, just return. + */ + if (!node->tuplestorestate) + return; + + /* + * copy the active read pointer to the mark. + */ + tuplestore_copy_read_pointer(node->tuplestorestate, 0, 1); + + /* + * since we may have advanced the mark, try to truncate the tuplestore. + */ + tuplestore_trim(node->tuplestorestate); +} + +/* ---------------------------------------------------------------- + * ExecMaterialRestrPos + * + * Calls tuplestore to restore the last saved file position. + * ---------------------------------------------------------------- + */ +void +ExecMaterialRestrPos(MaterialState *node) +{ + Assert(node->eflags & EXEC_FLAG_MARK); + + /* + * if we haven't materialized yet, just return. + */ + if (!node->tuplestorestate) + return; + + /* + * copy the mark to the active read pointer. + */ + tuplestore_copy_read_pointer(node->tuplestorestate, 1, 0); +} + +/* ---------------------------------------------------------------- + * ExecReScanMaterial + * + * Rescans the materialized relation. + * ---------------------------------------------------------------- + */ +void +ExecReScanMaterial(MaterialState *node) +{ + PlanState *outerPlan = outerPlanState(node); + + ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); + + if (node->eflags != 0) + { + /* + * If we haven't materialized yet, just return. If outerplan's + * chgParam is not NULL then it will be re-scanned by ExecProcNode, + * else no reason to re-scan it at all. + */ + if (!node->tuplestorestate) + return; + + /* + * If subnode is to be rescanned then we forget previous stored + * results; we have to re-read the subplan and re-store. Also, if we + * told tuplestore it needn't support rescan, we lose and must + * re-read. (This last should not happen in common cases; else our + * caller lied by not passing EXEC_FLAG_REWIND to us.) + * + * Otherwise we can just rewind and rescan the stored output. The + * state of the subnode does not change. + */ + if (outerPlan->chgParam != NULL || + (node->eflags & EXEC_FLAG_REWIND) == 0) + { + tuplestore_end(node->tuplestorestate); + node->tuplestorestate = NULL; + if (outerPlan->chgParam == NULL) + ExecReScan(outerPlan); + node->eof_underlying = false; + } + else + tuplestore_rescan(node->tuplestorestate); + } + else + { + /* In this case we are just passing on the subquery's output */ + + /* + * if chgParam of subnode is not null then plan will be re-scanned by + * first ExecProcNode. + */ + if (outerPlan->chgParam == NULL) + ExecReScan(outerPlan); + node->eof_underlying = false; + } +} |