]> granicus.if.org Git - postgresql/commitdiff
Remove support for OR'd indexscans internal to a single IndexScan plan
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 25 Apr 2005 01:30:14 +0000 (01:30 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 25 Apr 2005 01:30:14 +0000 (01:30 +0000)
node, as this behavior is now better done as a bitmap OR indexscan.
This allows considerable simplification in nodeIndexscan.c itself as
well as several planner modules concerned with indexscan plan generation.
Also we can improve the sharing of code between regular and bitmap
indexscans, since they are now working with nigh-identical Plan nodes.

21 files changed:
src/backend/commands/explain.c
src/backend/executor/nodeBitmapIndexscan.c
src/backend/executor/nodeIndexscan.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/orindxpath.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/util/pathnode.c
src/backend/optimizer/util/restrictinfo.c
src/backend/utils/adt/selfuncs.c
src/include/executor/nodeIndexscan.h
src/include/nodes/execnodes.h
src/include/nodes/plannodes.h
src/include/nodes/relation.h
src/include/optimizer/paths.h
src/include/optimizer/planmain.h
src/include/optimizer/restrictinfo.h

index 7e8d2db677b51f7b74f8528d6d37c0e5a5ccb1d4..d3dfe9edf7e53510d90dc56041a46728510d3d43 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.134 2005/04/22 21:58:31 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.135 2005/04/25 01:30:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,7 +52,7 @@ static void explain_outNode(StringInfo str,
                                Plan *plan, PlanState *planstate,
                                Plan *outer_plan,
                                int indent, ExplainState *es);
-static void show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
+static void show_scan_qual(List *qual, const char *qlabel,
                           int scanrelid, Plan *outer_plan,
                           StringInfo str, int indent, ExplainState *es);
 static void show_upper_qual(List *qual, const char *qlabel,
@@ -62,7 +62,6 @@ static void show_upper_qual(List *qual, const char *qlabel,
 static void show_sort_keys(List *tlist, int nkeys, AttrNumber *keycols,
                           const char *qlabel,
                           StringInfo str, int indent, ExplainState *es);
-static Node *make_ors_ands_explicit(List *orclauses);
 
 /*
  * ExplainQuery -
@@ -405,7 +404,6 @@ explain_outNode(StringInfo str,
                                Plan *outer_plan,
                                int indent, ExplainState *es)
 {
-       ListCell   *l;
        char       *pname;
        int                     i;
 
@@ -583,19 +581,10 @@ explain_outNode(StringInfo str,
        switch (nodeTag(plan))
        {
                case T_IndexScan:
-                       if (ScanDirectionIsBackward(((IndexScan *) plan)->indxorderdir))
+                       if (ScanDirectionIsBackward(((IndexScan *) plan)->indexorderdir))
                                appendStringInfoString(str, " Backward");
-                       appendStringInfoString(str, " using ");
-                       i = 0;
-                       foreach(l, ((IndexScan *) plan)->indxid)
-                       {
-                               char       *indname;
-
-                               indname = get_rel_name(lfirst_oid(l));
-                               appendStringInfo(str, "%s%s",
-                                                                (++i > 1) ? ", " : "",
-                                                                quote_identifier(indname));
-                       }
+                       appendStringInfo(str, " using %s",
+                                                        quote_identifier(get_rel_name(((IndexScan *) plan)->indexid)));
                        /* FALL THRU */
                case T_SeqScan:
                case T_BitmapHeapScan:
@@ -621,7 +610,7 @@ explain_outNode(StringInfo str,
                        break;
                case T_BitmapIndexScan:
                        appendStringInfo(str, " on %s",
-                                                        quote_identifier(get_rel_name(((BitmapIndexScan *) plan)->indxid)));
+                                                        quote_identifier(get_rel_name(((BitmapIndexScan *) plan)->indexid)));
                        break;
                case T_SubqueryScan:
                        if (((Scan *) plan)->scanrelid > 0)
@@ -702,19 +691,19 @@ explain_outNode(StringInfo str,
        switch (nodeTag(plan))
        {
                case T_IndexScan:
-                       show_scan_qual(((IndexScan *) plan)->indxqualorig, true,
+                       show_scan_qual(((IndexScan *) plan)->indexqualorig,
                                                   "Index Cond",
                                                   ((Scan *) plan)->scanrelid,
                                                   outer_plan,
                                                   str, indent, es);
-                       show_scan_qual(plan->qual, false,
+                       show_scan_qual(plan->qual,
                                                   "Filter",
                                                   ((Scan *) plan)->scanrelid,
                                                   outer_plan,
                                                   str, indent, es);
                        break;
                case T_BitmapIndexScan:
-                       show_scan_qual(((BitmapIndexScan *) plan)->indxqualorig, false,
+                       show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
                                                   "Index Cond",
                                                   ((Scan *) plan)->scanrelid,
                                                   outer_plan,
@@ -722,7 +711,7 @@ explain_outNode(StringInfo str,
                        break;
                case T_BitmapHeapScan:
                        /* XXX do we want to show this in production? */
-                       show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig, false,
+                       show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
                                                   "Recheck Cond",
                                                   ((Scan *) plan)->scanrelid,
                                                   outer_plan,
@@ -732,7 +721,7 @@ explain_outNode(StringInfo str,
                case T_TidScan:
                case T_SubqueryScan:
                case T_FunctionScan:
-                       show_scan_qual(plan->qual, false,
+                       show_scan_qual(plan->qual,
                                                   "Filter",
                                                   ((Scan *) plan)->scanrelid,
                                                   outer_plan,
@@ -997,7 +986,7 @@ explain_outNode(StringInfo str,
  * Show a qualifier expression for a scan plan node
  */
 static void
-show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
+show_scan_qual(List *qual, const char *qlabel,
                           int scanrelid, Plan *outer_plan,
                           StringInfo str, int indent, ExplainState *es)
 {
@@ -1012,14 +1001,9 @@ show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
        /* No work if empty qual */
        if (qual == NIL)
                return;
-       if (is_or_qual && list_length(qual) == 1 && linitial(qual) == NIL)
-               return;
 
-       /* Fix qual --- indexqual requires different processing */
-       if (is_or_qual)
-               node = make_ors_ands_explicit(qual);
-       else
-               node = (Node *) make_ands_explicit(qual);
+       /* Convert AND list to explicit AND */
+       node = (Node *) make_ands_explicit(qual);
 
        /* Generate deparse context */
        Assert(scanrelid > 0 && scanrelid <= list_length(es->rtable));
@@ -1177,26 +1161,3 @@ show_sort_keys(List *tlist, int nkeys, AttrNumber *keycols,
 
        appendStringInfo(str, "\n");
 }
-
-/*
- * Indexscan qual lists have an implicit OR-of-ANDs structure. Make it
- * explicit so deparsing works properly.
- */
-static Node *
-make_ors_ands_explicit(List *orclauses)
-{
-       if (orclauses == NIL)
-               return NULL;                    /* probably can't happen */
-       else if (list_length(orclauses) == 1)
-               return (Node *) make_ands_explicit(linitial(orclauses));
-       else
-       {
-               List       *args = NIL;
-               ListCell   *orptr;
-
-               foreach(orptr, orclauses)
-                       args = lappend(args, make_ands_explicit(lfirst(orptr)));
-
-               return (Node *) make_orclause(args);
-       }
-}
index 73acd0c95205ff90200d898a9efbeb9f8df75da4..19915a605990373b68c028562639321a28615d2a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.6 2005/04/24 18:16:38 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.7 2005/04/25 01:30:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "access/genam.h"
-#include "access/heapam.h"
 #include "executor/execdebug.h"
 #include "executor/instrument.h"
 #include "executor/nodeBitmapIndexscan.h"
+#include "executor/nodeIndexscan.h"
 #include "miscadmin.h"
-#include "nodes/nodeFuncs.h"
-#include "optimizer/clauses.h"
-#include "parser/parsetree.h"
 
 
 /* ----------------------------------------------------------------
@@ -41,7 +38,7 @@ MultiExecBitmapIndexScan(BitmapIndexScanState *node)
 {
 #define MAX_TIDS       1024
        TIDBitmap  *tbm;
-       Oid                     indxid;
+       Oid                     indexid;
        Relation        indexRelation;
        IndexScanDesc scandesc;
        ItemPointerData tids[MAX_TIDS];
@@ -70,8 +67,8 @@ MultiExecBitmapIndexScan(BitmapIndexScanState *node)
         * descriptors.  Note we acquire no locks here; the index machinery
         * does its own locks and unlocks.
         */
-       indxid = ((BitmapIndexScan *) node->ss.ps.plan)->indxid;
-       indexRelation = index_open(indxid);
+       indexid = ((BitmapIndexScan *) node->ss.ps.plan)->indexid;
+       indexRelation = index_open(indexid);
        scandesc = index_beginscan_multi(indexRelation,
                                                                         node->ss.ps.state->es_snapshot,
                                                                         node->biss_NumScanKeys,
@@ -166,47 +163,10 @@ ExecBitmapIndexReScan(BitmapIndexScanState *node, ExprContext *exprCtxt)
         */
        if (runtimeKeyInfo)
        {
-               int                     n_keys;
-               ScanKey         scan_keys;
-               ExprState **run_keys;
-               int                     j;
-
-               n_keys = node->biss_NumScanKeys;
-               scan_keys = node->biss_ScanKeys;
-               run_keys = runtimeKeyInfo;
-
-               for (j = 0; j < n_keys; j++)
-               {
-                       /*
-                        * If we have a run-time key, then extract the run-time
-                        * expression and evaluate it with respect to the current
-                        * outer tuple.  We then stick the result into the scan
-                        * key.
-                        *
-                        * Note: the result of the eval could be a pass-by-ref value
-                        * that's stored in the outer scan's tuple, not in
-                        * econtext->ecxt_per_tuple_memory.  We assume that the
-                        * outer tuple will stay put throughout our scan.  If this
-                        * is wrong, we could copy the result into our context
-                        * explicitly, but I think that's not necessary...
-                        */
-                       if (run_keys[j] != NULL)
-                       {
-                               Datum           scanvalue;
-                               bool            isNull;
-
-                               scanvalue = ExecEvalExprSwitchContext(run_keys[j],
-                                                                                                         econtext,
-                                                                                                         &isNull,
-                                                                                                         NULL);
-                               scan_keys[j].sk_argument = scanvalue;
-                               if (isNull)
-                                       scan_keys[j].sk_flags |= SK_ISNULL;
-                               else
-                                       scan_keys[j].sk_flags &= ~SK_ISNULL;
-                       }
-               }
-
+               ExecIndexEvalRuntimeKeys(econtext,
+                                                                runtimeKeyInfo,
+                                                                node->biss_ScanKeys,
+                                                                node->biss_NumScanKeys);
                node->biss_RuntimeKeysReady = true;
        }
 }
@@ -237,6 +197,8 @@ BitmapIndexScanState *
 ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate)
 {
        BitmapIndexScanState *indexstate;
+       ScanKey         scanKeys;
+       int                     numScanKeys;
        ExprState **runtimeKeyInfo;
        bool            have_runtime_keys;
 
@@ -262,7 +224,7 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate)
         *
         * We don't need to initialize targetlist or qual since neither are used.
         *
-        * Note: we don't initialize all of the indxqual expression, only the
+        * Note: we don't initialize all of the indexqual expression, only the
         * sub-parts corresponding to runtime keys (see below).
         */
 
@@ -271,169 +233,28 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate)
        /*
         * Initialize index-specific scan state
         */
-       indexstate->biss_ScanKeys = NULL;
-       indexstate->biss_NumScanKeys = 0;
-       indexstate->biss_RuntimeKeyInfo = NULL;
-       indexstate->biss_RuntimeContext = NULL;
        indexstate->biss_RuntimeKeysReady = false;
 
        CXT1_printf("ExecInitBitmapIndexScan: context is %d\n", CurrentMemoryContext);
 
-       /*
-        * initialize space for runtime key info (may not be needed)
-        */
-       have_runtime_keys = false;
-
        /*
         * build the index scan keys from the index qualification
         */
-       {
-               List       *quals;
-               List       *strategies;
-               List       *subtypes;
-               ListCell   *qual_cell;
-               ListCell   *strategy_cell;
-               ListCell   *subtype_cell;
-               int                     n_keys;
-               ScanKey         scan_keys;
-               ExprState **run_keys;
-               int                     j;
-
-               quals = node->indxqual;
-               strategies = node->indxstrategy;
-               subtypes = node->indxsubtype;
-               n_keys = list_length(quals);
-               scan_keys = (n_keys <= 0) ? NULL :
-                       (ScanKey) palloc(n_keys * sizeof(ScanKeyData));
-               run_keys = (n_keys <= 0) ? NULL :
-                       (ExprState **) palloc(n_keys * sizeof(ExprState *));
-
-               /*
-                * for each opclause in the given qual, convert each qual's
-                * opclause into a single scan key
-                */
-               qual_cell = list_head(quals);
-               strategy_cell = list_head(strategies);
-               subtype_cell = list_head(subtypes);
-               for (j = 0; j < n_keys; j++)
-               {
-                       OpExpr     *clause; /* one clause of index qual */
-                       Expr       *leftop; /* expr on lhs of operator */
-                       Expr       *rightop;    /* expr on rhs ... */
-                       int                     flags = 0;
-                       AttrNumber      varattno;               /* att number used in scan */
-                       StrategyNumber strategy;        /* op's strategy number */
-                       Oid                     subtype;        /* op's strategy subtype */
-                       RegProcedure opfuncid;          /* operator proc id used in scan */
-                       Datum           scanvalue;              /* value used in scan (if const) */
-
-                       /*
-                        * extract clause information from the qualification
-                        */
-                       clause = (OpExpr *) lfirst(qual_cell);
-                       qual_cell = lnext(qual_cell);
-                       strategy = lfirst_int(strategy_cell);
-                       strategy_cell = lnext(strategy_cell);
-                       subtype = lfirst_oid(subtype_cell);
-                       subtype_cell = lnext(subtype_cell);
-
-                       if (!IsA(clause, OpExpr))
-                               elog(ERROR, "indxqual is not an OpExpr");
-
-                       opfuncid = clause->opfuncid;
-
-                       /*
-                        * Here we figure out the contents of the index qual. The
-                        * usual case is (var op const) which means we form a scan key
-                        * for the attribute listed in the var node and use the value
-                        * of the const as comparison data.
-                        *
-                        * If we don't have a const node, it means our scan key is a
-                        * function of information obtained during the execution of
-                        * the plan, in which case we need to recalculate the index
-                        * scan key at run time.  Hence, we set have_runtime_keys to
-                        * true and place the appropriate subexpression in run_keys.
-                        * The corresponding scan key values are recomputed at run
-                        * time.
-                        */
-                       run_keys[j] = NULL;
-
-                       /*
-                        * determine information in leftop
-                        */
-                       leftop = (Expr *) get_leftop((Expr *) clause);
-
-                       if (leftop && IsA(leftop, RelabelType))
-                               leftop = ((RelabelType *) leftop)->arg;
-
-                       Assert(leftop != NULL);
-
-                       if (!(IsA(leftop, Var) &&
-                                 var_is_rel((Var *) leftop)))
-                               elog(ERROR, "indxqual doesn't have key on left side");
-
-                       varattno = ((Var *) leftop)->varattno;
-
-                       /*
-                        * now determine information in rightop
-                        */
-                       rightop = (Expr *) get_rightop((Expr *) clause);
-
-                       if (rightop && IsA(rightop, RelabelType))
-                               rightop = ((RelabelType *) rightop)->arg;
-
-                       Assert(rightop != NULL);
-
-                       if (IsA(rightop, Const))
-                       {
-                               /*
-                                * if the rightop is a const node then it means it
-                                * identifies the value to place in our scan key.
-                                */
-                               scanvalue = ((Const *) rightop)->constvalue;
-                               if (((Const *) rightop)->constisnull)
-                                       flags |= SK_ISNULL;
-                       }
-                       else
-                       {
-                               /*
-                                * otherwise, the rightop contains an expression evaluable
-                                * at runtime to figure out the value to place in our scan
-                                * key.
-                                */
-                               have_runtime_keys = true;
-                               run_keys[j] = ExecInitExpr(rightop, (PlanState *) indexstate);
-                               scanvalue = (Datum) 0;
-                       }
-
-                       /*
-                        * initialize the scan key's fields appropriately
-                        */
-                       ScanKeyEntryInitialize(&scan_keys[j],
-                                                                  flags,
-                                                                  varattno,    /* attribute number to
-                                                                                                * scan */
-                                                                  strategy,    /* op's strategy */
-                                                                  subtype,             /* strategy subtype */
-                                                                  opfuncid,    /* reg proc to use */
-                                                                  scanvalue);  /* constant */
-               }
-
-               /*
-                * store the key information into the node.
-                */
-               indexstate->biss_NumScanKeys = n_keys;
-               indexstate->biss_ScanKeys = scan_keys;
-               runtimeKeyInfo = run_keys;
-       }
+       have_runtime_keys =
+               ExecIndexBuildScanKeys((PlanState *) indexstate,
+                                                          node->indexqual,
+                                                          node->indexstrategy,
+                                                          node->indexsubtype,
+                                                          &runtimeKeyInfo,
+                                                          &scanKeys,
+                                                          &numScanKeys);
+
+       indexstate->biss_RuntimeKeyInfo = runtimeKeyInfo;
+       indexstate->biss_ScanKeys = scanKeys;
+       indexstate->biss_NumScanKeys = numScanKeys;
 
        /*
-        * If all of our keys have the form (var op const), then we have no
-        * runtime keys so we store NULL in the runtime key info. Otherwise
-        * runtime key info contains an array of pointers to runtime key
-        * expressions.
-        *
-        * If we do have runtime keys, we need an ExprContext to evaluate them.
+        * If we have runtime keys, we need an ExprContext to evaluate them.
         * We could just create a "standard" plan node exprcontext, but to
         * keep the code looking similar to nodeIndexscan.c, it seems better
         * to stick with the approach of using a separate ExprContext.
@@ -443,17 +264,12 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate)
                ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
 
                ExecAssignExprContext(estate, &indexstate->ss.ps);
-               indexstate->biss_RuntimeKeyInfo = runtimeKeyInfo;
                indexstate->biss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
                indexstate->ss.ps.ps_ExprContext = stdecontext;
        }
        else
        {
-               indexstate->biss_RuntimeKeyInfo = NULL;
                indexstate->biss_RuntimeContext = NULL;
-               /* Get rid of the speculatively-allocated flag array, too */
-               if (runtimeKeyInfo)
-                       pfree(runtimeKeyInfo);
        }
 
        /* We don't keep the table or index open across calls */
index 27cc29a62b4ebfbed541b4e6a7147447bb6d6c26..7136ec903c7ef195cd8ab2400fc866a0bc47f961 100644 (file)
@@ -1,14 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * nodeIndexscan.c
- *       Routines to support indexes and indexed scans of relations
+ *       Routines to support indexed scans of relations
  *
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.101 2005/04/23 21:32:34 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.102 2005/04/25 01:30:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "parser/parsetree.h"
 
 
-/*
- * In a multiple-index plan, we must take care to return any given tuple
- * only once, even if it matches conditions of several index scans.  Our
- * preferred way to do this is to record already-returned tuples in a hash
- * table (using the TID as unique identifier). However, in a very large
- * scan this could conceivably run out of memory.  We limit the hash table
- * to no more than work_mem KB; if it grows past that, we fall back to the
- * pre-7.4 technique: evaluate the prior-scan index quals again for each
- * tuple (which is space-efficient, but slow).
- *
- * When scanning backwards, we use scannum to determine when to emit the
- * tuple --- we have to re-emit a tuple in the same scan as it was first
- * encountered.
- *
- * Note: this code would break if the planner were ever to create a multiple
- * index plan with overall backwards direction, because the hashtable code
- * will emit a tuple the first time it is encountered (which would be the
- * highest scan in which it matches the index), but the evaluate-the-quals
- * code will emit a tuple in the lowest-numbered scan in which it's valid.
- * This could be fixed at need by making the evaluate-the-quals case more
- * complex.  Currently the planner will never create such a plan (since it
- * considers multi-index plans unordered anyway), so there's no need for
- * more complexity.
- */
-typedef struct
-{
-       /* tid is the hash key and so must be first! */
-       ItemPointerData tid;            /* TID of a tuple we've returned */
-       int                     scannum;                /* number of scan we returned it in */
-} DupHashTabEntry;
-
-
 static TupleTableSlot *IndexNext(IndexScanState *node);
-static void create_duphash(IndexScanState *node);
 
 
 /* ----------------------------------------------------------------
  *             IndexNext
  *
  *             Retrieve a tuple from the IndexScan node's currentRelation
- *             using the indices in the IndexScanState information.
- *
- *             note: the old code mentions 'Primary indices'.  to my knowledge
- *             we only support a single secondary index. -cim 9/11/89
- *
- * old comments:
- *             retrieve a tuple from relation using the indices given.
- *             The indices are used in the order they appear in 'indices'.
- *             The indices may be primary or secondary indices:
- *               * primary index --    scan the relation 'relID' using keys supplied.
- *               * secondary index --  scan the index relation to get the 'tid' for
- *                                                             a tuple in the relation 'relID'.
- *             If the current index(pointed by 'indexPtr') fails to return a
- *             tuple, the next index in the indices is used.
- *
- *               bug fix so that it should retrieve on a null scan key.
+ *             using the index specified in the IndexScanState information.
  * ----------------------------------------------------------------
  */
 static TupleTableSlot *
@@ -98,32 +50,25 @@ IndexNext(IndexScanState *node)
        EState     *estate;
        ExprContext *econtext;
        ScanDirection direction;
-       IndexScanDescPtr scanDescs;
-       List      **lossyQuals;
        IndexScanDesc scandesc;
-       List       *lossyQual;
        Index           scanrelid;
        HeapTuple       tuple;
        TupleTableSlot *slot;
-       int                     numIndices;
-       bool            bBackward;
-       int                     indexNumber;
 
        /*
         * extract necessary information from index scan node
         */
        estate = node->ss.ps.state;
        direction = estate->es_direction;
-       if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indxorderdir))
+       /* flip direction if this is an overall backward scan */
+       if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indexorderdir))
        {
                if (ScanDirectionIsForward(direction))
                        direction = BackwardScanDirection;
                else if (ScanDirectionIsBackward(direction))
                        direction = ForwardScanDirection;
        }
-       scanDescs = node->iss_ScanDescs;
-       lossyQuals = node->iss_LossyQuals;
-       numIndices = node->iss_NumIndices;
+       scandesc = node->iss_ScanDesc;
        econtext = node->ss.ps.ps_ExprContext;
        slot = node->ss.ss_ScanTupleSlot;
        scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid;
@@ -146,26 +91,19 @@ IndexNext(IndexScanState *node)
        if (estate->es_evTuple != NULL &&
                estate->es_evTuple[scanrelid - 1] != NULL)
        {
-               ListCell   *qual;
-
                if (estate->es_evTupleNull[scanrelid - 1])
                        return slot;            /* return empty slot */
 
                ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
                                           slot, InvalidBuffer, false);
 
-               /* Does the tuple meet any of the OR'd indxqual conditions? */
+               /* Does the tuple meet the indexqual condition? */
                econtext->ecxt_scantuple = slot;
 
                ResetExprContext(econtext);
 
-               foreach(qual, node->indxqualorig)
-               {
-                       if (ExecQual((List *) lfirst(qual), econtext, false))
-                               break;
-               }
-               if (qual == NULL)               /* would not be returned by indices */
-                       ExecClearTuple(slot);
+               if (!ExecQual(node->indexqualorig, econtext, false))
+                       ExecClearTuple(slot);   /* would not be returned by scan */
 
                /* Flag for the next call that no more tuples */
                estate->es_evTupleNull[scanrelid - 1] = true;
@@ -174,156 +112,22 @@ IndexNext(IndexScanState *node)
        }
 
        /*
-        * ok, now that we have what we need, fetch an index tuple. if
-        * scanning this index succeeded then return the appropriate heap
-        * tuple.. else return NULL.
+        * ok, now that we have what we need, fetch the next tuple.
         */
-       bBackward = ScanDirectionIsBackward(direction);
-       if (bBackward)
-       {
-               indexNumber = numIndices - node->iss_IndexPtr - 1;
-               if (indexNumber < 0)
-               {
-                       indexNumber = 0;
-                       node->iss_IndexPtr = numIndices - 1;
-               }
-       }
-       else
+       if ((tuple = index_getnext(scandesc, direction)) != NULL)
        {
-               if ((indexNumber = node->iss_IndexPtr) < 0)
-               {
-                       indexNumber = 0;
-                       node->iss_IndexPtr = 0;
-               }
-       }
-       while (indexNumber < numIndices)
-       {
-               scandesc = scanDescs[node->iss_IndexPtr];
-               lossyQual = lossyQuals[node->iss_IndexPtr];
-
-               while ((tuple = index_getnext(scandesc, direction)) != NULL)
-               {
-                       /*
-                        * Store the scanned tuple in the scan tuple slot of the scan
-                        * state.  Note: we pass 'false' because tuples returned by
-                        * amgetnext are pointers onto disk pages and must not be
-                        * pfree()'d.
-                        */
-                       ExecStoreTuple(tuple,           /* tuple to store */
-                                                  slot,        /* slot to store in */
-                                                  scandesc->xs_cbuf,   /* buffer containing tuple */
-                                                  false);              /* don't pfree */
-
-                       /*
-                        * If any of the index operators involved in this scan are
-                        * lossy, recheck them by evaluating the original operator
-                        * clauses.
-                        */
-                       if (lossyQual)
-                       {
-                               econtext->ecxt_scantuple = slot;
-                               ResetExprContext(econtext);
-                               if (!ExecQual(lossyQual, econtext, false))
-                               {
-                                       /*
-                                        * Fails lossy op, so drop it and loop back for
-                                        * another
-                                        */
-                                       ExecClearTuple(slot);
-                                       continue;
-                               }
-                       }
-
-                       /*
-                        * If it's a multiple-index scan, make sure not to
-                        * double-report a tuple matched by more than one index.  (See
-                        * notes above.)
-                        */
-                       if (numIndices > 1)
-                       {
-                               /* First try the hash table */
-                               if (node->iss_DupHash)
-                               {
-                                       DupHashTabEntry *entry;
-                                       bool            found;
-
-                                       entry = (DupHashTabEntry *)
-                                               hash_search(node->iss_DupHash,
-                                                                       &tuple->t_data->t_ctid,
-                                                                       HASH_ENTER,
-                                                                       &found);
-                                       if (entry == NULL ||
-                                       node->iss_DupHash->hctl->nentries > node->iss_MaxHash)
-                                       {
-                                               /* out of memory (either hard or soft limit) */
-                                               /* release hash table and fall thru to old code */
-                                               hash_destroy(node->iss_DupHash);
-                                               node->iss_DupHash = NULL;
-                                       }
-                                       else if (found)
-                                       {
-                                               /* pre-existing entry */
-
-                                               /*
-                                                * It's duplicate if first emitted in a different
-                                                * scan.  If same scan, we must be backing up, so
-                                                * okay to emit again.
-                                                */
-                                               if (entry->scannum != node->iss_IndexPtr)
-                                               {
-                                                       /* Dup, so drop it and loop back for another */
-                                                       ExecClearTuple(slot);
-                                                       continue;
-                                               }
-                                       }
-                                       else
-                                       {
-                                               /* new entry, finish filling it in */
-                                               entry->scannum = node->iss_IndexPtr;
-                                       }
-                               }
-                               /* If hash table has overflowed, do it the hard way */
-                               if (node->iss_DupHash == NULL &&
-                                       node->iss_IndexPtr > 0)
-                               {
-                                       bool            prev_matches = false;
-                                       int                     prev_index;
-                                       ListCell   *qual;
-
-                                       econtext->ecxt_scantuple = slot;
-                                       ResetExprContext(econtext);
-                                       qual = list_head(node->indxqualorig);
-                                       for (prev_index = 0;
-                                                prev_index < node->iss_IndexPtr;
-                                                prev_index++)
-                                       {
-                                               if (ExecQual((List *) lfirst(qual), econtext, false))
-                                               {
-                                                       prev_matches = true;
-                                                       break;
-                                               }
-                                               qual = lnext(qual);
-                                       }
-                                       if (prev_matches)
-                                       {
-                                               /* Dup, so drop it and loop back for another */
-                                               ExecClearTuple(slot);
-                                               continue;
-                                       }
-                               }
-                       }
-
-                       return slot;            /* OK to return tuple */
-               }
+               /*
+                * Store the scanned tuple in the scan tuple slot of the scan
+                * state.  Note: we pass 'false' because tuples returned by
+                * amgetnext are pointers onto disk pages and must not be
+                * pfree()'d.
+                */
+               ExecStoreTuple(tuple,                           /* tuple to store */
+                                          slot,                                /* slot to store in */
+                                          scandesc->xs_cbuf,   /* buffer containing tuple */
+                                          false);                              /* don't pfree */
 
-               if (indexNumber < numIndices)
-               {
-                       indexNumber++;
-                       if (bBackward)
-                               node->iss_IndexPtr--;
-                       else
-                               node->iss_IndexPtr++;
-               }
+               return slot;
        }
 
        /*
@@ -335,23 +139,6 @@ IndexNext(IndexScanState *node)
 
 /* ----------------------------------------------------------------
  *             ExecIndexScan(node)
- *
- * old comments:
- *             Scans the relation using primary or secondary indices and returns
- *                the next qualifying tuple in the direction specified.
- *             It calls ExecScan() and passes it the access methods which returns
- *             the next tuple using the indices.
- *
- *             Conditions:
- *               -- the "cursor" maintained by the AMI is positioned at the tuple
- *                      returned previously.
- *
- *             Initial States:
- *               -- the relation indicated is opened for scanning so that the
- *                      "cursor" is positioned before the first qualifying tuple.
- *               -- all index realtions are opened for scanning.
- *               -- indexPtr points to the first index.
- *               -- state variable ruleFlag = nil.
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
@@ -378,7 +165,6 @@ ExecIndexScan(IndexScanState *node)
  *             Updating the scan key was formerly done separately in
  *             ExecUpdateIndexScanKeys. Integrating it into ReScan makes
  *             rescans of indices and relations/general streams more uniform.
- *
  * ----------------------------------------------------------------
  */
 void
@@ -386,20 +172,14 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
 {
        EState     *estate;
        ExprContext *econtext;
-       int                     numIndices;
-       IndexScanDescPtr scanDescs;
-       ScanKey    *scanKeys;
-       ExprState ***runtimeKeyInfo;
-       int                *numScanKeys;
+       ScanKey         scanKeys;
+       ExprState **runtimeKeyInfo;
+       int                     numScanKeys;
        Index           scanrelid;
-       int                     i;
-       int                     j;
 
        estate = node->ss.ps.state;
        econtext = node->iss_RuntimeContext;            /* context for runtime
                                                                                                 * keys */
-       numIndices = node->iss_NumIndices;
-       scanDescs = node->iss_ScanDescs;
        scanKeys = node->iss_ScanKeys;
        runtimeKeyInfo = node->iss_RuntimeKeyInfo;
        numScanKeys = node->iss_NumScanKeys;
@@ -435,49 +215,10 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
         */
        if (runtimeKeyInfo)
        {
-               for (i = 0; i < numIndices; i++)
-               {
-                       int                     n_keys;
-                       ScanKey         scan_keys;
-                       ExprState **run_keys;
-
-                       n_keys = numScanKeys[i];
-                       scan_keys = scanKeys[i];
-                       run_keys = runtimeKeyInfo[i];
-
-                       for (j = 0; j < n_keys; j++)
-                       {
-                               /*
-                                * If we have a run-time key, then extract the run-time
-                                * expression and evaluate it with respect to the current
-                                * outer tuple.  We then stick the result into the scan
-                                * key.
-                                *
-                                * Note: the result of the eval could be a pass-by-ref value
-                                * that's stored in the outer scan's tuple, not in
-                                * econtext->ecxt_per_tuple_memory.  We assume that the
-                                * outer tuple will stay put throughout our scan.  If this
-                                * is wrong, we could copy the result into our context
-                                * explicitly, but I think that's not necessary...
-                                */
-                               if (run_keys[j] != NULL)
-                               {
-                                       Datum           scanvalue;
-                                       bool            isNull;
-
-                                       scanvalue = ExecEvalExprSwitchContext(run_keys[j],
-                                                                                                                 econtext,
-                                                                                                                 &isNull,
-                                                                                                                 NULL);
-                                       scan_keys[j].sk_argument = scanvalue;
-                                       if (isNull)
-                                               scan_keys[j].sk_flags |= SK_ISNULL;
-                                       else
-                                               scan_keys[j].sk_flags &= ~SK_ISNULL;
-                               }
-                       }
-               }
-
+               ExecIndexEvalRuntimeKeys(econtext,
+                                                                runtimeKeyInfo,
+                                                                scanKeys,
+                                                                numScanKeys);
                node->iss_RuntimeKeysReady = true;
        }
 
@@ -489,26 +230,52 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
                return;
        }
 
-       /* reset hash table */
-       if (numIndices > 1)
-       {
-               if (node->iss_DupHash)
-                       hash_destroy(node->iss_DupHash);
-               create_duphash(node);
-       }
+       /* reset index scan */
+       index_rescan(node->iss_ScanDesc, scanKeys);
+}
 
-       /* reset index scans */
-       if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indxorderdir))
-               node->iss_IndexPtr = numIndices;
-       else
-               node->iss_IndexPtr = -1;
 
-       for (i = 0; i < numIndices; i++)
-       {
-               IndexScanDesc scan = scanDescs[i];
-               ScanKey         skey = scanKeys[i];
+/*
+ * ExecIndexEvalRuntimeKeys
+ *             Evaluate any runtime key values, and update the scankeys.
+ */
+void
+ExecIndexEvalRuntimeKeys(ExprContext *econtext,
+                                                ExprState **run_keys,
+                                                ScanKey scan_keys,
+                                                int n_keys)
+{
+       int                     j;
 
-               index_rescan(scan, skey);
+       for (j = 0; j < n_keys; j++)
+       {
+               /*
+                * If we have a run-time key, then extract the run-time
+                * expression and evaluate it with respect to the current
+                * outer tuple.  We then stick the result into the scan key.
+                *
+                * Note: the result of the eval could be a pass-by-ref value
+                * that's stored in the outer scan's tuple, not in
+                * econtext->ecxt_per_tuple_memory.  We assume that the
+                * outer tuple will stay put throughout our scan.  If this
+                * is wrong, we could copy the result into our context
+                * explicitly, but I think that's not necessary...
+                */
+               if (run_keys[j] != NULL)
+               {
+                       Datum           scanvalue;
+                       bool            isNull;
+
+                       scanvalue = ExecEvalExprSwitchContext(run_keys[j],
+                                                                                                 econtext,
+                                                                                                 &isNull,
+                                                                                                 NULL);
+                       scan_keys[j].sk_argument = scanvalue;
+                       if (isNull)
+                               scan_keys[j].sk_flags |= SK_ISNULL;
+                       else
+                               scan_keys[j].sk_flags &= ~SK_ISNULL;
+               }
        }
 }
 
@@ -519,18 +286,15 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
 void
 ExecEndIndexScan(IndexScanState *node)
 {
-       int                     numIndices;
-       RelationPtr indexRelationDescs;
-       IndexScanDescPtr indexScanDescs;
+       Relation        indexRelationDesc;
+       IndexScanDesc indexScanDesc;
        Relation        relation;
-       int                     i;
 
        /*
         * extract information from the node
         */
-       numIndices = node->iss_NumIndices;
-       indexRelationDescs = node->iss_RelationDescs;
-       indexScanDescs = node->iss_ScanDescs;
+       indexRelationDesc = node->iss_RelationDesc;
+       indexScanDesc = node->iss_ScanDesc;
        relation = node->ss.ss_currentRelation;
 
        /*
@@ -548,21 +312,11 @@ ExecEndIndexScan(IndexScanState *node)
        ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
        ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
-       /* drop hash table */
-       if (node->iss_DupHash)
-               hash_destroy(node->iss_DupHash);
-
        /*
-        * close the index relations
+        * close the index relation
         */
-       for (i = 0; i < numIndices; i++)
-       {
-               if (indexScanDescs[i] != NULL)
-                       index_endscan(indexScanDescs[i]);
-
-               if (indexRelationDescs[i] != NULL)
-                       index_close(indexRelationDescs[i]);
-       }
+       index_endscan(indexScanDesc);
+       index_close(indexRelationDesc);
 
        /*
         * close the heap relation.
@@ -577,52 +331,22 @@ ExecEndIndexScan(IndexScanState *node)
 
 /* ----------------------------------------------------------------
  *             ExecIndexMarkPos
- *
- * old comments
- *             Marks scan position by marking the current index.
- *             Returns nothing.
  * ----------------------------------------------------------------
  */
 void
 ExecIndexMarkPos(IndexScanState *node)
 {
-       IndexScanDescPtr indexScanDescs;
-       IndexScanDesc scanDesc;
-       int                     indexPtr;
-
-       indexPtr = node->iss_MarkIndexPtr = node->iss_IndexPtr;
-       if (indexPtr >= 0 && indexPtr < node->iss_NumIndices)
-       {
-               indexScanDescs = node->iss_ScanDescs;
-               scanDesc = indexScanDescs[indexPtr];
-
-               index_markpos(scanDesc);
-       }
+       index_markpos(node->iss_ScanDesc);
 }
 
 /* ----------------------------------------------------------------
  *             ExecIndexRestrPos
- *
- * old comments
- *             Restores scan position by restoring the current index.
- *             Returns nothing.
  * ----------------------------------------------------------------
  */
 void
 ExecIndexRestrPos(IndexScanState *node)
 {
-       IndexScanDescPtr indexScanDescs;
-       IndexScanDesc scanDesc;
-       int                     indexPtr;
-
-       indexPtr = node->iss_IndexPtr = node->iss_MarkIndexPtr;
-       if (indexPtr >= 0 && indexPtr < node->iss_NumIndices)
-       {
-               indexScanDescs = node->iss_ScanDescs;
-               scanDesc = indexScanDescs[indexPtr];
-
-               index_restrpos(scanDesc);
-       }
+       index_restrpos(node->iss_ScanDesc);
 }
 
 /* ----------------------------------------------------------------
@@ -633,35 +357,16 @@ ExecIndexRestrPos(IndexScanState *node)
  *
  *             Note: index scans have 2 sets of state information because
  *                       we have to keep track of the base relation and the
- *                       index relations.
- *
- * old comments
- *             Creates the run-time state information for the node and
- *             sets the relation id to contain relevant descriptors.
- *
- *             Parameters:
- *               node: IndexNode node produced by the planner.
- *               estate: the execution state initialized in InitPlan.
+ *                       index relation.
  * ----------------------------------------------------------------
  */
 IndexScanState *
 ExecInitIndexScan(IndexScan *node, EState *estate)
 {
        IndexScanState *indexstate;
-       ListCell   *indxqual;
-       ListCell   *indxstrategy;
-       ListCell   *indxsubtype;
-       ListCell   *indxlossy;
-       ListCell   *indxid_item;
-       int                     i;
-       int                     numIndices;
-       int                     indexPtr;
-       ScanKey    *scanKeys;
-       int                *numScanKeys;
-       RelationPtr indexDescs;
-       IndexScanDescPtr scanDescs;
-       List      **lossyQuals;
-       ExprState ***runtimeKeyInfo;
+       ScanKey         scanKeys;
+       int                     numScanKeys;
+       ExprState **runtimeKeyInfo;
        bool            have_runtime_keys;
        RangeTblEntry *rtentry;
        Index           relid;
@@ -685,9 +390,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
        /*
         * initialize child expressions
         *
-        * Note: we don't initialize all of the indxqual expression, only the
+        * Note: we don't initialize all of the indexqual expression, only the
         * sub-parts corresponding to runtime keys (see below).  The
-        * indxqualorig expression is always initialized even though it will
+        * indexqualorig expression is always initialized even though it will
         * only be used in some uncommon cases --- would be nice to improve
         * that.  (Problem is that any SubPlans present in the expression must
         * be found now...)
@@ -698,8 +403,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
        indexstate->ss.ps.qual = (List *)
                ExecInitExpr((Expr *) node->scan.plan.qual,
                                         (PlanState *) indexstate);
-       indexstate->indxqualorig = (List *)
-               ExecInitExpr((Expr *) node->indxqualorig,
+       indexstate->indexqualorig = (List *)
+               ExecInitExpr((Expr *) node->indexqualorig,
                                         (PlanState *) indexstate);
 
 #define INDEXSCAN_NSLOTS 2
@@ -713,233 +418,29 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
        /*
         * Initialize index-specific scan state
         */
-       indexstate->iss_NumIndices = 0;
-       indexstate->iss_IndexPtr = -1;
-       indexstate->iss_ScanKeys = NULL;
-       indexstate->iss_NumScanKeys = NULL;
-       indexstate->iss_RuntimeKeyInfo = NULL;
-       indexstate->iss_RuntimeContext = NULL;
        indexstate->iss_RuntimeKeysReady = false;
-       indexstate->iss_RelationDescs = NULL;
-       indexstate->iss_ScanDescs = NULL;
-       indexstate->iss_LossyQuals = NULL;
-
-       /*
-        * get the index node information
-        */
-       indxid_item = list_head(node->indxid);
-       numIndices = list_length(node->indxid);
-       indexPtr = -1;
 
        CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
 
-       /*
-        * scanKeys is used to keep track of the ScanKey's. This is needed
-        * because a single scan may use several indices and each index has
-        * its own ScanKey.
-        */
-       numScanKeys = (int *) palloc(numIndices * sizeof(int));
-       scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey));
-       indexDescs = (RelationPtr) palloc(numIndices * sizeof(Relation));
-       scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
-       lossyQuals = (List **) palloc0(numIndices * sizeof(List *));
-
-       /*
-        * initialize space for runtime key info (may not be needed)
-        */
-       have_runtime_keys = false;
-       runtimeKeyInfo = (ExprState ***) palloc0(numIndices * sizeof(ExprState **));
-
        /*
         * build the index scan keys from the index qualification
         */
-       indxqual = list_head(node->indxqual);
-       indxstrategy = list_head(node->indxstrategy);
-       indxsubtype = list_head(node->indxsubtype);
-       indxlossy = list_head(node->indxlossy);
-       for (i = 0; i < numIndices; i++)
-       {
-               List       *quals;
-               List       *strategies;
-               List       *subtypes;
-               List       *lossyflags;
-               ListCell   *qual_cell;
-               ListCell   *strategy_cell;
-               ListCell   *subtype_cell;
-               ListCell   *lossyflag_cell;
-               int                     n_keys;
-               ScanKey         scan_keys;
-               ExprState **run_keys;
-               int                     j;
-
-               quals = (List *) lfirst(indxqual);
-               indxqual = lnext(indxqual);
-               strategies = (List *) lfirst(indxstrategy);
-               indxstrategy = lnext(indxstrategy);
-               subtypes = (List *) lfirst(indxsubtype);
-               indxsubtype = lnext(indxsubtype);
-               lossyflags = (List *) lfirst(indxlossy);
-               indxlossy = lnext(indxlossy);
-               n_keys = list_length(quals);
-               scan_keys = (n_keys <= 0) ? NULL :
-                       (ScanKey) palloc(n_keys * sizeof(ScanKeyData));
-               run_keys = (n_keys <= 0) ? NULL :
-                       (ExprState **) palloc(n_keys * sizeof(ExprState *));
-
-               /*
-                * for each opclause in the given qual, convert each qual's
-                * opclause into a single scan key
-                */
-               qual_cell = list_head(quals);
-               strategy_cell = list_head(strategies);
-               subtype_cell = list_head(subtypes);
-               lossyflag_cell = list_head(lossyflags);
-               for (j = 0; j < n_keys; j++)
-               {
-                       OpExpr     *clause; /* one clause of index qual */
-                       Expr       *leftop; /* expr on lhs of operator */
-                       Expr       *rightop;    /* expr on rhs ... */
-                       int                     flags = 0;
-                       AttrNumber      varattno;               /* att number used in scan */
-                       StrategyNumber strategy;        /* op's strategy number */
-                       Oid                     subtype;        /* op's strategy subtype */
-                       int                     lossy;  /* op's recheck flag */
-                       RegProcedure opfuncid;          /* operator proc id used in scan */
-                       Datum           scanvalue;              /* value used in scan (if const) */
-
-                       /*
-                        * extract clause information from the qualification
-                        */
-                       clause = (OpExpr *) lfirst(qual_cell);
-                       qual_cell = lnext(qual_cell);
-                       strategy = lfirst_int(strategy_cell);
-                       strategy_cell = lnext(strategy_cell);
-                       subtype = lfirst_oid(subtype_cell);
-                       subtype_cell = lnext(subtype_cell);
-                       lossy = lfirst_int(lossyflag_cell);
-                       lossyflag_cell = lnext(lossyflag_cell);
-
-                       if (!IsA(clause, OpExpr))
-                               elog(ERROR, "indxqual is not an OpExpr");
-
-                       opfuncid = clause->opfuncid;
-
-                       /*
-                        * Here we figure out the contents of the index qual. The
-                        * usual case is (var op const) which means we form a scan key
-                        * for the attribute listed in the var node and use the value
-                        * of the const as comparison data.
-                        *
-                        * If we don't have a const node, it means our scan key is a
-                        * function of information obtained during the execution of
-                        * the plan, in which case we need to recalculate the index
-                        * scan key at run time.  Hence, we set have_runtime_keys to
-                        * true and place the appropriate subexpression in run_keys.
-                        * The corresponding scan key values are recomputed at run
-                        * time.
-                        */
-                       run_keys[j] = NULL;
-
-                       /*
-                        * determine information in leftop
-                        */
-                       leftop = (Expr *) get_leftop((Expr *) clause);
-
-                       if (leftop && IsA(leftop, RelabelType))
-                               leftop = ((RelabelType *) leftop)->arg;
-
-                       Assert(leftop != NULL);
-
-                       if (!(IsA(leftop, Var) &&
-                                 var_is_rel((Var *) leftop)))
-                               elog(ERROR, "indxqual doesn't have key on left side");
-
-                       varattno = ((Var *) leftop)->varattno;
-
-                       /*
-                        * now determine information in rightop
-                        */
-                       rightop = (Expr *) get_rightop((Expr *) clause);
-
-                       if (rightop && IsA(rightop, RelabelType))
-                               rightop = ((RelabelType *) rightop)->arg;
-
-                       Assert(rightop != NULL);
-
-                       if (IsA(rightop, Const))
-                       {
-                               /*
-                                * if the rightop is a const node then it means it
-                                * identifies the value to place in our scan key.
-                                */
-                               scanvalue = ((Const *) rightop)->constvalue;
-                               if (((Const *) rightop)->constisnull)
-                                       flags |= SK_ISNULL;
-                       }
-                       else
-                       {
-                               /*
-                                * otherwise, the rightop contains an expression evaluable
-                                * at runtime to figure out the value to place in our scan
-                                * key.
-                                */
-                               have_runtime_keys = true;
-                               run_keys[j] = ExecInitExpr(rightop, (PlanState *) indexstate);
-                               scanvalue = (Datum) 0;
-                       }
-
-                       /*
-                        * initialize the scan key's fields appropriately
-                        */
-                       ScanKeyEntryInitialize(&scan_keys[j],
-                                                                  flags,
-                                                                  varattno,    /* attribute number to
-                                                                                                * scan */
-                                                                  strategy,    /* op's strategy */
-                                                                  subtype,             /* strategy subtype */
-                                                                  opfuncid,    /* reg proc to use */
-                                                                  scanvalue);  /* constant */
-
-                       /*
-                        * If this operator is lossy, add its indxqualorig expression
-                        * to the list of quals to recheck.  The list_nth() calls here
-                        * could be avoided by chasing the lists in parallel to all
-                        * the other lists, but since lossy operators are very
-                        * uncommon, it's probably a waste of time to do so.
-                        */
-                       if (lossy)
-                       {
-                               List       *qualOrig = indexstate->indxqualorig;
-
-                               lossyQuals[i] = lappend(lossyQuals[i],
-                                                       list_nth((List *) list_nth(qualOrig, i), j));
-                       }
-               }
-
-               /*
-                * store the key information into our arrays.
-                */
-               numScanKeys[i] = n_keys;
-               scanKeys[i] = scan_keys;
-               runtimeKeyInfo[i] = run_keys;
-       }
-
-       indexstate->iss_NumIndices = numIndices;
-       if (ScanDirectionIsBackward(node->indxorderdir))
-               indexPtr = numIndices;
-       indexstate->iss_IndexPtr = indexPtr;
+       have_runtime_keys =
+               ExecIndexBuildScanKeys((PlanState *) indexstate,
+                                                          node->indexqual,
+                                                          node->indexstrategy,
+                                                          node->indexsubtype,
+                                                          &runtimeKeyInfo,
+                                                          &scanKeys,
+                                                          &numScanKeys);
+
+       indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo;
        indexstate->iss_ScanKeys = scanKeys;
        indexstate->iss_NumScanKeys = numScanKeys;
 
        /*
-        * If all of our keys have the form (var op const), then we have no
-        * runtime keys so we store NULL in the runtime key info. Otherwise
-        * runtime key info contains an array of pointers (one for each index)
-        * to arrays of flags (one for each key) which indicate that the qual
-        * needs to be evaluated at runtime. -cim 10/24/89
-        *
-        * If we do have runtime keys, we need an ExprContext to evaluate them;
-        * the node's standard context won't do because we want to reset that
+        * If we have runtime keys, we need an ExprContext to evaluate them.
+        * The node's standard context won't do because we want to reset that
         * context for every tuple.  So, build another context just like the
         * other one... -tgl 7/11/00
         */
@@ -948,21 +449,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
                ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
 
                ExecAssignExprContext(estate, &indexstate->ss.ps);
-               indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo;
                indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
                indexstate->ss.ps.ps_ExprContext = stdecontext;
        }
        else
        {
-               indexstate->iss_RuntimeKeyInfo = NULL;
                indexstate->iss_RuntimeContext = NULL;
-               /* Get rid of the speculatively-allocated flag arrays, too */
-               for (i = 0; i < numIndices; i++)
-               {
-                       if (runtimeKeyInfo[i] != NULL)
-                               pfree(runtimeKeyInfo[i]);
-               }
-               pfree(runtimeKeyInfo);
        }
 
        /*
@@ -983,27 +475,17 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
        ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation), false);
 
        /*
-        * open the index relations and initialize relation and scan
+        * open the index relation and initialize relation and scan
         * descriptors.  Note we acquire no locks here; the index machinery
         * does its own locks and unlocks.      (We rely on having AccessShareLock
         * on the parent table to ensure the index won't go away!)
         */
-       for (i = 0; i < numIndices; i++)
-       {
-               Oid                     indexOid = lfirst_oid(indxid_item);
-
-               indexDescs[i] = index_open(indexOid);
-               scanDescs[i] = index_beginscan(currentRelation,
-                                                                          indexDescs[i],
-                                                                          estate->es_snapshot,
-                                                                          numScanKeys[i],
-                                                                          scanKeys[i]);
-               indxid_item = lnext(indxid_item);
-       }
-
-       indexstate->iss_RelationDescs = indexDescs;
-       indexstate->iss_ScanDescs = scanDescs;
-       indexstate->iss_LossyQuals = lossyQuals;
+       indexstate->iss_RelationDesc = index_open(node->indexid);
+       indexstate->iss_ScanDesc = index_beginscan(currentRelation,
+                                                                                          indexstate->iss_RelationDesc,
+                                                                                          estate->es_snapshot,
+                                                                                          numScanKeys,
+                                                                                          scanKeys);
 
        /*
         * Initialize result tuple type and projection info.
@@ -1011,42 +493,180 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
        ExecAssignResultTypeFromTL(&indexstate->ss.ps);
        ExecAssignScanProjectionInfo(&indexstate->ss);
 
-       /*
-        * Initialize hash table if needed.
-        */
-       if (numIndices > 1)
-               create_duphash(indexstate);
-       else
-               indexstate->iss_DupHash = NULL;
-
        /*
         * all done.
         */
        return indexstate;
 }
 
-static void
-create_duphash(IndexScanState *node)
+
+/*
+ * ExecIndexBuildScanKeys
+ *             Build the index scan keys from the index qualification
+ *
+ * Input params are:
+ *
+ * planstate: executor state node we are working for
+ * quals: indexquals expressions
+ * strategies: associated operator strategy numbers
+ * subtypes: associated operator subtype OIDs
+ *
+ * Output params are:
+ *
+ * *runtimeKeyInfo: receives ptr to array of runtime key exprstates
+ *             (NULL if no runtime keys)
+ * *scanKeys: receives ptr to array of ScanKeys
+ * *numScanKeys: receives number of scankeys/runtime keys
+ *
+ * Return value is TRUE if any runtime key expressions were found, else FALSE.
+ */
+bool
+ExecIndexBuildScanKeys(PlanState *planstate, List *quals,
+                                          List *strategies, List *subtypes,
+                                          ExprState ***runtimeKeyInfo,
+                                          ScanKey *scanKeys, int *numScanKeys)
 {
-       HASHCTL         hash_ctl;
-       long            nbuckets;
-
-       node->iss_MaxHash = (work_mem * 1024L) /
-               (MAXALIGN(sizeof(HASHELEMENT)) + MAXALIGN(sizeof(DupHashTabEntry)));
-       MemSet(&hash_ctl, 0, sizeof(hash_ctl));
-       hash_ctl.keysize = SizeOfIptrData;
-       hash_ctl.entrysize = sizeof(DupHashTabEntry);
-       hash_ctl.hash = tag_hash;
-       hash_ctl.hcxt = CurrentMemoryContext;
-       nbuckets = (long) ceil(node->ss.ps.plan->plan_rows);
-       if (nbuckets < 1)
-               nbuckets = 1;
-       if (nbuckets > node->iss_MaxHash)
-               nbuckets = node->iss_MaxHash;
-       node->iss_DupHash = hash_create("DupHashTable",
-                                                                       nbuckets,
-                                                                       &hash_ctl,
-                                                          HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+       bool            have_runtime_keys = false;
+       ListCell   *qual_cell;
+       ListCell   *strategy_cell;
+       ListCell   *subtype_cell;
+       int                     n_keys;
+       ScanKey         scan_keys;
+       ExprState **run_keys;
+       int                     j;
+
+       n_keys = list_length(quals);
+       scan_keys = (n_keys <= 0) ? NULL :
+               (ScanKey) palloc(n_keys * sizeof(ScanKeyData));
+       run_keys = (n_keys <= 0) ? NULL :
+               (ExprState **) palloc(n_keys * sizeof(ExprState *));
+
+       /*
+        * for each opclause in the given qual, convert each qual's
+        * opclause into a single scan key
+        */
+       qual_cell = list_head(quals);
+       strategy_cell = list_head(strategies);
+       subtype_cell = list_head(subtypes);
+
+       for (j = 0; j < n_keys; j++)
+       {
+               OpExpr     *clause;                     /* one clause of index qual */
+               Expr       *leftop;                     /* expr on lhs of operator */
+               Expr       *rightop;            /* expr on rhs ... */
+               int                     flags = 0;
+               AttrNumber      varattno;               /* att number used in scan */
+               StrategyNumber strategy;        /* op's strategy number */
+               Oid                     subtype;                /* op's strategy subtype */
+               RegProcedure opfuncid;          /* operator proc id used in scan */
+               Datum           scanvalue;              /* value used in scan (if const) */
+
+               /*
+                * extract clause information from the qualification
+                */
+               clause = (OpExpr *) lfirst(qual_cell);
+               qual_cell = lnext(qual_cell);
+               strategy = lfirst_int(strategy_cell);
+               strategy_cell = lnext(strategy_cell);
+               subtype = lfirst_oid(subtype_cell);
+               subtype_cell = lnext(subtype_cell);
+
+               if (!IsA(clause, OpExpr))
+                       elog(ERROR, "indexqual is not an OpExpr");
+
+               opfuncid = clause->opfuncid;
+
+               /*
+                * Here we figure out the contents of the index qual. The
+                * usual case is (var op const) which means we form a scan key
+                * for the attribute listed in the var node and use the value
+                * of the const as comparison data.
+                *
+                * If we don't have a const node, it means our scan key is a
+                * function of information obtained during the execution of
+                * the plan, in which case we need to recalculate the index
+                * scan key at run time.  Hence, we set have_runtime_keys to
+                * true and place the appropriate subexpression in run_keys.
+                * The corresponding scan key values are recomputed at run
+                * time.
+                */
+               run_keys[j] = NULL;
+
+               /*
+                * determine information in leftop
+                */
+               leftop = (Expr *) get_leftop((Expr *) clause);
+
+               if (leftop && IsA(leftop, RelabelType))
+                       leftop = ((RelabelType *) leftop)->arg;
+
+               Assert(leftop != NULL);
+
+               if (!(IsA(leftop, Var) &&
+                         var_is_rel((Var *) leftop)))
+                       elog(ERROR, "indexqual doesn't have key on left side");
+
+               varattno = ((Var *) leftop)->varattno;
+
+               /*
+                * now determine information in rightop
+                */
+               rightop = (Expr *) get_rightop((Expr *) clause);
+
+               if (rightop && IsA(rightop, RelabelType))
+                       rightop = ((RelabelType *) rightop)->arg;
+
+               Assert(rightop != NULL);
+
+               if (IsA(rightop, Const))
+               {
+                       /*
+                        * if the rightop is a const node then it means it
+                        * identifies the value to place in our scan key.
+                        */
+                       scanvalue = ((Const *) rightop)->constvalue;
+                       if (((Const *) rightop)->constisnull)
+                               flags |= SK_ISNULL;
+               }
+               else
+               {
+                       /*
+                        * otherwise, the rightop contains an expression evaluable
+                        * at runtime to figure out the value to place in our scan
+                        * key.
+                        */
+                       have_runtime_keys = true;
+                       run_keys[j] = ExecInitExpr(rightop, planstate);
+                       scanvalue = (Datum) 0;
+               }
+
+               /*
+                * initialize the scan key's fields appropriately
+                */
+               ScanKeyEntryInitialize(&scan_keys[j],
+                                                          flags,
+                                                          varattno,    /* attribute number to scan */
+                                                          strategy,    /* op's strategy */
+                                                          subtype,             /* strategy subtype */
+                                                          opfuncid,    /* reg proc to use */
+                                                          scanvalue);  /* constant */
+       }
+
+       /* If no runtime keys, get rid of speculatively-allocated array */
+       if (run_keys && !have_runtime_keys)
+       {
+               pfree(run_keys);
+               run_keys = NULL;
+       }
+
+       /*
+        * Return the info to our caller.
+        */
+       *numScanKeys = n_keys;
+       *scanKeys = scan_keys;
+       *runtimeKeyInfo = run_keys;
+
+       return have_runtime_keys;
 }
 
 int
index 71cea4bc2f374f2dd83665657c6a25630cbd924f..f0d8666091d91c2bc9e5ddd565a36aedc668fe67 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.302 2005/04/19 22:35:13 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.303 2005/04/25 01:30:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -253,13 +253,12 @@ _copyIndexScan(IndexScan *from)
        /*
         * copy remainder of node
         */
-       COPY_NODE_FIELD(indxid);
-       COPY_NODE_FIELD(indxqual);
-       COPY_NODE_FIELD(indxqualorig);
-       COPY_NODE_FIELD(indxstrategy);
-       COPY_NODE_FIELD(indxsubtype);
-       COPY_NODE_FIELD(indxlossy);
-       COPY_SCALAR_FIELD(indxorderdir);
+       COPY_SCALAR_FIELD(indexid);
+       COPY_NODE_FIELD(indexqual);
+       COPY_NODE_FIELD(indexqualorig);
+       COPY_NODE_FIELD(indexstrategy);
+       COPY_NODE_FIELD(indexsubtype);
+       COPY_SCALAR_FIELD(indexorderdir);
 
        return newnode;
 }
@@ -280,11 +279,11 @@ _copyBitmapIndexScan(BitmapIndexScan *from)
        /*
         * copy remainder of node
         */
-       COPY_SCALAR_FIELD(indxid);
-       COPY_NODE_FIELD(indxqual);
-       COPY_NODE_FIELD(indxqualorig);
-       COPY_NODE_FIELD(indxstrategy);
-       COPY_NODE_FIELD(indxsubtype);
+       COPY_SCALAR_FIELD(indexid);
+       COPY_NODE_FIELD(indexqual);
+       COPY_NODE_FIELD(indexqualorig);
+       COPY_NODE_FIELD(indexstrategy);
+       COPY_NODE_FIELD(indexsubtype);
 
        return newnode;
 }
index c1c6ac09f489f21ea2a2cc7cd990836d630769b5..484551a85039446f6e8feac577553bf20356ffff 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.249 2005/04/21 19:18:12 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.250 2005/04/25 01:30:13 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -351,13 +351,12 @@ _outIndexScan(StringInfo str, IndexScan *node)
 
        _outScanInfo(str, (Scan *) node);
 
-       WRITE_NODE_FIELD(indxid);
-       WRITE_NODE_FIELD(indxqual);
-       WRITE_NODE_FIELD(indxqualorig);
-       WRITE_NODE_FIELD(indxstrategy);
-       WRITE_NODE_FIELD(indxsubtype);
-       WRITE_NODE_FIELD(indxlossy);
-       WRITE_ENUM_FIELD(indxorderdir, ScanDirection);
+       WRITE_OID_FIELD(indexid);
+       WRITE_NODE_FIELD(indexqual);
+       WRITE_NODE_FIELD(indexqualorig);
+       WRITE_NODE_FIELD(indexstrategy);
+       WRITE_NODE_FIELD(indexsubtype);
+       WRITE_ENUM_FIELD(indexorderdir, ScanDirection);
 }
 
 static void
@@ -367,11 +366,11 @@ _outBitmapIndexScan(StringInfo str, BitmapIndexScan *node)
 
        _outScanInfo(str, (Scan *) node);
 
-       WRITE_OID_FIELD(indxid);
-       WRITE_NODE_FIELD(indxqual);
-       WRITE_NODE_FIELD(indxqualorig);
-       WRITE_NODE_FIELD(indxstrategy);
-       WRITE_NODE_FIELD(indxsubtype);
+       WRITE_OID_FIELD(indexid);
+       WRITE_NODE_FIELD(indexqual);
+       WRITE_NODE_FIELD(indexqualorig);
+       WRITE_NODE_FIELD(indexstrategy);
+       WRITE_NODE_FIELD(indexsubtype);
 }
 
 static void
index 9ad3a73e942dfdc990f4bb32461f6a84183a2c07..8675b6efd3ca74f552fe5458090479fab0d75327 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.127 2005/04/21 19:18:12 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.128 2005/04/25 01:30:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -174,13 +174,12 @@ set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
        /* Consider sequential scan */
        add_path(rel, create_seqscan_path(root, rel));
 
+       /* Consider index scans */
+       create_index_paths(root, rel);
+
        /* Consider TID scans */
        create_tidscan_paths(root, rel);
 
-       /* Consider index paths for both simple and OR index clauses */
-       create_index_paths(root, rel);
-       create_or_index_paths(root, rel);
-
        /* Now find the cheapest of the paths for this rel */
        set_cheapest(rel);
 }
index 1cdad87361e8bb51cc169550c8512629dd9b414a..f55f7fc9188918df38ad8b7a5cfb8b54225b2ed4 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.177 2005/04/23 01:57:34 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.178 2005/04/25 01:30:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -58,10 +58,6 @@ static List *find_usable_indexes(Query *root, RelOptInfo *rel,
                                                                 List *clauses, List *outer_clauses,
                                                                 bool istoplevel, bool isjoininner,
                                                                 Relids outer_relids);
-static List *generate_bitmap_or_paths(Query *root, RelOptInfo *rel,
-                                                                         List *clauses, List *outer_clauses,
-                                                                         bool isjoininner,
-                                                                         Relids outer_relids);
 static Path *choose_bitmap_and(Query *root, RelOptInfo *rel, List *paths);
 static int     bitmap_path_comparator(const void *a, const void *b);
 static Cost bitmap_and_cost_est(Query *root, RelOptInfo *rel, List *paths);
@@ -365,7 +361,7 @@ find_usable_indexes(Query *root, RelOptInfo *rel,
  * for the purpose of generating indexquals, but are not to be searched for
  * ORs.  (See find_usable_indexes() for motivation.)
  */
-static List *
+List *
 generate_bitmap_or_paths(Query *root, RelOptInfo *rel,
                                                 List *clauses, List *outer_clauses,
                                                 bool isjoininner,
@@ -520,11 +516,7 @@ choose_bitmap_and(Query *root, RelOptInfo *rel, List *paths)
        paths = list_make1(patharray[0]);
        costsofar = bitmap_and_cost_est(root, rel, paths);
        if (IsA(patharray[0], IndexPath))
-       {
-               Assert(list_length(((IndexPath *) patharray[0])->indexclauses) == 1);
-               qualsofar = (List *) linitial(((IndexPath *) patharray[0])->indexclauses);
-               qualsofar = list_copy(qualsofar);
-       }
+               qualsofar = list_copy(((IndexPath *) patharray[0])->indexclauses);
        else
                qualsofar = NIL;
        lastcell = list_head(paths);            /* for quick deletions */
@@ -537,8 +529,7 @@ choose_bitmap_and(Query *root, RelOptInfo *rel, List *paths)
 
                if (IsA(newpath, IndexPath))
                {
-                       Assert(list_length(((IndexPath *) newpath)->indexclauses) == 1);
-                       newqual = (List *) linitial(((IndexPath *) newpath)->indexclauses);
+                       newqual = ((IndexPath *) newpath)->indexclauses;
                        if (list_difference(newqual, qualsofar) == NIL)
                                continue;               /* redundant */
                }
@@ -714,108 +705,6 @@ group_clauses_by_indexkey(IndexOptInfo *index,
 }
 
 
-/*
- * group_clauses_by_indexkey_for_or
- *       Generate a list of sublists of clauses that can be used with an index
- *       to find rows matching an OR subclause.
- *
- * This is essentially just like group_clauses_by_indexkey() except that
- * we can use the given clause (or any AND subclauses of it) as well as
- * top-level restriction clauses of the relation.  Furthermore, we demand
- * that at least one such use be made, otherwise we fail and return NIL.
- * (Any path we made without such a use would be redundant with non-OR
- * indexscans.)
- *
- * XXX When we generate an indexqual list that uses both the OR subclause
- * and top-level restriction clauses, we end up with a slightly inefficient
- * plan because create_indexscan_plan is not very bright about figuring out
- * which restriction clauses are implied by the generated indexqual condition.
- * Currently we'll end up rechecking both the OR clause and the top-level
- * restriction clause as qpquals.  FIXME someday.
- */
-List *
-group_clauses_by_indexkey_for_or(IndexOptInfo *index, Expr *orsubclause)
-{
-       List       *clausegroup_list = NIL;
-       bool            matched = false;
-       int                     indexcol = 0;
-       Oid                *classes = index->classlist;
-
-       do
-       {
-               Oid                     curClass = classes[0];
-               List       *clausegroup = NIL;
-               ListCell   *item;
-
-               /* Try to match the OR subclause to the index key */
-               if (IsA(orsubclause, RestrictInfo))
-               {
-                       if (match_clause_to_indexcol(index, indexcol, curClass,
-                                                                                (RestrictInfo *) orsubclause,
-                                                                                NULL))
-                       {
-                               clausegroup = lappend(clausegroup, orsubclause);
-                               matched = true;
-                       }
-               }
-               else if (and_clause((Node *) orsubclause))
-               {
-                       foreach(item, ((BoolExpr *) orsubclause)->args)
-                       {
-                               RestrictInfo *subsubclause = (RestrictInfo *) lfirst(item);
-
-                               if (IsA(subsubclause, RestrictInfo) &&
-                                       match_clause_to_indexcol(index, indexcol, curClass,
-                                                                                        subsubclause,
-                                                                                        NULL))
-                               {
-                                       clausegroup = lappend(clausegroup, subsubclause);
-                                       matched = true;
-                               }
-                       }
-               }
-
-               /*
-                * If we found no clauses for this indexkey in the OR subclause
-                * itself, try looking in the rel's top-level restriction list.
-                *
-                * XXX should we always search the top-level list?      Slower but could
-                * sometimes yield a better plan.
-                */
-               if (clausegroup == NIL)
-               {
-                       foreach(item, index->rel->baserestrictinfo)
-                       {
-                               RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
-
-                               if (match_clause_to_indexcol(index, indexcol, curClass,
-                                                                                        rinfo,
-                                                                                        NULL))
-                                       clausegroup = lappend(clausegroup, rinfo);
-                       }
-               }
-
-               /*
-                * If still no clauses match this key, we're done; we don't want
-                * to look at keys to its right.
-                */
-               if (clausegroup == NIL)
-                       break;
-
-               clausegroup_list = lappend(clausegroup_list, clausegroup);
-
-               indexcol++;
-               classes++;
-       } while (!DoneMatchingIndexKeys(classes));
-
-       /* if OR clause was not used then forget it, per comments above */
-       if (!matched)
-               return NIL;
-
-       return clausegroup_list;
-}
-
-
 /*
  * match_clause_to_indexcol()
  *       Determines whether a restriction clause matches a column of an index.
@@ -2017,7 +1906,7 @@ find_clauses_for_join(Query *root, RelOptInfo *rel,
  *       of RestrictInfos.
  *
  * This is used to flatten out the result of group_clauses_by_indexkey()
- * or one of its sibling routines, to produce an indexclauses list.
+ * to produce an indexclauses list.
  */
 List *
 flatten_clausegroups_list(List *clausegroups)
@@ -2030,39 +1919,6 @@ flatten_clausegroups_list(List *clausegroups)
        return allclauses;
 }
 
-/*
- * make_expr_from_indexclauses()
- *       Given an indexclauses structure, produce an ordinary boolean expression.
- *
- * This consists of stripping out the RestrictInfo nodes and inserting
- * explicit AND and OR nodes as needed.  There's not much to it, but
- * the functionality is needed in a few places, so centralize the logic.
- */
-Expr *
-make_expr_from_indexclauses(List *indexclauses)
-{
-       List       *orclauses = NIL;
-       ListCell   *orlist;
-
-       /* There's no such thing as an indexpath with zero scans */
-       Assert(indexclauses != NIL);
-
-       foreach(orlist, indexclauses)
-       {
-               List       *andlist = (List *) lfirst(orlist);
-
-               /* Strip RestrictInfos */
-               andlist = get_actual_clauses(andlist);
-               /* Insert AND node if needed, and add to orclauses list */
-               orclauses = lappend(orclauses, make_ands_explicit(andlist));
-       }
-
-       if (list_length(orclauses) > 1)
-               return make_orclause(orclauses);
-       else
-               return (Expr *) linitial(orclauses);
-}
-
 
 /****************************************************************************
  *                             ----  ROUTINES TO CHECK OPERANDS  ----
@@ -2403,7 +2259,7 @@ match_special_index_operator(Expr *clause, Oid opclass,
  *
  * The input list is ordered by index key, and so the output list is too.
  * (The latter is not depended on by any part of the planner, so far as I can
- * tell; but some parts of the executor do assume that the indxqual list
+ * tell; but some parts of the executor do assume that the indexqual list
  * ultimately delivered to the executor is so ordered. One such place is
  * _bt_preprocess_keys() in the btree support. Perhaps that ought to be fixed
  * someday --- tgl 7/00)
index c30c26562c5aadf80ccd230b61c282d7b6231eb1..fb055400d41a3c759712e0f7ae84b168abb01c98 100644 (file)
@@ -8,32 +8,19 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.68 2005/04/21 02:28:01 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.69 2005/04/25 01:30:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
-#include "optimizer/clauses.h"
 #include "optimizer/cost.h"
-#include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
+#include "optimizer/planmain.h"
 #include "optimizer/restrictinfo.h"
 
 
-static IndexPath *best_or_subclause_indexes(Query *root, RelOptInfo *rel,
-                                                 List *subclauses);
-static bool best_or_subclause_index(Query *root,
-                                               RelOptInfo *rel,
-                                               Expr *subclause,
-                                               IndexOptInfo **retIndexInfo,
-                                               List **retIndexClauses,
-                                               List **retIndexQuals,
-                                               Cost *retStartupCost,
-                                               Cost *retTotalCost);
-
-
 /*----------
  * create_or_index_quals
  *       Examine join OR-of-AND quals to see if any useful restriction OR
@@ -94,7 +81,7 @@ static bool best_or_subclause_index(Query *root,
 bool
 create_or_index_quals(Query *root, RelOptInfo *rel)
 {
-       IndexPath  *bestpath = NULL;
+       BitmapOrPath *bestpath = NULL;
        RestrictInfo *bestrinfo = NULL;
        List       *newrinfos;
        RestrictInfo *or_rinfo;
@@ -103,8 +90,7 @@ create_or_index_quals(Query *root, RelOptInfo *rel)
        ListCell   *i;
 
        /*
-        * We use the best_or_subclause_indexes() machinery to locate the best
-        * combination of restriction subclauses.  Note we must ignore any
+        * Find potentially interesting OR joinclauses.  We must ignore any
         * joinclauses that are not marked valid_everywhere, because they
         * cannot be pushed down due to outer-join rules.
         */
@@ -120,18 +106,31 @@ create_or_index_quals(Query *root, RelOptInfo *rel)
                        if (restriction_is_or_clause(rinfo) &&
                                rinfo->valid_everywhere)
                        {
-                               IndexPath  *pathnode;
-
-                               pathnode = best_or_subclause_indexes(root,
-                                                                                                        rel,
-                                                                  ((BoolExpr *) rinfo->orclause)->args);
-
-                               if (pathnode)
+                               /*
+                                * Use the generate_bitmap_or_paths() machinery to estimate
+                                * the value of each OR clause.  We can use regular
+                                * restriction clauses along with the OR clause contents to
+                                * generate indexquals.  We pass outer_relids = NULL so that
+                                * sub-clauses that are actually joins will be ignored.
+                                */
+                               List *orpaths;
+                               ListCell *k;
+
+                               orpaths = generate_bitmap_or_paths(root, rel,
+                                                                                                  list_make1(rinfo),
+                                                                                                  rel->baserestrictinfo,
+                                                                                                  false, NULL);
+
+                               /* Locate the cheapest OR path */
+                               foreach(k, orpaths)
                                {
+                                       BitmapOrPath   *path = (BitmapOrPath *) lfirst(k);
+
+                                       Assert(IsA(path, BitmapOrPath));
                                        if (bestpath == NULL ||
-                                       pathnode->path.total_cost < bestpath->path.total_cost)
+                                               path->path.total_cost < bestpath->path.total_cost)
                                        {
-                                               bestpath = pathnode;
+                                               bestpath = path;
                                                bestrinfo = rinfo;
                                        }
                                }
@@ -144,13 +143,14 @@ create_or_index_quals(Query *root, RelOptInfo *rel)
                return false;
 
        /*
-        * Convert the indexclauses structure to a RestrictInfo tree, and add
-        * it to the rel's restriction list.
+        * Convert the path's indexclauses structure to a RestrictInfo tree,
+        * and add it to the rel's restriction list.
         */
-       newrinfos = make_restrictinfo_from_indexclauses(bestpath->indexclauses,
-                                                                                                       true, true);
+       newrinfos = create_bitmap_restriction((Path *) bestpath);
        Assert(list_length(newrinfos) == 1);
        or_rinfo = (RestrictInfo *) linitial(newrinfos);
+       Assert(IsA(or_rinfo, RestrictInfo));
+
        rel->baserestrictinfo = list_concat(rel->baserestrictinfo, newrinfos);
 
        /*
@@ -176,242 +176,3 @@ create_or_index_quals(Query *root, RelOptInfo *rel)
        /* Tell caller to recompute rel's rows estimate */
        return true;
 }
-
-/*
- * create_or_index_paths
- *       Creates multi-scan index paths for indexes that match OR clauses.
- *
- * 'rel' is the relation entry for which the paths are to be created
- *
- * Returns nothing, but adds paths to rel->pathlist via add_path().
- *
- * Note: check_partial_indexes() must have been run previously.
- */
-void
-create_or_index_paths(Query *root, RelOptInfo *rel)
-{
-       ListCell   *l;
-
-       /*
-        * Check each restriction clause to see if it is an OR clause, and if
-        * so, try to make a path using it.
-        */
-       foreach(l, rel->baserestrictinfo)
-       {
-               RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
-
-               if (restriction_is_or_clause(rinfo))
-               {
-                       IndexPath  *pathnode;
-
-                       pathnode = best_or_subclause_indexes(root,
-                                                                                                rel,
-                                                                  ((BoolExpr *) rinfo->orclause)->args);
-
-                       if (pathnode)
-                               add_path(rel, (Path *) pathnode);
-               }
-       }
-}
-
-/*
- * best_or_subclause_indexes
- *       Determine the best index to be used in conjunction with each subclause
- *       of an OR clause, and build a Path for a multi-index scan.
- *
- * 'rel' is the node of the relation to be scanned
- * 'subclauses' are the subclauses of the OR clause (must be the modified
- *             form that includes sub-RestrictInfo clauses)
- *
- * Returns an IndexPath if successful, or NULL if it is not possible to
- * find an index for each OR subclause.
- *
- * NOTE: we choose each scan on the basis of its total cost, ignoring startup
- * cost.  This is reasonable as long as all index types have zero or small
- * startup cost, but we might have to work harder if any index types with
- * nontrivial startup cost are ever invented.
- *
- * This routine also creates the indexqual list that will be needed by
- * the executor.  The indexqual list has one entry for each scan of the base
- * rel, which is a sublist of indexqual conditions to apply in that scan.
- * The implicit semantics are AND across each sublist of quals, and OR across
- * the toplevel list (note that the executor takes care not to return any
- * single tuple more than once).
- */
-static IndexPath *
-best_or_subclause_indexes(Query *root,
-                                                 RelOptInfo *rel,
-                                                 List *subclauses)
-{
-       List       *infos = NIL;
-       List       *clauses = NIL;
-       List       *quals = NIL;
-       Cost            path_startup_cost = 0;
-       Cost            path_total_cost = 0;
-       ListCell   *slist;
-       IndexPath  *pathnode;
-
-       /* Gather info for each OR subclause */
-       foreach(slist, subclauses)
-       {
-               Expr       *subclause = lfirst(slist);
-               IndexOptInfo *best_indexinfo;
-               List       *best_indexclauses;
-               List       *best_indexquals;
-               Cost            best_startup_cost;
-               Cost            best_total_cost;
-
-               if (!best_or_subclause_index(root, rel, subclause,
-                                                                        &best_indexinfo,
-                                                                        &best_indexclauses, &best_indexquals,
-                                                                  &best_startup_cost, &best_total_cost))
-                       return NULL;            /* failed to match this subclause */
-
-               infos = lappend(infos, best_indexinfo);
-               clauses = lappend(clauses, best_indexclauses);
-               quals = lappend(quals, best_indexquals);
-
-               /*
-                * Path startup_cost is the startup cost for the first index scan
-                * only; startup costs for later scans will be paid later on, so
-                * they just get reflected in total_cost.
-                *
-                * Total cost is sum of the per-scan costs.
-                */
-               if (slist == list_head(subclauses))             /* first scan? */
-                       path_startup_cost = best_startup_cost;
-               path_total_cost += best_total_cost;
-       }
-
-       /* We succeeded, so build an IndexPath node */
-       pathnode = makeNode(IndexPath);
-
-       pathnode->path.pathtype = T_IndexScan;
-       pathnode->path.parent = rel;
-       pathnode->path.startup_cost = path_startup_cost;
-       pathnode->path.total_cost = path_total_cost;
-
-       /*
-        * This is an IndexScan, but the overall result will consist of tuples
-        * extracted in multiple passes (one for each subclause of the OR), so
-        * the result cannot be claimed to have any particular ordering.
-        */
-       pathnode->path.pathkeys = NIL;
-
-       pathnode->indexinfo = infos;
-       pathnode->indexclauses = clauses;
-       pathnode->indexquals = quals;
-
-       /* It's not an innerjoin path. */
-       pathnode->isjoininner = false;
-
-       /* We don't actually care what order the index scans in. */
-       pathnode->indexscandir = NoMovementScanDirection;
-
-       /*
-        * The number of rows is the same as the parent rel's estimate, since
-        * this isn't a join inner indexscan.
-        */
-       pathnode->rows = rel->rows;
-
-       return pathnode;
-}
-
-/*
- * best_or_subclause_index
- *       Determines which is the best index to be used with a subclause of an
- *       OR clause by estimating the cost of using each index and selecting
- *       the least expensive (considering total cost only, for now).
- *
- * Returns FALSE if no index exists that can be used with this OR subclause;
- * in that case the output parameters are not set.
- *
- * 'rel' is the node of the relation to be scanned
- * 'subclause' is the OR subclause being considered
- *
- * '*retIndexInfo' gets the IndexOptInfo of the best index
- * '*retIndexClauses' gets a list of the index clauses for the best index
- * '*retIndexQuals' gets a list of the expanded indexquals for the best index
- * '*retStartupCost' gets the startup cost of a scan with that index
- * '*retTotalCost' gets the total cost of a scan with that index
- */
-static bool
-best_or_subclause_index(Query *root,
-                                               RelOptInfo *rel,
-                                               Expr *subclause,
-                                               IndexOptInfo **retIndexInfo,    /* return value */
-                                               List **retIndexClauses, /* return value */
-                                               List **retIndexQuals,   /* return value */
-                                               Cost *retStartupCost,   /* return value */
-                                               Cost *retTotalCost)             /* return value */
-{
-       bool            found = false;
-       ListCell   *ilist;
-
-       foreach(ilist, rel->indexlist)
-       {
-               IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
-               List       *indexclauses;
-               List       *indexquals;
-               IndexPath       subclause_path;
-
-               /*
-                * Ignore partial indexes that do not match the query.  If predOK
-                * is true then the index's predicate is implied by top-level
-                * restriction clauses, so we can use it.  However, it might also
-                * be implied by the current OR subclause (perhaps in conjunction
-                * with the top-level clauses), in which case we can use it for this
-                * particular scan.
-                *
-                * XXX this code is partially redundant with logic in
-                * group_clauses_by_indexkey_for_or(); consider refactoring.
-                */
-               if (index->indpred != NIL && !index->predOK)
-               {
-                       List   *subclauserinfos;
-
-                       if (and_clause((Node *) subclause))
-                               subclauserinfos = list_copy(((BoolExpr *) subclause)->args);
-                       else if (IsA(subclause, RestrictInfo))
-                               subclauserinfos = list_make1(subclause);
-                       else
-                               continue;               /* probably can't happen */
-                       if (!pred_test(index->indpred,
-                                                  list_concat(subclauserinfos, 
-                                                                          rel->baserestrictinfo)))
-                               continue;
-               }
-
-               /* Collect index clauses usable with this index */
-               indexclauses = group_clauses_by_indexkey_for_or(index, subclause);
-
-               /*
-                * Ignore index if it doesn't match the subclause at all; except
-                * that if it's a partial index matching the current OR subclause,
-                * consider it anyway, since effectively we are using the index
-                * predicate to match the subclause.  (Note: we exclude partial
-                * indexes that are predOK; else such a partial index would be
-                * considered to match *every* OR subclause, generating bogus OR
-                * plans that are redundant with the basic scan on that index.)
-                */
-               if (indexclauses == NIL && (index->indpred == NIL || index->predOK))
-                       continue;
-
-               /* Convert clauses to indexquals the executor can handle */
-               indexquals = expand_indexqual_conditions(index, indexclauses);
-
-               cost_index(&subclause_path, root, index, indexquals, false);
-
-               if (!found || subclause_path.path.total_cost < *retTotalCost)
-               {
-                       *retIndexInfo = index;
-                       *retIndexClauses = flatten_clausegroups_list(indexclauses);
-                       *retIndexQuals = indexquals;
-                       *retStartupCost = subclause_path.path.startup_cost;
-                       *retTotalCost = subclause_path.path.total_cost;
-                       found = true;
-               }
-       }
-
-       return found;
-}
index 21af9f7e3e2504975dc3dfab277db016c4675386..0ce65a93d192c2a66a316aca8ee56bdc7230fb62 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.184 2005/04/23 01:29:15 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.185 2005/04/25 01:30:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,13 +47,14 @@ static Plan *create_unique_plan(Query *root, UniquePath *best_path);
 static SeqScan *create_seqscan_plan(Query *root, Path *best_path,
                                        List *tlist, List *scan_clauses);
 static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path,
-                                         List *tlist, List *scan_clauses);
+                                         List *tlist, List *scan_clauses,
+                                         List **nonlossy_clauses);
 static BitmapHeapScan *create_bitmap_scan_plan(Query *root,
                                                                                           BitmapHeapPath *best_path,
                                                                                           List *tlist, List *scan_clauses);
-static Plan *create_bitmap_subplan(Query *root, Path *bitmapqual);
+static Plan *create_bitmap_subplan(Query *root, Path *bitmapqual,
+                                                                  List **qual, List **indexqual);
 static List *create_bitmap_qual(Path *bitmapqual);
-static List *create_bitmap_indxqual(Path *bitmapqual);
 static TidScan *create_tidscan_plan(Query *root, TidPath *best_path,
                                        List *tlist, List *scan_clauses);
 static SubqueryScan *create_subqueryscan_plan(Query *root, Path *best_path,
@@ -66,31 +67,26 @@ static MergeJoin *create_mergejoin_plan(Query *root, MergePath *best_path,
                                          Plan *outer_plan, Plan *inner_plan);
 static HashJoin *create_hashjoin_plan(Query *root, HashPath *best_path,
                                         Plan *outer_plan, Plan *inner_plan);
-static void fix_indxqual_references(List *indexquals, IndexPath *index_path,
-                                               List **fixed_indexquals,
-                                               List **indxstrategy,
-                                               List **indxsubtype,
-                                               List **indxlossy);
-static void fix_indxqual_sublist(List *indexqual, IndexOptInfo *index,
-                                        List **fixed_quals,
-                                        List **strategy,
-                                        List **subtype,
-                                        List **lossy);
-static Node *fix_indxqual_operand(Node *node, IndexOptInfo *index,
+static void fix_indexqual_references(List *indexquals, IndexPath *index_path,
+                                                List **fixed_indexquals,
+                                                List **nonlossy_indexquals,
+                                                List **indexstrategy,
+                                                List **indexsubtype);
+static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index,
                                                                  Oid *opclass);
 static List *get_switched_clauses(List *clauses, Relids outerrelids);
 static void copy_path_costsize(Plan *dest, Path *src);
 static void copy_plan_costsize(Plan *dest, Plan *src);
 static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
 static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
-                          List *indxid, List *indxqual, List *indxqualorig,
-                          List *indxstrategy, List *indxsubtype, List *indxlossy,
+                          Oid indexid, List *indexqual, List *indexqualorig,
+                          List *indexstrategy, List *indexsubtype,
                           ScanDirection indexscandir);
-static BitmapIndexScan *make_bitmap_indexscan(Index scanrelid, Oid indxid,
-                                                                                         List *indxqual,
-                                                                                         List *indxqualorig,
-                                                                                         List *indxstrategy,
-                                                                                         List *indxsubtype);
+static BitmapIndexScan *make_bitmap_indexscan(Index scanrelid, Oid indexid,
+                                                                                         List *indexqual,
+                                                                                         List *indexqualorig,
+                                                                                         List *indexstrategy,
+                                                                                         List *indexsubtype);
 static BitmapHeapScan *make_bitmap_heapscan(List *qptlist,
                                                                                        List *qpqual,
                                                                                        Plan *lefttree,
@@ -236,7 +232,8 @@ create_scan_plan(Query *root, Path *best_path)
                        plan = (Scan *) create_indexscan_plan(root,
                                                                                                  (IndexPath *) best_path,
                                                                                                  tlist,
-                                                                                                 scan_clauses);
+                                                                                                 scan_clauses,
+                                                                                                 NULL);
                        break;
 
                case T_BitmapHeapScan:
@@ -701,121 +698,84 @@ create_seqscan_plan(Query *root, Path *best_path,
  *       Returns an indexscan plan for the base relation scanned by 'best_path'
  *       with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  *
- * The indexquals list of the path contains a sublist of implicitly-ANDed
- * qual conditions for each scan of the index(es); if there is more than one
- * scan then the retrieved tuple sets are ORed together.  The indexquals
- * and indexinfo lists must have the same length, ie, the number of scans
- * that will occur.  Note it is possible for a qual condition sublist
- * to be empty --- then no index restrictions will be applied during that
- * scan.
+ * The indexquals list of the path contains implicitly-ANDed qual conditions.
+ * The list can be empty --- then no index restrictions will be applied during
+ * the scan.
+ *
+ * If nonlossy_clauses isn't NULL, *nonlossy_clauses receives a list of the
+ * nonlossy indexquals.
  */
 static IndexScan *
 create_indexscan_plan(Query *root,
                                          IndexPath *best_path,
                                          List *tlist,
-                                         List *scan_clauses)
+                                         List *scan_clauses,
+                                         List **nonlossy_clauses)
 {
-       List       *indxquals = best_path->indexquals;
+       List       *indexquals = best_path->indexquals;
        Index           baserelid = best_path->path.parent->relid;
+       Oid                     indexoid = best_path->indexinfo->indexoid;
        List       *qpqual;
-       Expr       *indxqual_or_expr = NULL;
-       List       *stripped_indxquals;
-       List       *fixed_indxquals;
-       List       *indxstrategy;
-       List       *indxsubtype;
-       List       *indxlossy;
-       List       *indexids;
-       ListCell   *l;
+       List       *stripped_indexquals;
+       List       *fixed_indexquals;
+       List       *nonlossy_indexquals;
+       List       *indexstrategy;
+       List       *indexsubtype;
        IndexScan  *scan_plan;
 
        /* it should be a base rel... */
        Assert(baserelid > 0);
        Assert(best_path->path.parent->rtekind == RTE_RELATION);
 
-       /* Build list of index OIDs */
-       indexids = NIL;
-       foreach(l, best_path->indexinfo)
-       {
-               IndexOptInfo *index = (IndexOptInfo *) lfirst(l);
-
-               indexids = lappend_oid(indexids, index->indexoid);
-       }
-
        /*
         * Build "stripped" indexquals structure (no RestrictInfos) to pass to
-        * executor as indxqualorig
+        * executor as indexqualorig
         */
-       stripped_indxquals = NIL;
-       foreach(l, indxquals)
-       {
-               List       *andlist = (List *) lfirst(l);
-
-               stripped_indxquals = lappend(stripped_indxquals,
-                                                                        get_actual_clauses(andlist));
-       }
+       stripped_indexquals = get_actual_clauses(indexquals);
 
        /*
         * The executor needs a copy with the indexkey on the left of each
         * clause and with index attr numbers substituted for table ones. This
         * pass also gets strategy info and looks for "lossy" operators.
         */
-       fix_indxqual_references(indxquals, best_path,
-                                                       &fixed_indxquals,
-                                                       &indxstrategy, &indxsubtype, &indxlossy);
+       fix_indexqual_references(indexquals, best_path,
+                                                        &fixed_indexquals,
+                                                        &nonlossy_indexquals,
+                                                        &indexstrategy,
+                                                        &indexsubtype);
+
+       /* pass back nonlossy quals if caller wants 'em */
+       if (nonlossy_clauses)
+               *nonlossy_clauses = nonlossy_indexquals;
 
        /*
-        * If this is a innerjoin scan, the indexclauses will contain join
+        * If this is an innerjoin scan, the indexclauses will contain join
         * clauses that are not present in scan_clauses (since the passed-in
         * value is just the rel's baserestrictinfo list).  We must add these
         * clauses to scan_clauses to ensure they get checked.  In most cases
         * we will remove the join clauses again below, but if a join clause
         * contains a special operator, we need to make sure it gets into the
         * scan_clauses.
+        *
+        * Note: pointer comparison should be enough to determine RestrictInfo
+        * matches.
         */
        if (best_path->isjoininner)
-       {
-               /*
-                * We don't currently support OR indexscans in joins, so we only
-                * need to worry about the plain AND case.      Also, pointer
-                * comparison should be enough to determine RestrictInfo matches.
-                */
-               Assert(list_length(best_path->indexclauses) == 1);
-               scan_clauses = list_union_ptr(scan_clauses,
-                                                        (List *) linitial(best_path->indexclauses));
-       }
-
-       /* Reduce RestrictInfo list to bare expressions */
-       scan_clauses = get_actual_clauses(scan_clauses);
+               scan_clauses = list_union_ptr(scan_clauses, best_path->indexclauses);
 
        /*
         * The qpqual list must contain all restrictions not automatically
         * handled by the index.  All the predicates in the indexquals will be
         * checked (either by the index itself, or by nodeIndexscan.c), but if
-        * there are any "special" operators involved then they must be added
-        * to qpqual.  The upshot is that qpquals must contain scan_clauses
-        * minus whatever appears in indxquals.
+        * there are any "special" operators involved then they must be included
+        * in qpqual.  Also, any lossy index operators must be rechecked in
+        * the qpqual.  The upshot is that qpquals must contain scan_clauses
+        * minus whatever appears in nonlossy_indexquals.
         */
-       if (list_length(indxquals) > 1)
-       {
-               /*
-                * Build an expression representation of the indexqual, expanding
-                * the implicit OR and AND semantics of the first- and
-                * second-level lists.  (The odds that this will exactly match any
-                * scan_clause are not great; perhaps we need more smarts here.)
-                */
-               indxqual_or_expr = make_expr_from_indexclauses(indxquals);
-               qpqual = list_difference(scan_clauses, list_make1(indxqual_or_expr));
-       }
-       else
-       {
-               /*
-                * Here, we can simply treat the first sublist as an independent
-                * set of qual expressions, since there is no top-level OR
-                * behavior.
-                */
-               Assert(stripped_indxquals != NIL);
-               qpqual = list_difference(scan_clauses, linitial(stripped_indxquals));
-       }
+       qpqual = list_difference_ptr(scan_clauses, nonlossy_indexquals);
+
+       /* Reduce RestrictInfo list to bare expressions */
+       qpqual = get_actual_clauses(qpqual);
 
        /* Sort clauses into best execution order */
        qpqual = order_qual_clauses(root, qpqual);
@@ -824,12 +784,11 @@ create_indexscan_plan(Query *root,
        scan_plan = make_indexscan(tlist,
                                                           qpqual,
                                                           baserelid,
-                                                          indexids,
-                                                          fixed_indxquals,
-                                                          stripped_indxquals,
-                                                          indxstrategy,
-                                                          indxsubtype,
-                                                          indxlossy,
+                                                          indexoid,
+                                                          fixed_indexquals,
+                                                          stripped_indexquals,
+                                                          indexstrategy,
+                                                          indexsubtype,
                                                           best_path->indexscandir);
 
        copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
@@ -861,14 +820,9 @@ create_bitmap_scan_plan(Query *root,
        Assert(baserelid > 0);
        Assert(best_path->path.parent->rtekind == RTE_RELATION);
 
-       /* Process the bitmapqual tree into a Plan tree */
-       bitmapqualplan = create_bitmap_subplan(root, best_path->bitmapqual);
-
-       /* Process the bitmapqual tree into an expression tree, too */
-       bitmapqualorig = create_bitmap_qual(best_path->bitmapqual);
-
-       /* Also extract the true index conditions */
-       indexquals = create_bitmap_indxqual(best_path->bitmapqual);
+       /* Process the bitmapqual tree into a Plan tree and qual lists */
+       bitmapqualplan = create_bitmap_subplan(root, best_path->bitmapqual,
+                                                                                  &bitmapqualorig, &indexquals);
 
        /* Reduce RestrictInfo list to bare expressions */
        scan_clauses = get_actual_clauses(scan_clauses);
@@ -922,70 +876,101 @@ create_bitmap_scan_plan(Query *root,
 
 /*
  * Given a bitmapqual tree, generate the Plan tree that implements it
+ *
+ * As byproducts, we also return in *qual and *indexqual the qual lists
+ * (in implicit-AND form, without RestrictInfos) describing the original index
+ * conditions and the generated indexqual conditions.  The latter is made to
+ * exclude lossy index operators.
  */
 static Plan *
-create_bitmap_subplan(Query *root, Path *bitmapqual)
+create_bitmap_subplan(Query *root, Path *bitmapqual,
+                                         List **qual, List **indexqual)
 {
        Plan       *plan;
 
        if (IsA(bitmapqual, BitmapAndPath))
        {
                BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
-               List       *newlist = NIL;
+               List       *subplans = NIL;
+               List       *subquals = NIL;
+               List       *subindexquals = NIL;
                ListCell   *l;
 
                foreach(l, apath->bitmapquals)
                {
-                       Plan   *subplan = create_bitmap_subplan(root, lfirst(l));
-
-                       newlist = lappend(newlist, subplan);
+                       Plan   *subplan;
+                       List   *subqual;
+                       List   *subindexqual;
+
+                       subplan = create_bitmap_subplan(root, (Path *) lfirst(l),
+                                                                                       &subqual, &subindexqual);
+                       subplans = lappend(subplans, subplan);
+                       subquals = list_concat(subquals, subqual);
+                       subindexquals = list_concat(subindexquals, subindexqual);
                }
-               plan = (Plan *) make_bitmap_and(newlist);
+               plan = (Plan *) make_bitmap_and(subplans);
                plan->startup_cost = apath->path.startup_cost;
                plan->total_cost = apath->path.total_cost;
                plan->plan_rows =
                        clamp_row_est(apath->bitmapselectivity * apath->path.parent->tuples);
                plan->plan_width = 0;   /* meaningless */
+               *qual = subquals;
+               *indexqual = subindexquals;
        }
        else if (IsA(bitmapqual, BitmapOrPath))
        {
                BitmapOrPath *opath = (BitmapOrPath *) bitmapqual;
-               List       *newlist = NIL;
+               List       *subplans = NIL;
+               List       *subquals = NIL;
+               List       *subindexquals = NIL;
                ListCell   *l;
 
                foreach(l, opath->bitmapquals)
                {
-                       Plan   *subplan = create_bitmap_subplan(root, lfirst(l));
-
-                       newlist = lappend(newlist, subplan);
+                       Plan   *subplan;
+                       List   *subqual;
+                       List   *subindexqual;
+
+                       subplan = create_bitmap_subplan(root, (Path *) lfirst(l),
+                                                                                       &subqual, &subindexqual);
+                       subplans = lappend(subplans, subplan);
+                       subquals = lappend(subquals,
+                                                          make_ands_explicit(subqual));
+                       subindexquals = lappend(subindexquals,
+                                                                       make_ands_explicit(subindexqual));
                }
-               plan = (Plan *) make_bitmap_or(newlist);
+               plan = (Plan *) make_bitmap_or(subplans);
                plan->startup_cost = opath->path.startup_cost;
                plan->total_cost = opath->path.total_cost;
                plan->plan_rows =
                        clamp_row_est(opath->bitmapselectivity * opath->path.parent->tuples);
                plan->plan_width = 0;   /* meaningless */
+               *qual = list_make1(make_orclause(subquals));
+               *indexqual = list_make1(make_orclause(subindexquals));
        }
        else if (IsA(bitmapqual, IndexPath))
        {
                IndexPath *ipath = (IndexPath *) bitmapqual;
                IndexScan *iscan;
+               List   *nonlossy_clauses;
 
                /* Use the regular indexscan plan build machinery... */
-               iscan = create_indexscan_plan(root, ipath, NIL, NIL);
-               Assert(list_length(iscan->indxid) == 1);
+               iscan = create_indexscan_plan(root, ipath, NIL, NIL,
+                                                                         &nonlossy_clauses);
                /* then convert to a bitmap indexscan */
                plan = (Plan *) make_bitmap_indexscan(iscan->scan.scanrelid,
-                                                                                         linitial_oid(iscan->indxid),
-                                                                                         linitial(iscan->indxqual),
-                                                                                         linitial(iscan->indxqualorig),
-                                                                                         linitial(iscan->indxstrategy),
-                                                                                         linitial(iscan->indxsubtype));
+                                                                                         iscan->indexid,
+                                                                                         iscan->indexqual,
+                                                                                         iscan->indexqualorig,
+                                                                                         iscan->indexstrategy,
+                                                                                         iscan->indexsubtype);
                plan->startup_cost = 0.0;
                plan->total_cost = ipath->indextotalcost;
                plan->plan_rows =
                        clamp_row_est(ipath->indexselectivity * ipath->path.parent->tuples);
                plan->plan_width = 0;   /* meaningless */
+               *qual = get_actual_clauses(ipath->indexclauses);
+               *indexqual = get_actual_clauses(nonlossy_clauses);
        }
        else
        {
@@ -1041,8 +1026,7 @@ create_bitmap_qual(Path *bitmapqual)
        {
                IndexPath *ipath = (IndexPath *) bitmapqual;
 
-               Assert(list_length(ipath->indexclauses) == 1);
-               result = get_actual_clauses(linitial(ipath->indexclauses));
+               result = get_actual_clauses(ipath->indexclauses);
        }
        else
        {
@@ -1054,132 +1038,27 @@ create_bitmap_qual(Path *bitmapqual)
 }
 
 /*
- * Same as above, except extract the indxqual conditions (which are different
- * if there are special index operators or lossy operators involved).
- *
- * The result essentially represents the conditions the indexscan guarantees
- * to enforce, which may be weaker than the original qual expressions.
+ * Given a bitmapqual tree, generate the equivalent RestrictInfo list.
  */
-static List *
-create_bitmap_indxqual(Path *bitmapqual)
+List *
+create_bitmap_restriction(Path *bitmapqual)
 {
-       List       *result;
-       List       *sublist;
+       List       *bitmapquals;
+       List       *bitmapclauses;
        ListCell   *l;
 
-       if (IsA(bitmapqual, BitmapAndPath))
-       {
-               BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
+       bitmapquals = create_bitmap_qual(bitmapqual);
 
-               result = NIL;
-               foreach(l, apath->bitmapquals)
-               {
-                       sublist = create_bitmap_indxqual(lfirst(l));
-                       result = list_concat(result, sublist);
-               }
-       }
-       else if (IsA(bitmapqual, BitmapOrPath))
+       /* must convert qual list to restrictinfos ... painful ... */
+       bitmapclauses = NIL;
+       foreach(l, bitmapquals)
        {
-               BitmapOrPath *opath = (BitmapOrPath *) bitmapqual;
-               List       *newlist = NIL;
-
-               foreach(l, opath->bitmapquals)
-               {
-                       sublist = create_bitmap_indxqual(lfirst(l));
-                       if (sublist == NIL)
-                       {
-                               /* constant TRUE input yields constant TRUE OR result */
-                               return NIL;
-                       }
-                       newlist = lappend(newlist, make_ands_explicit(sublist));
-               }
-               result = list_make1(make_orclause(newlist));
-       }
-       else if (IsA(bitmapqual, IndexPath))
-       {
-               IndexPath *ipath = (IndexPath *) bitmapqual;
-               IndexOptInfo *index;
-
-               Assert(list_length(ipath->indexinfo) == 1);
-               index = linitial(ipath->indexinfo);
-
-               /*
-                * We have to remove "lossy" index operators from the result, since
-                * the index isn't guaranteeing they are enforced.  (This will lead
-                * to the operators being rechecked as qpquals of the BitmapHeapScan
-                * node.)
-                *
-                * XXX look at restructuring to share code better with
-                * fix_indxqual_references()
-                */
-               result = NIL;
-               Assert(list_length(ipath->indexquals) == 1);
-               foreach(l, (List *) linitial(ipath->indexquals))
-               {
-                       RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
-                       OpExpr     *clause;
-                       Oid                     opno;
-                       Node       *indexkey;
-                       Oid                     opclass;
-                       int                     stratno;
-                       Oid                     stratsubtype;
-                       bool            recheck;
-
-                       Assert(IsA(rinfo, RestrictInfo));
-                       clause = (OpExpr *) rinfo->clause;
-                       if (!IsA(clause, OpExpr) || list_length(clause->args) != 2)
-                               elog(ERROR, "indexqual clause is not binary opclause");
-                       opno = clause->opno;
-
-                       /*
-                        * Check to see if the indexkey is on the right; if so, commute
-                        * the operator.  The indexkey should be the side that refers to
-                        * (only) the base relation.
-                        */
-                       if (!bms_equal(rinfo->left_relids, index->rel->relids))
-                       {
-                               opno = get_commutator(opno);
-                               if (!OidIsValid(opno))
-                                       elog(ERROR, "could not find commutator for operator %u",
-                                                clause->opno);
-                               indexkey = lsecond(clause->args);
-                       }
-                       else
-                               indexkey = linitial(clause->args);
-
-                       /*
-                        * Identify the index attribute and get the index opclass.
-                        * We use fix_indxqual_operand() which does a little more
-                        * than we really need, but it will do.
-                        */
-                       (void) fix_indxqual_operand(indexkey,
-                                                                               index,
-                                                                               &opclass);
-
-                       /*
-                        * Look up the (possibly commuted) operator in the operator class
-                        * to get its strategy numbers and the recheck indicator.  This
-                        * also double-checks that we found an operator matching the
-                        * index.
-                        */
-                       get_op_opclass_properties(opno, opclass,
-                                                                         &stratno, &stratsubtype, &recheck);
-
-                       /*
-                        * Finally, we can include the clause in the result if it's
-                        * not a lossy operator.
-                        */
-                       if (!recheck)
-                               result = lappend(result, clause);
-               }
-       }
-       else
-       {
-               elog(ERROR, "unrecognized node type: %d", nodeTag(bitmapqual));
-               result = NIL;                   /* keep compiler quiet */
+               bitmapclauses = lappend(bitmapclauses,
+                                                               make_restrictinfo((Expr *) lfirst(l),
+                                                                                                 true, true));
        }
 
-       return result;
+       return bitmapclauses;
 }
 
 /*
@@ -1299,10 +1178,7 @@ create_nestloop_plan(Query *root,
                 * An index is being used to reduce the number of tuples scanned
                 * in the inner relation.  If there are join clauses being used
                 * with the index, we may remove those join clauses from the list
-                * of clauses that have to be checked as qpquals at the join node
-                * --- but only if there's just one indexscan in the inner path
-                * (otherwise, several different sets of clauses are being ORed
-                * together).
+                * of clauses that have to be checked as qpquals at the join node.
                 *
                 * We can also remove any join clauses that are redundant with those
                 * being used in the index scan; prior redundancy checks will not
@@ -1313,15 +1189,13 @@ create_nestloop_plan(Query *root,
                 * not a special innerjoin path.
                 */
                IndexPath  *innerpath = (IndexPath *) best_path->innerjoinpath;
-               List       *indexclauses = innerpath->indexclauses;
 
-               if (innerpath->isjoininner &&
-                       list_length(indexclauses) == 1)         /* single indexscan? */
+               if (innerpath->isjoininner)
                {
                        joinrestrictclauses =
                                select_nonredundant_join_clauses(root,
                                                                                                 joinrestrictclauses,
-                                                                                                linitial(indexclauses),
+                                                                                                innerpath->indexclauses,
                                                                                                 IS_OUTER_JOIN(best_path->jointype));
                }
        }
@@ -1334,21 +1208,9 @@ create_nestloop_plan(Query *root,
 
                if (innerpath->isjoininner)
                {
-                       List       *bitmapquals;
                        List       *bitmapclauses;
-                       ListCell   *l;
-
-                       bitmapquals = create_bitmap_qual(innerpath->bitmapqual);
-
-                       /* must convert qual list to restrictinfos ... painful ... */
-                       bitmapclauses = NIL;
-                       foreach(l, bitmapquals)
-                       {
-                               bitmapclauses = lappend(bitmapclauses,
-                                                                               make_restrictinfo((Expr *) lfirst(l),
-                                                                                                                 true, true));
-                       }
 
+                       bitmapclauses = create_bitmap_restriction(innerpath->bitmapqual);
                        joinrestrictclauses =
                                select_nonredundant_join_clauses(root,
                                                                                                 joinrestrictclauses,
@@ -1542,95 +1404,54 @@ create_hashjoin_plan(Query *root,
  *****************************************************************************/
 
 /*
- * fix_indxqual_references
+ * fix_indexqual_references
  *       Adjust indexqual clauses to the form the executor's indexqual
  *       machinery needs, and check for recheckable (lossy) index conditions.
  *
- * We have four tasks here:
+ * We have five tasks here:
  *     * Remove RestrictInfo nodes from the input clauses.
  *     * Index keys must be represented by Var nodes with varattno set to the
  *       index's attribute number, not the attribute number in the original rel.
  *     * If the index key is on the right, commute the clause to put it on the
- *       left.  (Someday the executor might not need this, but for now it does.)
- *     * We must construct lists of operator strategy numbers, subtypes, and
- *       recheck (lossy-operator) flags for the top-level operators of each
- *       index clause.
- *
- * Both the input list and the "fixed" output list have the form of lists of
- * sublists of qual clauses --- the top-level list has one entry for each
- * indexscan to be performed.  The semantics are OR-of-ANDs.  Note however
- * that the input list contains RestrictInfos, while the output list doesn't.
+ *       left.
+ *     * We must construct lists of operator strategy numbers and subtypes
+ *       for the top-level operators of each index clause.
+ *     * We must detect any lossy index operators.  The API is that we return
+ *       a list of the input clauses whose operators are NOT lossy.
  *
- * fixed_indexquals receives a modified copy of the indexqual list --- the
+ * fixed_indexquals receives a modified copy of the indexquals list --- the
  * original is not changed.  Note also that the copy shares no substructure
  * with the original; this is needed in case there is a subplan in it (we need
  * two separate copies of the subplan tree, or things will go awry).
  *
- * indxstrategy receives a list of integer sublists of strategy numbers.
- * indxsubtype receives a list of OID sublists of strategy subtypes.
- * indxlossy receives a list of integer sublists of lossy-operator booleans.
- */
-static void
-fix_indxqual_references(List *indexquals, IndexPath *index_path,
-                                               List **fixed_indexquals,
-                                               List **indxstrategy,
-                                               List **indxsubtype,
-                                               List **indxlossy)
-{
-       List       *index_info = index_path->indexinfo;
-       ListCell   *iq,
-                          *ii;
-
-       *fixed_indexquals = NIL;
-       *indxstrategy = NIL;
-       *indxsubtype = NIL;
-       *indxlossy = NIL;
-       forboth(iq, indexquals, ii, index_info)
-       {
-               List       *indexqual = (List *) lfirst(iq);
-               IndexOptInfo *index = (IndexOptInfo *) lfirst(ii);
-               List       *fixed_qual;
-               List       *strategy;
-               List       *subtype;
-               List       *lossy;
-
-               fix_indxqual_sublist(indexqual, index,
-                                                        &fixed_qual, &strategy, &subtype, &lossy);
-               *fixed_indexquals = lappend(*fixed_indexquals, fixed_qual);
-               *indxstrategy = lappend(*indxstrategy, strategy);
-               *indxsubtype = lappend(*indxsubtype, subtype);
-               *indxlossy = lappend(*indxlossy, lossy);
-       }
-}
-
-/*
- * Fix the sublist of indexquals to be used in a particular scan.
- *
- * For each qual clause, commute if needed to put the indexkey operand on the
- * left, and then fix its varattno.  (We do not need to change the other side
- * of the clause.)     Then determine the operator's strategy number and subtype
- * number, and check for lossy index behavior.
+ * nonlossy_indexquals receives a list of the original input clauses (with
+ * RestrictInfos) that contain non-lossy operators.
  *
- * Returns four lists:
- *             the list of fixed indexquals
- *             the integer list of strategy numbers
- *             the OID list of strategy subtypes
- *             the integer list of lossiness flags (1/0)
+ * indexstrategy receives an integer list of strategy numbers.
+ * indexsubtype receives an OID list of strategy subtypes.
  */
 static void
-fix_indxqual_sublist(List *indexqual, IndexOptInfo *index,
-                                        List **fixed_quals,
-                                        List **strategy,
-                                        List **subtype,
-                                        List **lossy)
+fix_indexqual_references(List *indexquals, IndexPath *index_path,
+                                                List **fixed_indexquals,
+                                                List **nonlossy_indexquals,
+                                                List **indexstrategy,
+                                                List **indexsubtype)
 {
+       IndexOptInfo *index = index_path->indexinfo;
        ListCell   *l;
 
-       *fixed_quals = NIL;
-       *strategy = NIL;
-       *subtype = NIL;
-       *lossy = NIL;
-       foreach(l, indexqual)
+       *fixed_indexquals = NIL;
+       *nonlossy_indexquals = NIL;
+       *indexstrategy = NIL;
+       *indexsubtype = NIL;
+
+       /*
+        * For each qual clause, commute if needed to put the indexkey operand on
+        * the left, and then fix its varattno.  (We do not need to change the
+        * other side of the clause.)  Then determine the operator's strategy
+        * number and subtype number, and check for lossy index behavior.
+        */
+       foreach(l, indexquals)
        {
                RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
                OpExpr     *clause;
@@ -1642,7 +1463,8 @@ fix_indxqual_sublist(List *indexqual, IndexOptInfo *index,
 
                Assert(IsA(rinfo, RestrictInfo));
                clause = (OpExpr *) rinfo->clause;
-               if (!IsA(clause, OpExpr) ||list_length(clause->args) != 2)
+               if (!IsA(clause, OpExpr) ||
+                       list_length(clause->args) != 2)
                        elog(ERROR, "indexqual clause is not binary opclause");
 
                /*
@@ -1666,11 +1488,12 @@ fix_indxqual_sublist(List *indexqual, IndexOptInfo *index,
                 * Now, determine which index attribute this is, change the
                 * indexkey operand as needed, and get the index opclass.
                 */
-               linitial(newclause->args) = fix_indxqual_operand(linitial(newclause->args),
-                                                                                                                index,
-                                                                                                                &opclass);
+               linitial(newclause->args) =
+                       fix_indexqual_operand(linitial(newclause->args),
+                                                                 index,
+                                                                 &opclass);
 
-               *fixed_quals = lappend(*fixed_quals, newclause);
+               *fixed_indexquals = lappend(*fixed_indexquals, newclause);
 
                /*
                 * Look up the (possibly commuted) operator in the operator class
@@ -1681,14 +1504,17 @@ fix_indxqual_sublist(List *indexqual, IndexOptInfo *index,
                get_op_opclass_properties(newclause->opno, opclass,
                                                                  &stratno, &stratsubtype, &recheck);
 
-               *strategy = lappend_int(*strategy, stratno);
-               *subtype = lappend_oid(*subtype, stratsubtype);
-               *lossy = lappend_int(*lossy, (int) recheck);
+               *indexstrategy = lappend_int(*indexstrategy, stratno);
+               *indexsubtype = lappend_oid(*indexsubtype, stratsubtype);
+
+               /* If it's not lossy, add to nonlossy_indexquals */
+               if (!recheck)
+                       *nonlossy_indexquals = lappend(*nonlossy_indexquals, rinfo);
        }
 }
 
 static Node *
-fix_indxqual_operand(Node *node, IndexOptInfo *index, Oid *opclass)
+fix_indexqual_operand(Node *node, IndexOptInfo *index, Oid *opclass)
 {
        /*
         * We represent index keys by Var nodes having the varno of the base
@@ -1923,12 +1749,11 @@ static IndexScan *
 make_indexscan(List *qptlist,
                           List *qpqual,
                           Index scanrelid,
-                          List *indxid,
-                          List *indxqual,
-                          List *indxqualorig,
-                          List *indxstrategy,
-                          List *indxsubtype,
-                          List *indxlossy,
+                          Oid indexid,
+                          List *indexqual,
+                          List *indexqualorig,
+                          List *indexstrategy,
+                          List *indexsubtype,
                           ScanDirection indexscandir)
 {
        IndexScan  *node = makeNode(IndexScan);
@@ -1940,24 +1765,23 @@ make_indexscan(List *qptlist,
        plan->lefttree = NULL;
        plan->righttree = NULL;
        node->scan.scanrelid = scanrelid;
-       node->indxid = indxid;
-       node->indxqual = indxqual;
-       node->indxqualorig = indxqualorig;
-       node->indxstrategy = indxstrategy;
-       node->indxsubtype = indxsubtype;
-       node->indxlossy = indxlossy;
-       node->indxorderdir = indexscandir;
+       node->indexid = indexid;
+       node->indexqual = indexqual;
+       node->indexqualorig = indexqualorig;
+       node->indexstrategy = indexstrategy;
+       node->indexsubtype = indexsubtype;
+       node->indexorderdir = indexscandir;
 
        return node;
 }
 
 static BitmapIndexScan *
 make_bitmap_indexscan(Index scanrelid,
-                                         Oid indxid,
-                                         List *indxqual,
-                                         List *indxqualorig,
-                                         List *indxstrategy,
-                                         List *indxsubtype)
+                                         Oid indexid,
+                                         List *indexqual,
+                                         List *indexqualorig,
+                                         List *indexstrategy,
+                                         List *indexsubtype)
 {
        BitmapIndexScan *node = makeNode(BitmapIndexScan);
        Plan       *plan = &node->scan.plan;
@@ -1968,11 +1792,11 @@ make_bitmap_indexscan(Index scanrelid,
        plan->lefttree = NULL;
        plan->righttree = NULL;
        node->scan.scanrelid = scanrelid;
-       node->indxid = indxid;
-       node->indxqual = indxqual;
-       node->indxqualorig = indxqualorig;
-       node->indxstrategy = indxstrategy;
-       node->indxsubtype = indxsubtype;
+       node->indexid = indexid;
+       node->indexqual = indexqual;
+       node->indexqualorig = indexqualorig;
+       node->indexstrategy = indexstrategy;
+       node->indexsubtype = indexsubtype;
 
        return node;
 }
index 7ddcb26833d18a46c10def20c52a178971a37a7d..0dc0f1393c8503a59e34d65455a71dee8e542bfd 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.108 2005/04/22 21:58:31 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.109 2005/04/25 01:30:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -107,18 +107,18 @@ set_plan_references(Plan *plan, List *rtable)
                        fix_expr_references(plan, (Node *) plan->targetlist);
                        fix_expr_references(plan, (Node *) plan->qual);
                        fix_expr_references(plan,
-                                                               (Node *) ((IndexScan *) plan)->indxqual);
+                                                               (Node *) ((IndexScan *) plan)->indexqual);
                        fix_expr_references(plan,
-                                                       (Node *) ((IndexScan *) plan)->indxqualorig);
+                                                       (Node *) ((IndexScan *) plan)->indexqualorig);
                        break;
                case T_BitmapIndexScan:
                        /* no need to fix targetlist and qual */
                        Assert(plan->targetlist == NIL);
                        Assert(plan->qual == NIL);
                        fix_expr_references(plan,
-                                                               (Node *) ((BitmapIndexScan *) plan)->indxqual);
+                                                       (Node *) ((BitmapIndexScan *) plan)->indexqual);
                        fix_expr_references(plan,
-                                                       (Node *) ((BitmapIndexScan *) plan)->indxqualorig);
+                                               (Node *) ((BitmapIndexScan *) plan)->indexqualorig);
                        break;
                case T_BitmapHeapScan:
                        fix_expr_references(plan, (Node *) plan->targetlist);
@@ -422,31 +422,31 @@ set_inner_join_references(Plan *inner_plan,
                 * var nodes to refer to the outer side of the join.
                 */
                IndexScan  *innerscan = (IndexScan *) inner_plan;
-               List       *indxqualorig = innerscan->indxqualorig;
+               List       *indexqualorig = innerscan->indexqualorig;
 
-               /* No work needed if indxqual refers only to its own rel... */
-               if (NumRelids((Node *) indxqualorig) > 1)
+               /* No work needed if indexqual refers only to its own rel... */
+               if (NumRelids((Node *) indexqualorig) > 1)
                {
                        Index           innerrel = innerscan->scan.scanrelid;
 
                        /* only refs to outer vars get changed in the inner qual */
-                       innerscan->indxqualorig = join_references(indxqualorig,
-                                                                                                         rtable,
-                                                                                                         outer_tlist,
-                                                                                                         NIL,
-                                                                                                         innerrel,
-                                                                                                         tlists_have_non_vars);
-                       innerscan->indxqual = join_references(innerscan->indxqual,
-                                                                                                 rtable,
-                                                                                                 outer_tlist,
-                                                                                                 NIL,
-                                                                                                 innerrel,
-                                                                                                 tlists_have_non_vars);
+                       innerscan->indexqualorig = join_references(indexqualorig,
+                                                                                                          rtable,
+                                                                                                          outer_tlist,
+                                                                                                          NIL,
+                                                                                                          innerrel,
+                                                                                                          tlists_have_non_vars);
+                       innerscan->indexqual = join_references(innerscan->indexqual,
+                                                                                                  rtable,
+                                                                                                  outer_tlist,
+                                                                                                  NIL,
+                                                                                                  innerrel,
+                                                                                                  tlists_have_non_vars);
 
                        /*
                         * We must fix the inner qpqual too, if it has join
                         * clauses (this could happen if special operators are
-                        * involved: some indxquals may get rechecked as qpquals).
+                        * involved: some indexquals may get rechecked as qpquals).
                         */
                        if (NumRelids((Node *) inner_plan->qual) > 1)
                                inner_plan->qual = join_references(inner_plan->qual,
@@ -463,26 +463,26 @@ set_inner_join_references(Plan *inner_plan,
                 * Same, but index is being used within a bitmap plan.
                 */
                BitmapIndexScan *innerscan = (BitmapIndexScan *) inner_plan;
-               List       *indxqualorig = innerscan->indxqualorig;
+               List       *indexqualorig = innerscan->indexqualorig;
 
-               /* No work needed if indxqual refers only to its own rel... */
-               if (NumRelids((Node *) indxqualorig) > 1)
+               /* No work needed if indexqual refers only to its own rel... */
+               if (NumRelids((Node *) indexqualorig) > 1)
                {
                        Index           innerrel = innerscan->scan.scanrelid;
 
                        /* only refs to outer vars get changed in the inner qual */
-                       innerscan->indxqualorig = join_references(indxqualorig,
-                                                                                                         rtable,
-                                                                                                         outer_tlist,
-                                                                                                         NIL,
-                                                                                                         innerrel,
-                                                                                                         tlists_have_non_vars);
-                       innerscan->indxqual = join_references(innerscan->indxqual,
-                                                                                                 rtable,
-                                                                                                 outer_tlist,
-                                                                                                 NIL,
-                                                                                                 innerrel,
-                                                                                                 tlists_have_non_vars);
+                       innerscan->indexqualorig = join_references(indexqualorig,
+                                                                                                          rtable,
+                                                                                                          outer_tlist,
+                                                                                                          NIL,
+                                                                                                          innerrel,
+                                                                                                          tlists_have_non_vars);
+                       innerscan->indexqual = join_references(innerscan->indexqual,
+                                                                                                  rtable,
+                                                                                                  outer_tlist,
+                                                                                                  NIL,
+                                                                                                  innerrel,
+                                                                                                  tlists_have_non_vars);
                        /* no need to fix inner qpqual */
                        Assert(inner_plan->qual == NIL);
                }
@@ -512,7 +512,7 @@ set_inner_join_references(Plan *inner_plan,
                        /*
                         * We must fix the inner qpqual too, if it has join
                         * clauses (this could happen if special operators are
-                        * involved: some indxquals may get rechecked as qpquals).
+                        * involved: some indexquals may get rechecked as qpquals).
                         */
                        if (NumRelids((Node *) inner_plan->qual) > 1)
                                inner_plan->qual = join_references(inner_plan->qual,
index 7f5a4fde63bdbd20c70aab79e89703e066d260e0..0865feae2640780239a63493cc86a931b01aa606 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.97 2005/04/19 22:35:16 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.98 2005/04/25 01:30:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1028,21 +1028,21 @@ finalize_plan(Plan *plan, List *rtable,
                        break;
 
                case T_IndexScan:
-                       finalize_primnode((Node *) ((IndexScan *) plan)->indxqual,
+                       finalize_primnode((Node *) ((IndexScan *) plan)->indexqual,
                                                          &context);
 
                        /*
-                        * we need not look at indxqualorig, since it will have the
-                        * same param references as indxqual.
+                        * we need not look at indexqualorig, since it will have the
+                        * same param references as indexqual.
                         */
                        break;
 
                case T_BitmapIndexScan:
-                       finalize_primnode((Node *) ((BitmapIndexScan *) plan)->indxqual,
+                       finalize_primnode((Node *) ((BitmapIndexScan *) plan)->indexqual,
                                                          &context);
                        /*
-                        * we need not look at indxqualorig, since it will have the
-                        * same param references as indxqual.
+                        * we need not look at indexqualorig, since it will have the
+                        * same param references as indexqual.
                         */
                        break;
 
index c1736678ab3d4faf2b49650a421546095ec17b39..19bb36136c8e180f0e2407f470a31004d290dae9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.119 2005/04/22 21:58:31 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.120 2005/04/25 01:30:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -475,13 +475,10 @@ create_index_path(Query *root,
        /* Flatten the clause-groups list to produce indexclauses list */
        allclauses = flatten_clausegroups_list(clause_groups);
 
-       /*
-        * We are making a pathnode for a single-scan indexscan; therefore,
-        * indexinfo etc should be single-element lists.
-        */
-       pathnode->indexinfo = list_make1(index);
-       pathnode->indexclauses = list_make1(allclauses);
-       pathnode->indexquals = list_make1(indexquals);
+       /* Fill in the pathnode */
+       pathnode->indexinfo = index;
+       pathnode->indexclauses = allclauses;
+       pathnode->indexquals = indexquals;
 
        pathnode->isjoininner = isjoininner;
        pathnode->indexscandir = indexscandir;
index 414a583e9a3d370d544201d351bb6240d8946033..edf268019a80a0eccba22101f21d90813f3a8e23 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.33 2005/04/22 21:58:31 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.34 2005/04/25 01:30:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -65,55 +65,10 @@ make_restrictinfo(Expr *clause, bool is_pushed_down, bool valid_everywhere)
                                                                          is_pushed_down, valid_everywhere);
 }
 
-/*
- * make_restrictinfo_from_indexclauses
- *
- * Given an indexclauses structure, convert to ordinary expression format
- * and build RestrictInfo node(s).
- *
- * The result is a List since we might need to return multiple RestrictInfos.
- *
- * This could be done as make_restrictinfo(make_expr_from_indexclauses()),
- * but if we did it that way then we would strip the original RestrictInfo
- * nodes from the index clauses and be forced to build new ones.  It's better
- * to have a specialized routine that allows sharing of RestrictInfos.
- */
-List *
-make_restrictinfo_from_indexclauses(List *indexclauses,
-                                                                       bool is_pushed_down,
-                                                                       bool valid_everywhere)
-{
-       List       *withris = NIL;
-       List       *withoutris = NIL;
-       ListCell   *orlist;
-
-       /* Empty list probably can't happen, but here's what to do */
-       if (indexclauses == NIL)
-               return NIL;
-       /* If single indexscan, just return the ANDed clauses */
-       if (list_length(indexclauses) == 1)
-               return (List *) linitial(indexclauses);
-       /* Else we need an OR RestrictInfo structure */
-       foreach(orlist, indexclauses)
-       {
-               List       *andlist = (List *) lfirst(orlist);
-
-               /* Create AND subclause with RestrictInfos */
-               withris = lappend(withris, make_ands_explicit(andlist));
-               /* And one without */
-               andlist = get_actual_clauses(andlist);
-               withoutris = lappend(withoutris, make_ands_explicit(andlist));
-       }
-       return list_make1(make_restrictinfo_internal(make_orclause(withoutris),
-                                                                                                make_orclause(withris),
-                                                                                                is_pushed_down,
-                                                                                                valid_everywhere));
-}
-
 /*
  * make_restrictinfo_internal
  *
- * Common code for the above two entry points.
+ * Common code for the main entry point and the recursive cases.
  */
 static RestrictInfo *
 make_restrictinfo_internal(Expr *clause, Expr *orclause,
index a36cdb76f7a46b5b4a3d41615da0cd881d77e866..60c5dffad7714550f95fc439be5df710a9bc915a 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.177 2005/04/14 20:03:26 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.178 2005/04/25 01:30:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3682,7 +3682,7 @@ pattern_fixed_prefix(Const *patt, Pattern_Type ptype,
  * Estimate the selectivity of a fixed prefix for a pattern match.
  *
  * A fixed prefix "foo" is estimated as the selectivity of the expression
- * "variable >= 'foo' AND variable < 'fop'" (see also indxqual.c).
+ * "variable >= 'foo' AND variable < 'fop'" (see also indxpath.c).
  *
  * We use the >= and < operators from the specified btree opclass to do the
  * estimation. The given variable and Const must be of the associated
index 530ac5117bb0e134db57677b3ba1e9e76da1368a..69e7ea6ba9b00d84aec8d44a1962a7fd112aa278 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/nodeIndexscan.h,v 1.22 2005/04/19 22:35:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/nodeIndexscan.h,v 1.23 2005/04/25 01:30:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,4 +24,14 @@ extern void ExecIndexMarkPos(IndexScanState *node);
 extern void ExecIndexRestrPos(IndexScanState *node);
 extern void ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt);
 
+/* routines exported to share code with nodeBitmapIndexscan.c */
+extern bool ExecIndexBuildScanKeys(PlanState *planstate, List *quals,
+                                          List *strategies, List *subtypes,
+                                          ExprState ***runtimeKeyInfo,
+                                          ScanKey *scanKeys, int *numScanKeys);
+extern void ExecIndexEvalRuntimeKeys(ExprContext *econtext,
+                                                                        ExprState **run_keys,
+                                                                        ScanKey scan_keys,
+                                                                        int n_keys);
+
 #endif   /* NODEINDEXSCAN_H */
index 4a3af3cece0ef908e708f041065dd4095b107da0..ba665025df6379bb38667a3507121fa70678983d 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.128 2005/04/24 18:16:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.129 2005/04/25 01:30:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -862,40 +862,28 @@ typedef ScanState SeqScanState;
 /* ----------------
  *      IndexScanState information
  *
- *             indxqualorig       execution state for indxqualorig expressions
- *             NumIndices                 number of indices in this scan
- *             IndexPtr                   current index in use
- *             MarkIndexPtr       IndexPtr for marked scan point
- *             ScanKeys                   Skey structures to scan index rels
- *             NumScanKeys                array of no of keys in each Skey struct
- *             RuntimeKeyInfo     array of array of exprstates for Skeys
+ *             indexqualorig      execution state for indexqualorig expressions
+ *             ScanKeys                   Skey structures to scan index rel
+ *             NumScanKeys                number of Skey structs
+ *             RuntimeKeyInfo     array of exprstates for Skeys
  *                                                that will be evaluated at runtime
  *             RuntimeContext     expr context for evaling runtime Skeys
  *             RuntimeKeysReady   true if runtime Skeys have been computed
- *             RelationDescs      ptr to array of relation descriptors
- *             ScanDescs                  ptr to array of scan descriptors
- *             LossyQuals                 ptr to array of qual lists for lossy operators
- *             DupHash                    hashtable for recognizing dups in multiple scan
- *             MaxHash                    max # entries we will allow in hashtable
+ *             RelationDesc       index relation descriptor
+ *             ScanDesc                   index scan descriptor
  * ----------------
  */
 typedef struct IndexScanState
 {
        ScanState       ss;                             /* its first field is NodeTag */
-       List       *indxqualorig;
-       int                     iss_NumIndices;
-       int                     iss_IndexPtr;
-       int                     iss_MarkIndexPtr;
-       ScanKey    *iss_ScanKeys;
-       int                *iss_NumScanKeys;
-       ExprState ***iss_RuntimeKeyInfo;
+       List       *indexqualorig;
+       ScanKey         iss_ScanKeys;
+       int                     iss_NumScanKeys;
+       ExprState **iss_RuntimeKeyInfo;
        ExprContext *iss_RuntimeContext;
        bool            iss_RuntimeKeysReady;
-       RelationPtr iss_RelationDescs;
-       IndexScanDescPtr iss_ScanDescs;
-       List      **iss_LossyQuals;
-       HTAB       *iss_DupHash;
-       long            iss_MaxHash;
+       Relation        iss_RelationDesc;
+       IndexScanDesc iss_ScanDesc;
 } IndexScanState;
 
 /* ----------------
index 4ab8473e562f03ed553b0725ec82b1eb6d56c0cc..6e2e01166c905c1baa34d053537e95c1fee1408d 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.78 2005/04/19 22:35:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.79 2005/04/25 01:30:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -182,22 +182,35 @@ typedef Scan SeqScan;
 /* ----------------
  *             index scan node
  *
- * Note: this can actually represent N indexscans, all on the same table
- * but potentially using different indexes, put together with OR semantics.
- * (XXX that extension should probably go away, because bitmapindexscan will
- * largely eliminate the need for it.)
+ * indexqualorig is an implicitly-ANDed list of index qual expressions, each
+ * in the same form it appeared in the query WHERE condition.  Each should
+ * be of the form (indexkey OP comparisonval) or (comparisonval OP indexkey).
+ * The indexkey is a Var or expression referencing column(s) of the index's
+ * base table.  The comparisonval might be any expression, but it won't use
+ * any columns of the base table.
+ *
+ * indexqual has the same form, but the expressions have been commuted if
+ * necessary to put the indexkeys on the left, and the indexkeys are replaced
+ * by Var nodes identifying the index columns (varattno is the index column
+ * position, not the base table's column, even though varno is for the base
+ * table).  This is a bit hokey ... would be cleaner to use a special-purpose
+ * node type that could not be mistaken for a regular Var.  But it will do
+ * for now.
+ *
+ * indexstrategy and indexsubtype are lists corresponding one-to-one with
+ * indexqual; they give information about the indexable operators that appear
+ * at the top of each indexqual.
  * ----------------
  */
 typedef struct IndexScan
 {
        Scan            scan;
-       List       *indxid;                     /* list of index OIDs (1 per scan) */
-       List       *indxqual;           /* list of sublists of index quals */
-       List       *indxqualorig;       /* the same in original form */
-       List       *indxstrategy;       /* list of sublists of strategy numbers */
-       List       *indxsubtype;        /* list of sublists of strategy subtypes */
-       List       *indxlossy;          /* list of sublists of lossy flags (ints) */
-       ScanDirection indxorderdir; /* forward or backward or don't care */
+       Oid                     indexid;                        /* OID of index to scan */
+       List       *indexqual;                  /* list of index quals (OpExprs) */
+       List       *indexqualorig;              /* the same in original form */
+       List       *indexstrategy;              /* integer list of strategy numbers */
+       List       *indexsubtype;               /* OID list of strategy subtypes */
+       ScanDirection indexorderdir;    /* forward or backward or don't care */
 } IndexScan;
 
 /* ----------------
@@ -209,19 +222,22 @@ typedef struct IndexScan
  * intermediate BitmapAnd and/or BitmapOr nodes to combine it with
  * the results of other BitmapIndexScans.
  *
+ * The fields have the same meanings as for IndexScan, except we don't
+ * store a direction flag because direction is uninteresting.
+ *
  * In a BitmapIndexScan plan node, the targetlist and qual fields are
- * not used and are always NIL.  The indxqualorig field is useless at
+ * not used and are always NIL.  The indexqualorig field is unused at
  * run time too, but is saved for the benefit of EXPLAIN.
  * ----------------
  */
 typedef struct BitmapIndexScan
 {
        Scan            scan;
-       Oid                     indxid;                 /* OID of index to scan */
-       List       *indxqual;           /* list of index quals */
-       List       *indxqualorig;       /* list of original forms of index quals */
-       List       *indxstrategy;       /* list of strategy numbers */
-       List       *indxsubtype;        /* list of strategy subtypes */
+       Oid                     indexid;                        /* OID of index to scan */
+       List       *indexqual;                  /* list of index quals (OpExprs) */
+       List       *indexqualorig;              /* the same in original form */
+       List       *indexstrategy;              /* integer list of strategy numbers */
+       List       *indexsubtype;               /* OID list of strategy subtypes */
 } BitmapIndexScan;
 
 /* ----------------
index 945858e8a60da617d03dc61b3f7ca83c2bdd5d0a..4618cf3a07e65d362b7ad06acacb6dbc5377b29a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.108 2005/04/22 21:58:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.109 2005/04/25 01:30:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -332,33 +332,20 @@ typedef struct Path
 } Path;
 
 /*----------
- * IndexPath represents an index scan. Although an indexscan can only read
- * a single relation, it can scan it more than once, potentially using a
- * different index during each scan.  The result is the union (OR) of all the
- * tuples matched during any scan.     (The executor is smart enough not to return
- * the same tuple more than once, even if it is matched in multiple scans.)
+ * IndexPath represents an index scan over a single index.
  *
- * XXX bitmap index scans will probably obviate the need for plain OR
- * indexscans, allowing a lot of this to be simplified.
+ * 'indexinfo' is the index to be scanned.
  *
- * 'indexinfo' is a list of IndexOptInfo nodes, one per scan to be performed.
- *
- * 'indexclauses' is a list of index qualifications, also one per scan.
- * Each entry in 'indexclauses' is a sublist of qualification clauses to be
- * used for that scan, with implicit AND semantics across the sublist items.
- * NOTE that the semantics of the top-level list in 'indexclauses' is OR
- * combination, while the sublists are implicitly AND combinations!
+ * 'indexclauses' is a list of index qualification clauses, with implicit
+ * AND semantics across the list.  Each clause is a RestrictInfo node from
+ * the query's WHERE or JOIN conditions.
  *
  * 'indexquals' has the same structure as 'indexclauses', but it contains
- * the actual indexqual conditions that can be used with the index(es).
+ * the actual indexqual conditions that can be used with the index.
  * In simple cases this is identical to 'indexclauses', but when special
  * indexable operators appear in 'indexclauses', they are replaced by the
  * derived indexscannable conditions in 'indexquals'.
  *
- * Both 'indexclauses' and 'indexquals' are lists of sublists of RestrictInfo
- * nodes.  (Before 8.0, we kept bare operator expressions in these lists, but
- * storing RestrictInfos is more efficient since selectivities can be cached.)
- *
  * 'isjoininner' is TRUE if the path is a nestloop inner scan (that is,
  * some of the index conditions are join rather than restriction clauses).
  *
@@ -372,7 +359,8 @@ typedef struct Path
  *
  * 'indextotalcost' and 'indexselectivity' are saved in the IndexPath so that
  * we need not recompute them when considering using the same index in a
- * bitmap index/heap scan (see BitmapHeapPath).
+ * bitmap index/heap scan (see BitmapHeapPath).  The costs of the IndexPath
+ * itself represent the costs of an IndexScan plan type.
  *
  * 'rows' is the estimated result tuple count for the indexscan.  This
  * is the same as path.parent->rows for a simple indexscan, but it is
@@ -384,7 +372,7 @@ typedef struct Path
 typedef struct IndexPath
 {
        Path            path;
-       List       *indexinfo;
+       IndexOptInfo *indexinfo;
        List       *indexclauses;
        List       *indexquals;
        bool            isjoininner;
@@ -402,13 +390,13 @@ typedef struct IndexPath
  * out in physical heap order no matter what the underlying indexes did.
  *
  * The individual indexscans are represented by IndexPath nodes, and any
- * logic on top of them is represented by BitmapAndPath and BitmapOrPath.
- * Notice that we can use the same IndexPath node both to represent a regular
- * IndexScan plan, and as the child of a BitmapHeapPath that represents
- * scanning the same index using a BitmapIndexScan.  The startup_cost and
- * total_cost figures of an IndexPath always represent the costs to use it
- * as a regular IndexScan.  The costs of a BitmapIndexScan can be computed
- * using the IndexPath's indextotalcost and indexselectivity.
+ * logic on top of them is represented by a tree of BitmapAndPath and
+ * BitmapOrPath nodes.  Notice that we can use the same IndexPath node both
+ * to represent a regular IndexScan plan, and as the child of a BitmapHeapPath
+ * that represents scanning the same index using a BitmapIndexScan.  The
+ * startup_cost and total_cost figures of an IndexPath always represent the
+ * costs to use it as a regular IndexScan.  The costs of a BitmapIndexScan
+ * can be computed using the IndexPath's indextotalcost and indexselectivity.
  *
  * BitmapHeapPaths can be nestloop inner indexscans.  The isjoininner and
  * rows fields serve the same purpose as for plain IndexPaths.
index a91744fd5f09b9687caa215e3fd3a671637fa65a..3b90cd7f7c8cf2e48867b9a8f8f472d3dc01fc41 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.82 2005/04/22 21:58:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.83 2005/04/25 01:30:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,13 +36,15 @@ extern void debug_print_rel(Query *root, RelOptInfo *rel);
  *       routines to generate index paths
  */
 extern void create_index_paths(Query *root, RelOptInfo *rel);
+extern List *generate_bitmap_or_paths(Query *root, RelOptInfo *rel,
+                                                List *clauses, List *outer_clauses,
+                                                bool isjoininner,
+                                                Relids outer_relids);
 extern Path *best_inner_indexscan(Query *root, RelOptInfo *rel,
                                         Relids outer_relids, JoinType jointype);
 extern List *group_clauses_by_indexkey(IndexOptInfo *index,
                                                                           List *clauses, List *outer_clauses,
                                                                           Relids outer_relids);
-extern List *group_clauses_by_indexkey_for_or(IndexOptInfo *index,
-                                                                Expr *orsubclause);
 extern bool match_index_to_operand(Node *operand, int indexcol,
                                           IndexOptInfo *index);
 extern List *expand_indexqual_conditions(IndexOptInfo *index,
@@ -50,14 +52,12 @@ extern List *expand_indexqual_conditions(IndexOptInfo *index,
 extern void check_partial_indexes(Query *root, RelOptInfo *rel);
 extern bool pred_test(List *predicate_list, List *restrictinfo_list);
 extern List *flatten_clausegroups_list(List *clausegroups);
-extern Expr *make_expr_from_indexclauses(List *indexclauses);
 
 /*
  * orindxpath.c
  *       additional routines for indexable OR clauses
  */
 extern bool create_or_index_quals(Query *root, RelOptInfo *rel);
-extern void create_or_index_paths(Query *root, RelOptInfo *rel);
 
 /*
  * tidpath.h
index 4f39cc3527b61e2c630a7ece7133aa2f11bc4241..c98838a2b25b4751976d3685d844c1559e2fff16 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.82 2005/04/12 05:11:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.83 2005/04/25 01:30:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,6 +33,7 @@ extern Plan *optimize_minmax_aggregates(Query *root, List *tlist,
  * prototypes for plan/createplan.c
  */
 extern Plan *create_plan(Query *root, Path *best_path);
+extern List *create_bitmap_restriction(Path *bitmapqual);
 extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
                                  Index scanrelid, Plan *subplan);
 extern Append *make_append(List *appendplans, bool isTarget, List *tlist);
index 77c03bb4ec283c5389e586855e7a7477677f83d4..5c47a0d00b125a397671c78ffbedf47b4c4411b1 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.27 2005/04/22 21:58:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.28 2005/04/25 01:30:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,9 +18,6 @@
 
 extern RestrictInfo *make_restrictinfo(Expr *clause, bool is_pushed_down,
                                  bool valid_everywhere);
-extern List *make_restrictinfo_from_indexclauses(List *indexclauses,
-                                                                       bool is_pushed_down,
-                                                                       bool valid_everywhere);
 extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
 extern List *get_actual_clauses(List *restrictinfo_list);
 extern void get_actual_join_clauses(List *restrictinfo_list,