blob: f1a8358ffbe3d37a57188097c2ad77625e5dcff4 [file] [log] [blame]
/*-------------------------------------------------------------------------
*
* nodeWorktablescan.c
* routines to handle WorkTableScan nodes.
*
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/executor/nodeWorktablescan.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "executor/execdebug.h"
#include "executor/nodeWorktablescan.h"
static TupleTableSlot *WorkTableScanNext(WorkTableScanState *node);
/* ----------------------------------------------------------------
* WorkTableScanNext
*
* This is a workhorse for ExecWorkTableScan
* ----------------------------------------------------------------
*/
static TupleTableSlot *
WorkTableScanNext(WorkTableScanState *node)
{
TupleTableSlot *slot;
Tuplestorestate *tuplestorestate;
/*
* get information from the estate and scan state
*
* Note: we intentionally do not support backward scan. Although it would
* take only a couple more lines here, it would force nodeRecursiveunion.c
* to create the tuplestore with backward scan enabled, which has a
* performance cost. In practice backward scan is never useful for a
* worktable plan node, since it cannot appear high enough in the plan
* tree of a scrollable cursor to be exposed to a backward-scan
* requirement. So it's not worth expending effort to support it.
*
* Note: we are also assuming that this node is the only reader of the
* worktable. Therefore, we don't need a private read pointer for the
* tuplestore, nor do we need to tell tuplestore_gettupleslot to copy.
*/
/*
* RECURSIVE_CTE_FIXME: Double check we don't have backward scan required by
* plan (both planner and ORCA).
*/
Assert(ScanDirectionIsForward(node->ss.ps.state->es_direction));
tuplestorestate = node->rustate->working_table;
tuplestore_select_read_pointer(tuplestorestate, node->readptr);
/*
* Get the next tuple from tuplestore. Return NULL if no more tuples.
*/
slot = node->ss.ss_ScanTupleSlot;
(void) tuplestore_gettupleslot(tuplestorestate, true, false, slot);
return slot;
}
/*
* WorkTableScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
*/
static bool
WorkTableScanRecheck(WorkTableScanState *node, TupleTableSlot *slot)
{
/* nothing to check */
return true;
}
/* ----------------------------------------------------------------
* ExecWorkTableScan(node)
*
* Scans the worktable sequentially and returns the next qualifying tuple.
* We call the ExecScan() routine and pass it the appropriate
* access method functions.
* ----------------------------------------------------------------
*/
static TupleTableSlot *
ExecWorkTableScan(PlanState *pstate)
{
WorkTableScanState *node = castNode(WorkTableScanState, pstate);
return ExecScan(&node->ss,
(ExecScanAccessMtd) WorkTableScanNext,
(ExecScanRecheckMtd) WorkTableScanRecheck);
}
/* ----------------------------------------------------------------
* ExecInitWorkTableScan
* ----------------------------------------------------------------
*/
WorkTableScanState *
ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
{
WorkTableScanState *scanstate;
ParamExecData *param;
/* check for unsupported flags */
/*
* RECURSIVE_CTE_FIXME: Make sure we don't require EXEC_FLAG_BACKWARD
* in GPDB.
*/
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
/*
* WorkTableScan should not have any children.
*/
Assert(outerPlan(node) == NULL);
Assert(innerPlan(node) == NULL);
/*
* create new WorkTableScanState for node
*/
scanstate = makeNode(WorkTableScanState);
scanstate->ss.ps.plan = (Plan *) node;
scanstate->ss.ps.state = estate;
scanstate->ss.ps.ExecProcNode = ExecWorkTableScan;
/* In postgres, it can generate CTE SubPlans for WITH subqueries, if a CTE subplan have outer
* recursive refs with outer recursive CTE, it will exist a WorkTable in subplan. And initialize
* state information for subplans will be before initialize on the main query tree, so there are
* corner cases where we'll get the init call before the RecursiveUnion does.
* IN GPDB, we don't have CTE scan node, so we won't generate CTE subplan for WITH subqueries,
* also we won't call the ExecInitWorkTableScan func before ExecInitRecursiveUnion. Set rustate
* in the INIT step.
*/
param = &(estate->es_param_exec_vals[node->wtParam]);
Assert(param->execPlan == NULL);
Assert(!param->isnull);
scanstate->rustate = castNode(RecursiveUnionState, DatumGetPointer(param->value));
if (scanstate->rustate->refcount == 0)
scanstate->readptr = 0;
else
{
/* during node init, the work table hasn't been scanned yet, it must be at start, don't need to rescan here*/
scanstate->readptr = tuplestore_alloc_read_pointer(scanstate->rustate->working_table, EXEC_FLAG_REWIND);
}
scanstate->rustate->refcount++;
/*
* Miscellaneous initialization
*
* create expression context for node
*/
ExecAssignExprContext(estate, &scanstate->ss.ps);
/*
* tuple table initialization
*/
ExecInitResultTypeTL(&scanstate->ss.ps);
/* signal that return type is not yet known */
scanstate->ss.ps.resultopsset = true;
scanstate->ss.ps.resultopsfixed = false;
/* The scan tuple type (ie, the rowtype we expect to find in the work
* table) is the same as the result rowtype of the ancestor
* RecursiveUnion node. Note this depends on the assumption that
* RecursiveUnion doesn't allow projection.
*/
ExecInitScanTupleSlot(estate, &scanstate->ss, ExecGetResultType(&scanstate->rustate->ps), &TTSOpsMinimalTuple);
ExecAssignScanProjectionInfo(&scanstate->ss);
/*
* initialize child expressions
*/
scanstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
/*
* Do not yet initialize projection info, see ExecWorkTableScan() for
* details.
*/
return scanstate;
}
/* ----------------------------------------------------------------
* ExecEndWorkTableScan
*
* frees any storage allocated through C routines.
* ----------------------------------------------------------------
*/
void
ExecEndWorkTableScan(WorkTableScanState *node)
{
/*
* Free exprcontext
*/
ExecFreeExprContext(&node->ss.ps);
/*
* clean out the tuple table
*/
if (node->ss.ps.ps_ResultTupleSlot)
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
ExecClearTuple(node->ss.ss_ScanTupleSlot);
}
/* ----------------------------------------------------------------
* ExecReScanWorkTableScan
*
* Rescans the relation.
* ----------------------------------------------------------------
*/
void
ExecReScanWorkTableScan(WorkTableScanState *node)
{
if (node->ss.ps.ps_ResultTupleSlot)
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
ExecScanReScan(&node->ss);
/* No need (or way) to rescan if ExecWorkTableScan not called yet */
if (node->rustate)
{
tuplestore_select_read_pointer(node->rustate->working_table, node->readptr);
tuplestore_rescan(node->rustate->working_table);
}
}