Commit b0c4a50b authored by Tom Lane's avatar Tom Lane

Instead of rechecking lossy index operators by putting them into the

regular qpqual ('filter condition'), add special-purpose code to
nodeIndexscan.c to recheck them.  This ends being almost no net addition
of code, because the removal of planner code balances out the extra
executor code, but it is significantly more efficient when a lossy
operator is involved in an OR indexscan.  The old implementation had
to recheck the entire indexqual in such cases.
parent fa559a86
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.88 2003/12/30 20:05:05 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.89 2004/01/06 04:31:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -99,7 +99,9 @@ IndexNext(IndexScanState *node) ...@@ -99,7 +99,9 @@ IndexNext(IndexScanState *node)
ExprContext *econtext; ExprContext *econtext;
ScanDirection direction; ScanDirection direction;
IndexScanDescPtr scanDescs; IndexScanDescPtr scanDescs;
List **lossyQuals;
IndexScanDesc scandesc; IndexScanDesc scandesc;
List *lossyQual;
Index scanrelid; Index scanrelid;
HeapTuple tuple; HeapTuple tuple;
TupleTableSlot *slot; TupleTableSlot *slot;
...@@ -120,6 +122,7 @@ IndexNext(IndexScanState *node) ...@@ -120,6 +122,7 @@ IndexNext(IndexScanState *node)
direction = ForwardScanDirection; direction = ForwardScanDirection;
} }
scanDescs = node->iss_ScanDescs; scanDescs = node->iss_ScanDescs;
lossyQuals = node->iss_LossyQuals;
numIndices = node->iss_NumIndices; numIndices = node->iss_NumIndices;
econtext = node->ss.ps.ps_ExprContext; econtext = node->ss.ps.ps_ExprContext;
slot = node->ss.ss_ScanTupleSlot; slot = node->ss.ss_ScanTupleSlot;
...@@ -188,6 +191,8 @@ IndexNext(IndexScanState *node) ...@@ -188,6 +191,8 @@ IndexNext(IndexScanState *node)
while (indexNumber < numIndices) while (indexNumber < numIndices)
{ {
scandesc = scanDescs[node->iss_IndexPtr]; scandesc = scanDescs[node->iss_IndexPtr];
lossyQual = lossyQuals[node->iss_IndexPtr];
while ((tuple = index_getnext(scandesc, direction)) != NULL) while ((tuple = index_getnext(scandesc, direction)) != NULL)
{ {
/* /*
...@@ -201,6 +206,22 @@ IndexNext(IndexScanState *node) ...@@ -201,6 +206,22 @@ IndexNext(IndexScanState *node)
scandesc->xs_cbuf, /* buffer containing tuple */ scandesc->xs_cbuf, /* buffer containing tuple */
false); /* don't pfree */ 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 * If it's a multiple-index scan, make sure not to double-report
* a tuple matched by more than one index. (See notes above.) * a tuple matched by more than one index. (See notes above.)
...@@ -615,6 +636,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ...@@ -615,6 +636,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
List *indxqual; List *indxqual;
List *indxstrategy; List *indxstrategy;
List *indxsubtype; List *indxsubtype;
List *indxlossy;
List *indxid; List *indxid;
int i; int i;
int numIndices; int numIndices;
...@@ -623,6 +645,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ...@@ -623,6 +645,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
int *numScanKeys; int *numScanKeys;
RelationPtr indexDescs; RelationPtr indexDescs;
IndexScanDescPtr scanDescs; IndexScanDescPtr scanDescs;
List **lossyQuals;
ExprState ***runtimeKeyInfo; ExprState ***runtimeKeyInfo;
bool have_runtime_keys; bool have_runtime_keys;
RangeTblEntry *rtentry; RangeTblEntry *rtentry;
...@@ -680,6 +703,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ...@@ -680,6 +703,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
indexstate->iss_RuntimeKeysReady = false; indexstate->iss_RuntimeKeysReady = false;
indexstate->iss_RelationDescs = NULL; indexstate->iss_RelationDescs = NULL;
indexstate->iss_ScanDescs = NULL; indexstate->iss_ScanDescs = NULL;
indexstate->iss_LossyQuals = NULL;
/* /*
* get the index node information * get the index node information
...@@ -699,6 +723,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ...@@ -699,6 +723,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey)); scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey));
indexDescs = (RelationPtr) palloc(numIndices * sizeof(Relation)); indexDescs = (RelationPtr) palloc(numIndices * sizeof(Relation));
scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc)); scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
lossyQuals = (List **) palloc0(numIndices * sizeof(List *));
/* /*
* initialize space for runtime key info (may not be needed) * initialize space for runtime key info (may not be needed)
...@@ -712,11 +737,13 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ...@@ -712,11 +737,13 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
indxqual = node->indxqual; indxqual = node->indxqual;
indxstrategy = node->indxstrategy; indxstrategy = node->indxstrategy;
indxsubtype = node->indxsubtype; indxsubtype = node->indxsubtype;
indxlossy = node->indxlossy;
for (i = 0; i < numIndices; i++) for (i = 0; i < numIndices; i++)
{ {
List *quals; List *quals;
List *strategies; List *strategies;
List *subtypes; List *subtypes;
List *lossyflags;
int n_keys; int n_keys;
ScanKey scan_keys; ScanKey scan_keys;
ExprState **run_keys; ExprState **run_keys;
...@@ -728,6 +755,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ...@@ -728,6 +755,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
indxstrategy = lnext(indxstrategy); indxstrategy = lnext(indxstrategy);
subtypes = lfirst(indxsubtype); subtypes = lfirst(indxsubtype);
indxsubtype = lnext(indxsubtype); indxsubtype = lnext(indxsubtype);
lossyflags = lfirst(indxlossy);
indxlossy = lnext(indxlossy);
n_keys = length(quals); n_keys = length(quals);
scan_keys = (n_keys <= 0) ? (ScanKey) NULL : scan_keys = (n_keys <= 0) ? (ScanKey) NULL :
(ScanKey) palloc(n_keys * sizeof(ScanKeyData)); (ScanKey) palloc(n_keys * sizeof(ScanKeyData));
...@@ -747,6 +776,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ...@@ -747,6 +776,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
AttrNumber varattno; /* att number used in scan */ AttrNumber varattno; /* att number used in scan */
StrategyNumber strategy; /* op's strategy number */ StrategyNumber strategy; /* op's strategy number */
Oid subtype; /* op's strategy subtype */ Oid subtype; /* op's strategy subtype */
int lossy; /* op's recheck flag */
RegProcedure opfuncid; /* operator proc id used in scan */ RegProcedure opfuncid; /* operator proc id used in scan */
Datum scanvalue; /* value used in scan (if const) */ Datum scanvalue; /* value used in scan (if const) */
...@@ -759,6 +789,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ...@@ -759,6 +789,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
strategies = lnext(strategies); strategies = lnext(strategies);
subtype = lfirsto(subtypes); subtype = lfirsto(subtypes);
subtypes = lnext(subtypes); subtypes = lnext(subtypes);
lossy = lfirsti(lossyflags);
lossyflags = lnext(lossyflags);
if (!IsA(clause, OpExpr)) if (!IsA(clause, OpExpr))
elog(ERROR, "indxqual is not an OpExpr"); elog(ERROR, "indxqual is not an OpExpr");
...@@ -839,6 +871,20 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ...@@ -839,6 +871,20 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
subtype, /* strategy subtype */ subtype, /* strategy subtype */
opfuncid, /* reg proc to use */ opfuncid, /* reg proc to use */
scanvalue); /* constant */ scanvalue); /* constant */
/*
* If this operator is lossy, add its indxqualorig expression
* to the list of quals to recheck. The 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)
{
lossyQuals[i] = lappend(lossyQuals[i],
nth(j,
(List *) nth(i, indexstate->indxqualorig)));
}
} }
/* /*
...@@ -928,6 +974,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate) ...@@ -928,6 +974,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
indexstate->iss_RelationDescs = indexDescs; indexstate->iss_RelationDescs = indexDescs;
indexstate->iss_ScanDescs = scanDescs; indexstate->iss_ScanDescs = scanDescs;
indexstate->iss_LossyQuals = lossyQuals;
/* /*
* Initialize result tuple type and projection info. * Initialize result tuple type and projection info.
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.273 2004/01/05 05:07:35 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.274 2004/01/06 04:31:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -284,6 +284,17 @@ _copyIndexScan(IndexScan *from) ...@@ -284,6 +284,17 @@ _copyIndexScan(IndexScan *from)
} }
newnode->indxsubtype = newsubtype; newnode->indxsubtype = newsubtype;
} }
/* this can become COPY_NODE_FIELD when intlists are normal objects: */
{
List *newstrat = NIL;
List *tmp;
foreach(tmp, from->indxlossy)
{
newstrat = lappend(newstrat, listCopy(lfirst(tmp)));
}
newnode->indxlossy = newstrat;
}
COPY_SCALAR_FIELD(indxorderdir); COPY_SCALAR_FIELD(indxorderdir);
return newnode; return newnode;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.228 2004/01/05 23:39:53 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.229 2004/01/06 04:31:01 tgl Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -356,6 +356,16 @@ _outIndexScan(StringInfo str, IndexScan *node) ...@@ -356,6 +356,16 @@ _outIndexScan(StringInfo str, IndexScan *node)
_outOidList(str, lfirst(tmp)); _outOidList(str, lfirst(tmp));
} }
} }
/* this can become WRITE_NODE_FIELD when intlists are normal objects: */
{
List *tmp;
appendStringInfo(str, " :indxlossy ");
foreach(tmp, node->indxlossy)
{
_outIntList(str, lfirst(tmp));
}
}
WRITE_ENUM_FIELD(indxorderdir, ScanDirection); WRITE_ENUM_FIELD(indxorderdir, ScanDirection);
} }
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.121 2004/01/05 23:39:54 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.122 2004/01/06 04:31:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -384,10 +384,6 @@ cost_index(Path *path, Query *root, ...@@ -384,10 +384,6 @@ cost_index(Path *path, Query *root,
* some of the indexquals are join clauses and shouldn't be * some of the indexquals are join clauses and shouldn't be
* subtracted. Rather than work out exactly how much to subtract, we * subtracted. Rather than work out exactly how much to subtract, we
* don't subtract anything. * don't subtract anything.
*
* XXX For a lossy index, not all the quals will be removed and so we
* really shouldn't subtract their costs; but detecting that seems
* more expensive than it's worth.
*/ */
startup_cost += baserel->baserestrictcost.startup; startup_cost += baserel->baserestrictcost.startup;
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple; cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
...@@ -397,6 +393,7 @@ cost_index(Path *path, Query *root, ...@@ -397,6 +393,7 @@ cost_index(Path *path, Query *root,
QualCost index_qual_cost; QualCost index_qual_cost;
cost_qual_eval(&index_qual_cost, indexQuals); cost_qual_eval(&index_qual_cost, indexQuals);
/* any startup cost still has to be paid ... */
cpu_per_tuple -= index_qual_cost.per_tuple; cpu_per_tuple -= index_qual_cost.per_tuple;
} }
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.164 2004/01/05 23:39:54 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.165 2004/01/06 04:31:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -62,16 +62,16 @@ static HashJoin *create_hashjoin_plan(Query *root, HashPath *best_path, ...@@ -62,16 +62,16 @@ static HashJoin *create_hashjoin_plan(Query *root, HashPath *best_path,
Plan *outer_plan, Plan *inner_plan); Plan *outer_plan, Plan *inner_plan);
static void fix_indxqual_references(List *indexquals, IndexPath *index_path, static void fix_indxqual_references(List *indexquals, IndexPath *index_path,
List **fixed_indexquals, List **fixed_indexquals,
List **recheck_indexquals,
List **indxstrategy, List **indxstrategy,
List **indxsubtype); List **indxsubtype,
List **indxlossy);
static void fix_indxqual_sublist(List *indexqual, static void fix_indxqual_sublist(List *indexqual,
Relids baserelids, int baserelid, Relids baserelids, int baserelid,
IndexOptInfo *index, IndexOptInfo *index,
List **fixed_quals, List **fixed_quals,
List **recheck_quals,
List **strategy, List **strategy,
List **subtype); List **subtype,
List **lossy);
static Node *fix_indxqual_operand(Node *node, int baserelid, static Node *fix_indxqual_operand(Node *node, int baserelid,
IndexOptInfo *index, IndexOptInfo *index,
Oid *opclass); Oid *opclass);
...@@ -82,7 +82,7 @@ static void copy_plan_costsize(Plan *dest, Plan *src); ...@@ -82,7 +82,7 @@ static void copy_plan_costsize(Plan *dest, Plan *src);
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid); static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
List *indxid, List *indxqual, List *indxqualorig, List *indxid, List *indxqual, List *indxqualorig,
List *indxstrategy, List *indxsubtype, List *indxstrategy, List *indxsubtype, List *indxlossy,
ScanDirection indexscandir); ScanDirection indexscandir);
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
List *tideval); List *tideval);
...@@ -718,9 +718,9 @@ create_indexscan_plan(Query *root, ...@@ -718,9 +718,9 @@ create_indexscan_plan(Query *root,
Expr *indxqual_or_expr = NULL; Expr *indxqual_or_expr = NULL;
List *stripped_indxquals; List *stripped_indxquals;
List *fixed_indxquals; List *fixed_indxquals;
List *recheck_indxquals;
List *indxstrategy; List *indxstrategy;
List *indxsubtype; List *indxsubtype;
List *indxlossy;
FastList indexids; FastList indexids;
List *i; List *i;
IndexScan *scan_plan; IndexScan *scan_plan;
...@@ -735,8 +735,8 @@ create_indexscan_plan(Query *root, ...@@ -735,8 +735,8 @@ create_indexscan_plan(Query *root,
* value is just the rel's baserestrictinfo list). We must add these * value is just the rel's baserestrictinfo list). We must add these
* clauses to scan_clauses to ensure they get checked. In most cases * 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 * we will remove the join clauses again below, but if a join clause
* contains a lossy or special operator, we need to make sure it gets * contains a special operator, we need to make sure it gets into the
* into scan_clauses. * scan_clauses.
*/ */
if (best_path->isjoininner) if (best_path->isjoininner)
{ {
...@@ -780,22 +780,19 @@ create_indexscan_plan(Query *root, ...@@ -780,22 +780,19 @@ create_indexscan_plan(Query *root,
/* /*
* The qpqual list must contain all restrictions not automatically * The qpqual list must contain all restrictions not automatically
* handled by the index. Normally the predicates in the indexquals are * handled by the index. All the predicates in the indexquals will
* checked fully by the index, but if the index is "lossy" for a * be checked (either by the index itself, or by nodeIndexscan.c), but
* particular operator (as signaled by the amopreqcheck flag in * if there are any "special" operators involved then they must be
* pg_amop), then we need to double-check that predicate in qpqual, * added to qpqual. The upshot is that qpquals must contain scan_clauses
* because the index may return more tuples than match the predicate. * minus whatever appears in indxquals.
*
* Since the indexquals were generated from the restriction clauses given
* by scan_clauses, there will normally be duplications between the lists.
* We get rid of the duplicates, then add back if lossy.
*/ */
if (length(indxquals) > 1) if (length(indxquals) > 1)
{ {
/* /*
* Build an expression representation of the indexqual, expanding * Build an expression representation of the indexqual, expanding
* the implicit OR and AND semantics of the first- and * the implicit OR and AND semantics of the first- and
* second-level lists. * 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); indxqual_or_expr = make_expr_from_indexclauses(indxquals);
qpqual = set_difference(scan_clauses, makeList1(indxqual_or_expr)); qpqual = set_difference(scan_clauses, makeList1(indxqual_or_expr));
...@@ -814,34 +811,11 @@ create_indexscan_plan(Query *root, ...@@ -814,34 +811,11 @@ create_indexscan_plan(Query *root,
/* /*
* The executor needs a copy with the indexkey on the left of each * The executor needs a copy with the indexkey on the left of each
* clause and with index attr numbers substituted for table ones. This * clause and with index attr numbers substituted for table ones. This
* pass also looks for "lossy" operators. * pass also gets strategy info and looks for "lossy" operators.
*/ */
fix_indxqual_references(indxquals, best_path, fix_indxqual_references(indxquals, best_path,
&fixed_indxquals, &recheck_indxquals, &fixed_indxquals,
&indxstrategy, &indxsubtype); &indxstrategy, &indxsubtype, &indxlossy);
/*
* If there were any "lossy" operators, need to add back the
* appropriate qual clauses to the qpqual. When there is just one
* indexscan being performed (ie, we have simple AND semantics), we
* can just add the lossy clauses themselves to qpqual. If we have
* OR-of-ANDs, we'd better add the entire original indexquals to make
* sure that the semantics are correct.
*/
if (recheck_indxquals != NIL)
{
if (indxqual_or_expr)
{
/* Better do a deep copy of the original scanclauses */
qpqual = lappend(qpqual, copyObject(indxqual_or_expr));
}
else
{
/* Subroutine already copied quals, so just append to list */
Assert(length(recheck_indxquals) == 1);
qpqual = nconc(qpqual, (List *) lfirst(recheck_indxquals));
}
}
/* Finally ready to build the plan node */ /* Finally ready to build the plan node */
scan_plan = make_indexscan(tlist, scan_plan = make_indexscan(tlist,
...@@ -852,6 +826,7 @@ create_indexscan_plan(Query *root, ...@@ -852,6 +826,7 @@ create_indexscan_plan(Query *root,
stripped_indxquals, stripped_indxquals,
indxstrategy, indxstrategy,
indxsubtype, indxsubtype,
indxlossy,
best_path->indexscandir); best_path->indexscandir);
copy_path_costsize(&scan_plan->scan.plan, &best_path->path); copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
...@@ -1197,76 +1172,64 @@ create_hashjoin_plan(Query *root, ...@@ -1197,76 +1172,64 @@ create_hashjoin_plan(Query *root,
* Adjust indexqual clauses to the form the executor's indexqual * Adjust indexqual clauses to the form the executor's indexqual
* machinery needs, and check for recheckable (lossy) index conditions. * machinery needs, and check for recheckable (lossy) index conditions.
* *
* We have five tasks here: * We have four tasks here:
* * Remove RestrictInfo nodes from the input clauses. * * Remove RestrictInfo nodes from the input clauses.
* * Index keys must be represented by Var nodes with varattno set to the * * 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. * 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 * * 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.) * left. (Someday the executor might not need this, but for now it does.)
* * If the indexable operator is marked 'amopreqcheck' in pg_amop, then * * We must construct lists of operator strategy numbers, subtypes, and
* the index is "lossy" for this operator: it may return more tuples than * recheck (lossy-operator) flags for the top-level operators of each
* actually satisfy the operator condition. For each such operator, we * index clause.
* must add (the original form of) the indexqual clause to the "qpquals"
* of the indexscan node, where the operator will be re-evaluated to
* ensure it passes.
* * We must construct lists of operator strategy numbers and subtypes for
* the top-level operators of each index clause.
* *
* Both the input list and the output lists have the form of lists of sublists * Both the input list and the "fixed" output list have the form of lists of
* of qual clauses --- the top-level list has one entry for each indexscan * sublists of qual clauses --- the top-level list has one entry for each
* to be performed. The semantics are OR-of-ANDs. Note however that the * indexscan to be performed. The semantics are OR-of-ANDs. Note however
* input list contains RestrictInfos, while the output lists do not. * 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 indexqual list --- the
* original is not changed. Note also that the copy shares no substructure * 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 * 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). * two separate copies of the subplan tree, or things will go awry).
* *
* recheck_indexquals similarly receives a copy of whichever clauses
* need rechecking.
*
* indxstrategy receives a list of integer sublists of strategy numbers. * indxstrategy receives a list of integer sublists of strategy numbers.
* indxsubtype receives a list of OID sublists of strategy subtypes. * indxsubtype receives a list of OID sublists of strategy subtypes.
* indxlossy receives a list of integer sublists of lossy-operator booleans.
*/ */
static void static void
fix_indxqual_references(List *indexquals, IndexPath *index_path, fix_indxqual_references(List *indexquals, IndexPath *index_path,
List **fixed_indexquals, List **recheck_indexquals, List **fixed_indexquals,
List **indxstrategy, List **indxsubtype) List **indxstrategy,
List **indxsubtype,
List **indxlossy)
{ {
FastList fixed_quals;
FastList recheck_quals;
Relids baserelids = index_path->path.parent->relids; Relids baserelids = index_path->path.parent->relids;
int baserelid = index_path->path.parent->relid; int baserelid = index_path->path.parent->relid;
List *ixinfo = index_path->indexinfo; List *ixinfo = index_path->indexinfo;
List *i; List *i;
FastListInit(&fixed_quals); *fixed_indexquals = NIL;
FastListInit(&recheck_quals);
*indxstrategy = NIL; *indxstrategy = NIL;
*indxsubtype = NIL; *indxsubtype = NIL;
*indxlossy = NIL;
foreach(i, indexquals) foreach(i, indexquals)
{ {
List *indexqual = lfirst(i); List *indexqual = lfirst(i);
IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo); IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo);
List *fixed_qual; List *fixed_qual;
List *recheck_qual;
List *strategy; List *strategy;
List *subtype; List *subtype;
List *lossy;
fix_indxqual_sublist(indexqual, baserelids, baserelid, index, fix_indxqual_sublist(indexqual, baserelids, baserelid, index,
&fixed_qual, &recheck_qual, &fixed_qual, &strategy, &subtype, &lossy);
&strategy, &subtype); *fixed_indexquals = lappend(*fixed_indexquals, fixed_qual);
FastAppend(&fixed_quals, fixed_qual);
if (recheck_qual != NIL)
FastAppend(&recheck_quals, recheck_qual);
*indxstrategy = lappend(*indxstrategy, strategy); *indxstrategy = lappend(*indxstrategy, strategy);
*indxsubtype = lappend(*indxsubtype, subtype); *indxsubtype = lappend(*indxsubtype, subtype);
*indxlossy = lappend(*indxlossy, lossy);
ixinfo = lnext(ixinfo); ixinfo = lnext(ixinfo);
} }
*fixed_indexquals = FastListValue(&fixed_quals);
*recheck_indexquals = FastListValue(&recheck_quals);
} }
/* /*
...@@ -1274,34 +1237,30 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path, ...@@ -1274,34 +1237,30 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path,
* *
* For each qual clause, commute if needed to put the indexkey operand on the * 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 * left, and then fix its varattno. (We do not need to change the other side
* of the clause.) Also change the operator if necessary, check for * of the clause.) Then determine the operator's strategy number and subtype
* lossy index behavior, and determine the operator's strategy number and * number, and check for lossy index behavior.
* subtype number.
* *
* Returns four lists: * Returns four lists:
* the list of fixed indexquals * the list of fixed indexquals
* the list (usually empty) of original clauses that must be rechecked
* as qpquals because the index is lossy for this operator type
* the integer list of strategy numbers * the integer list of strategy numbers
* the OID list of strategy subtypes * the OID list of strategy subtypes
* the integer list of lossiness flags (1/0)
*/ */
static void static void
fix_indxqual_sublist(List *indexqual, fix_indxqual_sublist(List *indexqual,
Relids baserelids, int baserelid, Relids baserelids, int baserelid,
IndexOptInfo *index, IndexOptInfo *index,
List **fixed_quals, List **fixed_quals,
List **recheck_quals,
List **strategy, List **strategy,
List **subtype) List **subtype,
List **lossy)
{ {
FastList fixed_qual;
FastList recheck_qual;
List *i; List *i;
FastListInit(&fixed_qual); *fixed_quals = NIL;
FastListInit(&recheck_qual);
*strategy = NIL; *strategy = NIL;
*subtype = NIL; *subtype = NIL;
*lossy = NIL;
foreach(i, indexqual) foreach(i, indexqual)
{ {
RestrictInfo *rinfo = (RestrictInfo *) lfirst(i); RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
...@@ -1344,29 +1303,20 @@ fix_indxqual_sublist(List *indexqual, ...@@ -1344,29 +1303,20 @@ fix_indxqual_sublist(List *indexqual,
index, index,
&opclass); &opclass);
FastAppend(&fixed_qual, newclause); *fixed_quals = lappend(*fixed_quals, newclause);
/* /*
* Look up the operator in the operator class to get its strategy * Look up the (possibly commuted) operator in the operator class to
* numbers and the recheck indicator. This also double-checks that * get its strategy numbers and the recheck indicator. This also
* we found an operator matching the index. * double-checks that we found an operator matching the index.
*/ */
get_op_opclass_properties(newclause->opno, opclass, get_op_opclass_properties(newclause->opno, opclass,
&stratno, &stratsubtype, &recheck); &stratno, &stratsubtype, &recheck);
*strategy = lappendi(*strategy, stratno); *strategy = lappendi(*strategy, stratno);
*subtype = lappendo(*subtype, stratsubtype); *subtype = lappendo(*subtype, stratsubtype);
*lossy = lappendi(*lossy, (int) recheck);
/*
* If index is lossy for this operator, add (a copy of) original form
* of clause to recheck list.
*/
if (recheck)
FastAppend(&recheck_qual, copyObject((Node *) clause));
} }
*fixed_quals = FastListValue(&fixed_qual);
*recheck_quals = FastListValue(&recheck_qual);
} }
static Node * static Node *
...@@ -1612,6 +1562,7 @@ make_indexscan(List *qptlist, ...@@ -1612,6 +1562,7 @@ make_indexscan(List *qptlist,
List *indxqualorig, List *indxqualorig,
List *indxstrategy, List *indxstrategy,
List *indxsubtype, List *indxsubtype,
List *indxlossy,
ScanDirection indexscandir) ScanDirection indexscandir)
{ {
IndexScan *node = makeNode(IndexScan); IndexScan *node = makeNode(IndexScan);
...@@ -1628,6 +1579,7 @@ make_indexscan(List *qptlist, ...@@ -1628,6 +1579,7 @@ make_indexscan(List *qptlist,
node->indxqualorig = indxqualorig; node->indxqualorig = indxqualorig;
node->indxstrategy = indxstrategy; node->indxstrategy = indxstrategy;
node->indxsubtype = indxsubtype; node->indxsubtype = indxsubtype;
node->indxlossy = indxlossy;
node->indxorderdir = indexscandir; node->indxorderdir = indexscandir;
return node; return node;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.99 2003/11/29 19:51:50 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.100 2004/01/06 04:31:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -362,8 +362,8 @@ set_join_references(Join *join, List *rtable) ...@@ -362,8 +362,8 @@ set_join_references(Join *join, List *rtable)
/* /*
* We must fix the inner qpqual too, if it has join * We must fix the inner qpqual too, if it has join
* clauses (this could happen if the index is lossy: some * clauses (this could happen if special operators are
* indxquals may get rechecked as qpquals). * involved: some indxquals may get rechecked as qpquals).
*/ */
if (NumRelids((Node *) inner_plan->qual) > 1) if (NumRelids((Node *) inner_plan->qual) > 1)
inner_plan->qual = join_references(inner_plan->qual, inner_plan->qual = join_references(inner_plan->qual,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.109 2003/12/18 22:23:42 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.110 2004/01/06 04:31:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -776,6 +776,7 @@ typedef ScanState SeqScanState; ...@@ -776,6 +776,7 @@ typedef ScanState SeqScanState;
* RuntimeKeysReady true if runtime Skeys have been computed * RuntimeKeysReady true if runtime Skeys have been computed
* RelationDescs ptr to array of relation descriptors * RelationDescs ptr to array of relation descriptors
* ScanDescs ptr to array of scan 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 * DupHash hashtable for recognizing dups in multiple scan
* MaxHash max # entries we will allow in hashtable * MaxHash max # entries we will allow in hashtable
* ---------------- * ----------------
...@@ -795,6 +796,7 @@ typedef struct IndexScanState ...@@ -795,6 +796,7 @@ typedef struct IndexScanState
bool iss_RuntimeKeysReady; bool iss_RuntimeKeysReady;
RelationPtr iss_RelationDescs; RelationPtr iss_RelationDescs;
IndexScanDescPtr iss_ScanDescs; IndexScanDescPtr iss_ScanDescs;
List **iss_LossyQuals;
HTAB *iss_DupHash; HTAB *iss_DupHash;
long iss_MaxHash; long iss_MaxHash;
} IndexScanState; } IndexScanState;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.72 2003/11/29 22:41:06 pgsql Exp $ * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.73 2004/01/06 04:31:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -166,6 +166,7 @@ typedef struct IndexScan ...@@ -166,6 +166,7 @@ typedef struct IndexScan
List *indxqualorig; /* the same in original form */ List *indxqualorig; /* the same in original form */
List *indxstrategy; /* list of sublists of strategy numbers */ List *indxstrategy; /* list of sublists of strategy numbers */
List *indxsubtype; /* list of sublists of strategy subtypes */ 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 */ ScanDirection indxorderdir; /* forward or backward or don't care */
} IndexScan; } IndexScan;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment