1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-27 23:21:58 +03:00

Relax ordering-related hardcoded btree requirements in planning

There were several places in ordering-related planning where a
requirement for btree was hardcoded but an amcanorder index could
suffice.  This fixes that.  We just need to do the necessary mapping
between strategy numbers and compare types and adjust some related
APIs so that this works independent of btree strategy numbers.  For
instance, non-btree amcanorder indexes can now be used to support
sorting and merge joins.  Also, predtest.c works independent of btree
strategy numbers now.

To avoid performance regressions, some details on btree and other
built-in index types are still hardcoded as shortcuts, but other index
types now have access to the same features by providing the required
flags and callbacks.

Author: Mark Dilger <mark.dilger@enterprisedb.com>
Co-authored-by: Peter Eisentraut <peter@eisentraut.org>
Discussion: https://www.postgresql.org/message-id/flat/E72EAA49-354D-4C2E-8EB9-255197F55330@enterprisedb.com
This commit is contained in:
Peter Eisentraut
2025-04-06 14:43:51 +02:00
parent 3a1a7c5a70
commit a8025f5448
16 changed files with 298 additions and 226 deletions

View File

@ -120,6 +120,11 @@ IndexAmTranslateStrategy(StrategyNumber strategy, Oid amoid, Oid opfamily, bool
CompareType result; CompareType result;
IndexAmRoutine *amroutine; IndexAmRoutine *amroutine;
/* shortcut for common case */
if (amoid == BTREE_AM_OID &&
(strategy > InvalidStrategy && strategy <= BTMaxStrategyNumber))
return (CompareType) strategy;
amroutine = GetIndexAmRoutineByAmId(amoid, false); amroutine = GetIndexAmRoutineByAmId(amoid, false);
if (amroutine->amtranslatestrategy) if (amroutine->amtranslatestrategy)
result = amroutine->amtranslatestrategy(strategy, opfamily); result = amroutine->amtranslatestrategy(strategy, opfamily);
@ -145,6 +150,11 @@ IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool m
StrategyNumber result; StrategyNumber result;
IndexAmRoutine *amroutine; IndexAmRoutine *amroutine;
/* shortcut for common case */
if (amoid == BTREE_AM_OID &&
(cmptype > COMPARE_INVALID && cmptype <= COMPARE_GT))
return (StrategyNumber) cmptype;
amroutine = GetIndexAmRoutineByAmId(amoid, false); amroutine = GetIndexAmRoutineByAmId(amoid, false);
if (amroutine->amtranslatecmptype) if (amroutine->amtranslatecmptype)
result = amroutine->amtranslatecmptype(cmptype, opfamily); result = amroutine->amtranslatecmptype(cmptype, opfamily);

View File

@ -220,7 +220,7 @@ MJExamineQuals(List *mergeclauses,
&op_strategy, &op_strategy,
&op_lefttype, &op_lefttype,
&op_righttype); &op_righttype);
if (op_strategy != BTEqualStrategyNumber) /* should not happen */ if (IndexAmTranslateStrategy(op_strategy, get_opfamily_method(opfamily), opfamily, true) != COMPARE_EQ) /* should not happen */
elog(ERROR, "cannot merge using non-equality operator %u", elog(ERROR, "cannot merge using non-equality operator %u",
qual->opno); qual->opno);

View File

@ -2313,16 +2313,15 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
runopexpr = NULL; runopexpr = NULL;
runoperator = InvalidOid; runoperator = InvalidOid;
opinfos = get_op_btree_interpretation(opexpr->opno); opinfos = get_op_index_interpretation(opexpr->opno);
foreach(lc, opinfos) foreach(lc, opinfos)
{ {
OpBtreeInterpretation *opinfo = (OpBtreeInterpretation *) lfirst(lc); OpIndexInterpretation *opinfo = (OpIndexInterpretation *) lfirst(lc);
int strategy = opinfo->strategy; CompareType cmptype = opinfo->cmptype;
/* handle < / <= */ /* handle < / <= */
if (strategy == BTLessStrategyNumber || if (cmptype == COMPARE_LT || cmptype == COMPARE_LE)
strategy == BTLessEqualStrategyNumber)
{ {
/* /*
* < / <= is supported for monotonically increasing functions in * < / <= is supported for monotonically increasing functions in
@ -2339,8 +2338,7 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
break; break;
} }
/* handle > / >= */ /* handle > / >= */
else if (strategy == BTGreaterStrategyNumber || else if (cmptype == COMPARE_GT || cmptype == COMPARE_GE)
strategy == BTGreaterEqualStrategyNumber)
{ {
/* /*
* > / >= is supported for monotonically decreasing functions in * > / >= is supported for monotonically decreasing functions in
@ -2357,9 +2355,9 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
break; break;
} }
/* handle = */ /* handle = */
else if (strategy == BTEqualStrategyNumber) else if (cmptype == COMPARE_EQ)
{ {
int16 newstrategy; CompareType newcmptype;
/* /*
* When both monotonically increasing and decreasing then the * When both monotonically increasing and decreasing then the
@ -2383,19 +2381,19 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
* below the value in the equality condition. * below the value in the equality condition.
*/ */
if (res->monotonic & MONOTONICFUNC_INCREASING) if (res->monotonic & MONOTONICFUNC_INCREASING)
newstrategy = wfunc_left ? BTLessEqualStrategyNumber : BTGreaterEqualStrategyNumber; newcmptype = wfunc_left ? COMPARE_LE : COMPARE_GE;
else else
newstrategy = wfunc_left ? BTGreaterEqualStrategyNumber : BTLessEqualStrategyNumber; newcmptype = wfunc_left ? COMPARE_GE : COMPARE_LE;
/* We must keep the original equality qual */ /* We must keep the original equality qual */
*keep_original = true; *keep_original = true;
runopexpr = opexpr; runopexpr = opexpr;
/* determine the operator to use for the WindowFuncRunCondition */ /* determine the operator to use for the WindowFuncRunCondition */
runoperator = get_opfamily_member(opinfo->opfamily_id, runoperator = get_opfamily_member_for_cmptype(opinfo->opfamily_id,
opinfo->oplefttype, opinfo->oplefttype,
opinfo->oprighttype, opinfo->oprighttype,
newstrategy); newcmptype);
break; break;
} }
} }

View File

@ -1845,8 +1845,7 @@ select_equality_operator(EquivalenceClass *ec, Oid lefttype, Oid righttype)
Oid opfamily = lfirst_oid(lc); Oid opfamily = lfirst_oid(lc);
Oid opno; Oid opno;
opno = get_opfamily_member(opfamily, lefttype, righttype, opno = get_opfamily_member_for_cmptype(opfamily, lefttype, righttype, COMPARE_EQ);
BTEqualStrategyNumber);
if (!OidIsValid(opno)) if (!OidIsValid(opno))
continue; continue;
/* If no barrier quals in query, don't worry about leaky operators */ /* If no barrier quals in query, don't worry about leaky operators */

View File

@ -219,13 +219,13 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
* more than one opfamily. So we have to look up the opfamily's equality * more than one opfamily. So we have to look up the opfamily's equality
* operator and get its membership. * operator and get its membership.
*/ */
equality_op = get_opfamily_member(opfamily, equality_op = get_opfamily_member_for_cmptype(opfamily,
opcintype, opcintype,
opcintype, opcintype,
BTEqualStrategyNumber); COMPARE_EQ);
if (!OidIsValid(equality_op)) /* shouldn't happen */ if (!OidIsValid(equality_op)) /* shouldn't happen */
elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
BTEqualStrategyNumber, opcintype, opcintype, opfamily); COMPARE_EQ, opcintype, opcintype, opfamily);
opfamilies = get_mergejoin_opfamilies(equality_op); opfamilies = get_mergejoin_opfamilies(equality_op);
if (!opfamilies) /* certainly should find some */ if (!opfamilies) /* certainly should find some */
elog(ERROR, "could not find opfamilies for equality operator %u", elog(ERROR, "could not find opfamilies for equality operator %u",
@ -264,11 +264,11 @@ make_pathkey_from_sortop(PlannerInfo *root,
Oid opfamily, Oid opfamily,
opcintype, opcintype,
collation; collation;
int16 strategy; CompareType cmptype;
/* Find the operator in pg_amop --- failure shouldn't happen */ /* Find the operator in pg_amop --- failure shouldn't happen */
if (!get_ordering_op_properties(ordering_op, if (!get_ordering_op_properties(ordering_op,
&opfamily, &opcintype, &strategy)) &opfamily, &opcintype, &cmptype))
elog(ERROR, "operator %u is not a valid ordering operator", elog(ERROR, "operator %u is not a valid ordering operator",
ordering_op); ordering_op);
@ -1006,12 +1006,12 @@ build_expression_pathkey(PlannerInfo *root,
List *pathkeys; List *pathkeys;
Oid opfamily, Oid opfamily,
opcintype; opcintype;
int16 strategy; CompareType cmptype;
PathKey *cpathkey; PathKey *cpathkey;
/* Find the operator in pg_amop --- failure shouldn't happen */ /* Find the operator in pg_amop --- failure shouldn't happen */
if (!get_ordering_op_properties(opno, if (!get_ordering_op_properties(opno,
&opfamily, &opcintype, &strategy)) &opfamily, &opcintype, &cmptype))
elog(ERROR, "operator %u is not a valid ordering operator", elog(ERROR, "operator %u is not a valid ordering operator",
opno); opno);
@ -1020,8 +1020,8 @@ build_expression_pathkey(PlannerInfo *root,
opfamily, opfamily,
opcintype, opcintype,
exprCollation((Node *) expr), exprCollation((Node *) expr),
(strategy == BTGreaterStrategyNumber), (cmptype == COMPARE_GT),
(strategy == BTGreaterStrategyNumber), (cmptype == COMPARE_GT),
0, 0,
rel, rel,
create_it); create_it);

View File

@ -6893,13 +6893,13 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
* Look up the correct equality operator from the PathKey's slightly * Look up the correct equality operator from the PathKey's slightly
* abstracted representation. * abstracted representation.
*/ */
eqop = get_opfamily_member(pathkey->pk_opfamily, eqop = get_opfamily_member_for_cmptype(pathkey->pk_opfamily,
pk_datatype, pk_datatype,
pk_datatype, pk_datatype,
BTEqualStrategyNumber); COMPARE_EQ);
if (!OidIsValid(eqop)) /* should not happen */ if (!OidIsValid(eqop)) /* should not happen */
elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
BTEqualStrategyNumber, pk_datatype, pk_datatype, COMPARE_EQ, pk_datatype, pk_datatype,
pathkey->pk_opfamily); pathkey->pk_opfamily);
uniqColIdx[keyno] = tle->resno; uniqColIdx[keyno] = tle->resno;

View File

@ -365,14 +365,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
* Since "<" uniquely defines the behavior of a sort * Since "<" uniquely defines the behavior of a sort
* order, this is a sufficient test. * order, this is a sufficient test.
* *
* XXX This method is rather slow and also requires the * XXX This method is rather slow and complicated. It'd
* undesirable assumption that the other index AM numbers * be better to have a way to explicitly declare the
* its strategies the same as btree. It'd be better to * corresponding btree opfamily for each opfamily of the
* have a way to explicitly declare the corresponding * other index type.
* btree opfamily for each opfamily of the other index
* type. But given the lack of current or foreseeable
* amcanorder index types, it's not worth expending more
* effort on now.
*/ */
info->sortopfamily = (Oid *) palloc(sizeof(Oid) * nkeycolumns); info->sortopfamily = (Oid *) palloc(sizeof(Oid) * nkeycolumns);
info->reverse_sort = (bool *) palloc(sizeof(bool) * nkeycolumns); info->reverse_sort = (bool *) palloc(sizeof(bool) * nkeycolumns);
@ -382,27 +378,27 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
{ {
int16 opt = indexRelation->rd_indoption[i]; int16 opt = indexRelation->rd_indoption[i];
Oid ltopr; Oid ltopr;
Oid btopfamily; Oid opfamily;
Oid btopcintype; Oid opcintype;
int16 btstrategy; CompareType cmptype;
info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0; info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0; info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
ltopr = get_opfamily_member(info->opfamily[i], ltopr = get_opfamily_member_for_cmptype(info->opfamily[i],
info->opcintype[i], info->opcintype[i],
info->opcintype[i], info->opcintype[i],
BTLessStrategyNumber); COMPARE_LT);
if (OidIsValid(ltopr) && if (OidIsValid(ltopr) &&
get_ordering_op_properties(ltopr, get_ordering_op_properties(ltopr,
&btopfamily, &opfamily,
&btopcintype, &opcintype,
&btstrategy) && &cmptype) &&
btopcintype == info->opcintype[i] && opcintype == info->opcintype[i] &&
btstrategy == BTLessStrategyNumber) cmptype == COMPARE_LT)
{ {
/* Successful mapping */ /* Successful mapping */
info->sortopfamily[i] = btopfamily; info->sortopfamily[i] = opfamily;
} }
else else
{ {

View File

@ -1605,36 +1605,36 @@ clause_is_strict_for(Node *clause, Node *subexpr, bool allow_false)
/* /*
* Define "operator implication tables" for btree operators ("strategies"), * Define "operator implication tables" for index operators ("cmptypes"),
* and similar tables for refutation. * and similar tables for refutation.
* *
* The strategy numbers defined by btree indexes (see access/stratnum.h) are: * The row compare numbers defined by indexes (see access/cmptype.h) are:
* 1 < 2 <= 3 = 4 >= 5 > * 1 < 2 <= 3 = 4 >= 5 > 6 <>
* and in addition we use 6 to represent <>. <> is not a btree-indexable * and in addition we use 6 to represent <>. <> is not a btree-indexable
* operator, but we assume here that if an equality operator of a btree * operator, but we assume here that if an equality operator of a btree
* opfamily has a negator operator, the negator behaves as <> for the opfamily. * opfamily has a negator operator, the negator behaves as <> for the opfamily.
* (This convention is also known to get_op_btree_interpretation().) * (This convention is also known to get_op_index_interpretation().)
* *
* BT_implies_table[] and BT_refutes_table[] are used for cases where we have * RC_implies_table[] and RC_refutes_table[] are used for cases where we have
* two identical subexpressions and we want to know whether one operator * two identical subexpressions and we want to know whether one operator
* expression implies or refutes the other. That is, if the "clause" is * expression implies or refutes the other. That is, if the "clause" is
* EXPR1 clause_op EXPR2 and the "predicate" is EXPR1 pred_op EXPR2 for the * EXPR1 clause_op EXPR2 and the "predicate" is EXPR1 pred_op EXPR2 for the
* same two (immutable) subexpressions: * same two (immutable) subexpressions:
* BT_implies_table[clause_op-1][pred_op-1] * RC_implies_table[clause_op-1][pred_op-1]
* is true if the clause implies the predicate * is true if the clause implies the predicate
* BT_refutes_table[clause_op-1][pred_op-1] * RC_refutes_table[clause_op-1][pred_op-1]
* is true if the clause refutes the predicate * is true if the clause refutes the predicate
* where clause_op and pred_op are strategy numbers (from 1 to 6) in the * where clause_op and pred_op are cmptype numbers (from 1 to 6) in the
* same btree opfamily. For example, "x < y" implies "x <= y" and refutes * same opfamily. For example, "x < y" implies "x <= y" and refutes
* "x > y". * "x > y".
* *
* BT_implic_table[] and BT_refute_table[] are used where we have two * RC_implic_table[] and RC_refute_table[] are used where we have two
* constants that we need to compare. The interpretation of: * constants that we need to compare. The interpretation of:
* *
* test_op = BT_implic_table[clause_op-1][pred_op-1] * test_op = RC_implic_table[clause_op-1][pred_op-1]
* *
* where test_op, clause_op and pred_op are strategy numbers (from 1 to 6) * where test_op, clause_op and pred_op are cmptypes (from 1 to 6)
* of btree operators, is as follows: * of index operators, is as follows:
* *
* If you know, for some EXPR, that "EXPR clause_op CONST1" is true, and you * If you know, for some EXPR, that "EXPR clause_op CONST1" is true, and you
* want to determine whether "EXPR pred_op CONST2" must also be true, then * want to determine whether "EXPR pred_op CONST2" must also be true, then
@ -1645,7 +1645,7 @@ clause_is_strict_for(Node *clause, Node *subexpr, bool allow_false)
* For example, if clause is "Quantity > 10" and pred is "Quantity > 5" * For example, if clause is "Quantity > 10" and pred is "Quantity > 5"
* then we test "5 <= 10" which evals to true, so clause implies pred. * then we test "5 <= 10" which evals to true, so clause implies pred.
* *
* Similarly, the interpretation of a BT_refute_table entry is: * Similarly, the interpretation of a RC_refute_table entry is:
* *
* If you know, for some EXPR, that "EXPR clause_op CONST1" is true, and you * If you know, for some EXPR, that "EXPR clause_op CONST1" is true, and you
* want to determine whether "EXPR pred_op CONST2" must be false, then * want to determine whether "EXPR pred_op CONST2" must be false, then
@ -1659,17 +1659,17 @@ clause_is_strict_for(Node *clause, Node *subexpr, bool allow_false)
* An entry where test_op == 0 means the implication cannot be determined. * An entry where test_op == 0 means the implication cannot be determined.
*/ */
#define BTLT BTLessStrategyNumber #define RCLT COMPARE_LT
#define BTLE BTLessEqualStrategyNumber #define RCLE COMPARE_LE
#define BTEQ BTEqualStrategyNumber #define RCEQ COMPARE_EQ
#define BTGE BTGreaterEqualStrategyNumber #define RCGE COMPARE_GE
#define BTGT BTGreaterStrategyNumber #define RCGT COMPARE_GT
#define BTNE COMPARE_NE #define RCNE COMPARE_NE
/* We use "none" for 0/false to make the tables align nicely */ /* We use "none" for 0/false to make the tables align nicely */
#define none 0 #define none 0
static const bool BT_implies_table[6][6] = { static const bool RC_implies_table[6][6] = {
/* /*
* The predicate operator: * The predicate operator:
* LT LE EQ GE GT NE * LT LE EQ GE GT NE
@ -1682,7 +1682,7 @@ static const bool BT_implies_table[6][6] = {
{none, none, none, none, none, true} /* NE */ {none, none, none, none, none, true} /* NE */
}; };
static const bool BT_refutes_table[6][6] = { static const bool RC_refutes_table[6][6] = {
/* /*
* The predicate operator: * The predicate operator:
* LT LE EQ GE GT NE * LT LE EQ GE GT NE
@ -1695,30 +1695,30 @@ static const bool BT_refutes_table[6][6] = {
{none, none, true, none, none, none} /* NE */ {none, none, true, none, none, none} /* NE */
}; };
static const StrategyNumber BT_implic_table[6][6] = { static const CompareType RC_implic_table[6][6] = {
/* /*
* The predicate operator: * The predicate operator:
* LT LE EQ GE GT NE * LT LE EQ GE GT NE
*/ */
{BTGE, BTGE, none, none, none, BTGE}, /* LT */ {RCGE, RCGE, none, none, none, RCGE}, /* LT */
{BTGT, BTGE, none, none, none, BTGT}, /* LE */ {RCGT, RCGE, none, none, none, RCGT}, /* LE */
{BTGT, BTGE, BTEQ, BTLE, BTLT, BTNE}, /* EQ */ {RCGT, RCGE, RCEQ, RCLE, RCLT, RCNE}, /* EQ */
{none, none, none, BTLE, BTLT, BTLT}, /* GE */ {none, none, none, RCLE, RCLT, RCLT}, /* GE */
{none, none, none, BTLE, BTLE, BTLE}, /* GT */ {none, none, none, RCLE, RCLE, RCLE}, /* GT */
{none, none, none, none, none, BTEQ} /* NE */ {none, none, none, none, none, RCEQ} /* NE */
}; };
static const StrategyNumber BT_refute_table[6][6] = { static const CompareType RC_refute_table[6][6] = {
/* /*
* The predicate operator: * The predicate operator:
* LT LE EQ GE GT NE * LT LE EQ GE GT NE
*/ */
{none, none, BTGE, BTGE, BTGE, none}, /* LT */ {none, none, RCGE, RCGE, RCGE, none}, /* LT */
{none, none, BTGT, BTGT, BTGE, none}, /* LE */ {none, none, RCGT, RCGT, RCGE, none}, /* LE */
{BTLE, BTLT, BTNE, BTGT, BTGE, BTEQ}, /* EQ */ {RCLE, RCLT, RCNE, RCGT, RCGE, RCEQ}, /* EQ */
{BTLE, BTLT, BTLT, none, none, none}, /* GE */ {RCLE, RCLT, RCLT, none, none, none}, /* GE */
{BTLE, BTLE, BTLE, none, none, none}, /* GT */ {RCLE, RCLE, RCLE, none, none, none}, /* GT */
{none, none, BTEQ, none, none, none} /* NE */ {none, none, RCEQ, none, none, none} /* NE */
}; };
@ -2165,23 +2165,23 @@ lookup_proof_cache(Oid pred_op, Oid clause_op, bool refute_it)
* operator. This can happen in cases with incomplete sets of cross-type * operator. This can happen in cases with incomplete sets of cross-type
* comparison operators. * comparison operators.
*/ */
clause_op_infos = get_op_btree_interpretation(clause_op); clause_op_infos = get_op_index_interpretation(clause_op);
if (clause_op_infos) if (clause_op_infos)
pred_op_infos = get_op_btree_interpretation(pred_op); pred_op_infos = get_op_index_interpretation(pred_op);
else /* no point in looking */ else /* no point in looking */
pred_op_infos = NIL; pred_op_infos = NIL;
foreach(lcp, pred_op_infos) foreach(lcp, pred_op_infos)
{ {
OpBtreeInterpretation *pred_op_info = lfirst(lcp); OpIndexInterpretation *pred_op_info = lfirst(lcp);
Oid opfamily_id = pred_op_info->opfamily_id; Oid opfamily_id = pred_op_info->opfamily_id;
foreach(lcc, clause_op_infos) foreach(lcc, clause_op_infos)
{ {
OpBtreeInterpretation *clause_op_info = lfirst(lcc); OpIndexInterpretation *clause_op_info = lfirst(lcc);
StrategyNumber pred_strategy, CompareType pred_cmptype,
clause_strategy, clause_cmptype,
test_strategy; test_cmptype;
/* Must find them in same opfamily */ /* Must find them in same opfamily */
if (opfamily_id != clause_op_info->opfamily_id) if (opfamily_id != clause_op_info->opfamily_id)
@ -2189,51 +2189,51 @@ lookup_proof_cache(Oid pred_op, Oid clause_op, bool refute_it)
/* Lefttypes should match */ /* Lefttypes should match */
Assert(clause_op_info->oplefttype == pred_op_info->oplefttype); Assert(clause_op_info->oplefttype == pred_op_info->oplefttype);
pred_strategy = pred_op_info->strategy; pred_cmptype = pred_op_info->cmptype;
clause_strategy = clause_op_info->strategy; clause_cmptype = clause_op_info->cmptype;
/* /*
* Check to see if we can make a proof for same-subexpressions * Check to see if we can make a proof for same-subexpressions
* cases based on the operators' relationship in this opfamily. * cases based on the operators' relationship in this opfamily.
*/ */
if (refute_it) if (refute_it)
same_subexprs |= BT_refutes_table[clause_strategy - 1][pred_strategy - 1]; same_subexprs |= RC_refutes_table[clause_cmptype - 1][pred_cmptype - 1];
else else
same_subexprs |= BT_implies_table[clause_strategy - 1][pred_strategy - 1]; same_subexprs |= RC_implies_table[clause_cmptype - 1][pred_cmptype - 1];
/* /*
* Look up the "test" strategy number in the implication table * Look up the "test" cmptype number in the implication table
*/ */
if (refute_it) if (refute_it)
test_strategy = BT_refute_table[clause_strategy - 1][pred_strategy - 1]; test_cmptype = RC_refute_table[clause_cmptype - 1][pred_cmptype - 1];
else else
test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1]; test_cmptype = RC_implic_table[clause_cmptype - 1][pred_cmptype - 1];
if (test_strategy == 0) if (test_cmptype == 0)
{ {
/* Can't determine implication using this interpretation */ /* Can't determine implication using this interpretation */
continue; continue;
} }
/* /*
* See if opfamily has an operator for the test strategy and the * See if opfamily has an operator for the test cmptype and the
* datatypes. * datatypes.
*/ */
if (test_strategy == BTNE) if (test_cmptype == RCNE)
{ {
test_op = get_opfamily_member(opfamily_id, test_op = get_opfamily_member_for_cmptype(opfamily_id,
pred_op_info->oprighttype, pred_op_info->oprighttype,
clause_op_info->oprighttype, clause_op_info->oprighttype,
BTEqualStrategyNumber); COMPARE_EQ);
if (OidIsValid(test_op)) if (OidIsValid(test_op))
test_op = get_negator(test_op); test_op = get_negator(test_op);
} }
else else
{ {
test_op = get_opfamily_member(opfamily_id, test_op = get_opfamily_member_for_cmptype(opfamily_id,
pred_op_info->oprighttype, pred_op_info->oprighttype,
clause_op_info->oprighttype, clause_op_info->oprighttype,
test_strategy); test_cmptype);
} }
if (!OidIsValid(test_op)) if (!OidIsValid(test_op))

View File

@ -2915,7 +2915,7 @@ transformWindowDefinitions(ParseState *pstate,
{ {
SortGroupClause *sortcl; SortGroupClause *sortcl;
Node *sortkey; Node *sortkey;
int16 rangestrategy; CompareType rangecmptype;
if (list_length(wc->orderClause) != 1) if (list_length(wc->orderClause) != 1)
ereport(ERROR, ereport(ERROR,
@ -2928,7 +2928,7 @@ transformWindowDefinitions(ParseState *pstate,
if (!get_ordering_op_properties(sortcl->sortop, if (!get_ordering_op_properties(sortcl->sortop,
&rangeopfamily, &rangeopfamily,
&rangeopcintype, &rangeopcintype,
&rangestrategy)) &rangecmptype))
elog(ERROR, "operator %u is not a valid ordering operator", elog(ERROR, "operator %u is not a valid ordering operator",
sortcl->sortop); sortcl->sortop);
/* Record properties of sort ordering */ /* Record properties of sort ordering */

View File

@ -2832,7 +2832,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
ListCell *l, ListCell *l,
*r; *r;
List **opinfo_lists; List **opinfo_lists;
Bitmapset *strats; Bitmapset *cmptypes;
int nopers; int nopers;
int i; int i;
@ -2897,45 +2897,45 @@ make_row_comparison_op(ParseState *pstate, List *opname,
/* /*
* Now we must determine which row comparison semantics (= <> < <= > >=) * Now we must determine which row comparison semantics (= <> < <= > >=)
* apply to this set of operators. We look for btree opfamilies * apply to this set of operators. We look for opfamilies containing the
* containing the operators, and see which interpretations (strategy * operators, and see which interpretations (cmptypes) exist for each
* numbers) exist for each operator. * operator.
*/ */
opinfo_lists = (List **) palloc(nopers * sizeof(List *)); opinfo_lists = (List **) palloc(nopers * sizeof(List *));
strats = NULL; cmptypes = NULL;
i = 0; i = 0;
foreach(l, opexprs) foreach(l, opexprs)
{ {
Oid opno = ((OpExpr *) lfirst(l))->opno; Oid opno = ((OpExpr *) lfirst(l))->opno;
Bitmapset *this_strats; Bitmapset *this_cmptypes;
ListCell *j; ListCell *j;
opinfo_lists[i] = get_op_btree_interpretation(opno); opinfo_lists[i] = get_op_index_interpretation(opno);
/* /*
* convert strategy numbers into a Bitmapset to make the intersection * convert comparison types into a Bitmapset to make the intersection
* calculation easy. * calculation easy.
*/ */
this_strats = NULL; this_cmptypes = NULL;
foreach(j, opinfo_lists[i]) foreach(j, opinfo_lists[i])
{ {
OpBtreeInterpretation *opinfo = lfirst(j); OpIndexInterpretation *opinfo = lfirst(j);
this_strats = bms_add_member(this_strats, opinfo->strategy); this_cmptypes = bms_add_member(this_cmptypes, opinfo->cmptype);
} }
if (i == 0) if (i == 0)
strats = this_strats; cmptypes = this_cmptypes;
else else
strats = bms_int_members(strats, this_strats); cmptypes = bms_int_members(cmptypes, this_cmptypes);
i++; i++;
} }
/* /*
* If there are multiple common interpretations, we may use any one of * If there are multiple common interpretations, we may use any one of
* them ... this coding arbitrarily picks the lowest btree strategy * them ... this coding arbitrarily picks the lowest comparison type
* number. * number.
*/ */
i = bms_next_member(strats, -1); i = bms_next_member(cmptypes, -1);
if (i < 0) if (i < 0)
{ {
/* No common interpretation, so fail */ /* No common interpretation, so fail */
@ -2969,9 +2969,9 @@ make_row_comparison_op(ParseState *pstate, List *opname,
foreach(j, opinfo_lists[i]) foreach(j, opinfo_lists[i])
{ {
OpBtreeInterpretation *opinfo = lfirst(j); OpIndexInterpretation *opinfo = lfirst(j);
if (opinfo->strategy == cmptype) if (opinfo->cmptype == cmptype)
{ {
opfamily = opinfo->opfamily_id; opfamily = opinfo->opfamily_id;
break; break;

View File

@ -2947,7 +2947,7 @@ scalargejoinsel(PG_FUNCTION_ARGS)
* first join pair is found, which will affect the join's startup time. * first join pair is found, which will affect the join's startup time.
* *
* clause should be a clause already known to be mergejoinable. opfamily, * clause should be a clause already known to be mergejoinable. opfamily,
* strategy, and nulls_first specify the sort ordering being used. * cmptype, and nulls_first specify the sort ordering being used.
* *
* The outputs are: * The outputs are:
* *leftstart is set to the fraction of the left-hand variable expected * *leftstart is set to the fraction of the left-hand variable expected
@ -2958,7 +2958,7 @@ scalargejoinsel(PG_FUNCTION_ARGS)
*/ */
void void
mergejoinscansel(PlannerInfo *root, Node *clause, mergejoinscansel(PlannerInfo *root, Node *clause,
Oid opfamily, int strategy, bool nulls_first, Oid opfamily, CompareType cmptype, bool nulls_first,
Selectivity *leftstart, Selectivity *leftend, Selectivity *leftstart, Selectivity *leftend,
Selectivity *rightstart, Selectivity *rightend) Selectivity *rightstart, Selectivity *rightend)
{ {
@ -2966,6 +2966,7 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
*right; *right;
VariableStatData leftvar, VariableStatData leftvar,
rightvar; rightvar;
Oid opmethod;
int op_strategy; int op_strategy;
Oid op_lefttype; Oid op_lefttype;
Oid op_righttype; Oid op_righttype;
@ -2979,6 +2980,10 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
leop, leop,
revltop, revltop,
revleop; revleop;
StrategyNumber ltstrat,
lestrat,
gtstrat,
gestrat;
bool isgt; bool isgt;
Datum leftmin, Datum leftmin,
leftmax, leftmax,
@ -3005,12 +3010,14 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
examine_variable(root, left, 0, &leftvar); examine_variable(root, left, 0, &leftvar);
examine_variable(root, right, 0, &rightvar); examine_variable(root, right, 0, &rightvar);
opmethod = get_opfamily_method(opfamily);
/* Extract the operator's declared left/right datatypes */ /* Extract the operator's declared left/right datatypes */
get_op_opfamily_properties(opno, opfamily, false, get_op_opfamily_properties(opno, opfamily, false,
&op_strategy, &op_strategy,
&op_lefttype, &op_lefttype,
&op_righttype); &op_righttype);
Assert(op_strategy == BTEqualStrategyNumber); Assert(IndexAmTranslateStrategy(op_strategy, opmethod, opfamily, true) == COMPARE_EQ);
/* /*
* Look up the various operators we need. If we don't find them all, it * Look up the various operators we need. If we don't find them all, it
@ -3019,19 +3026,21 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
* Note: we expect that pg_statistic histograms will be sorted by the '<' * Note: we expect that pg_statistic histograms will be sorted by the '<'
* operator, regardless of which sort direction we are considering. * operator, regardless of which sort direction we are considering.
*/ */
switch (strategy) switch (cmptype)
{ {
case BTLessStrategyNumber: case COMPARE_LT:
isgt = false; isgt = false;
ltstrat = IndexAmTranslateCompareType(COMPARE_LT, opmethod, opfamily, true);
lestrat = IndexAmTranslateCompareType(COMPARE_LE, opmethod, opfamily, true);
if (op_lefttype == op_righttype) if (op_lefttype == op_righttype)
{ {
/* easy case */ /* easy case */
ltop = get_opfamily_member(opfamily, ltop = get_opfamily_member(opfamily,
op_lefttype, op_righttype, op_lefttype, op_righttype,
BTLessStrategyNumber); ltstrat);
leop = get_opfamily_member(opfamily, leop = get_opfamily_member(opfamily,
op_lefttype, op_righttype, op_lefttype, op_righttype,
BTLessEqualStrategyNumber); lestrat);
lsortop = ltop; lsortop = ltop;
rsortop = ltop; rsortop = ltop;
lstatop = lsortop; lstatop = lsortop;
@ -3043,43 +3052,46 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
{ {
ltop = get_opfamily_member(opfamily, ltop = get_opfamily_member(opfamily,
op_lefttype, op_righttype, op_lefttype, op_righttype,
BTLessStrategyNumber); ltstrat);
leop = get_opfamily_member(opfamily, leop = get_opfamily_member(opfamily,
op_lefttype, op_righttype, op_lefttype, op_righttype,
BTLessEqualStrategyNumber); lestrat);
lsortop = get_opfamily_member(opfamily, lsortop = get_opfamily_member(opfamily,
op_lefttype, op_lefttype, op_lefttype, op_lefttype,
BTLessStrategyNumber); ltstrat);
rsortop = get_opfamily_member(opfamily, rsortop = get_opfamily_member(opfamily,
op_righttype, op_righttype, op_righttype, op_righttype,
BTLessStrategyNumber); ltstrat);
lstatop = lsortop; lstatop = lsortop;
rstatop = rsortop; rstatop = rsortop;
revltop = get_opfamily_member(opfamily, revltop = get_opfamily_member(opfamily,
op_righttype, op_lefttype, op_righttype, op_lefttype,
BTLessStrategyNumber); ltstrat);
revleop = get_opfamily_member(opfamily, revleop = get_opfamily_member(opfamily,
op_righttype, op_lefttype, op_righttype, op_lefttype,
BTLessEqualStrategyNumber); lestrat);
} }
break; break;
case BTGreaterStrategyNumber: case COMPARE_GT:
/* descending-order case */ /* descending-order case */
isgt = true; isgt = true;
ltstrat = IndexAmTranslateCompareType(COMPARE_LT, opmethod, opfamily, true);
gtstrat = IndexAmTranslateCompareType(COMPARE_GT, opmethod, opfamily, true);
gestrat = IndexAmTranslateCompareType(COMPARE_GE, opmethod, opfamily, true);
if (op_lefttype == op_righttype) if (op_lefttype == op_righttype)
{ {
/* easy case */ /* easy case */
ltop = get_opfamily_member(opfamily, ltop = get_opfamily_member(opfamily,
op_lefttype, op_righttype, op_lefttype, op_righttype,
BTGreaterStrategyNumber); gtstrat);
leop = get_opfamily_member(opfamily, leop = get_opfamily_member(opfamily,
op_lefttype, op_righttype, op_lefttype, op_righttype,
BTGreaterEqualStrategyNumber); gestrat);
lsortop = ltop; lsortop = ltop;
rsortop = ltop; rsortop = ltop;
lstatop = get_opfamily_member(opfamily, lstatop = get_opfamily_member(opfamily,
op_lefttype, op_lefttype, op_lefttype, op_lefttype,
BTLessStrategyNumber); ltstrat);
rstatop = lstatop; rstatop = lstatop;
revltop = ltop; revltop = ltop;
revleop = leop; revleop = leop;
@ -3088,28 +3100,28 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
{ {
ltop = get_opfamily_member(opfamily, ltop = get_opfamily_member(opfamily,
op_lefttype, op_righttype, op_lefttype, op_righttype,
BTGreaterStrategyNumber); gtstrat);
leop = get_opfamily_member(opfamily, leop = get_opfamily_member(opfamily,
op_lefttype, op_righttype, op_lefttype, op_righttype,
BTGreaterEqualStrategyNumber); gestrat);
lsortop = get_opfamily_member(opfamily, lsortop = get_opfamily_member(opfamily,
op_lefttype, op_lefttype, op_lefttype, op_lefttype,
BTGreaterStrategyNumber); gtstrat);
rsortop = get_opfamily_member(opfamily, rsortop = get_opfamily_member(opfamily,
op_righttype, op_righttype, op_righttype, op_righttype,
BTGreaterStrategyNumber); gtstrat);
lstatop = get_opfamily_member(opfamily, lstatop = get_opfamily_member(opfamily,
op_lefttype, op_lefttype, op_lefttype, op_lefttype,
BTLessStrategyNumber); ltstrat);
rstatop = get_opfamily_member(opfamily, rstatop = get_opfamily_member(opfamily,
op_righttype, op_righttype, op_righttype, op_righttype,
BTLessStrategyNumber); ltstrat);
revltop = get_opfamily_member(opfamily, revltop = get_opfamily_member(opfamily,
op_righttype, op_lefttype, op_righttype, op_lefttype,
BTGreaterStrategyNumber); gtstrat);
revleop = get_opfamily_member(opfamily, revleop = get_opfamily_member(opfamily,
op_righttype, op_lefttype, op_righttype, op_lefttype,
BTGreaterEqualStrategyNumber); gestrat);
} }
break; break;
default: default:

View File

@ -206,11 +206,46 @@ get_opfamily_member_for_cmptype(Oid opfamily, Oid lefttype, Oid righttype,
return get_opfamily_member(opfamily, lefttype, righttype, strategy); return get_opfamily_member(opfamily, lefttype, righttype, strategy);
} }
/*
* get_opmethod_canorder
* Return amcanorder field for given index AM.
*
* To speed things up in the common cases, we're hardcoding the results from
* the built-in index types. Note that we also need to hardcode the negative
* results from the built-in non-btree index types, since you'll usually get a
* few hits for those as well. It would be nice to organize and cache this a
* bit differently to avoid the hardcoding.
*/
static bool
get_opmethod_canorder(Oid amoid)
{
switch (amoid)
{
case BTREE_AM_OID:
return true;
case HASH_AM_OID:
case GIST_AM_OID:
case GIN_AM_OID:
case SPGIST_AM_OID:
case BRIN_AM_OID:
return false;
default:
{
bool result;
IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
result = amroutine->amcanorder;
pfree(amroutine);
return result;
}
}
}
/* /*
* get_ordering_op_properties * get_ordering_op_properties
* Given the OID of an ordering operator (a btree "<" or ">" operator), * Given the OID of an ordering operator (a "<" or ">" operator),
* determine its opfamily, its declared input datatype, and its * determine its opfamily, its declared input datatype, and its
* strategy number (BTLessStrategyNumber or BTGreaterStrategyNumber). * comparison type.
* *
* Returns true if successful, false if no matching pg_amop entry exists. * Returns true if successful, false if no matching pg_amop entry exists.
* (This indicates that the operator is not a valid ordering operator.) * (This indicates that the operator is not a valid ordering operator.)
@ -228,7 +263,7 @@ get_opfamily_member_for_cmptype(Oid opfamily, Oid lefttype, Oid righttype,
*/ */
bool bool
get_ordering_op_properties(Oid opno, get_ordering_op_properties(Oid opno,
Oid *opfamily, Oid *opcintype, int16 *strategy) Oid *opfamily, Oid *opcintype, CompareType *cmptype)
{ {
bool result = false; bool result = false;
CatCList *catlist; CatCList *catlist;
@ -237,7 +272,7 @@ get_ordering_op_properties(Oid opno,
/* ensure outputs are initialized on failure */ /* ensure outputs are initialized on failure */
*opfamily = InvalidOid; *opfamily = InvalidOid;
*opcintype = InvalidOid; *opcintype = InvalidOid;
*strategy = 0; *cmptype = COMPARE_INVALID;
/* /*
* Search pg_amop to see if the target operator is registered as the "<" * Search pg_amop to see if the target operator is registered as the "<"
@ -249,13 +284,18 @@ get_ordering_op_properties(Oid opno,
{ {
HeapTuple tuple = &catlist->members[i]->tuple; HeapTuple tuple = &catlist->members[i]->tuple;
Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple); Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
CompareType am_cmptype;
/* must be btree */ /* must be ordering index */
if (aform->amopmethod != BTREE_AM_OID) if (!get_opmethod_canorder(aform->amopmethod))
continue; continue;
if (aform->amopstrategy == BTLessStrategyNumber || am_cmptype = IndexAmTranslateStrategy(aform->amopstrategy,
aform->amopstrategy == BTGreaterStrategyNumber) aform->amopmethod,
aform->amopfamily,
true);
if (am_cmptype == COMPARE_LT || am_cmptype == COMPARE_GT)
{ {
/* Found it ... should have consistent input types */ /* Found it ... should have consistent input types */
if (aform->amoplefttype == aform->amoprighttype) if (aform->amoplefttype == aform->amoprighttype)
@ -263,7 +303,7 @@ get_ordering_op_properties(Oid opno,
/* Found a suitable opfamily, return info */ /* Found a suitable opfamily, return info */
*opfamily = aform->amopfamily; *opfamily = aform->amopfamily;
*opcintype = aform->amoplefttype; *opcintype = aform->amoplefttype;
*strategy = aform->amopstrategy; *cmptype = am_cmptype;
result = true; result = true;
break; break;
} }
@ -277,7 +317,7 @@ get_ordering_op_properties(Oid opno,
/* /*
* get_equality_op_for_ordering_op * get_equality_op_for_ordering_op
* Get the OID of the datatype-specific btree equality operator * Get the OID of the datatype-specific equality operator
* associated with an ordering operator (a "<" or ">" operator). * associated with an ordering operator (a "<" or ">" operator).
* *
* If "reverse" isn't NULL, also set *reverse to false if the operator is "<", * If "reverse" isn't NULL, also set *reverse to false if the operator is "<",
@ -292,19 +332,19 @@ get_equality_op_for_ordering_op(Oid opno, bool *reverse)
Oid result = InvalidOid; Oid result = InvalidOid;
Oid opfamily; Oid opfamily;
Oid opcintype; Oid opcintype;
int16 strategy; CompareType cmptype;
/* Find the operator in pg_amop */ /* Find the operator in pg_amop */
if (get_ordering_op_properties(opno, if (get_ordering_op_properties(opno,
&opfamily, &opcintype, &strategy)) &opfamily, &opcintype, &cmptype))
{ {
/* Found a suitable opfamily, get matching equality operator */ /* Found a suitable opfamily, get matching equality operator */
result = get_opfamily_member(opfamily, result = get_opfamily_member_for_cmptype(opfamily,
opcintype, opcintype,
opcintype, opcintype,
BTEqualStrategyNumber); COMPARE_EQ);
if (reverse) if (reverse)
*reverse = (strategy == BTGreaterStrategyNumber); *reverse = (cmptype == COMPARE_GT);
} }
return result; return result;
@ -312,7 +352,7 @@ get_equality_op_for_ordering_op(Oid opno, bool *reverse)
/* /*
* get_ordering_op_for_equality_op * get_ordering_op_for_equality_op
* Get the OID of a datatype-specific btree "less than" ordering operator * Get the OID of a datatype-specific "less than" ordering operator
* associated with an equality operator. (If there are multiple * associated with an equality operator. (If there are multiple
* possibilities, assume any one will do.) * possibilities, assume any one will do.)
* *
@ -341,20 +381,25 @@ get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type)
{ {
HeapTuple tuple = &catlist->members[i]->tuple; HeapTuple tuple = &catlist->members[i]->tuple;
Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple); Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
CompareType cmptype;
/* must be btree */ /* must be ordering index */
if (aform->amopmethod != BTREE_AM_OID) if (!get_opmethod_canorder(aform->amopmethod))
continue; continue;
if (aform->amopstrategy == BTEqualStrategyNumber) cmptype = IndexAmTranslateStrategy(aform->amopstrategy,
aform->amopmethod,
aform->amopfamily,
true);
if (cmptype == COMPARE_EQ)
{ {
/* Found a suitable opfamily, get matching ordering operator */ /* Found a suitable opfamily, get matching ordering operator */
Oid typid; Oid typid;
typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype; typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype;
result = get_opfamily_member(aform->amopfamily, result = get_opfamily_member_for_cmptype(aform->amopfamily,
typid, typid, typid, typid,
BTLessStrategyNumber); COMPARE_LT);
if (OidIsValid(result)) if (OidIsValid(result))
break; break;
/* failure probably shouldn't happen, but keep looking if so */ /* failure probably shouldn't happen, but keep looking if so */
@ -369,7 +414,7 @@ get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type)
/* /*
* get_mergejoin_opfamilies * get_mergejoin_opfamilies
* Given a putatively mergejoinable operator, return a list of the OIDs * Given a putatively mergejoinable operator, return a list of the OIDs
* of the btree opfamilies in which it represents equality. * of the amcanorder opfamilies in which it represents equality.
* *
* It is possible (though at present unusual) for an operator to be equality * It is possible (though at present unusual) for an operator to be equality
* in more than one opfamily, hence the result is a list. This also lets us * in more than one opfamily, hence the result is a list. This also lets us
@ -394,7 +439,7 @@ get_mergejoin_opfamilies(Oid opno)
/* /*
* Search pg_amop to see if the target operator is registered as the "=" * Search pg_amop to see if the target operator is registered as the "="
* operator of any btree opfamily. * operator of any opfamily of an ordering index type.
*/ */
catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno)); catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
@ -403,9 +448,12 @@ get_mergejoin_opfamilies(Oid opno)
HeapTuple tuple = &catlist->members[i]->tuple; HeapTuple tuple = &catlist->members[i]->tuple;
Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple); Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
/* must be btree equality */ /* must be ordering index equality */
if (aform->amopmethod == BTREE_AM_OID && if (get_opmethod_canorder(aform->amopmethod) &&
aform->amopstrategy == BTEqualStrategyNumber) IndexAmTranslateStrategy(aform->amopstrategy,
aform->amopmethod,
aform->amopfamily,
true) == COMPARE_EQ)
result = lappend_oid(result, aform->amopfamily); result = lappend_oid(result, aform->amopfamily);
} }
@ -611,20 +659,20 @@ get_op_hash_functions(Oid opno,
} }
/* /*
* get_op_btree_interpretation * get_op_index_interpretation
* Given an operator's OID, find out which btree opfamilies it belongs to, * Given an operator's OID, find out which amcanorder opfamilies it belongs to,
* and what properties it has within each one. The results are returned * and what properties it has within each one. The results are returned
* as a palloc'd list of OpBtreeInterpretation structs. * as a palloc'd list of OpIndexInterpretation structs.
* *
* In addition to the normal btree operators, we consider a <> operator to be * In addition to the normal btree operators, we consider a <> operator to be
* a "member" of an opfamily if its negator is an equality operator of the * a "member" of an opfamily if its negator is an equality operator of the
* opfamily. COMPARE_NE is returned as the strategy number for this case. * opfamily. COMPARE_NE is returned as the strategy number for this case.
*/ */
List * List *
get_op_btree_interpretation(Oid opno) get_op_index_interpretation(Oid opno)
{ {
List *result = NIL; List *result = NIL;
OpBtreeInterpretation *thisresult; OpIndexInterpretation *thisresult;
CatCList *catlist; CatCList *catlist;
int i; int i;
@ -637,20 +685,26 @@ get_op_btree_interpretation(Oid opno)
{ {
HeapTuple op_tuple = &catlist->members[i]->tuple; HeapTuple op_tuple = &catlist->members[i]->tuple;
Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple); Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
StrategyNumber op_strategy; CompareType cmptype;
/* must be btree */ /* must be ordering index */
if (op_form->amopmethod != BTREE_AM_OID) if (!get_opmethod_canorder(op_form->amopmethod))
continue; continue;
/* Get the operator's btree strategy number */ /* Get the operator's comparision type */
op_strategy = (StrategyNumber) op_form->amopstrategy; cmptype = IndexAmTranslateStrategy(op_form->amopstrategy,
Assert(op_strategy >= 1 && op_strategy <= 5); op_form->amopmethod,
op_form->amopfamily,
true);
thisresult = (OpBtreeInterpretation *) /* should not happen */
palloc(sizeof(OpBtreeInterpretation)); if (cmptype == COMPARE_INVALID)
continue;
thisresult = (OpIndexInterpretation *)
palloc(sizeof(OpIndexInterpretation));
thisresult->opfamily_id = op_form->amopfamily; thisresult->opfamily_id = op_form->amopfamily;
thisresult->strategy = op_strategy; thisresult->cmptype = cmptype;
thisresult->oplefttype = op_form->amoplefttype; thisresult->oplefttype = op_form->amoplefttype;
thisresult->oprighttype = op_form->amoprighttype; thisresult->oprighttype = op_form->amoprighttype;
result = lappend(result, thisresult); result = lappend(result, thisresult);
@ -675,25 +729,28 @@ get_op_btree_interpretation(Oid opno)
{ {
HeapTuple op_tuple = &catlist->members[i]->tuple; HeapTuple op_tuple = &catlist->members[i]->tuple;
Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple); Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
StrategyNumber op_strategy; IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(op_form->amopmethod, false);
CompareType cmptype;
/* must be btree */ /* must be ordering index */
if (op_form->amopmethod != BTREE_AM_OID) if (!amroutine->amcanorder)
continue; continue;
/* Get the operator's btree strategy number */ /* Get the operator's comparision type */
op_strategy = (StrategyNumber) op_form->amopstrategy; cmptype = IndexAmTranslateStrategy(op_form->amopstrategy,
Assert(op_strategy >= 1 && op_strategy <= 5); op_form->amopmethod,
op_form->amopfamily,
true);
/* Only consider negators that are = */ /* Only consider negators that are = */
if (op_strategy != BTEqualStrategyNumber) if (cmptype != COMPARE_EQ)
continue; continue;
/* OK, report it with "strategy" COMPARE_NE */ /* OK, report it as COMPARE_NE */
thisresult = (OpBtreeInterpretation *) thisresult = (OpIndexInterpretation *)
palloc(sizeof(OpBtreeInterpretation)); palloc(sizeof(OpIndexInterpretation));
thisresult->opfamily_id = op_form->amopfamily; thisresult->opfamily_id = op_form->amopfamily;
thisresult->strategy = COMPARE_NE; thisresult->cmptype = COMPARE_NE;
thisresult->oplefttype = op_form->amoplefttype; thisresult->oplefttype = op_form->amoplefttype;
thisresult->oprighttype = op_form->amoprighttype; thisresult->oprighttype = op_form->amoprighttype;
result = lappend(result, thisresult); result = lappend(result, thisresult);

View File

@ -135,16 +135,16 @@ PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup)
{ {
Oid opfamily; Oid opfamily;
Oid opcintype; Oid opcintype;
int16 strategy; CompareType cmptype;
Assert(ssup->comparator == NULL); Assert(ssup->comparator == NULL);
/* Find the operator in pg_amop */ /* Find the operator in pg_amop */
if (!get_ordering_op_properties(orderingOp, &opfamily, &opcintype, if (!get_ordering_op_properties(orderingOp, &opfamily, &opcintype,
&strategy)) &cmptype))
elog(ERROR, "operator %u is not a valid ordering operator", elog(ERROR, "operator %u is not a valid ordering operator",
orderingOp); orderingOp);
ssup->ssup_reverse = (strategy == BTGreaterStrategyNumber); ssup->ssup_reverse = (cmptype == COMPARE_GT);
FinishSortSupportFunction(opfamily, opcintype, ssup); FinishSortSupportFunction(opfamily, opcintype, ssup);
} }

View File

@ -21,14 +21,14 @@
/* avoid including subscripting.h here */ /* avoid including subscripting.h here */
struct SubscriptRoutines; struct SubscriptRoutines;
/* Result list element for get_op_btree_interpretation */ /* Result list element for get_op_index_interpretation */
typedef struct OpBtreeInterpretation typedef struct OpIndexInterpretation
{ {
Oid opfamily_id; /* btree opfamily containing operator */ Oid opfamily_id; /* opfamily containing operator */
int strategy; /* its strategy number */ CompareType cmptype; /* its generic comparison type */
Oid oplefttype; /* declared left input datatype */ Oid oplefttype; /* declared left input datatype */
Oid oprighttype; /* declared right input datatype */ Oid oprighttype; /* declared right input datatype */
} OpBtreeInterpretation; } OpIndexInterpretation;
/* I/O function selector for get_type_io_data */ /* I/O function selector for get_type_io_data */
typedef enum IOFuncSelector typedef enum IOFuncSelector
@ -78,7 +78,7 @@ extern Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
extern Oid get_opfamily_member_for_cmptype(Oid opfamily, Oid lefttype, Oid righttype, extern Oid get_opfamily_member_for_cmptype(Oid opfamily, Oid lefttype, Oid righttype,
CompareType cmptype); CompareType cmptype);
extern bool get_ordering_op_properties(Oid opno, extern bool get_ordering_op_properties(Oid opno,
Oid *opfamily, Oid *opcintype, int16 *strategy); Oid *opfamily, Oid *opcintype, CompareType *cmptype);
extern Oid get_equality_op_for_ordering_op(Oid opno, bool *reverse); extern Oid get_equality_op_for_ordering_op(Oid opno, bool *reverse);
extern Oid get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type); extern Oid get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type);
extern List *get_mergejoin_opfamilies(Oid opno); extern List *get_mergejoin_opfamilies(Oid opno);
@ -86,7 +86,7 @@ extern bool get_compatible_hash_operators(Oid opno,
Oid *lhs_opno, Oid *rhs_opno); Oid *lhs_opno, Oid *rhs_opno);
extern bool get_op_hash_functions(Oid opno, extern bool get_op_hash_functions(Oid opno,
RegProcedure *lhs_procno, RegProcedure *rhs_procno); RegProcedure *lhs_procno, RegProcedure *rhs_procno);
extern List *get_op_btree_interpretation(Oid opno); extern List *get_op_index_interpretation(Oid opno);
extern bool equality_ops_are_compatible(Oid opno1, Oid opno2); extern bool equality_ops_are_compatible(Oid opno1, Oid opno2);
extern bool comparison_ops_are_compatible(Oid opno1, Oid opno2); extern bool comparison_ops_are_compatible(Oid opno1, Oid opno2);
extern Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, extern Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype,

View File

@ -210,7 +210,7 @@ extern Selectivity rowcomparesel(PlannerInfo *root,
int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo); int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo);
extern void mergejoinscansel(PlannerInfo *root, Node *clause, extern void mergejoinscansel(PlannerInfo *root, Node *clause,
Oid opfamily, int strategy, bool nulls_first, Oid opfamily, CompareType cmptype, bool nulls_first,
Selectivity *leftstart, Selectivity *leftend, Selectivity *leftstart, Selectivity *leftend,
Selectivity *rightstart, Selectivity *rightend); Selectivity *rightstart, Selectivity *rightend);

View File

@ -1794,11 +1794,11 @@ OnConflictAction
OnConflictClause OnConflictClause
OnConflictExpr OnConflictExpr
OnConflictSetState OnConflictSetState
OpBtreeInterpretation
OpClassCacheEnt OpClassCacheEnt
OpExpr OpExpr
OpFamilyMember OpFamilyMember
OpFamilyOpFuncGroup OpFamilyOpFuncGroup
OpIndexInterpretation
OpclassInfo OpclassInfo
Operator Operator
OperatorElement OperatorElement