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; + have_runtime_keys = + ExecIndexBuildScanKeys((PlanState *) indexstate, + node->indexqual, + node->indexstrategy, + node->indexsubtype, + &runtimeKeyInfo, + &scanKeys, + &numScanKeys); - 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; - } + 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) + if ((tuple = index_getnext(scandesc, direction)) != NULL) { - indexNumber = numIndices - node->iss_IndexPtr - 1; - if (indexNumber < 0) - { - indexNumber = 0; - node->iss_IndexPtr = numIndices - 1; - } - } - else - { - 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]; + /* + * 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 */ - 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 */ - } - - 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) + /* reset index scan */ + index_rescan(node->iss_ScanDesc, scanKeys); +} + + +/* + * 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; + + for (j = 0; j < n_keys; j++) { - if (node->iss_DupHash) - hash_destroy(node->iss_DupHash); - create_duphash(node); - } + /* + * 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; - /* 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]; - - index_rescan(scan, skey); + 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; + have_runtime_keys = + ExecIndexBuildScanKeys((PlanState *) indexstate, + node->indexqual, + node->indexstrategy, + node->indexsubtype, + &runtimeKeyInfo, + &scanKeys, + &numScanKeys); - 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; + indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo; indexstate->iss_ScanKeys = scanKeys; indexstate->iss_NumScanKeys = numScanKeys; /* - * If all of our keys have the form (var op const), then we have no - * runtime keys so we store NULL in the runtime key info. Otherwise - * runtime key info contains an array of pointers (one for each index) - * to arrays of flags (one for each key) which indicate that the qual - * needs to be evaluated at runtime. -cim 10/24/89 - * - * If we do have runtime keys, we need an ExprContext to evaluate them; - * the node's standard context won't do because we want to reset that + * If we have runtime keys, we need an ExprContext to evaluate them. + * The node's standard context won't do because we want to reset that * context for every tuple. So, build another context just like the * other one... -tgl 7/11/00 */ @@ -948,21 +449,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext; ExecAssignExprContext(estate, &indexstate->ss.ps); - indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo; indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext; indexstate->ss.ps.ps_ExprContext = stdecontext; } else { - indexstate->iss_RuntimeKeyInfo = NULL; indexstate->iss_RuntimeContext = NULL; - /* Get rid of the speculatively-allocated flag arrays, too */ - for (i = 0; i < numIndices; i++) - { - if (runtimeKeyInfo[i] != NULL) - pfree(runtimeKeyInfo[i]); - } - pfree(runtimeKeyInfo); } /* @@ -983,27 +475,17 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation), false); /* - * open the index relations and initialize relation and scan + * open the index relation and initialize relation and scan * descriptors. Note we acquire no locks here; the index machinery * does its own locks and unlocks. (We rely on having AccessShareLock * on the parent table to ensure the index won't go away!) */ - for (i = 0; i < numIndices; i++) - { - Oid indexOid = lfirst_oid(indxid_item); - - indexDescs[i] = index_open(indexOid); - scanDescs[i] = index_beginscan(currentRelation, - indexDescs[i], - estate->es_snapshot, - numScanKeys[i], - scanKeys[i]); - indxid_item = lnext(indxid_item); - } - - indexstate->iss_RelationDescs = indexDescs; - indexstate->iss_ScanDescs = scanDescs; - indexstate->iss_LossyQuals = lossyQuals; + indexstate->iss_RelationDesc = index_open(node->indexid); + indexstate->iss_ScanDesc = index_beginscan(currentRelation, + indexstate->iss_RelationDesc, + estate->es_snapshot, + numScanKeys, + scanKeys); /* * Initialize result tuple type and projection info. @@ -1011,42 +493,180 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ExecAssignResultTypeFromTL(&indexstate->ss.ps); ExecAssignScanProjectionInfo(&indexstate->ss); - /* - * Initialize hash table if needed. - */ - if (numIndices > 1) - create_duphash(indexstate); - else - indexstate->iss_DupHash = NULL; - /* * all done. */ return indexstate; } -static void -create_duphash(IndexScanState *node) -{ - 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); +/* + * 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) +{ + 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 */ } @@ -714,108 +705,6 @@ group_clauses_by_indexkey(IndexOptInfo *index, } -/* - * group_clauses_by_indexkey_for_or - * Generate a list of sublists of clauses that can be used with an index - * to find rows matching an OR subclause. - * - * This is essentially just like group_clauses_by_indexkey() except that - * we can use the given clause (or any AND subclauses of it) as well as - * top-level restriction clauses of the relation. Furthermore, we demand - * that at least one such use be made, otherwise we fail and return NIL. - * (Any path we made without such a use would be redundant with non-OR - * indexscans.) - * - * XXX When we generate an indexqual list that uses both the OR subclause - * and top-level restriction clauses, we end up with a slightly inefficient - * plan because create_indexscan_plan is not very bright about figuring out - * which restriction clauses are implied by the generated indexqual condition. - * Currently we'll end up rechecking both the OR clause and the top-level - * restriction clause as qpquals. FIXME someday. - */ -List * -group_clauses_by_indexkey_for_or(IndexOptInfo *index, Expr *orsubclause) -{ - List *clausegroup_list = NIL; - bool matched = false; - int indexcol = 0; - Oid *classes = index->classlist; - - do - { - Oid curClass = classes[0]; - List *clausegroup = NIL; - ListCell *item; - - /* Try to match the OR subclause to the index key */ - if (IsA(orsubclause, RestrictInfo)) - { - if (match_clause_to_indexcol(index, indexcol, curClass, - (RestrictInfo *) orsubclause, - NULL)) - { - clausegroup = lappend(clausegroup, orsubclause); - matched = true; - } - } - else if (and_clause((Node *) orsubclause)) - { - foreach(item, ((BoolExpr *) orsubclause)->args) - { - RestrictInfo *subsubclause = (RestrictInfo *) lfirst(item); - - if (IsA(subsubclause, RestrictInfo) && - match_clause_to_indexcol(index, indexcol, curClass, - subsubclause, - NULL)) - { - clausegroup = lappend(clausegroup, subsubclause); - matched = true; - } - } - } - - /* - * If we found no clauses for this indexkey in the OR subclause - * itself, try looking in the rel's top-level restriction list. - * - * XXX should we always search the top-level list? Slower but could - * sometimes yield a better plan. - */ - if (clausegroup == NIL) - { - foreach(item, index->rel->baserestrictinfo) - { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(item); - - if (match_clause_to_indexcol(index, indexcol, curClass, - rinfo, - NULL)) - clausegroup = lappend(clausegroup, rinfo); - } - } - - /* - * If still no clauses match this key, we're done; we don't want - * to look at keys to its right. - */ - if (clausegroup == NIL) - break; - - clausegroup_list = lappend(clausegroup_list, clausegroup); - - indexcol++; - classes++; - } while (!DoneMatchingIndexKeys(classes)); - - /* if OR clause was not used then forget it, per comments above */ - if (!matched) - return NIL; - - return clausegroup_list; -} - - /* * match_clause_to_indexcol() * Determines whether a restriction clause matches a column of an index. @@ -2017,7 +1906,7 @@ find_clauses_for_join(Query *root, RelOptInfo *rel, * of RestrictInfos. * * This is used to flatten out the result of group_clauses_by_indexkey() - * or one of its sibling routines, to produce an indexclauses list. + * to produce an indexclauses list. */ List * flatten_clausegroups_list(List *clausegroups) @@ -2030,39 +1919,6 @@ flatten_clausegroups_list(List *clausegroups) return allclauses; } -/* - * make_expr_from_indexclauses() - * Given an indexclauses structure, produce an ordinary boolean expression. - * - * This consists of stripping out the RestrictInfo nodes and inserting - * explicit AND and OR nodes as needed. There's not much to it, but - * the functionality is needed in a few places, so centralize the logic. - */ -Expr * -make_expr_from_indexclauses(List *indexclauses) -{ - List *orclauses = NIL; - ListCell *orlist; - - /* There's no such thing as an indexpath with zero scans */ - Assert(indexclauses != NIL); - - foreach(orlist, indexclauses) - { - List *andlist = (List *) lfirst(orlist); - - /* Strip RestrictInfos */ - andlist = get_actual_clauses(andlist); - /* Insert AND node if needed, and add to orclauses list */ - orclauses = lappend(orclauses, make_ands_explicit(andlist)); - } - - if (list_length(orclauses) > 1) - return make_orclause(orclauses); - else - return (Expr *) linitial(orclauses); -} - /**************************************************************************** * ---- ROUTINES TO CHECK OPERANDS ---- @@ -2403,7 +2259,7 @@ match_special_index_operator(Expr *clause, Oid opclass, * * The input list is ordered by index key, and so the output list is too. * (The latter is not depended on by any part of the planner, so far as I can - * tell; but some parts of the executor do assume that the indxqual list + * tell; but some parts of the executor do assume that the indexqual list * ultimately delivered to the executor is so ordered. One such place is * _bt_preprocess_keys() in the btree support. Perhaps that ought to be fixed * someday --- tgl 7/00) 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; + /* + * 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; - pathnode = best_or_subclause_indexes(root, - rel, - ((BoolExpr *) rinfo->orclause)->args); + orpaths = generate_bitmap_or_paths(root, rel, + list_make1(rinfo), + rel->baserestrictinfo, + false, NULL); - if (pathnode) + /* 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)); + Plan *subplan; + List *subqual; + List *subindexqual; - newlist = lappend(newlist, subplan); + 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)); + Plan *subplan; + List *subqual; + List *subindexqual; - newlist = lappend(newlist, subplan); + 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)) + bitmapquals = create_bitmap_qual(bitmapqual); + + /* must convert qual list to restrictinfos ... painful ... */ + bitmapclauses = NIL; + foreach(l, bitmapquals) { - BitmapAndPath *apath = (BitmapAndPath *) bitmapqual; - - result = NIL; - foreach(l, apath->bitmapquals) - { - sublist = create_bitmap_indxqual(lfirst(l)); - result = list_concat(result, sublist); - } - } - else if (IsA(bitmapqual, BitmapOrPath)) - { - 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. + * 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. * - * 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. - * - * 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. + * nonlossy_indexquals receives a list of the original input clauses (with + * RestrictInfos) that contain non-lossy operators. + * + * indexstrategy receives an integer list of strategy numbers. + * indexsubtype receives an OID list of strategy subtypes. */ 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. - * - * 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) - */ -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 $ * *------------------------------------------------------------------------- */ @@ -65,55 +65,10 @@ make_restrictinfo(Expr *clause, bool is_pushed_down, bool valid_everywhere) is_pushed_down, valid_everywhere); } -/* - * make_restrictinfo_from_indexclauses - * - * Given an indexclauses structure, convert to ordinary expression format - * and build RestrictInfo node(s). - * - * The result is a List since we might need to return multiple RestrictInfos. - * - * This could be done as make_restrictinfo(make_expr_from_indexclauses()), - * but if we did it that way then we would strip the original RestrictInfo - * nodes from the index clauses and be forced to build new ones. It's better - * to have a specialized routine that allows sharing of RestrictInfos. - */ -List * -make_restrictinfo_from_indexclauses(List *indexclauses, - bool is_pushed_down, - bool valid_everywhere) -{ - List *withris = NIL; - List *withoutris = NIL; - ListCell *orlist; - - /* Empty list probably can't happen, but here's what to do */ - if (indexclauses == NIL) - return NIL; - /* If single indexscan, just return the ANDed clauses */ - if (list_length(indexclauses) == 1) - return (List *) linitial(indexclauses); - /* Else we need an OR RestrictInfo structure */ - foreach(orlist, indexclauses) - { - List *andlist = (List *) lfirst(orlist); - - /* Create AND subclause with RestrictInfos */ - withris = lappend(withris, make_ands_explicit(andlist)); - /* And one without */ - andlist = get_actual_clauses(andlist); - withoutris = lappend(withoutris, make_ands_explicit(andlist)); - } - return list_make1(make_restrictinfo_internal(make_orclause(withoutris), - make_orclause(withris), - is_pushed_down, - valid_everywhere)); -} - /* * make_restrictinfo_internal * - * Common code for the above two entry points. + * Common code for the main entry point and the recursive cases. */ static RestrictInfo * make_restrictinfo_internal(Expr *clause, Expr *orclause, 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,