]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/nodeIndexscan.c
Create hooks to let a loadable plugin monitor (or even replace) the planner
[postgresql] / src / backend / executor / nodeIndexscan.c
index 6ed14e0ad9adf00bc978da1af43178772fbf927e..8c22e3ade0ec42229934d614a0d688c4e7997251 100644 (file)
@@ -1,20 +1,19 @@
 /*-------------------------------------------------------------------------
  *
  * 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.
@@ -121,165 +81,70 @@ IndexNext(IndexScan *node)
         * 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);
 }
 
 /* ----------------------------------------------------------------
@@ -288,822 +153,889 @@ ExecIndexScan(IndexScan *node)
  *             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,  /* */
-                                 &currentRelation,             /* return: rel desc */
-                                 (Pointer *) &currentScanDesc);                /* 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;
 }