]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Avoid collation lookup failure when considering a "char" column.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 28 Jun 2026 16:31:29 +0000 (12:31 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 28 Jun 2026 16:31:29 +0000 (12:31 -0400)
If a "char" column has a statistics histogram, scalarineqsel()
would fail with "cache lookup failed for collation 0".  Avoid
the failing lookup by acting as though the collation is "C".

Prior to commit 06421b084, this code didn't fail because
lc_collate_is_c() intentionally didn't spit up on InvalidOid.
It did act differently though: it would take the non-C-collation
code path and hence apply strxfrm using libc's prevailing locale.
But that seems like the wrong thing for a non-collatable comparison,
so let's not resurrect that aspect.

Author: Feng Wu <wufengwufengwufeng@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CACK3muq6s-O1Wc3w4dRL1Fe8YQ-Fz1zJbezeQwhuLgNxGNEFiA@mail.gmail.com
Backpatch-through: 18

src/backend/utils/adt/selfuncs.c
src/test/regress/expected/planner_est.out
src/test/regress/sql/planner_est.sql

index d6efd07073a1e6e4a7dc827fc5aa6b98d7de2a17..cbc70fde7168ef162baec8c61774ab695cea9909 100644 (file)
@@ -5295,6 +5295,14 @@ convert_string_datum(Datum value, Oid typid, Oid collid, bool *failure)
                        return NULL;
        }
 
+       /*
+        * If we don't have a collation, act as though it's "C".  This would
+        * normally happen only for the "char" type, but perhaps there are other
+        * cases.
+        */
+       if (!OidIsValid(collid))
+               return val;
+
        mylocale = pg_newlocale_from_collation(collid);
 
        if (!mylocale->collate_is_c)
index b62a47552fad4e91bd33b267fedea580b4a53f89..236cb274a78d925f5ab67cb69a1a2f0321b83542 100644 (file)
@@ -210,4 +210,15 @@ false, true, false, true);
      ->  Result  (cost=N..N rows=1 width=N)
 (4 rows)
 
+-- Verify that scalarineqsel() works on "char" columns
+CREATE TEMP TABLE char_table_1 AS
+  SELECT i::"char" AS c FROM generate_series(64,96) i;
+ANALYZE char_table_1;
+EXPLAIN (COSTS OFF) SELECT * FROM char_table_1 WHERE c < 'Q';
+         QUERY PLAN          
+-----------------------------
+ Seq Scan on char_table_1
+   Filter: (c < 'Q'::"char")
+(2 rows)
+
 DROP FUNCTION explain_mask_costs(text, bool, bool, bool, bool);
index 53210d5baad8e47f89882785f769bc81c92b1281..2b696a4e4e55d5535aa7fc579fc8f1bf645cfd8f 100644 (file)
@@ -147,4 +147,10 @@ SELECT explain_mask_costs($$
 SELECT * FROM tenk1 WHERE unique1 <> ALL (ARRAY[1, 2, 98, (SELECT 99), NULL]);$$,
 false, true, false, true);
 
+-- Verify that scalarineqsel() works on "char" columns
+CREATE TEMP TABLE char_table_1 AS
+  SELECT i::"char" AS c FROM generate_series(64,96) i;
+ANALYZE char_table_1;
+EXPLAIN (COSTS OFF) SELECT * FROM char_table_1 WHERE c < 'Q';
+
 DROP FUNCTION explain_mask_costs(text, bool, bool, bool, bool);