mirror of
https://github.com/postgres/postgres.git
synced 2025-04-29 13:56:47 +03:00
Make some improvements in the intelligence of the partial-index
predicate tester. It can now deal with commuted clauses (for instance, 4 < x implies x > 3), subclauses more complicated than a simple Var (for example, upper(x) = 't' implies upper(x) > 'a'), and <> operators (for example, x < 3 implies x <> 4). Still only understands operators associated with btree opclasses, though. Inspired by example from Martin Hampl.
This commit is contained in:
parent
504983859d
commit
cad5f4a8c4
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.155 2004/01/05 23:39:54 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.156 2004/01/07 22:02:48 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -918,13 +918,18 @@ pred_test_recurse_pred(Expr *predicate, Node *clause)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Define an "operator implication table" for btree operators ("strategies").
|
* Define an "operator implication table" for btree operators ("strategies").
|
||||||
* The "strategy numbers" are: (1) < (2) <= (3) = (4) >= (5) >
|
*
|
||||||
|
* The strategy numbers defined by btree indexes (see access/skey.h) are:
|
||||||
|
* (1) < (2) <= (3) = (4) >= (5) >
|
||||||
|
* and in addition we use (6) to represent <>. <> is not a btree-indexable
|
||||||
|
* operator, but we assume here that if the equality operator of a btree
|
||||||
|
* opclass has a negator operator, the negator behaves as <> for the opclass.
|
||||||
*
|
*
|
||||||
* The interpretation of:
|
* The interpretation of:
|
||||||
*
|
*
|
||||||
* test_op = BT_implic_table[given_op-1][target_op-1]
|
* test_op = BT_implic_table[given_op-1][target_op-1]
|
||||||
*
|
*
|
||||||
* where test_op, given_op and target_op are strategy numbers (from 1 to 5)
|
* where test_op, given_op and target_op are strategy numbers (from 1 to 6)
|
||||||
* of btree operators, is as follows:
|
* of btree operators, is as follows:
|
||||||
*
|
*
|
||||||
* If you know, for some ATTR, that "ATTR given_op CONST1" is true, and you
|
* If you know, for some ATTR, that "ATTR given_op CONST1" is true, and you
|
||||||
@ -933,17 +938,30 @@ pred_test_recurse_pred(Expr *predicate, Node *clause)
|
|||||||
* then the target expression must be true; if the test returns false, then
|
* then the target expression must be true; if the test returns false, then
|
||||||
* the target expression may be false.
|
* the target expression may be false.
|
||||||
*
|
*
|
||||||
* An entry where test_op==0 means the implication cannot be determined, i.e.,
|
* An entry where test_op == 0 means the implication cannot be determined,
|
||||||
* this test should always be considered false.
|
* i.e., this test should always be considered false.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define BTLT BTLessStrategyNumber
|
||||||
|
#define BTLE BTLessEqualStrategyNumber
|
||||||
|
#define BTEQ BTEqualStrategyNumber
|
||||||
|
#define BTGE BTGreaterEqualStrategyNumber
|
||||||
|
#define BTGT BTGreaterStrategyNumber
|
||||||
|
#define BTNE 6
|
||||||
|
|
||||||
static const StrategyNumber
|
static const StrategyNumber
|
||||||
BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = {
|
BT_implic_table[6][6] = {
|
||||||
{4, 4, 0, 0, 0},
|
/*
|
||||||
{5, 4, 0, 0, 0},
|
* The target operator:
|
||||||
{5, 4, 3, 2, 1},
|
*
|
||||||
{0, 0, 0, 2, 1},
|
* LT LE EQ GE GT NE
|
||||||
{0, 0, 0, 2, 2}
|
*/
|
||||||
|
{BTGE, BTGE, 0, 0, 0, BTGE}, /* LT */
|
||||||
|
{BTGT, BTGE, 0, 0, 0, BTGT}, /* LE */
|
||||||
|
{BTGT, BTGE, BTEQ, BTLE, BTLT, BTNE}, /* EQ */
|
||||||
|
{ 0, 0, 0, BTLE, BTLT, BTLT}, /* GE */
|
||||||
|
{ 0, 0, 0, BTLE, BTLE, BTLE}, /* GT */
|
||||||
|
{ 0, 0, 0, 0, 0, BTEQ} /* NE */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -969,12 +987,19 @@ static const StrategyNumber
|
|||||||
static bool
|
static bool
|
||||||
pred_test_simple_clause(Expr *predicate, Node *clause)
|
pred_test_simple_clause(Expr *predicate, Node *clause)
|
||||||
{
|
{
|
||||||
Var *pred_var,
|
Node *leftop,
|
||||||
|
*rightop;
|
||||||
|
Node *pred_var,
|
||||||
*clause_var;
|
*clause_var;
|
||||||
Const *pred_const,
|
Const *pred_const,
|
||||||
*clause_const;
|
*clause_const;
|
||||||
|
bool pred_var_on_left,
|
||||||
|
clause_var_on_left,
|
||||||
|
pred_op_negated;
|
||||||
Oid pred_op,
|
Oid pred_op,
|
||||||
clause_op,
|
clause_op,
|
||||||
|
pred_op_negator,
|
||||||
|
clause_op_negator,
|
||||||
test_op = InvalidOid;
|
test_op = InvalidOid;
|
||||||
Oid opclass_id;
|
Oid opclass_id;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
@ -997,40 +1022,89 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Can't do anything more unless they are both binary opclauses with a
|
* Can't do anything more unless they are both binary opclauses with a
|
||||||
* Var on the left and a Const on the right. (XXX someday try to
|
* Const on one side, and identical subexpressions on the other sides.
|
||||||
* commute Const/Var cases?) Note we don't have to think about binary
|
* Note we don't have to think about binary relabeling of the Const node,
|
||||||
* relabeling of the Const node, since that would have been folded right
|
* since that would have been folded right into the Const.
|
||||||
* into the Const.
|
*
|
||||||
|
* If either Const is null, we also fail right away; this assumes that
|
||||||
|
* the test operator will always be strict.
|
||||||
*/
|
*/
|
||||||
if (!is_opclause(predicate))
|
if (!is_opclause(predicate))
|
||||||
return false;
|
return false;
|
||||||
pred_var = (Var *) get_leftop(predicate);
|
leftop = get_leftop(predicate);
|
||||||
pred_const = (Const *) get_rightop(predicate);
|
rightop = get_rightop(predicate);
|
||||||
|
if (rightop == NULL)
|
||||||
|
return false; /* not a binary opclause */
|
||||||
|
if (IsA(rightop, Const))
|
||||||
|
{
|
||||||
|
pred_var = leftop;
|
||||||
|
pred_const = (Const *) rightop;
|
||||||
|
pred_var_on_left = true;
|
||||||
|
}
|
||||||
|
else if (IsA(leftop, Const))
|
||||||
|
{
|
||||||
|
pred_var = rightop;
|
||||||
|
pred_const = (Const *) leftop;
|
||||||
|
pred_var_on_left = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false; /* no Const to be found */
|
||||||
|
if (pred_const->constisnull)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!is_opclause(clause))
|
if (!is_opclause(clause))
|
||||||
return false;
|
return false;
|
||||||
clause_var = (Var *) get_leftop((Expr *) clause);
|
leftop = get_leftop((Expr *) clause);
|
||||||
clause_const = (Const *) get_rightop((Expr *) clause);
|
rightop = get_rightop((Expr *) clause);
|
||||||
|
if (rightop == NULL)
|
||||||
if (!IsA(clause_var, Var) ||
|
return false; /* not a binary opclause */
|
||||||
clause_const == NULL ||
|
if (IsA(rightop, Const))
|
||||||
!IsA(clause_const, Const) ||
|
{
|
||||||
!IsA(pred_var, Var) ||
|
clause_var = leftop;
|
||||||
pred_const == NULL ||
|
clause_const = (Const *) rightop;
|
||||||
!IsA(pred_const, Const))
|
clause_var_on_left = true;
|
||||||
|
}
|
||||||
|
else if (IsA(leftop, Const))
|
||||||
|
{
|
||||||
|
clause_var = rightop;
|
||||||
|
clause_const = (Const *) leftop;
|
||||||
|
clause_var_on_left = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false; /* no Const to be found */
|
||||||
|
if (clause_const->constisnull)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The implication can't be determined unless the predicate and the
|
* Check for matching subexpressions on the non-Const sides. We used to
|
||||||
* clause refer to the same attribute.
|
* only allow a simple Var, but it's about as easy to allow any
|
||||||
|
* expression. Remember we already know that the pred expression does
|
||||||
|
* not contain any non-immutable functions, so identical expressions
|
||||||
|
* should yield identical results.
|
||||||
*/
|
*/
|
||||||
if (clause_var->varno != pred_var->varno ||
|
if (!equal(pred_var, clause_var))
|
||||||
clause_var->varattno != pred_var->varattno)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Get the operators for the two clauses we're comparing */
|
/*
|
||||||
|
* Okay, get the operators in the two clauses we're comparing.
|
||||||
|
* Commute them if needed so that we can assume the variables are
|
||||||
|
* on the left.
|
||||||
|
*/
|
||||||
pred_op = ((OpExpr *) predicate)->opno;
|
pred_op = ((OpExpr *) predicate)->opno;
|
||||||
|
if (!pred_var_on_left)
|
||||||
|
{
|
||||||
|
pred_op = get_commutator(pred_op);
|
||||||
|
if (!OidIsValid(pred_op))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
clause_op = ((OpExpr *) clause)->opno;
|
clause_op = ((OpExpr *) clause)->opno;
|
||||||
|
if (!clause_var_on_left)
|
||||||
|
{
|
||||||
|
clause_op = get_commutator(clause_op);
|
||||||
|
if (!OidIsValid(clause_op))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to find a btree opclass containing the needed operators.
|
* Try to find a btree opclass containing the needed operators.
|
||||||
@ -1052,6 +1126,28 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
|||||||
ObjectIdGetDatum(pred_op),
|
ObjectIdGetDatum(pred_op),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we couldn't find any opclass containing the pred_op, perhaps it
|
||||||
|
* is a <> operator. See if it has a negator that is in an opclass.
|
||||||
|
*/
|
||||||
|
pred_op_negated = false;
|
||||||
|
if (catlist->n_members == 0)
|
||||||
|
{
|
||||||
|
pred_op_negator = get_negator(pred_op);
|
||||||
|
if (OidIsValid(pred_op_negator))
|
||||||
|
{
|
||||||
|
pred_op_negated = true;
|
||||||
|
ReleaseSysCacheList(catlist);
|
||||||
|
catlist = SearchSysCacheList(AMOPOPID, 1,
|
||||||
|
ObjectIdGetDatum(pred_op_negator),
|
||||||
|
0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Also may need the clause_op's negator */
|
||||||
|
clause_op_negator = get_negator(clause_op);
|
||||||
|
|
||||||
|
/* Now search the opclasses */
|
||||||
for (i = 0; i < catlist->n_members; i++)
|
for (i = 0; i < catlist->n_members; i++)
|
||||||
{
|
{
|
||||||
HeapTuple pred_tuple = &catlist->members[i]->tuple;
|
HeapTuple pred_tuple = &catlist->members[i]->tuple;
|
||||||
@ -1071,6 +1167,14 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
|||||||
pred_strategy = (StrategyNumber) pred_form->amopstrategy;
|
pred_strategy = (StrategyNumber) pred_form->amopstrategy;
|
||||||
Assert(pred_strategy >= 1 && pred_strategy <= 5);
|
Assert(pred_strategy >= 1 && pred_strategy <= 5);
|
||||||
|
|
||||||
|
if (pred_op_negated)
|
||||||
|
{
|
||||||
|
/* Only consider negators that are = */
|
||||||
|
if (pred_strategy != BTEqualStrategyNumber)
|
||||||
|
continue;
|
||||||
|
pred_strategy = BTNE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* From the same opclass, find a strategy number for the clause_op,
|
* From the same opclass, find a strategy number for the clause_op,
|
||||||
* if possible
|
* if possible
|
||||||
@ -1087,31 +1191,65 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
|||||||
clause_strategy = (StrategyNumber) clause_form->amopstrategy;
|
clause_strategy = (StrategyNumber) clause_form->amopstrategy;
|
||||||
Assert(clause_strategy >= 1 && clause_strategy <= 5);
|
Assert(clause_strategy >= 1 && clause_strategy <= 5);
|
||||||
clause_subtype = clause_form->amopsubtype;
|
clause_subtype = clause_form->amopsubtype;
|
||||||
|
|
||||||
/* done with clause_tuple */
|
|
||||||
ReleaseSysCache(clause_tuple);
|
ReleaseSysCache(clause_tuple);
|
||||||
|
}
|
||||||
/*
|
else if (OidIsValid(clause_op_negator))
|
||||||
* Look up the "test" strategy number in the implication table
|
{
|
||||||
*/
|
clause_tuple = SearchSysCache(AMOPOPID,
|
||||||
test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
|
ObjectIdGetDatum(clause_op_negator),
|
||||||
if (test_strategy == 0)
|
ObjectIdGetDatum(opclass_id),
|
||||||
|
0, 0);
|
||||||
|
if (HeapTupleIsValid(clause_tuple))
|
||||||
{
|
{
|
||||||
/* Can't determine implication using this interpretation */
|
Form_pg_amop clause_form = (Form_pg_amop) GETSTRUCT(clause_tuple);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/* Get the restriction clause operator's strategy/subtype */
|
||||||
* See if opclass has an operator for the test strategy and the
|
clause_strategy = (StrategyNumber) clause_form->amopstrategy;
|
||||||
* clause datatype.
|
Assert(clause_strategy >= 1 && clause_strategy <= 5);
|
||||||
*/
|
clause_subtype = clause_form->amopsubtype;
|
||||||
|
ReleaseSysCache(clause_tuple);
|
||||||
|
|
||||||
|
/* Only consider negators that are = */
|
||||||
|
if (clause_strategy != BTEqualStrategyNumber)
|
||||||
|
continue;
|
||||||
|
clause_strategy = BTNE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look up the "test" strategy number in the implication table
|
||||||
|
*/
|
||||||
|
test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
|
||||||
|
if (test_strategy == 0)
|
||||||
|
{
|
||||||
|
/* Can't determine implication using this interpretation */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See if opclass has an operator for the test strategy and the
|
||||||
|
* clause datatype.
|
||||||
|
*/
|
||||||
|
if (test_strategy == BTNE)
|
||||||
|
{
|
||||||
|
test_op = get_opclass_member(opclass_id, clause_subtype,
|
||||||
|
BTEqualStrategyNumber);
|
||||||
|
if (OidIsValid(test_op))
|
||||||
|
test_op = get_negator(test_op);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
test_op = get_opclass_member(opclass_id, clause_subtype,
|
test_op = get_opclass_member(opclass_id, clause_subtype,
|
||||||
test_strategy);
|
test_strategy);
|
||||||
if (OidIsValid(test_op))
|
}
|
||||||
{
|
if (OidIsValid(test_op))
|
||||||
found = true;
|
{
|
||||||
break;
|
found = true;
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1143,7 +1281,7 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
|||||||
|
|
||||||
/* And execute it. */
|
/* And execute it. */
|
||||||
test_result = ExecEvalExprSwitchContext(test_exprstate,
|
test_result = ExecEvalExprSwitchContext(test_exprstate,
|
||||||
GetPerTupleExprContext(estate),
|
GetPerTupleExprContext(estate),
|
||||||
&isNull, NULL);
|
&isNull, NULL);
|
||||||
|
|
||||||
/* Get back to outer memory context */
|
/* Get back to outer memory context */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user