Line data Source code
1 : /*------------------------------------------------------------------------- 2 : * execScan.h 3 : * Inline-able support functions for Scan nodes 4 : * 5 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group 6 : * Portions Copyright (c) 1994, Regents of the University of California 7 : * 8 : * IDENTIFICATION 9 : * src/include/executor/execScan.h 10 : *------------------------------------------------------------------------- 11 : */ 12 : 13 : #ifndef EXECSCAN_H 14 : #define EXECSCAN_H 15 : 16 : #include "miscadmin.h" 17 : #include "executor/executor.h" 18 : #include "nodes/execnodes.h" 19 : 20 : /* 21 : * ExecScanFetch -- check interrupts & fetch next potential tuple 22 : * 23 : * This routine substitutes a test tuple if inside an EvalPlanQual recheck. 24 : * Otherwise, it simply executes the access method's next-tuple routine. 25 : * 26 : * The pg_attribute_always_inline attribute allows the compiler to inline 27 : * this function into its caller. When EPQState is NULL, the EvalPlanQual 28 : * logic is completely eliminated at compile time, avoiding unnecessary 29 : * run-time checks and code for cases where EPQ is not required. 30 : */ 31 : static pg_attribute_always_inline TupleTableSlot * 32 110533002 : ExecScanFetch(ScanState *node, 33 : EPQState *epqstate, 34 : ExecScanAccessMtd accessMtd, 35 : ExecScanRecheckMtd recheckMtd) 36 : { 37 110533002 : CHECK_FOR_INTERRUPTS(); 38 : 39 110533002 : if (epqstate != NULL) 40 : { 41 : /* 42 : * We are inside an EvalPlanQual recheck. Return the test tuple if 43 : * one is available, after rechecking any access-method-specific 44 : * conditions. 45 : */ 46 678 : Index scanrelid = ((Scan *) node->ps.plan)->scanrelid; 47 : 48 678 : if (scanrelid == 0) 49 : { 50 : /* 51 : * This is a ForeignScan or CustomScan which has pushed down a 52 : * join to the remote side. The recheck method is responsible not 53 : * only for rechecking the scan/join quals but also for storing 54 : * the correct tuple in the slot. 55 : */ 56 : 57 0 : TupleTableSlot *slot = node->ss_ScanTupleSlot; 58 : 59 0 : if (!(*recheckMtd) (node, slot)) 60 0 : ExecClearTuple(slot); /* would not be returned by scan */ 61 0 : return slot; 62 : } 63 678 : else if (epqstate->relsubs_done[scanrelid - 1]) 64 : { 65 : /* 66 : * Return empty slot, as either there is no EPQ tuple for this rel 67 : * or we already returned it. 68 : */ 69 : 70 314 : TupleTableSlot *slot = node->ss_ScanTupleSlot; 71 : 72 314 : return ExecClearTuple(slot); 73 : } 74 364 : else if (epqstate->relsubs_slot[scanrelid - 1] != NULL) 75 : { 76 : /* 77 : * Return replacement tuple provided by the EPQ caller. 78 : */ 79 : 80 316 : TupleTableSlot *slot = epqstate->relsubs_slot[scanrelid - 1]; 81 : 82 : Assert(epqstate->relsubs_rowmark[scanrelid - 1] == NULL); 83 : 84 : /* Mark to remember that we shouldn't return it again */ 85 316 : epqstate->relsubs_done[scanrelid - 1] = true; 86 : 87 : /* Return empty slot if we haven't got a test tuple */ 88 316 : if (TupIsNull(slot)) 89 6 : return NULL; 90 : 91 : /* Check if it meets the access-method conditions */ 92 310 : if (!(*recheckMtd) (node, slot)) 93 8 : return ExecClearTuple(slot); /* would not be returned by 94 : * scan */ 95 302 : return slot; 96 : } 97 48 : else if (epqstate->relsubs_rowmark[scanrelid - 1] != NULL) 98 : { 99 : /* 100 : * Fetch and return replacement tuple using a non-locking rowmark. 101 : */ 102 : 103 26 : TupleTableSlot *slot = node->ss_ScanTupleSlot; 104 : 105 : /* Mark to remember that we shouldn't return more */ 106 26 : epqstate->relsubs_done[scanrelid - 1] = true; 107 : 108 26 : if (!EvalPlanQualFetchRowMark(epqstate, scanrelid, slot)) 109 0 : return NULL; 110 : 111 : /* Return empty slot if we haven't got a test tuple */ 112 26 : if (TupIsNull(slot)) 113 0 : return NULL; 114 : 115 : /* Check if it meets the access-method conditions */ 116 26 : if (!(*recheckMtd) (node, slot)) 117 0 : return ExecClearTuple(slot); /* would not be returned by 118 : * scan */ 119 26 : return slot; 120 : } 121 : } 122 : 123 : /* 124 : * Run the node-type-specific access method function to get the next tuple 125 : */ 126 110532346 : return (*accessMtd) (node); 127 : } 128 : 129 : /* ---------------------------------------------------------------- 130 : * ExecScanExtended 131 : * Scans the relation using the specified 'access method' and returns the 132 : * next tuple. Optionally checks the tuple against 'qual' and applies 133 : * 'projInfo' if provided. 134 : * 135 : * The 'recheck method' validates an arbitrary tuple of the relation against 136 : * conditions enforced by the access method. 137 : * 138 : * This function is an alternative to ExecScan, used when callers may omit 139 : * 'qual' or 'projInfo'. The pg_attribute_always_inline attribute allows the 140 : * compiler to eliminate non-relevant branches at compile time, avoiding 141 : * run-time checks in those cases. 142 : * 143 : * Conditions: 144 : * -- The AMI "cursor" is positioned at the previously returned tuple. 145 : * 146 : * Initial States: 147 : * -- The relation is opened for scanning, with the "cursor" 148 : * positioned before the first qualifying tuple. 149 : * ---------------------------------------------------------------- 150 : */ 151 : static pg_attribute_always_inline TupleTableSlot * 152 75519460 : ExecScanExtended(ScanState *node, 153 : ExecScanAccessMtd accessMtd, /* function returning a tuple */ 154 : ExecScanRecheckMtd recheckMtd, 155 : EPQState *epqstate, 156 : ExprState *qual, 157 : ProjectionInfo *projInfo) 158 : { 159 75519460 : ExprContext *econtext = node->ps.ps_ExprContext; 160 : 161 : /* interrupt checks are in ExecScanFetch */ 162 : 163 : /* 164 : * If we have neither a qual to check nor a projection to do, just skip 165 : * all the overhead and return the raw scan tuple. 166 : */ 167 75519460 : if (!qual && !projInfo) 168 : { 169 23966246 : ResetExprContext(econtext); 170 23966246 : return ExecScanFetch(node, epqstate, accessMtd, recheckMtd); 171 : } 172 : 173 : /* 174 : * Reset per-tuple memory context to free any expression evaluation 175 : * storage allocated in the previous tuple cycle. 176 : */ 177 51553214 : ResetExprContext(econtext); 178 : 179 : /* 180 : * get a tuple from the access method. Loop until we obtain a tuple that 181 : * passes the qualification. 182 : */ 183 : for (;;) 184 35013542 : { 185 : TupleTableSlot *slot; 186 : 187 86566756 : slot = ExecScanFetch(node, epqstate, accessMtd, recheckMtd); 188 : 189 : /* 190 : * if the slot returned by the accessMtd contains NULL, then it means 191 : * there is nothing more to scan so we just return an empty slot, 192 : * being careful to use the projection result slot so it has correct 193 : * tupleDesc. 194 : */ 195 86566238 : if (TupIsNull(slot)) 196 : { 197 1767416 : if (projInfo) 198 1603094 : return ExecClearTuple(projInfo->pi_state.resultslot); 199 : else 200 164322 : return slot; 201 : } 202 : 203 : /* 204 : * place the current tuple into the expr context 205 : */ 206 84798822 : econtext->ecxt_scantuple = slot; 207 : 208 : /* 209 : * check that the current tuple satisfies the qual-clause 210 : * 211 : * check for non-null qual here to avoid a function call to ExecQual() 212 : * when the qual is null ... saves only a few cycles, but they add up 213 : * ... 214 : */ 215 84798822 : if (qual == NULL || ExecQual(qual, econtext)) 216 : { 217 : /* 218 : * Found a satisfactory scan tuple. 219 : */ 220 49785246 : if (projInfo) 221 : { 222 : /* 223 : * Form a projection tuple, store it in the result tuple slot 224 : * and return it. 225 : */ 226 43844174 : return ExecProject(projInfo); 227 : } 228 : else 229 : { 230 : /* 231 : * Here, we aren't projecting, so just return scan tuple. 232 : */ 233 5941072 : return slot; 234 : } 235 : } 236 : else 237 35013542 : InstrCountFiltered1(node, 1); 238 : 239 : /* 240 : * Tuple fails qual, so free per-tuple memory and try again. 241 : */ 242 35013542 : ResetExprContext(econtext); 243 : } 244 : } 245 : 246 : #endif /* EXECSCAN_H */