1 /*-------------------------------------------------------------------------
4 * Routines to support indexes and indexed scans of relations
6 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.94 2004/05/26 04:41:16 neilc Exp $
13 *-------------------------------------------------------------------------
17 * ExecIndexScan scans a relation using indices
18 * ExecIndexNext using index to retrieve next tuple
19 * ExecInitIndexScan creates and initializes state info.
20 * ExecIndexReScan rescans the indexed relation.
21 * ExecEndIndexScan releases all storage.
22 * ExecIndexMarkPos marks scan position.
23 * ExecIndexRestrPos restores scan position.
27 #include "access/genam.h"
28 #include "access/heapam.h"
29 #include "executor/execdebug.h"
30 #include "executor/nodeIndexscan.h"
31 #include "miscadmin.h"
32 #include "nodes/nodeFuncs.h"
33 #include "optimizer/clauses.h"
34 #include "parser/parsetree.h"
38 * In a multiple-index plan, we must take care to return any given tuple
39 * only once, even if it matches conditions of several index scans. Our
40 * preferred way to do this is to record already-returned tuples in a hash
41 * table (using the TID as unique identifier). However, in a very large
42 * scan this could conceivably run out of memory. We limit the hash table
43 * to no more than work_mem KB; if it grows past that, we fall back to the
44 * pre-7.4 technique: evaluate the prior-scan index quals again for each
45 * tuple (which is space-efficient, but slow).
47 * When scanning backwards, we use scannum to determine when to emit the
48 * tuple --- we have to re-emit a tuple in the same scan as it was first
51 * Note: this code would break if the planner were ever to create a multiple
52 * index plan with overall backwards direction, because the hashtable code
53 * will emit a tuple the first time it is encountered (which would be the
54 * highest scan in which it matches the index), but the evaluate-the-quals
55 * code will emit a tuple in the lowest-numbered scan in which it's valid.
56 * This could be fixed at need by making the evaluate-the-quals case more
57 * complex. Currently the planner will never create such a plan (since it
58 * considers multi-index plans unordered anyway), so there's no need for
63 /* tid is the hash key and so must be first! */
64 ItemPointerData tid; /* TID of a tuple we've returned */
65 int scannum; /* number of scan we returned it in */
69 static TupleTableSlot *IndexNext(IndexScanState *node);
70 static void create_duphash(IndexScanState *node);
73 /* ----------------------------------------------------------------
76 * Retrieve a tuple from the IndexScan node's currentRelation
77 * using the indices in the IndexScanState information.
79 * note: the old code mentions 'Primary indices'. to my knowledge
80 * we only support a single secondary index. -cim 9/11/89
83 * retrieve a tuple from relation using the indices given.
84 * The indices are used in the order they appear in 'indices'.
85 * The indices may be primary or secondary indices:
86 * * primary index -- scan the relation 'relID' using keys supplied.
87 * * secondary index -- scan the index relation to get the 'tid' for
88 * a tuple in the relation 'relID'.
89 * If the current index(pointed by 'indexPtr') fails to return a
90 * tuple, the next index in the indices is used.
92 * bug fix so that it should retrieve on a null scan key.
93 * ----------------------------------------------------------------
95 static TupleTableSlot *
96 IndexNext(IndexScanState *node)
99 ExprContext *econtext;
100 ScanDirection direction;
101 IndexScanDescPtr scanDescs;
103 IndexScanDesc scandesc;
107 TupleTableSlot *slot;
113 * extract necessary information from index scan node
115 estate = node->ss.ps.state;
116 direction = estate->es_direction;
117 if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indxorderdir))
119 if (ScanDirectionIsForward(direction))
120 direction = BackwardScanDirection;
121 else if (ScanDirectionIsBackward(direction))
122 direction = ForwardScanDirection;
124 scanDescs = node->iss_ScanDescs;
125 lossyQuals = node->iss_LossyQuals;
126 numIndices = node->iss_NumIndices;
127 econtext = node->ss.ps.ps_ExprContext;
128 slot = node->ss.ss_ScanTupleSlot;
129 scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid;
132 * Clear any reference to the previously returned tuple. The idea here
133 * is to not have the tuple slot be the last holder of a pin on that
134 * tuple's buffer; if it is, we'll need a separate visit to the bufmgr
135 * to release the buffer. By clearing here, we get to have the release
136 * done by ReleaseAndReadBuffer inside index_getnext.
138 ExecClearTuple(slot);
141 * Check if we are evaluating PlanQual for tuple of this relation.
142 * Additional checking is not good, but no other way for now. We could
143 * introduce new nodes for this case and handle IndexScan --> NewNode
144 * switching in Init/ReScan plan...
146 if (estate->es_evTuple != NULL &&
147 estate->es_evTuple[scanrelid - 1] != NULL)
151 if (estate->es_evTupleNull[scanrelid - 1])
152 return slot; /* return empty slot */
154 ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
155 slot, InvalidBuffer, false);
157 /* Does the tuple meet any of the OR'd indxqual conditions? */
158 econtext->ecxt_scantuple = slot;
160 ResetExprContext(econtext);
162 foreach(qual, node->indxqualorig)
164 if (ExecQual((List *) lfirst(qual), econtext, false))
167 if (qual == NULL) /* would not be returned by indices */
170 /* Flag for the next call that no more tuples */
171 estate->es_evTupleNull[scanrelid - 1] = true;
177 * ok, now that we have what we need, fetch an index tuple. if
178 * scanning this index succeeded then return the appropriate heap
179 * tuple.. else return NULL.
181 bBackward = ScanDirectionIsBackward(direction);
184 indexNumber = numIndices - node->iss_IndexPtr - 1;
188 node->iss_IndexPtr = numIndices - 1;
193 if ((indexNumber = node->iss_IndexPtr) < 0)
196 node->iss_IndexPtr = 0;
199 while (indexNumber < numIndices)
201 scandesc = scanDescs[node->iss_IndexPtr];
202 lossyQual = lossyQuals[node->iss_IndexPtr];
204 while ((tuple = index_getnext(scandesc, direction)) != NULL)
207 * Store the scanned tuple in the scan tuple slot of the scan
208 * state. Note: we pass 'false' because tuples returned by
209 * amgetnext are pointers onto disk pages and must not be
212 ExecStoreTuple(tuple, /* tuple to store */
213 slot, /* slot to store in */
214 scandesc->xs_cbuf, /* buffer containing tuple */
215 false); /* don't pfree */
218 * If any of the index operators involved in this scan are lossy,
219 * recheck them by evaluating the original operator clauses.
223 econtext->ecxt_scantuple = slot;
224 ResetExprContext(econtext);
225 if (!ExecQual(lossyQual, econtext, false))
227 /* Fails lossy op, so drop it and loop back for another */
228 ExecClearTuple(slot);
234 * If it's a multiple-index scan, make sure not to double-report
235 * a tuple matched by more than one index. (See notes above.)
239 /* First try the hash table */
240 if (node->iss_DupHash)
242 DupHashTabEntry *entry;
245 entry = (DupHashTabEntry *)
246 hash_search(node->iss_DupHash,
247 &tuple->t_data->t_ctid,
251 node->iss_DupHash->hctl->nentries > node->iss_MaxHash)
253 /* out of memory (either hard or soft limit) */
254 /* release hash table and fall thru to old code */
255 hash_destroy(node->iss_DupHash);
256 node->iss_DupHash = NULL;
260 /* pre-existing entry */
263 * It's duplicate if first emitted in a different
264 * scan. If same scan, we must be backing up, so
265 * okay to emit again.
267 if (entry->scannum != node->iss_IndexPtr)
269 /* Dup, so drop it and loop back for another */
270 ExecClearTuple(slot);
276 /* new entry, finish filling it in */
277 entry->scannum = node->iss_IndexPtr;
280 /* If hash table has overflowed, do it the hard way */
281 if (node->iss_DupHash == NULL &&
282 node->iss_IndexPtr > 0)
284 bool prev_matches = false;
288 econtext->ecxt_scantuple = slot;
289 ResetExprContext(econtext);
290 qual = list_head(node->indxqualorig);
292 prev_index < node->iss_IndexPtr;
295 if (ExecQual((List *) lfirst(qual), econtext, false))
304 /* Dup, so drop it and loop back for another */
305 ExecClearTuple(slot);
311 return slot; /* OK to return tuple */
314 if (indexNumber < numIndices)
318 node->iss_IndexPtr--;
320 node->iss_IndexPtr++;
325 * if we get here it means the index scan failed so we are at the end
328 return ExecClearTuple(slot);
331 /* ----------------------------------------------------------------
332 * ExecIndexScan(node)
335 * Scans the relation using primary or secondary indices and returns
336 * the next qualifying tuple in the direction specified.
337 * It calls ExecScan() and passes it the access methods which returns
338 * the next tuple using the indices.
341 * -- the "cursor" maintained by the AMI is positioned at the tuple
342 * returned previously.
345 * -- the relation indicated is opened for scanning so that the
346 * "cursor" is positioned before the first qualifying tuple.
347 * -- all index realtions are opened for scanning.
348 * -- indexPtr points to the first index.
349 * -- state variable ruleFlag = nil.
350 * ----------------------------------------------------------------
353 ExecIndexScan(IndexScanState *node)
356 * If we have runtime keys and they've not already been set up, do it
359 if (node->iss_RuntimeKeyInfo && !node->iss_RuntimeKeysReady)
360 ExecReScan((PlanState *) node, NULL);
363 * use IndexNext as access method
365 return ExecScan(&node->ss, (ExecScanAccessMtd) IndexNext);
368 /* ----------------------------------------------------------------
369 * ExecIndexReScan(node)
371 * Recalculates the value of the scan keys whose value depends on
372 * information known at runtime and rescans the indexed relation.
373 * Updating the scan key was formerly done separately in
374 * ExecUpdateIndexScanKeys. Integrating it into ReScan makes
375 * rescans of indices and relations/general streams more uniform.
377 * ----------------------------------------------------------------
380 ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
383 ExprContext *econtext;
385 IndexScanDescPtr scanDescs;
387 ExprState ***runtimeKeyInfo;
393 estate = node->ss.ps.state;
394 econtext = node->iss_RuntimeContext; /* context for runtime
396 numIndices = node->iss_NumIndices;
397 scanDescs = node->iss_ScanDescs;
398 scanKeys = node->iss_ScanKeys;
399 runtimeKeyInfo = node->iss_RuntimeKeyInfo;
400 numScanKeys = node->iss_NumScanKeys;
401 scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid;
406 * If we are being passed an outer tuple, save it for runtime key
407 * calc. We also need to link it into the "regular" per-tuple
408 * econtext, so it can be used during indexqualorig evaluations.
410 if (exprCtxt != NULL)
412 ExprContext *stdecontext;
414 econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
415 stdecontext = node->ss.ps.ps_ExprContext;
416 stdecontext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
420 * Reset the runtime-key context so we don't leak memory as each
421 * outer tuple is scanned. Note this assumes that we will
422 * recalculate *all* runtime keys on each call.
424 ResetExprContext(econtext);
428 * If we are doing runtime key calculations (ie, the index keys depend
429 * on data from an outer scan), compute the new key values
433 for (i = 0; i < numIndices; i++)
437 ExprState **run_keys;
439 n_keys = numScanKeys[i];
440 scan_keys = scanKeys[i];
441 run_keys = runtimeKeyInfo[i];
443 for (j = 0; j < n_keys; j++)
446 * If we have a run-time key, then extract the run-time
447 * expression and evaluate it with respect to the current
448 * outer tuple. We then stick the result into the scan
451 * Note: the result of the eval could be a pass-by-ref value
452 * that's stored in the outer scan's tuple, not in
453 * econtext->ecxt_per_tuple_memory. We assume that the
454 * outer tuple will stay put throughout our scan. If this
455 * is wrong, we could copy the result into our context
456 * explicitly, but I think that's not necessary...
458 if (run_keys[j] != NULL)
463 scanvalue = ExecEvalExprSwitchContext(run_keys[j],
467 scan_keys[j].sk_argument = scanvalue;
469 scan_keys[j].sk_flags |= SK_ISNULL;
471 scan_keys[j].sk_flags &= ~SK_ISNULL;
476 node->iss_RuntimeKeysReady = true;
479 /* If this is re-scanning of PlanQual ... */
480 if (estate->es_evTuple != NULL &&
481 estate->es_evTuple[scanrelid - 1] != NULL)
483 estate->es_evTupleNull[scanrelid - 1] = false;
487 /* reset hash table */
490 if (node->iss_DupHash)
491 hash_destroy(node->iss_DupHash);
492 create_duphash(node);
495 /* reset index scans */
496 if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indxorderdir))
497 node->iss_IndexPtr = numIndices;
499 node->iss_IndexPtr = -1;
501 for (i = 0; i < numIndices; i++)
503 IndexScanDesc scan = scanDescs[i];
504 ScanKey skey = scanKeys[i];
506 index_rescan(scan, skey);
510 /* ----------------------------------------------------------------
512 * ----------------------------------------------------------------
515 ExecEndIndexScan(IndexScanState *node)
518 RelationPtr indexRelationDescs;
519 IndexScanDescPtr indexScanDescs;
524 * extract information from the node
526 numIndices = node->iss_NumIndices;
527 indexRelationDescs = node->iss_RelationDescs;
528 indexScanDescs = node->iss_ScanDescs;
529 relation = node->ss.ss_currentRelation;
532 * Free the exprcontext(s)
534 ExecFreeExprContext(&node->ss.ps);
535 if (node->iss_RuntimeContext)
536 FreeExprContext(node->iss_RuntimeContext);
539 * clear out tuple table slots
541 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
542 ExecClearTuple(node->ss.ss_ScanTupleSlot);
544 /* drop hash table */
545 if (node->iss_DupHash)
546 hash_destroy(node->iss_DupHash);
549 * close the index relations
551 for (i = 0; i < numIndices; i++)
553 if (indexScanDescs[i] != NULL)
554 index_endscan(indexScanDescs[i]);
556 if (indexRelationDescs[i] != NULL)
557 index_close(indexRelationDescs[i]);
561 * close the heap relation.
563 * Currently, we do not release the AccessShareLock acquired by
564 * ExecInitIndexScan. This lock should be held till end of
565 * transaction. (There is a faction that considers this too much
568 heap_close(relation, NoLock);
571 /* ----------------------------------------------------------------
575 * Marks scan position by marking the current index.
577 * ----------------------------------------------------------------
580 ExecIndexMarkPos(IndexScanState *node)
582 IndexScanDescPtr indexScanDescs;
583 IndexScanDesc scanDesc;
586 indexPtr = node->iss_MarkIndexPtr = node->iss_IndexPtr;
587 if (indexPtr >= 0 && indexPtr < node->iss_NumIndices)
589 indexScanDescs = node->iss_ScanDescs;
590 scanDesc = indexScanDescs[indexPtr];
592 index_markpos(scanDesc);
596 /* ----------------------------------------------------------------
600 * Restores scan position by restoring the current index.
602 * ----------------------------------------------------------------
605 ExecIndexRestrPos(IndexScanState *node)
607 IndexScanDescPtr indexScanDescs;
608 IndexScanDesc scanDesc;
611 indexPtr = node->iss_IndexPtr = node->iss_MarkIndexPtr;
612 if (indexPtr >= 0 && indexPtr < node->iss_NumIndices)
614 indexScanDescs = node->iss_ScanDescs;
615 scanDesc = indexScanDescs[indexPtr];
617 index_restrpos(scanDesc);
621 /* ----------------------------------------------------------------
624 * Initializes the index scan's state information, creates
625 * scan keys, and opens the base and index relations.
627 * Note: index scans have 2 sets of state information because
628 * we have to keep track of the base relation and the
632 * Creates the run-time state information for the node and
633 * sets the relation id to contain relevant descriptors.
636 * node: IndexNode node produced by the planner.
637 * estate: the execution state initialized in InitPlan.
638 * ----------------------------------------------------------------
641 ExecInitIndexScan(IndexScan *node, EState *estate)
643 IndexScanState *indexstate;
645 ListCell *indxstrategy;
646 ListCell *indxsubtype;
648 ListCell *indxid_item;
654 RelationPtr indexDescs;
655 IndexScanDescPtr scanDescs;
657 ExprState ***runtimeKeyInfo;
658 bool have_runtime_keys;
659 RangeTblEntry *rtentry;
662 Relation currentRelation;
665 * create state structure
667 indexstate = makeNode(IndexScanState);
668 indexstate->ss.ps.plan = (Plan *) node;
669 indexstate->ss.ps.state = estate;
672 * Miscellaneous initialization
674 * create expression context for node
676 ExecAssignExprContext(estate, &indexstate->ss.ps);
679 * initialize child expressions
681 * Note: we don't initialize all of the indxqual expression, only the
682 * sub-parts corresponding to runtime keys (see below). The indxqualorig
683 * expression is always initialized even though it will only be used in
684 * some uncommon cases --- would be nice to improve that. (Problem is
685 * that any SubPlans present in the expression must be found now...)
687 indexstate->ss.ps.targetlist = (List *)
688 ExecInitExpr((Expr *) node->scan.plan.targetlist,
689 (PlanState *) indexstate);
690 indexstate->ss.ps.qual = (List *)
691 ExecInitExpr((Expr *) node->scan.plan.qual,
692 (PlanState *) indexstate);
693 indexstate->indxqualorig = (List *)
694 ExecInitExpr((Expr *) node->indxqualorig,
695 (PlanState *) indexstate);
697 #define INDEXSCAN_NSLOTS 2
700 * tuple table initialization
702 ExecInitResultTupleSlot(estate, &indexstate->ss.ps);
703 ExecInitScanTupleSlot(estate, &indexstate->ss);
706 * Initialize index-specific scan state
708 indexstate->iss_NumIndices = 0;
709 indexstate->iss_IndexPtr = -1;
710 indexstate->iss_ScanKeys = NULL;
711 indexstate->iss_NumScanKeys = NULL;
712 indexstate->iss_RuntimeKeyInfo = NULL;
713 indexstate->iss_RuntimeContext = NULL;
714 indexstate->iss_RuntimeKeysReady = false;
715 indexstate->iss_RelationDescs = NULL;
716 indexstate->iss_ScanDescs = NULL;
717 indexstate->iss_LossyQuals = NULL;
720 * get the index node information
722 indxid_item = list_head(node->indxid);
723 numIndices = length(node->indxid);
726 CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
729 * scanKeys is used to keep track of the ScanKey's. This is needed
730 * because a single scan may use several indices and each index has
733 numScanKeys = (int *) palloc(numIndices * sizeof(int));
734 scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey));
735 indexDescs = (RelationPtr) palloc(numIndices * sizeof(Relation));
736 scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
737 lossyQuals = (List **) palloc0(numIndices * sizeof(List *));
740 * initialize space for runtime key info (may not be needed)
742 have_runtime_keys = false;
743 runtimeKeyInfo = (ExprState ***) palloc0(numIndices * sizeof(ExprState **));
746 * build the index scan keys from the index qualification
748 indxqual = list_head(node->indxqual);
749 indxstrategy = list_head(node->indxstrategy);
750 indxsubtype = list_head(node->indxsubtype);
751 indxlossy = list_head(node->indxlossy);
752 for (i = 0; i < numIndices; i++)
759 ListCell *strategy_cell;
760 ListCell *subtype_cell;
761 ListCell *lossyflag_cell;
764 ExprState **run_keys;
767 quals = (List *) lfirst(indxqual);
768 indxqual = lnext(indxqual);
769 strategies = (List *) lfirst(indxstrategy);
770 indxstrategy = lnext(indxstrategy);
771 subtypes = (List *) lfirst(indxsubtype);
772 indxsubtype = lnext(indxsubtype);
773 lossyflags = (List *) lfirst(indxlossy);
774 indxlossy = lnext(indxlossy);
775 n_keys = length(quals);
776 scan_keys = (n_keys <= 0) ? NULL :
777 (ScanKey) palloc(n_keys * sizeof(ScanKeyData));
778 run_keys = (n_keys <= 0) ? NULL :
779 (ExprState **) palloc(n_keys * sizeof(ExprState *));
782 * for each opclause in the given qual, convert each qual's
783 * opclause into a single scan key
785 qual_cell = list_head(quals);
786 strategy_cell = list_head(strategies);
787 subtype_cell = list_head(subtypes);
788 lossyflag_cell = list_head(lossyflags);
789 for (j = 0; j < n_keys; j++)
791 OpExpr *clause; /* one clause of index qual */
792 Expr *leftop; /* expr on lhs of operator */
793 Expr *rightop; /* expr on rhs ... */
795 AttrNumber varattno; /* att number used in scan */
796 StrategyNumber strategy; /* op's strategy number */
797 Oid subtype; /* op's strategy subtype */
798 int lossy; /* op's recheck flag */
799 RegProcedure opfuncid; /* operator proc id used in scan */
800 Datum scanvalue; /* value used in scan (if const) */
803 * extract clause information from the qualification
805 clause = (OpExpr *) lfirst(qual_cell);
806 qual_cell = lnext(qual_cell);
807 strategy = lfirsti(strategy_cell);
808 strategy_cell = lnext(strategy_cell);
809 subtype = lfirsto(subtype_cell);
810 subtype_cell = lnext(subtype_cell);
811 lossy = lfirsti(lossyflag_cell);
812 lossyflag_cell = lnext(lossyflag_cell);
814 if (!IsA(clause, OpExpr))
815 elog(ERROR, "indxqual is not an OpExpr");
817 opfuncid = clause->opfuncid;
820 * Here we figure out the contents of the index qual. The
821 * usual case is (var op const) which means we form a scan key
822 * for the attribute listed in the var node and use the value of
823 * the const as comparison data.
825 * If we don't have a const node, it means our scan key is a
826 * function of information obtained during the execution of the
827 * plan, in which case we need to recalculate the index scan key
828 * at run time. Hence, we set have_runtime_keys to true and place
829 * the appropriate subexpression in run_keys. The corresponding
830 * scan key values are recomputed at run time.
835 * determine information in leftop
837 leftop = (Expr *) get_leftop((Expr *) clause);
839 if (leftop && IsA(leftop, RelabelType))
840 leftop = ((RelabelType *) leftop)->arg;
842 Assert(leftop != NULL);
844 if (!(IsA(leftop, Var) &&
845 var_is_rel((Var *) leftop)))
846 elog(ERROR, "indxqual doesn't have key on left side");
848 varattno = ((Var *) leftop)->varattno;
851 * now determine information in rightop
853 rightop = (Expr *) get_rightop((Expr *) clause);
855 if (rightop && IsA(rightop, RelabelType))
856 rightop = ((RelabelType *) rightop)->arg;
858 Assert(rightop != NULL);
860 if (IsA(rightop, Const))
863 * if the rightop is a const node then it means it
864 * identifies the value to place in our scan key.
866 scanvalue = ((Const *) rightop)->constvalue;
867 if (((Const *) rightop)->constisnull)
873 * otherwise, the rightop contains an expression evaluable
874 * at runtime to figure out the value to place in our scan
877 have_runtime_keys = true;
878 run_keys[j] = ExecInitExpr(rightop, (PlanState *) indexstate);
879 scanvalue = (Datum) 0;
883 * initialize the scan key's fields appropriately
885 ScanKeyEntryInitialize(&scan_keys[j],
887 varattno, /* attribute number to
889 strategy, /* op's strategy */
890 subtype, /* strategy subtype */
891 opfuncid, /* reg proc to use */
892 scanvalue); /* constant */
895 * If this operator is lossy, add its indxqualorig expression
896 * to the list of quals to recheck. The nth() calls here could
897 * be avoided by chasing the lists in parallel to all the other
898 * lists, but since lossy operators are very uncommon, it's
899 * probably a waste of time to do so.
903 lossyQuals[i] = lappend(lossyQuals[i],
905 (List *) nth(i, indexstate->indxqualorig)));
910 * store the key information into our arrays.
912 numScanKeys[i] = n_keys;
913 scanKeys[i] = scan_keys;
914 runtimeKeyInfo[i] = run_keys;
917 indexstate->iss_NumIndices = numIndices;
918 if (ScanDirectionIsBackward(node->indxorderdir))
919 indexPtr = numIndices;
920 indexstate->iss_IndexPtr = indexPtr;
921 indexstate->iss_ScanKeys = scanKeys;
922 indexstate->iss_NumScanKeys = numScanKeys;
925 * If all of our keys have the form (var op const), then we have no
926 * runtime keys so we store NULL in the runtime key info. Otherwise
927 * runtime key info contains an array of pointers (one for each index)
928 * to arrays of flags (one for each key) which indicate that the qual
929 * needs to be evaluated at runtime. -cim 10/24/89
931 * If we do have runtime keys, we need an ExprContext to evaluate them;
932 * the node's standard context won't do because we want to reset that
933 * context for every tuple. So, build another context just like the
934 * other one... -tgl 7/11/00
936 if (have_runtime_keys)
938 ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
940 ExecAssignExprContext(estate, &indexstate->ss.ps);
941 indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo;
942 indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
943 indexstate->ss.ps.ps_ExprContext = stdecontext;
947 indexstate->iss_RuntimeKeyInfo = NULL;
948 indexstate->iss_RuntimeContext = NULL;
949 /* Get rid of the speculatively-allocated flag arrays, too */
950 for (i = 0; i < numIndices; i++)
952 if (runtimeKeyInfo[i] != NULL)
953 pfree(runtimeKeyInfo[i]);
955 pfree(runtimeKeyInfo);
959 * open the base relation and acquire AccessShareLock on it.
961 relid = node->scan.scanrelid;
962 rtentry = rt_fetch(relid, estate->es_range_table);
963 reloid = rtentry->relid;
965 currentRelation = heap_open(reloid, AccessShareLock);
967 indexstate->ss.ss_currentRelation = currentRelation;
968 indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
971 * get the scan type from the relation descriptor.
973 ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation), false);
976 * open the index relations and initialize relation and scan
977 * descriptors. Note we acquire no locks here; the index machinery
978 * does its own locks and unlocks. (We rely on having AccessShareLock
979 * on the parent table to ensure the index won't go away!)
981 for (i = 0; i < numIndices; i++)
983 Oid indexOid = lfirsto(indxid_item);
985 indexDescs[i] = index_open(indexOid);
986 scanDescs[i] = index_beginscan(currentRelation,
991 indxid_item = lnext(indxid_item);
994 indexstate->iss_RelationDescs = indexDescs;
995 indexstate->iss_ScanDescs = scanDescs;
996 indexstate->iss_LossyQuals = lossyQuals;
999 * Initialize result tuple type and projection info.
1001 ExecAssignResultTypeFromTL(&indexstate->ss.ps);
1002 ExecAssignScanProjectionInfo(&indexstate->ss);
1005 * Initialize hash table if needed.
1008 create_duphash(indexstate);
1010 indexstate->iss_DupHash = NULL;
1019 create_duphash(IndexScanState *node)
1024 node->iss_MaxHash = (work_mem * 1024L) /
1025 (MAXALIGN(sizeof(HASHELEMENT)) + MAXALIGN(sizeof(DupHashTabEntry)));
1026 MemSet(&hash_ctl, 0, sizeof(hash_ctl));
1027 hash_ctl.keysize = SizeOfIptrData;
1028 hash_ctl.entrysize = sizeof(DupHashTabEntry);
1029 hash_ctl.hash = tag_hash;
1030 hash_ctl.hcxt = CurrentMemoryContext;
1031 nbuckets = (long) ceil(node->ss.ps.plan->plan_rows);
1034 if (nbuckets > node->iss_MaxHash)
1035 nbuckets = node->iss_MaxHash;
1036 node->iss_DupHash = hash_create("DupHashTable",
1039 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
1040 if (node->iss_DupHash == NULL)
1042 (errcode(ERRCODE_OUT_OF_MEMORY),
1043 errmsg("out of memory")));
1047 ExecCountSlotsIndexScan(IndexScan *node)
1049 return ExecCountSlotsNode(outerPlan((Plan *) node)) +
1050 ExecCountSlotsNode(innerPlan((Plan *) node)) + INDEXSCAN_NSLOTS;