@@ -59,7 +59,7 @@ static bool _bt_check_compare(IndexScanDesc scan, ScanDirection dir,
5959 IndexTuple tuple , int tupnatts , TupleDesc tupdesc ,
6060 bool advancenonrequired , bool forcenonrequired ,
6161 bool * continuescan , int * ikey );
62- static bool _bt_check_rowcompare (ScanKey skey ,
62+ static bool _bt_check_rowcompare (ScanKey header ,
6363 IndexTuple tuple , int tupnatts , TupleDesc tupdesc ,
6464 ScanDirection dir , bool forcenonrequired , bool * continuescan );
6565static void _bt_checkkeys_look_ahead (IndexScanDesc scan , BTReadPageState * pstate ,
@@ -2911,19 +2911,64 @@ _bt_check_compare(IndexScanDesc scan, ScanDirection dir,
29112911 * it's not possible for any future tuples in the current scan direction
29122912 * to pass the qual.
29132913 *
2914- * This is a subroutine for _bt_checkkeys/_bt_check_compare.
2914+ * This is a subroutine for _bt_checkkeys/_bt_check_compare. Caller passes us
2915+ * a row compare header key taken from so->keyData[].
2916+ *
2917+ * Row value comparisons can be described in terms of logical expansions that
2918+ * use only scalar operators. Consider the following example row comparison:
2919+ *
2920+ * "(a, b, c) > (7, 'bar', 62)"
2921+ *
2922+ * This can be evaluated as:
2923+ *
2924+ * "(a = 7 AND b = 'bar' AND c > 62) OR (a = 7 AND b > 'bar') OR (a > 7)".
2925+ *
2926+ * Notice that this condition is satisfied by _all_ rows that satisfy "a > 7",
2927+ * and by a subset of all rows that satisfy "a >= 7" (possibly all such rows).
2928+ * It _can't_ be satisfied by other rows (where "a < 7" or where "a IS NULL").
2929+ * A row comparison header key can therefore often be treated as if it was a
2930+ * simple scalar inequality on the row compare's most significant column.
2931+ * (For example, _bt_advance_array_keys and most preprocessing routines treat
2932+ * row compares like any other same-strategy inequality on the same column.)
2933+ *
2934+ * Things get more complicated for our row compare given a row where "a = 7".
2935+ * Note that a row compare isn't necessarily satisfied by _every_ tuple that
2936+ * appears between the first and last satisfied tuple returned by the scan,
2937+ * due to the way that its lower-order subkeys are only conditionally applied.
2938+ * A forwards scan that uses our example qual might initially return a tuple
2939+ * "(a, b, c) = (7, 'zebra', 54)". But it won't subsequently return a tuple
2940+ * "(a, b, c) = (7, NULL, 1)" located to the right of the first matching tuple
2941+ * (assume that "b" was declared NULLS LAST here). The scan will only return
2942+ * additional matches upon reaching tuples where "a > 7". If you rereview our
2943+ * example row comparison's logical expansion, you'll understand why this is.
2944+ * (Here we assume that all subkeys could be marked required, guaranteeing
2945+ * that row comparison order matches index order. This is the common case.)
2946+ *
2947+ * Note that a row comparison header key behaves _exactly_ the same as a
2948+ * similar scalar inequality key on the row's most significant column once the
2949+ * scan reaches the point where it no longer needs to evaluate lower-order
2950+ * subkeys (or before the point where it starts needing to evaluate them).
2951+ * For example, once a forwards scan that uses our example qual reaches the
2952+ * first tuple "a > 7", we'll behave in just the same way as our caller would
2953+ * behave with a similar scalar inequality "a > 7" for the remainder of the
2954+ * scan (assuming that the scan never changes direction/never goes backwards).
2955+ * We'll even set continuescan=false according to exactly the same rules as
2956+ * the ones our caller applies with simple scalar inequalities, including the
2957+ * rules it applies when NULL tuple values don't satisfy an inequality qual.
29152958 */
29162959static bool
2917- _bt_check_rowcompare (ScanKey skey , IndexTuple tuple , int tupnatts ,
2960+ _bt_check_rowcompare (ScanKey header , IndexTuple tuple , int tupnatts ,
29182961 TupleDesc tupdesc , ScanDirection dir ,
29192962 bool forcenonrequired , bool * continuescan )
29202963{
2921- ScanKey subkey = (ScanKey ) DatumGetPointer (skey -> sk_argument );
2964+ ScanKey subkey = (ScanKey ) DatumGetPointer (header -> sk_argument );
29222965 int32 cmpresult = 0 ;
29232966 bool result ;
29242967
29252968 /* First subkey should be same as the header says */
2926- Assert (subkey -> sk_attno == skey -> sk_attno );
2969+ Assert (header -> sk_flags & SK_ROW_HEADER );
2970+ Assert (subkey -> sk_attno == header -> sk_attno );
2971+ Assert (subkey -> sk_strategy == header -> sk_strategy );
29272972
29282973 /* Loop over columns of the row condition */
29292974 for (;;)
@@ -2943,7 +2988,7 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, int tupnatts,
29432988 * columns are required for the scan direction, we can stop the
29442989 * scan, because there can't be another tuple that will succeed.
29452990 */
2946- Assert (subkey != (ScanKey ) DatumGetPointer (skey -> sk_argument ));
2991+ Assert (subkey != (ScanKey ) DatumGetPointer (header -> sk_argument ));
29472992 subkey -- ;
29482993 if (forcenonrequired )
29492994 {
@@ -3014,7 +3059,7 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, int tupnatts,
30143059 * can only happen with an "a" NULL some time after the scan
30153060 * completely stops needing to use its "b" and "c" members.)
30163061 */
3017- if (subkey == (ScanKey ) DatumGetPointer (skey -> sk_argument ))
3062+ if (subkey == (ScanKey ) DatumGetPointer (header -> sk_argument ))
30183063 reqflags |= SK_BT_REQFWD ; /* safe, first row member */
30193064
30203065 if ((subkey -> sk_flags & reqflags ) &&
@@ -3052,7 +3097,7 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, int tupnatts,
30523097 * happen with an "a" NULL some time after the scan completely
30533098 * stops needing to use its "b" and "c" members.)
30543099 */
3055- if (subkey == (ScanKey ) DatumGetPointer (skey -> sk_argument ))
3100+ if (subkey == (ScanKey ) DatumGetPointer (header -> sk_argument ))
30563101 reqflags |= SK_BT_REQBKWD ; /* safe, first row member */
30573102
30583103 if ((subkey -> sk_flags & reqflags ) &&
0 commit comments