diff options
Diffstat (limited to 'src/backend/executor/nodeIndexscan.c')
-rw-r--r-- | src/backend/executor/nodeIndexscan.c | 930 |
1 files changed, 275 insertions, 655 deletions
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 27cc29a62b..7136ec903c 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------- * * nodeIndexscan.c - * Routines to support indexes and indexed scans of relations + * Routines to support indexed scans of relations * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.101 2005/04/23 21:32:34 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.102 2005/04/25 01:30:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -34,62 +34,14 @@ #include "parser/parsetree.h" -/* - * 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 work_mem 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. + * using the index specified in the IndexScanState information. * ---------------------------------------------------------------- */ static TupleTableSlot * @@ -98,32 +50,25 @@ IndexNext(IndexScanState *node) EState *estate; ExprContext *econtext; ScanDirection direction; - IndexScanDescPtr scanDescs; - List **lossyQuals; IndexScanDesc scandesc; - List *lossyQual; 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)) + /* flip direction if this is an overall backward scan */ + if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indexorderdir)) { if (ScanDirectionIsForward(direction)) direction = BackwardScanDirection; else if (ScanDirectionIsBackward(direction)) direction = ForwardScanDirection; } - scanDescs = node->iss_ScanDescs; - lossyQuals = node->iss_LossyQuals; - numIndices = node->iss_NumIndices; + scandesc = node->iss_ScanDesc; econtext = node->ss.ps.ps_ExprContext; slot = node->ss.ss_ScanTupleSlot; scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid; @@ -146,26 +91,19 @@ IndexNext(IndexScanState *node) if (estate->es_evTuple != NULL && estate->es_evTuple[scanrelid - 1] != NULL) { - ListCell *qual; - 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? */ + /* Does the tuple meet the indexqual condition? */ econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - foreach(qual, node->indxqualorig) - { - if (ExecQual((List *) lfirst(qual), econtext, false)) - break; - } - if (qual == NULL) /* would not be returned by indices */ - ExecClearTuple(slot); + if (!ExecQual(node->indexqualorig, econtext, false)) + ExecClearTuple(slot); /* would not be returned by scan */ /* Flag for the next call that no more tuples */ estate->es_evTupleNull[scanrelid - 1] = true; @@ -174,156 +112,22 @@ IndexNext(IndexScanState *node) } /* - * 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. + * ok, now that we have what we need, fetch the next tuple. */ - bBackward = ScanDirectionIsBackward(direction); - if (bBackward) - { - indexNumber = numIndices - node->iss_IndexPtr - 1; - if (indexNumber < 0) - { - indexNumber = 0; - node->iss_IndexPtr = numIndices - 1; - } - } - else + if ((tuple = index_getnext(scandesc, direction)) != NULL) { - if ((indexNumber = node->iss_IndexPtr) < 0) - { - indexNumber = 0; - node->iss_IndexPtr = 0; - } - } - while (indexNumber < numIndices) - { - scandesc = scanDescs[node->iss_IndexPtr]; - lossyQual = lossyQuals[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 any of the index operators involved in this scan are - * lossy, recheck them by evaluating the original operator - * clauses. - */ - if (lossyQual) - { - econtext->ecxt_scantuple = slot; - ResetExprContext(econtext); - if (!ExecQual(lossyQual, econtext, false)) - { - /* - * Fails lossy op, so drop it and loop back for - * another - */ - ExecClearTuple(slot); - continue; - } - } - - /* - * 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; - ListCell *qual; - - econtext->ecxt_scantuple = slot; - ResetExprContext(econtext); - qual = list_head(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 */ - } + /* + * 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 (indexNumber < numIndices) - { - indexNumber++; - if (bBackward) - node->iss_IndexPtr--; - else - node->iss_IndexPtr++; - } + return slot; } /* @@ -335,23 +139,6 @@ IndexNext(IndexScanState *node) /* ---------------------------------------------------------------- * 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 * @@ -378,7 +165,6 @@ ExecIndexScan(IndexScanState *node) * Updating the scan key was formerly done separately in * ExecUpdateIndexScanKeys. Integrating it into ReScan makes * rescans of indices and relations/general streams more uniform. - * * ---------------------------------------------------------------- */ void @@ -386,20 +172,14 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt) { EState *estate; ExprContext *econtext; - int numIndices; - IndexScanDescPtr scanDescs; - ScanKey *scanKeys; - ExprState ***runtimeKeyInfo; - int *numScanKeys; + 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; @@ -435,49 +215,10 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt) */ 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; - } - } - } - + ExecIndexEvalRuntimeKeys(econtext, + runtimeKeyInfo, + scanKeys, + numScanKeys); node->iss_RuntimeKeysReady = true; } @@ -489,26 +230,52 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt) return; } - /* reset hash table */ - if (numIndices > 1) - { - if (node->iss_DupHash) - hash_destroy(node->iss_DupHash); - create_duphash(node); - } + /* reset index scan */ + index_rescan(node->iss_ScanDesc, scanKeys); +} - /* 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]; +/* + * ExecIndexEvalRuntimeKeys + * Evaluate any runtime key values, and update the scankeys. + */ +void +ExecIndexEvalRuntimeKeys(ExprContext *econtext, + ExprState **run_keys, + ScanKey scan_keys, + int n_keys) +{ + int j; - index_rescan(scan, skey); + 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; + } } } @@ -519,18 +286,15 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt) void ExecEndIndexScan(IndexScanState *node) { - int numIndices; - RelationPtr indexRelationDescs; - IndexScanDescPtr indexScanDescs; + Relation indexRelationDesc; + IndexScanDesc indexScanDesc; Relation relation; - int i; /* * extract information from the node */ - numIndices = node->iss_NumIndices; - indexRelationDescs = node->iss_RelationDescs; - indexScanDescs = node->iss_ScanDescs; + indexRelationDesc = node->iss_RelationDesc; + indexScanDesc = node->iss_ScanDesc; relation = node->ss.ss_currentRelation; /* @@ -548,21 +312,11 @@ ExecEndIndexScan(IndexScanState *node) 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 + * close the index relation */ - for (i = 0; i < numIndices; i++) - { - if (indexScanDescs[i] != NULL) - index_endscan(indexScanDescs[i]); - - if (indexRelationDescs[i] != NULL) - index_close(indexRelationDescs[i]); - } + index_endscan(indexScanDesc); + index_close(indexRelationDesc); /* * close the heap relation. @@ -577,52 +331,22 @@ ExecEndIndexScan(IndexScanState *node) /* ---------------------------------------------------------------- * ExecIndexMarkPos - * - * old comments - * Marks scan position by marking the current index. - * Returns nothing. * ---------------------------------------------------------------- */ void ExecIndexMarkPos(IndexScanState *node) { - IndexScanDescPtr indexScanDescs; - IndexScanDesc scanDesc; - int indexPtr; - - indexPtr = node->iss_MarkIndexPtr = node->iss_IndexPtr; - if (indexPtr >= 0 && indexPtr < node->iss_NumIndices) - { - indexScanDescs = node->iss_ScanDescs; - scanDesc = indexScanDescs[indexPtr]; - - index_markpos(scanDesc); - } + index_markpos(node->iss_ScanDesc); } /* ---------------------------------------------------------------- * ExecIndexRestrPos - * - * old comments - * Restores scan position by restoring the current index. - * Returns nothing. * ---------------------------------------------------------------- */ void ExecIndexRestrPos(IndexScanState *node) { - IndexScanDescPtr indexScanDescs; - IndexScanDesc scanDesc; - int indexPtr; - - indexPtr = node->iss_IndexPtr = node->iss_MarkIndexPtr; - if (indexPtr >= 0 && indexPtr < node->iss_NumIndices) - { - indexScanDescs = node->iss_ScanDescs; - scanDesc = indexScanDescs[indexPtr]; - - index_restrpos(scanDesc); - } + index_restrpos(node->iss_ScanDesc); } /* ---------------------------------------------------------------- @@ -633,35 +357,16 @@ ExecIndexRestrPos(IndexScanState *node) * * Note: index scans have 2 sets of state information because * we have to keep track of the base relation and the - * index relations. - * - * old comments - * Creates the run-time state information for the node and - * sets the relation id to contain relevant descriptors. - * - * Parameters: - * node: IndexNode node produced by the planner. - * estate: the execution state initialized in InitPlan. + * index relation. * ---------------------------------------------------------------- */ IndexScanState * ExecInitIndexScan(IndexScan *node, EState *estate) { IndexScanState *indexstate; - ListCell *indxqual; - ListCell *indxstrategy; - ListCell *indxsubtype; - ListCell *indxlossy; - ListCell *indxid_item; - int i; - int numIndices; - int indexPtr; - ScanKey *scanKeys; - int *numScanKeys; - RelationPtr indexDescs; - IndexScanDescPtr scanDescs; - List **lossyQuals; - ExprState ***runtimeKeyInfo; + ScanKey scanKeys; + int numScanKeys; + ExprState **runtimeKeyInfo; bool have_runtime_keys; RangeTblEntry *rtentry; Index relid; @@ -685,9 +390,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate) /* * initialize child expressions * - * Note: we don't initialize all of the indxqual expression, only the + * Note: we don't initialize all of the indexqual expression, only the * sub-parts corresponding to runtime keys (see below). The - * indxqualorig expression is always initialized even though it will + * indexqualorig expression is always initialized even though it will * only be used in some uncommon cases --- would be nice to improve * that. (Problem is that any SubPlans present in the expression must * be found now...) @@ -698,8 +403,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate) indexstate->ss.ps.qual = (List *) ExecInitExpr((Expr *) node->scan.plan.qual, (PlanState *) indexstate); - indexstate->indxqualorig = (List *) - ExecInitExpr((Expr *) node->indxqualorig, + indexstate->indexqualorig = (List *) + ExecInitExpr((Expr *) node->indexqualorig, (PlanState *) indexstate); #define INDEXSCAN_NSLOTS 2 @@ -713,233 +418,29 @@ ExecInitIndexScan(IndexScan *node, EState *estate) /* * Initialize index-specific scan state */ - indexstate->iss_NumIndices = 0; - indexstate->iss_IndexPtr = -1; - indexstate->iss_ScanKeys = NULL; - indexstate->iss_NumScanKeys = NULL; - indexstate->iss_RuntimeKeyInfo = NULL; - indexstate->iss_RuntimeContext = NULL; indexstate->iss_RuntimeKeysReady = false; - indexstate->iss_RelationDescs = NULL; - indexstate->iss_ScanDescs = NULL; - indexstate->iss_LossyQuals = NULL; - - /* - * get the index node information - */ - indxid_item = list_head(node->indxid); - numIndices = list_length(node->indxid); - indexPtr = -1; CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext); /* - * scanKeys is used to keep track of the ScanKey's. This is needed - * because a single scan may use several indices and each index has - * its own ScanKey. - */ - numScanKeys = (int *) palloc(numIndices * sizeof(int)); - scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey)); - indexDescs = (RelationPtr) palloc(numIndices * sizeof(Relation)); - scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc)); - lossyQuals = (List **) palloc0(numIndices * sizeof(List *)); - - /* - * initialize space for runtime key info (may not be needed) - */ - have_runtime_keys = false; - runtimeKeyInfo = (ExprState ***) palloc0(numIndices * sizeof(ExprState **)); - - /* * build the index scan keys from the index qualification */ - indxqual = list_head(node->indxqual); - indxstrategy = list_head(node->indxstrategy); - indxsubtype = list_head(node->indxsubtype); - indxlossy = list_head(node->indxlossy); - for (i = 0; i < numIndices; i++) - { - List *quals; - List *strategies; - List *subtypes; - List *lossyflags; - ListCell *qual_cell; - ListCell *strategy_cell; - ListCell *subtype_cell; - ListCell *lossyflag_cell; - int n_keys; - ScanKey scan_keys; - ExprState **run_keys; - int j; - - quals = (List *) lfirst(indxqual); - indxqual = lnext(indxqual); - strategies = (List *) lfirst(indxstrategy); - indxstrategy = lnext(indxstrategy); - subtypes = (List *) lfirst(indxsubtype); - indxsubtype = lnext(indxsubtype); - lossyflags = (List *) lfirst(indxlossy); - indxlossy = lnext(indxlossy); - n_keys = list_length(quals); - scan_keys = (n_keys <= 0) ? NULL : - (ScanKey) palloc(n_keys * sizeof(ScanKeyData)); - run_keys = (n_keys <= 0) ? NULL : - (ExprState **) palloc(n_keys * sizeof(ExprState *)); - - /* - * for each opclause in the given qual, convert each qual's - * opclause into a single scan key - */ - qual_cell = list_head(quals); - strategy_cell = list_head(strategies); - subtype_cell = list_head(subtypes); - lossyflag_cell = list_head(lossyflags); - for (j = 0; j < n_keys; j++) - { - OpExpr *clause; /* one clause of index qual */ - Expr *leftop; /* expr on lhs of operator */ - Expr *rightop; /* expr on rhs ... */ - int flags = 0; - AttrNumber varattno; /* att number used in scan */ - StrategyNumber strategy; /* op's strategy number */ - Oid subtype; /* op's strategy subtype */ - int lossy; /* op's recheck flag */ - RegProcedure opfuncid; /* operator proc id used in scan */ - Datum scanvalue; /* value used in scan (if const) */ - - /* - * extract clause information from the qualification - */ - clause = (OpExpr *) lfirst(qual_cell); - qual_cell = lnext(qual_cell); - strategy = lfirst_int(strategy_cell); - strategy_cell = lnext(strategy_cell); - subtype = lfirst_oid(subtype_cell); - subtype_cell = lnext(subtype_cell); - lossy = lfirst_int(lossyflag_cell); - lossyflag_cell = lnext(lossyflag_cell); - - if (!IsA(clause, OpExpr)) - elog(ERROR, "indxqual is not an OpExpr"); - - opfuncid = clause->opfuncid; - - /* - * Here we figure out the contents of the index qual. The - * usual case is (var op const) which means we form a scan key - * for the attribute listed in the var node and use the value - * of the const as comparison data. - * - * If we don't have a const node, it means our scan key is a - * function of information obtained during the execution of - * the plan, in which case we need to recalculate the index - * scan key at run time. Hence, we set have_runtime_keys to - * true and place the appropriate subexpression in run_keys. - * The corresponding scan key values are recomputed at run - * time. - */ - run_keys[j] = NULL; - - /* - * determine information in leftop - */ - leftop = (Expr *) get_leftop((Expr *) clause); - - if (leftop && IsA(leftop, RelabelType)) - leftop = ((RelabelType *) leftop)->arg; - - Assert(leftop != NULL); - - if (!(IsA(leftop, Var) && - var_is_rel((Var *) leftop))) - elog(ERROR, "indxqual doesn't have key on left side"); - - varattno = ((Var *) leftop)->varattno; - - /* - * now determine information in rightop - */ - rightop = (Expr *) get_rightop((Expr *) clause); - - if (rightop && IsA(rightop, RelabelType)) - rightop = ((RelabelType *) rightop)->arg; - - Assert(rightop != NULL); - - if (IsA(rightop, Const)) - { - /* - * if the rightop is a const node then it means it - * identifies the value to place in our scan key. - */ - scanvalue = ((Const *) rightop)->constvalue; - if (((Const *) rightop)->constisnull) - flags |= SK_ISNULL; - } - else - { - /* - * otherwise, the rightop contains an expression evaluable - * at runtime to figure out the value to place in our scan - * key. - */ - have_runtime_keys = true; - run_keys[j] = ExecInitExpr(rightop, (PlanState *) indexstate); - scanvalue = (Datum) 0; - } - - /* - * initialize the scan key's fields appropriately - */ - ScanKeyEntryInitialize(&scan_keys[j], - flags, - varattno, /* attribute number to - * scan */ - strategy, /* op's strategy */ - subtype, /* strategy subtype */ - opfuncid, /* reg proc to use */ - scanvalue); /* constant */ - - /* - * If this operator is lossy, add its indxqualorig expression - * to the list of quals to recheck. The list_nth() calls here - * could be avoided by chasing the lists in parallel to all - * the other lists, but since lossy operators are very - * uncommon, it's probably a waste of time to do so. - */ - if (lossy) - { - List *qualOrig = indexstate->indxqualorig; - - lossyQuals[i] = lappend(lossyQuals[i], - list_nth((List *) list_nth(qualOrig, i), j)); - } - } - - /* - * store the key information into our arrays. - */ - numScanKeys[i] = n_keys; - scanKeys[i] = scan_keys; - runtimeKeyInfo[i] = run_keys; - } - - indexstate->iss_NumIndices = numIndices; - if (ScanDirectionIsBackward(node->indxorderdir)) - indexPtr = numIndices; - indexstate->iss_IndexPtr = indexPtr; + have_runtime_keys = + ExecIndexBuildScanKeys((PlanState *) indexstate, + node->indexqual, + node->indexstrategy, + node->indexsubtype, + &runtimeKeyInfo, + &scanKeys, + &numScanKeys); + + indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo; indexstate->iss_ScanKeys = scanKeys; indexstate->iss_NumScanKeys = numScanKeys; /* - * If all of our keys have the form (var op const), then we have no - * runtime keys so we store NULL in the runtime key info. Otherwise - * runtime key info contains an array of pointers (one for each index) - * to arrays of flags (one for each key) which indicate that the qual - * needs to be evaluated at runtime. -cim 10/24/89 - * - * If we do have runtime keys, we need an ExprContext to evaluate them; - * the node's standard context won't do because we want to reset that + * 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 */ @@ -948,21 +449,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext; ExecAssignExprContext(estate, &indexstate->ss.ps); - indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo; indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext; indexstate->ss.ps.ps_ExprContext = stdecontext; } else { - indexstate->iss_RuntimeKeyInfo = NULL; indexstate->iss_RuntimeContext = NULL; - /* Get rid of the speculatively-allocated flag arrays, too */ - for (i = 0; i < numIndices; i++) - { - if (runtimeKeyInfo[i] != NULL) - pfree(runtimeKeyInfo[i]); - } - pfree(runtimeKeyInfo); } /* @@ -983,27 +475,17 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation), false); /* - * open the index relations and initialize relation and scan + * open the index relation and initialize relation and scan * descriptors. Note we acquire no locks here; the index machinery * does its own locks and unlocks. (We rely on having AccessShareLock * on the parent table to ensure the index won't go away!) */ - for (i = 0; i < numIndices; i++) - { - Oid indexOid = lfirst_oid(indxid_item); - - indexDescs[i] = index_open(indexOid); - scanDescs[i] = index_beginscan(currentRelation, - indexDescs[i], - estate->es_snapshot, - numScanKeys[i], - scanKeys[i]); - indxid_item = lnext(indxid_item); - } - - indexstate->iss_RelationDescs = indexDescs; - indexstate->iss_ScanDescs = scanDescs; - indexstate->iss_LossyQuals = lossyQuals; + indexstate->iss_RelationDesc = index_open(node->indexid); + indexstate->iss_ScanDesc = index_beginscan(currentRelation, + indexstate->iss_RelationDesc, + estate->es_snapshot, + numScanKeys, + scanKeys); /* * Initialize result tuple type and projection info. @@ -1012,41 +494,179 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ExecAssignScanProjectionInfo(&indexstate->ss); /* - * Initialize hash table if needed. - */ - if (numIndices > 1) - create_duphash(indexstate); - else - indexstate->iss_DupHash = NULL; - - /* * all done. */ return indexstate; } -static void -create_duphash(IndexScanState *node) + +/* + * ExecIndexBuildScanKeys + * Build the index scan keys from the index qualification + * + * Input params are: + * + * planstate: executor state node we are working for + * quals: indexquals expressions + * strategies: associated operator strategy numbers + * subtypes: associated operator subtype OIDs + * + * Output params are: + * + * *runtimeKeyInfo: receives ptr to array of runtime key exprstates + * (NULL if no runtime keys) + * *scanKeys: receives ptr to array of ScanKeys + * *numScanKeys: receives number of scankeys/runtime keys + * + * Return value is TRUE if any runtime key expressions were found, else FALSE. + */ +bool +ExecIndexBuildScanKeys(PlanState *planstate, List *quals, + List *strategies, List *subtypes, + ExprState ***runtimeKeyInfo, + ScanKey *scanKeys, int *numScanKeys) { - HASHCTL hash_ctl; - long nbuckets; - - node->iss_MaxHash = (work_mem * 1024L) / - (MAXALIGN(sizeof(HASHELEMENT)) + MAXALIGN(sizeof(DupHashTabEntry))); - MemSet(&hash_ctl, 0, sizeof(hash_ctl)); - hash_ctl.keysize = SizeOfIptrData; - hash_ctl.entrysize = sizeof(DupHashTabEntry); - hash_ctl.hash = tag_hash; - hash_ctl.hcxt = CurrentMemoryContext; - nbuckets = (long) ceil(node->ss.ps.plan->plan_rows); - if (nbuckets < 1) - nbuckets = 1; - if (nbuckets > node->iss_MaxHash) - nbuckets = node->iss_MaxHash; - node->iss_DupHash = hash_create("DupHashTable", - nbuckets, - &hash_ctl, - HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); + bool have_runtime_keys = false; + ListCell *qual_cell; + ListCell *strategy_cell; + ListCell *subtype_cell; + int n_keys; + ScanKey scan_keys; + ExprState **run_keys; + int j; + + n_keys = list_length(quals); + scan_keys = (n_keys <= 0) ? NULL : + (ScanKey) palloc(n_keys * sizeof(ScanKeyData)); + run_keys = (n_keys <= 0) ? NULL : + (ExprState **) palloc(n_keys * sizeof(ExprState *)); + + /* + * for each opclause in the given qual, convert each qual's + * opclause into a single scan key + */ + qual_cell = list_head(quals); + strategy_cell = list_head(strategies); + subtype_cell = list_head(subtypes); + + for (j = 0; j < n_keys; j++) + { + OpExpr *clause; /* one clause of index qual */ + Expr *leftop; /* expr on lhs of operator */ + Expr *rightop; /* expr on rhs ... */ + int flags = 0; + AttrNumber varattno; /* att number used in scan */ + StrategyNumber strategy; /* op's strategy number */ + Oid subtype; /* op's strategy subtype */ + RegProcedure opfuncid; /* operator proc id used in scan */ + Datum scanvalue; /* value used in scan (if const) */ + + /* + * extract clause information from the qualification + */ + clause = (OpExpr *) lfirst(qual_cell); + qual_cell = lnext(qual_cell); + strategy = lfirst_int(strategy_cell); + strategy_cell = lnext(strategy_cell); + subtype = lfirst_oid(subtype_cell); + subtype_cell = lnext(subtype_cell); + + if (!IsA(clause, OpExpr)) + elog(ERROR, "indexqual is not an OpExpr"); + + opfuncid = clause->opfuncid; + + /* + * Here we figure out the contents of the index qual. The + * usual case is (var op const) which means we form a scan key + * for the attribute listed in the var node and use the value + * of the const as comparison data. + * + * If we don't have a const node, it means our scan key is a + * function of information obtained during the execution of + * the plan, in which case we need to recalculate the index + * scan key at run time. Hence, we set have_runtime_keys to + * true and place the appropriate subexpression in run_keys. + * The corresponding scan key values are recomputed at run + * time. + */ + run_keys[j] = NULL; + + /* + * determine information in leftop + */ + leftop = (Expr *) get_leftop((Expr *) clause); + + if (leftop && IsA(leftop, RelabelType)) + leftop = ((RelabelType *) leftop)->arg; + + Assert(leftop != NULL); + + if (!(IsA(leftop, Var) && + var_is_rel((Var *) leftop))) + elog(ERROR, "indexqual doesn't have key on left side"); + + varattno = ((Var *) leftop)->varattno; + + /* + * now determine information in rightop + */ + rightop = (Expr *) get_rightop((Expr *) clause); + + if (rightop && IsA(rightop, RelabelType)) + rightop = ((RelabelType *) rightop)->arg; + + Assert(rightop != NULL); + + if (IsA(rightop, Const)) + { + /* + * if the rightop is a const node then it means it + * identifies the value to place in our scan key. + */ + scanvalue = ((Const *) rightop)->constvalue; + if (((Const *) rightop)->constisnull) + flags |= SK_ISNULL; + } + else + { + /* + * otherwise, the rightop contains an expression evaluable + * at runtime to figure out the value to place in our scan + * key. + */ + have_runtime_keys = true; + run_keys[j] = ExecInitExpr(rightop, planstate); + scanvalue = (Datum) 0; + } + + /* + * initialize the scan key's fields appropriately + */ + ScanKeyEntryInitialize(&scan_keys[j], + flags, + varattno, /* attribute number to scan */ + strategy, /* op's strategy */ + subtype, /* strategy subtype */ + opfuncid, /* reg proc to use */ + scanvalue); /* constant */ + } + + /* If no runtime keys, get rid of speculatively-allocated array */ + if (run_keys && !have_runtime_keys) + { + pfree(run_keys); + run_keys = NULL; + } + + /* + * Return the info to our caller. + */ + *numScanKeys = n_keys; + *scanKeys = scan_keys; + *runtimeKeyInfo = run_keys; + + return have_runtime_keys; } int |