*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
ExprContext *econtext;
ScanDirection direction;
IndexScanDescPtr scanDescs;
+ List **lossyQuals;
IndexScanDesc scandesc;
+ List *lossyQual;
Index scanrelid;
HeapTuple tuple;
TupleTableSlot *slot;
direction = ForwardScanDirection;
}
scanDescs = node->iss_ScanDescs;
+ lossyQuals = node->iss_LossyQuals;
numIndices = node->iss_NumIndices;
econtext = node->ss.ps.ps_ExprContext;
slot = node->ss.ss_ScanTupleSlot;
while (indexNumber < numIndices)
{
scandesc = scanDescs[node->iss_IndexPtr];
+ lossyQual = lossyQuals[node->iss_IndexPtr];
+
while ((tuple = index_getnext(scandesc, direction)) != NULL)
{
/*
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.)
List *indxqual;
List *indxstrategy;
List *indxsubtype;
+ List *indxlossy;
List *indxid;
int i;
int numIndices;
int *numScanKeys;
RelationPtr indexDescs;
IndexScanDescPtr scanDescs;
+ List **lossyQuals;
ExprState ***runtimeKeyInfo;
bool have_runtime_keys;
RangeTblEntry *rtentry;
indexstate->iss_RuntimeKeysReady = false;
indexstate->iss_RelationDescs = NULL;
indexstate->iss_ScanDescs = NULL;
+ indexstate->iss_LossyQuals = NULL;
/*
* get the index node information
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)
indxqual = node->indxqual;
indxstrategy = node->indxstrategy;
indxsubtype = node->indxsubtype;
+ indxlossy = node->indxlossy;
for (i = 0; i < numIndices; i++)
{
List *quals;
List *strategies;
List *subtypes;
+ List *lossyflags;
int n_keys;
ScanKey scan_keys;
ExprState **run_keys;
indxstrategy = lnext(indxstrategy);
subtypes = lfirst(indxsubtype);
indxsubtype = lnext(indxsubtype);
+ lossyflags = lfirst(indxlossy);
+ indxlossy = lnext(indxlossy);
n_keys = length(quals);
scan_keys = (n_keys <= 0) ? (ScanKey) NULL :
(ScanKey) palloc(n_keys * sizeof(ScanKeyData));
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) */
strategies = lnext(strategies);
subtype = lfirsto(subtypes);
subtypes = lnext(subtypes);
+ lossy = lfirsti(lossyflags);
+ lossyflags = lnext(lossyflags);
if (!IsA(clause, OpExpr))
elog(ERROR, "indxqual is not an OpExpr");
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 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)));
+ }
}
/*
indexstate->iss_RelationDescs = indexDescs;
indexstate->iss_ScanDescs = scanDescs;
+ indexstate->iss_LossyQuals = lossyQuals;
/*
* Initialize result tuple type and projection info.
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
Plan *outer_plan, Plan *inner_plan);
static void fix_indxqual_references(List *indexquals, IndexPath *index_path,
List **fixed_indexquals,
- List **recheck_indexquals,
List **indxstrategy,
- List **indxsubtype);
+ List **indxsubtype,
+ List **indxlossy);
static void fix_indxqual_sublist(List *indexqual,
Relids baserelids, int baserelid,
IndexOptInfo *index,
List **fixed_quals,
- List **recheck_quals,
List **strategy,
- List **subtype);
+ List **subtype,
+ List **lossy);
static Node *fix_indxqual_operand(Node *node, int baserelid,
IndexOptInfo *index,
Oid *opclass);
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 *indxstrategy, List *indxsubtype, List *indxlossy,
ScanDirection indexscandir);
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
List *tideval);
Expr *indxqual_or_expr = NULL;
List *stripped_indxquals;
List *fixed_indxquals;
- List *recheck_indxquals;
List *indxstrategy;
List *indxsubtype;
+ List *indxlossy;
FastList indexids;
List *i;
IndexScan *scan_plan;
* 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 lossy or special operator, we need to make sure it gets
- * into scan_clauses.
+ * contains a special operator, we need to make sure it gets into the
+ * scan_clauses.
*/
if (best_path->isjoininner)
{
/*
* The qpqual list must contain all restrictions not automatically
- * handled by the index. Normally the predicates in the indexquals are
- * checked fully by the index, but if the index is "lossy" for a
- * particular operator (as signaled by the amopreqcheck flag in
- * pg_amop), then we need to double-check that predicate in qpqual,
- * because the index may return more tuples than match the predicate.
- *
- * 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.
+ * 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.
*/
if (length(indxquals) > 1)
{
/*
* Build an expression representation of the indexqual, expanding
* 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);
qpqual = set_difference(scan_clauses, makeList1(indxqual_or_expr));
/*
* 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 looks for "lossy" operators.
+ * pass also gets strategy info and looks for "lossy" operators.
*/
fix_indxqual_references(indxquals, best_path,
- &fixed_indxquals, &recheck_indxquals,
- &indxstrategy, &indxsubtype);
-
- /*
- * 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));
- }
- }
+ &fixed_indxquals,
+ &indxstrategy, &indxsubtype, &indxlossy);
/* Finally ready to build the plan node */
scan_plan = make_indexscan(tlist,
stripped_indxquals,
indxstrategy,
indxsubtype,
+ indxlossy,
best_path->indexscandir);
copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
* Adjust indexqual clauses to the form the executor's indexqual
* 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.
* * 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.)
- * * If the indexable operator is marked 'amopreqcheck' in pg_amop, then
- * the index is "lossy" for this operator: it may return more tuples than
- * actually satisfy the operator condition. For each such operator, we
- * 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.
+ * * We must construct lists of operator strategy numbers, subtypes, and
+ * recheck (lossy-operator) flags for the top-level operators of each
+ * index clause.
*
- * Both the input list and the output lists 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 lists do not.
+ * 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
* 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).
*
- * recheck_indexquals similarly receives a copy of whichever clauses
- * need rechecking.
- *
* indxstrategy receives a list of integer sublists of strategy numbers.
* indxsubtype receives a list of OID sublists of strategy subtypes.
+ * indxlossy receives a list of integer sublists of lossy-operator booleans.
*/
static void
fix_indxqual_references(List *indexquals, IndexPath *index_path,
- List **fixed_indexquals, List **recheck_indexquals,
- List **indxstrategy, List **indxsubtype)
+ List **fixed_indexquals,
+ List **indxstrategy,
+ List **indxsubtype,
+ List **indxlossy)
{
- FastList fixed_quals;
- FastList recheck_quals;
Relids baserelids = index_path->path.parent->relids;
int baserelid = index_path->path.parent->relid;
List *ixinfo = index_path->indexinfo;
List *i;
- FastListInit(&fixed_quals);
- FastListInit(&recheck_quals);
+ *fixed_indexquals = NIL;
*indxstrategy = NIL;
*indxsubtype = NIL;
+ *indxlossy = NIL;
foreach(i, indexquals)
{
List *indexqual = lfirst(i);
IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo);
List *fixed_qual;
- List *recheck_qual;
List *strategy;
List *subtype;
+ List *lossy;
fix_indxqual_sublist(indexqual, baserelids, baserelid, index,
- &fixed_qual, &recheck_qual,
- &strategy, &subtype);
- FastAppend(&fixed_quals, fixed_qual);
- if (recheck_qual != NIL)
- FastAppend(&recheck_quals, recheck_qual);
+ &fixed_qual, &strategy, &subtype, &lossy);
+ *fixed_indexquals = lappend(*fixed_indexquals, fixed_qual);
*indxstrategy = lappend(*indxstrategy, strategy);
*indxsubtype = lappend(*indxsubtype, subtype);
+ *indxlossy = lappend(*indxlossy, lossy);
ixinfo = lnext(ixinfo);
}
-
- *fixed_indexquals = FastListValue(&fixed_quals);
- *recheck_indexquals = FastListValue(&recheck_quals);
}
/*
*
* 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.) Also change the operator if necessary, check for
- * lossy index behavior, and determine the operator's strategy number and
- * subtype number.
+ * 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 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 OID list of strategy subtypes
+ * the integer list of lossiness flags (1/0)
*/
static void
fix_indxqual_sublist(List *indexqual,
Relids baserelids, int baserelid,
IndexOptInfo *index,
List **fixed_quals,
- List **recheck_quals,
List **strategy,
- List **subtype)
+ List **subtype,
+ List **lossy)
{
- FastList fixed_qual;
- FastList recheck_qual;
List *i;
- FastListInit(&fixed_qual);
- FastListInit(&recheck_qual);
+ *fixed_quals = NIL;
*strategy = NIL;
*subtype = NIL;
+ *lossy = NIL;
foreach(i, indexqual)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
index,
&opclass);
- FastAppend(&fixed_qual, newclause);
+ *fixed_quals = lappend(*fixed_quals, newclause);
/*
- * Look up the 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.
+ * Look up the (possibly commuted) operator in the operator class to
+ * get its strategy numbers and the recheck indicator. This also
+ * double-checks that we found an operator matching the index.
*/
get_op_opclass_properties(newclause->opno, opclass,
&stratno, &stratsubtype, &recheck);
*strategy = lappendi(*strategy, stratno);
*subtype = lappendo(*subtype, stratsubtype);
-
- /*
- * 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));
+ *lossy = lappendi(*lossy, (int) recheck);
}
-
- *fixed_quals = FastListValue(&fixed_qual);
- *recheck_quals = FastListValue(&recheck_qual);
}
static Node *
List *indxqualorig,
List *indxstrategy,
List *indxsubtype,
+ List *indxlossy,
ScanDirection indexscandir)
{
IndexScan *node = makeNode(IndexScan);
node->indxqualorig = indxqualorig;
node->indxstrategy = indxstrategy;
node->indxsubtype = indxsubtype;
+ node->indxlossy = indxlossy;
node->indxorderdir = indexscandir;
return node;