| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * nodeWorktablescan.c |
| 4 | * routines to handle WorkTableScan nodes. |
| 5 | * |
| 6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
| 7 | * Portions Copyright (c) 1994, Regents of the University of California |
| 8 | * |
| 9 | * |
| 10 | * IDENTIFICATION |
| 11 | * src/backend/executor/nodeWorktablescan.c |
| 12 | * |
| 13 | *------------------------------------------------------------------------- |
| 14 | */ |
| 15 | |
| 16 | #include "postgres.h" |
| 17 | |
| 18 | #include "executor/execdebug.h" |
| 19 | #include "executor/nodeWorktablescan.h" |
| 20 | |
| 21 | static TupleTableSlot *WorkTableScanNext(WorkTableScanState *node); |
| 22 | |
| 23 | /* ---------------------------------------------------------------- |
| 24 | * WorkTableScanNext |
| 25 | * |
| 26 | * This is a workhorse for ExecWorkTableScan |
| 27 | * ---------------------------------------------------------------- |
| 28 | */ |
| 29 | static TupleTableSlot * |
| 30 | WorkTableScanNext(WorkTableScanState *node) |
| 31 | { |
| 32 | TupleTableSlot *slot; |
| 33 | Tuplestorestate *tuplestorestate; |
| 34 | |
| 35 | /* |
| 36 | * get information from the estate and scan state |
| 37 | * |
| 38 | * Note: we intentionally do not support backward scan. Although it would |
| 39 | * take only a couple more lines here, it would force nodeRecursiveunion.c |
| 40 | * to create the tuplestore with backward scan enabled, which has a |
| 41 | * performance cost. In practice backward scan is never useful for a |
| 42 | * worktable plan node, since it cannot appear high enough in the plan |
| 43 | * tree of a scrollable cursor to be exposed to a backward-scan |
| 44 | * requirement. So it's not worth expending effort to support it. |
| 45 | * |
| 46 | * Note: we are also assuming that this node is the only reader of the |
| 47 | * worktable. Therefore, we don't need a private read pointer for the |
| 48 | * tuplestore, nor do we need to tell tuplestore_gettupleslot to copy. |
| 49 | */ |
| 50 | Assert(ScanDirectionIsForward(node->ss.ps.state->es_direction)); |
| 51 | |
| 52 | tuplestorestate = node->rustate->working_table; |
| 53 | |
| 54 | /* |
| 55 | * Get the next tuple from tuplestore. Return NULL if no more tuples. |
| 56 | */ |
| 57 | slot = node->ss.ss_ScanTupleSlot; |
| 58 | (void) tuplestore_gettupleslot(tuplestorestate, true, false, slot); |
| 59 | return slot; |
| 60 | } |
| 61 | |
| 62 | /* |
| 63 | * WorkTableScanRecheck -- access method routine to recheck a tuple in EvalPlanQual |
| 64 | */ |
| 65 | static bool |
| 66 | WorkTableScanRecheck(WorkTableScanState *node, TupleTableSlot *slot) |
| 67 | { |
| 68 | /* nothing to check */ |
| 69 | return true; |
| 70 | } |
| 71 | |
| 72 | /* ---------------------------------------------------------------- |
| 73 | * ExecWorkTableScan(node) |
| 74 | * |
| 75 | * Scans the worktable sequentially and returns the next qualifying tuple. |
| 76 | * We call the ExecScan() routine and pass it the appropriate |
| 77 | * access method functions. |
| 78 | * ---------------------------------------------------------------- |
| 79 | */ |
| 80 | static TupleTableSlot * |
| 81 | ExecWorkTableScan(PlanState *pstate) |
| 82 | { |
| 83 | WorkTableScanState *node = castNode(WorkTableScanState, pstate); |
| 84 | |
| 85 | /* |
| 86 | * On the first call, find the ancestor RecursiveUnion's state via the |
| 87 | * Param slot reserved for it. (We can't do this during node init because |
| 88 | * there are corner cases where we'll get the init call before the |
| 89 | * RecursiveUnion does.) |
| 90 | */ |
| 91 | if (node->rustate == NULL) |
| 92 | { |
| 93 | WorkTableScan *plan = (WorkTableScan *) node->ss.ps.plan; |
| 94 | EState *estate = node->ss.ps.state; |
| 95 | ParamExecData *param; |
| 96 | |
| 97 | param = &(estate->es_param_exec_vals[plan->wtParam]); |
| 98 | Assert(param->execPlan == NULL); |
| 99 | Assert(!param->isnull); |
| 100 | node->rustate = castNode(RecursiveUnionState, DatumGetPointer(param->value)); |
| 101 | Assert(node->rustate); |
| 102 | |
| 103 | /* |
| 104 | * The scan tuple type (ie, the rowtype we expect to find in the work |
| 105 | * table) is the same as the result rowtype of the ancestor |
| 106 | * RecursiveUnion node. Note this depends on the assumption that |
| 107 | * RecursiveUnion doesn't allow projection. |
| 108 | */ |
| 109 | ExecAssignScanType(&node->ss, |
| 110 | ExecGetResultType(&node->rustate->ps)); |
| 111 | |
| 112 | /* |
| 113 | * Now we can initialize the projection info. This must be completed |
| 114 | * before we can call ExecScan(). |
| 115 | */ |
| 116 | ExecAssignScanProjectionInfo(&node->ss); |
| 117 | } |
| 118 | |
| 119 | return ExecScan(&node->ss, |
| 120 | (ExecScanAccessMtd) WorkTableScanNext, |
| 121 | (ExecScanRecheckMtd) WorkTableScanRecheck); |
| 122 | } |
| 123 | |
| 124 | |
| 125 | /* ---------------------------------------------------------------- |
| 126 | * ExecInitWorkTableScan |
| 127 | * ---------------------------------------------------------------- |
| 128 | */ |
| 129 | WorkTableScanState * |
| 130 | ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) |
| 131 | { |
| 132 | WorkTableScanState *scanstate; |
| 133 | |
| 134 | /* check for unsupported flags */ |
| 135 | Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); |
| 136 | |
| 137 | /* |
| 138 | * WorkTableScan should not have any children. |
| 139 | */ |
| 140 | Assert(outerPlan(node) == NULL); |
| 141 | Assert(innerPlan(node) == NULL); |
| 142 | |
| 143 | /* |
| 144 | * create new WorkTableScanState for node |
| 145 | */ |
| 146 | scanstate = makeNode(WorkTableScanState); |
| 147 | scanstate->ss.ps.plan = (Plan *) node; |
| 148 | scanstate->ss.ps.state = estate; |
| 149 | scanstate->ss.ps.ExecProcNode = ExecWorkTableScan; |
| 150 | scanstate->rustate = NULL; /* we'll set this later */ |
| 151 | |
| 152 | /* |
| 153 | * Miscellaneous initialization |
| 154 | * |
| 155 | * create expression context for node |
| 156 | */ |
| 157 | ExecAssignExprContext(estate, &scanstate->ss.ps); |
| 158 | |
| 159 | /* |
| 160 | * tuple table initialization |
| 161 | */ |
| 162 | ExecInitResultTypeTL(&scanstate->ss.ps); |
| 163 | |
| 164 | /* signal that return type is not yet known */ |
| 165 | scanstate->ss.ps.resultopsset = true; |
| 166 | scanstate->ss.ps.resultopsfixed = false; |
| 167 | |
| 168 | ExecInitScanTupleSlot(estate, &scanstate->ss, NULL, &TTSOpsMinimalTuple); |
| 169 | |
| 170 | /* |
| 171 | * initialize child expressions |
| 172 | */ |
| 173 | scanstate->ss.ps.qual = |
| 174 | ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate); |
| 175 | |
| 176 | /* |
| 177 | * Do not yet initialize projection info, see ExecWorkTableScan() for |
| 178 | * details. |
| 179 | */ |
| 180 | |
| 181 | return scanstate; |
| 182 | } |
| 183 | |
| 184 | /* ---------------------------------------------------------------- |
| 185 | * ExecEndWorkTableScan |
| 186 | * |
| 187 | * frees any storage allocated through C routines. |
| 188 | * ---------------------------------------------------------------- |
| 189 | */ |
| 190 | void |
| 191 | ExecEndWorkTableScan(WorkTableScanState *node) |
| 192 | { |
| 193 | /* |
| 194 | * Free exprcontext |
| 195 | */ |
| 196 | ExecFreeExprContext(&node->ss.ps); |
| 197 | |
| 198 | /* |
| 199 | * clean out the tuple table |
| 200 | */ |
| 201 | if (node->ss.ps.ps_ResultTupleSlot) |
| 202 | ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); |
| 203 | ExecClearTuple(node->ss.ss_ScanTupleSlot); |
| 204 | } |
| 205 | |
| 206 | /* ---------------------------------------------------------------- |
| 207 | * ExecReScanWorkTableScan |
| 208 | * |
| 209 | * Rescans the relation. |
| 210 | * ---------------------------------------------------------------- |
| 211 | */ |
| 212 | void |
| 213 | ExecReScanWorkTableScan(WorkTableScanState *node) |
| 214 | { |
| 215 | if (node->ss.ps.ps_ResultTupleSlot) |
| 216 | ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); |
| 217 | |
| 218 | ExecScanReScan(&node->ss); |
| 219 | |
| 220 | /* No need (or way) to rescan if ExecWorkTableScan not called yet */ |
| 221 | if (node->rustate) |
| 222 | tuplestore_rescan(node->rustate->working_table); |
| 223 | } |
| 224 | |