From f3de7070b6fbc4a509a45473a63034ed5889b690 Mon Sep 17 00:00:00 2001 From: Marcus Gartner Date: Wed, 14 Aug 2024 16:13:55 -0400 Subject: [PATCH] opt: fix BPCHAR type and CASE typing This commit addresses inconsistencies from Postgres' behavior. First, it makes the `BPCHAR` type distinct from `CHAR`. The former is a blank-padded character type with no type width, meaning that it could have any length. The latter is a blank-padded character type with a type width of exactly 1 - it is essentially an alias of `CHAR(1)`. Previously, a column of type `BPCHAR` behaved the same as a column of type `CHAR(1)` - it enforced a length limit of 1. Second, the typing of `CASE` and `CASE`-like expressions has been fixed. The branches of these conditionals is no longer forced to have the same type-width. Fixes #127889 Fixes #108360 Release note (bug fix): A bug has been fixed that caused incorrect evaluation of `CASE`, `COALESCE`, and `IF` expressions with branches producing fixed-width string-like types, such as `CHAR`. In addition, the `BPCHAR` type has been fixed so that it no longer incorrectly imposes a length limit of 1. --- .../tests/3node-tenant/generated_test.go | 7 +++ pkg/sql/logictest/testdata/logic_test/bpchar | 27 +++++++++ pkg/sql/logictest/testdata/logic_test/case | 47 +++++++++++++++ pkg/sql/logictest/testdata/logic_test/cast | 2 +- pkg/sql/logictest/testdata/logic_test/decimal | 2 +- .../testdata/logic_test/drop_function | 30 ++++++++++ .../logictest/testdata/logic_test/grant_table | 60 +++++++++---------- .../logictest/testdata/logic_test/pg_builtins | 6 +- pkg/sql/logictest/testdata/logic_test/typing | 6 +- .../tests/fakedist-disk/generated_test.go | 7 +++ .../tests/fakedist-vec-off/generated_test.go | 7 +++ .../tests/fakedist/generated_test.go | 7 +++ .../generated_test.go | 7 +++ .../tests/local-mixed-23.1/generated_test.go | 7 +++ .../tests/local-vec-off/generated_test.go | 7 +++ .../logictest/tests/local/generated_test.go | 14 +++++ pkg/sql/opt/optbuilder/scalar.go | 8 +-- pkg/sql/opt/optbuilder/testdata/scalar | 8 +-- pkg/sql/opt/xform/testdata/external/tpcc | 19 +++--- .../xform/testdata/external/tpcc-later-stats | 19 +++--- .../opt/xform/testdata/external/tpcc-no-stats | 19 +++--- .../testdata/external/tpcc-read-committed | 19 +++--- pkg/sql/opt/xform/testdata/rules/select | 2 +- .../comparator_generated_test.go | 10 ++++ pkg/sql/sem/builtins/fixed_oids.go | 60 +++++++++---------- pkg/sql/sem/tree/overload.go | 8 ++- pkg/sql/types/types.go | 7 +++ 27 files changed, 305 insertions(+), 117 deletions(-) create mode 100644 pkg/sql/logictest/testdata/logic_test/bpchar create mode 100644 pkg/sql/logictest/testdata/logic_test/case diff --git a/pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go b/pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go index 449962c7415e..1352858fb00f 100644 --- a/pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go +++ b/pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go @@ -328,6 +328,13 @@ func TestTenantLogic_bit( runLogicTest(t, "bit") } +func TestTenantLogic_bpchar( + t *testing.T, +) { + defer leaktest.AfterTest(t)() + runLogicTest(t, "bpchar") +} + func TestTenantLogic_builtin_function( t *testing.T, ) { diff --git a/pkg/sql/logictest/testdata/logic_test/bpchar b/pkg/sql/logictest/testdata/logic_test/bpchar new file mode 100644 index 000000000000..46377711358b --- /dev/null +++ b/pkg/sql/logictest/testdata/logic_test/bpchar @@ -0,0 +1,27 @@ +query T +SELECT 'foo'::BPCHAR +---- +foo + +statement ok +CREATE TABLE t (c BPCHAR PRIMARY KEY, FAMILY (c)) + +statement ok +INSERT INTO t VALUES ('foo'), ('ba'), ('c'), ('foobarbaz') + +query T rowsort +SELECT c FROM t +---- +foo +ba +c +foobarbaz + +query T +SELECT create_statement FROM [SHOW CREATE TABLE t] +---- +CREATE TABLE public.t ( + c BPCHAR NOT NULL, + CONSTRAINT t_pkey PRIMARY KEY (c ASC), + FAMILY fam_0_c (c) +) diff --git a/pkg/sql/logictest/testdata/logic_test/case b/pkg/sql/logictest/testdata/logic_test/case new file mode 100644 index 000000000000..b22e297a0f9f --- /dev/null +++ b/pkg/sql/logictest/testdata/logic_test/case @@ -0,0 +1,47 @@ +# LogicTest: local + +# Regression test for #127889. CASE-like expressions should not impose type +# widths of one branch on other branches. +subtest regression_127889 + +query T +SELECT CASE WHEN true THEN 'foo'::TEXT ELSE 'b'::CHAR END +---- +foo + +query T +SELECT COALESCE(NULL::CHAR, 'bar'::CHAR(2)) +---- +ba + +query T +SELECT IF(false, 'foo'::CHAR, 'bar'::CHAR(2)) +---- +ba + +query T +SELECT CASE WHEN false THEN 'b'::CHAR ELSE 'foo'::TEXT END +---- +foo + +query T +SELECT (CASE WHEN false THEN 'b'::CHAR ELSE 'foo'::TEXT END)::CHAR +---- +f + +query T +SELECT (CASE WHEN false THEN 'b'::CHAR ELSE 'foo'::TEXT END)::BPCHAR +---- +foo + +query R +SELECT CASE WHEN true THEN 1.2345::DECIMAL(5, 4) ELSE NULL::DECIMAL(10, 2) END +---- +1.2345 + +query R +SELECT CASE WHEN false THEN NULL::DECIMAL(10, 2) ELSE 1.2345::DECIMAL(5, 4) END +---- +1.2345 + +subtest end diff --git a/pkg/sql/logictest/testdata/logic_test/cast b/pkg/sql/logictest/testdata/logic_test/cast index 74ca74e02b17..020b3bff79e9 100644 --- a/pkg/sql/logictest/testdata/logic_test/cast +++ b/pkg/sql/logictest/testdata/logic_test/cast @@ -1446,7 +1446,7 @@ CREATE TABLE def_assn_cast ( id INT4, a INT4 DEFAULT 1.0::FLOAT4, b VARCHAR DEFAULT 'true'::BOOL, - c NAME DEFAULT 'foo'::BPCHAR + c NAME DEFAULT 'foo'::CHAR ) statement ok diff --git a/pkg/sql/logictest/testdata/logic_test/decimal b/pkg/sql/logictest/testdata/logic_test/decimal index 75b74ab3fee6..caae80de186d 100644 --- a/pkg/sql/logictest/testdata/logic_test/decimal +++ b/pkg/sql/logictest/testdata/logic_test/decimal @@ -468,7 +468,7 @@ NaN NaN NaN NaN NaN # TODO(drewk): There are a few differences vs postgres in the number of decimal # places and negative zeros. query RRRRR -WITH v(id, x) AS (VALUES (1, '0'::numeric), (2, '1'::numeric), (3, '-1'::numeric), +WITH v(id, x) AS (VALUES (1, '0'::numeric), (2, '1'::numeric), (3, '-1'::numeric), (4, '4.2'::numeric), (5, 'inf'::numeric), (6, '-inf'::numeric), (7, 'nan'::numeric) ) SELECT x1, x2, diff --git a/pkg/sql/logictest/testdata/logic_test/drop_function b/pkg/sql/logictest/testdata/logic_test/drop_function index 30edbc6a0c49..b1e22f131f76 100644 --- a/pkg/sql/logictest/testdata/logic_test/drop_function +++ b/pkg/sql/logictest/testdata/logic_test/drop_function @@ -250,3 +250,33 @@ statement error pgcode 42883 unknown function: f114677\(\) SHOW CREATE FUNCTION f114677; subtest end + +statement ok +CREATE FUNCTION f_char(c CHAR) RETURNS INT LANGUAGE SQL AS 'SELECT 1' + +statement ok +DROP FUNCTION f_char(BPCHAR) + +statement ok +CREATE FUNCTION f_char(c CHAR(2)) RETURNS INT LANGUAGE SQL AS 'SELECT 1' + +statement ok +DROP FUNCTION f_char(BPCHAR) + +statement ok +CREATE FUNCTION f_char(c BPCHAR) RETURNS INT LANGUAGE SQL AS 'SELECT 1' + +statement ok +DROP FUNCTION f_char(BPCHAR) + +statement ok +CREATE FUNCTION f_char(c BPCHAR) RETURNS INT LANGUAGE SQL AS 'SELECT 1' + +statement ok +DROP FUNCTION f_char(CHAR) + +statement ok +CREATE FUNCTION f_char(c BPCHAR) RETURNS INT LANGUAGE SQL AS 'SELECT 1' + +statement ok +DROP FUNCTION f_char(CHAR(2)) diff --git a/pkg/sql/logictest/testdata/logic_test/grant_table b/pkg/sql/logictest/testdata/logic_test/grant_table index 07c7622751cf..37c219a7f098 100644 --- a/pkg/sql/logictest/testdata/logic_test/grant_table +++ b/pkg/sql/logictest/testdata/logic_test/grant_table @@ -243,18 +243,18 @@ test pg_catalog box2d type test pg_catalog box2d[] type admin ALL false test pg_catalog box2d[] type public USAGE false test pg_catalog box2d[] type root ALL false +test pg_catalog bpchar type admin ALL false +test pg_catalog bpchar type public USAGE false +test pg_catalog bpchar type root ALL false +test pg_catalog bpchar[] type admin ALL false +test pg_catalog bpchar[] type public USAGE false +test pg_catalog bpchar[] type root ALL false test pg_catalog bytes type admin ALL false test pg_catalog bytes type public USAGE false test pg_catalog bytes type root ALL false test pg_catalog bytes[] type admin ALL false test pg_catalog bytes[] type public USAGE false test pg_catalog bytes[] type root ALL false -test pg_catalog char type admin ALL false -test pg_catalog char type public USAGE false -test pg_catalog char type root ALL false -test pg_catalog char[] type admin ALL false -test pg_catalog char[] type public USAGE false -test pg_catalog char[] type root ALL false test pg_catalog date type admin ALL false test pg_catalog date type public USAGE false test pg_catalog date type root ALL false @@ -635,14 +635,14 @@ test pg_catalog box2d type admin ALL test pg_catalog box2d type root ALL false test pg_catalog box2d[] type admin ALL false test pg_catalog box2d[] type root ALL false +test pg_catalog bpchar type admin ALL false +test pg_catalog bpchar type root ALL false +test pg_catalog bpchar[] type admin ALL false +test pg_catalog bpchar[] type root ALL false test pg_catalog bytes type admin ALL false test pg_catalog bytes type root ALL false test pg_catalog bytes[] type admin ALL false test pg_catalog bytes[] type root ALL false -test pg_catalog char type admin ALL false -test pg_catalog char type root ALL false -test pg_catalog char[] type admin ALL false -test pg_catalog char[] type root ALL false test pg_catalog date type admin ALL false test pg_catalog date type root ALL false test pg_catalog date[] type admin ALL false @@ -1239,14 +1239,14 @@ a pg_catalog box2d type admin a pg_catalog box2d type root ALL false a pg_catalog box2d[] type admin ALL false a pg_catalog box2d[] type root ALL false +a pg_catalog bpchar type admin ALL false +a pg_catalog bpchar type root ALL false +a pg_catalog bpchar[] type admin ALL false +a pg_catalog bpchar[] type root ALL false a pg_catalog bytes type admin ALL false a pg_catalog bytes type root ALL false a pg_catalog bytes[] type admin ALL false a pg_catalog bytes[] type root ALL false -a pg_catalog char type admin ALL false -a pg_catalog char type root ALL false -a pg_catalog char[] type admin ALL false -a pg_catalog char[] type root ALL false a pg_catalog date type admin ALL false a pg_catalog date type root ALL false a pg_catalog date[] type admin ALL false @@ -1415,14 +1415,14 @@ defaultdb pg_catalog box2d type admin defaultdb pg_catalog box2d type root ALL false defaultdb pg_catalog box2d[] type admin ALL false defaultdb pg_catalog box2d[] type root ALL false +defaultdb pg_catalog bpchar type admin ALL false +defaultdb pg_catalog bpchar type root ALL false +defaultdb pg_catalog bpchar[] type admin ALL false +defaultdb pg_catalog bpchar[] type root ALL false defaultdb pg_catalog bytes type admin ALL false defaultdb pg_catalog bytes type root ALL false defaultdb pg_catalog bytes[] type admin ALL false defaultdb pg_catalog bytes[] type root ALL false -defaultdb pg_catalog char type admin ALL false -defaultdb pg_catalog char type root ALL false -defaultdb pg_catalog char[] type admin ALL false -defaultdb pg_catalog char[] type root ALL false defaultdb pg_catalog date type admin ALL false defaultdb pg_catalog date type root ALL false defaultdb pg_catalog date[] type admin ALL false @@ -1591,14 +1591,14 @@ postgres pg_catalog box2d type admin postgres pg_catalog box2d type root ALL false postgres pg_catalog box2d[] type admin ALL false postgres pg_catalog box2d[] type root ALL false +postgres pg_catalog bpchar type admin ALL false +postgres pg_catalog bpchar type root ALL false +postgres pg_catalog bpchar[] type admin ALL false +postgres pg_catalog bpchar[] type root ALL false postgres pg_catalog bytes type admin ALL false postgres pg_catalog bytes type root ALL false postgres pg_catalog bytes[] type admin ALL false postgres pg_catalog bytes[] type root ALL false -postgres pg_catalog char type admin ALL false -postgres pg_catalog char type root ALL false -postgres pg_catalog char[] type admin ALL false -postgres pg_catalog char[] type root ALL false postgres pg_catalog date type admin ALL false postgres pg_catalog date type root ALL false postgres pg_catalog date[] type admin ALL false @@ -1767,14 +1767,14 @@ system pg_catalog box2d type admin system pg_catalog box2d type root ALL false system pg_catalog box2d[] type admin ALL false system pg_catalog box2d[] type root ALL false +system pg_catalog bpchar type admin ALL false +system pg_catalog bpchar type root ALL false +system pg_catalog bpchar[] type admin ALL false +system pg_catalog bpchar[] type root ALL false system pg_catalog bytes type admin ALL false system pg_catalog bytes type root ALL false system pg_catalog bytes[] type admin ALL false system pg_catalog bytes[] type root ALL false -system pg_catalog char type admin ALL false -system pg_catalog char type root ALL false -system pg_catalog char[] type admin ALL false -system pg_catalog char[] type root ALL false system pg_catalog date type admin ALL false system pg_catalog date type root ALL false system pg_catalog date[] type admin ALL false @@ -2315,14 +2315,14 @@ test pg_catalog box2d type admin test pg_catalog box2d type root ALL false test pg_catalog box2d[] type admin ALL false test pg_catalog box2d[] type root ALL false +test pg_catalog bpchar type admin ALL false +test pg_catalog bpchar type root ALL false +test pg_catalog bpchar[] type admin ALL false +test pg_catalog bpchar[] type root ALL false test pg_catalog bytes type admin ALL false test pg_catalog bytes type root ALL false test pg_catalog bytes[] type admin ALL false test pg_catalog bytes[] type root ALL false -test pg_catalog char type admin ALL false -test pg_catalog char type root ALL false -test pg_catalog char[] type admin ALL false -test pg_catalog char[] type root ALL false test pg_catalog date type admin ALL false test pg_catalog date type root ALL false test pg_catalog date[] type admin ALL false diff --git a/pkg/sql/logictest/testdata/logic_test/pg_builtins b/pkg/sql/logictest/testdata/logic_test/pg_builtins index 0e7ba76aa6d0..769175ee2910 100644 --- a/pkg/sql/logictest/testdata/logic_test/pg_builtins +++ b/pkg/sql/logictest/testdata/logic_test/pg_builtins @@ -521,7 +521,7 @@ ORDER BY t.oid ---- text 25 -1 float8 701 -1 -bpchar 1042 5 +bpchar 1042 -1 varchar 1043 68 bit 1560 1 varbit 1562 16 @@ -539,7 +539,7 @@ ORDER BY t.oid ---- text NULL float8 NULL -bpchar 1 +bpchar NULL varchar 64 bit 1 varbit 16 @@ -558,7 +558,7 @@ ORDER BY t.oid ---- text NULL float8 NULL -bpchar 1 +bpchar NULL varchar 64 bit 1 varbit 16 diff --git a/pkg/sql/logictest/testdata/logic_test/typing b/pkg/sql/logictest/testdata/logic_test/typing index 70f8d236201c..30bb3ecd2cd8 100644 --- a/pkg/sql/logictest/testdata/logic_test/typing +++ b/pkg/sql/logictest/testdata/logic_test/typing @@ -289,15 +289,12 @@ WHERE t102110_1.t NOT BETWEEN t102110_1.t AND (CASE WHEN NULL THEN t102110_2.c ELSE t102110_1.t END); ---- -# IF is typed differently (Postgres does not support IF). query T SELECT t102110_1.t FROM t102110_1, t102110_2 WHERE t102110_1.t NOT BETWEEN t102110_1.t AND IF(NULL, t102110_2.c, t102110_1.t); ---- -tt -# TODO(#108360): implicit casts from STRING to CHAR should use bpchar. statement ok CREATE TABLE t108360_1 (t TEXT); INSERT INTO t108360_1 VALUES ('tt'); @@ -306,10 +303,9 @@ statement ok CREATE TABLE t108360_2 (c CHAR); INSERT INTO t108360_2 VALUES ('c'); -# This returns one row with 'tt' in Postgres. The results are different because -# Postgres casts t108360_1.t to bpchar rather than char. query T SELECT (CASE WHEN t108360_1.t > t108360_2.c THEN t108360_1.t ELSE t108360_2.c END) FROM t108360_1, t108360_2 WHERE t108360_1.t = (CASE WHEN t108360_1.t > t108360_2.c THEN t108360_1.t ELSE t108360_2.c END); ---- +tt diff --git a/pkg/sql/logictest/tests/fakedist-disk/generated_test.go b/pkg/sql/logictest/tests/fakedist-disk/generated_test.go index 20eaddfeb77b..2a36751e7ef1 100644 --- a/pkg/sql/logictest/tests/fakedist-disk/generated_test.go +++ b/pkg/sql/logictest/tests/fakedist-disk/generated_test.go @@ -283,6 +283,13 @@ func TestLogic_bit( runLogicTest(t, "bit") } +func TestLogic_bpchar( + t *testing.T, +) { + defer leaktest.AfterTest(t)() + runLogicTest(t, "bpchar") +} + func TestLogic_builtin_function( t *testing.T, ) { diff --git a/pkg/sql/logictest/tests/fakedist-vec-off/generated_test.go b/pkg/sql/logictest/tests/fakedist-vec-off/generated_test.go index cf91528416df..d26dbe988f0a 100644 --- a/pkg/sql/logictest/tests/fakedist-vec-off/generated_test.go +++ b/pkg/sql/logictest/tests/fakedist-vec-off/generated_test.go @@ -283,6 +283,13 @@ func TestLogic_bit( runLogicTest(t, "bit") } +func TestLogic_bpchar( + t *testing.T, +) { + defer leaktest.AfterTest(t)() + runLogicTest(t, "bpchar") +} + func TestLogic_builtin_function( t *testing.T, ) { diff --git a/pkg/sql/logictest/tests/fakedist/generated_test.go b/pkg/sql/logictest/tests/fakedist/generated_test.go index 8026ef18217b..31609c563830 100644 --- a/pkg/sql/logictest/tests/fakedist/generated_test.go +++ b/pkg/sql/logictest/tests/fakedist/generated_test.go @@ -283,6 +283,13 @@ func TestLogic_bit( runLogicTest(t, "bit") } +func TestLogic_bpchar( + t *testing.T, +) { + defer leaktest.AfterTest(t)() + runLogicTest(t, "bpchar") +} + func TestLogic_builtin_function( t *testing.T, ) { diff --git a/pkg/sql/logictest/tests/local-legacy-schema-changer/generated_test.go b/pkg/sql/logictest/tests/local-legacy-schema-changer/generated_test.go index 71bd087e396f..5aa3d795729e 100644 --- a/pkg/sql/logictest/tests/local-legacy-schema-changer/generated_test.go +++ b/pkg/sql/logictest/tests/local-legacy-schema-changer/generated_test.go @@ -283,6 +283,13 @@ func TestLogic_bit( runLogicTest(t, "bit") } +func TestLogic_bpchar( + t *testing.T, +) { + defer leaktest.AfterTest(t)() + runLogicTest(t, "bpchar") +} + func TestLogic_builtin_function( t *testing.T, ) { diff --git a/pkg/sql/logictest/tests/local-mixed-23.1/generated_test.go b/pkg/sql/logictest/tests/local-mixed-23.1/generated_test.go index 6a35370d3889..85ae99f00629 100644 --- a/pkg/sql/logictest/tests/local-mixed-23.1/generated_test.go +++ b/pkg/sql/logictest/tests/local-mixed-23.1/generated_test.go @@ -283,6 +283,13 @@ func TestLogic_bit( runLogicTest(t, "bit") } +func TestLogic_bpchar( + t *testing.T, +) { + defer leaktest.AfterTest(t)() + runLogicTest(t, "bpchar") +} + func TestLogic_builtin_function( t *testing.T, ) { diff --git a/pkg/sql/logictest/tests/local-vec-off/generated_test.go b/pkg/sql/logictest/tests/local-vec-off/generated_test.go index 5b8ace4f7e6b..3eec5fbd41d5 100644 --- a/pkg/sql/logictest/tests/local-vec-off/generated_test.go +++ b/pkg/sql/logictest/tests/local-vec-off/generated_test.go @@ -283,6 +283,13 @@ func TestLogic_bit( runLogicTest(t, "bit") } +func TestLogic_bpchar( + t *testing.T, +) { + defer leaktest.AfterTest(t)() + runLogicTest(t, "bpchar") +} + func TestLogic_builtin_function( t *testing.T, ) { diff --git a/pkg/sql/logictest/tests/local/generated_test.go b/pkg/sql/logictest/tests/local/generated_test.go index f9eb43929c29..93528f796b2a 100644 --- a/pkg/sql/logictest/tests/local/generated_test.go +++ b/pkg/sql/logictest/tests/local/generated_test.go @@ -283,6 +283,13 @@ func TestLogic_bit( runLogicTest(t, "bit") } +func TestLogic_bpchar( + t *testing.T, +) { + defer leaktest.AfterTest(t)() + runLogicTest(t, "bpchar") +} + func TestLogic_builtin_function( t *testing.T, ) { @@ -311,6 +318,13 @@ func TestLogic_cascade( runLogicTest(t, "cascade") } +func TestLogic_case( + t *testing.T, +) { + defer leaktest.AfterTest(t)() + runLogicTest(t, "case") +} + func TestLogic_case_sensitive_names( t *testing.T, ) { diff --git a/pkg/sql/opt/optbuilder/scalar.go b/pkg/sql/opt/optbuilder/scalar.go index 1bd653410cc2..4c404ae38492 100644 --- a/pkg/sql/opt/optbuilder/scalar.go +++ b/pkg/sql/opt/optbuilder/scalar.go @@ -247,7 +247,7 @@ func (b *Builder) buildScalar( for i := range t.Whens { condExpr := t.Whens[i].Cond.(tree.TypedExpr) cond := b.buildScalar(condExpr, inScope, nil, nil, colRefs) - valExpr, ok := eval.ReType(t.Whens[i].Val.(tree.TypedExpr), valType) + valExpr, ok := eval.ReType(t.Whens[i].Val.(tree.TypedExpr), valType.WithoutTypeModifiers()) if !ok { panic(pgerror.Newf( pgcode.DatatypeMismatch, @@ -261,7 +261,7 @@ func (b *Builder) buildScalar( // Add the ELSE expression to the end of whens as a raw scalar expression. var orElse opt.ScalarExpr if t.Else != nil { - elseExpr, ok := eval.ReType(t.Else.(tree.TypedExpr), valType) + elseExpr, ok := eval.ReType(t.Else.(tree.TypedExpr), valType.WithoutTypeModifiers()) if !ok { panic(pgerror.Newf( pgcode.DatatypeMismatch, @@ -287,7 +287,7 @@ func (b *Builder) buildScalar( // The type of the CoalesceExpr might be different than the inputs (e.g. // when they are NULL). Force all inputs to be the same type, so that we // build coalesce operator with the correct type. - expr, ok := eval.ReType(t.TypedExprAt(i), typ) + expr, ok := eval.ReType(t.TypedExprAt(i), typ.WithoutTypeModifiers()) if !ok { panic(pgerror.Newf( pgcode.DatatypeMismatch, @@ -337,7 +337,7 @@ func (b *Builder) buildScalar( ifTrueExpr := reType(t.True.(tree.TypedExpr), valType) ifTrue := b.buildScalar(ifTrueExpr, inScope, nil, nil, colRefs) whens := memo.ScalarListExpr{b.factory.ConstructWhen(memo.TrueSingleton, ifTrue)} - orElseExpr, ok := eval.ReType(t.Else.(tree.TypedExpr), valType) + orElseExpr, ok := eval.ReType(t.Else.(tree.TypedExpr), valType.WithoutTypeModifiers()) if !ok { panic(pgerror.Newf( pgcode.DatatypeMismatch, diff --git a/pkg/sql/opt/optbuilder/testdata/scalar b/pkg/sql/opt/optbuilder/testdata/scalar index 935e5ea430f0..5a0da192a216 100644 --- a/pkg/sql/opt/optbuilder/testdata/scalar +++ b/pkg/sql/opt/optbuilder/testdata/scalar @@ -1628,7 +1628,7 @@ project │ │ └── columns: c:5 t102110_2.rowid:6!null t102110_2.crdb_internal_mvcc_timestamp:7 t102110_2.tableoid:8 │ └── filters (true) └── filters - └── NOT ((t:1 >= t:1) AND (t:1 <= CASE WHEN NULL THEN t:1::CHAR ELSE c:5 END)) + └── NOT ((t:1 >= t:1) AND (t:1 <= CASE WHEN NULL THEN t:1::BPCHAR ELSE c:5::BPCHAR END)) # IF is typed differently (Postgres does not support IF). build @@ -1648,7 +1648,7 @@ project │ │ └── columns: c:5 t102110_2.rowid:6!null t102110_2.crdb_internal_mvcc_timestamp:7 t102110_2.tableoid:8 │ └── filters (true) └── filters - └── NOT ((t:1 >= t:1) AND (t:1 <= CASE NULL WHEN true THEN c:5 ELSE t:1::CHAR END)) + └── NOT ((t:1 >= t:1) AND (t:1 <= CASE NULL WHEN true THEN c:5 ELSE t:1::BPCHAR END)) # TODO(#108360): implicit casts from STRING to CHAR should use bpchar. exec-ddl @@ -1676,6 +1676,6 @@ project │ │ │ └── columns: t108360_2.c:5 t108360_2.rowid:6!null t108360_2.crdb_internal_mvcc_timestamp:7 t108360_2.tableoid:8 │ │ └── filters (true) │ └── filters - │ └── t:1 = CASE WHEN t:1 > t108360_2.c:5 THEN t:1::CHAR ELSE t108360_2.c:5 END + │ └── t:1 = CASE WHEN t:1 > t108360_2.c:5 THEN t:1::BPCHAR ELSE t108360_2.c:5::BPCHAR END └── projections - └── CASE WHEN t:1 > t108360_2.c:5 THEN t:1::CHAR ELSE t108360_2.c:5 END [as=c:9] + └── CASE WHEN t:1 > t108360_2.c:5 THEN t:1::BPCHAR ELSE t108360_2.c:5::BPCHAR END [as=c:9] diff --git a/pkg/sql/opt/xform/testdata/external/tpcc b/pkg/sql/opt/xform/testdata/external/tpcc index 62f714bce82d..ab515df43338 100644 --- a/pkg/sql/opt/xform/testdata/external/tpcc +++ b/pkg/sql/opt/xform/testdata/external/tpcc @@ -580,11 +580,11 @@ RETURNING CASE c_credit WHEN 'BC' THEN "left"(c_data, 200) ELSE '' END ---- project - ├── columns: c_first:4 c_middle:5 c_last:6 c_street_1:7 c_street_2:8 c_city:9 c_state:10 c_zip:11 c_phone:12 c_since:13 c_credit:14 c_credit_lim:15 c_discount:16 c_balance:17 case:53 + ├── columns: c_first:4 c_middle:5 c_last:6 c_street_1:7 c_street_2:8 c_city:9 c_state:10 c_zip:11 c_phone:12 c_since:13 c_credit:14 c_credit_lim:15 c_discount:16 c_balance:17 case:54 ├── cardinality: [0 - 1] ├── volatile, mutations ├── key: () - ├── fd: ()-->(4-17,53) + ├── fd: ()-->(4-17,54) ├── update customer │ ├── columns: c_id:1!null c_d_id:2!null c_w_id:3!null c_first:4 c_middle:5 c_last:6 c_street_1:7 c_street_2:8 c_city:9 c_state:10 c_zip:11 c_phone:12 c_since:13 c_credit:14 c_credit_lim:15 c_discount:16 c_balance:17 c_data:21 │ ├── fetch columns: c_id:24 c_d_id:25 c_w_id:26 c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 @@ -592,7 +592,7 @@ project │ │ ├── c_balance_cast:51 => c_balance:17 │ │ ├── c_ytd_payment_cast:52 => c_ytd_payment:18 │ │ ├── c_payment_cnt_new:49 => c_payment_cnt:19 - │ │ └── c_data_new:50 => c_data:21 + │ │ └── c_data_cast:53 => c_data:21 │ ├── return-mapping: │ │ ├── c_id:24 => c_id:1 │ │ ├── c_d_id:25 => c_d_id:2 @@ -611,17 +611,17 @@ project │ │ ├── c_credit_lim:38 => c_credit_lim:15 │ │ ├── c_discount:39 => c_discount:16 │ │ ├── c_balance_cast:51 => c_balance:17 - │ │ └── c_data_new:50 => c_data:21 + │ │ └── c_data_cast:53 => c_data:21 │ ├── cardinality: [0 - 1] │ ├── volatile, mutations │ ├── key: () │ ├── fd: ()-->(1-17,21) │ └── project - │ ├── columns: c_balance_cast:51 c_ytd_payment_cast:52 c_payment_cnt_new:49 c_data_new:50 c_id:24!null c_d_id:25!null c_w_id:26!null c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 + │ ├── columns: c_balance_cast:51 c_ytd_payment_cast:52 c_data_cast:53 c_payment_cnt_new:49 c_id:24!null c_d_id:25!null c_w_id:26!null c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 │ ├── cardinality: [0 - 1] │ ├── immutable │ ├── key: () - │ ├── fd: ()-->(24-44,49-52) + │ ├── fd: ()-->(24-44,49,51-53) │ ├── scan customer │ │ ├── columns: c_id:24!null c_d_id:25!null c_w_id:26!null c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 │ │ ├── constraint: /26/25/24: [/10/5/1343 - /10/5/1343] @@ -633,10 +633,11 @@ project │ │ └── c_balance:40 - 3860.61 │ ├── assignment-cast: DECIMAL(12,2) [as=c_ytd_payment_cast:52, outer=(41), immutable] │ │ └── c_ytd_payment:41 + 3860.61 - │ ├── c_payment_cnt:42 + 1 [as=c_payment_cnt_new:49, outer=(42), immutable] - │ └── CASE c_credit:37 WHEN 'BC' THEN left((((((c_id:24::STRING || c_d_id:25::STRING) || c_w_id:26::STRING) || '5') || '10') || '3860.61') || c_data:44, 500)::VARCHAR(500) ELSE c_data:44 END [as=c_data_new:50, outer=(24-26,37,44), immutable] + │ ├── assignment-cast: VARCHAR(500) [as=c_data_cast:53, outer=(24-26,37,44), immutable] + │ │ └── CASE c_credit:37 WHEN 'BC' THEN left((((((c_id:24::STRING || c_d_id:25::STRING) || c_w_id:26::STRING) || '5') || '10') || '3860.61') || c_data:44, 500)::VARCHAR ELSE c_data:44::VARCHAR END + │ └── c_payment_cnt:42 + 1 [as=c_payment_cnt_new:49, outer=(42), immutable] └── projections - └── CASE c_credit:14 WHEN 'BC' THEN left(c_data:21, 200) ELSE '' END [as=case:53, outer=(14,21), immutable] + └── CASE c_credit:14 WHEN 'BC' THEN left(c_data:21, 200) ELSE '' END [as=case:54, outer=(14,21), immutable] opt format=hide-qual INSERT INTO history diff --git a/pkg/sql/opt/xform/testdata/external/tpcc-later-stats b/pkg/sql/opt/xform/testdata/external/tpcc-later-stats index d7eb4296cf0b..446f1056146e 100644 --- a/pkg/sql/opt/xform/testdata/external/tpcc-later-stats +++ b/pkg/sql/opt/xform/testdata/external/tpcc-later-stats @@ -579,11 +579,11 @@ RETURNING CASE c_credit WHEN 'BC' THEN "left"(c_data, 200) ELSE '' END ---- project - ├── columns: c_first:4 c_middle:5 c_last:6 c_street_1:7 c_street_2:8 c_city:9 c_state:10 c_zip:11 c_phone:12 c_since:13 c_credit:14 c_credit_lim:15 c_discount:16 c_balance:17 case:53 + ├── columns: c_first:4 c_middle:5 c_last:6 c_street_1:7 c_street_2:8 c_city:9 c_state:10 c_zip:11 c_phone:12 c_since:13 c_credit:14 c_credit_lim:15 c_discount:16 c_balance:17 case:54 ├── cardinality: [0 - 1] ├── volatile, mutations ├── key: () - ├── fd: ()-->(4-17,53) + ├── fd: ()-->(4-17,54) ├── update customer │ ├── columns: c_id:1!null c_d_id:2!null c_w_id:3!null c_first:4 c_middle:5 c_last:6 c_street_1:7 c_street_2:8 c_city:9 c_state:10 c_zip:11 c_phone:12 c_since:13 c_credit:14 c_credit_lim:15 c_discount:16 c_balance:17 c_data:21 │ ├── fetch columns: c_id:24 c_d_id:25 c_w_id:26 c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 @@ -591,7 +591,7 @@ project │ │ ├── c_balance_cast:51 => c_balance:17 │ │ ├── c_ytd_payment_cast:52 => c_ytd_payment:18 │ │ ├── c_payment_cnt_new:49 => c_payment_cnt:19 - │ │ └── c_data_new:50 => c_data:21 + │ │ └── c_data_cast:53 => c_data:21 │ ├── return-mapping: │ │ ├── c_id:24 => c_id:1 │ │ ├── c_d_id:25 => c_d_id:2 @@ -610,17 +610,17 @@ project │ │ ├── c_credit_lim:38 => c_credit_lim:15 │ │ ├── c_discount:39 => c_discount:16 │ │ ├── c_balance_cast:51 => c_balance:17 - │ │ └── c_data_new:50 => c_data:21 + │ │ └── c_data_cast:53 => c_data:21 │ ├── cardinality: [0 - 1] │ ├── volatile, mutations │ ├── key: () │ ├── fd: ()-->(1-17,21) │ └── project - │ ├── columns: c_balance_cast:51 c_ytd_payment_cast:52 c_payment_cnt_new:49 c_data_new:50 c_id:24!null c_d_id:25!null c_w_id:26!null c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 + │ ├── columns: c_balance_cast:51 c_ytd_payment_cast:52 c_data_cast:53 c_payment_cnt_new:49 c_id:24!null c_d_id:25!null c_w_id:26!null c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 │ ├── cardinality: [0 - 1] │ ├── immutable │ ├── key: () - │ ├── fd: ()-->(24-44,49-52) + │ ├── fd: ()-->(24-44,49,51-53) │ ├── scan customer │ │ ├── columns: c_id:24!null c_d_id:25!null c_w_id:26!null c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 │ │ ├── constraint: /26/25/24: [/10/5/1343 - /10/5/1343] @@ -632,10 +632,11 @@ project │ │ └── c_balance:40 - 3860.61 │ ├── assignment-cast: DECIMAL(12,2) [as=c_ytd_payment_cast:52, outer=(41), immutable] │ │ └── c_ytd_payment:41 + 3860.61 - │ ├── c_payment_cnt:42 + 1 [as=c_payment_cnt_new:49, outer=(42), immutable] - │ └── CASE c_credit:37 WHEN 'BC' THEN left((((((c_id:24::STRING || c_d_id:25::STRING) || c_w_id:26::STRING) || '5') || '10') || '3860.61') || c_data:44, 500)::VARCHAR(500) ELSE c_data:44 END [as=c_data_new:50, outer=(24-26,37,44), immutable] + │ ├── assignment-cast: VARCHAR(500) [as=c_data_cast:53, outer=(24-26,37,44), immutable] + │ │ └── CASE c_credit:37 WHEN 'BC' THEN left((((((c_id:24::STRING || c_d_id:25::STRING) || c_w_id:26::STRING) || '5') || '10') || '3860.61') || c_data:44, 500)::VARCHAR ELSE c_data:44::VARCHAR END + │ └── c_payment_cnt:42 + 1 [as=c_payment_cnt_new:49, outer=(42), immutable] └── projections - └── CASE c_credit:14 WHEN 'BC' THEN left(c_data:21, 200) ELSE '' END [as=case:53, outer=(14,21), immutable] + └── CASE c_credit:14 WHEN 'BC' THEN left(c_data:21, 200) ELSE '' END [as=case:54, outer=(14,21), immutable] opt format=hide-qual INSERT INTO history diff --git a/pkg/sql/opt/xform/testdata/external/tpcc-no-stats b/pkg/sql/opt/xform/testdata/external/tpcc-no-stats index b46dabe9cbdb..568165f14dc8 100644 --- a/pkg/sql/opt/xform/testdata/external/tpcc-no-stats +++ b/pkg/sql/opt/xform/testdata/external/tpcc-no-stats @@ -577,11 +577,11 @@ RETURNING CASE c_credit WHEN 'BC' THEN "left"(c_data, 200) ELSE '' END ---- project - ├── columns: c_first:4 c_middle:5 c_last:6 c_street_1:7 c_street_2:8 c_city:9 c_state:10 c_zip:11 c_phone:12 c_since:13 c_credit:14 c_credit_lim:15 c_discount:16 c_balance:17 case:53 + ├── columns: c_first:4 c_middle:5 c_last:6 c_street_1:7 c_street_2:8 c_city:9 c_state:10 c_zip:11 c_phone:12 c_since:13 c_credit:14 c_credit_lim:15 c_discount:16 c_balance:17 case:54 ├── cardinality: [0 - 1] ├── volatile, mutations ├── key: () - ├── fd: ()-->(4-17,53) + ├── fd: ()-->(4-17,54) ├── update customer │ ├── columns: c_id:1!null c_d_id:2!null c_w_id:3!null c_first:4 c_middle:5 c_last:6 c_street_1:7 c_street_2:8 c_city:9 c_state:10 c_zip:11 c_phone:12 c_since:13 c_credit:14 c_credit_lim:15 c_discount:16 c_balance:17 c_data:21 │ ├── fetch columns: c_id:24 c_d_id:25 c_w_id:26 c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 @@ -589,7 +589,7 @@ project │ │ ├── c_balance_cast:51 => c_balance:17 │ │ ├── c_ytd_payment_cast:52 => c_ytd_payment:18 │ │ ├── c_payment_cnt_new:49 => c_payment_cnt:19 - │ │ └── c_data_new:50 => c_data:21 + │ │ └── c_data_cast:53 => c_data:21 │ ├── return-mapping: │ │ ├── c_id:24 => c_id:1 │ │ ├── c_d_id:25 => c_d_id:2 @@ -608,17 +608,17 @@ project │ │ ├── c_credit_lim:38 => c_credit_lim:15 │ │ ├── c_discount:39 => c_discount:16 │ │ ├── c_balance_cast:51 => c_balance:17 - │ │ └── c_data_new:50 => c_data:21 + │ │ └── c_data_cast:53 => c_data:21 │ ├── cardinality: [0 - 1] │ ├── volatile, mutations │ ├── key: () │ ├── fd: ()-->(1-17,21) │ └── project - │ ├── columns: c_balance_cast:51 c_ytd_payment_cast:52 c_payment_cnt_new:49 c_data_new:50 c_id:24!null c_d_id:25!null c_w_id:26!null c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 + │ ├── columns: c_balance_cast:51 c_ytd_payment_cast:52 c_data_cast:53 c_payment_cnt_new:49 c_id:24!null c_d_id:25!null c_w_id:26!null c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 │ ├── cardinality: [0 - 1] │ ├── immutable │ ├── key: () - │ ├── fd: ()-->(24-44,49-52) + │ ├── fd: ()-->(24-44,49,51-53) │ ├── scan customer │ │ ├── columns: c_id:24!null c_d_id:25!null c_w_id:26!null c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 │ │ ├── constraint: /26/25/24: [/10/5/1343 - /10/5/1343] @@ -630,10 +630,11 @@ project │ │ └── c_balance:40 - 3860.61 │ ├── assignment-cast: DECIMAL(12,2) [as=c_ytd_payment_cast:52, outer=(41), immutable] │ │ └── c_ytd_payment:41 + 3860.61 - │ ├── c_payment_cnt:42 + 1 [as=c_payment_cnt_new:49, outer=(42), immutable] - │ └── CASE c_credit:37 WHEN 'BC' THEN left((((((c_id:24::STRING || c_d_id:25::STRING) || c_w_id:26::STRING) || '5') || '10') || '3860.61') || c_data:44, 500)::VARCHAR(500) ELSE c_data:44 END [as=c_data_new:50, outer=(24-26,37,44), immutable] + │ ├── assignment-cast: VARCHAR(500) [as=c_data_cast:53, outer=(24-26,37,44), immutable] + │ │ └── CASE c_credit:37 WHEN 'BC' THEN left((((((c_id:24::STRING || c_d_id:25::STRING) || c_w_id:26::STRING) || '5') || '10') || '3860.61') || c_data:44, 500)::VARCHAR ELSE c_data:44::VARCHAR END + │ └── c_payment_cnt:42 + 1 [as=c_payment_cnt_new:49, outer=(42), immutable] └── projections - └── CASE c_credit:14 WHEN 'BC' THEN left(c_data:21, 200) ELSE '' END [as=case:53, outer=(14,21), immutable] + └── CASE c_credit:14 WHEN 'BC' THEN left(c_data:21, 200) ELSE '' END [as=case:54, outer=(14,21), immutable] opt format=hide-qual INSERT INTO history diff --git a/pkg/sql/opt/xform/testdata/external/tpcc-read-committed b/pkg/sql/opt/xform/testdata/external/tpcc-read-committed index 85602a30e1d5..6392869d6d6f 100644 --- a/pkg/sql/opt/xform/testdata/external/tpcc-read-committed +++ b/pkg/sql/opt/xform/testdata/external/tpcc-read-committed @@ -591,11 +591,11 @@ RETURNING CASE c_credit WHEN 'BC' THEN "left"(c_data, 200) ELSE '' END ---- project - ├── columns: c_first:4 c_middle:5 c_last:6 c_street_1:7 c_street_2:8 c_city:9 c_state:10 c_zip:11 c_phone:12 c_since:13 c_credit:14 c_credit_lim:15 c_discount:16 c_balance:17 case:53 + ├── columns: c_first:4 c_middle:5 c_last:6 c_street_1:7 c_street_2:8 c_city:9 c_state:10 c_zip:11 c_phone:12 c_since:13 c_credit:14 c_credit_lim:15 c_discount:16 c_balance:17 case:54 ├── cardinality: [0 - 1] ├── volatile, mutations ├── key: () - ├── fd: ()-->(4-17,53) + ├── fd: ()-->(4-17,54) ├── update customer │ ├── columns: c_id:1!null c_d_id:2!null c_w_id:3!null c_first:4 c_middle:5 c_last:6 c_street_1:7 c_street_2:8 c_city:9 c_state:10 c_zip:11 c_phone:12 c_since:13 c_credit:14 c_credit_lim:15 c_discount:16 c_balance:17 c_data:21 │ ├── fetch columns: c_id:24 c_d_id:25 c_w_id:26 c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 @@ -603,7 +603,7 @@ project │ │ ├── c_balance_cast:51 => c_balance:17 │ │ ├── c_ytd_payment_cast:52 => c_ytd_payment:18 │ │ ├── c_payment_cnt_new:49 => c_payment_cnt:19 - │ │ └── c_data_new:50 => c_data:21 + │ │ └── c_data_cast:53 => c_data:21 │ ├── return-mapping: │ │ ├── c_id:24 => c_id:1 │ │ ├── c_d_id:25 => c_d_id:2 @@ -622,17 +622,17 @@ project │ │ ├── c_credit_lim:38 => c_credit_lim:15 │ │ ├── c_discount:39 => c_discount:16 │ │ ├── c_balance_cast:51 => c_balance:17 - │ │ └── c_data_new:50 => c_data:21 + │ │ └── c_data_cast:53 => c_data:21 │ ├── cardinality: [0 - 1] │ ├── volatile, mutations │ ├── key: () │ ├── fd: ()-->(1-17,21) │ └── project - │ ├── columns: c_balance_cast:51 c_ytd_payment_cast:52 c_payment_cnt_new:49 c_data_new:50 c_id:24!null c_d_id:25!null c_w_id:26!null c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 + │ ├── columns: c_balance_cast:51 c_ytd_payment_cast:52 c_data_cast:53 c_payment_cnt_new:49 c_id:24!null c_d_id:25!null c_w_id:26!null c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 │ ├── cardinality: [0 - 1] │ ├── immutable │ ├── key: () - │ ├── fd: ()-->(24-44,49-52) + │ ├── fd: ()-->(24-44,49,51-53) │ ├── scan customer │ │ ├── columns: c_id:24!null c_d_id:25!null c_w_id:26!null c_first:27 c_middle:28 c_last:29 c_street_1:30 c_street_2:31 c_city:32 c_state:33 c_zip:34 c_phone:35 c_since:36 c_credit:37 c_credit_lim:38 c_discount:39 c_balance:40 c_ytd_payment:41 c_payment_cnt:42 c_delivery_cnt:43 c_data:44 │ │ ├── constraint: /26/25/24: [/10/5/1343 - /10/5/1343] @@ -644,10 +644,11 @@ project │ │ └── c_balance:40 - 3860.61 │ ├── assignment-cast: DECIMAL(12,2) [as=c_ytd_payment_cast:52, outer=(41), immutable] │ │ └── c_ytd_payment:41 + 3860.61 - │ ├── c_payment_cnt:42 + 1 [as=c_payment_cnt_new:49, outer=(42), immutable] - │ └── CASE c_credit:37 WHEN 'BC' THEN left((((((c_id:24::STRING || c_d_id:25::STRING) || c_w_id:26::STRING) || '5') || '10') || '3860.61') || c_data:44, 500)::VARCHAR(500) ELSE c_data:44 END [as=c_data_new:50, outer=(24-26,37,44), immutable] + │ ├── assignment-cast: VARCHAR(500) [as=c_data_cast:53, outer=(24-26,37,44), immutable] + │ │ └── CASE c_credit:37 WHEN 'BC' THEN left((((((c_id:24::STRING || c_d_id:25::STRING) || c_w_id:26::STRING) || '5') || '10') || '3860.61') || c_data:44, 500)::VARCHAR ELSE c_data:44::VARCHAR END + │ └── c_payment_cnt:42 + 1 [as=c_payment_cnt_new:49, outer=(42), immutable] └── projections - └── CASE c_credit:14 WHEN 'BC' THEN left(c_data:21, 200) ELSE '' END [as=case:53, outer=(14,21), immutable] + └── CASE c_credit:14 WHEN 'BC' THEN left(c_data:21, 200) ELSE '' END [as=case:54, outer=(14,21), immutable] opt format=hide-qual isolation=ReadCommitted INSERT INTO history diff --git a/pkg/sql/opt/xform/testdata/rules/select b/pkg/sql/opt/xform/testdata/rules/select index 2fc659f31215..40c221e12884 100644 --- a/pkg/sql/opt/xform/testdata/rules/select +++ b/pkg/sql/opt/xform/testdata/rules/select @@ -13064,7 +13064,7 @@ select ├── scan t85356 │ └── columns: c0:1 └── filters - ├── c0:1 = CASE WHEN (c0:1 IS NOT DISTINCT FROM CAST(NULL AS DECIMAL(15,3))) AND CAST(NULL AS BOOL) THEN c0:1 ELSE 1.100 END [outer=(1), immutable, constraints=(/1: (/NULL - ])] + ├── c0:1 = CASE WHEN (c0:1 IS NOT DISTINCT FROM CAST(NULL AS DECIMAL(15,3))) AND CAST(NULL AS BOOL) THEN c0:1::DECIMAL ELSE 1.1 END [outer=(1), immutable, constraints=(/1: (/NULL - ])] └── c0:1 = 2 [outer=(1), immutable, constraints=(/1: [/2 - /2]; tight), fd=()-->(1)] exec-ddl diff --git a/pkg/sql/schemachanger/comparator_generated_test.go b/pkg/sql/schemachanger/comparator_generated_test.go index cb3ff73fd16f..44f902441243 100644 --- a/pkg/sql/schemachanger/comparator_generated_test.go +++ b/pkg/sql/schemachanger/comparator_generated_test.go @@ -163,6 +163,11 @@ func TestSchemaChangeComparator_bit(t *testing.T) { var logicTestFile = "pkg/sql/logictest/testdata/logic_test/bit" runSchemaChangeComparatorTest(t, logicTestFile) } +func TestSchemaChangeComparator_bpchar(t *testing.T) { + defer leaktest.AfterTest(t)() + var logicTestFile = "pkg/sql/logictest/testdata/logic_test/bpchar" + runSchemaChangeComparatorTest(t, logicTestFile) +} func TestSchemaChangeComparator_builtin_function(t *testing.T) { defer leaktest.AfterTest(t)() var logicTestFile = "pkg/sql/logictest/testdata/logic_test/builtin_function" @@ -183,6 +188,11 @@ func TestSchemaChangeComparator_cascade(t *testing.T) { var logicTestFile = "pkg/sql/logictest/testdata/logic_test/cascade" runSchemaChangeComparatorTest(t, logicTestFile) } +func TestSchemaChangeComparator_case(t *testing.T) { + defer leaktest.AfterTest(t)() + var logicTestFile = "pkg/sql/logictest/testdata/logic_test/case" + runSchemaChangeComparatorTest(t, logicTestFile) +} func TestSchemaChangeComparator_case_sensitive_names(t *testing.T) { defer leaktest.AfterTest(t)() var logicTestFile = "pkg/sql/logictest/testdata/logic_test/case_sensitive_names" diff --git a/pkg/sql/sem/builtins/fixed_oids.go b/pkg/sql/sem/builtins/fixed_oids.go index d4d6a27ca53e..b04742f38312 100644 --- a/pkg/sql/sem/builtins/fixed_oids.go +++ b/pkg/sql/sem/builtins/fixed_oids.go @@ -1840,10 +1840,10 @@ var builtinOidsArray = []string{ 1867: `geography_recv(input: anyelement) -> geography`, 1868: `geography_out(geography: geography) -> bytes`, 1869: `geography_in(input: anyelement) -> geography`, - 1870: `bpcharsend(char: char) -> bytes`, - 1871: `bpcharrecv(input: anyelement) -> char`, - 1872: `bpcharout(char: char) -> bytes`, - 1873: `bpcharin(input: anyelement) -> char`, + 1870: `bpcharsend(bpchar: bpchar) -> bytes`, + 1871: `bpcharrecv(input: anyelement) -> bpchar`, + 1872: `bpcharout(bpchar: bpchar) -> bytes`, + 1873: `bpcharin(input: anyelement) -> bpchar`, 1874: `geometry_send(geometry: geometry) -> bytes`, 1875: `geometry_recv(input: anyelement) -> geometry`, 1876: `geometry_out(geometry: geometry) -> bytes`, @@ -2289,30 +2289,30 @@ var builtinOidsArray = []string{ 2321: `date(timestamptz: timestamptz) -> date`, 2322: `date(string: string) -> date`, 2323: `date(date: date) -> date`, - 2324: `bpchar(uuid: uuid) -> char`, - 2325: `bpchar(bit: bit) -> char`, - 2326: `bpchar(box2d: box2d) -> char`, - 2327: `bpchar(interval: interval) -> char`, - 2328: `bpchar(decimal: decimal) -> char`, - 2329: `bpchar(tuple: tuple) -> char`, - 2330: `bpchar(date: date) -> char`, - 2331: `bpchar(oid: oid) -> char`, - 2332: `bpchar(geometry: geometry) -> char`, - 2333: `bpchar(time: time) -> char`, - 2334: `bpchar(tsvector: tsvector) -> char`, - 2335: `bpchar(bool: bool) -> char`, - 2336: `bpchar(int: int) -> char`, - 2337: `bpchar(timestamp: timestamp) -> char`, - 2338: `bpchar(timestamptz: timestamptz) -> char`, - 2339: `bpchar(tsquery: tsquery) -> char`, - 2340: `bpchar(float: float) -> char`, - 2341: `bpchar(geography: geography) -> char`, - 2342: `bpchar(inet: inet) -> char`, - 2343: `bpchar(timetz: timetz) -> char`, - 2344: `bpchar(void: void) -> char`, - 2345: `bpchar(bytes: bytes) -> char`, - 2346: `bpchar(jsonb: jsonb) -> char`, - 2347: `bpchar(string: string) -> char`, + 2324: `bpchar(uuid: uuid) -> bpchar`, + 2325: `bpchar(bit: bit) -> bpchar`, + 2326: `bpchar(box2d: box2d) -> bpchar`, + 2327: `bpchar(interval: interval) -> bpchar`, + 2328: `bpchar(decimal: decimal) -> bpchar`, + 2329: `bpchar(tuple: tuple) -> bpchar`, + 2330: `bpchar(date: date) -> bpchar`, + 2331: `bpchar(oid: oid) -> bpchar`, + 2332: `bpchar(geometry: geometry) -> bpchar`, + 2333: `bpchar(time: time) -> bpchar`, + 2334: `bpchar(tsvector: tsvector) -> bpchar`, + 2335: `bpchar(bool: bool) -> bpchar`, + 2336: `bpchar(int: int) -> bpchar`, + 2337: `bpchar(timestamp: timestamp) -> bpchar`, + 2338: `bpchar(timestamptz: timestamptz) -> bpchar`, + 2339: `bpchar(tsquery: tsquery) -> bpchar`, + 2340: `bpchar(float: float) -> bpchar`, + 2341: `bpchar(geography: geography) -> bpchar`, + 2342: `bpchar(inet: inet) -> bpchar`, + 2343: `bpchar(timetz: timetz) -> bpchar`, + 2344: `bpchar(void: void) -> bpchar`, + 2345: `bpchar(bytes: bytes) -> bpchar`, + 2346: `bpchar(jsonb: jsonb) -> bpchar`, + 2347: `bpchar(string: string) -> bpchar`, 2348: `numeric(interval: interval) -> decimal`, 2349: `numeric(decimal: decimal) -> decimal`, 2350: `numeric(date: date) -> decimal`, @@ -2387,7 +2387,7 @@ var builtinOidsArray = []string{ 2419: `pg_lsn(pg_lsn: pg_lsn) -> pg_lsn`, 2420: `varchar(pg_lsn: pg_lsn) -> varchar`, 2421: `text(pg_lsn: pg_lsn) -> string`, - 2422: `bpchar(pg_lsn: pg_lsn) -> char`, + 2422: `bpchar(pg_lsn: pg_lsn) -> bpchar`, 2423: `name(pg_lsn: pg_lsn) -> name`, 2424: `char(pg_lsn: pg_lsn) -> "char"`, 2425: `max(arg1: pg_lsn) -> anyelement`, @@ -2467,7 +2467,7 @@ var builtinOidsArray = []string{ 2501: `refcursorrecv(input: anyelement) -> refcursor`, 2502: `refcursor(refcursor: refcursor) -> refcursor`, 2503: `refcursor(string: string) -> refcursor`, - 2504: `bpchar(refcursor: refcursor) -> char`, + 2504: `bpchar(refcursor: refcursor) -> bpchar`, 2505: `char(refcursor: refcursor) -> "char"`, 2506: `name(refcursor: refcursor) -> name`, 2507: `text(refcursor: refcursor) -> string`, diff --git a/pkg/sql/sem/tree/overload.go b/pkg/sql/sem/tree/overload.go index 8a45511005b4..fc43561ee670 100644 --- a/pkg/sql/sem/tree/overload.go +++ b/pkg/sql/sem/tree/overload.go @@ -363,7 +363,7 @@ type TypeList interface { // Match checks if all types in the TypeList match the corresponding elements in types. Match(types []*types.T) bool // MatchIdentical is similar to match but checks that the types are identical matches, - //instead of equivalent matches. See types.T.Equivalent and types.T.Identical. + // instead of equivalent matches. See types.T.Equivalent and types.T.Identical. MatchIdentical(types []*types.T) bool // MatchAt checks if the parameter type at index i of the TypeList matches type typ. // In all implementations, types.Null will match with each parameter type, allowing @@ -439,7 +439,11 @@ func (p ParamTypes) MatchAt(typ *types.T, i int) bool { // MatchAtIdentical is part of the TypeList interface. func (p ParamTypes) MatchAtIdentical(typ *types.T, i int) bool { - return i < len(p) && (typ.Family() == types.UnknownFamily || p[i].Typ.Identical(typ)) + return i < len(p) && (typ.Family() == types.UnknownFamily || + p[i].Typ.Identical(typ) || + // Special case for CHAR, CHAR(N), and BPCHAR which are not "identical" + // but have the same OID. See #129007. + (p[i].Typ.Oid() == oid.T_bpchar && typ.Oid() == oid.T_bpchar)) } // MatchLen is part of the TypeList interface. diff --git a/pkg/sql/types/types.go b/pkg/sql/types/types.go index 9f21c9cb5fe8..737f1763a365 100644 --- a/pkg/sql/types/types.go +++ b/pkg/sql/types/types.go @@ -95,6 +95,7 @@ import ( // | VARCHAR(N) | STRING | T_varchar | 0 | N | // | CHAR | STRING | T_bpchar | 0 | 1 | // | CHAR(N) | STRING | T_bpchar | 0 | N | +// | BPCHAR | STRING | T_bpchar | 0 | 0 | // | "char" | STRING | T_char | 0 | 0 | // | NAME | STRING | T_name | 0 | 0 | // | | | | | | @@ -1565,6 +1566,9 @@ func (t *T) Name() string { case oid.T_text: return "string" case oid.T_bpchar: + if t.Width() == 0 { + return "bpchar" + } return "char" case oid.T_char: // Yes, that's the name. The ways of PostgreSQL are inscrutable. @@ -2829,6 +2833,9 @@ func (t *T) stringTypeSQL() string { case oid.T_varchar: typName = "VARCHAR" case oid.T_bpchar: + if t.Width() == 0 { + return "BPCHAR" + } typName = "CHAR" case oid.T_char: // Yes, that's the name. The ways of PostgreSQL are inscrutable.