Skip to content

Commit

Permalink
sql: add assignment cast for UPDATEs
Browse files Browse the repository at this point in the history
Fixes cockroachdb#70628

Release note: None
  • Loading branch information
mgartner authored and GustasValdavicius committed Jan 4, 2022
1 parent 297218c commit cb03bcc
Show file tree
Hide file tree
Showing 22 changed files with 1,801 additions and 680 deletions.
258 changes: 256 additions & 2 deletions pkg/sql/logictest/testdata/logic_test/cast
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ EXECUTE insert_c('foo'::STRING)
statement ok
DELETE FROM assn_cast

statement
statement ok
EXECUTE insert_c(' ')

statement
statement ok
EXECUTE insert_c(' '::STRING)

query T
Expand Down Expand Up @@ -93,6 +93,8 @@ INSERT INTO assn_cast(qc) VALUES (123) RETURNING qc
----
{

# An integer to "char" cast converts the integer into the corresponding 7-bit
# ASCII character. Anything greater than 127 is out of range.
statement error \"char\" out of range
INSERT INTO assn_cast(qc) VALUES (1234)

Expand Down Expand Up @@ -348,6 +350,258 @@ SELECT d, d_comp, d2, d2_comp FROM assn_cast_comp
----
2 2.00 2.78 3


# Tests for assignment casts in UPDATEs.
subtest assignment_casts_update

statement ok
DELETE FROM assn_cast

statement ok
INSERT INTO assn_cast (c) VALUES (NULL)

statement error value too long for type CHAR
UPDATE assn_cast SET c = 'abc'

query T
UPDATE assn_cast SET c = 1 RETURNING c
----
1

statement error value too long for type CHAR
UPDATE assn_cast SET c = 123

statement ok
UPDATE assn_cast SET c = NULL

statement ok
PREPARE update_c AS UPDATE assn_cast SET c = $1

statement error value too long for type CHAR
EXECUTE update_c('foo')

statement error value too long for type CHAR
EXECUTE update_c('foo'::STRING)

statement ok
EXECUTE update_c(' ')

query T
SELECT concat('"', c, '"') FROM assn_cast
----
""

statement ok
EXECUTE update_c(' '::STRING)

query T
SELECT concat('"', c, '"') FROM assn_cast
----
""

statement ok
UPDATE assn_cast SET qc = 'a'

query T
UPDATE assn_cast SET qc = 'abc' RETURNING qc
----
a

# An integer to "char" cast converts the integer into the corresponding 7-bit
# ASCII character. Anything greater than 127 is out of range.
statement error \"char\" out of range
UPDATE assn_cast SET qc = 1234

statement ok
PREPARE update_qc AS UPDATE assn_cast SET qc = $1

statement ok
EXECUTE update_qc('foo')

query T
SELECT qc FROM assn_cast
----
f

statement ok
EXECUTE update_qc('foo'::STRING)

query T
SELECT qc FROM assn_cast
----
f

statement ok
UPDATE assn_cast SET i = '1'

statement ok
PREPARE update_i AS UPDATE assn_cast SET i = $1

statement ok
EXECUTE update_i('1')

statement error value type string doesn't match type int of column \"i\"
UPDATE assn_cast SET i = '1'::STRING

statement error integer out of range for type int2
UPDATE assn_cast SET i2 = 999999999

statement ok
PREPARE update_i2 AS UPDATE assn_cast SET i2 = $1

statement error integer out of range for type int2
EXECUTE update_i2(99999999)

query F
UPDATE assn_cast SET d = 11.22 RETURNING d
----
11

query F
UPDATE assn_cast SET d = 11.22::DECIMAL(10, 0) RETURNING d
----
11

query F
UPDATE assn_cast SET d = 11.22::DECIMAL(10, 2) RETURNING d
----
11

statement ok
PREPARE update_d AS UPDATE assn_cast SET d = $1

statement ok
EXECUTE update_d(123.45)

query F
SELECT d FROM assn_cast
----
123

statement ok
PREPARE update_d2 AS UPDATE assn_cast SET d = (SELECT * FROM (VALUES ($1::DECIMAL(10, 2))))

statement ok
EXECUTE update_d2(67.89)

query F
SELECT d FROM assn_cast
----
68

query T
UPDATE assn_cast SET a = ARRAY[] RETURNING a
----
{}

query T
UPDATE assn_cast SET a = ARRAY[NULL] RETURNING a
----
{NULL}

query T
UPDATE assn_cast SET a = ARRAY[1.1] RETURNING a
----
{1}

query T
UPDATE assn_cast SET a = ARRAY[2.88, NULL, 15] RETURNING a
----
{3,NULL,15}

query T
UPDATE assn_cast SET a = ARRAY[3.99, NULL, 16]::DECIMAL(10, 2)[] RETURNING a
----
{4,NULL,16}

query T
UPDATE assn_cast SET a = ARRAY[5.55, 6.66::DECIMAL(10, 2)] RETURNING a
----
{6,7}

statement ok
PREPARE update_a AS UPDATE assn_cast SET a = $1

statement ok
EXECUTE update_a(ARRAY[7.77, 8.88::DECIMAL(10, 2)])

query T
SELECT a FROM assn_cast
----
{8,9}

statement ok
PREPARE update_a2 AS UPDATE assn_cast SET a = ARRAY[$1]

statement ok
EXECUTE update_a2(20.2)

query T
SELECT a FROM assn_cast
----
{20}

statement ok
PREPARE update_a3 AS UPDATE assn_cast SET a = ARRAY[30.12, $1, 32.1]

statement ok
EXECUTE update_a3(30.9)

query T
SELECT a FROM assn_cast
----
{30,31,32}

statement error value type tuple{int, int} doesn't match type timestamp of column "t"
UPDATE assn_cast SET t = (SELECT (10, 11))

statement error value type decimal doesn't match type timestamp of column "t"
UPDATE assn_cast SET t = 3.2

statement error value type decimal doesn't match type timestamp of column "t"
UPDATE assn_cast SET (i, t) = (1, 3.2)

# Tests for assignment casts in cascading UPDATEs.
subtest assignment_casts_update_cascade

statement ok
CREATE TABLE assn_cast_p (p DECIMAL(10, 2) PRIMARY KEY);
INSERT INTO assn_cast_p VALUES (1.0);

# Test ON UPDATE CASCADE.
statement ok
CREATE TABLE assn_cast_c (c INT PRIMARY KEY, p DECIMAL(10, 0) REFERENCES assn_cast_p(p) ON UPDATE CASCADE);
INSERT INTO assn_cast_c VALUES (1, 1.0);

statement error update on table "assn_cast_c" violates foreign key constraint "assn_cast_c_p_fkey"
UPDATE assn_cast_p SET p = 1.2

statement ok
UPDATE assn_cast_p SET p = 2.0

query IF
SELECT * FROM assn_cast_c
----
1 2

# Test ON UPDATE SET DEFAULT.
statement ok
DROP TABLE assn_cast_c;
CREATE TABLE assn_cast_c (c INT PRIMARY KEY, p DECIMAL(10, 0) DEFAULT 3.1 REFERENCES assn_cast_p(p) ON UPDATE SET DEFAULT);
INSERT INTO assn_cast_c VALUES (2, 2.0);

statement error update on table "assn_cast_c" violates foreign key constraint "assn_cast_c_p_fkey"
UPDATE assn_cast_p SET p = 1.2

statement ok
UPDATE assn_cast_p SET p = 3.0

query IF
SELECT * FROM assn_cast_c
----
2 3


# Regression tests.
subtest regressions

Expand Down
15 changes: 12 additions & 3 deletions pkg/sql/logictest/testdata/logic_test/update
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,24 @@ CREATE TABLE kv (
statement error value type tuple{int, int} doesn't match type int of column "v"
UPDATE kv SET v = (SELECT (10, 11))

statement error value type decimal doesn't match type int of column "v"
statement ok
UPDATE kv SET v = 3.2

statement error value type decimal doesn't match type int of column "v"
statement ok
UPDATE kv SET (k, v) = (3, 3.2)

statement error value type decimal doesn't match type int of column "v"
statement ok
UPDATE kv SET (k, v) = (SELECT 3, 3.2)

statement error value type string doesn't match type int of column "v"
UPDATE kv SET v = '3.2'::STRING

statement error value type string doesn't match type int of column "v"
UPDATE kv SET (k, v) = (3, '3.2'::STRING)

statement error value type string doesn't match type int of column "v"
UPDATE kv SET (k, v) = (SELECT 3, '3.2'::STRING)

statement count 4
INSERT INTO kv VALUES (1, 2), (3, 4), (5, 6), (7, 8)

Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/opt/exec/explain/testdata/gists_tpce
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ explain(shape):
└── • lookup join (anti)
│ table: status_type@status_type_pkey
│ equality: (t_st_id_new) = (st_id)
│ equality: (column127) = (st_id)
│ equality cols are key
└── • scan buffer
Expand Down
4 changes: 2 additions & 2 deletions pkg/sql/opt/optbuilder/fk_cascade.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ func (cb *onDeleteSetBuilder) Build(
updateExprs[i].Expr = tree.DefaultVal{}
}
}
mb.addUpdateCols(updateExprs)
mb.addUpdateCols(updateExprs, false /* isUpsert */)

// TODO(radu): consider plumbing a flag to prevent building the FK check
// against the parent we are cascading from. Need to investigate in which
Expand Down Expand Up @@ -687,7 +687,7 @@ func (cb *onUpdateCascadeBuilder) Build(
panic(errors.AssertionFailedf("unsupported action"))
}
}
mb.addUpdateCols(updateExprs)
mb.addUpdateCols(updateExprs, false /* isUpsert */)

mb.buildUpdate(nil /* returning */)
return mb.outScope.expr
Expand Down
5 changes: 3 additions & 2 deletions pkg/sql/opt/optbuilder/insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ func (b *Builder) buildInsert(ins *tree.Insert, inScope *scope) (outScope *scope

// Add additional columns for computed expressions that may depend on any
// updated columns, as well as mutation columns with default values.
mb.addSynthesizedColsForUpdate()
mb.addSynthesizedColsForUpdate(true /* isUpsert */)
}

// Build the final upsert statement, including any returned expressions.
Expand All @@ -334,7 +334,7 @@ func (b *Builder) buildInsert(ins *tree.Insert, inScope *scope) (outScope *scope
mb.addTargetColsForUpdate(ins.OnConflict.Exprs)

// Build each of the SET expressions.
mb.addUpdateCols(ins.OnConflict.Exprs)
mb.addUpdateCols(ins.OnConflict.Exprs, true /* isUpsert */)

// Build the final upsert statement, including any returned expressions.
mb.buildUpsert(returning)
Expand Down Expand Up @@ -646,6 +646,7 @@ func (mb *mutationBuilder) buildInputForInsert(
}

if !isUpsert {
// Add assignment casts for insert columns.
mb.addAssignmentCasts(mb.insertColIDs)
}
}
Expand Down
Loading

0 comments on commit cb03bcc

Please sign in to comment.