From ddcab183bb2ff374a835ef49266cb93485ccd122 Mon Sep 17 00:00:00 2001 From: Andrew Kimball Date: Mon, 11 Mar 2019 17:29:41 -0400 Subject: [PATCH] opt: Fix panic on insert with check constraint and dropping column The panic occurs because the delete-only column is never referenced nor used by the Insert statement, and yet the disambiguateColumns method was trying to get its ColumnID. The fix is to skip over the column, since there's no need to disambiguate a column that's never referenced. Fixes #35611 Release note: None --- pkg/sql/logictest/testdata/logic_test/insert | 14 + pkg/sql/opt/optbuilder/mutation_builder.go | 28 +- pkg/sql/opt/optbuilder/testdata/insert | 66 ++-- pkg/sql/opt/optbuilder/testdata/update | 144 +++++--- pkg/sql/opt/optbuilder/testdata/upsert | 351 ++++++++++--------- 5 files changed, 351 insertions(+), 252 deletions(-) diff --git a/pkg/sql/logictest/testdata/logic_test/insert b/pkg/sql/logictest/testdata/logic_test/insert index 9279e75fcdc7..af262116c4c0 100644 --- a/pkg/sql/logictest/testdata/logic_test/insert +++ b/pkg/sql/logictest/testdata/logic_test/insert @@ -672,3 +672,17 @@ INSERT INTO xy (x, y) SELECT a, b FROM ab ORDER BY -b LIMIT 10 RETURNING *; statement ok DROP TABLE xy; DROP TABLE ab + +subtest regression_35611 + +statement ok +CREATE TABLE t35611(a INT PRIMARY KEY, CHECK (a > 0)) + +statement ok +BEGIN; ALTER TABLE t35611 ADD COLUMN b INT + +statement ok +INSERT INTO t35611 (a) VALUES (1) + +statement ok +COMMIT diff --git a/pkg/sql/opt/optbuilder/mutation_builder.go b/pkg/sql/opt/optbuilder/mutation_builder.go index ff20ee5332fa..7622b26aebde 100644 --- a/pkg/sql/opt/optbuilder/mutation_builder.go +++ b/pkg/sql/opt/optbuilder/mutation_builder.go @@ -406,7 +406,12 @@ func (mb *mutationBuilder) addCheckConstraintCols() { func (mb *mutationBuilder) disambiguateColumns() { for i, n := 0, mb.tab.DeletableColumnCount(); i < n; i++ { colName := mb.tab.Column(i).ColName() - colID := mb.mapToReturnColID(i) + colID := mb.mapToInputColID(i) + if colID == 0 { + // Column not involved in the statement, so skip it (e.g. a delete-only + // column in an Insert statement). + continue + } for i := range mb.outScope.cols { col := &mb.outScope.cols[i] if col.name == colName { @@ -441,16 +446,20 @@ func (mb *mutationBuilder) makeMutationPrivate(needResults bool) *memo.MutationP // can be non-zero. private.ReturnCols = make(opt.ColList, mb.tab.DeletableColumnCount()) for i, n := 0, mb.tab.ColumnCount(); i < n; i++ { - private.ReturnCols[i] = mb.mapToReturnColID(i) + private.ReturnCols[i] = mb.mapToInputColID(i) + if private.ReturnCols[i] == 0 { + panic(fmt.Sprintf("column %d is not available in the mutation input", i)) + } } } return private } -// mapToReturnColID returns the ID of the input column that will provide the -// value for the corresponding return column. Columns take priority in this -// order: +// mapToInputColID returns the ID of the input column that provides the final +// value for the column at the given ordinal position in the table. This value +// might mutate the column, or it might be returned by the mutation statement, +// or it might not be used at all. Columns take priority in this order: // // upsert, update, fetch, insert // @@ -459,7 +468,11 @@ func (mb *mutationBuilder) makeMutationPrivate(needResults bool) *memo.MutationP // available, then it overrides any fetch value. Finally, the relative priority // of fetch and insert columns doesn't matter, since they're only used together // in the upsert case where an upsert column would be available. -func (mb *mutationBuilder) mapToReturnColID(ord int) opt.ColumnID { +// +// If the column is never referenced by the statement, then mapToInputColID +// returns 0. This would be the case for delete-only columns in an Insert +// statement, because they're neither fetched nor mutated. +func (mb *mutationBuilder) mapToInputColID(ord int) opt.ColumnID { switch { case mb.upsertColList != nil && mb.upsertColList[ord] != 0: return mb.upsertColList[ord] @@ -474,7 +487,8 @@ func (mb *mutationBuilder) mapToReturnColID(ord int) opt.ColumnID { return mb.insertColList[ord] default: - panic("could not find return column") + // Column is never referenced by the statement. + return 0 } } diff --git a/pkg/sql/opt/optbuilder/testdata/insert b/pkg/sql/opt/optbuilder/testdata/insert index 3c059f409d5e..8068e5f9f721 100644 --- a/pkg/sql/opt/optbuilder/testdata/insert +++ b/pkg/sql/opt/optbuilder/testdata/insert @@ -50,7 +50,8 @@ CREATE TABLE mutation ( n INT, "o:write-only" INT DEFAULT(10), "p:write-only" INT AS (o + n) STORED, - "q:delete-only" INT + "q:delete-only" INT AS (m * p) STORED, + CHECK (m > 0) ) ---- TABLE mutation @@ -59,8 +60,9 @@ TABLE mutation ├── o int (mutation) ├── p int (mutation) ├── q int (mutation) - └── INDEX primary - └── m int not null + ├── INDEX primary + │ └── m int not null + └── CHECK (m > 0) exec-ddl CREATE TABLE checks ( @@ -1186,21 +1188,28 @@ insert mutation │ ├── column2:7 => n:2 │ ├── column8:8 => o:3 │ └── column9:9 => p:4 + ├── check columns: check1:10(bool) └── project - ├── columns: column9:9(int) column1:6(int) column2:7(int) column8:8(int!null) + ├── columns: check1:10(bool) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) ├── project - │ ├── columns: column8:8(int!null) column1:6(int) column2:7(int) - │ ├── values - │ │ ├── columns: column1:6(int) column2:7(int) - │ │ └── tuple [type=tuple{int, int}] - │ │ ├── const: 1 [type=int] - │ │ └── const: 2 [type=int] + │ ├── columns: column9:9(int) column1:6(int) column2:7(int) column8:8(int!null) + │ ├── project + │ │ ├── columns: column8:8(int!null) column1:6(int) column2:7(int) + │ │ ├── values + │ │ │ ├── columns: column1:6(int) column2:7(int) + │ │ │ └── tuple [type=tuple{int, int}] + │ │ │ ├── const: 1 [type=int] + │ │ │ └── const: 2 [type=int] + │ │ └── projections + │ │ └── const: 10 [type=int] │ └── projections - │ └── const: 10 [type=int] + │ └── plus [type=int] + │ ├── variable: column8 [type=int] + │ └── variable: column2 [type=int] └── projections - └── plus [type=int] - ├── variable: column8 [type=int] - └── variable: column2 [type=int] + └── gt [type=bool] + ├── variable: column1 [type=int] + └── const: 0 [type=int] # Use RETURNING clause and ensure that mutation columns aren't projected. build @@ -1213,21 +1222,28 @@ insert mutation │ ├── column2:7 => n:2 │ ├── column8:8 => o:3 │ └── column9:9 => p:4 + ├── check columns: check1:10(bool) └── project - ├── columns: column9:9(int) column1:6(int) column2:7(int) column8:8(int!null) + ├── columns: check1:10(bool) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) ├── project - │ ├── columns: column8:8(int!null) column1:6(int) column2:7(int) - │ ├── values - │ │ ├── columns: column1:6(int) column2:7(int) - │ │ └── tuple [type=tuple{int, int}] - │ │ ├── const: 1 [type=int] - │ │ └── const: 2 [type=int] + │ ├── columns: column9:9(int) column1:6(int) column2:7(int) column8:8(int!null) + │ ├── project + │ │ ├── columns: column8:8(int!null) column1:6(int) column2:7(int) + │ │ ├── values + │ │ │ ├── columns: column1:6(int) column2:7(int) + │ │ │ └── tuple [type=tuple{int, int}] + │ │ │ ├── const: 1 [type=int] + │ │ │ └── const: 2 [type=int] + │ │ └── projections + │ │ └── const: 10 [type=int] │ └── projections - │ └── const: 10 [type=int] + │ └── plus [type=int] + │ ├── variable: column8 [type=int] + │ └── variable: column2 [type=int] └── projections - └── plus [type=int] - ├── variable: column8 [type=int] - └── variable: column2 [type=int] + └── gt [type=bool] + ├── variable: column1 [type=int] + └── const: 0 [type=int] # Try to reference write-only mutation column in RETURNING clause. build diff --git a/pkg/sql/opt/optbuilder/testdata/update b/pkg/sql/opt/optbuilder/testdata/update index 530b49291801..061f4849dc5d 100644 --- a/pkg/sql/opt/optbuilder/testdata/update +++ b/pkg/sql/opt/optbuilder/testdata/update @@ -49,7 +49,9 @@ CREATE TABLE mutation ( m INT PRIMARY KEY, n INT, "o:write-only" INT DEFAULT(10), - "p:write-only" INT AS (o + n) STORED + "p:write-only" INT AS (o + n) STORED, + "q:delete-only" INT AS (m * p) STORED, + CHECK (m > 0) ) ---- TABLE mutation @@ -57,8 +59,10 @@ TABLE mutation ├── n int ├── o int (mutation) ├── p int (mutation) - └── INDEX primary - └── m int not null + ├── q int (mutation) + ├── INDEX primary + │ └── m int not null + └── CHECK (m > 0) exec-ddl CREATE TABLE checks ( @@ -1222,22 +1226,29 @@ UPDATE mutation SET m=1 ---- update mutation ├── columns: - ├── fetch columns: m:5(int) n:6(int) o:7(int) p:8(int) + ├── fetch columns: m:6(int) n:7(int) o:8(int) p:9(int) q:10(int) ├── update-mapping: - │ ├── column9:9 => m:1 - │ └── column10:10 => p:4 + │ ├── column11:11 => m:1 + │ └── column12:12 => p:4 + ├── check columns: check1:13(bool) └── project - ├── columns: column10:10(int) m:5(int!null) n:6(int) o:7(int) p:8(int) column9:9(int!null) + ├── columns: check1:13(bool) m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) column11:11(int!null) column12:12(int) ├── project - │ ├── columns: column9:9(int!null) m:5(int!null) n:6(int) o:7(int) p:8(int) - │ ├── scan mutation - │ │ └── columns: m:5(int!null) n:6(int) o:7(int) p:8(int) + │ ├── columns: column12:12(int) m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) column11:11(int!null) + │ ├── project + │ │ ├── columns: column11:11(int!null) m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) + │ │ ├── scan mutation + │ │ │ └── columns: m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) + │ │ └── projections + │ │ └── const: 1 [type=int] │ └── projections - │ └── const: 1 [type=int] + │ └── plus [type=int] + │ ├── variable: o [type=int] + │ └── variable: n [type=int] └── projections - └── plus [type=int] - ├── variable: o [type=int] - └── variable: n [type=int] + └── gt [type=bool] + ├── variable: column11 [type=int] + └── const: 0 [type=int] # Test update that requires computed mutation column to be recalculated. build @@ -1245,24 +1256,31 @@ UPDATE mutation SET m=1, n=2 ---- update mutation ├── columns: - ├── fetch columns: m:5(int) n:6(int) o:7(int) p:8(int) + ├── fetch columns: m:6(int) n:7(int) o:8(int) p:9(int) q:10(int) ├── update-mapping: - │ ├── column9:9 => m:1 - │ ├── column10:10 => n:2 - │ └── column11:11 => p:4 + │ ├── column11:11 => m:1 + │ ├── column12:12 => n:2 + │ └── column13:13 => p:4 + ├── check columns: check1:14(bool) └── project - ├── columns: column11:11(int) m:5(int!null) n:6(int) o:7(int) p:8(int) column9:9(int!null) column10:10(int!null) + ├── columns: check1:14(bool) m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) column11:11(int!null) column12:12(int!null) column13:13(int) ├── project - │ ├── columns: column9:9(int!null) column10:10(int!null) m:5(int!null) n:6(int) o:7(int) p:8(int) - │ ├── scan mutation - │ │ └── columns: m:5(int!null) n:6(int) o:7(int) p:8(int) + │ ├── columns: column13:13(int) m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) column11:11(int!null) column12:12(int!null) + │ ├── project + │ │ ├── columns: column11:11(int!null) column12:12(int!null) m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) + │ │ ├── scan mutation + │ │ │ └── columns: m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) + │ │ └── projections + │ │ ├── const: 1 [type=int] + │ │ └── const: 2 [type=int] │ └── projections - │ ├── const: 1 [type=int] - │ └── const: 2 [type=int] + │ └── plus [type=int] + │ ├── variable: o [type=int] + │ └── variable: column12 [type=int] └── projections - └── plus [type=int] - ├── variable: o [type=int] - └── variable: column10 [type=int] + └── gt [type=bool] + ├── variable: column11 [type=int] + └── const: 0 [type=int] # Ensure that ORDER BY wildcard does not select mutation columns. build @@ -1270,29 +1288,36 @@ UPDATE mutation SET m=1 ORDER BY mutation.* LIMIT 10 ---- update mutation ├── columns: - ├── fetch columns: m:5(int) n:6(int) o:7(int) p:8(int) + ├── fetch columns: m:6(int) n:7(int) o:8(int) p:9(int) q:10(int) ├── update-mapping: - │ ├── column9:9 => m:1 - │ └── column10:10 => p:4 + │ ├── column11:11 => m:1 + │ └── column12:12 => p:4 + ├── check columns: check1:13(bool) └── project - ├── columns: column10:10(int) m:5(int!null) n:6(int) o:7(int) p:8(int) column9:9(int!null) + ├── columns: check1:13(bool) m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) column11:11(int!null) column12:12(int) ├── project - │ ├── columns: column9:9(int!null) m:5(int!null) n:6(int) o:7(int) p:8(int) - │ ├── limit - │ │ ├── columns: m:5(int!null) n:6(int) o:7(int) p:8(int) - │ │ ├── internal-ordering: +5,+6 - │ │ ├── sort - │ │ │ ├── columns: m:5(int!null) n:6(int) o:7(int) p:8(int) - │ │ │ ├── ordering: +5,+6 - │ │ │ └── scan mutation - │ │ │ └── columns: m:5(int!null) n:6(int) o:7(int) p:8(int) - │ │ └── const: 10 [type=int] + │ ├── columns: column12:12(int) m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) column11:11(int!null) + │ ├── project + │ │ ├── columns: column11:11(int!null) m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) + │ │ ├── limit + │ │ │ ├── columns: m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) + │ │ │ ├── internal-ordering: +6,+7 + │ │ │ ├── sort + │ │ │ │ ├── columns: m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) + │ │ │ │ ├── ordering: +6,+7 + │ │ │ │ └── scan mutation + │ │ │ │ └── columns: m:6(int!null) n:7(int) o:8(int) p:9(int) q:10(int) + │ │ │ └── const: 10 [type=int] + │ │ └── projections + │ │ └── const: 1 [type=int] │ └── projections - │ └── const: 1 [type=int] + │ └── plus [type=int] + │ ├── variable: o [type=int] + │ └── variable: n [type=int] └── projections - └── plus [type=int] - ├── variable: o [type=int] - └── variable: n [type=int] + └── gt [type=bool] + ├── variable: column11 [type=int] + └── const: 0 [type=int] # Use column "o" from higher scope that is shadowed by mutation column "o". build @@ -1308,22 +1333,29 @@ project └── exists [type=bool] └── update mutation ├── columns: mutation.m:7(int!null) mutation.n:8(int) - ├── fetch columns: mutation.m:11(int) mutation.n:12(int) mutation.o:13(int) p:14(int) + ├── fetch columns: mutation.m:12(int) mutation.n:13(int) mutation.o:14(int) p:15(int) q:16(int) ├── update-mapping: - │ ├── o:15 => mutation.m:7 - │ └── column16:16 => p:10 + │ ├── o:17 => mutation.m:7 + │ └── column18:18 => p:10 + ├── check columns: check1:19(bool) └── project - ├── columns: column16:16(int) mutation.m:11(int!null) mutation.n:12(int) mutation.o:13(int) p:14(int) o:15(int) + ├── columns: check1:19(bool) mutation.m:12(int!null) mutation.n:13(int) mutation.o:14(int) p:15(int) q:16(int) o:17(int) column18:18(int) ├── project - │ ├── columns: o:15(int) mutation.m:11(int!null) mutation.n:12(int) mutation.o:13(int) p:14(int) - │ ├── scan mutation - │ │ └── columns: mutation.m:11(int!null) mutation.n:12(int) mutation.o:13(int) p:14(int) + │ ├── columns: column18:18(int) mutation.m:12(int!null) mutation.n:13(int) mutation.o:14(int) p:15(int) q:16(int) o:17(int) + │ ├── project + │ │ ├── columns: o:17(int) mutation.m:12(int!null) mutation.n:13(int) mutation.o:14(int) p:15(int) q:16(int) + │ │ ├── scan mutation + │ │ │ └── columns: mutation.m:12(int!null) mutation.n:13(int) mutation.o:14(int) p:15(int) q:16(int) + │ │ └── projections + │ │ └── variable: mno.o [type=int] │ └── projections - │ └── variable: mno.o [type=int] + │ └── plus [type=int] + │ ├── variable: mutation.o [type=int] + │ └── variable: mutation.n [type=int] └── projections - └── plus [type=int] - ├── variable: mutation.o [type=int] - └── variable: mutation.n [type=int] + └── gt [type=bool] + ├── variable: o [type=int] + └── const: 0 [type=int] # Try to return a mutation column. build diff --git a/pkg/sql/opt/optbuilder/testdata/upsert b/pkg/sql/opt/optbuilder/testdata/upsert index 169906064198..1396fe7e4925 100644 --- a/pkg/sql/opt/optbuilder/testdata/upsert +++ b/pkg/sql/opt/optbuilder/testdata/upsert @@ -84,7 +84,8 @@ CREATE TABLE mutation ( n INT, "o:write-only" INT DEFAULT(10), "p:write-only" INT AS (o + n) STORED, - "q:delete-only" INT + "q:delete-only" INT AS (m * p) STORED, + CHECK (m > 0) ) ---- TABLE mutation @@ -93,8 +94,9 @@ TABLE mutation ├── o int (mutation) ├── p int (mutation) ├── q int (mutation) - └── INDEX primary - └── m int not null + ├── INDEX primary + │ └── m int not null + └── CHECK (m > 0) exec-ddl CREATE TABLE checks ( @@ -1088,76 +1090,83 @@ upsert mutation ├── update-mapping: │ ├── upsert_m:17 => m:1 │ └── upsert_p:20 => p:4 + ├── check columns: check1:21(bool) └── project - ├── columns: upsert_m:17(int) upsert_n:18(int) upsert_o:19(int) upsert_p:20(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) + ├── columns: check1:21(bool) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) upsert_m:17(int) upsert_n:18(int) upsert_o:19(int) upsert_p:20(int) ├── project - │ ├── columns: column16:16(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) column15:15(int) + │ ├── columns: upsert_m:17(int) upsert_n:18(int) upsert_o:19(int) upsert_p:20(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) │ ├── project - │ │ ├── columns: column15:15(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) - │ │ ├── left-join - │ │ │ ├── columns: column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) - │ │ │ ├── project - │ │ │ │ ├── columns: column9:9(int) column1:6(int) column2:7(int) column8:8(int!null) + │ │ ├── columns: column16:16(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) column15:15(int) + │ │ ├── project + │ │ │ ├── columns: column15:15(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) + │ │ │ ├── left-join + │ │ │ │ ├── columns: column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) │ │ │ │ ├── project - │ │ │ │ │ ├── columns: column8:8(int!null) column1:6(int) column2:7(int) - │ │ │ │ │ ├── values - │ │ │ │ │ │ ├── columns: column1:6(int) column2:7(int) - │ │ │ │ │ │ └── tuple [type=tuple{int, int}] - │ │ │ │ │ │ ├── const: 1 [type=int] - │ │ │ │ │ │ └── const: 2 [type=int] + │ │ │ │ │ ├── columns: column9:9(int) column1:6(int) column2:7(int) column8:8(int!null) + │ │ │ │ │ ├── project + │ │ │ │ │ │ ├── columns: column8:8(int!null) column1:6(int) column2:7(int) + │ │ │ │ │ │ ├── values + │ │ │ │ │ │ │ ├── columns: column1:6(int) column2:7(int) + │ │ │ │ │ │ │ └── tuple [type=tuple{int, int}] + │ │ │ │ │ │ │ ├── const: 1 [type=int] + │ │ │ │ │ │ │ └── const: 2 [type=int] + │ │ │ │ │ │ └── projections + │ │ │ │ │ │ └── const: 10 [type=int] │ │ │ │ │ └── projections - │ │ │ │ │ └── const: 10 [type=int] - │ │ │ │ └── projections - │ │ │ │ └── plus [type=int] - │ │ │ │ ├── variable: column8 [type=int] - │ │ │ │ └── variable: column2 [type=int] - │ │ │ ├── scan mutation - │ │ │ │ └── columns: m:10(int!null) n:11(int) o:12(int) p:13(int) q:14(int) - │ │ │ └── filters - │ │ │ └── eq [type=bool] - │ │ │ ├── variable: column1 [type=int] - │ │ │ └── variable: m [type=int] + │ │ │ │ │ └── plus [type=int] + │ │ │ │ │ ├── variable: column8 [type=int] + │ │ │ │ │ └── variable: column2 [type=int] + │ │ │ │ ├── scan mutation + │ │ │ │ │ └── columns: m:10(int!null) n:11(int) o:12(int) p:13(int) q:14(int) + │ │ │ │ └── filters + │ │ │ │ └── eq [type=bool] + │ │ │ │ ├── variable: column1 [type=int] + │ │ │ │ └── variable: m [type=int] + │ │ │ └── projections + │ │ │ └── plus [type=int] + │ │ │ ├── variable: m [type=int] + │ │ │ └── const: 1 [type=int] │ │ └── projections │ │ └── plus [type=int] - │ │ ├── variable: m [type=int] - │ │ └── const: 1 [type=int] + │ │ ├── variable: o [type=int] + │ │ └── variable: n [type=int] │ └── projections - │ └── plus [type=int] - │ ├── variable: o [type=int] - │ └── variable: n [type=int] + │ ├── case [type=int] + │ │ ├── true [type=bool] + │ │ ├── when [type=int] + │ │ │ ├── is [type=bool] + │ │ │ │ ├── variable: m [type=int] + │ │ │ │ └── null [type=unknown] + │ │ │ └── variable: column1 [type=int] + │ │ └── variable: column15 [type=int] + │ ├── case [type=int] + │ │ ├── true [type=bool] + │ │ ├── when [type=int] + │ │ │ ├── is [type=bool] + │ │ │ │ ├── variable: m [type=int] + │ │ │ │ └── null [type=unknown] + │ │ │ └── variable: column2 [type=int] + │ │ └── variable: n [type=int] + │ ├── case [type=int] + │ │ ├── true [type=bool] + │ │ ├── when [type=int] + │ │ │ ├── is [type=bool] + │ │ │ │ ├── variable: m [type=int] + │ │ │ │ └── null [type=unknown] + │ │ │ └── variable: column8 [type=int] + │ │ └── variable: o [type=int] + │ └── case [type=int] + │ ├── true [type=bool] + │ ├── when [type=int] + │ │ ├── is [type=bool] + │ │ │ ├── variable: m [type=int] + │ │ │ └── null [type=unknown] + │ │ └── variable: column9 [type=int] + │ └── variable: column16 [type=int] └── projections - ├── case [type=int] - │ ├── true [type=bool] - │ ├── when [type=int] - │ │ ├── is [type=bool] - │ │ │ ├── variable: m [type=int] - │ │ │ └── null [type=unknown] - │ │ └── variable: column1 [type=int] - │ └── variable: column15 [type=int] - ├── case [type=int] - │ ├── true [type=bool] - │ ├── when [type=int] - │ │ ├── is [type=bool] - │ │ │ ├── variable: m [type=int] - │ │ │ └── null [type=unknown] - │ │ └── variable: column2 [type=int] - │ └── variable: n [type=int] - ├── case [type=int] - │ ├── true [type=bool] - │ ├── when [type=int] - │ │ ├── is [type=bool] - │ │ │ ├── variable: m [type=int] - │ │ │ └── null [type=unknown] - │ │ └── variable: column8 [type=int] - │ └── variable: o [type=int] - └── case [type=int] - ├── true [type=bool] - ├── when [type=int] - │ ├── is [type=bool] - │ │ ├── variable: m [type=int] - │ │ └── null [type=unknown] - │ └── variable: column9 [type=int] - └── variable: column16 [type=int] + └── gt [type=bool] + ├── variable: upsert_m [type=int] + └── const: 0 [type=int] # ------------------------------------------------------------------------------ # Test UPSERT. @@ -1419,62 +1428,69 @@ upsert mutation ├── update-mapping: │ ├── column2:7 => n:2 │ └── upsert_p:18 => p:4 + ├── check columns: check1:19(bool) └── project - ├── columns: upsert_m:16(int) upsert_o:17(int) upsert_p:18(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) + ├── columns: check1:19(bool) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) upsert_m:16(int) upsert_o:17(int) upsert_p:18(int) ├── project - │ ├── columns: column15:15(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) - │ ├── left-join - │ │ ├── columns: column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) - │ │ ├── project - │ │ │ ├── columns: column9:9(int) column1:6(int) column2:7(int) column8:8(int!null) + │ ├── columns: upsert_m:16(int) upsert_o:17(int) upsert_p:18(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) + │ ├── project + │ │ ├── columns: column15:15(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) + │ │ ├── left-join + │ │ │ ├── columns: column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) │ │ │ ├── project - │ │ │ │ ├── columns: column8:8(int!null) column1:6(int) column2:7(int) - │ │ │ │ ├── values - │ │ │ │ │ ├── columns: column1:6(int) column2:7(int) - │ │ │ │ │ └── tuple [type=tuple{int, int}] - │ │ │ │ │ ├── const: 1 [type=int] - │ │ │ │ │ └── const: 2 [type=int] + │ │ │ │ ├── columns: column9:9(int) column1:6(int) column2:7(int) column8:8(int!null) + │ │ │ │ ├── project + │ │ │ │ │ ├── columns: column8:8(int!null) column1:6(int) column2:7(int) + │ │ │ │ │ ├── values + │ │ │ │ │ │ ├── columns: column1:6(int) column2:7(int) + │ │ │ │ │ │ └── tuple [type=tuple{int, int}] + │ │ │ │ │ │ ├── const: 1 [type=int] + │ │ │ │ │ │ └── const: 2 [type=int] + │ │ │ │ │ └── projections + │ │ │ │ │ └── const: 10 [type=int] │ │ │ │ └── projections - │ │ │ │ └── const: 10 [type=int] - │ │ │ └── projections - │ │ │ └── plus [type=int] - │ │ │ ├── variable: column8 [type=int] - │ │ │ └── variable: column2 [type=int] - │ │ ├── scan mutation - │ │ │ └── columns: m:10(int!null) n:11(int) o:12(int) p:13(int) q:14(int) - │ │ └── filters - │ │ └── eq [type=bool] - │ │ ├── variable: column1 [type=int] - │ │ └── variable: m [type=int] + │ │ │ │ └── plus [type=int] + │ │ │ │ ├── variable: column8 [type=int] + │ │ │ │ └── variable: column2 [type=int] + │ │ │ ├── scan mutation + │ │ │ │ └── columns: m:10(int!null) n:11(int) o:12(int) p:13(int) q:14(int) + │ │ │ └── filters + │ │ │ └── eq [type=bool] + │ │ │ ├── variable: column1 [type=int] + │ │ │ └── variable: m [type=int] + │ │ └── projections + │ │ └── plus [type=int] + │ │ ├── variable: o [type=int] + │ │ └── variable: column2 [type=int] │ └── projections - │ └── plus [type=int] - │ ├── variable: o [type=int] - │ └── variable: column2 [type=int] + │ ├── case [type=int] + │ │ ├── true [type=bool] + │ │ ├── when [type=int] + │ │ │ ├── is [type=bool] + │ │ │ │ ├── variable: m [type=int] + │ │ │ │ └── null [type=unknown] + │ │ │ └── variable: column1 [type=int] + │ │ └── variable: m [type=int] + │ ├── case [type=int] + │ │ ├── true [type=bool] + │ │ ├── when [type=int] + │ │ │ ├── is [type=bool] + │ │ │ │ ├── variable: m [type=int] + │ │ │ │ └── null [type=unknown] + │ │ │ └── variable: column8 [type=int] + │ │ └── variable: o [type=int] + │ └── case [type=int] + │ ├── true [type=bool] + │ ├── when [type=int] + │ │ ├── is [type=bool] + │ │ │ ├── variable: m [type=int] + │ │ │ └── null [type=unknown] + │ │ └── variable: column9 [type=int] + │ └── variable: column15 [type=int] └── projections - ├── case [type=int] - │ ├── true [type=bool] - │ ├── when [type=int] - │ │ ├── is [type=bool] - │ │ │ ├── variable: m [type=int] - │ │ │ └── null [type=unknown] - │ │ └── variable: column1 [type=int] - │ └── variable: m [type=int] - ├── case [type=int] - │ ├── true [type=bool] - │ ├── when [type=int] - │ │ ├── is [type=bool] - │ │ │ ├── variable: m [type=int] - │ │ │ └── null [type=unknown] - │ │ └── variable: column8 [type=int] - │ └── variable: o [type=int] - └── case [type=int] - ├── true [type=bool] - ├── when [type=int] - │ ├── is [type=bool] - │ │ ├── variable: m [type=int] - │ │ └── null [type=unknown] - │ └── variable: column9 [type=int] - └── variable: column15 [type=int] + └── gt [type=bool] + ├── variable: upsert_m [type=int] + └── const: 0 [type=int] # Don't directly update mutation columns. However, computed columns do need to # be updated. Use implicit target columns. @@ -1493,62 +1509,69 @@ upsert mutation ├── update-mapping: │ ├── column2:7 => n:2 │ └── upsert_p:18 => p:4 + ├── check columns: check1:19(bool) └── project - ├── columns: upsert_m:16(int) upsert_o:17(int) upsert_p:18(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) + ├── columns: check1:19(bool) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) upsert_m:16(int) upsert_o:17(int) upsert_p:18(int) ├── project - │ ├── columns: column15:15(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) - │ ├── left-join - │ │ ├── columns: column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) - │ │ ├── project - │ │ │ ├── columns: column9:9(int) column1:6(int) column2:7(int) column8:8(int!null) + │ ├── columns: upsert_m:16(int) upsert_o:17(int) upsert_p:18(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) + │ ├── project + │ │ ├── columns: column15:15(int) column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) + │ │ ├── left-join + │ │ │ ├── columns: column1:6(int) column2:7(int) column8:8(int!null) column9:9(int) m:10(int) n:11(int) o:12(int) p:13(int) q:14(int) │ │ │ ├── project - │ │ │ │ ├── columns: column8:8(int!null) column1:6(int) column2:7(int) - │ │ │ │ ├── values - │ │ │ │ │ ├── columns: column1:6(int) column2:7(int) - │ │ │ │ │ └── tuple [type=tuple{int, int}] - │ │ │ │ │ ├── const: 1 [type=int] - │ │ │ │ │ └── const: 2 [type=int] + │ │ │ │ ├── columns: column9:9(int) column1:6(int) column2:7(int) column8:8(int!null) + │ │ │ │ ├── project + │ │ │ │ │ ├── columns: column8:8(int!null) column1:6(int) column2:7(int) + │ │ │ │ │ ├── values + │ │ │ │ │ │ ├── columns: column1:6(int) column2:7(int) + │ │ │ │ │ │ └── tuple [type=tuple{int, int}] + │ │ │ │ │ │ ├── const: 1 [type=int] + │ │ │ │ │ │ └── const: 2 [type=int] + │ │ │ │ │ └── projections + │ │ │ │ │ └── const: 10 [type=int] │ │ │ │ └── projections - │ │ │ │ └── const: 10 [type=int] - │ │ │ └── projections - │ │ │ └── plus [type=int] - │ │ │ ├── variable: column8 [type=int] - │ │ │ └── variable: column2 [type=int] - │ │ ├── scan mutation - │ │ │ └── columns: m:10(int!null) n:11(int) o:12(int) p:13(int) q:14(int) - │ │ └── filters - │ │ └── eq [type=bool] - │ │ ├── variable: column1 [type=int] - │ │ └── variable: m [type=int] + │ │ │ │ └── plus [type=int] + │ │ │ │ ├── variable: column8 [type=int] + │ │ │ │ └── variable: column2 [type=int] + │ │ │ ├── scan mutation + │ │ │ │ └── columns: m:10(int!null) n:11(int) o:12(int) p:13(int) q:14(int) + │ │ │ └── filters + │ │ │ └── eq [type=bool] + │ │ │ ├── variable: column1 [type=int] + │ │ │ └── variable: m [type=int] + │ │ └── projections + │ │ └── plus [type=int] + │ │ ├── variable: o [type=int] + │ │ └── variable: column2 [type=int] │ └── projections - │ └── plus [type=int] - │ ├── variable: o [type=int] - │ └── variable: column2 [type=int] + │ ├── case [type=int] + │ │ ├── true [type=bool] + │ │ ├── when [type=int] + │ │ │ ├── is [type=bool] + │ │ │ │ ├── variable: m [type=int] + │ │ │ │ └── null [type=unknown] + │ │ │ └── variable: column1 [type=int] + │ │ └── variable: m [type=int] + │ ├── case [type=int] + │ │ ├── true [type=bool] + │ │ ├── when [type=int] + │ │ │ ├── is [type=bool] + │ │ │ │ ├── variable: m [type=int] + │ │ │ │ └── null [type=unknown] + │ │ │ └── variable: column8 [type=int] + │ │ └── variable: o [type=int] + │ └── case [type=int] + │ ├── true [type=bool] + │ ├── when [type=int] + │ │ ├── is [type=bool] + │ │ │ ├── variable: m [type=int] + │ │ │ └── null [type=unknown] + │ │ └── variable: column9 [type=int] + │ └── variable: column15 [type=int] └── projections - ├── case [type=int] - │ ├── true [type=bool] - │ ├── when [type=int] - │ │ ├── is [type=bool] - │ │ │ ├── variable: m [type=int] - │ │ │ └── null [type=unknown] - │ │ └── variable: column1 [type=int] - │ └── variable: m [type=int] - ├── case [type=int] - │ ├── true [type=bool] - │ ├── when [type=int] - │ │ ├── is [type=bool] - │ │ │ ├── variable: m [type=int] - │ │ │ └── null [type=unknown] - │ │ └── variable: column8 [type=int] - │ └── variable: o [type=int] - └── case [type=int] - ├── true [type=bool] - ├── when [type=int] - │ ├── is [type=bool] - │ │ ├── variable: m [type=int] - │ │ └── null [type=unknown] - │ └── variable: column9 [type=int] - └── variable: column15 [type=int] + └── gt [type=bool] + ├── variable: upsert_m [type=int] + └── const: 0 [type=int] # Use unknown name in upsert column list. build