diff options
Diffstat (limited to 'src/backend/optimizer/plan/createplan.c')
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 148 |
1 files changed, 102 insertions, 46 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 78e22c7b9e3..9a32ae7a1b9 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.107 2001/06/05 05:26:04 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.108 2001/08/21 16:36:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -56,9 +56,12 @@ static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist, List *joinclauses, List *otherclauses, Plan *outer_plan, List *outer_tlist, Plan *inner_plan, List *inner_tlist); -static List *fix_indxqual_references(List *indexquals, IndexPath *index_path); -static List *fix_indxqual_sublist(List *indexqual, int baserelid, - IndexOptInfo *index); +static void fix_indxqual_references(List *indexquals, IndexPath *index_path, + List **fixed_indexquals, + List **recheck_indexquals); +static void fix_indxqual_sublist(List *indexqual, int baserelid, + IndexOptInfo *index, + List **fixed_quals, List **recheck_quals); static Node *fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index, Oid *opclass); @@ -381,11 +384,12 @@ create_indexscan_plan(Query *root, List *indxqual = best_path->indexqual; Index baserelid; List *qpqual; + Expr *indxqual_or_expr = NULL; List *fixed_indxqual; + List *recheck_indxqual; List *indexids; List *ixinfo; IndexScan *scan_plan; - bool lossy; /* there should be exactly one base rel involved... */ Assert(length(best_path->path.parent->relids) == 1); @@ -394,25 +398,23 @@ create_indexscan_plan(Query *root, baserelid = lfirsti(best_path->path.parent->relids); /* - * Build list of index OIDs, and check to see if any of the indices - * are lossy. + * Build list of index OIDs. */ indexids = NIL; - lossy = false; foreach(ixinfo, best_path->indexinfo) { IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo); indexids = lappendi(indexids, index->indexoid); - lossy |= index->lossy; } /* * The qpqual list must contain all restrictions not automatically - * handled by the index. Note that for non-lossy indices, the - * predicates in the indxqual are checked fully by the index, while - * for lossy indices the indxqual predicates need to be double-checked - * after the index fetches the best-guess tuples. + * handled by the index. Normally the predicates in the indxqual + * 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 some duplications between @@ -420,7 +422,6 @@ create_indexscan_plan(Query *root, */ if (length(indxqual) > 1) { - /* * Build an expression representation of the indexqual, expanding * the implicit OR and AND semantics of the first- and @@ -428,32 +429,24 @@ create_indexscan_plan(Query *root, */ List *orclauses = NIL; List *orclause; - Expr *indxqual_expr; foreach(orclause, indxqual) + { orclauses = lappend(orclauses, make_ands_explicit(lfirst(orclause))); - indxqual_expr = make_orclause(orclauses); - - qpqual = set_difference(scan_clauses, makeList1(indxqual_expr)); + } + indxqual_or_expr = make_orclause(orclauses); - if (lossy) - qpqual = lappend(qpqual, copyObject(indxqual_expr)); + qpqual = set_difference(scan_clauses, makeList1(indxqual_or_expr)); } else if (indxqual != NIL) { - /* * Here, we can simply treat the first sublist as an independent * set of qual expressions, since there is no top-level OR * behavior. */ - List *indxqual_list = lfirst(indxqual); - - qpqual = set_difference(scan_clauses, indxqual_list); - - if (lossy) - qpqual = nconc(qpqual, (List *) copyObject(indxqual_list)); + qpqual = set_difference(scan_clauses, lfirst(indxqual)); } else qpqual = scan_clauses; @@ -461,9 +454,35 @@ create_indexscan_plan(Query *root, /* * 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. */ - fixed_indxqual = fix_indxqual_references(indxqual, best_path); + fix_indxqual_references(indxqual, best_path, + &fixed_indxqual, &recheck_indxqual); + /* + * 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 indexqual to make sure that the + * semantics are correct. + */ + if (recheck_indxqual != 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_indxqual) == 1); + qpqual = nconc(qpqual, (List *) lfirst(recheck_indxqual)); + } + } + + /* Finally ready to build the plan node */ scan_plan = make_indexscan(tlist, qpqual, baserelid, @@ -868,9 +887,9 @@ create_hashjoin_plan(HashPath *best_path, /* * fix_indxqual_references * Adjust indexqual clauses to the form the executor's indexqual - * machinery needs. + * machinery needs, and check for recheckable (lossy) index conditions. * - * We have three tasks here: + * We have four tasks here: * * 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. * * indxpath.c may have selected an index that is binary-compatible with @@ -879,20 +898,34 @@ create_hashjoin_plan(HashPath *best_path, * equivalent operator that the index will recognize. * * 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. * * This code used to be entirely bogus for multi-index scans. Now it keeps * track of which index applies to each subgroup of index qual clauses... * - * Returns 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 + * 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. + * + * 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 full copy of whichever clauses + * need rechecking. */ - -static List * -fix_indxqual_references(List *indexquals, IndexPath *index_path) +static void +fix_indxqual_references(List *indexquals, IndexPath *index_path, + List **fixed_indexquals, List **recheck_indexquals) { List *fixed_quals = NIL; + List *recheck_quals = NIL; int baserelid = lfirsti(index_path->path.parent->relids); List *ixinfo = index_path->indexinfo; List *i; @@ -901,14 +934,20 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path) { List *indexqual = lfirst(i); IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo); + List *fixed_qual; + List *recheck_qual; + + fix_indxqual_sublist(indexqual, baserelid, index, + &fixed_qual, &recheck_qual); + fixed_quals = lappend(fixed_quals, fixed_qual); + if (recheck_qual != NIL) + recheck_quals = lappend(recheck_quals, recheck_qual); - fixed_quals = lappend(fixed_quals, - fix_indxqual_sublist(indexqual, - baserelid, - index)); ixinfo = lnext(ixinfo); } - return fixed_quals; + + *fixed_indexquals = fixed_quals; + *recheck_indexquals = recheck_quals; } /* @@ -916,12 +955,19 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path) * * 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. + * of the clause.) Also change the operator if necessary, and check for + * lossy index behavior. + * + * Returns two lists: the list of fixed indexquals, and the list (usually + * empty) of original clauses that must be rechecked as qpquals because + * the index is lossy for this operator type. */ -static List * -fix_indxqual_sublist(List *indexqual, int baserelid, IndexOptInfo *index) +static void +fix_indxqual_sublist(List *indexqual, int baserelid, IndexOptInfo *index, + List **fixed_quals, List **recheck_quals) { List *fixed_qual = NIL; + List *recheck_qual = NIL; List *i; foreach(i, indexqual) @@ -968,14 +1014,24 @@ fix_indxqual_sublist(List *indexqual, int baserelid, IndexOptInfo *index) * is merely binary-compatible with the index. This shouldn't * fail, since indxpath.c found it before... */ - newopno = indexable_operator(newclause, opclass, index->relam, true); + newopno = indexable_operator(newclause, opclass, true); if (newopno == InvalidOid) elog(ERROR, "fix_indxqual_sublist: failed to find substitute op"); ((Oper *) newclause->oper)->opno = newopno; fixed_qual = lappend(fixed_qual, newclause); + + /* + * Finally, check to see if index is lossy for this operator. + * If so, add (a copy of) original form of clause to recheck list. + */ + if (op_requires_recheck(newopno, opclass)) + recheck_qual = lappend(recheck_qual, + copyObject((Node *) clause)); } - return fixed_qual; + + *fixed_quals = fixed_qual; + *recheck_quals = recheck_qual; } static Node * |