exec-ddl
CREATE TABLE abc (a int primary key, b int, c int)
----

exec-ddl
CREATE TABLE new_abc (a int, b int, c int)
----

exec-ddl
CREATE TABLE dec (k INT PRIMARY KEY, d DECIMAL(10, 2))
----

# Test a self join.
opt
UPDATE abc SET b = other.b + 1, c = other.c + 1 FROM abc AS other WHERE abc.a = other.a
----
update abc
 ├── columns: <none>
 ├── fetch columns: abc.a:6 abc.b:7 abc.c:8
 ├── passthrough columns: other.a:11 other.b:12 other.c:13 other.crdb_internal_mvcc_timestamp:14 other.tableoid:15
 ├── update-mapping:
 │    ├── b_new:16 => abc.b:2
 │    └── c_new:17 => abc.c:3
 └── project
      ├── columns: b_new:16 c_new:17 abc.a:6!null abc.b:7 abc.c:8 other.a:11!null other.b:12 other.c:13 other.crdb_internal_mvcc_timestamp:14 other.tableoid:15
      ├── project
      │    ├── columns: other.a:11!null other.b:12 other.c:13 other.crdb_internal_mvcc_timestamp:14 other.tableoid:15 abc.a:6!null abc.b:7 abc.c:8
      │    ├── scan abc
      │    │    ├── columns: abc.a:6!null abc.b:7 abc.c:8 abc.crdb_internal_mvcc_timestamp:9 abc.tableoid:10
      │    │    └── flags: avoid-full-scan
      │    └── projections
      │         ├── abc.a:6 [as=other.a:11]
      │         ├── abc.b:7 [as=other.b:12]
      │         ├── abc.c:8 [as=other.c:13]
      │         ├── abc.crdb_internal_mvcc_timestamp:9 [as=other.crdb_internal_mvcc_timestamp:14]
      │         └── abc.tableoid:10 [as=other.tableoid:15]
      └── projections
           ├── other.b:12 + 1 [as=b_new:16]
           └── other.c:13 + 1 [as=c_new:17]

# Test when Update uses multiple tables.
opt
UPDATE abc SET b = other.b, c = other.c FROM new_abc AS other WHERE abc.a = other.a
----
update abc
 ├── columns: <none>
 ├── fetch columns: abc.a:6 abc.b:7 abc.c:8
 ├── passthrough columns: other.a:11 other.b:12 other.c:13 rowid:14 other.crdb_internal_mvcc_timestamp:15 other.tableoid:16
 ├── update-mapping:
 │    ├── other.b:12 => abc.b:2
 │    └── other.c:13 => abc.c:3
 └── distinct-on
      ├── columns: abc.a:6!null abc.b:7 abc.c:8 other.a:11!null other.b:12 other.c:13 rowid:14!null other.crdb_internal_mvcc_timestamp:15 other.tableoid:16
      ├── grouping columns: abc.a:6!null
      ├── inner-join (lookup abc)
      │    ├── columns: abc.a:6!null abc.b:7 abc.c:8 other.a:11!null other.b:12 other.c:13 rowid:14!null other.crdb_internal_mvcc_timestamp:15 other.tableoid:16
      │    ├── key columns: [11] = [6]
      │    ├── lookup columns are key
      │    ├── scan new_abc [as=other]
      │    │    └── columns: other.a:11 other.b:12 other.c:13 rowid:14!null other.crdb_internal_mvcc_timestamp:15 other.tableoid:16
      │    └── filters (true)
      └── aggregations
           ├── first-agg [as=abc.b:7]
           │    └── abc.b:7
           ├── first-agg [as=abc.c:8]
           │    └── abc.c:8
           ├── first-agg [as=other.a:11]
           │    └── other.a:11
           ├── first-agg [as=other.b:12]
           │    └── other.b:12
           ├── first-agg [as=other.c:13]
           │    └── other.c:13
           ├── first-agg [as=rowid:14]
           │    └── rowid:14
           ├── first-agg [as=other.crdb_internal_mvcc_timestamp:15]
           │    └── other.crdb_internal_mvcc_timestamp:15
           └── first-agg [as=other.tableoid:16]
                └── other.tableoid:16

# Check if UPDATE FROM works well with RETURNING expressions that reference the FROM tables.
opt
UPDATE abc
SET
  b = old.b + 1, c = old.c + 2
FROM
  abc AS old
WHERE
  abc.a = old.a
RETURNING
  abc.a, abc.b AS new_b, old.b as old_b, abc.c as new_c, old.c as old_c
----
update abc
 ├── columns: a:1!null new_b:2 old_b:12 new_c:3 old_c:13
 ├── fetch columns: abc.a:6 abc.b:7 abc.c:8
 ├── passthrough columns: old.b:12 old.c:13
 ├── update-mapping:
 │    ├── b_new:16 => abc.b:2
 │    └── c_new:17 => abc.c:3
 ├── return-mapping:
 │    ├── abc.a:6 => abc.a:1
 │    ├── b_new:16 => abc.b:2
 │    └── c_new:17 => abc.c:3
 └── project
      ├── columns: b_new:16 c_new:17 abc.a:6!null abc.b:7 abc.c:8 old.b:12 old.c:13
      ├── project
      │    ├── columns: old.b:12 old.c:13 abc.a:6!null abc.b:7 abc.c:8
      │    ├── scan abc
      │    │    ├── columns: abc.a:6!null abc.b:7 abc.c:8
      │    │    └── flags: avoid-full-scan
      │    └── projections
      │         ├── abc.b:7 [as=old.b:12]
      │         └── abc.c:8 [as=old.c:13]
      └── projections
           ├── old.b:12 + 1 [as=b_new:16]
           └── old.c:13 + 2 [as=c_new:17]

# Check if RETURNING * returns everything
opt
UPDATE abc SET b = old.b + 1, c = old.c + 2 FROM abc AS old WHERE abc.a = old.a RETURNING *
----
update abc
 ├── columns: a:1!null b:2 c:3 a:11 b:12 c:13
 ├── fetch columns: abc.a:6 abc.b:7 abc.c:8
 ├── passthrough columns: old.a:11 old.b:12 old.c:13
 ├── update-mapping:
 │    ├── b_new:16 => abc.b:2
 │    └── c_new:17 => abc.c:3
 ├── return-mapping:
 │    ├── abc.a:6 => abc.a:1
 │    ├── b_new:16 => abc.b:2
 │    └── c_new:17 => abc.c:3
 └── project
      ├── columns: b_new:16 c_new:17 abc.a:6!null abc.b:7 abc.c:8 old.a:11!null old.b:12 old.c:13
      ├── project
      │    ├── columns: old.a:11!null old.b:12 old.c:13 abc.a:6!null abc.b:7 abc.c:8
      │    ├── scan abc
      │    │    ├── columns: abc.a:6!null abc.b:7 abc.c:8
      │    │    └── flags: avoid-full-scan
      │    └── projections
      │         ├── abc.a:6 [as=old.a:11]
      │         ├── abc.b:7 [as=old.b:12]
      │         └── abc.c:8 [as=old.c:13]
      └── projections
           ├── old.b:12 + 1 [as=b_new:16]
           └── old.c:13 + 2 [as=c_new:17]

# Check if the joins are optimized (check if the filters are pushed down).
opt
UPDATE abc SET b = old.b + 1, c = old.c + 2 FROM abc AS old WHERE abc.a = old.a AND abc.a = 2
----
update abc
 ├── columns: <none>
 ├── fetch columns: abc.a:6 abc.b:7 abc.c:8
 ├── passthrough columns: old.a:11 old.b:12 old.c:13 old.crdb_internal_mvcc_timestamp:14 old.tableoid:15
 ├── update-mapping:
 │    ├── b_new:16 => abc.b:2
 │    └── c_new:17 => abc.c:3
 └── project
      ├── columns: b_new:16 c_new:17 abc.a:6!null abc.b:7 abc.c:8 old.a:11!null old.b:12 old.c:13 old.crdb_internal_mvcc_timestamp:14 old.tableoid:15
      ├── inner-join (cross)
      │    ├── columns: abc.a:6!null abc.b:7 abc.c:8 old.a:11!null old.b:12 old.c:13 old.crdb_internal_mvcc_timestamp:14 old.tableoid:15
      │    ├── scan abc
      │    │    ├── columns: abc.a:6!null abc.b:7 abc.c:8
      │    │    ├── constraint: /6: [/2 - /2]
      │    │    └── flags: avoid-full-scan
      │    ├── scan abc [as=old]
      │    │    ├── columns: old.a:11!null old.b:12 old.c:13 old.crdb_internal_mvcc_timestamp:14 old.tableoid:15
      │    │    └── constraint: /11: [/2 - /2]
      │    └── filters (true)
      └── projections
           ├── old.b:12 + 1 [as=b_new:16]
           └── old.c:13 + 2 [as=c_new:17]

# Update values of table from values expression
opt
UPDATE abc SET b = other.b, c = other.c FROM (values (1, 2, 3), (2, 3, 4)) as other ("a", "b", "c") WHERE abc.a = other.a
----
update abc
 ├── columns: <none>
 ├── fetch columns: a:6 b:7 c:8
 ├── passthrough columns: column1:11 column2:12 column3:13
 ├── update-mapping:
 │    ├── column2:12 => b:2
 │    └── column3:13 => c:3
 └── distinct-on
      ├── columns: a:6!null b:7 c:8 column1:11!null column2:12!null column3:13!null
      ├── grouping columns: a:6!null
      ├── inner-join (lookup abc)
      │    ├── columns: a:6!null b:7 c:8 column1:11!null column2:12!null column3:13!null
      │    ├── key columns: [11] = [6]
      │    ├── lookup columns are key
      │    ├── values
      │    │    ├── columns: column1:11!null column2:12!null column3:13!null
      │    │    ├── (1, 2, 3)
      │    │    └── (2, 3, 4)
      │    └── filters (true)
      └── aggregations
           ├── first-agg [as=b:7]
           │    └── b:7
           ├── first-agg [as=c:8]
           │    └── c:8
           ├── first-agg [as=column1:11]
           │    └── column1:11
           ├── first-agg [as=column2:12]
           │    └── column2:12
           └── first-agg [as=column3:13]
                └── column3:13

# Check if UPDATE ... FROM works with multiple tables.
exec-ddl
CREATE TABLE ab (a INT, b INT)
----

exec-ddl
CREATE TABLE ac (a INT, c INT)
----

opt
UPDATE abc SET b = ab.b, c = ac.c FROM ab, ac WHERE abc.a = ab.a AND abc.a = ac.a
----
update abc
 ├── columns: <none>
 ├── fetch columns: abc.a:6 abc.b:7 abc.c:8
 ├── passthrough columns: ab.a:11 ab.b:12 ab.rowid:13 ab.crdb_internal_mvcc_timestamp:14 ab.tableoid:15 ac.a:16 ac.c:17 ac.rowid:18 ac.crdb_internal_mvcc_timestamp:19 ac.tableoid:20
 ├── update-mapping:
 │    ├── ab.b:12 => abc.b:2
 │    └── ac.c:17 => abc.c:3
 └── distinct-on
      ├── columns: abc.a:6!null abc.b:7 abc.c:8 ab.a:11!null ab.b:12 ab.rowid:13!null ab.crdb_internal_mvcc_timestamp:14 ab.tableoid:15 ac.a:16!null ac.c:17 ac.rowid:18!null ac.crdb_internal_mvcc_timestamp:19 ac.tableoid:20
      ├── grouping columns: abc.a:6!null
      ├── inner-join (hash)
      │    ├── columns: abc.a:6!null abc.b:7 abc.c:8 ab.a:11!null ab.b:12 ab.rowid:13!null ab.crdb_internal_mvcc_timestamp:14 ab.tableoid:15 ac.a:16!null ac.c:17 ac.rowid:18!null ac.crdb_internal_mvcc_timestamp:19 ac.tableoid:20
      │    ├── scan ab
      │    │    └── columns: ab.a:11 ab.b:12 ab.rowid:13!null ab.crdb_internal_mvcc_timestamp:14 ab.tableoid:15
      │    ├── inner-join (lookup abc)
      │    │    ├── columns: abc.a:6!null abc.b:7 abc.c:8 ac.a:16!null ac.c:17 ac.rowid:18!null ac.crdb_internal_mvcc_timestamp:19 ac.tableoid:20
      │    │    ├── key columns: [16] = [6]
      │    │    ├── lookup columns are key
      │    │    ├── scan ac
      │    │    │    └── columns: ac.a:16 ac.c:17 ac.rowid:18!null ac.crdb_internal_mvcc_timestamp:19 ac.tableoid:20
      │    │    └── filters (true)
      │    └── filters
      │         └── ab.a:11 = ac.a:16
      └── aggregations
           ├── first-agg [as=abc.b:7]
           │    └── abc.b:7
           ├── first-agg [as=abc.c:8]
           │    └── abc.c:8
           ├── first-agg [as=ab.a:11]
           │    └── ab.a:11
           ├── first-agg [as=ab.b:12]
           │    └── ab.b:12
           ├── first-agg [as=ab.rowid:13]
           │    └── ab.rowid:13
           ├── first-agg [as=ab.crdb_internal_mvcc_timestamp:14]
           │    └── ab.crdb_internal_mvcc_timestamp:14
           ├── first-agg [as=ab.tableoid:15]
           │    └── ab.tableoid:15
           ├── first-agg [as=ac.a:16]
           │    └── ac.a:16
           ├── first-agg [as=ac.c:17]
           │    └── ac.c:17
           ├── first-agg [as=ac.rowid:18]
           │    └── ac.rowid:18
           ├── first-agg [as=ac.crdb_internal_mvcc_timestamp:19]
           │    └── ac.crdb_internal_mvcc_timestamp:19
           └── first-agg [as=ac.tableoid:20]
                └── ac.tableoid:20

# Make sure UPDATE ... FROM works with LATERAL.
opt
UPDATE abc
SET
  b=ab.b, c = other.c
FROM
  ab, LATERAL
    (SELECT * FROM ac WHERE ab.a=ac.a) AS other
WHERE
  abc.a=ab.a
RETURNING
  *
----
update abc
 ├── columns: a:1!null b:2 c:3 a:11 b:12 a:16 c:17
 ├── fetch columns: abc.a:6 abc.b:7 abc.c:8
 ├── passthrough columns: ab.a:11 ab.b:12 ac.a:16 ac.c:17
 ├── update-mapping:
 │    ├── ab.b:12 => abc.b:2
 │    └── ac.c:17 => abc.c:3
 ├── return-mapping:
 │    ├── abc.a:6 => abc.a:1
 │    ├── ab.b:12 => abc.b:2
 │    └── ac.c:17 => abc.c:3
 └── distinct-on
      ├── columns: abc.a:6!null abc.b:7 abc.c:8 ab.a:11!null ab.b:12 ac.a:16!null ac.c:17
      ├── grouping columns: abc.a:6!null
      ├── inner-join (hash)
      │    ├── columns: abc.a:6!null abc.b:7 abc.c:8 ab.a:11!null ab.b:12 ac.a:16!null ac.c:17
      │    ├── scan ab
      │    │    └── columns: ab.a:11 ab.b:12
      │    ├── inner-join (lookup abc)
      │    │    ├── columns: abc.a:6!null abc.b:7 abc.c:8 ac.a:16!null ac.c:17
      │    │    ├── key columns: [16] = [6]
      │    │    ├── lookup columns are key
      │    │    ├── scan ac
      │    │    │    └── columns: ac.a:16 ac.c:17
      │    │    └── filters (true)
      │    └── filters
      │         └── ab.a:11 = ac.a:16
      └── aggregations
           ├── first-agg [as=abc.b:7]
           │    └── abc.b:7
           ├── first-agg [as=abc.c:8]
           │    └── abc.c:8
           ├── first-agg [as=ab.a:11]
           │    └── ab.a:11
           ├── first-agg [as=ab.b:12]
           │    └── ab.b:12
           ├── first-agg [as=ac.a:16]
           │    └── ac.a:16
           └── first-agg [as=ac.c:17]
                └── ac.c:17

# Make sure UPDATE ... FROM can return hidden columns.
opt
UPDATE abc
SET
  b=ab.b, c = ac.c
FROM
  ab, ac
WHERE
  abc.a=ab.a AND abc.a = ac.a
RETURNING
  *, ab.rowid, ac.rowid
----
update abc
 ├── columns: a:1!null b:2 c:3 a:11 b:12 a:16 c:17 rowid:13 rowid:18
 ├── fetch columns: abc.a:6 abc.b:7 abc.c:8
 ├── passthrough columns: ab.a:11 ab.b:12 ab.rowid:13 ac.a:16 ac.c:17 ac.rowid:18
 ├── update-mapping:
 │    ├── ab.b:12 => abc.b:2
 │    └── ac.c:17 => abc.c:3
 ├── return-mapping:
 │    ├── abc.a:6 => abc.a:1
 │    ├── ab.b:12 => abc.b:2
 │    └── ac.c:17 => abc.c:3
 └── distinct-on
      ├── columns: abc.a:6!null abc.b:7 abc.c:8 ab.a:11!null ab.b:12 ab.rowid:13!null ac.a:16!null ac.c:17 ac.rowid:18!null
      ├── grouping columns: abc.a:6!null
      ├── inner-join (hash)
      │    ├── columns: abc.a:6!null abc.b:7 abc.c:8 ab.a:11!null ab.b:12 ab.rowid:13!null ac.a:16!null ac.c:17 ac.rowid:18!null
      │    ├── scan ab
      │    │    └── columns: ab.a:11 ab.b:12 ab.rowid:13!null
      │    ├── inner-join (lookup abc)
      │    │    ├── columns: abc.a:6!null abc.b:7 abc.c:8 ac.a:16!null ac.c:17 ac.rowid:18!null
      │    │    ├── key columns: [16] = [6]
      │    │    ├── lookup columns are key
      │    │    ├── scan ac
      │    │    │    └── columns: ac.a:16 ac.c:17 ac.rowid:18!null
      │    │    └── filters (true)
      │    └── filters
      │         └── ab.a:11 = ac.a:16
      └── aggregations
           ├── first-agg [as=abc.b:7]
           │    └── abc.b:7
           ├── first-agg [as=abc.c:8]
           │    └── abc.c:8
           ├── first-agg [as=ab.a:11]
           │    └── ab.a:11
           ├── first-agg [as=ab.b:12]
           │    └── ab.b:12
           ├── first-agg [as=ab.rowid:13]
           │    └── ab.rowid:13
           ├── first-agg [as=ac.a:16]
           │    └── ac.a:16
           ├── first-agg [as=ac.c:17]
           │    └── ac.c:17
           └── first-agg [as=ac.rowid:18]
                └── ac.rowid:18

opt
UPDATE abc SET b = other.d FROM dec AS other WHERE abc.a = other.k
----
update abc
 ├── columns: <none>
 ├── fetch columns: a:6 b:7 c:8
 ├── passthrough columns: k:11 d:12 other.crdb_internal_mvcc_timestamp:13 other.tableoid:14
 ├── update-mapping:
 │    └── b_cast:15 => b:2
 └── project
      ├── columns: b_cast:15 a:6!null b:7 c:8 k:11!null d:12 other.crdb_internal_mvcc_timestamp:13 other.tableoid:14
      ├── inner-join (lookup abc)
      │    ├── columns: a:6!null b:7 c:8 k:11!null d:12 other.crdb_internal_mvcc_timestamp:13 other.tableoid:14
      │    ├── key columns: [11] = [6]
      │    ├── lookup columns are key
      │    ├── scan dec [as=other]
      │    │    └── columns: k:11!null d:12 other.crdb_internal_mvcc_timestamp:13 other.tableoid:14
      │    └── filters (true)
      └── projections
           └── assignment-cast: INT8 [as=b_cast:15]
                └── d:12

# Regression test for #61520. The new value for column a should be in-scope when
# building the partial index PUT expression when the value in the FROM clause
# must be rounded.

exec-ddl
CREATE TABLE t61520 (
  a DECIMAL(10, 2),
  CHECK (a > 0)
)
----

build
UPDATE t61520 t SET a = v.b FROM (VALUES (1.0)) v(b) WHERE t.a = v.b RETURNING a, b
----
project
 ├── columns: a:1!null b:9
 └── update t61520 [as=t]
      ├── columns: a:1!null rowid:2!null column1:9
      ├── fetch columns: a:5 rowid:6
      ├── passthrough columns: column1:9
      ├── update-mapping:
      │    └── a_cast:10 => a:1
      ├── return-mapping:
      │    ├── a_cast:10 => a:1
      │    └── rowid:6 => rowid:2
      ├── check columns: check1:11
      └── project
           ├── columns: check1:11!null a:5!null rowid:6!null crdb_internal_mvcc_timestamp:7 tableoid:8 column1:9!null a_cast:10!null
           ├── project
           │    ├── columns: a_cast:10!null a:5!null rowid:6!null crdb_internal_mvcc_timestamp:7 tableoid:8 column1:9!null
           │    ├── distinct-on
           │    │    ├── columns: a:5!null rowid:6!null crdb_internal_mvcc_timestamp:7 tableoid:8 column1:9!null
           │    │    ├── grouping columns: rowid:6!null
           │    │    ├── select
           │    │    │    ├── columns: a:5!null rowid:6!null crdb_internal_mvcc_timestamp:7 tableoid:8 column1:9!null
           │    │    │    ├── inner-join (cross)
           │    │    │    │    ├── columns: a:5 rowid:6!null crdb_internal_mvcc_timestamp:7 tableoid:8 column1:9!null
           │    │    │    │    ├── scan t61520 [as=t]
           │    │    │    │    │    ├── columns: a:5 rowid:6!null crdb_internal_mvcc_timestamp:7 tableoid:8
           │    │    │    │    │    └── flags: avoid-full-scan
           │    │    │    │    ├── values
           │    │    │    │    │    ├── columns: column1:9!null
           │    │    │    │    │    └── (1.0,)
           │    │    │    │    └── filters (true)
           │    │    │    └── filters
           │    │    │         └── a:5 = column1:9
           │    │    └── aggregations
           │    │         ├── first-agg [as=a:5]
           │    │         │    └── a:5
           │    │         ├── first-agg [as=crdb_internal_mvcc_timestamp:7]
           │    │         │    └── crdb_internal_mvcc_timestamp:7
           │    │         ├── first-agg [as=tableoid:8]
           │    │         │    └── tableoid:8
           │    │         └── first-agg [as=column1:9]
           │    │              └── column1:9
           │    └── projections
           │         └── assignment-cast: DECIMAL(10,2) [as=a_cast:10]
           │              └── column1:9
           └── projections
                └── a_cast:10 > 0 [as=check1:11]

# Regression test for #89779. Do not should not omit hidden PK columns from the
# distinct-on operator the deduplicates rows from the target table.
exec-ddl
CREATE TABLE t89779 (a INT)
----

build
UPDATE t89779 SET a = 2 FROM (VALUES (1)) v(i) WHERE a = i RETURNING rowid, a
----
project
 ├── columns: rowid:2!null a:1!null
 └── update t89779
      ├── columns: a:1!null rowid:2!null column1:9
      ├── fetch columns: a:5 rowid:6
      ├── passthrough columns: column1:9
      ├── update-mapping:
      │    └── a_new:10 => a:1
      ├── return-mapping:
      │    ├── a_new:10 => a:1
      │    └── rowid:6 => rowid:2
      └── project
           ├── columns: a_new:10!null a:5!null rowid:6!null crdb_internal_mvcc_timestamp:7 tableoid:8 column1:9!null
           ├── distinct-on
           │    ├── columns: a:5!null rowid:6!null crdb_internal_mvcc_timestamp:7 tableoid:8 column1:9!null
           │    ├── grouping columns: rowid:6!null
           │    ├── select
           │    │    ├── columns: a:5!null rowid:6!null crdb_internal_mvcc_timestamp:7 tableoid:8 column1:9!null
           │    │    ├── inner-join (cross)
           │    │    │    ├── columns: a:5 rowid:6!null crdb_internal_mvcc_timestamp:7 tableoid:8 column1:9!null
           │    │    │    ├── scan t89779
           │    │    │    │    ├── columns: a:5 rowid:6!null crdb_internal_mvcc_timestamp:7 tableoid:8
           │    │    │    │    └── flags: avoid-full-scan
           │    │    │    ├── values
           │    │    │    │    ├── columns: column1:9!null
           │    │    │    │    └── (1,)
           │    │    │    └── filters (true)
           │    │    └── filters
           │    │         └── a:5 = column1:9
           │    └── aggregations
           │         ├── first-agg [as=a:5]
           │         │    └── a:5
           │         ├── first-agg [as=crdb_internal_mvcc_timestamp:7]
           │         │    └── crdb_internal_mvcc_timestamp:7
           │         ├── first-agg [as=tableoid:8]
           │         │    └── tableoid:8
           │         └── first-agg [as=column1:9]
           │              └── column1:9
           └── projections
                └── 2 [as=a_new:10]
