/*------------------------------------------------------------------------- * * nodeWorktablescan.c * routines to handle WorkTableScan nodes. * * Portions Copyright (c) 1996-2020, 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. */ Assert(ScanDirectionIsForward(node->ss.ps.state->es_direction)); tuplestorestate = node->rustate->working_table; /* * 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); /* * On the first call, find the ancestor RecursiveUnion's state via the * Param slot reserved for it. (We can't do this during node init because * there are corner cases where we'll get the init call before the * RecursiveUnion does.) */ if (node->rustate == NULL) { WorkTableScan *plan = (WorkTableScan *) node->ss.ps.plan; EState *estate = node->ss.ps.state; ParamExecData *param; param = &(estate->es_param_exec_vals[plan->wtParam]); Assert(param->execPlan == NULL); Assert(!param->isnull); node->rustate = castNode(RecursiveUnionState, DatumGetPointer(param->value)); Assert(node->rustate); /* * 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. */ ExecAssignScanType(&node->ss, ExecGetResultType(&node->rustate->ps)); /* * Now we can initialize the projection info. This must be completed * before we can call ExecScan(). */ ExecAssignScanProjectionInfo(&node->ss); } return ExecScan(&node->ss, (ExecScanAccessMtd) WorkTableScanNext, (ExecScanRecheckMtd) WorkTableScanRecheck); } /* ---------------------------------------------------------------- * ExecInitWorkTableScan * ---------------------------------------------------------------- */ WorkTableScanState * ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) { WorkTableScanState *scanstate; /* check for unsupported flags */ 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; scanstate->rustate = NULL; /* we'll set this later */ /* * 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; ExecInitScanTupleSlot(estate, &scanstate->ss, NULL, &TTSOpsMinimalTuple); /* * 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_rescan(node->rustate->working_table); }