aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/nodeBitmapIndexscan.c94
-rw-r--r--src/backend/executor/nodeIndexscan.c476
-rw-r--r--src/backend/optimizer/path/clausesel.c39
-rw-r--r--src/backend/optimizer/path/indxpath.c241
-rw-r--r--src/backend/optimizer/plan/createplan.c100
-rw-r--r--src/backend/optimizer/plan/planagg.c3
-rw-r--r--src/backend/optimizer/util/restrictinfo.c4
-rw-r--r--src/backend/utils/adt/selfuncs.c221
-rw-r--r--src/include/executor/nodeIndexscan.h16
-rw-r--r--src/include/nodes/execnodes.h49
-rw-r--r--src/include/optimizer/paths.h11
-rw-r--r--src/include/utils/selfuncs.h6
12 files changed, 942 insertions, 318 deletions
diff --git a/src/backend/executor/nodeBitmapIndexscan.c b/src/backend/executor/nodeBitmapIndexscan.c
index 902eeb35a43..46e8c146521 100644
--- a/src/backend/executor/nodeBitmapIndexscan.c
+++ b/src/backend/executor/nodeBitmapIndexscan.c
@@ -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.
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 4f6fadfde49..f34a14c8596 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -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
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 9a4990898e9..eba6bf1d148 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -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 */
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 6f3157a23f6..fde89455685 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -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)
@@ -50,16 +48,18 @@
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
*/
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 978419842e3..3bd760fda3a 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -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);
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index f3a49b30063..f4a3c605c35 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -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)
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index dc135ae9b34..a14117032b9 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -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;
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 9ff98f05cbe..a92ff26260b 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -1300,6 +1300,173 @@ nulltestsel(PlannerInfo *root, NullTestType nulltesttype,
}
/*
+ * 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 "="
*/
Datum
@@ -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 */
diff --git a/src/include/executor/nodeIndexscan.h b/src/include/executor/nodeIndexscan.h
index 7f280c892e1..21bb254f63e 100644
--- a/src/include/executor/nodeIndexscan.h
+++ b/src/include/executor/nodeIndexscan.h
@@ -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 */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 63e864e4636..f70847798e6 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -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;
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 4020f4bf49a..eba65c699c0 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -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);
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index 7ba2dde1d90..2c021ad2502 100644
--- a/src/include/utils/selfuncs.h
+++ b/src/include/utils/selfuncs.h
@@ -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,