summaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeIndexscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeIndexscan.c')
-rw-r--r--src/backend/executor/nodeIndexscan.c930
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