]> granicus.if.org Git - postgresql/commitdiff
Teach planner and executor to handle ScalarArrayOpExpr as an indexable
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 25 Nov 2005 19:47:50 +0000 (19:47 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 25 Nov 2005 19:47:50 +0000 (19:47 +0000)
qualification when the underlying operator is indexable and useOr is true.
That is, indexkey op ANY (ARRAY[...]) is effectively translated into an
OR combination of one indexscan for each array element.  This only works
for bitmap index scans, of course, since regular indexscans no longer
support OR'ing of scans.  There are still some loose ends to clean up
before changing 'x IN (list)' to translate as a ScalarArrayOpExpr;
for instance predtest.c ought to be taught about it.  But this gets the
basic functionality in place.

12 files changed:
src/backend/executor/nodeBitmapIndexscan.c
src/backend/executor/nodeIndexscan.c
src/backend/optimizer/path/clausesel.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planagg.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/optimizer/paths.h
src/include/utils/selfuncs.h

index 902eeb35a438d71032877fa0f263823bc86d3a40..46e8c14652176677b7ea77797a2de700384d5bc2 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.11 2005/11/22 18:17:10 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.12 2005/11/25 19:47:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,6 +43,7 @@ MultiExecBitmapIndexScan(BitmapIndexScanState *node)
        ItemPointerData tids[MAX_TIDS];
        int32           ntids;
        double          nTuples = 0;
+       bool            doscan;
 
        /* must provide our own instrumentation support */
        if (node->ss.ps.instrument)
@@ -55,9 +56,18 @@ MultiExecBitmapIndexScan(BitmapIndexScanState *node)
 
        /*
         * If we have runtime keys and they've not already been set up, do it now.
+        * Array keys are also treated as runtime keys; note that if ExecReScan
+        * returns with biss_RuntimeKeysReady still false, then there is an
+        * empty array key so we should do nothing.
         */
-       if (node->biss_RuntimeKeyInfo && !node->biss_RuntimeKeysReady)
+       if (!node->biss_RuntimeKeysReady &&
+               (node->biss_NumRuntimeKeys != 0 || node->biss_NumArrayKeys != 0))
+       {
                ExecReScan((PlanState *) node, NULL);
+               doscan = node->biss_RuntimeKeysReady;
+       }
+       else
+               doscan = true;
 
        /*
         * Prepare the result bitmap.  Normally we just create a new one to pass
@@ -79,7 +89,7 @@ MultiExecBitmapIndexScan(BitmapIndexScanState *node)
        /*
         * Get TIDs from index and insert into bitmap
         */
-       for (;;)
+       while (doscan)
        {
                bool            more = index_getmulti(scandesc, tids, MAX_TIDS, &ntids);
 
@@ -89,10 +99,15 @@ MultiExecBitmapIndexScan(BitmapIndexScanState *node)
                        nTuples += ntids;
                }
 
-               if (!more)
-                       break;
-
                CHECK_FOR_INTERRUPTS();
+
+               if (!more)
+               {
+                       doscan = ExecIndexAdvanceArrayKeys(node->biss_ArrayKeys,
+                                                                                          node->biss_NumArrayKeys);
+                       if (doscan)                     /* reset index scan */
+                               index_rescan(node->biss_ScanDesc, node->biss_ScanKeys);
+               }
        }
 
        /* must provide our own instrumentation support */
@@ -113,10 +128,8 @@ void
 ExecBitmapIndexReScan(BitmapIndexScanState *node, ExprContext *exprCtxt)
 {
        ExprContext *econtext;
-       ExprState **runtimeKeyInfo;
 
        econtext = node->biss_RuntimeContext;           /* context for runtime keys */
-       runtimeKeyInfo = node->biss_RuntimeKeyInfo;
 
        if (econtext)
        {
@@ -137,19 +150,27 @@ ExecBitmapIndexReScan(BitmapIndexScanState *node, ExprContext *exprCtxt)
 
        /*
         * If we are doing runtime key calculations (ie, the index keys depend on
-        * data from an outer scan), compute the new key values
+        * data from an outer scan), compute the new key values.
+        *
+        * Array keys are also treated as runtime keys; note that if we
+        * return with biss_RuntimeKeysReady still false, then there is an
+        * empty array key so no index scan is needed.
         */
-       if (runtimeKeyInfo)
-       {
+       if (node->biss_NumRuntimeKeys != 0)
                ExecIndexEvalRuntimeKeys(econtext,
-                                                                runtimeKeyInfo,
-                                                                node->biss_ScanKeys,
-                                                                node->biss_NumScanKeys);
+                                                                node->biss_RuntimeKeys,
+                                                                node->biss_NumRuntimeKeys);
+       if (node->biss_NumArrayKeys != 0)
+               node->biss_RuntimeKeysReady =
+                       ExecIndexEvalArrayKeys(econtext,
+                                                                  node->biss_ArrayKeys,
+                                                                  node->biss_NumArrayKeys);
+       else
                node->biss_RuntimeKeysReady = true;
-       }
 
        /* reset index scan */
-       index_rescan(node->biss_ScanDesc, node->biss_ScanKeys);
+       if (node->biss_RuntimeKeysReady)
+               index_rescan(node->biss_ScanDesc, node->biss_ScanKeys);
 }
 
 /* ----------------------------------------------------------------
@@ -193,10 +214,6 @@ BitmapIndexScanState *
 ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate)
 {
        BitmapIndexScanState *indexstate;
-       ScanKey         scanKeys;
-       int                     numScanKeys;
-       ExprState **runtimeKeyInfo;
-       bool            have_runtime_keys;
 
        /*
         * create state structure
@@ -236,26 +253,25 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate)
        /*
         * build the index scan keys from the index qualification
         */
-       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;
+       ExecIndexBuildScanKeys((PlanState *) indexstate,
+                                                  node->indexqual,
+                                                  node->indexstrategy,
+                                                  node->indexsubtype,
+                                                  &indexstate->biss_ScanKeys,
+                                                  &indexstate->biss_NumScanKeys,
+                                                  &indexstate->biss_RuntimeKeys,
+                                                  &indexstate->biss_NumRuntimeKeys,
+                                                  &indexstate->biss_ArrayKeys,
+                                                  &indexstate->biss_NumArrayKeys);
 
        /*
-        * 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.
+        * If we have runtime keys or array 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.
         */
-       if (have_runtime_keys)
+       if (indexstate->biss_NumRuntimeKeys != 0 ||
+               indexstate->biss_NumArrayKeys != 0)
        {
                ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
 
@@ -286,8 +302,8 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate)
        indexstate->biss_ScanDesc =
                index_beginscan_multi(indexstate->biss_RelationDesc,
                                                          estate->es_snapshot,
-                                                         numScanKeys,
-                                                         scanKeys);
+                                                         indexstate->biss_NumScanKeys,
+                                                         indexstate->biss_ScanKeys);
 
        /*
         * all done.
index 4f6fadfde490fce28df8c8f45964e94f7f470cfe..f34a14c85961d3d9609ea0797fbb9389c226ffc8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.106 2005/11/25 04:24:48 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.107 2005/11/25 19:47:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +32,8 @@
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "parser/parsetree.h"
+#include "utils/array.h"
+#include "utils/lsyscache.h"
 #include "utils/memutils.h"
 
 
@@ -138,7 +140,7 @@ ExecIndexScan(IndexScanState *node)
        /*
         * If we have runtime keys and they've not already been set up, do it now.
         */
-       if (node->iss_RuntimeKeyInfo && !node->iss_RuntimeKeysReady)
+       if (node->iss_NumRuntimeKeys != 0 && !node->iss_RuntimeKeysReady)
                ExecReScan((PlanState *) node, NULL);
 
        /*
@@ -162,16 +164,10 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
 {
        EState     *estate;
        ExprContext *econtext;
-       ScanKey         scanKeys;
-       ExprState **runtimeKeyInfo;
-       int                     numScanKeys;
        Index           scanrelid;
 
        estate = node->ss.ps.state;
        econtext = node->iss_RuntimeContext;            /* context for runtime keys */
-       scanKeys = node->iss_ScanKeys;
-       runtimeKeyInfo = node->iss_RuntimeKeyInfo;
-       numScanKeys = node->iss_NumScanKeys;
        scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid;
 
        if (econtext)
@@ -202,14 +198,11 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
         * If we are doing runtime key calculations (ie, the index keys depend on
         * data from an outer scan), compute the new key values
         */
-       if (runtimeKeyInfo)
-       {
+       if (node->iss_NumRuntimeKeys != 0)
                ExecIndexEvalRuntimeKeys(econtext,
-                                                                runtimeKeyInfo,
-                                                                scanKeys,
-                                                                numScanKeys);
-               node->iss_RuntimeKeysReady = true;
-       }
+                                                                node->iss_RuntimeKeys,
+                                                                node->iss_NumRuntimeKeys);
+       node->iss_RuntimeKeysReady = true;
 
        /* If this is re-scanning of PlanQual ... */
        if (estate->es_evTuple != NULL &&
@@ -220,7 +213,7 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
        }
 
        /* reset index scan */
-       index_rescan(node->iss_ScanDesc, scanKeys);
+       index_rescan(node->iss_ScanDesc, node->iss_ScanKeys);
 }
 
 
@@ -230,18 +223,21 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
  */
 void
 ExecIndexEvalRuntimeKeys(ExprContext *econtext,
-                                                ExprState **run_keys,
-                                                ScanKey scan_keys,
-                                                int n_keys)
+                                                IndexRuntimeKeyInfo *runtimeKeys, int numRuntimeKeys)
 {
        int                     j;
 
-       for (j = 0; j < n_keys; j++)
+       for (j = 0; j < numRuntimeKeys; j++)
        {
+               ScanKey         scan_key = runtimeKeys[j].scan_key;
+               ExprState  *key_expr = runtimeKeys[j].key_expr;
+               Datum           scanvalue;
+               bool            isNull;
+
                /*
-                * If we have a run-time key, then extract the run-time expression and
+                * For each run-time key, extract the run-time expression and
                 * evaluate it with respect to the current outer tuple.  We then stick
-                * the result into the scan key.
+                * the result into the proper scan key.
                 *
                 * Note: the result of the eval could be a pass-by-ref value that's
                 * stored in the outer scan's tuple, not in
@@ -250,24 +246,140 @@ ExecIndexEvalRuntimeKeys(ExprContext *econtext,
                 * the result into our context explicitly, but I think that's not
                 * necessary...
                 */
-               if (run_keys[j] != NULL)
+               scanvalue = ExecEvalExprSwitchContext(key_expr,
+                                                                                         econtext,
+                                                                                         &isNull,
+                                                                                         NULL);
+               scan_key->sk_argument = scanvalue;
+               if (isNull)
+                       scan_key->sk_flags |= SK_ISNULL;
+               else
+                       scan_key->sk_flags &= ~SK_ISNULL;
+       }
+}
+
+/*
+ * ExecIndexEvalArrayKeys
+ *             Evaluate any array key values, and set up to iterate through arrays.
+ *
+ * Returns TRUE if there are array elements to consider; FALSE means there
+ * is at least one null or empty array, so no match is possible.  On TRUE
+ * result, the scankeys are initialized with the first elements of the arrays.
+ */
+bool
+ExecIndexEvalArrayKeys(ExprContext *econtext,
+                                          IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
+{
+       bool            result = true;
+       int                     j;
+       MemoryContext oldContext;
+
+       /* We want to keep the arrays in per-tuple memory */
+       oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+       for (j = 0; j < numArrayKeys; j++)
+       {
+               ScanKey         scan_key = arrayKeys[j].scan_key;
+               ExprState  *array_expr = arrayKeys[j].array_expr;
+               Datum           arraydatum;
+               bool            isNull;
+               ArrayType  *arrayval;
+               int16           elmlen;
+               bool            elmbyval;
+               char            elmalign;
+               int                     num_elems;
+               Datum      *elem_values;
+               bool       *elem_nulls;
+
+               /*
+                * Compute and deconstruct the array expression.
+                * (Notes in ExecIndexEvalRuntimeKeys() apply here too.)
+                */
+               arraydatum = ExecEvalExpr(array_expr,
+                                                                 econtext,
+                                                                 &isNull,
+                                                                 NULL);
+               if (isNull)
                {
-                       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;
+                       result = false;
+                       break;                          /* no point in evaluating more */
+               }
+               arrayval = DatumGetArrayTypeP(arraydatum);
+               /* We could cache this data, but not clear it's worth it */
+               get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
+                                                        &elmlen, &elmbyval, &elmalign);
+               deconstruct_array(arrayval,
+                                                 ARR_ELEMTYPE(arrayval),
+                                                 elmlen, elmbyval, elmalign,
+                                                 &elem_values, &elem_nulls, &num_elems);
+               if (num_elems <= 0)
+               {
+                       result = false;
+                       break;                          /* no point in evaluating more */
+               }
+
+               /*
+                * Note: we expect the previous array data, if any, to be automatically
+                * freed by resetting the per-tuple context; hence no pfree's here.
+                */
+               arrayKeys[j].elem_values = elem_values;
+               arrayKeys[j].elem_nulls = elem_nulls;
+               arrayKeys[j].num_elems = num_elems;
+               scan_key->sk_argument = elem_values[0];
+               if (elem_nulls[0])
+                       scan_key->sk_flags |= SK_ISNULL;
+               else
+                       scan_key->sk_flags &= ~SK_ISNULL;
+               arrayKeys[j].next_elem = 1;
+       }
+
+       MemoryContextSwitchTo(oldContext);
+
+       return result;
+}
+
+/*
+ * ExecIndexAdvanceArrayKeys
+ *             Advance to the next set of array key values, if any.
+ *
+ * Returns TRUE if there is another set of values to consider, FALSE if not.
+ * On TRUE result, the scankeys are initialized with the next set of values.
+ */
+bool
+ExecIndexAdvanceArrayKeys(IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
+{
+       bool            found = false;
+       int                     j;
+
+       for (j = 0; j < numArrayKeys; j++)
+       {
+               ScanKey         scan_key = arrayKeys[j].scan_key;
+               int                     next_elem = arrayKeys[j].next_elem;
+               int                     num_elems = arrayKeys[j].num_elems;
+               Datum      *elem_values = arrayKeys[j].elem_values;
+               bool       *elem_nulls = arrayKeys[j].elem_nulls;
+
+               if (next_elem >= num_elems)
+               {
+                       next_elem = 0;
+                       found = false;          /* need to advance next array key */
                }
+               else
+                       found = true;
+               scan_key->sk_argument = elem_values[next_elem];
+               if (elem_nulls[next_elem])
+                       scan_key->sk_flags |= SK_ISNULL;
+               else
+                       scan_key->sk_flags &= ~SK_ISNULL;
+               arrayKeys[j].next_elem = next_elem + 1;
+               if (found)
+                       break;
        }
+
+       return found;
 }
 
+
 /* ----------------------------------------------------------------
  *             ExecEndIndexScan
  * ----------------------------------------------------------------
@@ -352,10 +464,6 @@ IndexScanState *
 ExecInitIndexScan(IndexScan *node, EState *estate)
 {
        IndexScanState *indexstate;
-       ScanKey         scanKeys;
-       int                     numScanKeys;
-       ExprState **runtimeKeyInfo;
-       bool            have_runtime_keys;
        RangeTblEntry *rtentry;
        Index           relid;
        Oid                     reloid;
@@ -412,18 +520,16 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
        /*
         * build the index scan keys from the index qualification
         */
-       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;
+       ExecIndexBuildScanKeys((PlanState *) indexstate,
+                                                  node->indexqual,
+                                                  node->indexstrategy,
+                                                  node->indexsubtype,
+                                                  &indexstate->iss_ScanKeys,
+                                                  &indexstate->iss_NumScanKeys,
+                                                  &indexstate->iss_RuntimeKeys,
+                                                  &indexstate->iss_NumRuntimeKeys,
+                                                  NULL,                                /* no ArrayKeys */
+                                                  NULL);
 
        /*
         * If we have runtime keys, we need an ExprContext to evaluate them. The
@@ -431,7 +537,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
         * for every tuple.  So, build another context just like the other one...
         * -tgl 7/11/00
         */
-       if (have_runtime_keys)
+       if (indexstate->iss_NumRuntimeKeys != 0)
        {
                ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
 
@@ -471,8 +577,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
        indexstate->iss_ScanDesc = index_beginscan(currentRelation,
                                                                                           indexstate->iss_RelationDesc,
                                                                                           estate->es_snapshot,
-                                                                                          numScanKeys,
-                                                                                          scanKeys);
+                                                                                          indexstate->iss_NumScanKeys,
+                                                                                          indexstate->iss_ScanKeys);
 
        /*
         * Initialize result tuple type and projection info.
@@ -489,7 +595,26 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 
 /*
  * ExecIndexBuildScanKeys
- *             Build the index scan keys from the index qualification
+ *             Build the index scan keys from the index qualification expressions
+ *
+ * The index quals are passed to the index AM in the form of a ScanKey array.
+ * This routine sets up the ScanKeys, fills in all constant fields of the
+ * ScanKeys, and prepares information about the keys that have non-constant
+ * comparison values.  We divide index qual expressions into three types:
+ *
+ * 1. Simple operator with constant comparison value ("indexkey op constant").
+ * For these, we just fill in a ScanKey containing the constant value.
+ *
+ * 2. Simple operator with non-constant value ("indexkey op expression").
+ * For these, we create a ScanKey with everything filled in except the
+ * expression value, and set up an IndexRuntimeKeyInfo struct to drive
+ * evaluation of the expression at the right times.
+ *
+ * 3. ScalarArrayOpExpr ("indexkey op ANY (array-expression)").  For these,
+ * we create a ScanKey with everything filled in except the comparison value,
+ * and set up an IndexArrayKeyInfo struct to drive processing of the qual.
+ * (Note that we treat all array-expressions as requiring runtime evaluation,
+ * even if they happen to be constants.)
  *
  * Input params are:
  *
@@ -500,33 +625,43 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
  *
  * 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
+ * *numScanKeys: receives number of scankeys
+ * *runtimeKeys: receives ptr to array of IndexRuntimeKeyInfos, or NULL if none
+ * *numRuntimeKeys: receives number of runtime keys
+ * *arrayKeys: receives ptr to array of IndexArrayKeyInfos, or NULL if none
+ * *numArrayKeys: receives number of array keys
  *
- * Return value is TRUE if any runtime key expressions were found, else FALSE.
+ * Caller may pass NULL for arrayKeys and numArrayKeys to indicate that
+ * ScalarArrayOpExpr quals are not supported.
  */
-bool
+void
 ExecIndexBuildScanKeys(PlanState *planstate, List *quals,
                                           List *strategies, List *subtypes,
-                                          ExprState ***runtimeKeyInfo,
-                                          ScanKey *scanKeys, int *numScanKeys)
+                                          ScanKey *scanKeys, int *numScanKeys,
+                                          IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
+                                          IndexArrayKeyInfo **arrayKeys, int *numArrayKeys)
 {
-       bool            have_runtime_keys = false;
        ListCell   *qual_cell;
        ListCell   *strategy_cell;
        ListCell   *subtype_cell;
-       int                     n_keys;
        ScanKey         scan_keys;
-       ExprState **run_keys;
+       IndexRuntimeKeyInfo *runtime_keys;
+       IndexArrayKeyInfo *array_keys;
+       int                     n_scan_keys;
+       int                     n_runtime_keys;
+       int                     n_array_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 *));
+       n_scan_keys = list_length(quals);
+       scan_keys = (ScanKey) palloc(n_scan_keys * sizeof(ScanKeyData));
+       /* Allocate these arrays as large as they could possibly need to be */
+       runtime_keys = (IndexRuntimeKeyInfo *)
+               palloc(n_scan_keys * sizeof(IndexRuntimeKeyInfo));
+       array_keys = (IndexArrayKeyInfo *)
+               palloc0(n_scan_keys * sizeof(IndexArrayKeyInfo));
+       n_runtime_keys = 0;
+       n_array_keys = 0;
 
        /*
         * for each opclause in the given qual, convert each qual's opclause into
@@ -536,122 +671,171 @@ ExecIndexBuildScanKeys(PlanState *planstate, List *quals,
        strategy_cell = list_head(strategies);
        subtype_cell = list_head(subtypes);
 
-       for (j = 0; j < n_keys; j++)
+       for (j = 0; j < n_scan_keys; j++)
        {
-               OpExpr     *clause;             /* one clause of index qual */
+               ScanKey         this_scan_key = &scan_keys[j];
+               Expr       *clause;             /* one clause of index qual */
+               RegProcedure opfuncid;  /* operator proc id used in scan */
+               StrategyNumber strategy;        /* op's strategy number */
+               Oid                     subtype;        /* op's strategy subtype */
                Expr       *leftop;             /* expr on lhs of operator */
                Expr       *rightop;    /* expr on rhs ... */
-               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);
+               clause = (Expr *) lfirst(qual_cell);
                qual_cell = lnext(qual_cell);
                strategy = lfirst_int(strategy_cell);
                strategy_cell = lnext(strategy_cell);
                subtype = lfirst_oid(subtype_cell);
                subtype_cell = lnext(subtype_cell);
 
-               if (!IsA(clause, OpExpr))
-                       elog(ERROR, "indexqual is not an OpExpr");
+               if (IsA(clause, OpExpr))
+               {
+                       /* indexkey op const or indexkey op expression */
+                       int                     flags = 0;
+                       Datum           scanvalue;
 
-               opfuncid = clause->opfuncid;
+                       opfuncid = ((OpExpr *) 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;
+                       /*
+                        * leftop should be the index key Var, possibly relabeled
+                        */
+                       leftop = (Expr *) get_leftop(clause);
 
-               /*
-                * determine information in leftop
-                */
-               leftop = (Expr *) get_leftop((Expr *) clause);
+                       if (leftop && IsA(leftop, RelabelType))
+                               leftop = ((RelabelType *) leftop)->arg;
 
-               if (leftop && IsA(leftop, RelabelType))
-                       leftop = ((RelabelType *) leftop)->arg;
+                       Assert(leftop != NULL);
 
-               Assert(leftop != NULL);
+                       if (!(IsA(leftop, Var) &&
+                                 var_is_rel((Var *) leftop)))
+                               elog(ERROR, "indexqual doesn't have key on left side");
 
-               if (!(IsA(leftop, Var) &&
-                         var_is_rel((Var *) leftop)))
-                       elog(ERROR, "indexqual doesn't have key on left side");
+                       varattno = ((Var *) leftop)->varattno;
 
-               varattno = ((Var *) leftop)->varattno;
+                       /*
+                        * rightop is the constant or variable comparison value
+                        */
+                       rightop = (Expr *) get_rightop(clause);
 
-               /*
-                * now determine information in rightop
-                */
-               rightop = (Expr *) get_rightop((Expr *) clause);
+                       if (rightop && IsA(rightop, RelabelType))
+                               rightop = ((RelabelType *) rightop)->arg;
 
-               if (rightop && IsA(rightop, RelabelType))
-                       rightop = ((RelabelType *) rightop)->arg;
+                       Assert(rightop != NULL);
 
-               Assert(rightop != NULL);
+                       if (IsA(rightop, Const))
+                       {
+                               /* OK, simple constant comparison value */
+                               scanvalue = ((Const *) rightop)->constvalue;
+                               if (((Const *) rightop)->constisnull)
+                                       flags |= SK_ISNULL;
+                       }
+                       else
+                       {
+                               /* Need to treat this one as a runtime key */
+                               runtime_keys[n_runtime_keys].scan_key = this_scan_key;
+                               runtime_keys[n_runtime_keys].key_expr =
+                                       ExecInitExpr(rightop, planstate);
+                               n_runtime_keys++;
+                               scanvalue = (Datum) 0;
+                       }
 
-               if (IsA(rightop, Const))
-               {
                        /*
-                        * if the rightop is a const node then it means it identifies the
-                        * value to place in our scan key.
+                        * initialize the scan key's fields appropriately
                         */
-                       scanvalue = ((Const *) rightop)->constvalue;
-                       if (((Const *) rightop)->constisnull)
-                               flags |= SK_ISNULL;
+                       ScanKeyEntryInitialize(this_scan_key,
+                                                                  flags,
+                                                                  varattno,    /* attribute number to scan */
+                                                                  strategy,    /* op's strategy */
+                                                                  subtype,             /* strategy subtype */
+                                                                  opfuncid,    /* reg proc to use */
+                                                                  scanvalue);  /* constant */
                }
-               else
+               else if (IsA(clause, ScalarArrayOpExpr))
                {
+                       /* indexkey op ANY (array-expression) */
+                       ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+
+                       Assert(saop->useOr);
+                       opfuncid = saop->opfuncid;
+
                        /*
-                        * otherwise, the rightop contains an expression evaluable at
-                        * runtime to figure out the value to place in our scan key.
+                        * leftop should be the index key Var, possibly relabeled
                         */
-                       have_runtime_keys = true;
-                       run_keys[j] = ExecInitExpr(rightop, planstate);
-                       scanvalue = (Datum) 0;
-               }
+                       leftop = (Expr *) linitial(saop->args);
 
-               /*
-                * 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 (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;
+
+                       /*
+                        * rightop is the constant or variable array value
+                        */
+                       rightop = (Expr *) lsecond(saop->args);
+
+                       if (rightop && IsA(rightop, RelabelType))
+                               rightop = ((RelabelType *) rightop)->arg;
+
+                       Assert(rightop != NULL);
+
+                       array_keys[n_array_keys].scan_key = this_scan_key;
+                       array_keys[n_array_keys].array_expr =
+                               ExecInitExpr(rightop, planstate);
+                       /* the remaining fields were zeroed by palloc0 */
+                       n_array_keys++;
+
+                       /*
+                        * initialize the scan key's fields appropriately
+                        */
+                       ScanKeyEntryInitialize(this_scan_key,
+                                                                  0,                   /* flags */
+                                                                  varattno,    /* attribute number to scan */
+                                                                  strategy,    /* op's strategy */
+                                                                  subtype,             /* strategy subtype */
+                                                                  opfuncid,    /* reg proc to use */
+                                                                  (Datum) 0);  /* constant */
+               }
+               else
+                       elog(ERROR, "unsupported indexqual type: %d",
+                                (int) nodeTag(clause));
        }
 
-       /* If no runtime keys, get rid of speculatively-allocated array */
-       if (run_keys && !have_runtime_keys)
+       /* Get rid of any unused arrays */
+       if (n_runtime_keys == 0)
+       {
+               pfree(runtime_keys);
+               runtime_keys = NULL;
+       }
+       if (n_array_keys == 0)
        {
-               pfree(run_keys);
-               run_keys = NULL;
+               pfree(array_keys);
+               array_keys = NULL;
        }
 
        /*
-        * Return the info to our caller.
+        * Return info to our caller.
         */
-       *numScanKeys = n_keys;
        *scanKeys = scan_keys;
-       *runtimeKeyInfo = run_keys;
-
-       return have_runtime_keys;
+       *numScanKeys = n_scan_keys;
+       *runtimeKeys = runtime_keys;
+       *numRuntimeKeys = n_runtime_keys;
+       if (arrayKeys)
+       {
+               *arrayKeys = array_keys;
+               *numArrayKeys = n_array_keys;
+       }
+       else if (n_array_keys != 0)
+               elog(ERROR, "ScalarArrayOpExpr index qual found where not allowed");
 }
 
 int
index 9a4990898e92fa685b3109b6b1216ec831559148..eba6bf1d148622e815f43cbfc3d8724396de2455 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.75 2005/10/15 02:49:19 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.76 2005/11/25 19:47:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -624,12 +624,45 @@ clause_selectivity(PlannerInfo *root,
                 */
                s1 = (Selectivity) 0.5;
        }
-       else if (IsA(clause, DistinctExpr) ||
-                        IsA(clause, ScalarArrayOpExpr))
+       else if (IsA(clause, DistinctExpr))
        {
                /* can we do better? */
                s1 = (Selectivity) 0.5;
        }
+       else if (IsA(clause, ScalarArrayOpExpr))
+       {
+               /* First, decide if it's a join clause, same as for OpExpr */
+               bool            is_join_clause;
+
+               if (varRelid != 0)
+               {
+                       /*
+                        * If we are considering a nestloop join then all clauses are
+                        * restriction clauses, since we are only interested in the one
+                        * relation.
+                        */
+                       is_join_clause = false;
+               }
+               else
+               {
+                       /*
+                        * Otherwise, it's a join if there's more than one relation used.
+                        * We can optimize this calculation if an rinfo was passed.
+                        */
+                       if (rinfo)
+                               is_join_clause = (bms_membership(rinfo->clause_relids) ==
+                                                                 BMS_MULTIPLE);
+                       else
+                               is_join_clause = (NumRelids(clause) > 1);
+               }
+
+               /* Use node specific selectivity calculation function */
+               s1 = scalararraysel(root,
+                                                       (ScalarArrayOpExpr *) clause,
+                                                       is_join_clause,
+                                                       varRelid,
+                                                       jointype);
+       }
        else if (IsA(clause, NullTest))
        {
                /* Use node specific selectivity calculation function */
index 6f3157a23f608dc1fb6b00fdb7f9357a54aad36e..fde89455685e76a5ecd84580f81f32d48ba592a4 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.193 2005/11/22 18:17:12 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.194 2005/11/25 19:47:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,7 @@
 #include "optimizer/paths.h"
 #include "optimizer/predtest.h"
 #include "optimizer/restrictinfo.h"
+#include "optimizer/var.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -40,9 +41,6 @@
  */
 #define DoneMatchingIndexKeys(classes) (classes[0] == InvalidOid)
 
-#define is_indexable_operator(clause,opclass,indexkey_on_left) \
-       (indexable_operator(clause,opclass,indexkey_on_left) != InvalidOid)
-
 #define IsBooleanOpclass(opclass) \
        ((opclass) == BOOL_BTREE_OPS_OID || (opclass) == BOOL_HASH_OPS_OID)
 
 static List *find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
                                        List *clauses, List *outer_clauses,
                                        bool istoplevel, bool isjoininner,
-                                       Relids outer_relids);
+                                       Relids outer_relids,
+                                       SaOpControl saop_control);
 static Path *choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths);
 static int     bitmap_path_comparator(const void *a, const void *b);
 static Cost bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths);
 static bool match_clause_to_indexcol(IndexOptInfo *index,
                                                 int indexcol, Oid opclass,
                                                 RestrictInfo *rinfo,
-                                                Relids outer_relids);
-static Oid indexable_operator(Expr *clause, Oid opclass,
-                                  bool indexkey_on_left);
+                                                Relids outer_relids,
+                                                SaOpControl saop_control);
+static bool is_indexable_operator(Oid expr_op, Oid opclass,
+                                                                 bool indexkey_on_left);
 static Relids indexable_outerrelids(RelOptInfo *rel);
 static bool matches_any_index(RestrictInfo *rinfo, RelOptInfo *rel,
                                  Relids outer_relids);
@@ -150,7 +150,7 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
         */
        indexpaths = find_usable_indexes(root, rel,
                                                                         rel->baserestrictinfo, NIL,
-                                                                        true, false, NULL);
+                                                                        true, false, NULL, SAOP_FORBID);
 
        /*
         * We can submit them all to add_path.  (This generates access paths for
@@ -228,6 +228,7 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
  *             given clauses are join clauses)
  * 'outer_relids' identifies the outer side of the join (pass NULL
  *             if not isjoininner)
+ * 'saop_control' indicates whether ScalarArrayOpExpr clauses can be used
  *
  * Note: check_partial_indexes() must have been run previously.
  *----------
@@ -236,7 +237,8 @@ static List *
 find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
                                        List *clauses, List *outer_clauses,
                                        bool istoplevel, bool isjoininner,
-                                       Relids outer_relids)
+                                       Relids outer_relids,
+                                       SaOpControl saop_control)
 {
        List       *result = NIL;
        List       *all_clauses = NIL;          /* not computed till needed */
@@ -267,6 +269,10 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
                 * predOK index to an arm of an OR, which would be a legal but
                 * pointlessly inefficient plan.  (A better plan will be generated by
                 * just scanning the predOK index alone, no OR.)
+                *
+                * If saop_control is SAOP_REQUIRE and istoplevel is false, the caller
+                * is only interested in indexquals involving ScalarArrayOps, so don't
+                * set useful_predicate to true.
                 */
                useful_predicate = false;
                if (index->indpred != NIL)
@@ -292,7 +298,8 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
                                if (!predicate_implied_by(index->indpred, all_clauses))
                                        continue;       /* can't use it at all */
 
-                               if (!predicate_implied_by(index->indpred, outer_clauses))
+                               if (saop_control != SAOP_REQUIRE &&
+                                       !predicate_implied_by(index->indpred, outer_clauses))
                                        useful_predicate = true;
                        }
                }
@@ -300,12 +307,14 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
                /*
                 * 1. Match the index against the available restriction clauses.
                 * found_clause is set true only if at least one of the current
-                * clauses was used.
+                * clauses was used (and, if saop_control is SAOP_REQUIRE, it
+                * has to have been a ScalarArrayOpExpr clause).
                 */
                restrictclauses = group_clauses_by_indexkey(index,
                                                                                                        clauses,
                                                                                                        outer_clauses,
                                                                                                        outer_relids,
+                                                                                                       saop_control,
                                                                                                        &found_clause);
 
                /*
@@ -380,9 +389,9 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
 
 /*
  * generate_bitmap_or_paths
- *             Look through the list of clauses to find OR clauses, and generate
- *             a BitmapOrPath for each one we can handle that way.  Return a list
- *             of the generated BitmapOrPaths.
+ *             Look through the list of clauses to find OR clauses and
+ *             ScalarArrayOpExpr clauses, and generate a BitmapOrPath for each one
+ *             we can handle that way.  Return a list of the generated BitmapOrPaths.
  *
  * outer_clauses is a list of additional clauses that can be assumed true
  * for the purpose of generating indexquals, but are not to be searched for
@@ -396,6 +405,7 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
 {
        List       *result = NIL;
        List       *all_clauses;
+       bool            have_saop = false;
        ListCell   *l;
 
        /*
@@ -412,9 +422,16 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
                ListCell   *j;
 
                Assert(IsA(rinfo, RestrictInfo));
-               /* Ignore RestrictInfos that aren't ORs */
+               /*
+                * In this loop we ignore RestrictInfos that aren't ORs; but take
+                * note of ScalarArrayOpExpr for later.
+                */
                if (!restriction_is_or_clause(rinfo))
+               {
+                       if (IsA(rinfo->clause, ScalarArrayOpExpr))
+                               have_saop = true;
                        continue;
+               }
 
                /*
                 * We must be able to match at least one index to each of the arms of
@@ -436,7 +453,8 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
                                                                                          all_clauses,
                                                                                          false,
                                                                                          isjoininner,
-                                                                                         outer_relids);
+                                                                                         outer_relids,
+                                                                                         SAOP_ALLOW);
                                /* Recurse in case there are sub-ORs */
                                indlist = list_concat(indlist,
                                                                          generate_bitmap_or_paths(root, rel,
@@ -454,7 +472,8 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
                                                                                          all_clauses,
                                                                                          false,
                                                                                          isjoininner,
-                                                                                         outer_relids);
+                                                                                         outer_relids,
+                                                                                         SAOP_ALLOW);
                        }
 
                        /*
@@ -486,6 +505,29 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
                }
        }
 
+       /*
+        * If we saw any top-level ScalarArrayOpExpr clauses, see if we can
+        * generate a bitmap index path that uses those but not any OR clauses.
+        */
+       if (have_saop)
+       {
+               List       *pathlist;
+               Path       *bitmapqual;
+
+               pathlist = find_usable_indexes(root, rel,
+                                                                          clauses,
+                                                                          outer_clauses,
+                                                                          false,
+                                                                          isjoininner,
+                                                                          outer_relids,
+                                                                          SAOP_REQUIRE);
+               if (pathlist != NIL)
+               {
+                       bitmapqual = (Path *) create_bitmap_or_path(root, rel, pathlist);
+                       result = lappend(result, bitmapqual);
+               }
+       }
+
        return result;
 }
 
@@ -526,7 +568,8 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
         *
         * We also make some effort to detect directly redundant input paths, as
         * can happen if there are multiple possibly usable indexes.  For this we
-        * look only at plain IndexPath inputs, not at sub-OR clauses. And we
+        * look only at plain IndexPath and single-element BitmapOrPath inputs
+        * (the latter can arise in the presence of ScalarArrayOpExpr quals).  We
         * consider an index redundant if all its index conditions were already
         * used by earlier indexes.  (We could use predicate_implied_by to have a
         * more intelligent, but much more expensive, check --- but in most cases
@@ -555,10 +598,17 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
 
        paths = list_make1(patharray[0]);
        costsofar = bitmap_and_cost_est(root, rel, paths);
+       qualsofar = NIL;
        if (IsA(patharray[0], IndexPath))
                qualsofar = list_copy(((IndexPath *) patharray[0])->indexclauses);
-       else
-               qualsofar = NIL;
+       else if (IsA(patharray[0], BitmapOrPath))
+       {
+               List   *orquals = ((BitmapOrPath *) patharray[0])->bitmapquals;
+
+               if (list_length(orquals) == 1 &&
+                       IsA(linitial(orquals), IndexPath))
+               qualsofar = list_copy(((IndexPath *) linitial(orquals))->indexclauses);
+       }
        lastcell = list_head(paths);    /* for quick deletions */
 
        for (i = 1; i < npaths; i++)
@@ -573,6 +623,16 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
                        if (list_difference_ptr(newqual, qualsofar) == NIL)
                                continue;               /* redundant */
                }
+               else if (IsA(newpath, BitmapOrPath))
+               {
+                       List   *orquals = ((BitmapOrPath *) newpath)->bitmapquals;
+
+                       if (list_length(orquals) == 1 &&
+                               IsA(linitial(orquals), IndexPath))
+                               newqual = ((IndexPath *) linitial(orquals))->indexclauses;
+                       if (list_difference_ptr(newqual, qualsofar) == NIL)
+                               continue;               /* redundant */
+               }
 
                paths = lappend(paths, newpath);
                newcost = bitmap_and_cost_est(root, rel, paths);
@@ -665,6 +725,10 @@ bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths)
  * outer_relids determines what Vars will be allowed on the other side
  * of a possible index qual; see match_clause_to_indexcol().
  *
+ * 'saop_control' indicates whether ScalarArrayOpExpr clauses can be used.
+ * When it's SAOP_REQUIRE, *found_clause is set TRUE only if we used at least
+ * one ScalarArrayOpExpr from the current clauses list.
+ *
  * If the index has amoptionalkey = false, we give up and return NIL when
  * there are no restriction clauses matching the first index key.  Otherwise,
  * we return NIL if there are no restriction clauses matching any index key.
@@ -682,6 +746,7 @@ List *
 group_clauses_by_indexkey(IndexOptInfo *index,
                                                  List *clauses, List *outer_clauses,
                                                  Relids outer_relids,
+                                                 SaOpControl saop_control,
                                                  bool *found_clause)
 {
        List       *clausegroup_list = NIL;
@@ -710,10 +775,13 @@ group_clauses_by_indexkey(IndexOptInfo *index,
                                                                                 indexcol,
                                                                                 curClass,
                                                                                 rinfo,
-                                                                                outer_relids))
+                                                                                outer_relids,
+                                                                                saop_control))
                        {
                                clausegroup = list_append_unique_ptr(clausegroup, rinfo);
-                               *found_clause = true;
+                               if (saop_control != SAOP_REQUIRE ||
+                                       IsA(rinfo->clause, ScalarArrayOpExpr))
+                                       *found_clause = true;
                        }
                }
 
@@ -727,7 +795,8 @@ group_clauses_by_indexkey(IndexOptInfo *index,
                                                                                 indexcol,
                                                                                 curClass,
                                                                                 rinfo,
-                                                                                outer_relids))
+                                                                                outer_relids,
+                                                                                saop_control))
                        {
                                clausegroup = list_append_unique_ptr(clausegroup, rinfo);
                                found_outer_clause = true;
@@ -785,6 +854,11 @@ group_clauses_by_indexkey(IndexOptInfo *index,
  *       We do not actually do the commuting here, but we check whether a
  *       suitable commutator operator is available.
  *
+ *       It is also possible to match ScalarArrayOpExpr clauses to indexes, when
+ *       the clause is of the form "indexkey op ANY (arrayconst)".  Since the
+ *       executor can only handle these in the context of bitmap index scans,
+ *       our caller specifies whether to allow these or not.
+ *
  *       For boolean indexes, it is also possible to match the clause directly
  *       to the indexkey; or perhaps the clause is (NOT indexkey).
  *
@@ -792,6 +866,7 @@ group_clauses_by_indexkey(IndexOptInfo *index,
  * 'indexcol' is a column number of 'index' (counting from 0).
  * 'opclass' is the corresponding operator class.
  * 'rinfo' is the clause to be tested (as a RestrictInfo node).
+ * 'saop_control' indicates whether ScalarArrayOpExpr clauses can be used.
  *
  * Returns true if the clause can be used with this index key.
  *
@@ -803,11 +878,16 @@ match_clause_to_indexcol(IndexOptInfo *index,
                                                 int indexcol,
                                                 Oid opclass,
                                                 RestrictInfo *rinfo,
-                                                Relids outer_relids)
+                                                Relids outer_relids,
+                                                SaOpControl saop_control)
 {
        Expr       *clause = rinfo->clause;
        Node       *leftop,
                           *rightop;
+       Relids          left_relids;
+       Relids          right_relids;
+       Oid                     expr_op;
+       bool            plain_op;
 
        /* First check for boolean-index cases. */
        if (IsBooleanOpclass(opclass))
@@ -816,12 +896,37 @@ match_clause_to_indexcol(IndexOptInfo *index,
                        return true;
        }
 
-       /* Else clause must be a binary opclause. */
-       if (!is_opclause(clause))
-               return false;
-       leftop = get_leftop(clause);
-       rightop = get_rightop(clause);
-       if (!leftop || !rightop)
+       /*
+        * Clause must be a binary opclause, or possibly a ScalarArrayOpExpr
+        * (which is always binary, by definition).
+        */
+       if (is_opclause(clause))
+       {
+               leftop = get_leftop(clause);
+               rightop = get_rightop(clause);
+               if (!leftop || !rightop)
+                       return false;
+               left_relids = rinfo->left_relids;
+               right_relids = rinfo->right_relids;
+               expr_op = ((OpExpr *) clause)->opno;
+               plain_op = true;
+       }
+       else if (saop_control != SAOP_FORBID &&
+                        clause && IsA(clause, ScalarArrayOpExpr))
+       {
+               ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+
+               /* We only accept ANY clauses, not ALL */
+               if (!saop->useOr)
+                       return false;
+               leftop = (Node *) linitial(saop->args);
+               rightop = (Node *) lsecond(saop->args);
+               left_relids = NULL;             /* not actually needed */
+               right_relids = pull_varnos(rightop);
+               expr_op = saop->opno;
+               plain_op = false;
+       }
+       else
                return false;
 
        /*
@@ -829,26 +934,28 @@ match_clause_to_indexcol(IndexOptInfo *index,
         * (constant operator indexkey).  See above notes about const-ness.
         */
        if (match_index_to_operand(leftop, indexcol, index) &&
-               bms_is_subset(rinfo->right_relids, outer_relids) &&
+               bms_is_subset(right_relids, outer_relids) &&
                !contain_volatile_functions(rightop))
        {
-               if (is_indexable_operator(clause, opclass, true))
+               if (is_indexable_operator(expr_op, opclass, true))
                        return true;
 
                /*
                 * If we didn't find a member of the index's opclass, see whether it
                 * is a "special" indexable operator.
                 */
-               if (match_special_index_operator(clause, opclass, true))
+               if (plain_op &&
+                       match_special_index_operator(clause, opclass, true))
                        return true;
                return false;
        }
 
-       if (match_index_to_operand(rightop, indexcol, index) &&
-               bms_is_subset(rinfo->left_relids, outer_relids) &&
+       if (plain_op &&
+               match_index_to_operand(rightop, indexcol, index) &&
+               bms_is_subset(left_relids, outer_relids) &&
                !contain_volatile_functions(leftop))
        {
-               if (is_indexable_operator(clause, opclass, false))
+               if (is_indexable_operator(expr_op, opclass, false))
                        return true;
 
                /*
@@ -864,36 +971,26 @@ match_clause_to_indexcol(IndexOptInfo *index,
 }
 
 /*
- * indexable_operator
- *       Does a binary opclause contain an operator matching the index opclass?
+ * is_indexable_operator
+ *       Does the operator match the specified index opclass?
  *
  * If the indexkey is on the right, what we actually want to know
  * is whether the operator has a commutator operator that matches
- * the index's opclass.
- *
- * Returns the OID of the matching operator, or InvalidOid if no match.
- * (Formerly, this routine might return a binary-compatible operator
- * rather than the original one, but that kluge is history.)
+ * the opclass.
  */
-static Oid
-indexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left)
+static bool
+is_indexable_operator(Oid expr_op, Oid opclass, bool indexkey_on_left)
 {
-       Oid                     expr_op = ((OpExpr *) clause)->opno;
-       Oid                     commuted_op;
-
        /* Get the commuted operator if necessary */
-       if (indexkey_on_left)
-               commuted_op = expr_op;
-       else
-               commuted_op = get_commutator(expr_op);
-       if (commuted_op == InvalidOid)
-               return InvalidOid;
+       if (!indexkey_on_left)
+       {
+               expr_op = get_commutator(expr_op);
+               if (expr_op == InvalidOid)
+                       return false;
+       }
 
        /* OK if the (commuted) operator is a member of the index's opclass */
-       if (op_in_opclass(commuted_op, opclass))
-               return expr_op;
-
-       return InvalidOid;
+       return op_in_opclass(expr_op, opclass);
 }
 
 /****************************************************************************
@@ -1031,7 +1128,8 @@ matches_any_index(RestrictInfo *rinfo, RelOptInfo *rel, Relids outer_relids)
                                                                                 indexcol,
                                                                                 curClass,
                                                                                 rinfo,
-                                                                                outer_relids))
+                                                                                outer_relids,
+                                                                                SAOP_ALLOW))
                                return true;
 
                        indexcol++;
@@ -1137,16 +1235,17 @@ best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel,
 
        /*
         * Find all the index paths that are usable for this join, except for
-        * stuff involving OR clauses.
+        * stuff involving OR and ScalarArrayOpExpr clauses.
         */
        indexpaths = find_usable_indexes(root, rel,
                                                                         clause_list, NIL,
                                                                         false, true,
-                                                                        outer_relids);
+                                                                        outer_relids,
+                                                                        SAOP_FORBID);
 
        /*
-        * Generate BitmapOrPaths for any suitable OR-clauses present in the
-        * clause list.
+        * Generate BitmapOrPaths for any suitable OR-clauses or ScalarArrayOpExpr
+        * clauses present in the clause list.
         */
        bitindexpaths = generate_bitmap_or_paths(root, rel,
                                                                                         clause_list, NIL,
@@ -1384,7 +1483,10 @@ identify_ignorable_ordering_cols(PlannerInfo *root,
                        bool            varonleft;
                        bool            ispc;
 
-                       /* We know this clause passed match_clause_to_indexcol */
+                       /*
+                        * We know this clause passed match_clause_to_indexcol as a
+                        * toplevel clause; so it's not a ScalarArrayOp.
+                        */
 
                        /* First check for boolean-index cases. */
                        if (IsBooleanOpclass(opclass))
@@ -1923,6 +2025,13 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
                                }
                        }
 
+                       /* Next check for ScalarArrayOp cases */
+                       if (IsA(rinfo->clause, ScalarArrayOpExpr))
+                       {
+                               resultquals = lappend(resultquals, rinfo);
+                               continue;
+                       }
+
                        resultquals = list_concat(resultquals,
                                                                          expand_indexqual_condition(rinfo,
                                                                                                                                 curClass));
@@ -2001,7 +2110,7 @@ expand_boolean_index_clause(Node *clause,
 
 /*
  * expand_indexqual_condition --- expand a single indexqual condition
- *             (other than a boolean-qual case)
+ *             (other than a boolean-qual or ScalarArrayOp case)
  *
  * The input is a single RestrictInfo, the output a list of RestrictInfos
  */
index 978419842e3b9654d784374b41c42e18b151fd5a..3bd760fda3a6714c047ce9dd3d7f265603e5ce08 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.203 2005/11/22 18:17:12 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.204 2005/11/25 19:47:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1069,17 +1069,28 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
                                subindexquals = lappend(subindexquals,
                                                                                make_ands_explicit(subindexqual));
                }
-               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 */
+               /*
+                * In the presence of ScalarArrayOpExpr quals, we might have built
+                * BitmapOrPaths with just one subpath; don't add an OR step.
+                */
+               if (list_length(subplans) == 1)
+               {
+                       plan = (Plan *) linitial(subplans);
+               }
+               else
+               {
+                       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 */
+               }
 
                /*
                 * If there were constant-TRUE subquals, the OR reduces to constant
                 * TRUE.  Also, avoid generating one-element ORs, which could happen
-                * due to redundancy elimination.
+                * due to redundancy elimination or ScalarArrayOpExpr quals.
                 */
                if (const_true_subqual)
                        *qual = NIL;
@@ -1531,18 +1542,14 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
        foreach(l, indexquals)
        {
                RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
-               OpExpr     *clause;
-               OpExpr     *newclause;
+               Expr       *clause;
+               Oid                     clause_op;
                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");
 
                /*
                 * Make a copy that will become the fixed clause.
@@ -1551,33 +1558,62 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
                 * is a subplan in the arguments of the opclause.  So just do a full
                 * copy.
                 */
-               newclause = (OpExpr *) copyObject((Node *) clause);
+               clause = (Expr *) copyObject((Node *) rinfo->clause);
 
-               /*
-                * Check to see if the indexkey is on the right; if so, commute the
-                * clause.      The indexkey should be the side that refers to (only) the
-                * base relation.
-                */
-               if (!bms_equal(rinfo->left_relids, index->rel->relids))
-                       CommuteClause(newclause);
+               if (IsA(clause, OpExpr))
+               {
+                       OpExpr *op = (OpExpr *) clause;
 
-               /*
-                * Now, determine which index attribute this is, change the indexkey
-                * operand as needed, and get the index opclass.
-                */
-               linitial(newclause->args) =
-                       fix_indexqual_operand(linitial(newclause->args),
-                                                                 index,
-                                                                 &opclass);
+                       if (list_length(op->args) != 2)
+                               elog(ERROR, "indexqual clause is not binary opclause");
+
+                       /*
+                        * Check to see if the indexkey is on the right; if so, commute
+                        * the clause. The indexkey should be the side that refers to
+                        * (only) the base relation.
+                        */
+                       if (!bms_equal(rinfo->left_relids, index->rel->relids))
+                               CommuteClause(op);
+
+                       /*
+                        * Now, determine which index attribute this is, change the
+                        * indexkey operand as needed, and get the index opclass.
+                        */
+                       linitial(op->args) = fix_indexqual_operand(linitial(op->args),
+                                                                                                          index,
+                                                                                                          &opclass);
+                       clause_op = op->opno;
+               }
+               else if (IsA(clause, ScalarArrayOpExpr))
+               {
+                       ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+
+                       /* Never need to commute... */
+
+                       /*
+                        * Now, determine which index attribute this is, change the
+                        * indexkey operand as needed, and get the index opclass.
+                        */
+                       linitial(saop->args) = fix_indexqual_operand(linitial(saop->args),
+                                                                                                                index,
+                                                                                                                &opclass);
+                       clause_op = saop->opno;
+               }
+               else
+               {
+                       elog(ERROR, "unsupported indexqual type: %d",
+                                (int) nodeTag(clause));
+                       continue;                       /* keep compiler quiet */
+               }
 
-               *fixed_indexquals = lappend(*fixed_indexquals, newclause);
+               *fixed_indexquals = lappend(*fixed_indexquals, clause);
 
                /*
                 * 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(newclause->opno, opclass,
+               get_op_opclass_properties(clause_op, opclass,
                                                                  &stratno, &stratsubtype, &recheck);
 
                *indexstrategy = lappend_int(*indexstrategy, stratno);
index f3a49b30063d7eaa6357e7e0c0fa8d2cadedc75f..f4a3c605c35fc9cefcecc339b157b73f1b3fc26d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.11 2005/11/22 18:17:13 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.12 2005/11/25 19:47:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -347,6 +347,7 @@ build_minmax_path(PlannerInfo *root, RelOptInfo *rel, MinMaxAggInfo *info)
                                                                                                index->rel->baserestrictinfo,
                                                                                                        NIL,
                                                                                                        NULL,
+                                                                                                       SAOP_FORBID,
                                                                                                        &found_clause);
 
                if (list_length(restrictclauses) < indexcol)
index dc135ae9b34d584b59a30988e97122289da3e0c2..a14117032b9004b0ed51e423091d764092d5a58e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.44 2005/11/22 18:17:15 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.45 2005/11/25 19:47:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -171,7 +171,7 @@ make_restrictinfo_from_bitmapqual(Path *bitmapqual,
 
                /*
                 * Avoid generating one-element ORs, which could happen due to
-                * redundancy elimination.
+                * redundancy elimination or ScalarArrayOpExpr quals.
                 */
                if (list_length(withris) <= 1)
                        result = withris;
index 9ff98f05cbe61eb73d1cca34532f52cb4528e338..a92ff26260bdf3c3b6b7f6a8f0de1aaaeb4c5202 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.193 2005/11/22 18:17:23 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.194 2005/11/25 19:47:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1299,6 +1299,173 @@ nulltestsel(PlannerInfo *root, NullTestType nulltesttype,
        return (Selectivity) selec;
 }
 
+/*
+ *             scalararraysel          - Selectivity of ScalarArrayOpExpr Node.
+ */
+Selectivity
+scalararraysel(PlannerInfo *root,
+                          ScalarArrayOpExpr *clause,
+                          bool is_join_clause,
+                          int varRelid, JoinType jointype)
+{
+       Oid                     operator = clause->opno;
+       bool            useOr = clause->useOr;
+       Node       *leftop;
+       Node       *rightop;
+       RegProcedure oprsel;
+       FmgrInfo        oprselproc;
+       Datum           selarg4;
+       Selectivity     s1;
+
+       /*
+        * First, look up the underlying operator's selectivity estimator.
+        * Punt if it hasn't got one.
+        */
+       if (is_join_clause)
+       {
+               oprsel = get_oprjoin(operator);
+               selarg4 = Int16GetDatum(jointype);
+
+       }
+       else
+       {
+               oprsel = get_oprrest(operator);
+               selarg4 = Int32GetDatum(varRelid);
+       }
+       if (!oprsel)
+               return (Selectivity) 0.5;
+       fmgr_info(oprsel, &oprselproc);
+
+       /*
+        * We consider three cases:
+        *
+        * 1. rightop is an Array constant: deconstruct the array, apply the
+        * operator's selectivity function for each array element, and merge
+        * the results in the same way that clausesel.c does for AND/OR
+        * combinations.
+        *
+        * 2. rightop is an ARRAY[] construct: apply the operator's selectivity
+        * function for each element of the ARRAY[] construct, and merge.
+        *
+        * 3. otherwise, make a guess ...
+        */
+       Assert(list_length(clause->args) == 2);
+       leftop = (Node *) linitial(clause->args);
+       rightop = (Node *) lsecond(clause->args);
+
+       if (rightop && IsA(rightop, Const))
+       {
+               Datum           arraydatum = ((Const *) rightop)->constvalue;
+               bool            arrayisnull = ((Const *) rightop)->constisnull;
+               ArrayType  *arrayval;
+               int16           elmlen;
+               bool            elmbyval;
+               char            elmalign;
+               int                     num_elems;
+               Datum      *elem_values;
+               bool       *elem_nulls;
+               int                     i;
+
+               if (arrayisnull)                /* qual can't succeed if null array */
+                       return (Selectivity) 0.0;
+               arrayval = DatumGetArrayTypeP(arraydatum);
+               get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
+                                                        &elmlen, &elmbyval, &elmalign);
+               deconstruct_array(arrayval,
+                                                 ARR_ELEMTYPE(arrayval),
+                                                 elmlen, elmbyval, elmalign,
+                                                 &elem_values, &elem_nulls, &num_elems);
+               s1 = useOr ? 0.0 : 1.0;
+               for (i = 0; i < num_elems; i++)
+               {
+                       List    *args;
+                       Selectivity s2;
+
+                       args = list_make2(leftop,
+                                                         makeConst(ARR_ELEMTYPE(arrayval),
+                                                                               elmlen,
+                                                                               elem_values[i],
+                                                                               elem_nulls[i],
+                                                                               elmbyval));
+                       s2 = DatumGetFloat8(FunctionCall4(&oprselproc,
+                                                                                         PointerGetDatum(root),
+                                                                                         ObjectIdGetDatum(operator),
+                                                                                         PointerGetDatum(args),
+                                                                                         selarg4));
+                       if (useOr)
+                               s1 = s1 + s2 - s1 * s2;
+                       else
+                               s1 = s1 * s2;
+               }
+       }
+       else if (rightop && IsA(rightop, ArrayExpr) &&
+                        !((ArrayExpr *) rightop)->multidims)
+       {
+               ArrayExpr  *arrayexpr = (ArrayExpr *) rightop;
+               int16           elmlen;
+               bool            elmbyval;
+               ListCell   *l;
+
+               get_typlenbyval(arrayexpr->element_typeid,
+                                               &elmlen, &elmbyval);
+               s1 = useOr ? 0.0 : 1.0;
+               foreach(l, arrayexpr->elements)
+               {
+                       List    *args;
+                       Selectivity s2;
+
+                       args = list_make2(leftop, lfirst(l));
+                       s2 = DatumGetFloat8(FunctionCall4(&oprselproc,
+                                                                                         PointerGetDatum(root),
+                                                                                         ObjectIdGetDatum(operator),
+                                                                                         PointerGetDatum(args),
+                                                                                         selarg4));
+                       if (useOr)
+                               s1 = s1 + s2 - s1 * s2;
+                       else
+                               s1 = s1 * s2;
+               }
+       }
+       else
+       {
+               CaseTestExpr *dummyexpr;
+               List    *args;
+               Selectivity s2;
+               int             i;
+
+               /*
+                * We need a dummy rightop to pass to the operator selectivity
+                * routine.  It can be pretty much anything that doesn't look like
+                * a constant; CaseTestExpr is a convenient choice.
+                */
+               dummyexpr = makeNode(CaseTestExpr);
+               dummyexpr->typeId = get_element_type(exprType(rightop));
+               dummyexpr->typeMod = -1;
+               args = list_make2(leftop, dummyexpr);
+               s2 = DatumGetFloat8(FunctionCall4(&oprselproc,
+                                                                                 PointerGetDatum(root),
+                                                                                 ObjectIdGetDatum(operator),
+                                                                                 PointerGetDatum(args),
+                                                                                 selarg4));
+               s1 = useOr ? 0.0 : 1.0;
+               /*
+                * Arbitrarily assume 10 elements in the eventual array value
+                */
+               for (i = 0; i < 10; i++)
+               {
+                       if (useOr)
+                               s1 = s1 + s2 - s1 * s2;
+                       else
+                               s1 = s1 * s2;
+               }
+       }
+
+       /* result should be in range, but make sure... */
+       CLAMP_PROBABILITY(s1);
+
+       return s1;
+}
+
 /*
  *             eqjoinsel               - Join selectivity of "="
  */
@@ -4330,6 +4497,7 @@ btcostestimate(PG_FUNCTION_ARGS)
        List       *indexBoundQuals;
        int                     indexcol;
        bool            eqQualHere;
+       bool            found_saop;
        ListCell   *l;
 
        /*
@@ -4341,26 +4509,52 @@ btcostestimate(PG_FUNCTION_ARGS)
         * for estimating numIndexTuples.  So we must examine the given indexQuals
         * to find out which ones count as boundary quals.      We rely on the
         * knowledge that they are given in index column order.
+        *
+        * If there's a ScalarArrayOpExpr in the quals, we'll actually perform
+        * N index scans not one, but the ScalarArrayOpExpr's operator can be
+        * considered to act the same as it normally does.
         */
        indexBoundQuals = NIL;
        indexcol = 0;
        eqQualHere = false;
+       found_saop = false;
        foreach(l, indexQuals)
        {
                RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
                Expr       *clause;
+               Node       *leftop,
+                                  *rightop;
                Oid                     clause_op;
                int                     op_strategy;
 
                Assert(IsA(rinfo, RestrictInfo));
                clause = rinfo->clause;
-               Assert(IsA(clause, OpExpr));
-               clause_op = ((OpExpr *) clause)->opno;
-               if (match_index_to_operand(get_leftop(clause), indexcol, index))
+               if (IsA(clause, OpExpr))
+               {
+                       leftop = get_leftop(clause);
+                       rightop = get_rightop(clause);
+                       clause_op = ((OpExpr *) clause)->opno;
+               }
+               else if (IsA(clause, ScalarArrayOpExpr))
+               {
+                       ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+
+                       leftop = (Node *) linitial(saop->args);
+                       rightop = (Node *) lsecond(saop->args);
+                       clause_op = saop->opno;
+                       found_saop = true;
+               }
+               else
+               {
+                       elog(ERROR, "unsupported indexqual type: %d",
+                                (int) nodeTag(clause));
+                       continue;                       /* keep compiler quiet */
+               }
+               if (match_index_to_operand(leftop, indexcol, index))
                {
                        /* clause_op is correct */
                }
-               else if (match_index_to_operand(get_rightop(clause), indexcol, index))
+               else if (match_index_to_operand(rightop, indexcol, index))
                {
                        /* Must flip operator to get the opclass member */
                        clause_op = get_commutator(clause_op);
@@ -4372,12 +4566,11 @@ btcostestimate(PG_FUNCTION_ARGS)
                                break;                  /* done if no '=' qual for indexcol */
                        indexcol++;
                        eqQualHere = false;
-                       if (match_index_to_operand(get_leftop(clause), indexcol, index))
+                       if (match_index_to_operand(leftop, indexcol, index))
                        {
                                /* clause_op is correct */
                        }
-                       else if (match_index_to_operand(get_rightop(clause),
-                                                                                       indexcol, index))
+                       else if (match_index_to_operand(rightop, indexcol, index))
                        {
                                /* Must flip operator to get the opclass member */
                                clause_op = get_commutator(clause_op);
@@ -4401,7 +4594,10 @@ btcostestimate(PG_FUNCTION_ARGS)
         * just assume numIndexTuples = 1 and skip the expensive
         * clauselist_selectivity calculations.
         */
-       if (index->unique && indexcol == index->ncolumns - 1 && eqQualHere)
+       if (index->unique &&
+               indexcol == index->ncolumns - 1 &&
+               eqQualHere &&
+               !found_saop)
                numIndexTuples = 1.0;
        else
        {
@@ -4424,7 +4620,14 @@ btcostestimate(PG_FUNCTION_ARGS)
         * is that multiple columns dilute the importance of the first column's
         * ordering, but don't negate it entirely.  Before 8.0 we divided the
         * correlation by the number of columns, but that seems too strong.)
+        *
+        * We can skip all this if we found a ScalarArrayOpExpr, because then
+        * the call must be for a bitmap index scan, and the caller isn't going
+        * to care what the index correlation is.
         */
+       if (found_saop)
+               PG_RETURN_VOID();
+
        if (index->indexkeys[0] != 0)
        {
                /* Simple variable --- look to stats for the underlying table */
index 7f280c892e18a72b1bcecbfae0d2ae9df2f3269c..21bb254f63e898501092593216b025a39ecbf103 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.24 2005/10/15 02:49:44 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/executor/nodeIndexscan.h,v 1.25 2005/11/25 19:47:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,13 +25,15 @@ 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,
+extern void ExecIndexBuildScanKeys(PlanState *planstate, List *quals,
                                           List *strategies, List *subtypes,
-                                          ExprState ***runtimeKeyInfo,
-                                          ScanKey *scanKeys, int *numScanKeys);
+                                          ScanKey *scanKeys, int *numScanKeys,
+                                          IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
+                                          IndexArrayKeyInfo **arrayKeys, int *numArrayKeys);
 extern void ExecIndexEvalRuntimeKeys(ExprContext *econtext,
-                                                ExprState **run_keys,
-                                                ScanKey scan_keys,
-                                                int n_keys);
+                                                IndexRuntimeKeyInfo *runtimeKeys, int numRuntimeKeys);
+extern bool ExecIndexEvalArrayKeys(ExprContext *econtext,
+                                                IndexArrayKeyInfo *arrayKeys, int numArrayKeys);
+extern bool ExecIndexAdvanceArrayKeys(IndexArrayKeyInfo *arrayKeys, int numArrayKeys);
 
 #endif   /* NODEINDEXSCAN_H */
index 63e864e4636bdcad27ff86b47f2a3309a83f57b6..f70847798e662bbcb16e5898de554cdfafa8ba2a 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.141 2005/11/22 18:17:30 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.142 2005/11/25 19:47:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -871,16 +871,37 @@ typedef struct ScanState
  */
 typedef ScanState SeqScanState;
 
+/*
+ * These structs store information about index quals that don't have simple
+ * constant right-hand sides.  See comments for ExecIndexBuildScanKeys()
+ * for discussion.
+ */
+typedef struct
+{
+       ScanKey         scan_key;               /* scankey to put value into */
+       ExprState  *key_expr;           /* expr to evaluate to get value */
+} IndexRuntimeKeyInfo;
+
+typedef struct
+{
+       ScanKey         scan_key;               /* scankey to put value into */
+       ExprState  *array_expr;         /* expr to evaluate to get array value */
+       int                     next_elem;              /* next array element to use */
+       int                     num_elems;              /* number of elems in current array value */
+       Datum      *elem_values;        /* array of num_elems Datums */
+       bool       *elem_nulls;         /* array of num_elems is-null flags */
+} IndexArrayKeyInfo;
+
 /* ----------------
  *      IndexScanState information
  *
  *             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
+ *             RuntimeKeys                info about Skeys that must be evaluated at runtime
+ *             NumRuntimeKeys     number of RuntimeKeys structs
  *             RuntimeKeysReady   true if runtime Skeys have been computed
+ *             RuntimeContext     expr context for evaling runtime Skeys
  *             RelationDesc       index relation descriptor
  *             ScanDesc                   index scan descriptor
  * ----------------
@@ -891,9 +912,10 @@ typedef struct IndexScanState
        List       *indexqualorig;
        ScanKey         iss_ScanKeys;
        int                     iss_NumScanKeys;
-       ExprState **iss_RuntimeKeyInfo;
-       ExprContext *iss_RuntimeContext;
+       IndexRuntimeKeyInfo *iss_RuntimeKeys;
+       int                     iss_NumRuntimeKeys;
        bool            iss_RuntimeKeysReady;
+       ExprContext *iss_RuntimeContext;
        Relation        iss_RelationDesc;
        IndexScanDesc iss_ScanDesc;
 } IndexScanState;
@@ -904,10 +926,12 @@ typedef struct IndexScanState
  *             result                     bitmap to return output into, or NULL
  *             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
+ *             RuntimeKeys                info about Skeys that must be evaluated at runtime
+ *             NumRuntimeKeys     number of RuntimeKeys structs
+ *             ArrayKeys                  info about Skeys that come from ScalarArrayOpExprs
+ *             NumArrayKeys       number of ArrayKeys structs
  *             RuntimeKeysReady   true if runtime Skeys have been computed
+ *             RuntimeContext     expr context for evaling runtime Skeys
  *             RelationDesc       index relation descriptor
  *             ScanDesc                   index scan descriptor
  * ----------------
@@ -918,9 +942,12 @@ typedef struct BitmapIndexScanState
        TIDBitmap  *biss_result;
        ScanKey         biss_ScanKeys;
        int                     biss_NumScanKeys;
-       ExprState **biss_RuntimeKeyInfo;
-       ExprContext *biss_RuntimeContext;
+       IndexRuntimeKeyInfo *biss_RuntimeKeys;
+       int                     biss_NumRuntimeKeys;
+       IndexArrayKeyInfo *biss_ArrayKeys;
+       int                     biss_NumArrayKeys;
        bool            biss_RuntimeKeysReady;
+       ExprContext *biss_RuntimeContext;
        Relation        biss_RelationDesc;
        IndexScanDesc biss_ScanDesc;
 } BitmapIndexScanState;
index 4020f4bf49a3dede46f49290cd9c0a66b9122aea..eba65c699c057afa05400616a04598fbbdcf5109 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/paths.h,v 1.88 2005/10/15 02:49:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.89 2005/11/25 19:47:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,6 +34,14 @@ extern void debug_print_rel(PlannerInfo *root, RelOptInfo *rel);
  * indxpath.c
  *       routines to generate index paths
  */
+typedef enum
+{
+       /* Whether to use ScalarArrayOpExpr to build index qualifications */
+       SAOP_FORBID,                            /* Do not use ScalarArrayOpExpr */
+       SAOP_ALLOW,                                     /* OK to use ScalarArrayOpExpr */
+       SAOP_REQUIRE                            /* Require ScalarArrayOpExpr */
+} SaOpControl;
+
 extern void create_index_paths(PlannerInfo *root, RelOptInfo *rel);
 extern List *generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
                                                 List *clauses, List *outer_clauses,
@@ -44,6 +52,7 @@ extern Path *best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel,
 extern List *group_clauses_by_indexkey(IndexOptInfo *index,
                                                  List *clauses, List *outer_clauses,
                                                  Relids outer_relids,
+                                                 SaOpControl saop_control,
                                                  bool *found_clause);
 extern bool match_index_to_operand(Node *operand, int indexcol,
                                           IndexOptInfo *index);
index 7ba2dde1d90c45b0ea4202d3d1f326b534c097c6..2c021ad25022881f64594f190dd36c0592c2d54a 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/utils/selfuncs.h,v 1.25 2005/11/07 17:36:47 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/selfuncs.h,v 1.26 2005/11/25 19:47:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -112,6 +112,10 @@ extern Selectivity booltestsel(PlannerInfo *root, BoolTestType booltesttype,
                        Node *arg, int varRelid, JoinType jointype);
 extern Selectivity nulltestsel(PlannerInfo *root, NullTestType nulltesttype,
                        Node *arg, int varRelid);
+extern Selectivity scalararraysel(PlannerInfo *root,
+                                                                 ScalarArrayOpExpr *clause,
+                                                                 bool is_join_clause,
+                                                                 int varRelid, JoinType jointype);
 
 extern void mergejoinscansel(PlannerInfo *root, Node *clause,
                                 Selectivity *leftscan,