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