📄 nodeindexscan.c
字号:
/*------------------------------------------------------------------------- * * nodeIndexscan.c * Routines to support indexes and indexed scans of relations * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.84.2.1 2003/12/30 20:05:15 tgl Exp $ * *------------------------------------------------------------------------- *//* * INTERFACE ROUTINES * ExecIndexScan scans a relation using indices * ExecIndexNext using index to retrieve next tuple * ExecInitIndexScan creates and initializes state info. * ExecIndexReScan rescans the indexed relation. * ExecEndIndexScan releases all storage. * ExecIndexMarkPos marks scan position. * ExecIndexRestrPos restores scan position. */#include "postgres.h"#include "access/genam.h"#include "access/heapam.h"#include "executor/execdebug.h"#include "executor/nodeIndexscan.h"#include "miscadmin.h"#include "nodes/nodeFuncs.h"#include "optimizer/clauses.h"#include "parser/parsetree.h"#define NO_OP 0#define LEFT_OP 1#define RIGHT_OP 2/* * In a multiple-index plan, we must take care to return any given tuple * only once, even if it matches conditions of several index scans. Our * preferred way to do this is to record already-returned tuples in a hash * table (using the TID as unique identifier). However, in a very large * scan this could conceivably run out of memory. We limit the hash table * to no more than SortMem KB; if it grows past that, we fall back to the * pre-7.4 technique: evaluate the prior-scan index quals again for each * tuple (which is space-efficient, but slow). * * When scanning backwards, we use scannum to determine when to emit the * tuple --- we have to re-emit a tuple in the same scan as it was first * encountered. * * Note: this code would break if the planner were ever to create a multiple * index plan with overall backwards direction, because the hashtable code * will emit a tuple the first time it is encountered (which would be the * highest scan in which it matches the index), but the evaluate-the-quals * code will emit a tuple in the lowest-numbered scan in which it's valid. * This could be fixed at need by making the evaluate-the-quals case more * complex. Currently the planner will never create such a plan (since it * considers multi-index plans unordered anyway), so there's no need for * more complexity. */typedef struct{ /* tid is the hash key and so must be first! */ ItemPointerData tid; /* TID of a tuple we've returned */ int scannum; /* number of scan we returned it in */} DupHashTabEntry;static TupleTableSlot *IndexNext(IndexScanState *node);static void create_duphash(IndexScanState *node);/* ---------------------------------------------------------------- * IndexNext * * Retrieve a tuple from the IndexScan node's currentRelation * using the indices in the IndexScanState information. * * note: the old code mentions 'Primary indices'. to my knowledge * we only support a single secondary index. -cim 9/11/89 * * old comments: * retrieve a tuple from relation using the indices given. * The indices are used in the order they appear in 'indices'. * The indices may be primary or secondary indices: * * primary index -- scan the relation 'relID' using keys supplied. * * secondary index -- scan the index relation to get the 'tid' for * a tuple in the relation 'relID'. * If the current index(pointed by 'indexPtr') fails to return a * tuple, the next index in the indices is used. * * bug fix so that it should retrieve on a null scan key. * ---------------------------------------------------------------- */static TupleTableSlot *IndexNext(IndexScanState *node){ EState *estate; ExprContext *econtext; ScanDirection direction; IndexScanDescPtr scanDescs; IndexScanDesc scandesc; Index scanrelid; HeapTuple tuple; TupleTableSlot *slot; int numIndices; bool bBackward; int indexNumber; /* * extract necessary information from index scan node */ estate = node->ss.ps.state; direction = estate->es_direction; if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indxorderdir)) { if (ScanDirectionIsForward(direction)) direction = BackwardScanDirection; else if (ScanDirectionIsBackward(direction)) direction = ForwardScanDirection; } scanDescs = node->iss_ScanDescs; numIndices = node->iss_NumIndices; econtext = node->ss.ps.ps_ExprContext; slot = node->ss.ss_ScanTupleSlot; scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid; /* * Check if we are evaluating PlanQual for tuple of this relation. * Additional checking is not good, but no other way for now. We could * introduce new nodes for this case and handle IndexScan --> NewNode * switching in Init/ReScan plan... */ if (estate->es_evTuple != NULL && estate->es_evTuple[scanrelid - 1] != NULL) { List *qual; ExecClearTuple(slot); if (estate->es_evTupleNull[scanrelid - 1]) return slot; /* return empty slot */ ExecStoreTuple(estate->es_evTuple[scanrelid - 1], slot, InvalidBuffer, false); /* Does the tuple meet any of the OR'd indxqual conditions? */ econtext->ecxt_scantuple = slot; ResetExprContext(econtext); foreach(qual, node->indxqualorig) { if (ExecQual((List *) lfirst(qual), econtext, false)) break; } if (qual == NIL) /* would not be returned by indices */ slot->val = NULL; /* Flag for the next call that no more tuples */ estate->es_evTupleNull[scanrelid - 1] = true; return slot; } /* * ok, now that we have what we need, fetch an index tuple. if * scanning this index succeeded then return the appropriate heap * tuple.. else return NULL. */ bBackward = ScanDirectionIsBackward(direction); if (bBackward) { indexNumber = numIndices - node->iss_IndexPtr - 1; if (indexNumber < 0) { indexNumber = 0; node->iss_IndexPtr = numIndices - 1; } } else { if ((indexNumber = node->iss_IndexPtr) < 0) { indexNumber = 0; node->iss_IndexPtr = 0; } } while (indexNumber < numIndices) { scandesc = scanDescs[node->iss_IndexPtr]; while ((tuple = index_getnext(scandesc, direction)) != NULL) { /* * Store the scanned tuple in the scan tuple slot of the scan * state. Note: we pass 'false' because tuples returned by * amgetnext are pointers onto disk pages and must not be * pfree()'d. */ ExecStoreTuple(tuple, /* tuple to store */ slot, /* slot to store in */ scandesc->xs_cbuf, /* buffer containing tuple */ false); /* don't pfree */ /* * If it's a multiple-index scan, make sure not to double-report * a tuple matched by more than one index. (See notes above.) */ if (numIndices > 1) { /* First try the hash table */ if (node->iss_DupHash) { DupHashTabEntry *entry; bool found; entry = (DupHashTabEntry *) hash_search(node->iss_DupHash, &tuple->t_data->t_ctid, HASH_ENTER, &found); if (entry == NULL || node->iss_DupHash->hctl->nentries > node->iss_MaxHash) { /* out of memory (either hard or soft limit) */ /* release hash table and fall thru to old code */ hash_destroy(node->iss_DupHash); node->iss_DupHash = NULL; } else if (found) { /* pre-existing entry */ /* * It's duplicate if first emitted in a different * scan. If same scan, we must be backing up, so * okay to emit again. */ if (entry->scannum != node->iss_IndexPtr) { /* Dup, so drop it and loop back for another */ ExecClearTuple(slot); continue; } } else { /* new entry, finish filling it in */ entry->scannum = node->iss_IndexPtr; } } /* If hash table has overflowed, do it the hard way */ if (node->iss_DupHash == NULL && node->iss_IndexPtr > 0) { bool prev_matches = false; int prev_index; List *qual; econtext->ecxt_scantuple = slot; ResetExprContext(econtext); qual = node->indxqualorig; for (prev_index = 0; prev_index < node->iss_IndexPtr; prev_index++) { if (ExecQual((List *) lfirst(qual), econtext, false)) { prev_matches = true; break; } qual = lnext(qual); } if (prev_matches) { /* Dup, so drop it and loop back for another */ ExecClearTuple(slot); continue; } } } return slot; /* OK to return tuple */ } if (indexNumber < numIndices) { indexNumber++; if (bBackward) node->iss_IndexPtr--; else node->iss_IndexPtr++; } } /* * if we get here it means the index scan failed so we are at the end * of the scan.. */ return ExecClearTuple(slot);}/* ---------------------------------------------------------------- * ExecIndexScan(node) * * old comments: * Scans the relation using primary or secondary indices and returns * the next qualifying tuple in the direction specified. * It calls ExecScan() and passes it the access methods which returns * the next tuple using the indices. * * Conditions: * -- the "cursor" maintained by the AMI is positioned at the tuple * returned previously. * * Initial States: * -- the relation indicated is opened for scanning so that the * "cursor" is positioned before the first qualifying tuple. * -- all index realtions are opened for scanning. * -- indexPtr points to the first index. * -- state variable ruleFlag = nil. * ---------------------------------------------------------------- */TupleTableSlot *ExecIndexScan(IndexScanState *node){ /* * If we have runtime keys and they've not already been set up, do it * now. */ if (node->iss_RuntimeKeyInfo && !node->iss_RuntimeKeysReady) ExecReScan((PlanState *) node, NULL); /* * use IndexNext as access method */ return ExecScan(&node->ss, (ExecScanAccessMtd) IndexNext);}/* ---------------------------------------------------------------- * ExecIndexReScan(node) * * Recalculates the value of the scan keys whose value depends on * information known at runtime and rescans the indexed relation. * Updating the scan key was formerly done separately in * ExecUpdateIndexScanKeys. Integrating it into ReScan makes * rescans of indices and relations/general streams more uniform. * * ---------------------------------------------------------------- */voidExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt){ EState *estate; ExprContext *econtext; int numIndices; IndexScanDescPtr scanDescs; ScanKey *scanKeys; ExprState ***runtimeKeyInfo; int *numScanKeys; Index scanrelid; int i; int j; estate = node->ss.ps.state; econtext = node->iss_RuntimeContext; /* context for runtime * keys */ numIndices = node->iss_NumIndices; scanDescs = node->iss_ScanDescs; scanKeys = node->iss_ScanKeys; runtimeKeyInfo = node->iss_RuntimeKeyInfo; numScanKeys = node->iss_NumScanKeys; scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid; if (econtext) { /* * If we are being passed an outer tuple, save it for runtime key * calc. We also need to link it into the "regular" per-tuple * econtext, so it can be used during indexqualorig evaluations. */ if (exprCtxt != NULL) { ExprContext *stdecontext; econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple; stdecontext = node->ss.ps.ps_ExprContext; stdecontext->ecxt_outertuple = exprCtxt->ecxt_outertuple; } /* * Reset the runtime-key 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. */ ResetExprContext(econtext); } /* * If we are doing runtime key calculations (ie, the index keys depend * on data from an outer scan), compute the new key values */ if (runtimeKeyInfo) { for (i = 0; i < numIndices; i++) { int n_keys; ScanKey scan_keys; ExprState **run_keys; n_keys = numScanKeys[i]; scan_keys = scanKeys[i]; run_keys = runtimeKeyInfo[i]; for (j = 0; j < n_keys; j++) { /* * If we have a run-time key, then extract the run-time * expression and evaluate it with respect to the current * outer tuple. We then stick the result into the scan * key. * * Note: the result of the eval could be a pass-by-ref value * that's stored in the outer scan's tuple, not in * econtext->ecxt_per_tuple_memory. We assume that the * outer tuple will stay put throughout our scan. If this * is wrong, we could copy the result into our context * explicitly, but I think that's not necessary... */ if (run_keys[j] != NULL) { Datum scanvalue; bool isNull; scanvalue = ExecEvalExprSwitchContext(run_keys[j], econtext, &isNull, NULL); scan_keys[j].sk_argument = scanvalue; if (isNull) scan_keys[j].sk_flags |= SK_ISNULL; else scan_keys[j].sk_flags &= ~SK_ISNULL; } } } node->iss_RuntimeKeysReady = true; } /* If this is re-scanning of PlanQual ... */ if (estate->es_evTuple != NULL && estate->es_evTuple[scanrelid - 1] != NULL) { estate->es_evTupleNull[scanrelid - 1] = false; return; } /* reset hash table */ if (numIndices > 1) { if (node->iss_DupHash) hash_destroy(node->iss_DupHash); create_duphash(node); } /* reset index scans */ if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indxorderdir)) node->iss_IndexPtr = numIndices; else node->iss_IndexPtr = -1; for (i = 0; i < numIndices; i++) { IndexScanDesc scan = scanDescs[i]; ScanKey skey = scanKeys[i]; index_rescan(scan, skey); }}/* ---------------------------------------------------------------- * ExecEndIndexScan * ---------------------------------------------------------------- */voidExecEndIndexScan(IndexScanState *node){ int numIndices; RelationPtr indexRelationDescs; IndexScanDescPtr indexScanDescs; Relation relation; int i; /* * extract information from the node */ numIndices = node->iss_NumIndices; indexRelationDescs = node->iss_RelationDescs; indexScanDescs = node->iss_ScanDescs; relation = node->ss.ss_currentRelation; /* * Free the exprcontext(s) */ ExecFreeExprContext(&node->ss.ps); if (node->iss_RuntimeContext) FreeExprContext(node->iss_RuntimeContext); /* * clear out tuple table slots */ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecClearTuple(node->ss.ss_ScanTupleSlot); /* drop hash table */ if (node->iss_DupHash) hash_destroy(node->iss_DupHash); /* * close the index relations */ for (i = 0; i < numIndices; i++)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -