#include "postgres.h"
#include "access/relscan.h"
#include "access/visibilitymap.h"
#include "executor/execdebug.h"
#include "executor/nodeIndexonlyscan.h"
#include "executor/nodeIndexscan.h"
#include "storage/bufmgr.h"
#include "storage/predicate.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Go to the source code of this file.
Functions | |
static TupleTableSlot * | IndexOnlyNext (IndexOnlyScanState *node) |
static void | StoreIndexTuple (TupleTableSlot *slot, IndexTuple itup, TupleDesc itupdesc) |
static bool | IndexOnlyRecheck (IndexOnlyScanState *node, TupleTableSlot *slot) |
TupleTableSlot * | ExecIndexOnlyScan (IndexOnlyScanState *node) |
void | ExecReScanIndexOnlyScan (IndexOnlyScanState *node) |
void | ExecEndIndexOnlyScan (IndexOnlyScanState *node) |
void | ExecIndexOnlyMarkPos (IndexOnlyScanState *node) |
void | ExecIndexOnlyRestrPos (IndexOnlyScanState *node) |
IndexOnlyScanState * | ExecInitIndexOnlyScan (IndexOnlyScan *node, EState *estate, int eflags) |
void ExecEndIndexOnlyScan | ( | IndexOnlyScanState * | node | ) |
Definition at line 283 of file nodeIndexonlyscan.c.
References ExecClearTuple(), ExecCloseScanRelation(), ExecFreeExprContext(), FreeExprContext(), index_close(), index_endscan(), InvalidBuffer, IndexOnlyScanState::ioss_RelationDesc, IndexOnlyScanState::ioss_RuntimeContext, IndexOnlyScanState::ioss_ScanDesc, IndexOnlyScanState::ioss_VMBuffer, NoLock, ScanState::ps, PlanState::ps_ResultTupleSlot, ReleaseBuffer(), IndexOnlyScanState::ss, ScanState::ss_currentRelation, and ScanState::ss_ScanTupleSlot.
Referenced by ExecEndNode().
{ Relation indexRelationDesc; IndexScanDesc indexScanDesc; Relation relation; /* * extract information from the node */ indexRelationDesc = node->ioss_RelationDesc; indexScanDesc = node->ioss_ScanDesc; relation = node->ss.ss_currentRelation; /* Release VM buffer pin, if any. */ if (node->ioss_VMBuffer != InvalidBuffer) { ReleaseBuffer(node->ioss_VMBuffer); node->ioss_VMBuffer = InvalidBuffer; } /* * Free the exprcontext(s) ... now dead code, see ExecFreeExprContext */ #ifdef NOT_USED ExecFreeExprContext(&node->ss.ps); if (node->ioss_RuntimeContext) FreeExprContext(node->ioss_RuntimeContext, true); #endif /* * clear out tuple table slots */ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecClearTuple(node->ss.ss_ScanTupleSlot); /* * close the index relation (no-op if we didn't open it) */ if (indexScanDesc) index_endscan(indexScanDesc); if (indexRelationDesc) index_close(indexRelationDesc, NoLock); /* * close the heap relation. */ ExecCloseScanRelation(relation); }
void ExecIndexOnlyMarkPos | ( | IndexOnlyScanState * | node | ) |
Definition at line 337 of file nodeIndexonlyscan.c.
References index_markpos(), and IndexOnlyScanState::ioss_ScanDesc.
Referenced by ExecMarkPos().
{ index_markpos(node->ioss_ScanDesc); }
void ExecIndexOnlyRestrPos | ( | IndexOnlyScanState * | node | ) |
Definition at line 347 of file nodeIndexonlyscan.c.
References index_restrpos(), and IndexOnlyScanState::ioss_ScanDesc.
Referenced by ExecRestrPos().
{ index_restrpos(node->ioss_ScanDesc); }
TupleTableSlot* ExecIndexOnlyScan | ( | IndexOnlyScanState * | node | ) |
Definition at line 224 of file nodeIndexonlyscan.c.
References ExecReScan(), ExecScan(), IndexOnlyNext(), IndexOnlyRecheck(), IndexOnlyScanState::ioss_NumRuntimeKeys, and IndexOnlyScanState::ioss_RuntimeKeysReady.
Referenced by ExecProcNode().
{ /* * If we have runtime keys and they've not already been set up, do it now. */ if (node->ioss_NumRuntimeKeys != 0 && !node->ioss_RuntimeKeysReady) ExecReScan((PlanState *) node); return ExecScan(&node->ss, (ExecScanAccessMtd) IndexOnlyNext, (ExecScanRecheckMtd) IndexOnlyRecheck); }
IndexOnlyScanState* ExecInitIndexOnlyScan | ( | IndexOnlyScan * | node, | |
EState * | estate, | |||
int | eflags | |||
) |
Definition at line 364 of file nodeIndexonlyscan.c.
References AccessShareLock, EState::es_snapshot, EXEC_FLAG_EXPLAIN_ONLY, ExecAssignExprContext(), ExecAssignResultTypeFromTL(), ExecAssignScanProjectionInfo(), ExecAssignScanType(), ExecIndexBuildScanKeys(), ExecInitExpr(), ExecInitResultTupleSlot(), ExecInitScanTupleSlot(), ExecOpenScanRelation(), ExecRelationIsTargetRelation(), ExecTypeFromTL(), index_beginscan(), index_open(), index_rescan(), IndexOnlyScan::indexid, IndexOnlyScan::indexorderby, IndexOnlyScan::indexqual, IndexOnlyScanState::indexqual, IndexOnlyScan::indextlist, IndexOnlyScanState::ioss_HeapFetches, IndexOnlyScanState::ioss_NumRuntimeKeys, IndexOnlyScanState::ioss_RelationDesc, IndexOnlyScanState::ioss_RuntimeKeys, IndexOnlyScanState::ioss_RuntimeKeysReady, makeNode, NoLock, NULL, Scan::plan, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_TupFromTlist, Plan::qual, PlanState::qual, IndexOnlyScan::scan, Scan::scanrelid, IndexOnlyScanState::ss, ScanState::ss_currentRelation, ScanState::ss_currentScanDesc, PlanState::state, Plan::targetlist, and PlanState::targetlist.
Referenced by ExecInitNode().
{ IndexOnlyScanState *indexstate; Relation currentRelation; bool relistarget; TupleDesc tupDesc; /* * create state structure */ indexstate = makeNode(IndexOnlyScanState); indexstate->ss.ps.plan = (Plan *) node; indexstate->ss.ps.state = estate; indexstate->ioss_HeapFetches = 0; /* * Miscellaneous initialization * * create expression context for node */ ExecAssignExprContext(estate, &indexstate->ss.ps); indexstate->ss.ps.ps_TupFromTlist = false; /* * initialize child expressions * * Note: we don't initialize all of the indexorderby expression, only the * sub-parts corresponding to runtime keys (see below). */ indexstate->ss.ps.targetlist = (List *) ExecInitExpr((Expr *) node->scan.plan.targetlist, (PlanState *) indexstate); indexstate->ss.ps.qual = (List *) ExecInitExpr((Expr *) node->scan.plan.qual, (PlanState *) indexstate); indexstate->indexqual = (List *) ExecInitExpr((Expr *) node->indexqual, (PlanState *) indexstate); /* * tuple table initialization */ ExecInitResultTupleSlot(estate, &indexstate->ss.ps); ExecInitScanTupleSlot(estate, &indexstate->ss); /* * open the base relation and acquire appropriate lock on it. */ currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags); indexstate->ss.ss_currentRelation = currentRelation; indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */ /* * Build the scan tuple type using the indextlist generated by the * planner. We use this, rather than the index's physical tuple * descriptor, because the latter contains storage column types not the * types of the original datums. (It's the AM's responsibility to return * suitable data anyway.) */ tupDesc = ExecTypeFromTL(node->indextlist, false); ExecAssignScanType(&indexstate->ss, tupDesc); /* * Initialize result tuple type and projection info. */ ExecAssignResultTypeFromTL(&indexstate->ss.ps); ExecAssignScanProjectionInfo(&indexstate->ss); /* * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop * here. This allows an index-advisor plugin to EXPLAIN a plan containing * references to nonexistent indexes. */ if (eflags & EXEC_FLAG_EXPLAIN_ONLY) return indexstate; /* * Open the index relation. * * If the parent table is one of the target relations of the query, then * InitPlan already opened and write-locked the index, so we can avoid * taking another lock here. Otherwise we need a normal reader's lock. */ relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid); indexstate->ioss_RelationDesc = index_open(node->indexid, relistarget ? NoLock : AccessShareLock); /* * Initialize index-specific scan state */ indexstate->ioss_RuntimeKeysReady = false; indexstate->ioss_RuntimeKeys = NULL; indexstate->ioss_NumRuntimeKeys = 0; /* * build the index scan keys from the index qualification */ ExecIndexBuildScanKeys((PlanState *) indexstate, indexstate->ioss_RelationDesc, node->indexqual, false, &indexstate->ioss_ScanKeys, &indexstate->ioss_NumScanKeys, &indexstate->ioss_RuntimeKeys, &indexstate->ioss_NumRuntimeKeys, NULL, /* no ArrayKeys */ NULL); /* * any ORDER BY exprs have to be turned into scankeys in the same way */ ExecIndexBuildScanKeys((PlanState *) indexstate, indexstate->ioss_RelationDesc, node->indexorderby, true, &indexstate->ioss_OrderByKeys, &indexstate->ioss_NumOrderByKeys, &indexstate->ioss_RuntimeKeys, &indexstate->ioss_NumRuntimeKeys, NULL, /* no ArrayKeys */ NULL); /* * If we have runtime keys, we need an ExprContext to evaluate them. The * node's standard context won't do because we want to reset that context * for every tuple. So, build another context just like the other one... * -tgl 7/11/00 */ if (indexstate->ioss_NumRuntimeKeys != 0) { ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext; ExecAssignExprContext(estate, &indexstate->ss.ps); indexstate->ioss_RuntimeContext = indexstate->ss.ps.ps_ExprContext; indexstate->ss.ps.ps_ExprContext = stdecontext; } else { indexstate->ioss_RuntimeContext = NULL; } /* * Initialize scan descriptor. */ indexstate->ioss_ScanDesc = index_beginscan(currentRelation, indexstate->ioss_RelationDesc, estate->es_snapshot, indexstate->ioss_NumScanKeys, indexstate->ioss_NumOrderByKeys); /* Set it up for index-only scan */ indexstate->ioss_ScanDesc->xs_want_itup = true; indexstate->ioss_VMBuffer = InvalidBuffer; /* * If no run-time keys to calculate, go ahead and pass the scankeys to the * index AM. */ if (indexstate->ioss_NumRuntimeKeys == 0) index_rescan(indexstate->ioss_ScanDesc, indexstate->ioss_ScanKeys, indexstate->ioss_NumScanKeys, indexstate->ioss_OrderByKeys, indexstate->ioss_NumOrderByKeys); /* * all done. */ return indexstate; }
void ExecReScanIndexOnlyScan | ( | IndexOnlyScanState * | node | ) |
Definition at line 249 of file nodeIndexonlyscan.c.
References ExecIndexEvalRuntimeKeys(), ExecScanReScan(), index_rescan(), IndexOnlyScanState::ioss_NumOrderByKeys, IndexOnlyScanState::ioss_NumRuntimeKeys, IndexOnlyScanState::ioss_NumScanKeys, IndexOnlyScanState::ioss_OrderByKeys, IndexOnlyScanState::ioss_RuntimeContext, IndexOnlyScanState::ioss_RuntimeKeys, IndexOnlyScanState::ioss_RuntimeKeysReady, IndexOnlyScanState::ioss_ScanDesc, IndexOnlyScanState::ioss_ScanKeys, ResetExprContext, and IndexOnlyScanState::ss.
Referenced by ExecReScan().
{ /* * If we are doing runtime key calculations (ie, any of the index key * values weren't simple Consts), compute the new key values. But first, * reset the context so we don't leak memory as each outer tuple is * scanned. Note this assumes that we will recalculate *all* runtime keys * on each call. */ if (node->ioss_NumRuntimeKeys != 0) { ExprContext *econtext = node->ioss_RuntimeContext; ResetExprContext(econtext); ExecIndexEvalRuntimeKeys(econtext, node->ioss_RuntimeKeys, node->ioss_NumRuntimeKeys); } node->ioss_RuntimeKeysReady = true; /* reset index scan */ index_rescan(node->ioss_ScanDesc, node->ioss_ScanKeys, node->ioss_NumScanKeys, node->ioss_OrderByKeys, node->ioss_NumOrderByKeys); ExecScanReScan(&node->ss); }
static TupleTableSlot * IndexOnlyNext | ( | IndexOnlyScanState * | node | ) | [static] |
Definition at line 50 of file nodeIndexonlyscan.c.
References ExprContext::ecxt_scantuple, elog, ERROR, EState::es_direction, EState::es_snapshot, ExecClearTuple(), ExecQual(), IndexScanDescData::heapRelation, index_fetch_heap(), index_getnext_tid(), IndexOnlyScanState::indexqual, InstrCountFiltered2, IndexOnlyScanState::ioss_HeapFetches, IndexOnlyScanState::ioss_ScanDesc, IndexOnlyScanState::ioss_VMBuffer, ItemPointerGetBlockNumber, NULL, PlanState::plan, PredicateLockPage(), ScanState::ps, PlanState::ps_ExprContext, ResetExprContext, ScanDirectionIsBackward, ScanDirectionIsForward, IndexOnlyScanState::ss, ScanState::ss_ScanTupleSlot, PlanState::state, StoreIndexTuple(), visibilitymap_test(), IndexScanDescData::xs_continue_hot, IndexScanDescData::xs_itup, IndexScanDescData::xs_itupdesc, and IndexScanDescData::xs_recheck.
Referenced by ExecIndexOnlyScan().
{ EState *estate; ExprContext *econtext; ScanDirection direction; IndexScanDesc scandesc; TupleTableSlot *slot; ItemPointer tid; /* * extract necessary information from index scan node */ estate = node->ss.ps.state; direction = estate->es_direction; /* flip direction if this is an overall backward scan */ if (ScanDirectionIsBackward(((IndexOnlyScan *) node->ss.ps.plan)->indexorderdir)) { if (ScanDirectionIsForward(direction)) direction = BackwardScanDirection; else if (ScanDirectionIsBackward(direction)) direction = ForwardScanDirection; } scandesc = node->ioss_ScanDesc; econtext = node->ss.ps.ps_ExprContext; slot = node->ss.ss_ScanTupleSlot; /* * OK, now that we have what we need, fetch the next tuple. */ while ((tid = index_getnext_tid(scandesc, direction)) != NULL) { HeapTuple tuple = NULL; /* * We can skip the heap fetch if the TID references a heap page on * which all tuples are known visible to everybody. In any case, * we'll use the index tuple not the heap tuple as the data source. * * Note on Memory Ordering Effects: visibilitymap_test does not lock * the visibility map buffer, and therefore the result we read here * could be slightly stale. However, it can't be stale enough to * matter. It suffices to show that (1) there is a read barrier * between the time we read the index TID and the time we test the * visibility map; and (2) there is a write barrier between the time * some other concurrent process clears the visibility map bit and the * time it inserts the index TID. Since acquiring or releasing a * LWLock interposes a full barrier, this is easy to show: (1) is * satisfied by the release of the index buffer content lock after * reading the TID; and (2) is satisfied by the acquisition of the * buffer content lock in order to insert the TID. */ if (!visibilitymap_test(scandesc->heapRelation, ItemPointerGetBlockNumber(tid), &node->ioss_VMBuffer)) { /* * Rats, we have to visit the heap to check visibility. */ node->ioss_HeapFetches++; tuple = index_fetch_heap(scandesc); if (tuple == NULL) continue; /* no visible tuple, try next index entry */ /* * Only MVCC snapshots are supported here, so there should be no * need to keep following the HOT chain once a visible entry has * been found. If we did want to allow that, we'd need to keep * more state to remember not to call index_getnext_tid next time. */ if (scandesc->xs_continue_hot) elog(ERROR, "non-MVCC snapshots are not supported in index-only scans"); /* * Note: at this point we are holding a pin on the heap page, as * recorded in scandesc->xs_cbuf. We could release that pin now, * but it's not clear whether it's a win to do so. The next index * entry might require a visit to the same heap page. */ } /* * Fill the scan tuple slot with data from the index. */ StoreIndexTuple(slot, scandesc->xs_itup, scandesc->xs_itupdesc); /* * If the index was lossy, we have to recheck the index quals. * (Currently, this can never happen, but we should support the case * for possible future use, eg with GiST indexes.) */ if (scandesc->xs_recheck) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); if (!ExecQual(node->indexqual, econtext, false)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); continue; } } /* * Predicate locks for index-only scans must be acquired at the page * level when the heap is not accessed, since tuple-level predicate * locks need the tuple's xmin value. If we had to visit the tuple * anyway, then we already have the tuple-level lock and can skip the * page lock. */ if (tuple == NULL) PredicateLockPage(scandesc->heapRelation, ItemPointerGetBlockNumber(tid), estate->es_snapshot); return slot; } /* * if we get here it means the index scan failed so we are at the end of * the scan.. */ return ExecClearTuple(slot); }
static bool IndexOnlyRecheck | ( | IndexOnlyScanState * | node, | |
TupleTableSlot * | slot | |||
) | [static] |
Definition at line 213 of file nodeIndexonlyscan.c.
Referenced by ExecIndexOnlyScan().
static void StoreIndexTuple | ( | TupleTableSlot * | slot, | |
IndexTuple | itup, | |||
TupleDesc | itupdesc | |||
) | [static] |
Definition at line 182 of file nodeIndexonlyscan.c.
References Assert, ExecClearTuple(), ExecStoreVirtualTuple(), i, index_getattr, tupleDesc::natts, TupleTableSlot::tts_isnull, TupleTableSlot::tts_tupleDescriptor, TupleTableSlot::tts_values, and values.
Referenced by IndexOnlyNext().
{ int nindexatts = itupdesc->natts; Datum *values = slot->tts_values; bool *isnull = slot->tts_isnull; int i; /* * Note: we must use the tupdesc supplied by the AM in index_getattr, not * the slot's tupdesc, in case the latter has different datatypes (this * happens for btree name_ops in particular). They'd better have the same * number of columns though, as well as being datatype-compatible which is * something we can't so easily check. */ Assert(slot->tts_tupleDescriptor->natts == nindexatts); ExecClearTuple(slot); for (i = 0; i < nindexatts; i++) values[i] = index_getattr(itup, i + 1, itupdesc, &isnull[i]); ExecStoreVirtualTuple(slot); }