From 9a13053ebef71837bee89215ed09256ce80f6eab Mon Sep 17 00:00:00 2001 From: Marcus Gartner Date: Mon, 13 Dec 2021 15:37:30 -0500 Subject: [PATCH] opt: eliminate assignment casts with identical source and target types The `EliminateAssignmentCast` rule has been combined with the `EliminateCast` rule. Now an assignment cast is eliminated if the source and target type are identical. This now possible thanks to some changes to type resolution, including: 1. Placeholder types are resolved with unspecified type modifiers. This ensures that assignment casts won't be eliminated if the a placeholder value does not conform to the target type's modifiers. 2. When constant integer `NumVal`s are resolved as a typed-value, they are validated to ensure they fit within their type's width. There may be more types we need to perform similar validation for, such as floats (see #73743). 3. Columns in values expressions with values that have non-identical types but the same type OID will be typed with type modifiers. For example, if a values expression has a CHAR(1) value and a CHAR(3) value in the same column, the column will be typed as a CHAR without an explicit width. Fixes #73450 Release note (bug fix): A bug has been fixed that caused incorrect evaluation of placeholder values in EXECUTE statements. The bug presented when the PREPARE statement cast a placeholder value, for example `PREPARE s AS SELECT $1::INT2`. If the assigned value for `$1` exceeded the maximum width value of the cast target type, the result value of the cast could be incorrect. This bug has been present since version 19.1 or earlier. --- pkg/sql/logictest/testdata/logic_test/cast | 63 +++++++++++++++++++ pkg/sql/opt/exec/execbuilder/testdata/insert | 18 +++--- pkg/sql/opt/norm/rules/scalar.opt | 24 +++---- .../opt/norm/testdata/rules/fold_constants | 2 +- pkg/sql/opt/norm/testdata/rules/groupby | 47 +++----------- pkg/sql/opt/norm/testdata/rules/project | 17 ++--- pkg/sql/opt/norm/testdata/rules/scalar | 37 +++-------- pkg/sql/opt/optbuilder/testdata/insert | 4 +- pkg/sql/opt/optbuilder/values.go | 2 + pkg/sql/opt/xform/testdata/external/tpce | 12 ++-- .../opt/xform/testdata/external/tpce-no-stats | 12 ++-- pkg/sql/opt/xform/testdata/physprops/ordering | 59 ++++++++--------- pkg/sql/opt/xform/testdata/rules/groupby | 34 ++++++---- pkg/sql/sem/tree/cast.go | 4 +- pkg/sql/sem/tree/constant.go | 2 +- pkg/sql/sem/tree/testdata/eval/int_arith | 28 +++++++-- pkg/sql/sem/tree/type_check.go | 2 +- 17 files changed, 198 insertions(+), 169 deletions(-) diff --git a/pkg/sql/logictest/testdata/logic_test/cast b/pkg/sql/logictest/testdata/logic_test/cast index 0d56b1679a7..58d9a4aeec4 100644 --- a/pkg/sql/logictest/testdata/logic_test/cast +++ b/pkg/sql/logictest/testdata/logic_test/cast @@ -8,6 +8,8 @@ CREATE TABLE assn_cast ( qc "char", b BIT, i INT, + i2 INT2, + f4 FLOAT4, t timestamp, d DECIMAL(10, 0), a DECIMAL(10, 0)[], @@ -141,6 +143,33 @@ EXECUTE insert_i('1') statement error value type string doesn't match type int of column \"i\" INSERT INTO assn_cast(i) VALUES ('1'::STRING) +statement error integer out of range for type int2 +INSERT INTO assn_cast(i2) VALUES (999999999) + +statement ok +PREPARE insert_i2 AS INSERT INTO assn_cast(i2) VALUES ($1) + +statement error integer out of range for type int2 +EXECUTE insert_i2(99999999) + +statement ok +INSERT INTO assn_cast(f4) VALUES (18754999.99) + +statement ok +PREPARE insert_f4 AS INSERT INTO assn_cast(f4) VALUES ($1) + +statement ok +EXECUTE insert_f4(18754999.99) + +# TODO(mgartner): Values are not correctly truncated when cast to FLOAT4, either +# in an assignment or explicit context. The columns should have a value of +# 1.8755e+07. See #73743. +query F +SELECT f4 FROM assn_cast WHERE f4 IS NOT NULL +---- +1.875499999e+07 +1.875499999e+07 + statement ok INSERT INTO assn_cast(t) VALUES ('1970-01-01'::timestamptz) @@ -350,3 +379,37 @@ SELECT '-'::regclass, '-'::regclass::oid, '-'::regrole, '-'::regrole::oid ---- - 0 - 0 - 0 - 0 - 0 - 0 + +# Regression test for #73450. Eliding casts should not cause incorrect results. +subtest regression_73450 + +statement ok +PREPARE s73450_a AS SELECT $1::INT2 + +statement error integer out of range for type int2 +EXECUTE s73450_a(999999) + +statement ok +PREPARE s73450_b AS SELECT $1::CHAR + +query T +EXECUTE s73450_b('foo') +---- +f + +statement ok +CREATE TABLE t73450 (c CHAR); +INSERT INTO t73450 VALUES ('f') + +query T +SELECT * FROM t73450 WHERE c = 'foo'::CHAR +---- +f + +statement ok +PREPARE s73450_c AS SELECT * FROM t73450 WHERE c = $1::CHAR + +query T +EXECUTE s73450_c('foo') +---- +f diff --git a/pkg/sql/opt/exec/execbuilder/testdata/insert b/pkg/sql/opt/exec/execbuilder/testdata/insert index 304b05a5dd9..1bef5b1226c 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/insert +++ b/pkg/sql/opt/exec/execbuilder/testdata/insert @@ -318,8 +318,8 @@ vectorized: true │ columns: (x, v, rowid_default) │ estimated row count: 10 (missing stats) │ render rowid_default: unique_rowid() - │ render x: crdb_internal.assignment_cast(x, NULL::INT8) - │ render v: crdb_internal.assignment_cast(v, NULL::INT8) + │ render x: x + │ render v: v │ └── • top-k │ columns: (x, v) @@ -355,8 +355,8 @@ vectorized: true │ columns: (x, v, rowid_default) │ estimated row count: 1 (missing stats) │ render rowid_default: unique_rowid() - │ render x: crdb_internal.assignment_cast(x, NULL::INT8) - │ render v: crdb_internal.assignment_cast(v, NULL::INT8) + │ render x: x + │ render v: v │ └── • scan columns: (x, v) @@ -471,8 +471,8 @@ vectorized: true │ columns: (length, "?column?", rowid_default) │ estimated row count: 10 (missing stats) │ render rowid_default: unique_rowid() - │ render length: crdb_internal.assignment_cast(length, NULL::INT8) - │ render ?column?: crdb_internal.assignment_cast("?column?", NULL::INT8) + │ render length: length + │ render ?column?: "?column?" │ └── • top-k │ columns: (length, "?column?", column12) @@ -600,9 +600,9 @@ vectorized: true │ columns: (a, b, c, rowid_default) │ estimated row count: 1,000 (missing stats) │ render rowid_default: unique_rowid() - │ render a: crdb_internal.assignment_cast(a, NULL::INT8) - │ render b: crdb_internal.assignment_cast(b, NULL::INT8) - │ render c: crdb_internal.assignment_cast(c, NULL::INT8) + │ render a: a + │ render b: b + │ render c: c │ └── • scan columns: (a, b, c) diff --git a/pkg/sql/opt/norm/rules/scalar.opt b/pkg/sql/opt/norm/rules/scalar.opt index 414293faaf6..ed3b41a1d35 100644 --- a/pkg/sql/opt/norm/rules/scalar.opt +++ b/pkg/sql/opt/norm/rules/scalar.opt @@ -58,26 +58,18 @@ $item => (SimplifyCoalesce $args) -# EliminateCast discards the cast operator if its input already has a type -# that's identical to the desired static type. +# EliminateCast discards a cast or assignment cast operator if its input already +# has a type that's identical to the desired static type. # # Note that CastExpr removes unnecessary casts during type-checking; this rule # can still be helpful if some other rule creates an unnecessary CastExpr. -[EliminateCast, Normalize] -(Cast $input:* $targetTyp:* & (HasColType $input $targetTyp)) -=> -$input - -# EliminateAssignmentCast discards the assignment cast operator if the input -# type and the target type are identical and the assignment cast is guaranteed -# to be a no-op. See AssignmentCastIsNoop for an explanation of what makes an -# assignment cast a no-op. -[EliminateAssignmentCast, Normalize] -(AssignmentCast +# +# EliminateCast is marked as high-priority so that it matches before +# FoldAssignmentCast. +[EliminateCast, Normalize, HighPriority] +(Cast | AssignmentCast $input:* - $targetTyp:* & - (HasColType $input $targetTyp) & - (AssignmentCastIsNoop $targetTyp) + $targetTyp:* & (HasColType $input $targetTyp) ) => $input diff --git a/pkg/sql/opt/norm/testdata/rules/fold_constants b/pkg/sql/opt/norm/testdata/rules/fold_constants index 58456c50c66..2a679a63619 100644 --- a/pkg/sql/opt/norm/testdata/rules/fold_constants +++ b/pkg/sql/opt/norm/testdata/rules/fold_constants @@ -905,7 +905,7 @@ insert assn_cast # Do not fold non-constants even if they have an identical type as the target # type. -norm expect-not=FoldAssignmentCast +norm expect-not=FoldAssignmentCast disable=EliminateCast INSERT INTO assn_cast(c) VALUES ($1::CHAR) ---- insert assn_cast diff --git a/pkg/sql/opt/norm/testdata/rules/groupby b/pkg/sql/opt/norm/testdata/rules/groupby index d41cb882560..1685fafe221 100644 --- a/pkg/sql/opt/norm/testdata/rules/groupby +++ b/pkg/sql/opt/norm/testdata/rules/groupby @@ -953,17 +953,14 @@ insert xy └── limit ├── columns: y:9!null y_default:10 ├── cardinality: [0 - 1] - ├── immutable ├── key: () ├── fd: ()-->(9,10) ├── anti-join (hash) │ ├── columns: y:9!null y_default:10 - │ ├── immutable │ ├── fd: ()-->(9,10) │ ├── limit hint: 1.00 │ ├── project │ │ ├── columns: y_default:10 y:9!null - │ │ ├── immutable │ │ ├── fd: ()-->(9,10) │ │ ├── select │ │ │ ├── columns: xy.y:6!null @@ -974,8 +971,7 @@ insert xy │ │ │ └── xy.y:6 = 0 [outer=(6), constraints=(/6: [/0 - /0]; tight), fd=()-->(6)] │ │ └── projections │ │ ├── CAST(NULL AS INT8) [as=y_default:10] - │ │ └── assignment-cast: INT8 [as=y:9, outer=(6), immutable] - │ │ └── xy.y:6 + │ │ └── xy.y:6 [as=y:9, outer=(6)] │ ├── scan xy │ │ ├── columns: x:11!null │ │ └── key: (11) @@ -1056,16 +1052,13 @@ insert xy └── upsert-distinct-on ├── columns: y:9 y_default:10 ├── grouping columns: y:9 - ├── immutable ├── lax-key: (9) ├── fd: ()-->(9,10) ├── anti-join (hash) │ ├── columns: y:9 y_default:10 - │ ├── immutable │ ├── fd: ()-->(9,10) │ ├── project │ │ ├── columns: y_default:10 y:9 - │ │ ├── immutable │ │ ├── fd: ()-->(9,10) │ │ ├── select │ │ │ ├── columns: xy.y:6 @@ -1076,8 +1069,7 @@ insert xy │ │ │ └── xy.y:6 IS NULL [outer=(6), constraints=(/6: [/NULL - /NULL]; tight), fd=()-->(6)] │ │ └── projections │ │ ├── CAST(NULL AS INT8) [as=y_default:10] - │ │ └── assignment-cast: INT8 [as=y:9, outer=(6), immutable] - │ │ └── xy.y:6 + │ │ └── xy.y:6 [as=y:9, outer=(6)] │ ├── scan xy │ │ ├── columns: x:11!null │ │ └── key: (11) @@ -1687,17 +1679,14 @@ insert a └── limit ├── columns: "?column?":17!null i:18!null "?column?":19!null f_default:20 j_default:21 ├── cardinality: [0 - 1] - ├── immutable ├── key: () ├── fd: ()-->(17-21) ├── anti-join (hash) │ ├── columns: "?column?":17!null i:18!null "?column?":19!null f_default:20 j_default:21 - │ ├── immutable │ ├── fd: ()-->(17-21) │ ├── limit hint: 1.00 │ ├── project │ │ ├── columns: f_default:20 j_default:21 "?column?":17!null i:18!null "?column?":19!null - │ │ ├── immutable │ │ ├── fd: ()-->(17-21) │ │ ├── select │ │ │ ├── columns: a.i:9!null @@ -1710,8 +1699,7 @@ insert a │ │ ├── CAST(NULL AS FLOAT8) [as=f_default:20] │ │ ├── CAST(NULL AS JSONB) [as=j_default:21] │ │ ├── 1 [as="?column?":17] - │ │ ├── assignment-cast: INT8 [as=i:18, outer=(9), immutable] - │ │ │ └── a.i:9 + │ │ ├── a.i:9 [as=i:18, outer=(9)] │ │ └── 'foo' [as="?column?":19] │ ├── select │ │ ├── columns: a.i:23!null s:25!null @@ -2465,18 +2453,8 @@ insert a │ │ │ │ │ ├── columns: column1:12 column2:13!null column3:14!null column4:15!null │ │ │ │ │ ├── cardinality: [2 - 2] │ │ │ │ │ ├── volatile - │ │ │ │ │ ├── tuple - │ │ │ │ │ │ ├── assignment-cast: INT8 - │ │ │ │ │ │ │ └── unique_rowid() - │ │ │ │ │ │ ├── 'foo' - │ │ │ │ │ │ ├── 1 - │ │ │ │ │ │ └── 1.0 - │ │ │ │ │ └── tuple - │ │ │ │ │ ├── assignment-cast: INT8 - │ │ │ │ │ │ └── unique_rowid() - │ │ │ │ │ ├── 'bar' - │ │ │ │ │ ├── 2 - │ │ │ │ │ └── 2.0 + │ │ │ │ │ ├── (unique_rowid(), 'foo', 1, 1.0) + │ │ │ │ │ └── (unique_rowid(), 'bar', 2, 2.0) │ │ │ │ └── projections │ │ │ │ └── CAST(NULL AS JSONB) [as=j_default:16] │ │ │ ├── scan a @@ -2526,27 +2504,22 @@ insert a └── upsert-distinct-on ├── columns: i:16!null "?column?":17!null i:18!null f_default:19 j_default:20 ├── grouping columns: i:18!null - ├── immutable ├── key: (18) - ├── fd: ()-->(17,19,20), (18)-->(16,17,19,20) + ├── fd: ()-->(17,19,20), (16)==(18), (18)==(16), (18)-->(16,17,19,20) ├── anti-join (hash) │ ├── columns: i:16!null "?column?":17!null i:18!null f_default:19 j_default:20 - │ ├── immutable - │ ├── fd: ()-->(17,19,20) + │ ├── fd: ()-->(17,19,20), (16)==(18), (18)==(16) │ ├── project │ │ ├── columns: f_default:19 j_default:20 i:16!null "?column?":17!null i:18!null - │ │ ├── immutable - │ │ ├── fd: ()-->(17,19,20) + │ │ ├── fd: ()-->(17,19,20), (16)==(18), (18)==(16) │ │ ├── scan a │ │ │ └── columns: a.i:9!null │ │ └── projections │ │ ├── CAST(NULL AS FLOAT8) [as=f_default:19] │ │ ├── CAST(NULL AS JSONB) [as=j_default:20] - │ │ ├── assignment-cast: INT8 [as=i:16, outer=(9), immutable] - │ │ │ └── a.i:9 + │ │ ├── a.i:9 [as=i:16, outer=(9)] │ │ ├── 'foo' [as="?column?":17] - │ │ └── assignment-cast: INT8 [as=i:18, outer=(9), immutable] - │ │ └── a.i:9 + │ │ └── a.i:9 [as=i:18, outer=(9)] │ ├── select │ │ ├── columns: a.i:22!null s:24!null │ │ ├── key: (22) diff --git a/pkg/sql/opt/norm/testdata/rules/project b/pkg/sql/opt/norm/testdata/rules/project index 7dcefe0eb18..9fab2423be1 100644 --- a/pkg/sql/opt/norm/testdata/rules/project +++ b/pkg/sql/opt/norm/testdata/rules/project @@ -1103,10 +1103,8 @@ insert assn_cast │ │ └── $1 │ ├── assignment-cast: "char" │ │ └── $2 - │ ├── assignment-cast: INT8 - │ │ └── $3 - │ └── assignment-cast: STRING - │ └── $4 + │ ├── $3 + │ └── $4 └── projections └── unique_rowid() [as=rowid_default:16, volatile] @@ -1158,12 +1156,12 @@ insert assn_cast ├── cardinality: [0 - 0] ├── volatile, mutations └── project - ├── columns: c_default:12 qc_default:13 rowid_default:14 x:10!null text:11!null + ├── columns: c_default:12 qc_default:13 rowid_default:14 text:11!null x:10!null ├── cardinality: [2 - 2] ├── volatile - ├── fd: ()-->(12,13) + ├── fd: ()-->(12,13), (10)-->(11) ├── values - │ ├── columns: column1:8!null + │ ├── columns: x:10!null │ ├── cardinality: [2 - 2] │ ├── (1,) │ └── (2,) @@ -1171,10 +1169,7 @@ insert assn_cast ├── CAST(NULL AS CHAR) [as=c_default:12] ├── CAST(NULL AS "char") [as=qc_default:13] ├── unique_rowid() [as=rowid_default:14, volatile] - ├── assignment-cast: INT8 [as=x:10, outer=(8), immutable] - │ └── column1:8 - └── assignment-cast: STRING [as=text:11, outer=(8), immutable] - └── (column1:8 + 1)::STRING + └── (x:10 + 1)::STRING [as=text:11, outer=(10), immutable] # Do not match if there are no assignment cast projections. norm expect-not=PushAssignmentCastsIntoValues diff --git a/pkg/sql/opt/norm/testdata/rules/scalar b/pkg/sql/opt/norm/testdata/rules/scalar index eba5d08c25f..bfe15ee0a46 100644 --- a/pkg/sql/opt/norm/testdata/rules/scalar +++ b/pkg/sql/opt/norm/testdata/rules/scalar @@ -225,11 +225,8 @@ project ├── ARRAY[a.i:2, 2]::OIDVECTOR [as=array:14, outer=(2), stable] └── ARRAY[a.i:2, 2]::INT2VECTOR [as=array:15, outer=(2), immutable] -# -------------------------------------------------- -# EliminateAssignmentCast -# -------------------------------------------------- - -norm expect=EliminateAssignmentCast +# Eliminate assignment casts. +norm expect=EliminateCast INSERT INTO u VALUES ('b1621d9e-df89-4e8a-89f5-82262bca907b') ---- insert u @@ -245,7 +242,8 @@ insert u ├── fd: ()-->(5) └── ('b1621d9e-df89-4e8a-89f5-82262bca907b',) -norm expect=EliminateAssignmentCast +# Eliminate assignment casts. +norm expect=EliminateCast INSERT INTO u VALUES (gen_random_uuid()) ---- insert u @@ -263,7 +261,7 @@ insert u └── (gen_random_uuid(),) # Do not eliminate assignment casts when the input type does not match the target type. -norm expect-not=EliminateAssignmentCast disable=FoldAssignmentCast +norm disable=FoldAssignmentCast INSERT INTO a (k, s) VALUES (1, 'b1621d9e-df89-4e8a-89f5-82262bca907b'::UUID) ---- insert a @@ -277,40 +275,19 @@ insert a ├── cardinality: [0 - 0] ├── volatile, mutations └── values - ├── columns: column1:10 column2:11 i_default:12 f_default:13 arr_default:14 + ├── columns: column1:10!null column2:11 i_default:12 f_default:13 arr_default:14 ├── cardinality: [1 - 1] ├── immutable ├── key: () ├── fd: ()-->(10-14) └── tuple - ├── assignment-cast: INT8 - │ └── 1 + ├── 1 ├── assignment-cast: STRING │ └── 'b1621d9e-df89-4e8a-89f5-82262bca907b' ├── CAST(NULL AS INT8) ├── CAST(NULL AS FLOAT8) └── CAST(NULL AS INT8[]) -# Do not eliminate assignment casts for the CHAR type. -norm expect-not=EliminateAssignmentCast disable=FoldAssignmentCast -INSERT INTO c VALUES ('f'::CHAR) ----- -insert c - ├── columns: - ├── insert-mapping: - │ └── column1:5 => c:1 - ├── cardinality: [0 - 0] - ├── volatile, mutations - └── values - ├── columns: column1:5 - ├── cardinality: [1 - 1] - ├── immutable - ├── key: () - ├── fd: ()-->(5) - └── tuple - └── assignment-cast: CHAR - └── 'f' - # -------------------------------------------------- # NormalizeInConst # -------------------------------------------------- diff --git a/pkg/sql/opt/optbuilder/testdata/insert b/pkg/sql/opt/optbuilder/testdata/insert index d898828fe87..90320299bd2 100644 --- a/pkg/sql/opt/optbuilder/testdata/insert +++ b/pkg/sql/opt/optbuilder/testdata/insert @@ -1585,7 +1585,7 @@ insert defvals │ └── assignment-cast: INT8 [as=column1:7] │ └── column1:6 └── projections - ├── ARRAY[] [as=arr1_default:8] + ├── ARRAY[]::STRING(100)[] [as=arr1_default:8] └── ARRAY[] [as=arr2_default:9] exec-ddl @@ -1616,7 +1616,7 @@ insert defvals2 │ └── assignment-cast: INT8 [as=column1:7] │ └── column1:6 └── projections - ├── ARRAY[NULL] [as=arr1_default:8] + ├── ARRAY[NULL]::STRING(100)[] [as=arr1_default:8] └── ARRAY[NULL] [as=arr2_default:9] # Regression test for #67100. Do not prune check columns for INSERTs even if the diff --git a/pkg/sql/opt/optbuilder/values.go b/pkg/sql/opt/optbuilder/values.go index 1779d6697df..f4d2597aa09 100644 --- a/pkg/sql/opt/optbuilder/values.go +++ b/pkg/sql/opt/optbuilder/values.go @@ -86,6 +86,8 @@ func (b *Builder) buildValuesClause( } else if !typ.Equivalent(colTypes[colIdx]) { panic(pgerror.Newf(pgcode.DatatypeMismatch, "VALUES types %s and %s cannot be matched", typ, colTypes[colIdx])) + } else if !typ.Identical(colTypes[colIdx]) { + colTypes[colIdx] = colTypes[colIdx].WithoutTypeModifiers() } } elems[elemPos] = b.buildScalar(texpr, inScope, nil, nil, nil) diff --git a/pkg/sql/opt/xform/testdata/external/tpce b/pkg/sql/opt/xform/testdata/external/tpce index b617e724b0a..d2b99c7655d 100644 --- a/pkg/sql/opt/xform/testdata/external/tpce +++ b/pkg/sql/opt/xform/testdata/external/tpce @@ -677,10 +677,11 @@ with &2 (update_last_trade) │ │ │ └── "?column?":63 => trade_history.th_st_id:53 │ │ ├── input binding: &5 │ │ ├── volatile, mutations + │ │ ├── key: (51) │ │ ├── fd: ()-->(53) │ │ ├── project │ │ │ ├── columns: tr_t_id:62!null "?column?":63!null timestamp:64!null - │ │ │ ├── immutable + │ │ │ ├── key: (62) │ │ │ ├── fd: ()-->(63,64) │ │ │ ├── with-scan &3 (request_list) │ │ │ │ ├── columns: tr_t_id:56!null @@ -688,8 +689,7 @@ with &2 (update_last_trade) │ │ │ │ │ └── trade_request.tr_t_id:20 => tr_t_id:56 │ │ │ │ └── key: (56) │ │ │ └── projections - │ │ │ ├── assignment-cast: INT8 [as=tr_t_id:62, outer=(56), immutable] - │ │ │ │ └── tr_t_id:56 + │ │ │ ├── tr_t_id:56 [as=tr_t_id:62, outer=(56)] │ │ │ ├── 'SBMT' [as="?column?":63] │ │ │ └── '2020-06-15 22:27:42.148484' [as=timestamp:64] │ │ └── f-k-checks @@ -698,10 +698,12 @@ with &2 (update_last_trade) │ │ │ ├── columns: th_t_id:65!null │ │ │ ├── key columns: [65] = [66] │ │ │ ├── lookup columns are key + │ │ │ ├── key: (65) │ │ │ ├── with-scan &5 │ │ │ │ ├── columns: th_t_id:65!null - │ │ │ │ └── mapping: - │ │ │ │ └── tr_t_id:62 => th_t_id:65 + │ │ │ │ ├── mapping: + │ │ │ │ │ └── tr_t_id:62 => th_t_id:65 + │ │ │ │ └── key: (65) │ │ │ └── filters (true) │ │ └── f-k-checks-item: trade_history(th_st_id) -> status_type(st_id) │ │ └── anti-join (lookup status_type) diff --git a/pkg/sql/opt/xform/testdata/external/tpce-no-stats b/pkg/sql/opt/xform/testdata/external/tpce-no-stats index 1135486b38d..fa50fcc0c3b 100644 --- a/pkg/sql/opt/xform/testdata/external/tpce-no-stats +++ b/pkg/sql/opt/xform/testdata/external/tpce-no-stats @@ -695,10 +695,11 @@ with &2 (update_last_trade) │ │ │ └── "?column?":63 => trade_history.th_st_id:53 │ │ ├── input binding: &5 │ │ ├── volatile, mutations + │ │ ├── key: (51) │ │ ├── fd: ()-->(53) │ │ ├── project │ │ │ ├── columns: tr_t_id:62!null "?column?":63!null timestamp:64!null - │ │ │ ├── immutable + │ │ │ ├── key: (62) │ │ │ ├── fd: ()-->(63,64) │ │ │ ├── with-scan &3 (request_list) │ │ │ │ ├── columns: tr_t_id:56!null @@ -706,8 +707,7 @@ with &2 (update_last_trade) │ │ │ │ │ └── trade_request.tr_t_id:20 => tr_t_id:56 │ │ │ │ └── key: (56) │ │ │ └── projections - │ │ │ ├── assignment-cast: INT8 [as=tr_t_id:62, outer=(56), immutable] - │ │ │ │ └── tr_t_id:56 + │ │ │ ├── tr_t_id:56 [as=tr_t_id:62, outer=(56)] │ │ │ ├── 'SBMT' [as="?column?":63] │ │ │ └── '2020-06-15 22:27:42.148484' [as=timestamp:64] │ │ └── f-k-checks @@ -716,10 +716,12 @@ with &2 (update_last_trade) │ │ │ ├── columns: th_t_id:65!null │ │ │ ├── key columns: [65] = [66] │ │ │ ├── lookup columns are key + │ │ │ ├── key: (65) │ │ │ ├── with-scan &5 │ │ │ │ ├── columns: th_t_id:65!null - │ │ │ │ └── mapping: - │ │ │ │ └── tr_t_id:62 => th_t_id:65 + │ │ │ │ ├── mapping: + │ │ │ │ │ └── tr_t_id:62 => th_t_id:65 + │ │ │ │ └── key: (65) │ │ │ └── filters (true) │ │ └── f-k-checks-item: trade_history(th_st_id) -> status_type(st_id) │ │ └── anti-join (lookup status_type) diff --git a/pkg/sql/opt/xform/testdata/physprops/ordering b/pkg/sql/opt/xform/testdata/physprops/ordering index 0bafb91b8f8..ec74a0ae60d 100644 --- a/pkg/sql/opt/xform/testdata/physprops/ordering +++ b/pkg/sql/opt/xform/testdata/physprops/ordering @@ -1759,11 +1759,13 @@ sort ├── columns: a:14!null b:15!null c:16!null ├── cardinality: [0 - 2] ├── volatile, mutations + ├── key: (14-16) ├── ordering: +15 └── with &1 ├── columns: a:14!null b:15!null c:16!null ├── cardinality: [0 - 2] ├── volatile, mutations + ├── key: (14-16) ├── insert abc │ ├── columns: abc.a:1!null abc.b:2!null abc.c:3!null │ ├── insert-mapping: @@ -1772,10 +1774,11 @@ sort │ │ └── z:13 => abc.c:3 │ ├── cardinality: [0 - 2] │ ├── volatile, mutations + │ ├── key: (1-3) │ └── project │ ├── columns: x:11!null y:12!null z:13!null │ ├── cardinality: [0 - 2] - │ ├── immutable + │ ├── key: (11-13) │ ├── top-k │ │ ├── columns: xyz.x:6!null xyz.y:7!null xyz.z:8!null │ │ ├── internal-ordering: +7,+8 @@ -1786,19 +1789,17 @@ sort │ │ ├── columns: xyz.x:6!null xyz.y:7!null xyz.z:8!null │ │ └── key: (6-8) │ └── projections - │ ├── assignment-cast: INT8 [as=x:11, outer=(6), immutable] - │ │ └── xyz.x:6 - │ ├── assignment-cast: INT8 [as=y:12, outer=(7), immutable] - │ │ └── xyz.y:7 - │ └── assignment-cast: INT8 [as=z:13, outer=(8), immutable] - │ └── xyz.z:8 + │ ├── xyz.x:6 [as=x:11, outer=(6)] + │ ├── xyz.y:7 [as=y:12, outer=(7)] + │ └── xyz.z:8 [as=z:13, outer=(8)] └── with-scan &1 ├── columns: a:14!null b:15!null c:16!null ├── mapping: │ ├── abc.a:1 => a:14 │ ├── abc.b:2 => b:15 │ └── abc.c:3 => c:16 - └── cardinality: [0 - 2] + ├── cardinality: [0 - 2] + └── key: (14-16) # Verify that provided orderings are derived correctly. opt @@ -1826,17 +1827,13 @@ sort │ └── project │ ├── columns: b:13 c:14 d:15 │ ├── cardinality: [0 - 2] - │ ├── immutable │ ├── scan abcd@cd │ │ ├── columns: abcd.b:7 abcd.c:8 abcd.d:9 │ │ └── limit: 2 │ └── projections - │ ├── assignment-cast: INT8 [as=b:13, outer=(7), immutable] - │ │ └── abcd.b:7 - │ ├── assignment-cast: INT8 [as=c:14, outer=(8), immutable] - │ │ └── abcd.c:8 - │ └── assignment-cast: INT8 [as=d:15, outer=(9), immutable] - │ └── abcd.d:9 + │ ├── abcd.b:7 [as=b:13, outer=(7)] + │ ├── abcd.c:8 [as=c:14, outer=(8)] + │ └── abcd.d:9 [as=d:15, outer=(9)] └── with-scan &1 ├── columns: x:16!null y:17!null z:18!null ├── mapping: @@ -1875,17 +1872,13 @@ sort │ └── project │ ├── columns: b:13 c:14 d:15 │ ├── cardinality: [0 - 2] - │ ├── immutable │ ├── scan abcd@cd │ │ ├── columns: abcd.b:7 abcd.c:8 abcd.d:9 │ │ └── limit: 2 │ └── projections - │ ├── assignment-cast: INT8 [as=b:13, outer=(7), immutable] - │ │ └── abcd.b:7 - │ ├── assignment-cast: INT8 [as=c:14, outer=(8), immutable] - │ │ └── abcd.c:8 - │ └── assignment-cast: INT8 [as=d:15, outer=(9), immutable] - │ └── abcd.d:9 + │ ├── abcd.b:7 [as=b:13, outer=(7)] + │ ├── abcd.c:8 [as=c:14, outer=(8)] + │ └── abcd.d:9 [as=d:15, outer=(9)] └── select ├── columns: x:16!null y:17!null z:18!null ├── cardinality: [0 - 2] @@ -1907,6 +1900,7 @@ SELECT * FROM [INSERT INTO abc SELECT * FROM xyz ORDER BY y, z RETURNING *] with &1 ├── columns: a:14!null b:15!null c:16!null ├── volatile, mutations + ├── key: (14-16) ├── insert abc │ ├── columns: abc.a:1!null abc.b:2!null abc.c:3!null │ ├── insert-mapping: @@ -1914,25 +1908,24 @@ with &1 │ │ ├── y:12 => abc.b:2 │ │ └── z:13 => abc.c:3 │ ├── volatile, mutations + │ ├── key: (1-3) │ └── project │ ├── columns: x:11!null y:12!null z:13!null - │ ├── immutable + │ ├── key: (11-13) │ ├── scan xyz │ │ ├── columns: xyz.x:6!null xyz.y:7!null xyz.z:8!null │ │ └── key: (6-8) │ └── projections - │ ├── assignment-cast: INT8 [as=x:11, outer=(6), immutable] - │ │ └── xyz.x:6 - │ ├── assignment-cast: INT8 [as=y:12, outer=(7), immutable] - │ │ └── xyz.y:7 - │ └── assignment-cast: INT8 [as=z:13, outer=(8), immutable] - │ └── xyz.z:8 + │ ├── xyz.x:6 [as=x:11, outer=(6)] + │ ├── xyz.y:7 [as=y:12, outer=(7)] + │ └── xyz.z:8 [as=z:13, outer=(8)] └── with-scan &1 ├── columns: a:14!null b:15!null c:16!null - └── mapping: - ├── abc.a:1 => a:14 - ├── abc.b:2 => b:15 - └── abc.c:3 => c:16 + ├── mapping: + │ ├── abc.a:1 => a:14 + │ ├── abc.b:2 => b:15 + │ └── abc.c:3 => c:16 + └── key: (14-16) # -------------------------------------------------- # Update operator. diff --git a/pkg/sql/opt/xform/testdata/rules/groupby b/pkg/sql/opt/xform/testdata/rules/groupby index 37b92ce5b52..9f6182b80e4 100644 --- a/pkg/sql/opt/xform/testdata/rules/groupby +++ b/pkg/sql/opt/xform/testdata/rules/groupby @@ -2246,27 +2246,36 @@ memo (optimized, ~27KB, required=[presentation: w:12] [ordering: +13,+1]) memo INSERT INTO xyz SELECT v, w, 1.0 FROM kuvw ON CONFLICT (x) DO NOTHING ---- -memo (optimized, ~21KB, required=[]) +memo (optimized, ~23KB, required=[]) ├── G1: (insert G2 G3 G4 xyz) │ └── [] │ ├── best: (insert G2 G3 G4 xyz) - │ └── cost: 2219.17 - ├── G2: (upsert-distinct-on G5 G6 cols=(13)) + │ └── cost: 2209.02 + ├── G2: (upsert-distinct-on G5 G6 cols=(13)) (upsert-distinct-on G5 G6 cols=(13),ordering=+13 opt(15)) │ └── [] │ ├── best: (upsert-distinct-on G5 G6 cols=(13)) - │ └── cost: 2219.16 + │ └── cost: 2209.01 ├── G3: (unique-checks) ├── G4: (f-k-checks) - ├── G5: (anti-join G7 G8 G9) (lookup-join G7 G10 xyz,keyCols=[13],outCols=(13-16)) (lookup-join G7 G10 xyz@xy,keyCols=[13],outCols=(13-16)) + ├── G5: (anti-join G7 G8 G9) (merge-join G7 G8 G10 anti-join,+13,+16) (lookup-join G7 G10 xyz,keyCols=[13],outCols=(13-16)) (lookup-join G7 G10 xyz@xy,keyCols=[13],outCols=(13-16)) + │ ├── [ordering: +13 opt(15)] + │ │ ├── best: (merge-join G7="[ordering: +13 opt(15)]" G8="[ordering: +16]" G10 anti-join,+13,+16) + │ │ └── cost: 2208.98 │ └── [] - │ ├── best: (anti-join G7 G8 G9) - │ └── cost: 2219.13 + │ ├── best: (merge-join G7="[ordering: +13 opt(15)]" G8="[ordering: +16]" G10 anti-join,+13,+16) + │ └── cost: 2208.98 ├── G6: (aggregations G11 G12) ├── G7: (project G13 G14) + │ ├── [ordering: +13 opt(15)] + │ │ ├── best: (project G13="[ordering: +8]" G14) + │ │ └── cost: 1124.64 │ └── [] │ ├── best: (project G13 G14) │ └── cost: 1124.64 ├── G8: (scan xyz,cols=(16)) (scan xyz@xy,cols=(16)) (scan xyz@zyx,cols=(16)) (scan xyz@yy,cols=(16)) + │ ├── [ordering: +16] + │ │ ├── best: (scan xyz@xy,cols=(16)) + │ │ └── cost: 1054.32 │ └── [] │ ├── best: (scan xyz@xy,cols=(16)) │ └── cost: 1054.32 @@ -2275,6 +2284,9 @@ memo (optimized, ~21KB, required=[]) ├── G11: (first-agg G16) ├── G12: (first-agg G17) ├── G13: (scan kuvw,cols=(8,9)) (scan kuvw@uvw,cols=(8,9)) (scan kuvw@wvu,cols=(8,9)) (scan kuvw@vw,cols=(8,9)) (scan kuvw@w,cols=(8,9)) + │ ├── [ordering: +8] + │ │ ├── best: (scan kuvw@vw,cols=(8,9)) + │ │ └── cost: 1084.62 │ └── [] │ ├── best: (scan kuvw,cols=(8,9)) │ └── cost: 1084.62 @@ -2282,13 +2294,11 @@ memo (optimized, ~21KB, required=[]) ├── G15: (eq G21 G22) ├── G16: (variable w) ├── G17: (variable "?column?") - ├── G18: (assignment-cast G23) - ├── G19: (assignment-cast G24) + ├── G18: (variable kuvw.v) + ├── G19: (variable kuvw.w) ├── G20: (const 1.0) ├── G21: (variable v) - ├── G22: (variable x) - ├── G23: (variable kuvw.v) - └── G24: (variable kuvw.w) + └── G22: (variable x) # Ensure that streaming ensure-upsert-distinct-on will be used. memo diff --git a/pkg/sql/sem/tree/cast.go b/pkg/sql/sem/tree/cast.go index 6be3ef932d2..00a7160df1c 100644 --- a/pkg/sql/sem/tree/cast.go +++ b/pkg/sql/sem/tree/cast.go @@ -776,8 +776,8 @@ func ForEachCast(fn func(src, tgt oid.Oid)) { // ValidCast returns true if a valid cast exists from src to tgt in the given // context. func ValidCast(src, tgt *types.T, ctx CastContext) bool { - // If src and tgt are identical, a cast is valid in any context. - if src.Identical(tgt) { + // If src and tgt are the same type, a cast is valid in any context. + if src.Oid() == tgt.Oid() { return true } diff --git a/pkg/sql/sem/tree/constant.go b/pkg/sql/sem/tree/constant.go index 084aecc4405..ab12cf2dca4 100644 --- a/pkg/sql/sem/tree/constant.go +++ b/pkg/sql/sem/tree/constant.go @@ -305,7 +305,7 @@ func (expr *NumVal) ResolveAsType( return nil, err } } - return &expr.resInt, nil + return AdjustValueToType(typ, &expr.resInt) case types.FloatFamily: if strings.EqualFold(expr.origString, "NaN") { // We need to check NaN separately since expr.value is unknownVal for NaN. diff --git a/pkg/sql/sem/tree/testdata/eval/int_arith b/pkg/sql/sem/tree/testdata/eval/int_arith index 49f5e43f19d..d4abf790ee3 100644 --- a/pkg/sql/sem/tree/testdata/eval/int_arith +++ b/pkg/sql/sem/tree/testdata/eval/int_arith @@ -123,15 +123,25 @@ eval ---- integer out of range +eval +-32767:::int2 * -1:::int2 +---- +32767 + eval -32768:::int2 * -1:::int2 ---- -32768 +integer out of range for type int2 + +eval +-2147483647:::int4 * -1:::int4 +---- +2147483647 eval -2147483648:::int4 * -1:::int4 ---- -2147483648 +integer out of range for type int4 eval (-9223372036854775807:::int8 - 1) * -1:::int8 @@ -172,15 +182,25 @@ eval # Integer division. # ##################### +eval +-32767:::int2 / -1:::int2 +---- +32767 + eval -32768:::int2 / -1:::int2 ---- -32768 +integer out of range for type int2 + +eval +-2147483647:::int4 / -1:::int4 +---- +2147483647 eval -2147483648:::int4 / -1:::int4 ---- -2147483648 +integer out of range for type int4 eval (-9223372036854775807:::int8 - 1) / -1:::int8 diff --git a/pkg/sql/sem/tree/type_check.go b/pkg/sql/sem/tree/type_check.go index 339b6b8e283..df217e6a182 100644 --- a/pkg/sql/sem/tree/type_check.go +++ b/pkg/sql/sem/tree/type_check.go @@ -1546,7 +1546,7 @@ func (expr *Array) TypeCheck( ) (TypedExpr, error) { desiredParam := types.Any if desired.Family() == types.ArrayFamily { - desiredParam = desired.ArrayContents() + desiredParam = desired.ArrayContents().WithoutTypeModifiers() } if len(expr.Exprs) == 0 {