diff options
Diffstat (limited to 'src/backend/access/nbtree/nbtutils.c')
-rw-r--r-- | src/backend/access/nbtree/nbtutils.c | 166 |
1 files changed, 134 insertions, 32 deletions
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index edfea2acaff..41b4fbd1c37 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -62,6 +62,7 @@ static bool _bt_check_compare(IndexScanDesc scan, ScanDirection dir, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, bool advancenonrequired, bool forcenonrequired, bool *continuescan, int *ikey); +static bool _bt_rowcompare_cmpresult(ScanKey subkey, int cmpresult); static bool _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, ScanDirection dir, bool forcenonrequired, bool *continuescan); @@ -2438,15 +2439,103 @@ _bt_set_startikey(IndexScanDesc scan, BTReadPageState *pstate) * Determine if it's safe to set pstate.startikey to an offset to a * key that comes after this key, by examining this key */ - if (!(key->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD))) - { - /* Scan key isn't marked required (corner case) */ - break; /* unsafe */ - } if (key->sk_flags & SK_ROW_HEADER) { - /* RowCompare inequalities currently aren't supported */ - break; /* "unsafe" */ + /* RowCompare inequality (header key) */ + ScanKey subkey = (ScanKey) DatumGetPointer(key->sk_argument); + bool satisfied = false; + + for (;;) + { + int cmpresult; + bool firstsatisfies = false; + + if (subkey->sk_attno > firstchangingattnum) /* >, not >= */ + break; /* unsafe, preceding attr has multiple + * distinct values */ + + if (subkey->sk_flags & SK_ISNULL) + break; /* unsafe, unsatisfiable NULL subkey arg */ + + firstdatum = index_getattr(firsttup, subkey->sk_attno, + tupdesc, &firstnull); + lastdatum = index_getattr(lasttup, subkey->sk_attno, + tupdesc, &lastnull); + + if (firstnull || lastnull) + break; /* unsafe, NULL value won't satisfy subkey */ + + /* + * Compare the first tuple's datum for this row compare member + */ + cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func, + subkey->sk_collation, + firstdatum, + subkey->sk_argument)); + if (subkey->sk_flags & SK_BT_DESC) + INVERT_COMPARE_RESULT(cmpresult); + + if (cmpresult != 0 || (subkey->sk_flags & SK_ROW_END)) + { + firstsatisfies = _bt_rowcompare_cmpresult(subkey, + cmpresult); + if (!firstsatisfies) + { + /* Unsafe, firstdatum does not satisfy subkey */ + break; + } + } + + /* + * Compare the last tuple's datum for this row compare member + */ + cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func, + subkey->sk_collation, + lastdatum, + subkey->sk_argument)); + if (subkey->sk_flags & SK_BT_DESC) + INVERT_COMPARE_RESULT(cmpresult); + + if (cmpresult != 0 || (subkey->sk_flags & SK_ROW_END)) + { + if (!firstsatisfies) + { + /* + * It's only safe to set startikey beyond the row + * compare header key when both firsttup and lasttup + * satisfy the key as a whole based on the same + * deciding subkey/attribute. That can't happen now. + */ + break; /* unsafe */ + } + + satisfied = _bt_rowcompare_cmpresult(subkey, cmpresult); + break; /* safe iff 'satisfied' is true */ + } + + /* Move on to next row member/subkey */ + if (subkey->sk_flags & SK_ROW_END) + break; /* defensive */ + subkey++; + + /* + * We deliberately don't check if the next subkey has the same + * strategy as this iteration's subkey (which happens when + * subkeys for both ASC and DESC columns are used together), + * nor if any subkey is marked required. This is safe because + * in general all prior index attributes must have only one + * distinct value (across all of the tuples on the page) in + * order for us to even consider any subkey's attribute. + */ + } + + if (satisfied) + { + /* Safe, row compare satisfied by every tuple on page */ + continue; + } + + break; /* unsafe */ } if (key->sk_strategy != BTEqualStrategyNumber) { @@ -2915,6 +3004,42 @@ _bt_check_compare(IndexScanDesc scan, ScanDirection dir, } /* + * Call here when a row compare member returns a non-zero result, or with the + * result for the final ROW_END row compare member (no matter the cmpresult). + * + * cmpresult indicates the overall result of the row comparison (must already + * be commuted for DESC subkeys), and subkey is the deciding row member. + */ +static bool +_bt_rowcompare_cmpresult(ScanKey subkey, int cmpresult) +{ + bool satisfied; + + switch (subkey->sk_strategy) + { + case BTLessStrategyNumber: + satisfied = (cmpresult < 0); + break; + case BTLessEqualStrategyNumber: + satisfied = (cmpresult <= 0); + break; + case BTGreaterEqualStrategyNumber: + satisfied = (cmpresult >= 0); + break; + case BTGreaterStrategyNumber: + satisfied = (cmpresult > 0); + break; + default: + /* EQ and NE cases aren't allowed here */ + elog(ERROR, "unexpected strategy number %d", subkey->sk_strategy); + satisfied = false; /* keep compiler quiet */ + break; + } + + return satisfied; +} + +/* * Test whether an indextuple satisfies a row-comparison scan condition. * * Return true if so, false if not. If not, also clear *continuescan if @@ -3094,31 +3219,8 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, int tupnatts, subkey++; } - /* - * At this point cmpresult indicates the overall result of the row - * comparison, and subkey points to the deciding column (or the last - * column if the result is "="). - */ - switch (subkey->sk_strategy) - { - /* EQ and NE cases aren't allowed here */ - case BTLessStrategyNumber: - result = (cmpresult < 0); - break; - case BTLessEqualStrategyNumber: - result = (cmpresult <= 0); - break; - case BTGreaterEqualStrategyNumber: - result = (cmpresult >= 0); - break; - case BTGreaterStrategyNumber: - result = (cmpresult > 0); - break; - default: - elog(ERROR, "unexpected strategy number %d", subkey->sk_strategy); - result = 0; /* keep compiler quiet */ - break; - } + /* Final subkey/column determines if row compare is satisfied */ + result = _bt_rowcompare_cmpresult(subkey, cmpresult); if (!result && !forcenonrequired) { |