/* * Mark a scankey with info from the index's indoption array. * * We copy the appropriate indoption value into the scankey sk_flags * (shifting to avoid clobbering system-defined flag bits). Also, if * the DESC option is set, commute (flip) the operator strategy number. * * This function is applied to the *input* scankey structure; therefore * on a rescan we will be looking at already-processed scankeys. Hence * we have to be careful not to re-commute the strategy if we already did it. * It's a bit ugly to modify the caller's copy of the scankey but in practice * there shouldn't be any problem, since the index's indoptions are certainly * not going to change while the scankey survives. */ static void _bt_mark_scankey_with_indoption(ScanKey skey, int16 *indoption) { int addflags; addflags = indoption[skey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT; if ((addflags & SK_BT_DESC) && !(skey->sk_flags & SK_BT_DESC)) skey->sk_strategy = BTCommuteStrategyNumber(skey->sk_strategy); skey->sk_flags |= addflags; if (skey->sk_flags & SK_ROW_HEADER) { ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument); for (;;) { Assert(subkey->sk_flags & SK_ROW_MEMBER); addflags = indoption[subkey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT; if ((addflags & SK_BT_DESC) && !(subkey->sk_flags & SK_BT_DESC)) subkey->sk_strategy = BTCommuteStrategyNumber(subkey->sk_strategy); subkey->sk_flags |= addflags; if (subkey->sk_flags & SK_ROW_END) break; subkey++; } } }
/* * Adjust a scankey's strategy and flags setting as needed for indoptions. * * We copy the appropriate indoption value into the scankey sk_flags * (shifting to avoid clobbering system-defined flag bits). Also, if * the DESC option is set, commute (flip) the operator strategy number. * * A secondary purpose is to check for IS NULL/NOT NULL scankeys and set up * the strategy field correctly for them. * * Lastly, for ordinary scankeys (not IS NULL/NOT NULL), we check for a * NULL comparison value. Since all btree operators are assumed strict, * a NULL means that the qual cannot be satisfied. We return TRUE if the * comparison value isn't NULL, or FALSE if the scan should be abandoned. * * This function is applied to the *input* scankey structure; therefore * on a rescan we will be looking at already-processed scankeys. Hence * we have to be careful not to re-commute the strategy if we already did it. * It's a bit ugly to modify the caller's copy of the scankey but in practice * there shouldn't be any problem, since the index's indoptions are certainly * not going to change while the scankey survives. */ static bool _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption) { int addflags; addflags = indoption[skey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT; /* * We treat all btree operators as strict (even if they're not so marked * in pg_proc). This means that it is impossible for an operator condition * with a NULL comparison constant to succeed, and we can reject it right * away. * * However, we now also support "x IS NULL" clauses as search conditions, * so in that case keep going. The planner has not filled in any * particular strategy in this case, so set it to BTEqualStrategyNumber * --- we can treat IS NULL as an equality operator for purposes of search * strategy. * * Likewise, "x IS NOT NULL" is supported. We treat that as either "less * than NULL" in a NULLS LAST index, or "greater than NULL" in a NULLS * FIRST index. * * Note: someday we might have to fill in sk_collation from the index * column's collation. At the moment this is a non-issue because we'll * never actually call the comparison operator on a NULL. */ if (skey->sk_flags & SK_ISNULL) { /* SK_ISNULL shouldn't be set in a row header scankey */ Assert(!(skey->sk_flags & SK_ROW_HEADER)); /* Set indoption flags in scankey (might be done already) */ skey->sk_flags |= addflags; /* Set correct strategy for IS NULL or NOT NULL search */ if (skey->sk_flags & SK_SEARCHNULL) { skey->sk_strategy = BTEqualStrategyNumber; skey->sk_subtype = InvalidOid; skey->sk_collation = InvalidOid; } else if (skey->sk_flags & SK_SEARCHNOTNULL) { if (skey->sk_flags & SK_BT_NULLS_FIRST) skey->sk_strategy = BTGreaterStrategyNumber; else skey->sk_strategy = BTLessStrategyNumber; skey->sk_subtype = InvalidOid; skey->sk_collation = InvalidOid; } else { /* regular qual, so it cannot be satisfied */ return false; } /* Needn't do the rest */ return true; } /* Adjust strategy for DESC, if we didn't already */ if ((addflags & SK_BT_DESC) && !(skey->sk_flags & SK_BT_DESC)) skey->sk_strategy = BTCommuteStrategyNumber(skey->sk_strategy); skey->sk_flags |= addflags; /* If it's a row header, fix row member flags and strategies similarly */ if (skey->sk_flags & SK_ROW_HEADER) { ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument); for (;;) { Assert(subkey->sk_flags & SK_ROW_MEMBER); addflags = indoption[subkey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT; if ((addflags & SK_BT_DESC) && !(subkey->sk_flags & SK_BT_DESC)) subkey->sk_strategy = BTCommuteStrategyNumber(subkey->sk_strategy); subkey->sk_flags |= addflags; if (subkey->sk_flags & SK_ROW_END) break; subkey++; } } return true; }
/* * Compare two scankey values using a specified operator. Both values * must be already known non-NULL. * * The test we want to perform is logically "leftarg op rightarg", where * leftarg and rightarg are the sk_argument values in those ScanKeys, and * the comparison operator is the one in the op ScanKey. However, in * cross-data-type situations we may need to look up the correct operator in * the index's opfamily: it is the one having amopstrategy = op->sk_strategy * and amoplefttype/amoprighttype equal to the two argument datatypes. * * If the opfamily doesn't supply a complete set of cross-type operators we * may not be able to make the comparison. If we can make the comparison * we store the operator result in *result and return TRUE. We return FALSE * if the comparison could not be made. * * Note: op always points at the same ScanKey as either leftarg or rightarg. * Since we don't scribble on the scankeys, this aliasing should cause no * trouble. * * Note: this routine needs to be insensitive to any DESC option applied * to the index column. For example, "x < 4" is a tighter constraint than * "x < 5" regardless of which way the index is sorted. We don't worry about * NULLS FIRST/LAST either, since the given values are never nulls. */ static bool _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op, ScanKey leftarg, ScanKey rightarg, bool *result) { Relation rel = scan->indexRelation; Oid lefttype, righttype, optype, opcintype, cmp_op; StrategyNumber strat; /* * The opfamily we need to worry about is identified by the index column. */ Assert(leftarg->sk_attno == rightarg->sk_attno); opcintype = rel->rd_opcintype[leftarg->sk_attno - 1]; /* * Determine the actual datatypes of the ScanKey arguments. We have to * support the convention that sk_subtype == InvalidOid means the opclass * input type; this is a hack to simplify life for ScanKeyInit(). */ lefttype = leftarg->sk_subtype; if (lefttype == InvalidOid) lefttype = opcintype; righttype = rightarg->sk_subtype; if (righttype == InvalidOid) righttype = opcintype; optype = op->sk_subtype; if (optype == InvalidOid) optype = opcintype; /* * If leftarg and rightarg match the types expected for the "op" scankey, * we can use its already-looked-up comparison function. */ if (lefttype == opcintype && righttype == optype) { *result = DatumGetBool(FunctionCall2(&op->sk_func, leftarg->sk_argument, rightarg->sk_argument)); return true; } /* * Otherwise, we need to go to the syscache to find the appropriate * operator. (This cannot result in infinite recursion, since no * indexscan initiated by syscache lookup will use cross-data-type * operators.) * * If the sk_strategy was flipped by _bt_mark_scankey_with_indoption, we * have to un-flip it to get the correct opfamily member. */ strat = op->sk_strategy; if (op->sk_flags & SK_BT_DESC) strat = BTCommuteStrategyNumber(strat); cmp_op = get_opfamily_member(rel->rd_opfamily[leftarg->sk_attno - 1], lefttype, righttype, strat); if (OidIsValid(cmp_op)) { RegProcedure cmp_proc = get_opcode(cmp_op); if (RegProcedureIsValid(cmp_proc)) { *result = DatumGetBool(OidFunctionCall2(cmp_proc, leftarg->sk_argument, rightarg->sk_argument)); return true; } } /* Can't make the comparison */ *result = false; /* suppress compiler warnings */ return false; }
/* * Compare two scankey values using a specified operator. * * The test we want to perform is logically "leftarg op rightarg", where * leftarg and rightarg are the sk_argument values in those ScanKeys, and * the comparison operator is the one in the op ScanKey. However, in * cross-data-type situations we may need to look up the correct operator in * the index's opfamily: it is the one having amopstrategy = op->sk_strategy * and amoplefttype/amoprighttype equal to the two argument datatypes. * * If the opfamily doesn't supply a complete set of cross-type operators we * may not be able to make the comparison. If we can make the comparison * we store the operator result in *result and return TRUE. We return FALSE * if the comparison could not be made. * * Note: op always points at the same ScanKey as either leftarg or rightarg. * Since we don't scribble on the scankeys, this aliasing should cause no * trouble. * * Note: this routine needs to be insensitive to any DESC option applied * to the index column. For example, "x < 4" is a tighter constraint than * "x < 5" regardless of which way the index is sorted. */ static bool _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op, ScanKey leftarg, ScanKey rightarg, bool *result) { Relation rel = scan->indexRelation; Oid lefttype, righttype, optype, opcintype, cmp_op; StrategyNumber strat; /* * First, deal with cases where one or both args are NULL. This should * only happen when the scankeys represent IS NULL/NOT NULL conditions. */ if ((leftarg->sk_flags | rightarg->sk_flags) & SK_ISNULL) { bool leftnull, rightnull; if (leftarg->sk_flags & SK_ISNULL) { Assert(leftarg->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL)); leftnull = true; } else leftnull = false; if (rightarg->sk_flags & SK_ISNULL) { Assert(rightarg->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL)); rightnull = true; } else rightnull = false; /* * We treat NULL as either greater than or less than all other values. * Since true > false, the tests below work correctly for NULLS LAST * logic. If the index is NULLS FIRST, we need to flip the strategy. */ strat = op->sk_strategy; if (op->sk_flags & SK_BT_NULLS_FIRST) strat = BTCommuteStrategyNumber(strat); switch (strat) { case BTLessStrategyNumber: *result = (leftnull < rightnull); break; case BTLessEqualStrategyNumber: *result = (leftnull <= rightnull); break; case BTEqualStrategyNumber: *result = (leftnull == rightnull); break; case BTGreaterEqualStrategyNumber: *result = (leftnull >= rightnull); break; case BTGreaterStrategyNumber: *result = (leftnull > rightnull); break; default: elog(ERROR, "unrecognized StrategyNumber: %d", (int) strat); *result = false; /* keep compiler quiet */ break; } return true; } /* * The opfamily we need to worry about is identified by the index column. */ Assert(leftarg->sk_attno == rightarg->sk_attno); opcintype = rel->rd_opcintype[leftarg->sk_attno - 1]; /* * Determine the actual datatypes of the ScanKey arguments. We have to * support the convention that sk_subtype == InvalidOid means the opclass * input type; this is a hack to simplify life for ScanKeyInit(). */ lefttype = leftarg->sk_subtype; if (lefttype == InvalidOid) lefttype = opcintype; righttype = rightarg->sk_subtype; if (righttype == InvalidOid) righttype = opcintype; optype = op->sk_subtype; if (optype == InvalidOid) optype = opcintype; /* * If leftarg and rightarg match the types expected for the "op" scankey, * we can use its already-looked-up comparison function. */ if (lefttype == opcintype && righttype == optype) { *result = DatumGetBool(FunctionCall2Coll(&op->sk_func, op->sk_collation, leftarg->sk_argument, rightarg->sk_argument)); return true; } /* * Otherwise, we need to go to the syscache to find the appropriate * operator. (This cannot result in infinite recursion, since no * indexscan initiated by syscache lookup will use cross-data-type * operators.) * * If the sk_strategy was flipped by _bt_fix_scankey_strategy, we have to * un-flip it to get the correct opfamily member. */ strat = op->sk_strategy; if (op->sk_flags & SK_BT_DESC) strat = BTCommuteStrategyNumber(strat); cmp_op = get_opfamily_member(rel->rd_opfamily[leftarg->sk_attno - 1], lefttype, righttype, strat); if (OidIsValid(cmp_op)) { RegProcedure cmp_proc = get_opcode(cmp_op); if (RegProcedureIsValid(cmp_proc)) { *result = DatumGetBool(OidFunctionCall2Coll(cmp_proc, op->sk_collation, leftarg->sk_argument, rightarg->sk_argument)); return true; } } /* Can't make the comparison */ *result = false; /* suppress compiler warnings */ return false; }