Fix CLUSTER tuplesorts on abbreviated expressions.
authorPeter Geoghegan <[email protected]>
Thu, 21 Apr 2022 00:17:43 +0000 (17:17 -0700)
committerPeter Geoghegan <[email protected]>
Thu, 21 Apr 2022 00:17:43 +0000 (17:17 -0700)
CLUSTER sort won't use the datum1 SortTuple field when clustering
against an index whose leading key is an expression.  This makes it
unsafe to use the abbreviated keys optimization, which was missed by the
logic that sets up SortSupport state.  Affected tuplesorts output tuples
in a completely bogus order as a result (the wrong SortSupport based
comparator was used for the leading attribute).

This issue is similar to the bug fixed on the master branch by recent
commit cc58eecc5d.  But it's a far older issue, that dates back to the
introduction of the abbreviated keys optimization by commit 4ea51cdfe8.

Backpatch to all supported versions.

Author: Peter Geoghegan <[email protected]>
Author: Thomas Munro <[email protected]>
Discussion: https://postgr.es/m/CA+hUKG+bA+bmwD36_oDxAoLrCwZjVtST2fqe=b4=qZcmU7u89A@mail.gmail.com
Backpatch: 10-

src/backend/utils/sort/tuplesort.c
src/test/regress/expected/cluster.out
src/test/regress/sql/cluster.sql

index 1174e1a31c9605df3c40c78a349121445cfa7de0..10e89e520878101a32bce8d56d5549654bc52306 100644 (file)
@@ -1051,7 +1051,7 @@ tuplesort_begin_heap(TupleDesc tupDesc,
        sortKey->ssup_nulls_first = nullsFirstFlags[i];
        sortKey->ssup_attno = attNums[i];
        /* Convey if abbreviation optimization is applicable in principle */
-       sortKey->abbreviate = (i == 0);
+       sortKey->abbreviate = (i == 0 && state->haveDatum1);
 
        PrepareSortSupportFromOrderingOp(sortOperators[i], sortKey);
    }
@@ -1157,7 +1157,7 @@ tuplesort_begin_cluster(TupleDesc tupDesc,
            (scanKey->sk_flags & SK_BT_NULLS_FIRST) != 0;
        sortKey->ssup_attno = scanKey->sk_attno;
        /* Convey if abbreviation optimization is applicable in principle */
-       sortKey->abbreviate = (i == 0);
+       sortKey->abbreviate = (i == 0 && state->haveDatum1);
 
        AssertState(sortKey->ssup_attno != 0);
 
@@ -1238,7 +1238,7 @@ tuplesort_begin_index_btree(Relation heapRel,
            (scanKey->sk_flags & SK_BT_NULLS_FIRST) != 0;
        sortKey->ssup_attno = scanKey->sk_attno;
        /* Convey if abbreviation optimization is applicable in principle */
-       sortKey->abbreviate = (i == 0);
+       sortKey->abbreviate = (i == 0 && state->haveDatum1);
 
        AssertState(sortKey->ssup_attno != 0);
 
@@ -1348,7 +1348,7 @@ tuplesort_begin_index_gist(Relation heapRel,
        sortKey->ssup_nulls_first = false;
        sortKey->ssup_attno = i + 1;
        /* Convey if abbreviation optimization is applicable in principle */
-       sortKey->abbreviate = (i == 0);
+       sortKey->abbreviate = (i == 0 && state->haveDatum1);
 
        AssertState(sortKey->ssup_attno != 0);
 
index 6c8c4c929abdaafdea43fab2cba3d38f50adccf3..542c2e098c5b1d32826cada761f3dc584cfe20f3 100644 (file)
@@ -577,6 +577,13 @@ SELECT * FROM clstr_expression WHERE -a = -3 ORDER BY -a, b;
 COMMIT;
 -- and after clustering on clstr_expression_minus_a
 CLUSTER clstr_expression USING clstr_expression_minus_a;
+WITH rows AS
+  (SELECT ctid, lag(a) OVER (ORDER BY ctid) AS la, a FROM clstr_expression)
+SELECT * FROM rows WHERE la < a;
+ ctid | la | a 
+------+----+---
+(0 rows)
+
 BEGIN;
 SET LOCAL enable_seqscan = false;
 EXPLAIN (COSTS OFF) SELECT * FROM clstr_expression WHERE upper(b) = 'PREFIX3';
@@ -611,6 +618,13 @@ SELECT * FROM clstr_expression WHERE -a = -3 ORDER BY -a, b;
 COMMIT;
 -- and after clustering on clstr_expression_upper_b
 CLUSTER clstr_expression USING clstr_expression_upper_b;
+WITH rows AS
+  (SELECT ctid, lag(b) OVER (ORDER BY ctid) AS lb, b FROM clstr_expression)
+SELECT * FROM rows WHERE upper(lb) > upper(b);
+ ctid | lb | b 
+------+----+---
+(0 rows)
+
 BEGIN;
 SET LOCAL enable_seqscan = false;
 EXPLAIN (COSTS OFF) SELECT * FROM clstr_expression WHERE upper(b) = 'PREFIX3';
index e758088ef6a6e0b7f916de66b46495b099e209ed..6cb9c926c06eb4e9f7eff611b3a73ac778066731 100644 (file)
@@ -284,6 +284,9 @@ COMMIT;
 
 -- and after clustering on clstr_expression_minus_a
 CLUSTER clstr_expression USING clstr_expression_minus_a;
+WITH rows AS
+  (SELECT ctid, lag(a) OVER (ORDER BY ctid) AS la, a FROM clstr_expression)
+SELECT * FROM rows WHERE la < a;
 BEGIN;
 SET LOCAL enable_seqscan = false;
 EXPLAIN (COSTS OFF) SELECT * FROM clstr_expression WHERE upper(b) = 'PREFIX3';
@@ -294,6 +297,9 @@ COMMIT;
 
 -- and after clustering on clstr_expression_upper_b
 CLUSTER clstr_expression USING clstr_expression_upper_b;
+WITH rows AS
+  (SELECT ctid, lag(b) OVER (ORDER BY ctid) AS lb, b FROM clstr_expression)
+SELECT * FROM rows WHERE upper(lb) > upper(b);
 BEGIN;
 SET LOCAL enable_seqscan = false;
 EXPLAIN (COSTS OFF) SELECT * FROM clstr_expression WHERE upper(b) = 'PREFIX3';