1 /*-------------------------------------------------------------------------
4 * Routines to support indexes and indexed scans of relations
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.34 1999/04/13 17:18:29 momjian Exp $
12 *-------------------------------------------------------------------------
16 * ExecInsertIndexTuples inserts tuples into indices on result relation
18 * ExecIndexScan scans a relation using indices
19 * ExecIndexNext using index to retrieve next tuple
20 * ExecInitIndexScan creates and initializes state info.
21 * ExecIndexReScan rescans the indexed relation.
22 * ExecEndIndexScan releases all storage.
23 * ExecIndexMarkPos marks scan position.
24 * ExecIndexRestrPos restores scan position.
27 * the code supporting ExecInsertIndexTuples should be
28 * collected and merged with the genam stuff.
33 #include "executor/executor.h"
34 #include "executor/execdebug.h"
35 #include "executor/nodeIndexscan.h"
37 #include "optimizer/clauses.h" /* for get_op, get_leftop, get_rightop */
38 #include "parser/parsetree.h" /* for rt_fetch() */
40 #include "access/skey.h"
41 #include "access/heapam.h"
42 #include "access/genam.h"
43 #include "utils/palloc.h"
44 #include "utils/mcxt.h"
45 #include "catalog/index.h"
46 #include "storage/bufmgr.h"
47 #include "storage/lmgr.h"
48 #include "nodes/nodeFuncs.h"
51 * Misc stuff to move to executor.h soon -cim 6/5/90
58 static TupleTableSlot *IndexNext(IndexScan *node);
60 /* ----------------------------------------------------------------
63 * Retrieve a tuple from the IndexScan node's currentRelation
64 * using the indices in the IndexScanState information.
66 * note: the old code mentions 'Primary indices'. to my knowledge
67 * we only support a single secondary index. -cim 9/11/89
70 * retrieve a tuple from relation using the indices given.
71 * The indices are used in the order they appear in 'indices'.
72 * The indices may be primary or secondary indices:
73 * * primary index -- scan the relation 'relID' using keys supplied.
74 * * secondary index -- scan the index relation to get the 'tid' for
75 * a tuple in the relation 'relID'.
76 * If the current index(pointed by 'indexPtr') fails to return a
77 * tuple, the next index in the indices is used.
79 * bug fix so that it should retrieve on a null scan key.
80 * ----------------------------------------------------------------
82 static TupleTableSlot *
83 IndexNext(IndexScan *node)
86 CommonScanState *scanstate;
87 IndexScanState *indexstate;
88 ScanDirection direction;
90 IndexScanDescPtr scanDescs;
91 IndexScanDesc scandesc;
92 Relation heapRelation;
93 RetrieveIndexResult result;
96 Buffer buffer = InvalidBuffer;
102 * extract necessary information from index scan node
105 estate = node->scan.plan.state;
106 direction = estate->es_direction;
107 snapshot = estate->es_snapshot;
108 scanstate = node->scan.scanstate;
109 indexstate = node->indxstate;
110 scanDescs = indexstate->iss_ScanDescs;
111 heapRelation = scanstate->css_currentRelation;
112 numIndices = indexstate->iss_NumIndices;
113 slot = scanstate->css_ScanTupleSlot;
116 * Check if we are evaluating PlanQual for tuple of this relation.
117 * Additional checking is not good, but no other way for now.
118 * We could introduce new nodes for this case and handle
119 * IndexScan --> NewNode switching in Init/ReScan plan...
121 if (estate->es_evTuple != NULL &&
122 estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
126 slot->ttc_buffer = InvalidBuffer;
127 slot->ttc_shouldFree = false;
128 if (estate->es_evTupleNull[node->scan.scanrelid - 1])
130 slot->val = NULL; /* must not free tuple! */
133 slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
134 for (iptr = 0; iptr < numIndices; iptr++)
136 scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
137 if (ExecQual(nth(iptr, node->indxqualorig),
138 scanstate->cstate.cs_ExprContext))
141 if (iptr == numIndices) /* would not be returned by indices */
143 /* Flag for the next call that no more tuples */
144 estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
148 tuple = &(indexstate->iss_htup);
151 * ok, now that we have what we need, fetch an index tuple.
152 * if scanning this index succeeded then return the
153 * appropriate heap tuple.. else return NULL.
156 bBackward = ScanDirectionIsBackward(direction);
159 indexNumber = numIndices - indexstate->iss_IndexPtr - 1;
163 indexstate->iss_IndexPtr = numIndices - 1;
168 if ((indexNumber = indexstate->iss_IndexPtr) < 0)
171 indexstate->iss_IndexPtr = 0;
174 while (indexNumber < numIndices)
176 scandesc = scanDescs[indexstate->iss_IndexPtr];
177 while ((result = index_getnext(scandesc, direction)) != NULL)
179 tuple->t_self = result->heap_iptr;
180 heap_fetch(heapRelation, snapshot, tuple, &buffer);
183 if (tuple->t_data != NULL)
185 bool prev_matches = false;
189 * store the scanned tuple in the scan tuple slot of
190 * the scan state. Eventually we will only do this and not
191 * return a tuple. Note: we pass 'false' because tuples
192 * returned by amgetnext are pointers onto disk pages and
193 * were not created with palloc() and so should not be pfree()'d.
196 ExecStoreTuple(tuple, /* tuple to store */
197 slot, /* slot to store in */
198 buffer, /* buffer associated with tuple */
199 false); /* don't pfree */
202 * We must check to see if the current tuple would have
203 * been matched by an earlier index, so we don't double
204 * report it. We do this by passing the tuple through
205 * ExecQual and look for failure with all previous
208 for (prev_index = 0; prev_index < indexstate->iss_IndexPtr;
211 scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
212 if (ExecQual(nth(prev_index, node->indxqualorig),
213 scanstate->cstate.cs_ExprContext))
222 ExecClearTuple(slot);
224 if (BufferIsValid(buffer))
225 ReleaseBuffer(buffer);
227 if (indexNumber < numIndices)
231 indexstate->iss_IndexPtr--;
233 indexstate->iss_IndexPtr++;
237 * if we get here it means the index scan failed so we
238 * are at the end of the scan..
241 return ExecClearTuple(slot);
244 /* ----------------------------------------------------------------
245 * ExecIndexScan(node)
248 * Scans the relation using primary or secondary indices and returns
249 * the next qualifying tuple in the direction specified.
250 * It calls ExecScan() and passes it the access methods which returns
251 * the next tuple using the indices.
254 * -- the "cursor" maintained by the AMI is positioned at the tuple
255 * returned previously.
258 * -- the relation indicated is opened for scanning so that the
259 * "cursor" is positioned before the first qualifying tuple.
260 * -- all index realtions are opened for scanning.
261 * -- indexPtr points to the first index.
262 * -- state variable ruleFlag = nil.
263 * ----------------------------------------------------------------
266 ExecIndexScan(IndexScan *node)
269 * use IndexNext as access method
272 return ExecScan(&node->scan, IndexNext);
275 /* ----------------------------------------------------------------
276 * ExecIndexReScan(node)
278 * Recalculates the value of the scan keys whose value depends on
279 * information known at runtime and rescans the indexed relation.
280 * Updating the scan key was formerly done separately in
281 * ExecUpdateIndexScanKeys. Integrating it into ReScan
282 * makes rescans of indices and
283 * relations/general streams more uniform.
285 * ----------------------------------------------------------------
288 ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
291 IndexScanState *indexstate;
292 ScanDirection direction;
293 IndexScanDescPtr scanDescs;
300 Pointer *runtimeKeyInfo;
314 indexstate = node->indxstate;
315 estate = node->scan.plan.state;
316 direction = estate->es_direction;
317 numIndices = indexstate->iss_NumIndices;
318 scanDescs = indexstate->iss_ScanDescs;
319 scanKeys = indexstate->iss_ScanKeys;
320 runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
321 indxqual = node->indxqual;
322 numScanKeys = indexstate->iss_NumScanKeys;
323 indexstate->iss_IndexPtr = -1;
325 /* If this is re-scanning of PlanQual ... */
326 if (estate->es_evTuple != NULL &&
327 estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
329 estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
333 /* it's possible in subselects */
334 if (exprCtxt == NULL)
335 exprCtxt = node->scan.scanstate->cstate.cs_ExprContext;
337 node->scan.scanstate->cstate.cs_ExprContext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
340 * get the index qualifications and recalculate the appropriate values
342 for (i = 0; i < numIndices; i++)
344 qual = nth(i, indxqual);
345 n_keys = numScanKeys[i];
346 scan_keys = (ScanKey) scanKeys[i];
350 run_keys = (int *) runtimeKeyInfo[i];
351 for (j = 0; j < n_keys; j++)
355 * If we have a run-time key, then extract the run-time
356 * expression and evaluate it with respect to the current
357 * outer tuple. We then stick the result into the scan
360 if (run_keys[j] != NO_OP)
362 clause = nth(j, qual);
363 scanexpr = (run_keys[j] == RIGHT_OP) ?
364 (Node *) get_rightop(clause) : (Node *) get_leftop(clause);
367 * pass in isDone but ignore it. We don't iterate in
371 ExecEvalExpr(scanexpr, exprCtxt, &isNull, &isDone);
372 scan_keys[j].sk_argument = scanvalue;
374 scan_keys[j].sk_flags |= SK_ISNULL;
376 scan_keys[j].sk_flags &= ~SK_ISNULL;
382 index_rescan(scan, direction, skey);
385 * perhaps return something meaningful
391 /* ----------------------------------------------------------------
395 * Releases any storage allocated through C routines.
397 * ----------------------------------------------------------------
400 ExecEndIndexScan(IndexScan *node)
402 CommonScanState *scanstate;
403 IndexScanState *indexstate;
404 Pointer *runtimeKeyInfo;
411 scanstate = node->scan.scanstate;
412 indexstate = node->indxstate;
413 indxqual = node->indxqual;
414 runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
417 * extract information from the node
420 numIndices = indexstate->iss_NumIndices;
421 scanKeys = indexstate->iss_ScanKeys;
422 numScanKeys = indexstate->iss_NumScanKeys;
425 * Free the projection info and the scan attribute info
427 * Note: we don't ExecFreeResultType(scanstate)
428 * because the rule manager depends on the tupType
429 * returned by ExecMain(). So for now, this
430 * is freed at end-transaction time. -cim 6/2/91
433 ExecFreeProjectionInfo(&scanstate->cstate);
436 * close the heap and index relations
439 ExecCloseR((Plan *) node);
442 * free the scan keys used in scanning the indices
445 for (i = 0; i < numIndices; i++)
447 if (scanKeys[i] != NULL)
455 for (i = 0; i < numIndices; i++)
460 qual = nth(i, indxqual);
461 n_keys = length(qual);
463 pfree(runtimeKeyInfo[i]);
465 pfree(runtimeKeyInfo);
469 * clear out tuple table slots
472 ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
473 ExecClearTuple(scanstate->css_ScanTupleSlot);
474 /* ExecClearTuple(scanstate->css_RawTupleSlot); */
477 /* ----------------------------------------------------------------
481 * Marks scan position by marking the current index.
483 * ----------------------------------------------------------------
486 ExecIndexMarkPos(IndexScan *node)
488 IndexScanState *indexstate;
489 IndexScanDescPtr indexScanDescs;
490 IndexScanDesc scanDesc;
493 indexstate = node->indxstate;
494 indexPtr = indexstate->iss_MarkIndexPtr = indexstate->iss_IndexPtr;
495 indexScanDescs = indexstate->iss_ScanDescs;
496 scanDesc = indexScanDescs[indexPtr];
499 IndexScanMarkPosition(scanDesc);
501 index_markpos(scanDesc);
504 /* ----------------------------------------------------------------
508 * Restores scan position by restoring the current index.
511 * XXX Assumes previously marked scan position belongs to current index
512 * ----------------------------------------------------------------
515 ExecIndexRestrPos(IndexScan *node)
517 IndexScanState *indexstate;
518 IndexScanDescPtr indexScanDescs;
519 IndexScanDesc scanDesc;
522 indexstate = node->indxstate;
523 indexPtr = indexstate->iss_IndexPtr = indexstate->iss_MarkIndexPtr;
524 indexScanDescs = indexstate->iss_ScanDescs;
525 scanDesc = indexScanDescs[indexPtr];
528 IndexScanRestorePosition(scanDesc);
530 index_restrpos(scanDesc);
533 /* ----------------------------------------------------------------
536 * Initializes the index scan's state information, creates
537 * scan keys, and opens the base and index relations.
539 * Note: index scans have 2 sets of state information because
540 * we have to keep track of the base relation and the
544 * Creates the run-time state information for the node and
545 * sets the relation id to contain relevant decriptors.
548 * node: IndexNode node produced by the planner.
549 * estate: the execution state initialized in InitPlan.
550 * ----------------------------------------------------------------
553 ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
555 IndexScanState *indexstate;
556 CommonScanState *scanstate;
564 RelationPtr relationDescs;
565 IndexScanDescPtr scanDescs;
566 Pointer *runtimeKeyInfo;
567 bool have_runtime_keys;
569 RangeTblEntry *rtentry;
573 Relation currentRelation;
574 HeapScanDesc currentScanDesc;
575 ScanDirection direction;
578 List *execParam = NULL;
581 * assign execution state to node
584 node->scan.plan.state = estate;
586 /* --------------------------------
587 * Part 1) initialize scan state
589 * create new CommonScanState for node
590 * --------------------------------
592 scanstate = makeNode(CommonScanState);
594 scanstate->ss_ProcOuterFlag = false;
595 scanstate->ss_OldRelId = 0;
598 node->scan.scanstate = scanstate;
601 * assign node's base_id .. we don't use AssignNodeBaseid() because
602 * the increment is done later on after we assign the index scan's
603 * scanstate. see below.
606 baseid = estate->es_BaseId;
607 /* scanstate->csstate.cstate.bnode.base_id = baseid; */
608 scanstate->cstate.cs_base_id = baseid;
611 * create expression context for node
614 ExecAssignExprContext(estate, &scanstate->cstate);
616 #define INDEXSCAN_NSLOTS 3
618 * tuple table initialization
621 ExecInitResultTupleSlot(estate, &scanstate->cstate);
622 ExecInitScanTupleSlot(estate, scanstate);
623 /* ExecInitRawTupleSlot(estate, scanstate); */
626 * initialize projection info. result type comes from scan desc
630 ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
632 /* --------------------------------
633 * Part 2) initialize index scan state
635 * create new IndexScanState for node
636 * --------------------------------
638 indexstate = makeNode(IndexScanState);
639 indexstate->iss_NumIndices = 0;
640 indexstate->iss_IndexPtr = -1;
641 indexstate->iss_ScanKeys = NULL;
642 indexstate->iss_NumScanKeys = NULL;
643 indexstate->iss_RuntimeKeyInfo = NULL;
644 indexstate->iss_RelationDescs = NULL;
645 indexstate->iss_ScanDescs = NULL;
647 node->indxstate = indexstate;
650 * assign base id to index scan state also
653 indexstate->cstate.cs_base_id = baseid;
655 estate->es_BaseId = baseid;
658 * get the index node information
661 indxid = node->indxid;
662 indxqual = node->indxqual;
663 numIndices = length(indxid);
666 CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
669 * scanKeys is used to keep track of the ScanKey's. This is needed
670 * because a single scan may use several indices and each index has
674 numScanKeys = (int *) palloc(numIndices * sizeof(int));
675 scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey));
676 relationDescs = (RelationPtr) palloc(numIndices * sizeof(Relation));
677 scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
680 * initialize runtime key info.
683 have_runtime_keys = false;
684 runtimeKeyInfo = (Pointer *)
685 palloc(numIndices * sizeof(Pointer));
688 * build the index scan keys from the index qualification
691 for (i = 0; i < numIndices; i++)
699 qual = nth(i, indxqual);
700 n_keys = length(qual);
701 scan_keys = (n_keys <= 0) ? NULL :
702 (ScanKey) palloc(n_keys * sizeof(ScanKeyData));
703 run_keys = (n_keys <= 0) ? NULL :
704 (int *) palloc(n_keys * sizeof(int));
706 CXT1_printf("ExecInitIndexScan: context is %d\n",
707 CurrentMemoryContext);
710 * for each opclause in the given qual,
711 * convert each qual's opclause into a single scan key
714 for (j = 0; j < n_keys; j++)
716 Expr *clause; /* one part of index qual */
717 Oper *op; /* operator used in scan.. */
718 Node *leftop; /* expr on lhs of operator */
719 Node *rightop;/* expr on rhs ... */
722 int scanvar;/* which var identifies varattno */
723 AttrNumber varattno = 0; /* att number used in scan */
724 Oid opid; /* operator id used in scan */
725 Datum scanvalue = 0; /* value used in scan (if const) */
728 * extract clause information from the qualification
731 clause = nth(j, qual);
733 op = (Oper *) clause->oper;
735 elog(ERROR, "ExecInitIndexScan: op not an Oper!");
740 * Here we figure out the contents of the index qual.
741 * The usual case is (op var const) or (op const var)
742 * which means we form a scan key for the attribute
743 * listed in the var node and use the value of the const.
745 * If we don't have a const node, then it means that
746 * one of the var nodes refers to the "scan" tuple and
747 * is used to determine which attribute to scan, and the
748 * other expression is used to calculate the value used in
749 * scanning the index.
751 * This means our index scan's scan key is a function of
752 * information obtained during the execution of the plan
753 * in which case we need to recalculate the index scan key
756 * Hence, we set have_runtime_keys to true and then set
757 * the appropriate flag in run_keys to LEFT_OP or RIGHT_OP.
758 * The corresponding scan keys are recomputed at run time.
765 * determine information in leftop
768 leftop = (Node *) get_leftop(clause);
770 if (IsA(leftop, Var) &&var_is_rel((Var *) leftop))
773 * if the leftop is a "rel-var", then it means
774 * that it is a var node which tells us which
775 * attribute to use for our scan key.
778 varattno = ((Var *) leftop)->varattno;
781 else if (IsA(leftop, Const))
784 * if the leftop is a const node then it means
785 * it identifies the value to place in our scan key.
789 scanvalue = ((Const *) leftop)->constvalue;
791 else if (IsA(leftop, Param))
796 * if the leftop is a Param node then it means
797 * it identifies the value to place in our scan key.
801 /* Life was so easy before ... subselects */
802 if (((Param *) leftop)->paramkind == PARAM_EXEC)
804 have_runtime_keys = true;
805 run_keys[j] = LEFT_OP;
806 execParam = lappendi(execParam, ((Param *) leftop)->paramid);
810 scanvalue = ExecEvalParam((Param *) leftop,
811 scanstate->cstate.cs_ExprContext,
819 else if (leftop != NULL &&
820 is_funcclause(leftop) &&
821 var_is_rel(lfirst(((Expr *) leftop)->args)))
824 * if the leftop is a func node then it means
825 * it identifies the value to place in our scan key.
826 * Since functional indices have only one attribute
827 * the attno must always be set to 1.
837 * otherwise, the leftop contains information usable
838 * at runtime to figure out the value to place in our
842 have_runtime_keys = true;
843 run_keys[j] = LEFT_OP;
844 scanvalue = Int32GetDatum((int32) true);
848 * now determine information in rightop
851 rightop = (Node *) get_rightop(clause);
853 if (IsA(rightop, Var) &&var_is_rel((Var *) rightop))
856 * here we make sure only one op identifies the
860 if (scanvar == LEFT_OP)
861 elog(ERROR, "ExecInitIndexScan: %s",
862 "both left and right op's are rel-vars");
865 * if the rightop is a "rel-var", then it means
866 * that it is a var node which tells us which
867 * attribute to use for our scan key.
870 varattno = ((Var *) rightop)->varattno;
874 else if (IsA(rightop, Const))
877 * if the leftop is a const node then it means
878 * it identifies the value to place in our scan key.
882 scanvalue = ((Const *) rightop)->constvalue;
884 else if (IsA(rightop, Param))
889 * if the rightop is a Param node then it means
890 * it identifies the value to place in our scan key.
894 /* Life was so easy before ... subselects */
895 if (((Param *) rightop)->paramkind == PARAM_EXEC)
897 have_runtime_keys = true;
898 run_keys[j] = RIGHT_OP;
899 execParam = lappendi(execParam, ((Param *) rightop)->paramid);
903 scanvalue = ExecEvalParam((Param *) rightop,
904 scanstate->cstate.cs_ExprContext,
912 else if (rightop != NULL &&
913 is_funcclause(rightop) &&
914 var_is_rel(lfirst(((Expr *) rightop)->args)))
917 * if the rightop is a func node then it means
918 * it identifies the value to place in our scan key.
919 * Since functional indices have only one attribute
920 * the attno must always be set to 1.
923 if (scanvar == LEFT_OP)
924 elog(ERROR, "ExecInitIndexScan: %s",
925 "both left and right ops are rel-vars");
934 * otherwise, the leftop contains information usable
935 * at runtime to figure out the value to place in our
939 have_runtime_keys = true;
940 run_keys[j] = RIGHT_OP;
941 scanvalue = Int32GetDatum((int32) true);
945 * now check that at least one op tells us the scan
949 if (scanvar == NO_OP)
950 elog(ERROR, "ExecInitIndexScan: %s",
951 "neither leftop nor rightop refer to scan relation");
954 * initialize the scan key's fields appropriately
957 ScanKeyEntryInitialize(&scan_keys[j],
959 varattno, /* attribute number to
961 (RegProcedure) opid, /* reg proc to use */
962 (Datum) scanvalue); /* constant */
966 * store the key information into our array.
969 numScanKeys[i] = n_keys;
970 scanKeys[i] = scan_keys;
971 runtimeKeyInfo[i] = (Pointer) run_keys;
974 indexstate->iss_NumIndices = numIndices;
975 indexstate->iss_IndexPtr = indexPtr;
976 indexstate->iss_ScanKeys = scanKeys;
977 indexstate->iss_NumScanKeys = numScanKeys;
980 * If all of our keys have the form (op var const) , then we have no
981 * runtime keys so we store NULL in the runtime key info.
982 * Otherwise runtime key info contains an array of pointers
983 * (one for each index) to arrays of flags (one for each key)
984 * which indicate that the qual needs to be evaluated at runtime.
988 if (have_runtime_keys)
989 indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo;
991 indexstate->iss_RuntimeKeyInfo = NULL;
994 * get the range table and direction information
995 * from the execution state (these are needed to
996 * open the relations).
999 rangeTable = estate->es_range_table;
1000 direction = estate->es_direction;
1003 * open the base relation
1006 relid = node->scan.scanrelid;
1007 rtentry = rt_fetch(relid, rangeTable);
1008 reloid = rtentry->relid;
1010 ExecOpenScanR(reloid, /* relation */
1012 (ScanKey) NULL, /* scan key */
1014 direction, /* scan direction */
1015 estate->es_snapshot, /* */
1016 ¤tRelation, /* return: rel desc */
1017 (Pointer *) ¤tScanDesc); /* return: scan desc */
1019 scanstate->css_currentRelation = currentRelation;
1020 scanstate->css_currentScanDesc = currentScanDesc;
1024 * get the scan type from the relation descriptor.
1027 ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
1028 ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
1031 * index scans don't have subtrees..
1034 /* scanstate->ss_ProcOuterFlag = false; */
1037 * open the index relations and initialize
1038 * relation and scan descriptors.
1041 for (i = 0; i < numIndices; i++)
1045 indexOid = (Oid) nthi(i, indxid);
1049 ExecOpenScanR(indexOid, /* relation */
1050 numScanKeys[i], /* nkeys */
1051 scanKeys[i], /* scan key */
1052 true, /* is index */
1053 direction, /* scan direction */
1054 estate->es_snapshot,
1055 &(relationDescs[i]), /* return: rel desc */
1056 (Pointer *) &(scanDescs[i]));
1057 /* return: scan desc */
1061 indexstate->iss_RelationDescs = relationDescs;
1062 indexstate->iss_ScanDescs = scanDescs;
1064 indexstate->cstate.cs_TupFromTlist = false;
1067 * if there are some PARAM_EXEC in skankeys then force index rescan on
1070 ((Plan *) node)->chgParam = execParam;
1080 ExecCountSlotsIndexScan(IndexScan *node)
1082 return ExecCountSlotsNode(outerPlan((Plan *) node)) +
1083 ExecCountSlotsNode(innerPlan((Plan *) node)) + INDEXSCAN_NSLOTS;