|
|
|
@ -9,7 +9,7 @@
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* IDENTIFICATION
|
|
|
|
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.169 2005/03/02 04:10:53 tgl Exp $
|
|
|
|
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.170 2005/03/26 23:29:17 tgl Exp $
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
@ -50,6 +50,9 @@
|
|
|
|
|
#define is_indexable_operator(clause,opclass,indexkey_on_left) \
|
|
|
|
|
(indexable_operator(clause,opclass,indexkey_on_left) != InvalidOid)
|
|
|
|
|
|
|
|
|
|
#define IsBooleanOpclass(opclass) \
|
|
|
|
|
((opclass) == BOOL_BTREE_OPS_OID || (opclass) == BOOL_HASH_OPS_OID)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index);
|
|
|
|
|
static List *group_clauses_by_indexkey_for_join(Query *root,
|
|
|
|
@ -72,8 +75,16 @@ static Path *make_innerjoin_index_path(Query *root,
|
|
|
|
|
List *clausegroups);
|
|
|
|
|
static bool match_index_to_operand(Node *operand, int indexcol,
|
|
|
|
|
RelOptInfo *rel, IndexOptInfo *index);
|
|
|
|
|
static bool match_boolean_index_clause(Node *clause,
|
|
|
|
|
int indexcol,
|
|
|
|
|
RelOptInfo *rel,
|
|
|
|
|
IndexOptInfo *index);
|
|
|
|
|
static bool match_special_index_operator(Expr *clause, Oid opclass,
|
|
|
|
|
bool indexkey_on_left);
|
|
|
|
|
static Expr *expand_boolean_index_clause(Node *clause,
|
|
|
|
|
int indexcol,
|
|
|
|
|
RelOptInfo *rel,
|
|
|
|
|
IndexOptInfo *index);
|
|
|
|
|
static List *expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass);
|
|
|
|
|
static List *prefix_quals(Node *leftop, Oid opclass,
|
|
|
|
|
Const *prefix, Pattern_Prefix_Status pstatus);
|
|
|
|
@ -511,7 +522,7 @@ group_clauses_by_indexkey_for_or(RelOptInfo *rel,
|
|
|
|
|
* match_clause_to_indexcol()
|
|
|
|
|
* Determines whether a restriction clause matches a column of an index.
|
|
|
|
|
*
|
|
|
|
|
* To match, the clause:
|
|
|
|
|
* To match a normal index, the clause:
|
|
|
|
|
*
|
|
|
|
|
* (1) must be in the form (indexkey op const) or (const op indexkey);
|
|
|
|
|
* and
|
|
|
|
@ -525,6 +536,9 @@ group_clauses_by_indexkey_for_or(RelOptInfo *rel,
|
|
|
|
|
* We do not actually do the commuting here, but we check whether a
|
|
|
|
|
* suitable commutator operator is available.
|
|
|
|
|
*
|
|
|
|
|
* For boolean indexes, it is also possible to match the clause directly
|
|
|
|
|
* to the indexkey; or perhaps the clause is (NOT indexkey).
|
|
|
|
|
*
|
|
|
|
|
* 'rel' is the relation of interest.
|
|
|
|
|
* 'index' is an index on 'rel'.
|
|
|
|
|
* 'indexcol' is a column number of 'index' (counting from 0).
|
|
|
|
@ -547,7 +561,15 @@ match_clause_to_indexcol(RelOptInfo *rel,
|
|
|
|
|
Node *leftop,
|
|
|
|
|
*rightop;
|
|
|
|
|
|
|
|
|
|
/* Clause must be a binary opclause. */
|
|
|
|
|
/* First check for boolean-index cases. */
|
|
|
|
|
if (IsBooleanOpclass(opclass))
|
|
|
|
|
{
|
|
|
|
|
if (match_boolean_index_clause((Node *) clause,
|
|
|
|
|
indexcol, rel, index))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Else clause must be a binary opclause. */
|
|
|
|
|
if (!is_opclause(clause))
|
|
|
|
|
return false;
|
|
|
|
|
leftop = get_leftop(clause);
|
|
|
|
@ -606,6 +628,8 @@ match_clause_to_indexcol(RelOptInfo *rel,
|
|
|
|
|
* operator for this column, or is a "special" operator as recognized
|
|
|
|
|
* by match_special_index_operator().
|
|
|
|
|
*
|
|
|
|
|
* The boolean-index cases don't apply.
|
|
|
|
|
*
|
|
|
|
|
* As above, we must be able to commute the clause to put the indexkey
|
|
|
|
|
* on the left.
|
|
|
|
|
*
|
|
|
|
@ -1662,7 +1686,7 @@ make_innerjoin_index_path(Query *root,
|
|
|
|
|
pathnode->path.pathkeys = NIL;
|
|
|
|
|
|
|
|
|
|
/* Convert clauses to indexquals the executor can handle */
|
|
|
|
|
indexquals = expand_indexqual_conditions(index, clausegroups);
|
|
|
|
|
indexquals = expand_indexqual_conditions(rel, index, clausegroups);
|
|
|
|
|
|
|
|
|
|
/* Flatten the clausegroups list to produce indexclauses list */
|
|
|
|
|
allclauses = flatten_clausegroups_list(clausegroups);
|
|
|
|
@ -1868,21 +1892,78 @@ match_index_to_operand(Node *operand,
|
|
|
|
|
* from LIKE to indexscan limits rather harder than one might think ...
|
|
|
|
|
* but that's the basic idea.)
|
|
|
|
|
*
|
|
|
|
|
* Two routines are provided here, match_special_index_operator() and
|
|
|
|
|
* expand_indexqual_conditions(). match_special_index_operator() is
|
|
|
|
|
* just an auxiliary function for match_clause_to_indexcol(); after
|
|
|
|
|
* the latter fails to recognize a restriction opclause's operator
|
|
|
|
|
* as a member of an index's opclass, it asks match_special_index_operator()
|
|
|
|
|
* whether the clause should be considered an indexqual anyway.
|
|
|
|
|
* Another thing that we do with this machinery is to provide special
|
|
|
|
|
* smarts for "boolean" indexes (that is, indexes on boolean columns
|
|
|
|
|
* that support boolean equality). We can transform a plain reference
|
|
|
|
|
* to the indexkey into "indexkey = true", or "NOT indexkey" into
|
|
|
|
|
* "indexkey = false", so as to make the expression indexable using the
|
|
|
|
|
* regular index operators. (As of Postgres 8.1, we must do this here
|
|
|
|
|
* because constant simplification does the reverse transformation;
|
|
|
|
|
* without this code there'd be no way to use such an index at all.)
|
|
|
|
|
*
|
|
|
|
|
* Three routines are provided here:
|
|
|
|
|
*
|
|
|
|
|
* match_special_index_operator() is just an auxiliary function for
|
|
|
|
|
* match_clause_to_indexcol(); after the latter fails to recognize a
|
|
|
|
|
* restriction opclause's operator as a member of an index's opclass,
|
|
|
|
|
* it asks match_special_index_operator() whether the clause should be
|
|
|
|
|
* considered an indexqual anyway.
|
|
|
|
|
*
|
|
|
|
|
* match_boolean_index_clause() similarly detects clauses that can be
|
|
|
|
|
* converted into boolean equality operators.
|
|
|
|
|
*
|
|
|
|
|
* expand_indexqual_conditions() converts a list of lists of RestrictInfo
|
|
|
|
|
* nodes (with implicit AND semantics across list elements) into
|
|
|
|
|
* a list of clauses that the executor can actually handle. For operators
|
|
|
|
|
* that are members of the index's opclass this transformation is a no-op,
|
|
|
|
|
* but operators recognized by match_special_index_operator() must be
|
|
|
|
|
* converted into one or more "regular" indexqual conditions.
|
|
|
|
|
* but clauses recognized by match_special_index_operator() or
|
|
|
|
|
* match_boolean_index_clause() must be converted into one or more "regular"
|
|
|
|
|
* indexqual conditions.
|
|
|
|
|
*----------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* match_boolean_index_clause
|
|
|
|
|
* Recognize restriction clauses that can be matched to a boolean index.
|
|
|
|
|
*
|
|
|
|
|
* This should be called only when IsBooleanOpclass() recognizes the
|
|
|
|
|
* index's operator class. We check to see if the clause matches the
|
|
|
|
|
* index's key.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
match_boolean_index_clause(Node *clause,
|
|
|
|
|
int indexcol,
|
|
|
|
|
RelOptInfo *rel,
|
|
|
|
|
IndexOptInfo *index)
|
|
|
|
|
{
|
|
|
|
|
/* Direct match? */
|
|
|
|
|
if (match_index_to_operand(clause, indexcol, rel, index))
|
|
|
|
|
return true;
|
|
|
|
|
/* NOT clause? */
|
|
|
|
|
if (not_clause(clause))
|
|
|
|
|
{
|
|
|
|
|
if (match_index_to_operand((Node *) get_notclausearg((Expr *) clause),
|
|
|
|
|
indexcol, rel, index))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Since we only consider clauses at top level of WHERE, we can convert
|
|
|
|
|
* indexkey IS TRUE and indexkey IS FALSE to index searches as well.
|
|
|
|
|
* The different meaning for NULL isn't important.
|
|
|
|
|
*/
|
|
|
|
|
else if (clause && IsA(clause, BooleanTest))
|
|
|
|
|
{
|
|
|
|
|
BooleanTest *btest = (BooleanTest *) clause;
|
|
|
|
|
|
|
|
|
|
if (btest->booltesttype == IS_TRUE ||
|
|
|
|
|
btest->booltesttype == IS_FALSE)
|
|
|
|
|
if (match_index_to_operand((Node *) btest->arg,
|
|
|
|
|
indexcol, rel, index))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* match_special_index_operator
|
|
|
|
|
* Recognize restriction clauses that can be used to generate
|
|
|
|
@ -2042,9 +2123,9 @@ match_special_index_operator(Expr *clause, Oid opclass,
|
|
|
|
|
* expand_indexqual_conditions
|
|
|
|
|
* Given a list of sublists of RestrictInfo nodes, produce a flat list
|
|
|
|
|
* of index qual clauses. Standard qual clauses (those in the index's
|
|
|
|
|
* opclass) are passed through unchanged. "Special" index operators
|
|
|
|
|
* are expanded into clauses that the indexscan machinery will know
|
|
|
|
|
* what to do with.
|
|
|
|
|
* opclass) are passed through unchanged. Boolean clauses and "special"
|
|
|
|
|
* index operators are expanded into clauses that the indexscan machinery
|
|
|
|
|
* will know what to do with.
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
@ -2054,10 +2135,11 @@ match_special_index_operator(Expr *clause, Oid opclass,
|
|
|
|
|
* someday --- tgl 7/00)
|
|
|
|
|
*/
|
|
|
|
|
List *
|
|
|
|
|
expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
|
|
|
|
|
expand_indexqual_conditions(RelOptInfo *rel, IndexOptInfo *index, List *clausegroups)
|
|
|
|
|
{
|
|
|
|
|
List *resultquals = NIL;
|
|
|
|
|
ListCell *clausegroup_item;
|
|
|
|
|
int indexcol = 0;
|
|
|
|
|
Oid *classes = index->classlist;
|
|
|
|
|
|
|
|
|
|
if (clausegroups == NIL)
|
|
|
|
@ -2073,12 +2155,32 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
|
|
|
|
|
{
|
|
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
|
|
|
|
|
|
|
|
|
/* First check for boolean cases */
|
|
|
|
|
if (IsBooleanOpclass(curClass))
|
|
|
|
|
{
|
|
|
|
|
Expr *boolqual;
|
|
|
|
|
|
|
|
|
|
boolqual = expand_boolean_index_clause((Node *) rinfo->clause,
|
|
|
|
|
indexcol,
|
|
|
|
|
rel,
|
|
|
|
|
index);
|
|
|
|
|
if (boolqual)
|
|
|
|
|
{
|
|
|
|
|
resultquals = lappend(resultquals,
|
|
|
|
|
make_restrictinfo(boolqual,
|
|
|
|
|
true, true));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resultquals = list_concat(resultquals,
|
|
|
|
|
expand_indexqual_condition(rinfo,
|
|
|
|
|
curClass));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clausegroup_item = lnext(clausegroup_item);
|
|
|
|
|
|
|
|
|
|
indexcol++;
|
|
|
|
|
classes++;
|
|
|
|
|
} while (clausegroup_item != NULL && !DoneMatchingIndexKeys(classes));
|
|
|
|
|
|
|
|
|
@ -2087,8 +2189,70 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
|
|
|
|
|
return resultquals;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* expand_boolean_index_clause
|
|
|
|
|
* Convert a clause recognized by match_boolean_index_clause into
|
|
|
|
|
* a boolean equality operator clause.
|
|
|
|
|
*
|
|
|
|
|
* Returns NULL if the clause isn't a boolean index qual.
|
|
|
|
|
*/
|
|
|
|
|
static Expr *
|
|
|
|
|
expand_boolean_index_clause(Node *clause,
|
|
|
|
|
int indexcol,
|
|
|
|
|
RelOptInfo *rel,
|
|
|
|
|
IndexOptInfo *index)
|
|
|
|
|
{
|
|
|
|
|
/* Direct match? */
|
|
|
|
|
if (match_index_to_operand(clause, indexcol, rel, index))
|
|
|
|
|
{
|
|
|
|
|
/* convert to indexkey = TRUE */
|
|
|
|
|
return make_opclause(BooleanEqualOperator, BOOLOID, false,
|
|
|
|
|
(Expr *) clause,
|
|
|
|
|
(Expr *) makeBoolConst(true, false));
|
|
|
|
|
}
|
|
|
|
|
/* NOT clause? */
|
|
|
|
|
if (not_clause(clause))
|
|
|
|
|
{
|
|
|
|
|
Node *arg = (Node *) get_notclausearg((Expr *) clause);
|
|
|
|
|
|
|
|
|
|
/* It must have matched the indexkey */
|
|
|
|
|
Assert(match_index_to_operand(arg, indexcol, rel, index));
|
|
|
|
|
/* convert to indexkey = FALSE */
|
|
|
|
|
return make_opclause(BooleanEqualOperator, BOOLOID, false,
|
|
|
|
|
(Expr *) arg,
|
|
|
|
|
(Expr *) makeBoolConst(false, false));
|
|
|
|
|
}
|
|
|
|
|
if (clause && IsA(clause, BooleanTest))
|
|
|
|
|
{
|
|
|
|
|
BooleanTest *btest = (BooleanTest *) clause;
|
|
|
|
|
Node *arg = (Node *) btest->arg;
|
|
|
|
|
|
|
|
|
|
/* It must have matched the indexkey */
|
|
|
|
|
Assert(match_index_to_operand(arg, indexcol, rel, index));
|
|
|
|
|
if (btest->booltesttype == IS_TRUE)
|
|
|
|
|
{
|
|
|
|
|
/* convert to indexkey = TRUE */
|
|
|
|
|
return make_opclause(BooleanEqualOperator, BOOLOID, false,
|
|
|
|
|
(Expr *) arg,
|
|
|
|
|
(Expr *) makeBoolConst(true, false));
|
|
|
|
|
}
|
|
|
|
|
if (btest->booltesttype == IS_FALSE)
|
|
|
|
|
{
|
|
|
|
|
/* convert to indexkey = FALSE */
|
|
|
|
|
return make_opclause(BooleanEqualOperator, BOOLOID, false,
|
|
|
|
|
(Expr *) arg,
|
|
|
|
|
(Expr *) makeBoolConst(false, false));
|
|
|
|
|
}
|
|
|
|
|
/* Oops */
|
|
|
|
|
Assert(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* expand_indexqual_condition --- expand a single indexqual condition
|
|
|
|
|
* (other than a boolean-qual case)
|
|
|
|
|
*
|
|
|
|
|
* The input is a single RestrictInfo, the output a list of RestrictInfos
|
|
|
|
|
*/
|
|
|
|
@ -2096,7 +2260,6 @@ static List *
|
|
|
|
|
expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass)
|
|
|
|
|
{
|
|
|
|
|
Expr *clause = rinfo->clause;
|
|
|
|
|
|
|
|
|
|
/* we know these will succeed */
|
|
|
|
|
Node *leftop = get_leftop(clause);
|
|
|
|
|
Node *rightop = get_rightop(clause);
|
|
|
|
|