From f542e2e2b1c9e226b4c8541215a9b7f2911090b4 Mon Sep 17 00:00:00 2001 From: Marcus Gartner Date: Wed, 25 May 2022 17:27:47 -0400 Subject: [PATCH] opt: assignment casts of non-equivalent types in DEFAULT and ON UPDATE This commit allows the optimizer to build assignment casts for `DEFAULT` and `ON UPDATE` expressions that have types that are not equivalent with their column's type. If the cast from the `DEFAULT` or `ON UPDATE` expression type to the column type is not a valid assignment cast, an error is returned. In practice, this error should never occur because 1) it is currently impossible to create `DEFAULT` and `ON UPDATE` expressions that do not match their column's type (the tests here get around this limitation because they use the optimizer test catalog which has much looser restrictions) and 2) a future PR will allow creating `DEFAULT` and `ON UPDATE` expressions that do not match their column's type only if the cast from the expression type to the column type is a valid assignment cast. Unblocks #81071 Release note: None --- pkg/sql/opt/optbuilder/project.go | 2 +- pkg/sql/opt/optbuilder/testdata/insert | 69 +++++++++++++++----------- pkg/sql/opt/optbuilder/testdata/update | 60 +++++++++++++--------- 3 files changed, 79 insertions(+), 52 deletions(-) diff --git a/pkg/sql/opt/optbuilder/project.go b/pkg/sql/opt/optbuilder/project.go index 1f6d087231ca..ec7ce5845193 100644 --- a/pkg/sql/opt/optbuilder/project.go +++ b/pkg/sql/opt/optbuilder/project.go @@ -352,7 +352,7 @@ func (pb *projectionBuilder) Add( pb.outScope = pb.inScope.replace() pb.outScope.appendColumnsFromScope(pb.inScope) } - typedExpr := pb.inScope.resolveAndRequireType(expr, desiredType) + typedExpr := pb.inScope.resolveType(expr, desiredType) scopeCol := pb.outScope.addColumn(name, typedExpr) scalar := pb.b.buildScalar(typedExpr, pb.inScope, pb.outScope, scopeCol, nil) diff --git a/pkg/sql/opt/optbuilder/testdata/insert b/pkg/sql/opt/optbuilder/testdata/insert index 699a1e42a4bb..ceb00f6922fc 100644 --- a/pkg/sql/opt/optbuilder/testdata/insert +++ b/pkg/sql/opt/optbuilder/testdata/insert @@ -1333,52 +1333,65 @@ exec-ddl CREATE TABLE assn_cast_default ( k INT PRIMARY KEY, i2 INT2 DEFAULT 10::INT, + i3 INT DEFAULT 1.0::FLOAT, c1 CHAR DEFAULT 'foo', c2 CHAR DEFAULT 'bar'::TEXT, d1 DECIMAL(10, 0) DEFAULT 1.23, d2 DECIMAL(10, 0) DEFAULT 4.56::DECIMAL(10, 2), + b BOOL DEFAULT 1.0::FLOAT, s TEXT DEFAULT NULL ) ---- build -INSERT INTO assn_cast_default (k) VALUES (1) +INSERT INTO assn_cast_default (k, b) VALUES (1, true) ---- insert assn_cast_default ├── columns: ├── insert-mapping: - │ ├── column1:10 => k:1 - │ ├── i2_cast:17 => i2:2 - │ ├── c1_cast:18 => c1:3 - │ ├── c2_cast:19 => c2:4 - │ ├── d1_cast:20 => d1:5 - │ ├── d2_cast:21 => d2:6 - │ └── s_default:16 => s:7 + │ ├── column1:12 => k:1 + │ ├── i2_cast:21 => i2:2 + │ ├── i3_cast:22 => i3:3 + │ ├── c1_cast:23 => c1:4 + │ ├── c2_cast:24 => c2:5 + │ ├── d1_cast:25 => d1:6 + │ ├── d2_cast:26 => d2:7 + │ ├── column2:13 => b:8 + │ └── s_default:20 => s:9 └── project - ├── columns: i2_cast:17!null c1_cast:18!null c2_cast:19!null d1_cast:20!null d2_cast:21!null column1:10!null s_default:16 + ├── columns: i2_cast:21!null i3_cast:22!null c1_cast:23!null c2_cast:24!null d1_cast:25!null d2_cast:26!null column1:12!null column2:13!null s_default:20 ├── project - │ ├── columns: i2_default:11!null c1_default:12!null c2_default:13!null d1_default:14!null d2_default:15!null s_default:16 column1:10!null + │ ├── columns: i2_default:14!null i3_default:15!null c1_default:16!null c2_default:17!null d1_default:18!null d2_default:19!null s_default:20 column1:12!null column2:13!null │ ├── values - │ │ ├── columns: column1:10!null - │ │ └── (1,) + │ │ ├── columns: column1:12!null column2:13!null + │ │ └── (1, true) │ └── projections - │ ├── 10 [as=i2_default:11] - │ ├── 'foo' [as=c1_default:12] - │ ├── 'bar' [as=c2_default:13] - │ ├── 1.23 [as=d1_default:14] - │ ├── 4.56::DECIMAL(10,2) [as=d2_default:15] - │ └── NULL::STRING [as=s_default:16] + │ ├── 10 [as=i2_default:14] + │ ├── 1.0 [as=i3_default:15] + │ ├── 'foo' [as=c1_default:16] + │ ├── 'bar' [as=c2_default:17] + │ ├── 1.23 [as=d1_default:18] + │ ├── 4.56::DECIMAL(10,2) [as=d2_default:19] + │ └── NULL::STRING [as=s_default:20] └── projections - ├── assignment-cast: INT2 [as=i2_cast:17] - │ └── i2_default:11 - ├── assignment-cast: CHAR [as=c1_cast:18] - │ └── c1_default:12 - ├── assignment-cast: CHAR [as=c2_cast:19] - │ └── c2_default:13 - ├── assignment-cast: DECIMAL(10) [as=d1_cast:20] - │ └── d1_default:14 - └── assignment-cast: DECIMAL(10) [as=d2_cast:21] - └── d2_default:15 + ├── assignment-cast: INT2 [as=i2_cast:21] + │ └── i2_default:14 + ├── assignment-cast: INT8 [as=i3_cast:22] + │ └── i3_default:15 + ├── assignment-cast: CHAR [as=c1_cast:23] + │ └── c1_default:16 + ├── assignment-cast: CHAR [as=c2_cast:24] + │ └── c2_default:17 + ├── assignment-cast: DECIMAL(10) [as=d1_cast:25] + │ └── d1_default:18 + └── assignment-cast: DECIMAL(10) [as=d2_cast:26] + └── d2_default:19 + +# This fails because the cast from FLOAT to BOOL is not a valid assignment cast. +build +INSERT INTO assn_cast_default (k) VALUES (1) +---- +error (42804): value type float doesn't match type bool of column "b" exec-ddl CREATE TABLE assn_cast_comp ( diff --git a/pkg/sql/opt/optbuilder/testdata/update b/pkg/sql/opt/optbuilder/testdata/update index c5d2e0a36660..7ec5c56a7f74 100644 --- a/pkg/sql/opt/optbuilder/testdata/update +++ b/pkg/sql/opt/optbuilder/testdata/update @@ -81,9 +81,11 @@ CREATE TABLE assn_cast ( exec-ddl CREATE TABLE assn_cast_on_update ( i INT, + i2 INT ON UPDATE 1.0::FLOAT, d DECIMAL(10, 1) ON UPDATE 1.23, d2 DECIMAL(10, 1) ON UPDATE 1.23::DECIMAL(10, 2), - d_comp DECIMAL(10, 0) AS (d) STORED + d_comp DECIMAL(10, 0) AS (d) STORED, + b BOOL ON UPDATE 1.0::FLOAT ) ---- @@ -2218,42 +2220,54 @@ update assn_cast # Test ON UPDATE columns that require assignment casts. build -UPDATE assn_cast_on_update SET i=1 +UPDATE assn_cast_on_update SET i=1, b=true ---- update assn_cast_on_update ├── columns: - ├── fetch columns: i:8 d:9 d2:10 d_comp:11 rowid:12 + ├── fetch columns: i:10 i2:11 d:12 d2:13 d_comp:14 b:15 rowid:16 ├── update-mapping: - │ ├── i_new:15 => i:1 - │ ├── d_cast:18 => d:2 - │ ├── d2_cast:19 => d2:3 - │ └── d_comp_cast:20 => d_comp:4 + │ ├── i_new:19 => i:1 + │ ├── i2_cast:24 => i2:2 + │ ├── d_cast:25 => d:3 + │ ├── d2_cast:26 => d2:4 + │ ├── d_comp_cast:27 => d_comp:5 + │ └── b_new:20 => b:6 └── project - ├── columns: d_comp_cast:20!null i:8 d:9 d2:10 d_comp:11 rowid:12!null crdb_internal_mvcc_timestamp:13 tableoid:14 i_new:15!null d_cast:18!null d2_cast:19!null + ├── columns: d_comp_cast:27!null i:10 i2:11 d:12 d2:13 d_comp:14 b:15 rowid:16!null crdb_internal_mvcc_timestamp:17 tableoid:18 i_new:19!null b_new:20!null i2_cast:24!null d_cast:25!null d2_cast:26!null ├── project - │ ├── columns: d_cast:18!null d2_cast:19!null i:8 d:9 d2:10 d_comp:11 rowid:12!null crdb_internal_mvcc_timestamp:13 tableoid:14 i_new:15!null + │ ├── columns: i2_cast:24!null d_cast:25!null d2_cast:26!null i:10 i2:11 d:12 d2:13 d_comp:14 b:15 rowid:16!null crdb_internal_mvcc_timestamp:17 tableoid:18 i_new:19!null b_new:20!null │ ├── project - │ │ ├── columns: d_on_update:16!null d2_on_update:17!null i:8 d:9 d2:10 d_comp:11 rowid:12!null crdb_internal_mvcc_timestamp:13 tableoid:14 i_new:15!null + │ │ ├── columns: i2_on_update:21!null d_on_update:22!null d2_on_update:23!null i:10 i2:11 d:12 d2:13 d_comp:14 b:15 rowid:16!null crdb_internal_mvcc_timestamp:17 tableoid:18 i_new:19!null b_new:20!null │ │ ├── project - │ │ │ ├── columns: i_new:15!null i:8 d:9 d2:10 d_comp:11 rowid:12!null crdb_internal_mvcc_timestamp:13 tableoid:14 + │ │ │ ├── columns: i_new:19!null b_new:20!null i:10 i2:11 d:12 d2:13 d_comp:14 b:15 rowid:16!null crdb_internal_mvcc_timestamp:17 tableoid:18 │ │ │ ├── scan assn_cast_on_update - │ │ │ │ ├── columns: i:8 d:9 d2:10 d_comp:11 rowid:12!null crdb_internal_mvcc_timestamp:13 tableoid:14 + │ │ │ │ ├── columns: i:10 i2:11 d:12 d2:13 d_comp:14 b:15 rowid:16!null crdb_internal_mvcc_timestamp:17 tableoid:18 │ │ │ │ └── computed column expressions - │ │ │ │ └── d_comp:11 - │ │ │ │ └── d:9 + │ │ │ │ └── d_comp:14 + │ │ │ │ └── d:12 │ │ │ └── projections - │ │ │ └── 1 [as=i_new:15] + │ │ │ ├── 1 [as=i_new:19] + │ │ │ └── true [as=b_new:20] │ │ └── projections - │ │ ├── 1.23 [as=d_on_update:16] - │ │ └── 1.23::DECIMAL(10,2) [as=d2_on_update:17] + │ │ ├── 1.0 [as=i2_on_update:21] + │ │ ├── 1.23 [as=d_on_update:22] + │ │ └── 1.23::DECIMAL(10,2) [as=d2_on_update:23] │ └── projections - │ ├── assignment-cast: DECIMAL(10,1) [as=d_cast:18] - │ │ └── d_on_update:16 - │ └── assignment-cast: DECIMAL(10,1) [as=d2_cast:19] - │ └── d2_on_update:17 + │ ├── assignment-cast: INT8 [as=i2_cast:24] + │ │ └── i2_on_update:21 + │ ├── assignment-cast: DECIMAL(10,1) [as=d_cast:25] + │ │ └── d_on_update:22 + │ └── assignment-cast: DECIMAL(10,1) [as=d2_cast:26] + │ └── d2_on_update:23 └── projections - └── assignment-cast: DECIMAL(10) [as=d_comp_cast:20] - └── d_cast:18 + └── assignment-cast: DECIMAL(10) [as=d_comp_cast:27] + └── d_cast:25 + +# This fails because the cast from FLOAT to BOOL is not a valid assignment cast. +build +UPDATE assn_cast_on_update SET i=1 +---- +error (42804): value type float doesn't match type bool of column "b" # ------------------------------------------------------------------------------ # ON UPDATE tests.