Skip to content

Commit

Permalink
tree: optionally treat string constants as collated
Browse files Browse the repository at this point in the history
Previously, the type checker would reject comparisons between string
constants and collated collated strings without an explicit type cast.
This patch relaxes that restriction so that comparison of collated
strings against string literals does the collated comparison as one
would expect.

Fixes #133141

Release note (bug fix): String constants can now be compared against
collated strings.
  • Loading branch information
mw5h committed Oct 30, 2024
1 parent 615110b commit 2ec5a52
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 1 deletion.
52 changes: 51 additions & 1 deletion pkg/sql/logictest/testdata/logic_test/collatedstring
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
statement error pq: invalid locale bad_locale: language: subtag "locale" is well-formed but unknown
SELECT 'a' COLLATE bad_locale

statement error pq: unsupported comparison operator: <collatedstring{en}> = <string>
query B
SELECT 'A' COLLATE en = 'a'
----
false

statement error pq: unsupported comparison operator: <collatedstring{en}> = <collatedstring{de}>
SELECT 'A' COLLATE en = 'a' COLLATE de
Expand Down Expand Up @@ -560,3 +562,51 @@ SELECT * FROM t45142 WHERE c < SOME ('' COLLATE en, '' COLLATE de);
statement ok
SELECT * FROM t45142 WHERE c < SOME (CASE WHEN true THEN NULL END, '' COLLATE en);
SELECT * FROM t45142 WHERE c < SOME ('' COLLATE en, CASE WHEN true THEN NULL END);

subtest issue_132867

statement ok
CREATE TABLE test_collate (
id INT8 PRIMARY KEY,
"string_field" STRING COLLATE en_US_u_ks_level2 NULL
)

statement ok
INSERT INTO test_collate VALUES (1, 'Str_Collate_1')

query IT
SELECT * FROM test_collate WHERE (("id", "string_field")) = ANY(Array[(1, 'str_collate_1')])
----
1 Str_Collate_1

query I
SELECT id FROM test_collate WHERE "string_field" = 'sTR_cOLLATE_1'
----
1

statement ok
INSERT INTO test_collate VALUES (2, 'Foo'), (3, 'Bar'), (4, 'Baz')

query T
SELECT string_field FROM test_collate WHERE string_field < 'baz' ORDER BY id
----
Bar

query T
SELECT string_field FROM test_collate WHERE string_field <= 'baz' ORDER BY id
----
Bar
Baz

query T
SELECT string_field FROM test_collate WHERE string_field > 'baz' ORDER BY id
----
Str_Collate_1
Foo

query T
SELECT string_field FROM test_collate WHERE string_field >= 'baz' ORDER BY id
----
Str_Collate_1
Foo
Baz
1 change: 1 addition & 0 deletions pkg/sql/sem/tree/constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ var (
// default type that raw strings get parsed into, without any casts or type
// assertions.
types.String,
types.AnyCollatedString,
types.Bytes,
types.Bool,
types.Int,
Expand Down
20 changes: 20 additions & 0 deletions pkg/sql/sem/tree/constant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ func TestAvailTypesAreSets(t *testing.T) {
for i, test := range testCases {
seen := make(map[oid.Oid]struct{})
for _, newType := range test.availTypes {
// Collated strings have the same Oid as uncollated strings, but we need the
// ability to parse constants as collated strings when that is the desired
// type.
if newType.Family() == types.CollatedStringFamily {
continue
}
if _, ok := seen[newType.Oid()]; ok {
t.Errorf("%d: found duplicate type: %v", i, newType)
}
Expand Down Expand Up @@ -214,6 +220,13 @@ func TestStringConstantVerifyAvailableTypes(t *testing.T) {
continue
}

// The collated string value in c.AvailableTypes() is AnyCollatedString, so we
// will not be able to resolve that exact type. In actual execution, the constant
// would be resolved with an actual desired locale.
if availType.Family() == types.CollatedStringFamily {
continue
}

semaCtx := tree.MakeSemaContext(nil /* resolver */)
if _, err := test.c.ResolveAsType(context.Background(), &semaCtx, availType); err != nil {
if !strings.Contains(err.Error(), "could not parse") &&
Expand Down Expand Up @@ -680,6 +693,13 @@ func TestStringConstantResolveAvailableTypes(t *testing.T) {
continue
}

// The collated string value in c.AvailableTypes() is AnyCollatedString, so we
// will not be able to resolve that exact type. In actual execution, the constant
// would be resolved with an actual desired locale.
if availType.Family() == types.CollatedStringFamily {
continue
}

semaCtx := tree.MakeSemaContext(nil /* resolver */)
typedExpr, err := test.c.ResolveAsType(ctx, &semaCtx, availType)
var res tree.Datum
Expand Down

0 comments on commit 2ec5a52

Please sign in to comment.