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);
* 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)
{
return true;
}
+/*
+ * 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.
*
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)
{