/*-------------------------------------------------------------------------
*
* nodeIndexscan.c
- * Routines to support indexes and indexed scans of relations
+ * Routines to support indexed scans of relations
*
- * Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.44 2000/01/19 23:54:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.122 2007/05/25 17:54:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
- * ExecInsertIndexTuples inserts tuples into indices on result relation
- *
* ExecIndexScan scans a relation using indices
* ExecIndexNext using index to retrieve next tuple
* ExecInitIndexScan creates and initializes state info.
* ExecEndIndexScan releases all storage.
* ExecIndexMarkPos marks scan position.
* ExecIndexRestrPos restores scan position.
- *
- * NOTES
- * the code supporting ExecInsertIndexTuples should be
- * collected and merged with the genam stuff.
- *
*/
#include "postgres.h"
-
-
#include "access/genam.h"
-#include "access/heapam.h"
+#include "access/nbtree.h"
#include "executor/execdebug.h"
-#include "executor/executor.h"
#include "executor/nodeIndexscan.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
-#include "parser/parsetree.h"
+#include "utils/array.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
-/* ----------------
- * Misc stuff to move to executor.h soon -cim 6/5/90
- * ----------------
- */
-#define NO_OP 0
-#define LEFT_OP 1
-#define RIGHT_OP 2
-static TupleTableSlot *IndexNext(IndexScan *node);
+static TupleTableSlot *IndexNext(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 *
-IndexNext(IndexScan *node)
+IndexNext(IndexScanState *node)
{
EState *estate;
- CommonScanState *scanstate;
- IndexScanState *indexstate;
+ ExprContext *econtext;
ScanDirection direction;
- Snapshot snapshot;
- IndexScanDescPtr scanDescs;
IndexScanDesc scandesc;
- Relation heapRelation;
- RetrieveIndexResult result;
+ Index scanrelid;
HeapTuple tuple;
TupleTableSlot *slot;
- Buffer buffer = InvalidBuffer;
- int numIndices;
- bool bBackward;
- int indexNumber;
-
- /* ----------------
- * extract necessary information from index scan node
- * ----------------
+ /*
+ * extract necessary information from index scan node
*/
- estate = node->scan.plan.state;
+ estate = node->ss.ps.state;
direction = estate->es_direction;
- if (ScanDirectionIsBackward(node->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;
- }
- snapshot = estate->es_snapshot;
- scanstate = node->scan.scanstate;
- indexstate = node->indxstate;
- scanDescs = indexstate->iss_ScanDescs;
- heapRelation = scanstate->css_currentRelation;
- numIndices = indexstate->iss_NumIndices;
- slot = scanstate->css_ScanTupleSlot;
+ }
+ scandesc = node->iss_ScanDesc;
+ econtext = node->ss.ps.ps_ExprContext;
+ slot = node->ss.ss_ScanTupleSlot;
+ scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid;
/*
* Check if we are evaluating PlanQual for tuple of this relation.
* switching in Init/ReScan plan...
*/
if (estate->es_evTuple != NULL &&
- estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
+ estate->es_evTuple[scanrelid - 1] != NULL)
{
- int iptr;
+ if (estate->es_evTupleNull[scanrelid - 1])
+ return ExecClearTuple(slot);
- ExecClearTuple(slot);
- if (estate->es_evTupleNull[node->scan.scanrelid - 1])
- return slot; /* return empty slot */
+ ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
+ slot, InvalidBuffer, false);
- /* probably ought to use ExecStoreTuple here... */
- slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
- slot->ttc_shouldFree = false;
+ /* Does the tuple meet the indexqual condition? */
+ econtext->ecxt_scantuple = slot;
- for (iptr = 0; iptr < numIndices; iptr++)
- {
- scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
- if (ExecQual(nth(iptr, node->indxqualorig),
- scanstate->cstate.cs_ExprContext,
- false))
- break;
- }
- if (iptr == numIndices) /* would not be returned by indices */
- slot->val = NULL;
+ ResetExprContext(econtext);
+
+ 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[node->scan.scanrelid - 1] = true;
- return (slot);
- }
+ estate->es_evTupleNull[scanrelid - 1] = true;
- tuple = &(indexstate->iss_htup);
+ return slot;
+ }
- /* ----------------
- * ok, now that we have what we need, fetch an index tuple.
- * if scanning this index succeeded then return the
- * appropriate heap tuple.. else return NULL.
- * ----------------
+ /*
+ * ok, now that we have what we need, fetch the next tuple.
*/
- bBackward = ScanDirectionIsBackward(direction);
- if (bBackward)
+ if ((tuple = index_getnext(scandesc, direction)) != NULL)
{
- indexNumber = numIndices - indexstate->iss_IndexPtr - 1;
- if (indexNumber < 0)
- {
- indexNumber = 0;
- indexstate->iss_IndexPtr = numIndices - 1;
- }
- }
- else
- {
- if ((indexNumber = indexstate->iss_IndexPtr) < 0)
- {
- indexNumber = 0;
- indexstate->iss_IndexPtr = 0;
- }
- }
- while (indexNumber < numIndices)
- {
- scandesc = scanDescs[indexstate->iss_IndexPtr];
- while ((result = index_getnext(scandesc, direction)) != NULL)
- {
- tuple->t_self = result->heap_iptr;
- heap_fetch(heapRelation, snapshot, tuple, &buffer);
- pfree(result);
-
- if (tuple->t_data != NULL)
- {
- bool prev_matches = false;
- int prev_index;
-
- /* ----------------
- * store the scanned tuple in the scan tuple slot of
- * the scan state. Eventually we will only do this and not
- * return a tuple. 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 */
- buffer, /* buffer associated with tuple */
- false); /* don't pfree */
-
- /*
- * At this point we have an extra pin on the buffer,
- * because ExecStoreTuple incremented the pin count.
- * Drop our local pin.
- */
- ReleaseBuffer(buffer);
+ /*
+ * 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 */
- /*
- * We must check to see if the current tuple would have
- * been matched by an earlier index, so we don't double
- * report it. We do this by passing the tuple through
- * ExecQual and look for failure with all previous
- * qualifications.
- */
- for (prev_index = 0; prev_index < indexstate->iss_IndexPtr;
- prev_index++)
- {
- scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
- if (ExecQual(nth(prev_index, node->indxqualorig),
- scanstate->cstate.cs_ExprContext,
- false))
- {
- prev_matches = true;
- break;
- }
- }
- if (!prev_matches)
- return slot;
- else
- ExecClearTuple(slot);
- }
- }
- if (indexNumber < numIndices)
- {
- indexNumber++;
- if (bBackward)
- indexstate->iss_IndexPtr--;
- else
- indexstate->iss_IndexPtr++;
- }
+ return slot;
}
- /* ----------------
- * if we get here it means the index scan failed so we
- * are at the end of the scan..
- * ----------------
+
+ /*
+ * if we get here it means the index scan failed so we are at the end of
+ * the scan..
*/
return ExecClearTuple(slot);
}
/* ----------------------------------------------------------------
* ExecIndexScan(node)
- *
- * old comments:
- * Scans the relation using primary or secondary indices and returns
- * the next qualifying tuple in the direction specified.
- * It calls ExecScan() and passes it the access methods which returns
- * the next tuple using the indices.
- *
- * Conditions:
- * -- the "cursor" maintained by the AMI is positioned at the tuple
- * returned previously.
- *
- * Initial States:
- * -- the relation indicated is opened for scanning so that the
- * "cursor" is positioned before the first qualifying tuple.
- * -- all index realtions are opened for scanning.
- * -- indexPtr points to the first index.
- * -- state variable ruleFlag = nil.
* ----------------------------------------------------------------
*/
TupleTableSlot *
-ExecIndexScan(IndexScan *node)
+ExecIndexScan(IndexScanState *node)
{
- /* ----------------
- * use IndexNext as access method
- * ----------------
+ /*
+ * If we have runtime keys and they've not already been set up, do it now.
*/
- return ExecScan(&node->scan, IndexNext);
+ if (node->iss_NumRuntimeKeys != 0 && !node->iss_RuntimeKeysReady)
+ ExecReScan((PlanState *) node, NULL);
+
+ /*
+ * use IndexNext as access method
+ */
+ return ExecScan(&node->ss, (ExecScanAccessMtd) IndexNext);
}
/* ----------------------------------------------------------------
* Recalculates the value of the scan keys whose value depends on
* information known at runtime and rescans the indexed relation.
* Updating the scan key was formerly done separately in
- * ExecUpdateIndexScanKeys. Integrating it into ReScan
- * makes rescans of indices and
- * relations/general streams more uniform.
- *
+ * ExecUpdateIndexScanKeys. Integrating it into ReScan makes
+ * rescans of indices and relations/general streams more uniform.
* ----------------------------------------------------------------
*/
void
-ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
+ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
{
EState *estate;
- IndexScanState *indexstate;
- ScanDirection direction;
- IndexScanDescPtr scanDescs;
- ScanKey *scanKeys;
- IndexScanDesc scan;
- ScanKey skey;
- int numIndices;
- int i;
-
- Pointer *runtimeKeyInfo;
- int *numScanKeys;
- List *indxqual;
- List *qual;
- int n_keys;
- ScanKey scan_keys;
- int *run_keys;
- int j;
- Expr *clause;
- Node *scanexpr;
- Datum scanvalue;
- bool isNull;
- bool isDone;
-
- indexstate = node->indxstate;
- estate = node->scan.plan.state;
- direction = estate->es_direction;
- numIndices = indexstate->iss_NumIndices;
- scanDescs = indexstate->iss_ScanDescs;
- scanKeys = indexstate->iss_ScanKeys;
- runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
- indxqual = node->indxqual;
- numScanKeys = indexstate->iss_NumScanKeys;
- indexstate->iss_IndexPtr = -1;
- if (ScanDirectionIsBackward(node->indxorderdir))
- indexstate->iss_IndexPtr = numIndices;
+ ExprContext *econtext;
+ Index scanrelid;
+
+ estate = node->ss.ps.state;
+ econtext = node->iss_RuntimeContext; /* context for runtime keys */
+ scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid;
+
+ node->ss.ps.ps_TupFromTlist = false;
+
+ if (econtext)
+ {
+ /*
+ * If we are being passed an outer tuple, save it for runtime key
+ * calc. We also need to link it into the "regular" per-tuple
+ * econtext, so it can be used during indexqualorig evaluations.
+ */
+ if (exprCtxt != NULL)
+ {
+ ExprContext *stdecontext;
+
+ econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
+ stdecontext = node->ss.ps.ps_ExprContext;
+ stdecontext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
+ }
+
+ /*
+ * Reset the runtime-key context so we don't leak memory as each outer
+ * tuple is scanned. Note this assumes that we will recalculate *all*
+ * runtime keys on each call.
+ */
+ ResetExprContext(econtext);
+ }
+
+ /*
+ * If we are doing runtime key calculations (ie, the index keys depend on
+ * data from an outer scan), compute the new key values
+ */
+ if (node->iss_NumRuntimeKeys != 0)
+ ExecIndexEvalRuntimeKeys(econtext,
+ node->iss_RuntimeKeys,
+ node->iss_NumRuntimeKeys);
+ node->iss_RuntimeKeysReady = true;
/* If this is re-scanning of PlanQual ... */
if (estate->es_evTuple != NULL &&
- estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
+ estate->es_evTuple[scanrelid - 1] != NULL)
{
- estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
+ estate->es_evTupleNull[scanrelid - 1] = false;
return;
}
- /* it's possible in subselects */
- if (exprCtxt == NULL)
- exprCtxt = node->scan.scanstate->cstate.cs_ExprContext;
+ /* reset index scan */
+ index_rescan(node->iss_ScanDesc, node->iss_ScanKeys);
+}
- node->scan.scanstate->cstate.cs_ExprContext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
- /*
- * get the index qualifications and recalculate the appropriate values
- */
- for (i = 0; i < numIndices; i++)
+/*
+ * ExecIndexEvalRuntimeKeys
+ * Evaluate any runtime key values, and update the scankeys.
+ */
+void
+ExecIndexEvalRuntimeKeys(ExprContext *econtext,
+ IndexRuntimeKeyInfo *runtimeKeys, int numRuntimeKeys)
+{
+ int j;
+
+ for (j = 0; j < numRuntimeKeys; j++)
{
- qual = nth(i, indxqual);
- n_keys = numScanKeys[i];
- scan_keys = (ScanKey) scanKeys[i];
+ ScanKey scan_key = runtimeKeys[j].scan_key;
+ ExprState *key_expr = runtimeKeys[j].key_expr;
+ Datum scanvalue;
+ bool isNull;
+
+ /*
+ * For each run-time key, extract the run-time expression and evaluate
+ * it with respect to the current outer tuple. We then stick the
+ * result into the proper 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...
+ */
+ scanvalue = ExecEvalExprSwitchContext(key_expr,
+ econtext,
+ &isNull,
+ NULL);
+ scan_key->sk_argument = scanvalue;
+ if (isNull)
+ scan_key->sk_flags |= SK_ISNULL;
+ else
+ scan_key->sk_flags &= ~SK_ISNULL;
+ }
+}
+
+/*
+ * ExecIndexEvalArrayKeys
+ * Evaluate any array key values, and set up to iterate through arrays.
+ *
+ * Returns TRUE if there are array elements to consider; FALSE means there
+ * is at least one null or empty array, so no match is possible. On TRUE
+ * result, the scankeys are initialized with the first elements of the arrays.
+ */
+bool
+ExecIndexEvalArrayKeys(ExprContext *econtext,
+ IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
+{
+ bool result = true;
+ int j;
+ MemoryContext oldContext;
+
+ /* We want to keep the arrays in per-tuple memory */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
- if (runtimeKeyInfo)
+ for (j = 0; j < numArrayKeys; j++)
+ {
+ ScanKey scan_key = arrayKeys[j].scan_key;
+ ExprState *array_expr = arrayKeys[j].array_expr;
+ Datum arraydatum;
+ bool isNull;
+ ArrayType *arrayval;
+ int16 elmlen;
+ bool elmbyval;
+ char elmalign;
+ int num_elems;
+ Datum *elem_values;
+ bool *elem_nulls;
+
+ /*
+ * Compute and deconstruct the array expression. (Notes in
+ * ExecIndexEvalRuntimeKeys() apply here too.)
+ */
+ arraydatum = ExecEvalExpr(array_expr,
+ econtext,
+ &isNull,
+ NULL);
+ if (isNull)
{
- run_keys = (int *) runtimeKeyInfo[i];
- for (j = 0; j < n_keys; j++)
- {
+ result = false;
+ break; /* no point in evaluating more */
+ }
+ arrayval = DatumGetArrayTypeP(arraydatum);
+ /* We could cache this data, but not clear it's worth it */
+ get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
+ &elmlen, &elmbyval, &elmalign);
+ deconstruct_array(arrayval,
+ ARR_ELEMTYPE(arrayval),
+ elmlen, elmbyval, elmalign,
+ &elem_values, &elem_nulls, &num_elems);
+ if (num_elems <= 0)
+ {
+ result = false;
+ break; /* no point in evaluating more */
+ }
- /*
- * 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.
- */
- if (run_keys[j] != NO_OP)
- {
- clause = nth(j, qual);
- scanexpr = (run_keys[j] == RIGHT_OP) ?
- (Node *) get_rightop(clause) : (Node *) get_leftop(clause);
-
- /*
- * pass in isDone but ignore it. We don't iterate in
- * quals
- */
- scanvalue = (Datum)
- ExecEvalExpr(scanexpr, exprCtxt, &isNull, &isDone);
- scan_keys[j].sk_argument = scanvalue;
- if (isNull)
- scan_keys[j].sk_flags |= SK_ISNULL;
- else
- scan_keys[j].sk_flags &= ~SK_ISNULL;
- }
- }
+ /*
+ * Note: we expect the previous array data, if any, to be
+ * automatically freed by resetting the per-tuple context; hence no
+ * pfree's here.
+ */
+ arrayKeys[j].elem_values = elem_values;
+ arrayKeys[j].elem_nulls = elem_nulls;
+ arrayKeys[j].num_elems = num_elems;
+ scan_key->sk_argument = elem_values[0];
+ if (elem_nulls[0])
+ scan_key->sk_flags |= SK_ISNULL;
+ else
+ scan_key->sk_flags &= ~SK_ISNULL;
+ arrayKeys[j].next_elem = 1;
+ }
+
+ MemoryContextSwitchTo(oldContext);
+
+ return result;
+}
+
+/*
+ * ExecIndexAdvanceArrayKeys
+ * Advance to the next set of array key values, if any.
+ *
+ * Returns TRUE if there is another set of values to consider, FALSE if not.
+ * On TRUE result, the scankeys are initialized with the next set of values.
+ */
+bool
+ExecIndexAdvanceArrayKeys(IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
+{
+ bool found = false;
+ int j;
+
+ for (j = 0; j < numArrayKeys; j++)
+ {
+ ScanKey scan_key = arrayKeys[j].scan_key;
+ int next_elem = arrayKeys[j].next_elem;
+ int num_elems = arrayKeys[j].num_elems;
+ Datum *elem_values = arrayKeys[j].elem_values;
+ bool *elem_nulls = arrayKeys[j].elem_nulls;
+
+ if (next_elem >= num_elems)
+ {
+ next_elem = 0;
+ found = false; /* need to advance next array key */
}
- scan = scanDescs[i];
- skey = scanKeys[i];
- index_rescan(scan, direction, skey);
+ else
+ found = true;
+ scan_key->sk_argument = elem_values[next_elem];
+ if (elem_nulls[next_elem])
+ scan_key->sk_flags |= SK_ISNULL;
+ else
+ scan_key->sk_flags &= ~SK_ISNULL;
+ arrayKeys[j].next_elem = next_elem + 1;
+ if (found)
+ break;
}
- /* ----------------
- * perhaps return something meaningful
- * ----------------
- */
- return;
+
+ return found;
}
+
/* ----------------------------------------------------------------
* ExecEndIndexScan
- *
- * old comments
- * Releases any storage allocated through C routines.
- * Returns nothing.
* ----------------------------------------------------------------
*/
void
-ExecEndIndexScan(IndexScan *node)
+ExecEndIndexScan(IndexScanState *node)
{
- CommonScanState *scanstate;
- IndexScanState *indexstate;
- Pointer *runtimeKeyInfo;
- ScanKey *scanKeys;
- List *indxqual;
- int *numScanKeys;
- int numIndices;
- int i;
-
- scanstate = node->scan.scanstate;
- indexstate = node->indxstate;
- indxqual = node->indxqual;
- runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
-
- /* ----------------
- * extract information from the node
- * ----------------
- */
- numIndices = indexstate->iss_NumIndices;
- scanKeys = indexstate->iss_ScanKeys;
- numScanKeys = indexstate->iss_NumScanKeys;
+ Relation indexRelationDesc;
+ IndexScanDesc indexScanDesc;
+ Relation relation;
- /* ----------------
- * Free the projection info and the scan attribute info
- *
- * Note: we don't ExecFreeResultType(scanstate)
- * because the rule manager depends on the tupType
- * returned by ExecMain(). So for now, this
- * is freed at end-transaction time. -cim 6/2/91
- * ----------------
+ /*
+ * extract information from the node
*/
- ExecFreeProjectionInfo(&scanstate->cstate);
+ indexRelationDesc = node->iss_RelationDesc;
+ indexScanDesc = node->iss_ScanDesc;
+ relation = node->ss.ss_currentRelation;
- /* ----------------
- * close the heap and index relations
- * ----------------
+ /*
+ * Free the exprcontext(s) ... now dead code, see ExecFreeExprContext
*/
- ExecCloseR((Plan *) node);
+#ifdef NOT_USED
+ ExecFreeExprContext(&node->ss.ps);
+ if (node->iss_RuntimeContext)
+ FreeExprContext(node->iss_RuntimeContext);
+#endif
- /* ----------------
- * free the scan keys used in scanning the indices
- * ----------------
+ /*
+ * clear out tuple table slots
*/
- for (i = 0; i < numIndices; i++)
- {
- if (scanKeys[i] != NULL)
- pfree(scanKeys[i]);
- }
- pfree(scanKeys);
- pfree(numScanKeys);
+ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+ ExecClearTuple(node->ss.ss_ScanTupleSlot);
- if (runtimeKeyInfo)
- {
- for (i = 0; i < numIndices; i++)
- {
- List *qual;
- int n_keys;
-
- qual = nth(i, indxqual);
- n_keys = length(qual);
- if (n_keys > 0)
- pfree(runtimeKeyInfo[i]);
- }
- pfree(runtimeKeyInfo);
- }
+ /*
+ * close the index relation (no-op if we didn't open it)
+ */
+ if (indexScanDesc)
+ index_endscan(indexScanDesc);
+ if (indexRelationDesc)
+ index_close(indexRelationDesc, NoLock);
- /* ----------------
- * clear out tuple table slots
- * ----------------
+ /*
+ * close the heap relation.
*/
- ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
- ExecClearTuple(scanstate->css_ScanTupleSlot);
-/* ExecClearTuple(scanstate->css_RawTupleSlot); */
+ ExecCloseScanRelation(relation);
}
/* ----------------------------------------------------------------
* ExecIndexMarkPos
- *
- * old comments
- * Marks scan position by marking the current index.
- * Returns nothing.
* ----------------------------------------------------------------
*/
void
-ExecIndexMarkPos(IndexScan *node)
+ExecIndexMarkPos(IndexScanState *node)
{
- IndexScanState *indexstate;
- IndexScanDescPtr indexScanDescs;
- IndexScanDesc scanDesc;
- int indexPtr;
-
- indexstate = node->indxstate;
- indexPtr = indexstate->iss_MarkIndexPtr = indexstate->iss_IndexPtr;
- indexScanDescs = indexstate->iss_ScanDescs;
- scanDesc = indexScanDescs[indexPtr];
-
-#ifdef NOT_USED
- IndexScanMarkPosition(scanDesc);
-#endif
- index_markpos(scanDesc);
+ index_markpos(node->iss_ScanDesc);
}
/* ----------------------------------------------------------------
* ExecIndexRestrPos
- *
- * old comments
- * Restores scan position by restoring the current index.
- * Returns nothing.
- *
- * XXX Assumes previously marked scan position belongs to current index
* ----------------------------------------------------------------
*/
void
-ExecIndexRestrPos(IndexScan *node)
+ExecIndexRestrPos(IndexScanState *node)
{
- IndexScanState *indexstate;
- IndexScanDescPtr indexScanDescs;
- IndexScanDesc scanDesc;
- int indexPtr;
-
- indexstate = node->indxstate;
- indexPtr = indexstate->iss_IndexPtr = indexstate->iss_MarkIndexPtr;
- indexScanDescs = indexstate->iss_ScanDescs;
- scanDesc = indexScanDescs[indexPtr];
-
-#ifdef NOT_USED
- IndexScanRestorePosition(scanDesc);
-#endif
- index_restrpos(scanDesc);
+ index_restrpos(node->iss_ScanDesc);
}
/* ----------------------------------------------------------------
* ExecInitIndexScan
- *
+ *
* Initializes the index scan's state information, creates
* scan keys, and opens the base and index relations.
*
* 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 decriptors.
- *
- * Parameters:
- * node: IndexNode node produced by the planner.
- * estate: the execution state initialized in InitPlan.
+ * index relation.
* ----------------------------------------------------------------
*/
-bool
-ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
+IndexScanState *
+ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
{
IndexScanState *indexstate;
- CommonScanState *scanstate;
- List *indxqual;
- List *indxid;
- int i;
- int numIndices;
- int indexPtr;
- ScanKey *scanKeys;
- int *numScanKeys;
- RelationPtr relationDescs;
- IndexScanDescPtr scanDescs;
- Pointer *runtimeKeyInfo;
- bool have_runtime_keys;
- List *rangeTable;
- RangeTblEntry *rtentry;
- Index relid;
- Oid reloid;
-
Relation currentRelation;
- HeapScanDesc currentScanDesc;
- ScanDirection direction;
- int baseid;
+ bool relistarget;
- List *execParam = NULL;
+ /*
+ * create state structure
+ */
+ indexstate = makeNode(IndexScanState);
+ indexstate->ss.ps.plan = (Plan *) node;
+ indexstate->ss.ps.state = estate;
- /* ----------------
- * assign execution state to node
- * ----------------
+ /*
+ * Miscellaneous initialization
+ *
+ * create expression context for node
*/
- node->scan.plan.state = estate;
+ ExecAssignExprContext(estate, &indexstate->ss.ps);
- /* --------------------------------
- * Part 1) initialize scan state
+ indexstate->ss.ps.ps_TupFromTlist = false;
+
+ /*
+ * initialize child expressions
*
- * create new CommonScanState for node
- * --------------------------------
+ * Note: we don't initialize all of the indexqual expression, only the
+ * sub-parts corresponding to runtime keys (see below). The 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...)
*/
- scanstate = makeNode(CommonScanState);
-/*
- scanstate->ss_ProcOuterFlag = false;
- scanstate->ss_OldRelId = 0;
-*/
+ indexstate->ss.ps.targetlist = (List *)
+ ExecInitExpr((Expr *) node->scan.plan.targetlist,
+ (PlanState *) indexstate);
+ indexstate->ss.ps.qual = (List *)
+ ExecInitExpr((Expr *) node->scan.plan.qual,
+ (PlanState *) indexstate);
+ indexstate->indexqualorig = (List *)
+ ExecInitExpr((Expr *) node->indexqualorig,
+ (PlanState *) indexstate);
+
+#define INDEXSCAN_NSLOTS 2
- node->scan.scanstate = scanstate;
+ /*
+ * tuple table initialization
+ */
+ ExecInitResultTupleSlot(estate, &indexstate->ss.ps);
+ ExecInitScanTupleSlot(estate, &indexstate->ss);
- /* ----------------
- * assign node's base_id .. we don't use AssignNodeBaseid() because
- * the increment is done later on after we assign the index scan's
- * scanstate. see below.
- * ----------------
+ /*
+ * open the base relation and acquire appropriate lock on it.
*/
- baseid = estate->es_BaseId;
-/* scanstate->csstate.cstate.bnode.base_id = baseid; */
- scanstate->cstate.cs_base_id = baseid;
+ currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
- /* ----------------
- * create expression context for node
- * ----------------
+ indexstate->ss.ss_currentRelation = currentRelation;
+ indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
+
+ /*
+ * get the scan type from the relation descriptor.
*/
- ExecAssignExprContext(estate, &scanstate->cstate);
+ ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation));
-#define INDEXSCAN_NSLOTS 3
- /* ----------------
- * tuple table initialization
- * ----------------
+ /*
+ * If we are just doing EXPLAIN (ie, aren't going to run the plan),
+ * stop here. This allows an index-advisor plugin to EXPLAIN a plan
+ * containing references to nonexistent indexes.
*/
- ExecInitResultTupleSlot(estate, &scanstate->cstate);
- ExecInitScanTupleSlot(estate, scanstate);
-/* ExecInitRawTupleSlot(estate, scanstate); */
-
- /* ----------------
- * initialize projection info. result type comes from scan desc
- * below..
- * ----------------
+ if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
+ return indexstate;
+
+ /*
+ * Open the index relation.
+ *
+ * If the parent table is one of the target relations of the query, then
+ * InitPlan already opened and write-locked the index, so we can avoid
+ * taking another lock here. Otherwise we need a normal reader's lock.
*/
- ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
-
- /* --------------------------------
- * Part 2) initialize index scan state
- *
- * create new IndexScanState for node
- * --------------------------------
- */
- indexstate = makeNode(IndexScanState);
- indexstate->iss_NumIndices = 0;
- indexstate->iss_IndexPtr = -1;
- indexstate->iss_ScanKeys = NULL;
- indexstate->iss_NumScanKeys = NULL;
- indexstate->iss_RuntimeKeyInfo = NULL;
- indexstate->iss_RelationDescs = NULL;
- indexstate->iss_ScanDescs = NULL;
-
- node->indxstate = indexstate;
-
- /* ----------------
- * assign base id to index scan state also
- * ----------------
+ relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid);
+ indexstate->iss_RelationDesc = index_open(node->indexid,
+ relistarget ? NoLock : AccessShareLock);
+
+ /*
+ * Initialize index-specific scan state
*/
- indexstate->cstate.cs_base_id = baseid;
- baseid++;
- estate->es_BaseId = baseid;
+ indexstate->iss_RuntimeKeysReady = false;
- /* ----------------
- * get the index node information
- * ----------------
+ /*
+ * build the index scan keys from the index qualification
*/
- indxid = node->indxid;
- indxqual = node->indxqual;
- numIndices = length(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.
- * ----------------
+ ExecIndexBuildScanKeys((PlanState *) indexstate,
+ indexstate->iss_RelationDesc,
+ node->indexqual,
+ node->indexstrategy,
+ node->indexsubtype,
+ &indexstate->iss_ScanKeys,
+ &indexstate->iss_NumScanKeys,
+ &indexstate->iss_RuntimeKeys,
+ &indexstate->iss_NumRuntimeKeys,
+ NULL, /* no ArrayKeys */
+ NULL);
+
+ /*
+ * 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
*/
- numScanKeys = (int *) palloc(numIndices * sizeof(int));
- scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey));
- relationDescs = (RelationPtr) palloc(numIndices * sizeof(Relation));
- scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
-
- /* ----------------
- * initialize runtime key info.
- * ----------------
+ if (indexstate->iss_NumRuntimeKeys != 0)
+ {
+ ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
+
+ ExecAssignExprContext(estate, &indexstate->ss.ps);
+ indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
+ indexstate->ss.ps.ps_ExprContext = stdecontext;
+ }
+ else
+ {
+ indexstate->iss_RuntimeContext = NULL;
+ }
+
+ /*
+ * Initialize scan descriptor.
*/
- have_runtime_keys = false;
- runtimeKeyInfo = (Pointer *)
- palloc(numIndices * sizeof(Pointer));
+ indexstate->iss_ScanDesc = index_beginscan(currentRelation,
+ indexstate->iss_RelationDesc,
+ estate->es_snapshot,
+ indexstate->iss_NumScanKeys,
+ indexstate->iss_ScanKeys);
- /* ----------------
- * build the index scan keys from the index qualification
- * ----------------
+ /*
+ * Initialize result tuple type and projection info.
*/
- for (i = 0; i < numIndices; i++)
+ ExecAssignResultTypeFromTL(&indexstate->ss.ps);
+ ExecAssignScanProjectionInfo(&indexstate->ss);
+
+ /*
+ * all done.
+ */
+ return indexstate;
+}
+
+
+/*
+ * ExecIndexBuildScanKeys
+ * Build the index scan keys from the index qualification expressions
+ *
+ * The index quals are passed to the index AM in the form of a ScanKey array.
+ * This routine sets up the ScanKeys, fills in all constant fields of the
+ * ScanKeys, and prepares information about the keys that have non-constant
+ * comparison values. We divide index qual expressions into five types:
+ *
+ * 1. Simple operator with constant comparison value ("indexkey op constant").
+ * For these, we just fill in a ScanKey containing the constant value.
+ *
+ * 2. Simple operator with non-constant value ("indexkey op expression").
+ * For these, we create a ScanKey with everything filled in except the
+ * expression value, and set up an IndexRuntimeKeyInfo struct to drive
+ * evaluation of the expression at the right times.
+ *
+ * 3. RowCompareExpr ("(indexkey, indexkey, ...) op (expr, expr, ...)").
+ * For these, we create a header ScanKey plus a subsidiary ScanKey array,
+ * as specified in access/skey.h. The elements of the row comparison
+ * can have either constant or non-constant comparison values.
+ *
+ * 4. ScalarArrayOpExpr ("indexkey op ANY (array-expression)"). For these,
+ * we create a ScanKey with everything filled in except the comparison value,
+ * and set up an IndexArrayKeyInfo struct to drive processing of the qual.
+ * (Note that we treat all array-expressions as requiring runtime evaluation,
+ * even if they happen to be constants.)
+ *
+ * 5. NullTest ("indexkey IS NULL"). We just fill in the ScanKey properly.
+ *
+ * Input params are:
+ *
+ * planstate: executor state node we are working for
+ * index: the index we are building scan keys for
+ * quals: indexquals expressions
+ * strategies: associated operator strategy numbers
+ * subtypes: associated operator subtype OIDs
+ *
+ * (Any elements of the strategies and subtypes lists that correspond to
+ * RowCompareExpr quals are not used here; instead we look up the info
+ * afresh.)
+ *
+ * Output params are:
+ *
+ * *scanKeys: receives ptr to array of ScanKeys
+ * *numScanKeys: receives number of scankeys
+ * *runtimeKeys: receives ptr to array of IndexRuntimeKeyInfos, or NULL if none
+ * *numRuntimeKeys: receives number of runtime keys
+ * *arrayKeys: receives ptr to array of IndexArrayKeyInfos, or NULL if none
+ * *numArrayKeys: receives number of array keys
+ *
+ * Caller may pass NULL for arrayKeys and numArrayKeys to indicate that
+ * ScalarArrayOpExpr quals are not supported.
+ */
+void
+ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
+ List *quals, List *strategies, List *subtypes,
+ ScanKey *scanKeys, int *numScanKeys,
+ IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
+ IndexArrayKeyInfo **arrayKeys, int *numArrayKeys)
+{
+ ListCell *qual_cell;
+ ListCell *strategy_cell;
+ ListCell *subtype_cell;
+ ScanKey scan_keys;
+ IndexRuntimeKeyInfo *runtime_keys;
+ IndexArrayKeyInfo *array_keys;
+ int n_scan_keys;
+ int extra_scan_keys;
+ int n_runtime_keys;
+ int n_array_keys;
+ int j;
+
+ /*
+ * If there are any RowCompareExpr quals, we need extra ScanKey entries
+ * for them, and possibly extra runtime-key entries. Count up what's
+ * needed. (The subsidiary ScanKey arrays for the RowCompareExprs could
+ * be allocated as separate chunks, but we have to count anyway to make
+ * runtime_keys large enough, so might as well just do one palloc.)
+ */
+ n_scan_keys = list_length(quals);
+ extra_scan_keys = 0;
+ foreach(qual_cell, quals)
{
- int j;
- List *qual;
- int n_keys;
- ScanKey scan_keys;
- int *run_keys;
-
- qual = nth(i, indxqual);
- n_keys = length(qual);
- scan_keys = (n_keys <= 0) ? NULL :
- (ScanKey) palloc(n_keys * sizeof(ScanKeyData));
- run_keys = (n_keys <= 0) ? NULL :
- (int *) palloc(n_keys * sizeof(int));
-
- CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
-
- /* ----------------
- * for each opclause in the given qual,
- * convert each qual's opclause into a single scan key
- * ----------------
+ if (IsA(lfirst(qual_cell), RowCompareExpr))
+ extra_scan_keys +=
+ list_length(((RowCompareExpr *) lfirst(qual_cell))->opnos);
+ }
+ scan_keys = (ScanKey)
+ palloc((n_scan_keys + extra_scan_keys) * sizeof(ScanKeyData));
+ /* Allocate these arrays as large as they could possibly need to be */
+ runtime_keys = (IndexRuntimeKeyInfo *)
+ palloc((n_scan_keys + extra_scan_keys) * sizeof(IndexRuntimeKeyInfo));
+ array_keys = (IndexArrayKeyInfo *)
+ palloc0(n_scan_keys * sizeof(IndexArrayKeyInfo));
+ n_runtime_keys = 0;
+ n_array_keys = 0;
+
+ /*
+ * Below here, extra_scan_keys is index of first cell to use for next
+ * RowCompareExpr
+ */
+ extra_scan_keys = n_scan_keys;
+
+ /*
+ * 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_scan_keys; j++)
+ {
+ ScanKey this_scan_key = &scan_keys[j];
+ Expr *clause; /* one clause of index qual */
+ RegProcedure opfuncid; /* operator proc id used in scan */
+ StrategyNumber strategy; /* op's strategy number */
+ Oid subtype; /* op's strategy subtype */
+ Expr *leftop; /* expr on lhs of operator */
+ Expr *rightop; /* expr on rhs ... */
+ AttrNumber varattno; /* att number used in scan */
+
+ /*
+ * extract clause information from the qualification
*/
- for (j = 0; j < n_keys; j++)
+ clause = (Expr *) 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))
{
- Expr *clause; /* one part of index qual */
- Oper *op; /* operator used in scan.. */
- Node *leftop; /* expr on lhs of operator */
- Node *rightop;/* expr on rhs ... */
- bits16 flags = 0;
-
- int scanvar;/* which var identifies varattno */
- AttrNumber varattno = 0; /* att number used in scan */
- Oid opid; /* operator id used in scan */
- Datum scanvalue = 0; /* value used in scan (if const) */
-
- /* ----------------
- * extract clause information from the qualification
- * ----------------
- */
- clause = nth(j, qual);
-
- op = (Oper *) clause->oper;
- if (!IsA(op, Oper))
- elog(ERROR, "ExecInitIndexScan: op not an Oper!");
-
- opid = op->opid;
-
- /* ----------------
- * Here we figure out the contents of the index qual.
- * The usual case is (var op const) or (const op var)
- * which means we form a scan key for the attribute
- * listed in the var node and use the value of the const.
- *
- * If we don't have a const node, then it means that
- * one of the var nodes refers to the "scan" tuple and
- * is used to determine which attribute to scan, and the
- * other expression is used to calculate the value used in
- * scanning the index.
- *
- * This means our index scan's 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 then set
- * the appropriate flag in run_keys to LEFT_OP or RIGHT_OP.
- * The corresponding scan keys are recomputed at run time.
- *
- * XXX Although this code *thinks* it can handle an indexqual
- * with the indexkey on either side, in fact it cannot.
- * Indexscans only work with quals that have the indexkey on
- * the left (the planner/optimizer makes sure it never passes
- * anything else). The reason: the scankey machinery has no
- * provision for distinguishing which side of the operator is
- * the indexed attribute and which is the compared-to constant.
- * It just assumes that the attribute is on the left :-(
- *
- * I am leaving this code able to support both ways, even though
- * half of it is dead code, on the off chance that someone will
- * fix the scankey machinery someday --- tgl 8/11/99.
- * ----------------
- */
+ /* indexkey op const or indexkey op expression */
+ int flags = 0;
+ Datum scanvalue;
- scanvar = NO_OP;
+ opfuncid = ((OpExpr *) clause)->opfuncid;
- /* ----------------
- * determine information in leftop
- * ----------------
+ /*
+ * leftop should be the index key Var, possibly relabeled
*/
- leftop = (Node *) get_leftop(clause);
+ leftop = (Expr *) get_leftop(clause);
+
+ if (leftop && IsA(leftop, RelabelType))
+ leftop = ((RelabelType *) leftop)->arg;
Assert(leftop != NULL);
- if (IsA(leftop, Var) && var_is_rel((Var *) leftop))
- {
- /* ----------------
- * if the leftop is a "rel-var", then it means
- * that it is a var node which tells us which
- * attribute to use for our scan key.
- * ----------------
- */
- varattno = ((Var *) leftop)->varattno;
- scanvar = LEFT_OP;
- }
- else if (is_funcclause(leftop) &&
- var_is_rel(lfirst(((Expr *) leftop)->args)))
+ if (!(IsA(leftop, Var) &&
+ var_is_rel((Var *) leftop)))
+ elog(ERROR, "indexqual doesn't have key on left side");
+
+ varattno = ((Var *) leftop)->varattno;
+
+ /*
+ * rightop is the constant or variable comparison value
+ */
+ rightop = (Expr *) get_rightop(clause);
+
+ if (rightop && IsA(rightop, RelabelType))
+ rightop = ((RelabelType *) rightop)->arg;
+
+ Assert(rightop != NULL);
+
+ if (IsA(rightop, Const))
{
- /* ----------------
- * if the leftop is a func node then it means
- * it identifies the value to place in our scan key.
- * Since functional indices have only one attribute
- * the attno must always be set to 1.
- * ----------------
- */
- varattno = 1;
- scanvar = LEFT_OP;
+ /* OK, simple constant comparison value */
+ scanvalue = ((Const *) rightop)->constvalue;
+ if (((Const *) rightop)->constisnull)
+ flags |= SK_ISNULL;
}
- else if (IsA(leftop, Const))
+ else
{
- /* ----------------
- * if the leftop is a const node then it means
- * it identifies the value to place in our scan key.
- * ----------------
- */
- run_keys[j] = NO_OP;
- scanvalue = ((Const *) leftop)->constvalue;
+ /* Need to treat this one as a runtime key */
+ runtime_keys[n_runtime_keys].scan_key = this_scan_key;
+ runtime_keys[n_runtime_keys].key_expr =
+ ExecInitExpr(rightop, planstate);
+ n_runtime_keys++;
+ scanvalue = (Datum) 0;
}
- else if (IsA(leftop, Param))
+
+ /*
+ * initialize the scan key's fields appropriately
+ */
+ ScanKeyEntryInitialize(this_scan_key,
+ flags,
+ varattno, /* attribute number to scan */
+ strategy, /* op's strategy */
+ subtype, /* strategy subtype */
+ opfuncid, /* reg proc to use */
+ scanvalue); /* constant */
+ }
+ else if (IsA(clause, RowCompareExpr))
+ {
+ /* (indexkey, indexkey, ...) op (expression, expression, ...) */
+ RowCompareExpr *rc = (RowCompareExpr *) clause;
+ ListCell *largs_cell = list_head(rc->largs);
+ ListCell *rargs_cell = list_head(rc->rargs);
+ ListCell *opnos_cell = list_head(rc->opnos);
+ ScanKey first_sub_key = &scan_keys[extra_scan_keys];
+
+ /* Scan RowCompare columns and generate subsidiary ScanKey items */
+ while (opnos_cell != NULL)
{
- bool isnull;
+ ScanKey this_sub_key = &scan_keys[extra_scan_keys];
+ int flags = SK_ROW_MEMBER;
+ Datum scanvalue;
+ Oid opno;
+ Oid opfamily;
+ int op_strategy;
+ Oid op_lefttype;
+ Oid op_righttype;
+ bool op_recheck;
- /* ----------------
- * if the leftop is a Param node then it means
- * it identifies the value to place in our scan key.
- * ----------------
+ /*
+ * leftop should be the index key Var, possibly relabeled
*/
+ leftop = (Expr *) lfirst(largs_cell);
+ largs_cell = lnext(largs_cell);
- /* Life was so easy before ... subselects */
- if (((Param *) leftop)->paramkind == PARAM_EXEC)
- {
- have_runtime_keys = true;
- run_keys[j] = LEFT_OP;
- execParam = lappendi(execParam, ((Param *) leftop)->paramid);
- }
- else
- {
- scanvalue = ExecEvalParam((Param *) leftop,
- scanstate->cstate.cs_ExprContext,
- &isnull);
- if (isnull)
- flags |= SK_ISNULL;
+ if (leftop && IsA(leftop, RelabelType))
+ leftop = ((RelabelType *) leftop)->arg;
- run_keys[j] = NO_OP;
- }
- }
- else
- {
- /* ----------------
- * otherwise, the leftop contains information usable
- * at runtime to figure out the value to place in our
- * scan key.
- * ----------------
- */
- have_runtime_keys = true;
- run_keys[j] = LEFT_OP;
- scanvalue = Int32GetDatum((int32) true);
- }
+ Assert(leftop != NULL);
- /* ----------------
- * now determine information in rightop
- * ----------------
- */
- rightop = (Node *) get_rightop(clause);
+ if (!(IsA(leftop, Var) &&
+ var_is_rel((Var *) leftop)))
+ elog(ERROR, "indexqual doesn't have key on left side");
- Assert(rightop != NULL);
+ varattno = ((Var *) leftop)->varattno;
- if (IsA(rightop, Var) && var_is_rel((Var *) rightop))
- {
- /* ----------------
- * here we make sure only one op identifies the
- * scan-attribute...
- * ----------------
- */
- if (scanvar == LEFT_OP)
- elog(ERROR, "ExecInitIndexScan: %s",
- "both left and right op's are rel-vars");
-
- /* ----------------
- * if the rightop is a "rel-var", then it means
- * that it is a var node which tells us which
- * attribute to use for our scan key.
- * ----------------
- */
- varattno = ((Var *) rightop)->varattno;
- scanvar = RIGHT_OP;
- }
- else if (is_funcclause(rightop) &&
- var_is_rel(lfirst(((Expr *) rightop)->args)))
- {
- /* ----------------
- * if the rightop is a func node then it means
- * it identifies the value to place in our scan key.
- * Since functional indices have only one attribute
- * the attno must always be set to 1.
- * ----------------
+ /*
+ * rightop is the constant or variable comparison value
*/
- if (scanvar == LEFT_OP)
- elog(ERROR, "ExecInitIndexScan: %s",
- "both left and right ops are rel-vars");
+ rightop = (Expr *) lfirst(rargs_cell);
+ rargs_cell = lnext(rargs_cell);
- varattno = 1;
- scanvar = RIGHT_OP;
- }
- else if (IsA(rightop, Const))
- {
- /* ----------------
- * if the rightop is a const node then it means
- * it identifies the value to place in our scan key.
- * ----------------
- */
- run_keys[j] = NO_OP;
- scanvalue = ((Const *) rightop)->constvalue;
- }
- else if (IsA(rightop, Param))
- {
- bool isnull;
+ if (rightop && IsA(rightop, RelabelType))
+ rightop = ((RelabelType *) rightop)->arg;
- /* ----------------
- * if the rightop is a Param node then it means
- * it identifies the value to place in our scan key.
- * ----------------
- */
+ Assert(rightop != NULL);
- /* Life was so easy before ... subselects */
- if (((Param *) rightop)->paramkind == PARAM_EXEC)
+ if (IsA(rightop, Const))
{
- have_runtime_keys = true;
- run_keys[j] = RIGHT_OP;
- execParam = lappendi(execParam, ((Param *) rightop)->paramid);
+ /* OK, simple constant comparison value */
+ scanvalue = ((Const *) rightop)->constvalue;
+ if (((Const *) rightop)->constisnull)
+ flags |= SK_ISNULL;
}
else
{
- scanvalue = ExecEvalParam((Param *) rightop,
- scanstate->cstate.cs_ExprContext,
- &isnull);
- if (isnull)
- flags |= SK_ISNULL;
-
- run_keys[j] = NO_OP;
+ /* Need to treat this one as a runtime key */
+ runtime_keys[n_runtime_keys].scan_key = this_sub_key;
+ runtime_keys[n_runtime_keys].key_expr =
+ ExecInitExpr(rightop, planstate);
+ n_runtime_keys++;
+ scanvalue = (Datum) 0;
}
- }
- else
- {
- /* ----------------
- * otherwise, the rightop contains information usable
- * at runtime to figure out the value to place in our
- * scan key.
- * ----------------
+
+ /*
+ * We have to look up the operator's associated btree support
+ * function
+ */
+ opno = lfirst_oid(opnos_cell);
+ opnos_cell = lnext(opnos_cell);
+
+ if (index->rd_rel->relam != BTREE_AM_OID ||
+ varattno < 1 || varattno > index->rd_index->indnatts)
+ elog(ERROR, "bogus RowCompare index qualification");
+ opfamily = index->rd_opfamily[varattno - 1];
+
+ get_op_opfamily_properties(opno, opfamily,
+ &op_strategy,
+ &op_lefttype,
+ &op_righttype,
+ &op_recheck);
+
+ if (op_strategy != rc->rctype)
+ elog(ERROR, "RowCompare index qualification contains wrong operator");
+
+ opfuncid = get_opfamily_proc(opfamily,
+ op_lefttype,
+ op_righttype,
+ BTORDER_PROC);
+
+ /*
+ * initialize the subsidiary scan key's fields appropriately
*/
- have_runtime_keys = true;
- run_keys[j] = RIGHT_OP;
- scanvalue = Int32GetDatum((int32) true);
+ ScanKeyEntryInitialize(this_sub_key,
+ flags,
+ varattno, /* attribute number */
+ op_strategy, /* op's strategy */
+ op_righttype, /* strategy subtype */
+ opfuncid, /* reg proc to use */
+ scanvalue); /* constant */
+ extra_scan_keys++;
}
- /* ----------------
- * now check that at least one op tells us the scan
- * attribute...
- * ----------------
- */
- if (scanvar == NO_OP)
- elog(ERROR, "ExecInitIndexScan: %s",
- "neither leftop nor rightop refer to scan relation");
+ /* Mark the last subsidiary scankey correctly */
+ scan_keys[extra_scan_keys - 1].sk_flags |= SK_ROW_END;
- /* ----------------
- * initialize the scan key's fields appropriately
- * ----------------
+ /*
+ * We don't use ScanKeyEntryInitialize for the header because it
+ * isn't going to contain a valid sk_func pointer.
*/
- ScanKeyEntryInitialize(&scan_keys[j],
- flags,
- varattno, /* attribute number to
- * scan */
- (RegProcedure) opid, /* reg proc to use */
- (Datum) scanvalue); /* constant */
+ MemSet(this_scan_key, 0, sizeof(ScanKeyData));
+ this_scan_key->sk_flags = SK_ROW_HEADER;
+ this_scan_key->sk_attno = first_sub_key->sk_attno;
+ this_scan_key->sk_strategy = rc->rctype;
+ /* sk_subtype, sk_func not used in a header */
+ this_scan_key->sk_argument = PointerGetDatum(first_sub_key);
}
+ else if (IsA(clause, ScalarArrayOpExpr))
+ {
+ /* indexkey op ANY (array-expression) */
+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
- /* ----------------
- * store the key information into our array.
- * ----------------
- */
- numScanKeys[i] = n_keys;
- scanKeys[i] = scan_keys;
- runtimeKeyInfo[i] = (Pointer) run_keys;
- }
+ Assert(saop->useOr);
+ opfuncid = saop->opfuncid;
- indexstate->iss_NumIndices = numIndices;
- if (ScanDirectionIsBackward(node->indxorderdir))
- indexPtr = numIndices;
- indexstate->iss_IndexPtr = indexPtr;
- indexstate->iss_ScanKeys = scanKeys;
- indexstate->iss_NumScanKeys = numScanKeys;
-
- /* ----------------
- * If all of our keys have the form (op var 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 (have_runtime_keys)
- indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo;
- else
- indexstate->iss_RuntimeKeyInfo = NULL;
+ /*
+ * leftop should be the index key Var, possibly relabeled
+ */
+ leftop = (Expr *) linitial(saop->args);
- /* ----------------
- * get the range table and direction information
- * from the execution state (these are needed to
- * open the relations).
- * ----------------
- */
- rangeTable = estate->es_range_table;
- direction = estate->es_direction;
+ if (leftop && IsA(leftop, RelabelType))
+ leftop = ((RelabelType *) leftop)->arg;
- /* ----------------
- * open the base relation
- * ----------------
- */
- relid = node->scan.scanrelid;
- rtentry = rt_fetch(relid, rangeTable);
- reloid = rtentry->relid;
-
- ExecOpenScanR(reloid, /* relation */
- 0, /* nkeys */
- (ScanKey) NULL, /* scan key */
- 0, /* is index */
- direction, /* scan direction */
- estate->es_snapshot, /* */
- ¤tRelation, /* return: rel desc */
- (Pointer *) ¤tScanDesc); /* return: scan desc */
-
- scanstate->css_currentRelation = currentRelation;
- scanstate->css_currentScanDesc = currentScanDesc;
-
-
- /* ----------------
- * get the scan type from the relation descriptor.
- * ----------------
- */
- ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
- ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
+ Assert(leftop != NULL);
- /* ----------------
- * index scans don't have subtrees..
- * ----------------
- */
-/* scanstate->ss_ProcOuterFlag = false; */
+ if (!(IsA(leftop, Var) &&
+ var_is_rel((Var *) leftop)))
+ elog(ERROR, "indexqual doesn't have key on left side");
- /* ----------------
- * open the index relations and initialize
- * relation and scan descriptors.
- * ----------------
- */
- for (i = 0; i < numIndices; i++)
- {
- Oid indexOid;
+ varattno = ((Var *) leftop)->varattno;
+
+ /*
+ * rightop is the constant or variable array value
+ */
+ rightop = (Expr *) lsecond(saop->args);
+
+ if (rightop && IsA(rightop, RelabelType))
+ rightop = ((RelabelType *) rightop)->arg;
- indexOid = (Oid) nthi(i, indxid);
+ Assert(rightop != NULL);
+
+ array_keys[n_array_keys].scan_key = this_scan_key;
+ array_keys[n_array_keys].array_expr =
+ ExecInitExpr(rightop, planstate);
+ /* the remaining fields were zeroed by palloc0 */
+ n_array_keys++;
- if (indexOid != 0)
+ /*
+ * initialize the scan key's fields appropriately
+ */
+ ScanKeyEntryInitialize(this_scan_key,
+ 0, /* flags */
+ varattno, /* attribute number to scan */
+ strategy, /* op's strategy */
+ subtype, /* strategy subtype */
+ opfuncid, /* reg proc to use */
+ (Datum) 0); /* constant */
+ }
+ else if (IsA(clause, NullTest))
{
- ExecOpenScanR(indexOid, /* relation */
- numScanKeys[i], /* nkeys */
- scanKeys[i], /* scan key */
- true, /* is index */
- direction, /* scan direction */
- estate->es_snapshot,
- &(relationDescs[i]), /* return: rel desc */
- (Pointer *) &(scanDescs[i]));
- /* return: scan desc */
+ /* indexkey IS NULL */
+ Assert(((NullTest *) clause)->nulltesttype == IS_NULL);
+
+ /*
+ * argument should be the index key Var, possibly relabeled
+ */
+ leftop = ((NullTest *) clause)->arg;
+
+ if (leftop && IsA(leftop, RelabelType))
+ leftop = ((RelabelType *) leftop)->arg;
+
+ Assert(leftop != NULL);
+
+ if (!(IsA(leftop, Var) &&
+ var_is_rel((Var *) leftop)))
+ elog(ERROR, "NullTest indexqual has wrong key");
+
+ varattno = ((Var *) leftop)->varattno;
+
+ /*
+ * initialize the scan key's fields appropriately
+ */
+ ScanKeyEntryInitialize(this_scan_key,
+ SK_ISNULL | SK_SEARCHNULL,
+ varattno, /* attribute number to scan */
+ strategy, /* op's strategy */
+ subtype, /* strategy subtype */
+ InvalidOid, /* no reg proc for this */
+ (Datum) 0); /* constant */
}
+ else
+ elog(ERROR, "unsupported indexqual type: %d",
+ (int) nodeTag(clause));
}
- indexstate->iss_RelationDescs = relationDescs;
- indexstate->iss_ScanDescs = scanDescs;
-
- indexstate->cstate.cs_TupFromTlist = false;
+ /* Get rid of any unused arrays */
+ if (n_runtime_keys == 0)
+ {
+ pfree(runtime_keys);
+ runtime_keys = NULL;
+ }
+ if (n_array_keys == 0)
+ {
+ pfree(array_keys);
+ array_keys = NULL;
+ }
/*
- * if there are some PARAM_EXEC in skankeys then force index rescan on
- * first scan.
- */
- ((Plan *) node)->chgParam = execParam;
-
- /* ----------------
- * all done.
- * ----------------
+ * Return info to our caller.
*/
- return TRUE;
+ *scanKeys = scan_keys;
+ *numScanKeys = n_scan_keys;
+ *runtimeKeys = runtime_keys;
+ *numRuntimeKeys = n_runtime_keys;
+ if (arrayKeys)
+ {
+ *arrayKeys = array_keys;
+ *numArrayKeys = n_array_keys;
+ }
+ else if (n_array_keys != 0)
+ elog(ERROR, "ScalarArrayOpExpr index qual found where not allowed");
}
int
ExecCountSlotsIndexScan(IndexScan *node)
{
return ExecCountSlotsNode(outerPlan((Plan *) node)) +
- ExecCountSlotsNode(innerPlan((Plan *) node)) + INDEXSCAN_NSLOTS;
+ ExecCountSlotsNode(innerPlan((Plan *) node)) + INDEXSCAN_NSLOTS;
}