1/*-------------------------------------------------------------------------
4 * Routines to support direct tid scans of relations
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/executor/nodeTidscan.c
13 *-------------------------------------------------------------------------
18 * ExecTidScan scans a relation using tids
19 * ExecInitTidScan creates and initializes state info.
20 * ExecReScanTidScan rescans the tid relation.
21 * ExecEndTidScan releases all storage.
38 * It's sufficient to check varattno to identify the CTID variable, as any
39 * Var in the relation scan qual must be for our table. (Even if it's a
40 * parameterized scan referencing some other table's CTID, the other table's
41 * Var would have become a Param by the time it gets here.)
43 #define IsCTIDVar(node) \
46 ((Var *) (node))->varattno == SelfItemPointerAttributeNumber)
48/* one element in tss_tidexprs */
52 bool isarray;
/* if true, it yields tid[] not just tid */
63 * Extract the qual subexpressions that yield TIDs to search for,
64 * and compile them into ExprStates if they're ordinary expressions.
66 * CURRENT OF is a special case that we can't compile usefully;
67 * just drop it into the TidExpr list as-is.
97 elog(
ERROR,
"could not identify CTID variable");
113 tidexpr->
cexpr = cexpr;
117 elog(
ERROR,
"could not identify CTID expression");
122 /* CurrentOfExpr could never appear OR'd with something else */
128 * Compute the list of TIDs to be visited, by evaluating the expressions
131 * (The result is actually an array, not a list.)
144 * Start scan on-demand - initializing a scan isn't free (e.g. heap stats
145 * the size of the table), so it makes sense to delay that until needed -
146 * the node might never get executed.
155 * We initialize the array with enough slots for the case that all quals
156 * are simple OpExprs or CurrentOfExprs. If there are any
157 * ScalarArrayOpExprs, we may have to enlarge the array.
180 * We silently discard any TIDs that the AM considers invalid
181 * (E.g. for heap, they could be out of range at the time of scan
182 * start. Since we hold at least AccessShareLock on the table, it
183 * won't be possible for someone to truncate away the blocks we
189 if (numTids >= numAllocTids)
196 tidList[numTids++] = *itemptr;
214 if (numTids + ndatums > numAllocTids)
216 numAllocTids = numTids + ndatums;
221 for (
i = 0;
i < ndatums;
i++)
231 tidList[numTids++] = *itemptr;
245 if (numTids >= numAllocTids)
252 tidList[numTids++] = cursor_tid;
258 * Sort the array of TIDs into order, and eliminate duplicates.
259 * Eliminating duplicates is necessary since we want OR semantics across
260 * the list. Sorting makes it easier to detect duplicates, and as a bonus
261 * ensures that we will visit the heap in the most efficient way.
265 /* CurrentOfExpr could never appear OR'd with something else */
280 * qsort comparator for ItemPointerData items
303/* ----------------------------------------------------------------
306 * Retrieve a tuple from the TidScan node's currentRelation
307 * using the tids in the TidScanState information.
309 * ----------------------------------------------------------------
325 * extract necessary information from tid scan node
334 * First time through, compute the list of TIDs to be visited
344 * Initialize or advance scan position, depending on direction.
351 /* initialize for backward scan */
361 /* initialize for forward scan */
373 * For WHERE CURRENT OF, the tuple retrieved from the cursor might
374 * since have been updated; if so, we should fetch the version that is
375 * current according to our snapshot.
383 /* Bad TID or failed snapshot qual; try next */
393 * if we get here it means the tid scan failed so we are at the end of the
400 * TidRecheck -- access method routine to recheck a tuple in EvalPlanQual
407 /* WHERE CURRENT OF always intends to resolve to the latest tuple */
415 * Binary search the TidList to see if this ctid is mentioned and return
421 return match != NULL;
425/* ----------------------------------------------------------------
428 * Scans the relation using tids and returns
429 * the next qualifying tuple in the direction specified.
430 * We call the ExecScan() routine and pass it the appropriate
431 * access method functions.
434 * -- the "cursor" maintained by the AMI is positioned at the tuple
435 * returned previously.
438 * -- the relation indicated is opened for scanning so that the
439 * "cursor" is positioned before the first qualifying tuple.
440 * -- tss_TidPtr is -1.
441 * ----------------------------------------------------------------
453/* ----------------------------------------------------------------
454 * ExecReScanTidScan(node)
455 * ----------------------------------------------------------------
466 /* not really necessary, but seems good form */
473/* ----------------------------------------------------------------
476 * Releases any storage allocated through C routines.
478 * ----------------------------------------------------------------
487/* ----------------------------------------------------------------
490 * Initializes the tid scan's state information, creates
491 * scan keys, and opens the base and tid relations.
494 * node: TidScan node produced by the planner.
495 * estate: the execution state initialized in InitPlan.
496 * ----------------------------------------------------------------
505 * create state structure
513 * Miscellaneous initialization
515 * create expression context for node
520 * mark tid list as not computed yet
527 * open the scan relation
535 * get the scan type from the relation descriptor.
542 * Initialize result type and projection.
548 * initialize child expressions
#define DatumGetArrayTypeP(X)
void deconstruct_array_builtin(ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Oid table_oid, ItemPointer current_tid)
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
ExprState * ExecInitQual(List *qual, PlanState *parent)
TupleTableSlot * ExecScan(ScanState *node, ExecScanAccessMtd accessMtd, ExecScanRecheckMtd recheckMtd)
void ExecAssignScanProjectionInfo(ScanState *node)
void ExecScanReScan(ScanState *node)
void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
void ExecInitResultTypeTL(PlanState *planstate)
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
bool(* ExecScanRecheckMtd)(ScanState *node, TupleTableSlot *slot)
TupleTableSlot *(* ExecScanAccessMtd)(ScanState *node)
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Assert(PointerIsAligned(start, uint64))
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
ItemPointerData * ItemPointer
List * lappend(List *list, void *datum)
void * repalloc(void *pointer, Size size)
void pfree(void *pointer)
void * palloc0(Size size)
#define CHECK_FOR_INTERRUPTS()
static Node * get_rightop(const void *clause)
static bool is_opclause(const void *clause)
static Node * get_leftop(const void *clause)
static TupleTableSlot * ExecTidScan(PlanState *pstate)
static int itemptr_comparator(const void *a, const void *b)
TidScanState * ExecInitTidScan(TidScan *node, EState *estate, int eflags)
static bool TidRecheck(TidScanState *node, TupleTableSlot *slot)
void ExecEndTidScan(TidScanState *node)
void ExecReScanTidScan(TidScanState *node)
static void TidListEval(TidScanState *tidstate)
static TupleTableSlot * TidNext(TidScanState *node)
static void TidExprListCreate(TidScanState *tidstate)
#define IsA(nodeptr, _type_)
#define castNode(_type_, nodeptr)
static int list_length(const List *l)
#define qsort(a, b, c, d)
static Pointer DatumGetPointer(Datum X)
static size_t qunique(void *array, size_t elements, size_t width, int(*compare)(const void *, const void *))
#define RelationGetRelid(relation)
#define RelationGetDescr(relation)
#define ScanDirectionIsBackward(direction)
ScanDirection es_direction
ExprContext * ps_ExprContext
ExecProcNodeMtd ExecProcNode
Relation ss_currentRelation
TupleTableSlot * ss_ScanTupleSlot
struct TableScanDescData * ss_currentScanDesc
ItemPointerData * tss_TidList
void table_tuple_get_latest_tid(TableScanDesc scan, ItemPointer tid)
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
static void table_endscan(TableScanDesc scan)
static bool table_tuple_tid_valid(TableScanDesc scan, ItemPointer tid)
static void table_rescan(TableScanDesc scan, ScanKeyData *key)
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
static TableScanDesc table_beginscan_tid(Relation rel, Snapshot snapshot)
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)