Fix oversight in handling of row-comparison index keys: if the row comparison
authorTom Lane <[email protected]>
Sun, 7 Jan 2007 01:56:24 +0000 (01:56 +0000)
committerTom Lane <[email protected]>
Sun, 7 Jan 2007 01:56:24 +0000 (01:56 +0000)
doesn't exactly match the index, we may have to change our initial positioning
strategy.  For example, given an index on (f1,f2,f3) and a WHERE condition
"ROW(f1,f3) > ROW(2,3)", the code extracted the initial-positioning condition
"f1 > 2", which is wrong ... it has to be "f1 >= 2", else some rows matching
the WHERE condition may fail to be returned.

Applying patch to 8.2 only --- I'll fix it in HEAD later as part of the
planned index improvements (reverse-sort and NULLS FIRST/LAST work).

src/backend/access/nbtree/nbtsearch.c

index bb21b15ef336445648f57067013656f405bcd917..0ab51f9886813c86c407cb06e64a20a98bf3e7e2 100644 (file)
@@ -617,11 +617,12 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
                         * in the first row member makes the condition unmatchable, just
                         * like qual_ok = false.
                         */
-                       cur = (ScanKey) DatumGetPointer(cur->sk_argument);
-                       Assert(cur->sk_flags & SK_ROW_MEMBER);
-                       if (cur->sk_flags & SK_ISNULL)
+                       ScanKey         subkey = (ScanKey) DatumGetPointer(cur->sk_argument);
+
+                       Assert(subkey->sk_flags & SK_ROW_MEMBER);
+                       if (subkey->sk_flags & SK_ISNULL)
                                return false;
-                       memcpy(scankeys + i, cur, sizeof(ScanKeyData));
+                       memcpy(scankeys + i, subkey, sizeof(ScanKeyData));
 
                        /*
                         * If the row comparison is the last positioning key we accepted,
@@ -632,21 +633,46 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
                         * even if the row comparison is of ">" or "<" type, because the
                         * condition applied to all but the last row member is effectively
                         * ">=" or "<=", and so the extra keys don't break the positioning
-                        * scheme.
+                        * scheme.  But, by the same token, if we aren't able to use all
+                        * the row members, then the part of the row comparison that we
+                        * did use has to be treated as just a ">=" or "<=" condition,
+                        * and so we'd better adjust strat_total accordingly.
                         */
                        if (i == keysCount - 1)
                        {
-                               while (!(cur->sk_flags & SK_ROW_END))
+                               bool            used_all_subkeys = false;
+
+                               Assert(!(subkey->sk_flags & SK_ROW_END));
+                               for(;;)
                                {
-                                       cur++;
-                                       Assert(cur->sk_flags & SK_ROW_MEMBER);
-                                       if (cur->sk_attno != keysCount + 1)
+                                       subkey++;
+                                       Assert(subkey->sk_flags & SK_ROW_MEMBER);
+                                       if (subkey->sk_attno != keysCount + 1)
                                                break;  /* out-of-sequence, can't use it */
-                                       if (cur->sk_flags & SK_ISNULL)
+                                       if (subkey->sk_strategy != cur->sk_strategy)
+                                               break;  /* wrong direction, can't use it */
+                                       if (subkey->sk_flags & SK_ISNULL)
                                                break;  /* can't use null keys */
                                        Assert(keysCount < INDEX_MAX_KEYS);
-                                       memcpy(scankeys + keysCount, cur, sizeof(ScanKeyData));
+                                       memcpy(scankeys + keysCount, subkey, sizeof(ScanKeyData));
                                        keysCount++;
+                                       if (subkey->sk_flags & SK_ROW_END)
+                                       {
+                                               used_all_subkeys = true;
+                                               break;
+                                       }
+                               }
+                               if (!used_all_subkeys)
+                               {
+                                       switch (strat_total)
+                                       {
+                                               case BTLessStrategyNumber:
+                                                       strat_total = BTLessEqualStrategyNumber;
+                                                       break;
+                                               case BTGreaterStrategyNumber:
+                                                       strat_total = BTGreaterEqualStrategyNumber;
+                                                       break;
+                                       }
                                }
                                break;                  /* done with outer loop */
                        }