Skip to content

Commit cad5f4a

Browse files
committed
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.
1 parent 5049838 commit cad5f4a

File tree

1 file changed

+190
-52
lines changed

1 file changed

+190
-52
lines changed

src/backend/optimizer/path/indxpath.c

Lines changed: 190 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.155 2004/01/05 23:39:54 tgl Exp $
12+
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.156 2004/01/07 22:02:48 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -918,13 +918,18 @@ pred_test_recurse_pred(Expr *predicate, Node *clause)
918918

919919
/*
920920
* Define an "operator implication table" for btree operators ("strategies").
921-
* The "strategy numbers" are: (1) < (2) <= (3) = (4) >= (5) >
921+
*
922+
* The strategy numbers defined by btree indexes (see access/skey.h) are:
923+
* (1) < (2) <= (3) = (4) >= (5) >
924+
* and in addition we use (6) to represent <>. <> is not a btree-indexable
925+
* operator, but we assume here that if the equality operator of a btree
926+
* opclass has a negator operator, the negator behaves as <> for the opclass.
922927
*
923928
* The interpretation of:
924929
*
925930
* test_op = BT_implic_table[given_op-1][target_op-1]
926931
*
927-
* where test_op, given_op and target_op are strategy numbers (from 1 to 5)
932+
* where test_op, given_op and target_op are strategy numbers (from 1 to 6)
928933
* of btree operators, is as follows:
929934
*
930935
* 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)
933938
* then the target expression must be true; if the test returns false, then
934939
* the target expression may be false.
935940
*
936-
* An entry where test_op==0 means the implication cannot be determined, i.e.,
937-
* this test should always be considered false.
941+
* An entry where test_op == 0 means the implication cannot be determined,
942+
* i.e., this test should always be considered false.
938943
*/
939944

945+
#define BTLT BTLessStrategyNumber
946+
#define BTLE BTLessEqualStrategyNumber
947+
#define BTEQ BTEqualStrategyNumber
948+
#define BTGE BTGreaterEqualStrategyNumber
949+
#define BTGT BTGreaterStrategyNumber
950+
#define BTNE 6
951+
940952
static const StrategyNumber
941-
BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = {
942-
{4, 4, 0, 0, 0},
943-
{5, 4, 0, 0, 0},
944-
{5, 4, 3, 2, 1},
945-
{0, 0, 0, 2, 1},
946-
{0, 0, 0, 2, 2}
953+
BT_implic_table[6][6] = {
954+
/*
955+
* The target operator:
956+
*
957+
* LT LE EQ GE GT NE
958+
*/
959+
{BTGE, BTGE, 0, 0, 0, BTGE}, /* LT */
960+
{BTGT, BTGE, 0, 0, 0, BTGT}, /* LE */
961+
{BTGT, BTGE, BTEQ, BTLE, BTLT, BTNE}, /* EQ */
962+
{ 0, 0, 0, BTLE, BTLT, BTLT}, /* GE */
963+
{ 0, 0, 0, BTLE, BTLE, BTLE}, /* GT */
964+
{ 0, 0, 0, 0, 0, BTEQ} /* NE */
947965
};
948966

949967

@@ -969,12 +987,19 @@ static const StrategyNumber
969987
static bool
970988
pred_test_simple_clause(Expr *predicate, Node *clause)
971989
{
972-
Var *pred_var,
990+
Node *leftop,
991+
*rightop;
992+
Node *pred_var,
973993
*clause_var;
974994
Const *pred_const,
975995
*clause_const;
996+
bool pred_var_on_left,
997+
clause_var_on_left,
998+
pred_op_negated;
976999
Oid pred_op,
9771000
clause_op,
1001+
pred_op_negator,
1002+
clause_op_negator,
9781003
test_op = InvalidOid;
9791004
Oid opclass_id;
9801005
bool found = false;
@@ -997,40 +1022,89 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
9971022

9981023
/*
9991024
* Can't do anything more unless they are both binary opclauses with a
1000-
* Var on the left and a Const on the right. (XXX someday try to
1001-
* commute Const/Var cases?) Note we don't have to think about binary
1002-
* relabeling of the Const node, since that would have been folded right
1003-
* into the Const.
1025+
* Const on one side, and identical subexpressions on the other sides.
1026+
* Note we don't have to think about binary relabeling of the Const node,
1027+
* since that would have been folded right into the Const.
1028+
*
1029+
* If either Const is null, we also fail right away; this assumes that
1030+
* the test operator will always be strict.
10041031
*/
10051032
if (!is_opclause(predicate))
10061033
return false;
1007-
pred_var = (Var *) get_leftop(predicate);
1008-
pred_const = (Const *) get_rightop(predicate);
1034+
leftop = get_leftop(predicate);
1035+
rightop = get_rightop(predicate);
1036+
if (rightop == NULL)
1037+
return false; /* not a binary opclause */
1038+
if (IsA(rightop, Const))
1039+
{
1040+
pred_var = leftop;
1041+
pred_const = (Const *) rightop;
1042+
pred_var_on_left = true;
1043+
}
1044+
else if (IsA(leftop, Const))
1045+
{
1046+
pred_var = rightop;
1047+
pred_const = (Const *) leftop;
1048+
pred_var_on_left = false;
1049+
}
1050+
else
1051+
return false; /* no Const to be found */
1052+
if (pred_const->constisnull)
1053+
return false;
10091054

10101055
if (!is_opclause(clause))
10111056
return false;
1012-
clause_var = (Var *) get_leftop((Expr *) clause);
1013-
clause_const = (Const *) get_rightop((Expr *) clause);
1014-
1015-
if (!IsA(clause_var, Var) ||
1016-
clause_const == NULL ||
1017-
!IsA(clause_const, Const) ||
1018-
!IsA(pred_var, Var) ||
1019-
pred_const == NULL ||
1020-
!IsA(pred_const, Const))
1057+
leftop = get_leftop((Expr *) clause);
1058+
rightop = get_rightop((Expr *) clause);
1059+
if (rightop == NULL)
1060+
return false; /* not a binary opclause */
1061+
if (IsA(rightop, Const))
1062+
{
1063+
clause_var = leftop;
1064+
clause_const = (Const *) rightop;
1065+
clause_var_on_left = true;
1066+
}
1067+
else if (IsA(leftop, Const))
1068+
{
1069+
clause_var = rightop;
1070+
clause_const = (Const *) leftop;
1071+
clause_var_on_left = false;
1072+
}
1073+
else
1074+
return false; /* no Const to be found */
1075+
if (clause_const->constisnull)
10211076
return false;
10221077

10231078
/*
1024-
* The implication can't be determined unless the predicate and the
1025-
* clause refer to the same attribute.
1079+
* Check for matching subexpressions on the non-Const sides. We used to
1080+
* only allow a simple Var, but it's about as easy to allow any
1081+
* expression. Remember we already know that the pred expression does
1082+
* not contain any non-immutable functions, so identical expressions
1083+
* should yield identical results.
10261084
*/
1027-
if (clause_var->varno != pred_var->varno ||
1028-
clause_var->varattno != pred_var->varattno)
1085+
if (!equal(pred_var, clause_var))
10291086
return false;
10301087

1031-
/* Get the operators for the two clauses we're comparing */
1088+
/*
1089+
* Okay, get the operators in the two clauses we're comparing.
1090+
* Commute them if needed so that we can assume the variables are
1091+
* on the left.
1092+
*/
10321093
pred_op = ((OpExpr *) predicate)->opno;
1094+
if (!pred_var_on_left)
1095+
{
1096+
pred_op = get_commutator(pred_op);
1097+
if (!OidIsValid(pred_op))
1098+
return false;
1099+
}
1100+
10331101
clause_op = ((OpExpr *) clause)->opno;
1102+
if (!clause_var_on_left)
1103+
{
1104+
clause_op = get_commutator(clause_op);
1105+
if (!OidIsValid(clause_op))
1106+
return false;
1107+
}
10341108

10351109
/*
10361110
* Try to find a btree opclass containing the needed operators.
@@ -1052,6 +1126,28 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
10521126
ObjectIdGetDatum(pred_op),
10531127
0, 0, 0);
10541128

1129+
/*
1130+
* If we couldn't find any opclass containing the pred_op, perhaps it
1131+
* is a <> operator. See if it has a negator that is in an opclass.
1132+
*/
1133+
pred_op_negated = false;
1134+
if (catlist->n_members == 0)
1135+
{
1136+
pred_op_negator = get_negator(pred_op);
1137+
if (OidIsValid(pred_op_negator))
1138+
{
1139+
pred_op_negated = true;
1140+
ReleaseSysCacheList(catlist);
1141+
catlist = SearchSysCacheList(AMOPOPID, 1,
1142+
ObjectIdGetDatum(pred_op_negator),
1143+
0, 0, 0);
1144+
}
1145+
}
1146+
1147+
/* Also may need the clause_op's negator */
1148+
clause_op_negator = get_negator(clause_op);
1149+
1150+
/* Now search the opclasses */
10551151
for (i = 0; i < catlist->n_members; i++)
10561152
{
10571153
HeapTuple pred_tuple = &catlist->members[i]->tuple;
@@ -1071,6 +1167,14 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
10711167
pred_strategy = (StrategyNumber) pred_form->amopstrategy;
10721168
Assert(pred_strategy >= 1 && pred_strategy <= 5);
10731169

1170+
if (pred_op_negated)
1171+
{
1172+
/* Only consider negators that are = */
1173+
if (pred_strategy != BTEqualStrategyNumber)
1174+
continue;
1175+
pred_strategy = BTNE;
1176+
}
1177+
10741178
/*
10751179
* From the same opclass, find a strategy number for the clause_op,
10761180
* if possible
@@ -1087,31 +1191,65 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
10871191
clause_strategy = (StrategyNumber) clause_form->amopstrategy;
10881192
Assert(clause_strategy >= 1 && clause_strategy <= 5);
10891193
clause_subtype = clause_form->amopsubtype;
1090-
1091-
/* done with clause_tuple */
10921194
ReleaseSysCache(clause_tuple);
1093-
1094-
/*
1095-
* Look up the "test" strategy number in the implication table
1096-
*/
1097-
test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
1098-
if (test_strategy == 0)
1195+
}
1196+
else if (OidIsValid(clause_op_negator))
1197+
{
1198+
clause_tuple = SearchSysCache(AMOPOPID,
1199+
ObjectIdGetDatum(clause_op_negator),
1200+
ObjectIdGetDatum(opclass_id),
1201+
0, 0);
1202+
if (HeapTupleIsValid(clause_tuple))
10991203
{
1100-
/* Can't determine implication using this interpretation */
1101-
continue;
1204+
Form_pg_amop clause_form = (Form_pg_amop) GETSTRUCT(clause_tuple);
1205+
1206+
/* Get the restriction clause operator's strategy/subtype */
1207+
clause_strategy = (StrategyNumber) clause_form->amopstrategy;
1208+
Assert(clause_strategy >= 1 && clause_strategy <= 5);
1209+
clause_subtype = clause_form->amopsubtype;
1210+
ReleaseSysCache(clause_tuple);
1211+
1212+
/* Only consider negators that are = */
1213+
if (clause_strategy != BTEqualStrategyNumber)
1214+
continue;
1215+
clause_strategy = BTNE;
11021216
}
1217+
else
1218+
continue;
1219+
}
1220+
else
1221+
continue;
11031222

1104-
/*
1105-
* See if opclass has an operator for the test strategy and the
1106-
* clause datatype.
1107-
*/
1223+
/*
1224+
* Look up the "test" strategy number in the implication table
1225+
*/
1226+
test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
1227+
if (test_strategy == 0)
1228+
{
1229+
/* Can't determine implication using this interpretation */
1230+
continue;
1231+
}
1232+
1233+
/*
1234+
* See if opclass has an operator for the test strategy and the
1235+
* clause datatype.
1236+
*/
1237+
if (test_strategy == BTNE)
1238+
{
11081239
test_op = get_opclass_member(opclass_id, clause_subtype,
1109-
test_strategy);
1240+
BTEqualStrategyNumber);
11101241
if (OidIsValid(test_op))
1111-
{
1112-
found = true;
1113-
break;
1114-
}
1242+
test_op = get_negator(test_op);
1243+
}
1244+
else
1245+
{
1246+
test_op = get_opclass_member(opclass_id, clause_subtype,
1247+
test_strategy);
1248+
}
1249+
if (OidIsValid(test_op))
1250+
{
1251+
found = true;
1252+
break;
11151253
}
11161254
}
11171255

@@ -1143,7 +1281,7 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
11431281

11441282
/* And execute it. */
11451283
test_result = ExecEvalExprSwitchContext(test_exprstate,
1146-
GetPerTupleExprContext(estate),
1284+
GetPerTupleExprContext(estate),
11471285
&isNull, NULL);
11481286

11491287
/* Get back to outer memory context */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy