1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-14 08:21:07 +03:00

Restructure pg_opclass, pg_amop, and pg_amproc per previous discussions in

pgsql-hackers.  pg_opclass now has a row for each opclass supported by each
index AM, not a row for each opclass name.  This allows pg_opclass to show
directly whether an AM supports an opclass, and furthermore makes it possible
to store additional information about an opclass that might be AM-dependent.
pg_opclass and pg_amop now store "lossy" and "haskeytype" information that we
previously expected the user to remember to provide in CREATE INDEX commands.
Lossiness is no longer an index-level property, but is associated with the
use of a particular operator in a particular index opclass.

Along the way, IndexSupportInitialize now uses the syscaches to retrieve
pg_amop and pg_amproc entries.  I find this reduces backend launch time by
about ten percent, at the cost of a couple more special cases in catcache.c's
IndexScanOK.

Initial work by Oleg Bartunov and Teodor Sigaev, further hacking by Tom Lane.

initdb forced.
This commit is contained in:
Tom Lane
2001-08-21 16:36:06 +00:00
parent c2d1566912
commit f933766ba7
60 changed files with 1918 additions and 1929 deletions

View File

@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.77 2001/06/11 00:17:08 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.78 2001/08/21 16:36:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -339,16 +339,16 @@ cost_index(Path *path, Query *root,
*
* Normally the indexquals will be removed from the list of
* restriction clauses that we have to evaluate as qpquals, so we
* should subtract their costs from baserestrictcost. For a lossy
* index, however, we will have to recheck all the quals and so
* mustn't subtract anything. Also, if we are doing a join then some
* of the indexquals are join clauses and shouldn't be subtracted.
* Rather than work out exactly how much to subtract, we don't
* subtract anything in that case either.
* should subtract their costs from baserestrictcost. 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. Also, if we are doing a join then some of the indexquals
* are join clauses and shouldn't be subtracted. Rather than work out
* exactly how much to subtract, we don't subtract anything.
*/
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
if (!index->lossy && !is_injoin)
if (!is_injoin)
cpu_per_tuple -= cost_qual_eval(indexQuals);
run_cost += cpu_per_tuple * tuples_fetched;

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.110 2001/08/06 18:09:45 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.111 2001/08/21 16:36:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -46,16 +46,16 @@
*
* Determine whether we should continue matching index keys in a clause.
* Depends on if there are more to match or if this is a functional index.
* In the latter case we stop after the first match since the there can
* be only key (i.e. the function's return value) and the attributes in
* In the latter case we stop after the first match since there can
* be only 1 key (i.e. the function's return value) and the attributes in
* keys list represent the arguments to the function. -mer 3 Oct. 1991
*/
#define DoneMatchingIndexKeys(indexkeys, index) \
(indexkeys[0] == 0 || \
(index->indproc != InvalidOid))
#define is_indexable_operator(clause,opclass,relam,indexkey_on_left) \
(indexable_operator(clause,opclass,relam,indexkey_on_left) != InvalidOid)
#define is_indexable_operator(clause,opclass,indexkey_on_left) \
(indexable_operator(clause,opclass,indexkey_on_left) != InvalidOid)
static void match_index_orclauses(RelOptInfo *rel, IndexOptInfo *index,
@ -92,7 +92,7 @@ static bool match_index_to_operand(int indexkey, Var *operand,
RelOptInfo *rel, IndexOptInfo *index);
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel,
IndexOptInfo *index);
static bool match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
static bool match_special_index_operator(Expr *clause, Oid opclass,
bool indexkey_on_left);
static List *prefix_quals(Var *leftop, Oid expr_op,
char *prefix, Pattern_Prefix_Status pstatus);
@ -754,30 +754,28 @@ match_clause_to_indexkey(RelOptInfo *rel,
if (match_index_to_operand(indexkey, leftop, rel, index) &&
is_pseudo_constant_clause((Node *) rightop))
{
if (is_indexable_operator(clause, opclass, index->relam, true))
if (is_indexable_operator(clause, opclass, true))
return true;
/*
* If we didn't find a member of the index's opclass, see
* whether it is a "special" indexable operator.
*/
if (match_special_index_operator(clause, opclass, index->relam,
true))
if (match_special_index_operator(clause, opclass, true))
return true;
return false;
}
if (match_index_to_operand(indexkey, rightop, rel, index) &&
is_pseudo_constant_clause((Node *) leftop))
{
if (is_indexable_operator(clause, opclass, index->relam, false))
if (is_indexable_operator(clause, opclass, false))
return true;
/*
* If we didn't find a member of the index's opclass, see
* whether it is a "special" indexable operator.
*/
if (match_special_index_operator(clause, opclass, index->relam,
false))
if (match_special_index_operator(clause, opclass, false))
return true;
return false;
}
@ -799,7 +797,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
isIndexable =
!intMember(lfirsti(rel->relids), othervarnos) &&
!contain_noncachable_functions((Node *) rightop) &&
is_indexable_operator(clause, opclass, index->relam, true);
is_indexable_operator(clause, opclass, true);
freeList(othervarnos);
return isIndexable;
}
@ -811,7 +809,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
isIndexable =
!intMember(lfirsti(rel->relids), othervarnos) &&
!contain_noncachable_functions((Node *) leftop) &&
is_indexable_operator(clause, opclass, index->relam, false);
is_indexable_operator(clause, opclass, false);
freeList(othervarnos);
return isIndexable;
}
@ -822,12 +820,11 @@ match_clause_to_indexkey(RelOptInfo *rel,
/*
* indexable_operator
* Does a binary opclause contain an operator matching the index's
* access method?
* Does a binary opclause contain an operator matching the index opclass?
*
* If the indexkey is on the right, what we actually want to know
* is whether the operator has a commutator operator that matches
* the index's access method.
* the index's opclass.
*
* We try both the straightforward match and matches that rely on
* recognizing binary-compatible datatypes. For example, if we have
@ -844,8 +841,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
* OID is *not* commuted; it can be plugged directly into the given clause.
*/
Oid
indexable_operator(Expr *clause, Oid opclass, Oid relam,
bool indexkey_on_left)
indexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left)
{
Oid expr_op = ((Oper *) clause->oper)->opno;
Oid commuted_op,
@ -865,8 +861,8 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
if (commuted_op == InvalidOid)
return InvalidOid;
/* Done if the (commuted) operator is a member of the index's AM */
if (op_class(commuted_op, opclass, relam))
/* Done if the (commuted) operator is a member of the index's opclass */
if (op_in_opclass(commuted_op, opclass))
return expr_op;
/*
@ -937,7 +933,7 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
if (commuted_op == InvalidOid)
return InvalidOid;
if (op_class(commuted_op, opclass, relam))
if (op_in_opclass(commuted_op, opclass))
return new_op;
}
}
@ -1171,8 +1167,8 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
Oid pred_op,
clause_op,
test_op;
Oid opclass_id;
StrategyNumber pred_strategy,
Oid opclass_id = InvalidOid;
StrategyNumber pred_strategy = 0,
clause_strategy,
test_strategy;
Oper *test_oper;
@ -1182,7 +1178,7 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
Relation relation;
HeapScanDesc scan;
HeapTuple tuple;
ScanKeyData entry[3];
ScanKeyData entry[1];
Form_pg_amop aform;
ExprContext *econtext;
@ -1227,23 +1223,6 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
/*
* 1. Find a "btree" strategy number for the pred_op
*
* XXX consider using syscache lookups for these searches. Right
* now we don't have caches that match all of the search conditions,
* but reconsider it after upcoming restructuring of pg_opclass.
*/
relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
ScanKeyEntryInitialize(&entry[0], 0,
Anum_pg_amop_amopid,
F_OIDEQ,
ObjectIdGetDatum(BTREE_AM_OID));
ScanKeyEntryInitialize(&entry[1], 0,
Anum_pg_amop_amopopr,
F_OIDEQ,
ObjectIdGetDatum(pred_op));
/*
* The following assumes that any given operator will only be in a
* single btree operator class. This is true at least for all the
* pre-defined operator classes. If it isn't true, then whichever
@ -1251,46 +1230,47 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
* will be used to find the associated strategy numbers for the test.
* --Nels, Jan '93
*/
scan = heap_beginscan(relation, false, SnapshotNow, 2, entry);
tuple = heap_getnext(scan, 0);
if (!HeapTupleIsValid(tuple))
ScanKeyEntryInitialize(&entry[0], 0x0,
Anum_pg_amop_amopopr,
F_OIDEQ,
ObjectIdGetDatum(pred_op));
relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
scan = heap_beginscan(relation, false, SnapshotNow, 1, entry);
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{
/* predicate operator isn't btree-indexable */
heap_endscan(scan);
heap_close(relation, AccessShareLock);
return false;
aform = (Form_pg_amop) GETSTRUCT(tuple);
if (opclass_is_btree(aform->amopclaid))
{
/* Get the predicate operator's btree strategy number (1 to 5) */
pred_strategy = (StrategyNumber) aform->amopstrategy;
Assert(pred_strategy >= 1 && pred_strategy <= 5);
/* Remember which operator class this strategy number came from */
opclass_id = aform->amopclaid;
break;
}
}
aform = (Form_pg_amop) GETSTRUCT(tuple);
/* Get the predicate operator's btree strategy number (1 to 5) */
pred_strategy = (StrategyNumber) aform->amopstrategy;
Assert(pred_strategy >= 1 && pred_strategy <= 5);
/* Remember which operator class this strategy number came from */
opclass_id = aform->amopclaid;
heap_endscan(scan);
heap_close(relation, AccessShareLock);
if (!OidIsValid(opclass_id))
{
/* predicate operator isn't btree-indexable */
return false;
}
/*
* 2. From the same opclass, find a strategy num for the clause_op
*/
ScanKeyEntryInitialize(&entry[1], 0,
Anum_pg_amop_amopclaid,
F_OIDEQ,
ObjectIdGetDatum(opclass_id));
ScanKeyEntryInitialize(&entry[2], 0,
Anum_pg_amop_amopopr,
F_OIDEQ,
ObjectIdGetDatum(clause_op));
scan = heap_beginscan(relation, false, SnapshotNow, 3, entry);
tuple = heap_getnext(scan, 0);
tuple = SearchSysCache(AMOPOPID,
ObjectIdGetDatum(opclass_id),
ObjectIdGetDatum(clause_op),
0, 0);
if (!HeapTupleIsValid(tuple))
{
/* clause operator isn't btree-indexable, or isn't in this opclass */
heap_endscan(scan);
heap_close(relation, AccessShareLock);
return false;
}
aform = (Form_pg_amop) GETSTRUCT(tuple);
@ -1299,7 +1279,7 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
clause_strategy = (StrategyNumber) aform->amopstrategy;
Assert(clause_strategy >= 1 && clause_strategy <= 5);
heap_endscan(scan);
ReleaseSysCache(tuple);
/*
* 3. Look up the "test" strategy number in the implication table
@ -1307,26 +1287,20 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
if (test_strategy == 0)
{
heap_close(relation, AccessShareLock);
return false; /* the implication cannot be determined */
}
/*
* 4. From the same opclass, find the operator for the test strategy
*/
ScanKeyEntryInitialize(&entry[2], 0,
Anum_pg_amop_amopstrategy,
F_INT2EQ,
Int16GetDatum(test_strategy));
scan = heap_beginscan(relation, false, SnapshotNow, 3, entry);
tuple = heap_getnext(scan, 0);
tuple = SearchSysCache(AMOPSTRATEGY,
ObjectIdGetDatum(opclass_id),
Int16GetDatum(test_strategy),
0, 0);
if (!HeapTupleIsValid(tuple))
{
/* this probably shouldn't fail? */
elog(DEBUG, "pred_test_simple_clause: unknown test_op");
heap_endscan(scan);
heap_close(relation, AccessShareLock);
return false;
}
aform = (Form_pg_amop) GETSTRUCT(tuple);
@ -1334,9 +1308,7 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
/* Get the test operator */
test_op = aform->amopopr;
heap_endscan(scan);
heap_close(relation, AccessShareLock);
ReleaseSysCache(tuple);
/*
* 5. Evaluate the test
@ -1681,7 +1653,7 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
* Return 'true' if we can do something with it anyway.
*/
static bool
match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
match_special_index_operator(Expr *clause, Oid opclass,
bool indexkey_on_left)
{
bool isIndexable = false;
@ -1806,8 +1778,8 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
case OID_TEXT_ICLIKE_OP:
case OID_TEXT_REGEXEQ_OP:
case OID_TEXT_ICREGEXEQ_OP:
if (!op_class(find_operator(">=", TEXTOID), opclass, relam) ||
!op_class(find_operator("<", TEXTOID), opclass, relam))
if (!op_in_opclass(find_operator(">=", TEXTOID), opclass) ||
!op_in_opclass(find_operator("<", TEXTOID), opclass))
isIndexable = false;
break;
@ -1815,8 +1787,8 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
case OID_BPCHAR_ICLIKE_OP:
case OID_BPCHAR_REGEXEQ_OP:
case OID_BPCHAR_ICREGEXEQ_OP:
if (!op_class(find_operator(">=", BPCHAROID), opclass, relam) ||
!op_class(find_operator("<", BPCHAROID), opclass, relam))
if (!op_in_opclass(find_operator(">=", BPCHAROID), opclass) ||
!op_in_opclass(find_operator("<", BPCHAROID), opclass))
isIndexable = false;
break;
@ -1824,8 +1796,8 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
case OID_VARCHAR_ICLIKE_OP:
case OID_VARCHAR_REGEXEQ_OP:
case OID_VARCHAR_ICREGEXEQ_OP:
if (!op_class(find_operator(">=", VARCHAROID), opclass, relam) ||
!op_class(find_operator("<", VARCHAROID), opclass, relam))
if (!op_in_opclass(find_operator(">=", VARCHAROID), opclass) ||
!op_in_opclass(find_operator("<", VARCHAROID), opclass))
isIndexable = false;
break;
@ -1833,24 +1805,24 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
case OID_NAME_ICLIKE_OP:
case OID_NAME_REGEXEQ_OP:
case OID_NAME_ICREGEXEQ_OP:
if (!op_class(find_operator(">=", NAMEOID), opclass, relam) ||
!op_class(find_operator("<", NAMEOID), opclass, relam))
if (!op_in_opclass(find_operator(">=", NAMEOID), opclass) ||
!op_in_opclass(find_operator("<", NAMEOID), opclass))
isIndexable = false;
break;
case OID_INET_SUB_OP:
case OID_INET_SUBEQ_OP:
/* for SUB we actually need ">" not ">=", but this should do */
if (!op_class(find_operator(">=", INETOID), opclass, relam) ||
!op_class(find_operator("<=", INETOID), opclass, relam))
if (!op_in_opclass(find_operator(">=", INETOID), opclass) ||
!op_in_opclass(find_operator("<=", INETOID), opclass))
isIndexable = false;
break;
case OID_CIDR_SUB_OP:
case OID_CIDR_SUBEQ_OP:
/* for SUB we actually need ">" not ">=", but this should do */
if (!op_class(find_operator(">=", CIDROID), opclass, relam) ||
!op_class(find_operator("<=", CIDROID), opclass, relam))
if (!op_in_opclass(find_operator(">=", CIDROID), opclass) ||
!op_in_opclass(find_operator("<=", CIDROID), opclass))
isIndexable = false;
break;
}

View File

@ -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);
}
indxqual_or_expr = make_orclause(orclauses);
qpqual = set_difference(scan_clauses, makeList1(indxqual_expr));
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 *

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.67 2001/07/15 22:48:18 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.68 2001/08/21 16:36:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -103,8 +103,7 @@ find_secondary_indexes(Oid relationObjectId)
IndexOptInfo *info;
int i;
Relation indexRelation;
Oid relam;
uint16 amorderstrategy;
int16 amorderstrategy;
indexTuple = SearchSysCache(INDEXRELID,
ObjectIdGetDatum(indexoid),
@ -138,7 +137,6 @@ find_secondary_indexes(Oid relationObjectId)
else
info->indpred = NIL;
info->unique = index->indisunique;
info->lossy = index->indislossy;
for (i = 0; i < INDEX_MAX_KEYS; i++)
{
@ -160,8 +158,7 @@ find_secondary_indexes(Oid relationObjectId)
/* Extract info from the relation descriptor for the index */
indexRelation = index_open(index->indexrelid);
relam = indexRelation->rd_rel->relam;
info->relam = relam;
info->relam = indexRelation->rd_rel->relam;
info->pages = indexRelation->rd_rel->relpages;
info->tuples = indexRelation->rd_rel->reltuples;
info->amcostestimate = index_cost_estimator(indexRelation);
@ -181,14 +178,12 @@ find_secondary_indexes(Oid relationObjectId)
amopTuple =
SearchSysCache(AMOPSTRATEGY,
ObjectIdGetDatum(relam),
ObjectIdGetDatum(index->indclass[i]),
UInt16GetDatum(amorderstrategy),
0);
Int16GetDatum(amorderstrategy),
0, 0);
if (!HeapTupleIsValid(amopTuple))
elog(ERROR, "find_secondary_indexes: no amop %u %u %d",
relam, index->indclass[i],
(int) amorderstrategy);
elog(ERROR, "find_secondary_indexes: no amop %u %d",
index->indclass[i], (int) amorderstrategy);
amop = (Form_pg_amop) GETSTRUCT(amopTuple);
info->ordering[i] = amop->amopopr;
ReleaseSysCache(amopTuple);
@ -370,7 +365,7 @@ has_unique_index(RelOptInfo *rel, AttrNumber attno)
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
/*
* Note: ignore functional, partial, or lossy indexes, since they
* Note: ignore functional and partial indexes, since they
* don't allow us to conclude that all attr values are distinct.
* Also, a multicolumn unique index doesn't allow us to conclude
* that just the specified attr is unique.
@ -379,8 +374,7 @@ has_unique_index(RelOptInfo *rel, AttrNumber attno)
index->nkeys == 1 &&
index->indexkeys[0] == attno &&
index->indproc == InvalidOid &&
index->indpred == NIL &&
!index->lossy)
index->indpred == NIL)
return true;
}
return false;