exec-ddl
CREATE TABLE abc (
    a INT NOT NULL,
    b INT DEFAULT (10),
    c INT AS (b + 1) STORED,
    UNIQUE(a),
    UNIQUE(b, c)
)
----

exec-ddl
CREATE TABLE xyz (
    x INT PRIMARY KEY,
    y INT,
    z INT,
    UNIQUE (y, z),
    UNIQUE (z, y),
    INDEX (y DESC)
)
----

exec-ddl
CREATE TABLE uv (
    u INT,
    v INT,
    PRIMARY KEY (u, v)
)
----

exec-ddl
CREATE TABLE noindex (
    x INT PRIMARY KEY,
    y INT,
    z INT
)
----

exec-ddl
CREATE TABLE mutation (
    m INT PRIMARY KEY,
    n INT,
    "o:write-only" INT DEFAULT(10),
    "p:write-only" INT AS (o + n) STORED,
    "q:delete-only" INT AS (m * p) STORED,
    CHECK (m > 0)
)
----

exec-ddl
CREATE TABLE checks (
    a INT PRIMARY KEY CHECK (a > 0),
    b INT,
    c INT,
    d INT AS (c + 1) STORED,
    CHECK (b < d)
)
----

exec-ddl
CREATE TABLE on_update_bare (
    a INT PRIMARY KEY,
    v INT ON UPDATE 5
)
----

exec-ddl
CREATE TABLE on_update_with_default (
    a INT PRIMARY KEY,
    b INT,
    v INT DEFAULT 4 ON UPDATE 10
)
----

exec-ddl
CREATE TABLE decimals (
    a DECIMAL(10,0) PRIMARY KEY CHECK (round(a) = a),
    b DECIMAL(5,1)[] CHECK (b[0] > 1),
    c DECIMAL(10,1) DEFAULT (1.23),
    d DECIMAL(10,1) AS (a+c) STORED
)
----

exec-ddl
CREATE TABLE uniq (
  x STRING PRIMARY KEY,
  y STRING UNIQUE,
  z STRING UNIQUE
)
----

exec-ddl
CREATE TABLE assn_cast (
    k INT PRIMARY KEY,
    c CHAR,
    qc "char",
    i INT DEFAULT 10::INT2,
    s STRING,
    d DECIMAL(10, 0),
    d_comp DECIMAL(10, 0) AS (d + 10.0) STORED
)
----

exec-ddl
CREATE TABLE assn_cast_on_update (
    k INT PRIMARY KEY,
    i INT,
    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
)
----

# ------------------------------------------------------------------------------
# Basic tests.
# ------------------------------------------------------------------------------

# Set single column, single column conflict.
build
INSERT INTO abc (a, b)
SELECT x, y FROM xyz
ON CONFLICT (a) DO
UPDATE SET a=5
----
upsert abc
 ├── arbiter indexes: abc_a_key
 ├── columns: <none>
 ├── canary column: a:14
 ├── fetch columns: a:14 b:15 c:16 rowid:17
 ├── insert-mapping:
 │    ├── x:7 => a:1
 │    ├── y:8 => b:2
 │    ├── c_comp:13 => c:3
 │    └── rowid_default:12 => rowid:4
 ├── update-mapping:
 │    └── upsert_a:22 => a:1
 └── project
      ├── columns: upsert_a:22!null upsert_b:23 upsert_c:24 upsert_rowid:25 x:7!null y:8 rowid_default:12 c_comp:13 a:14 b:15 c:16 rowid:17 abc.crdb_internal_mvcc_timestamp:18 abc.tableoid:19 a_new:20!null c_comp:21
      ├── project
      │    ├── columns: c_comp:21 x:7!null y:8 rowid_default:12 c_comp:13 a:14 b:15 c:16 rowid:17 abc.crdb_internal_mvcc_timestamp:18 abc.tableoid:19 a_new:20!null
      │    ├── project
      │    │    ├── columns: a_new:20!null x:7!null y:8 rowid_default:12 c_comp:13 a:14 b:15 c:16 rowid:17 abc.crdb_internal_mvcc_timestamp:18 abc.tableoid:19
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: x:7!null y:8 rowid_default:12 c_comp:13 a:14 b:15 c:16 rowid:17 abc.crdb_internal_mvcc_timestamp:18 abc.tableoid:19
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: x:7!null y:8 rowid_default:12 c_comp:13
      │    │    │    │    ├── grouping columns: x:7!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: c_comp:13 x:7!null y:8 rowid_default:12
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: rowid_default:12 x:7!null y:8
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: x:7!null y:8
      │    │    │    │    │    │    │    └── scan xyz
      │    │    │    │    │    │    │         └── columns: x:7!null y:8 z:9 xyz.crdb_internal_mvcc_timestamp:10 xyz.tableoid:11
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── unique_rowid() [as=rowid_default:12]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── y:8 + 1 [as=c_comp:13]
      │    │    │    │    └── aggregations
      │    │    │    │         ├── first-agg [as=y:8]
      │    │    │    │         │    └── y:8
      │    │    │    │         ├── first-agg [as=rowid_default:12]
      │    │    │    │         │    └── rowid_default:12
      │    │    │    │         └── first-agg [as=c_comp:13]
      │    │    │    │              └── c_comp:13
      │    │    │    ├── scan abc
      │    │    │    │    ├── columns: a:14!null b:15 c:16 rowid:17!null abc.crdb_internal_mvcc_timestamp:18 abc.tableoid:19
      │    │    │    │    ├── computed column expressions
      │    │    │    │    │    └── c:16
      │    │    │    │    │         └── b:15 + 1
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── x:7 = a:14
      │    │    └── projections
      │    │         └── 5 [as=a_new:20]
      │    └── projections
      │         └── b:15 + 1 [as=c_comp:21]
      └── projections
           ├── CASE WHEN a:14 IS NULL THEN x:7 ELSE a_new:20 END [as=upsert_a:22]
           ├── CASE WHEN a:14 IS NULL THEN y:8 ELSE b:15 END [as=upsert_b:23]
           ├── CASE WHEN a:14 IS NULL THEN c_comp:13 ELSE c:16 END [as=upsert_c:24]
           └── CASE WHEN a:14 IS NULL THEN rowid_default:12 ELSE rowid:17 END [as=upsert_rowid:25]

# Set all columns, multi-column conflict.
build
INSERT INTO abc (a, b, rowid)
SELECT x, y, z FROM xyz
ON CONFLICT (b, c) DO
UPDATE SET a=1, b=2, rowid=3
RETURNING *
----
project
 ├── columns: a:1!null b:2 c:3
 └── upsert abc
      ├── columns: a:1!null b:2 c:3 rowid:4!null
      ├── arbiter indexes: abc_b_c_key
      ├── canary column: rowid:16
      ├── fetch columns: a:13 b:14 c:15 rowid:16
      ├── insert-mapping:
      │    ├── x:7 => a:1
      │    ├── y:8 => b:2
      │    ├── c_comp:12 => c:3
      │    └── z:9 => rowid:4
      ├── update-mapping:
      │    ├── upsert_a:23 => a:1
      │    ├── upsert_b:24 => b:2
      │    ├── upsert_c:25 => c:3
      │    └── upsert_rowid:26 => rowid:4
      ├── return-mapping:
      │    ├── upsert_a:23 => a:1
      │    ├── upsert_b:24 => b:2
      │    ├── upsert_c:25 => c:3
      │    └── upsert_rowid:26 => rowid:4
      └── project
           ├── columns: upsert_a:23!null upsert_b:24 upsert_c:25 upsert_rowid:26 x:7!null y:8 z:9 c_comp:12 a:13 b:14 c:15 rowid:16 abc.crdb_internal_mvcc_timestamp:17 abc.tableoid:18 a_new:19!null b_new:20!null rowid_new:21!null c_comp:22!null
           ├── project
           │    ├── columns: c_comp:22!null x:7!null y:8 z:9 c_comp:12 a:13 b:14 c:15 rowid:16 abc.crdb_internal_mvcc_timestamp:17 abc.tableoid:18 a_new:19!null b_new:20!null rowid_new:21!null
           │    ├── project
           │    │    ├── columns: a_new:19!null b_new:20!null rowid_new:21!null x:7!null y:8 z:9 c_comp:12 a:13 b:14 c:15 rowid:16 abc.crdb_internal_mvcc_timestamp:17 abc.tableoid:18
           │    │    ├── left-join (hash)
           │    │    │    ├── columns: x:7!null y:8 z:9 c_comp:12 a:13 b:14 c:15 rowid:16 abc.crdb_internal_mvcc_timestamp:17 abc.tableoid:18
           │    │    │    ├── ensure-upsert-distinct-on
           │    │    │    │    ├── columns: x:7!null y:8 z:9 c_comp:12
           │    │    │    │    ├── grouping columns: y:8 c_comp:12
           │    │    │    │    ├── project
           │    │    │    │    │    ├── columns: c_comp:12 x:7!null y:8 z:9
           │    │    │    │    │    ├── project
           │    │    │    │    │    │    ├── columns: x:7!null y:8 z:9
           │    │    │    │    │    │    └── scan xyz
           │    │    │    │    │    │         └── columns: x:7!null y:8 z:9 xyz.crdb_internal_mvcc_timestamp:10 xyz.tableoid:11
           │    │    │    │    │    └── projections
           │    │    │    │    │         └── y:8 + 1 [as=c_comp:12]
           │    │    │    │    └── aggregations
           │    │    │    │         ├── first-agg [as=x:7]
           │    │    │    │         │    └── x:7
           │    │    │    │         └── first-agg [as=z:9]
           │    │    │    │              └── z:9
           │    │    │    ├── scan abc
           │    │    │    │    ├── columns: a:13!null b:14 c:15 rowid:16!null abc.crdb_internal_mvcc_timestamp:17 abc.tableoid:18
           │    │    │    │    ├── computed column expressions
           │    │    │    │    │    └── c:15
           │    │    │    │    │         └── b:14 + 1
           │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
           │    │    │    └── filters
           │    │    │         ├── y:8 = b:14
           │    │    │         └── c_comp:12 = c:15
           │    │    └── projections
           │    │         ├── 1 [as=a_new:19]
           │    │         ├── 2 [as=b_new:20]
           │    │         └── 3 [as=rowid_new:21]
           │    └── projections
           │         └── b_new:20 + 1 [as=c_comp:22]
           └── projections
                ├── CASE WHEN rowid:16 IS NULL THEN x:7 ELSE a_new:19 END [as=upsert_a:23]
                ├── CASE WHEN rowid:16 IS NULL THEN y:8 ELSE b_new:20 END [as=upsert_b:24]
                ├── CASE WHEN rowid:16 IS NULL THEN c_comp:12 ELSE c_comp:22 END [as=upsert_c:25]
                └── CASE WHEN rowid:16 IS NULL THEN z:9 ELSE rowid_new:21 END [as=upsert_rowid:26]

# UPDATE + WHERE clause.
build
INSERT INTO abc
SELECT x, y FROM xyz
ON CONFLICT (a) DO
UPDATE SET b=10
WHERE abc.a>0
----
upsert abc
 ├── arbiter indexes: abc_a_key
 ├── columns: <none>
 ├── canary column: a:14
 ├── fetch columns: a:14 b:15 c:16 rowid:17
 ├── insert-mapping:
 │    ├── x:7 => a:1
 │    ├── y:8 => b:2
 │    ├── c_comp:13 => c:3
 │    └── rowid_default:12 => rowid:4
 ├── update-mapping:
 │    ├── upsert_b:23 => b:2
 │    └── upsert_c:24 => c:3
 └── project
      ├── columns: upsert_a:22 upsert_b:23 upsert_c:24 upsert_rowid:25 x:7!null y:8 rowid_default:12 c_comp:13 a:14 b:15 c:16 rowid:17 abc.crdb_internal_mvcc_timestamp:18 abc.tableoid:19 b_new:20!null c_comp:21!null
      ├── project
      │    ├── columns: c_comp:21!null x:7!null y:8 rowid_default:12 c_comp:13 a:14 b:15 c:16 rowid:17 abc.crdb_internal_mvcc_timestamp:18 abc.tableoid:19 b_new:20!null
      │    ├── project
      │    │    ├── columns: b_new:20!null x:7!null y:8 rowid_default:12 c_comp:13 a:14 b:15 c:16 rowid:17 abc.crdb_internal_mvcc_timestamp:18 abc.tableoid:19
      │    │    ├── select
      │    │    │    ├── columns: x:7!null y:8 rowid_default:12 c_comp:13 a:14 b:15 c:16 rowid:17 abc.crdb_internal_mvcc_timestamp:18 abc.tableoid:19
      │    │    │    ├── left-join (hash)
      │    │    │    │    ├── columns: x:7!null y:8 rowid_default:12 c_comp:13 a:14 b:15 c:16 rowid:17 abc.crdb_internal_mvcc_timestamp:18 abc.tableoid:19
      │    │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    │    ├── columns: x:7!null y:8 rowid_default:12 c_comp:13
      │    │    │    │    │    ├── grouping columns: x:7!null
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: c_comp:13 x:7!null y:8 rowid_default:12
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: rowid_default:12 x:7!null y:8
      │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    ├── columns: x:7!null y:8
      │    │    │    │    │    │    │    │    └── scan xyz
      │    │    │    │    │    │    │    │         └── columns: x:7!null y:8 z:9 xyz.crdb_internal_mvcc_timestamp:10 xyz.tableoid:11
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── unique_rowid() [as=rowid_default:12]
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── y:8 + 1 [as=c_comp:13]
      │    │    │    │    │    └── aggregations
      │    │    │    │    │         ├── first-agg [as=y:8]
      │    │    │    │    │         │    └── y:8
      │    │    │    │    │         ├── first-agg [as=rowid_default:12]
      │    │    │    │    │         │    └── rowid_default:12
      │    │    │    │    │         └── first-agg [as=c_comp:13]
      │    │    │    │    │              └── c_comp:13
      │    │    │    │    ├── scan abc
      │    │    │    │    │    ├── columns: a:14!null b:15 c:16 rowid:17!null abc.crdb_internal_mvcc_timestamp:18 abc.tableoid:19
      │    │    │    │    │    ├── computed column expressions
      │    │    │    │    │    │    └── c:16
      │    │    │    │    │    │         └── b:15 + 1
      │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    └── filters
      │    │    │    │         └── x:7 = a:14
      │    │    │    └── filters
      │    │    │         └── (a:14 IS NULL) OR (a:14 > 0)
      │    │    └── projections
      │    │         └── 10 [as=b_new:20]
      │    └── projections
      │         └── b_new:20 + 1 [as=c_comp:21]
      └── projections
           ├── CASE WHEN a:14 IS NULL THEN x:7 ELSE a:14 END [as=upsert_a:22]
           ├── CASE WHEN a:14 IS NULL THEN y:8 ELSE b_new:20 END [as=upsert_b:23]
           ├── CASE WHEN a:14 IS NULL THEN c_comp:13 ELSE c_comp:21 END [as=upsert_c:24]
           └── CASE WHEN a:14 IS NULL THEN rowid_default:12 ELSE rowid:17 END [as=upsert_rowid:25]

# Use RETURNING INSERT..ON CONFLICT as a FROM clause.
build
SELECT *
FROM [INSERT INTO abc (a, b) VALUES (1,2), (3,4) ON CONFLICT (a) DO UPDATE SET b=1 RETURNING *]
ORDER BY a, b DESC
----
sort
 ├── columns: a:23!null b:24!null c:25!null
 ├── ordering: +23,-24
 └── with &1
      ├── columns: a:23!null b:24!null c:25!null
      ├── project
      │    ├── columns: abc.a:1!null abc.b:2!null abc.c:3!null
      │    └── upsert abc
      │         ├── columns: abc.a:1!null abc.b:2!null abc.c:3!null rowid:4!null
      │         ├── arbiter indexes: abc_a_key
      │         ├── canary column: abc.a:11
      │         ├── fetch columns: abc.a:11 abc.b:12 abc.c:13 rowid:14
      │         ├── insert-mapping:
      │         │    ├── column1:7 => abc.a:1
      │         │    ├── column2:8 => abc.b:2
      │         │    ├── c_comp:10 => abc.c:3
      │         │    └── rowid_default:9 => rowid:4
      │         ├── update-mapping:
      │         │    ├── upsert_b:20 => abc.b:2
      │         │    └── upsert_c:21 => abc.c:3
      │         ├── return-mapping:
      │         │    ├── upsert_a:19 => abc.a:1
      │         │    ├── upsert_b:20 => abc.b:2
      │         │    ├── upsert_c:21 => abc.c:3
      │         │    └── upsert_rowid:22 => rowid:4
      │         └── project
      │              ├── columns: upsert_a:19 upsert_b:20!null upsert_c:21!null upsert_rowid:22 column1:7!null column2:8!null rowid_default:9 c_comp:10!null abc.a:11 abc.b:12 abc.c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16 b_new:17!null c_comp:18!null
      │              ├── project
      │              │    ├── columns: c_comp:18!null column1:7!null column2:8!null rowid_default:9 c_comp:10!null abc.a:11 abc.b:12 abc.c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16 b_new:17!null
      │              │    ├── project
      │              │    │    ├── columns: b_new:17!null column1:7!null column2:8!null rowid_default:9 c_comp:10!null abc.a:11 abc.b:12 abc.c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │              │    │    ├── left-join (hash)
      │              │    │    │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null abc.a:11 abc.b:12 abc.c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │              │    │    │    ├── ensure-upsert-distinct-on
      │              │    │    │    │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null
      │              │    │    │    │    ├── grouping columns: column1:7!null
      │              │    │    │    │    ├── project
      │              │    │    │    │    │    ├── columns: c_comp:10!null column1:7!null column2:8!null rowid_default:9
      │              │    │    │    │    │    ├── project
      │              │    │    │    │    │    │    ├── columns: rowid_default:9 column1:7!null column2:8!null
      │              │    │    │    │    │    │    ├── values
      │              │    │    │    │    │    │    │    ├── columns: column1:7!null column2:8!null
      │              │    │    │    │    │    │    │    ├── (1, 2)
      │              │    │    │    │    │    │    │    └── (3, 4)
      │              │    │    │    │    │    │    └── projections
      │              │    │    │    │    │    │         └── unique_rowid() [as=rowid_default:9]
      │              │    │    │    │    │    └── projections
      │              │    │    │    │    │         └── column2:8 + 1 [as=c_comp:10]
      │              │    │    │    │    └── aggregations
      │              │    │    │    │         ├── first-agg [as=column2:8]
      │              │    │    │    │         │    └── column2:8
      │              │    │    │    │         ├── first-agg [as=rowid_default:9]
      │              │    │    │    │         │    └── rowid_default:9
      │              │    │    │    │         └── first-agg [as=c_comp:10]
      │              │    │    │    │              └── c_comp:10
      │              │    │    │    ├── scan abc
      │              │    │    │    │    ├── columns: abc.a:11!null abc.b:12 abc.c:13 rowid:14!null crdb_internal_mvcc_timestamp:15 tableoid:16
      │              │    │    │    │    ├── computed column expressions
      │              │    │    │    │    │    └── abc.c:13
      │              │    │    │    │    │         └── abc.b:12 + 1
      │              │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │              │    │    │    └── filters
      │              │    │    │         └── column1:7 = abc.a:11
      │              │    │    └── projections
      │              │    │         └── 1 [as=b_new:17]
      │              │    └── projections
      │              │         └── b_new:17 + 1 [as=c_comp:18]
      │              └── projections
      │                   ├── CASE WHEN abc.a:11 IS NULL THEN column1:7 ELSE abc.a:11 END [as=upsert_a:19]
      │                   ├── CASE WHEN abc.a:11 IS NULL THEN column2:8 ELSE b_new:17 END [as=upsert_b:20]
      │                   ├── CASE WHEN abc.a:11 IS NULL THEN c_comp:10 ELSE c_comp:18 END [as=upsert_c:21]
      │                   └── CASE WHEN abc.a:11 IS NULL THEN rowid_default:9 ELSE rowid:14 END [as=upsert_rowid:22]
      └── with-scan &1
           ├── columns: a:23!null b:24!null c:25!null
           └── mapping:
                ├──  abc.a:1 => a:23
                ├──  abc.b:2 => b:24
                └──  abc.c:3 => c:25

# Use table alias.
build
INSERT INTO abc AS tab (a, b)
VALUES (1, 2)
ON CONFLICT (a) DO
UPDATE SET a=tab.a*excluded.a
----
upsert abc [as=tab]
 ├── arbiter indexes: abc_a_key
 ├── columns: <none>
 ├── canary column: a:11
 ├── fetch columns: a:11 b:12 c:13 rowid:14
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── c_comp:10 => c:3
 │    └── rowid_default:9 => rowid:4
 ├── update-mapping:
 │    └── upsert_a:19 => a:1
 └── project
      ├── columns: upsert_a:19 upsert_b:20 upsert_c:21 upsert_rowid:22 column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16 a_new:17 c_comp:18
      ├── project
      │    ├── columns: c_comp:18 column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16 a_new:17
      │    ├── project
      │    │    ├── columns: a_new:17 column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null
      │    │    │    │    ├── grouping columns: column1:7!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: c_comp:10!null column1:7!null column2:8!null rowid_default:9
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: rowid_default:9 column1:7!null column2:8!null
      │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    ├── columns: column1:7!null column2:8!null
      │    │    │    │    │    │    │    └── (1, 2)
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── unique_rowid() [as=rowid_default:9]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── column2:8 + 1 [as=c_comp:10]
      │    │    │    │    └── aggregations
      │    │    │    │         ├── first-agg [as=column2:8]
      │    │    │    │         │    └── column2:8
      │    │    │    │         ├── first-agg [as=rowid_default:9]
      │    │    │    │         │    └── rowid_default:9
      │    │    │    │         └── first-agg [as=c_comp:10]
      │    │    │    │              └── c_comp:10
      │    │    │    ├── scan abc [as=tab]
      │    │    │    │    ├── columns: a:11!null b:12 c:13 rowid:14!null crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    │    ├── computed column expressions
      │    │    │    │    │    └── c:13
      │    │    │    │    │         └── b:12 + 1
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── column1:7 = a:11
      │    │    └── projections
      │    │         └── a:11 * column1:7 [as=a_new:17]
      │    └── projections
      │         └── b:12 + 1 [as=c_comp:18]
      └── projections
           ├── CASE WHEN a:11 IS NULL THEN column1:7 ELSE a_new:17 END [as=upsert_a:19]
           ├── CASE WHEN a:11 IS NULL THEN column2:8 ELSE b:12 END [as=upsert_b:20]
           ├── CASE WHEN a:11 IS NULL THEN c_comp:10 ELSE c:13 END [as=upsert_c:21]
           └── CASE WHEN a:11 IS NULL THEN rowid_default:9 ELSE rowid:14 END [as=upsert_rowid:22]

# Conflict columns are in different order than index key columns.
build
INSERT INTO abc (a, b)
VALUES (1, 2)
ON CONFLICT (c, b) DO
UPDATE SET a=5
----
upsert abc
 ├── arbiter indexes: abc_b_c_key
 ├── columns: <none>
 ├── canary column: rowid:14
 ├── fetch columns: a:11 b:12 c:13 rowid:14
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── c_comp:10 => c:3
 │    └── rowid_default:9 => rowid:4
 ├── update-mapping:
 │    └── upsert_a:19 => a:1
 └── project
      ├── columns: upsert_a:19!null upsert_b:20 upsert_c:21 upsert_rowid:22 column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16 a_new:17!null c_comp:18
      ├── project
      │    ├── columns: c_comp:18 column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16 a_new:17!null
      │    ├── project
      │    │    ├── columns: a_new:17!null column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null
      │    │    │    │    ├── grouping columns: column2:8!null c_comp:10!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: c_comp:10!null column1:7!null column2:8!null rowid_default:9
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: rowid_default:9 column1:7!null column2:8!null
      │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    ├── columns: column1:7!null column2:8!null
      │    │    │    │    │    │    │    └── (1, 2)
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── unique_rowid() [as=rowid_default:9]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── column2:8 + 1 [as=c_comp:10]
      │    │    │    │    └── aggregations
      │    │    │    │         ├── first-agg [as=column1:7]
      │    │    │    │         │    └── column1:7
      │    │    │    │         └── first-agg [as=rowid_default:9]
      │    │    │    │              └── rowid_default:9
      │    │    │    ├── scan abc
      │    │    │    │    ├── columns: a:11!null b:12 c:13 rowid:14!null crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    │    ├── computed column expressions
      │    │    │    │    │    └── c:13
      │    │    │    │    │         └── b:12 + 1
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         ├── column2:8 = b:12
      │    │    │         └── c_comp:10 = c:13
      │    │    └── projections
      │    │         └── 5 [as=a_new:17]
      │    └── projections
      │         └── b:12 + 1 [as=c_comp:18]
      └── projections
           ├── CASE WHEN rowid:14 IS NULL THEN column1:7 ELSE a_new:17 END [as=upsert_a:19]
           ├── CASE WHEN rowid:14 IS NULL THEN column2:8 ELSE b:12 END [as=upsert_b:20]
           ├── CASE WHEN rowid:14 IS NULL THEN c_comp:10 ELSE c:13 END [as=upsert_c:21]
           └── CASE WHEN rowid:14 IS NULL THEN rowid_default:9 ELSE rowid:14 END [as=upsert_rowid:22]

# Conflict columns don't match unique index (too few columns).
build
INSERT INTO abc (a, b)
VALUES (1, 2)
ON CONFLICT (b) DO
UPDATE SET a=5
----
error (42P10): there is no unique or exclusion constraint matching the ON CONFLICT specification

# Conflict columns don't match unique index (too many columns).
build
INSERT INTO abc (a, b)
VALUES (1, 2)
ON CONFLICT (a, b) DO
UPDATE SET a=5
----
error (42P10): there is no unique or exclusion constraint matching the ON CONFLICT specification

# Conflict column not found.
build
INSERT INTO abc (a, b)
VALUES (1, 2)
ON CONFLICT (a, unknown) DO
UPDATE SET a=5
----
error (42703): column "unknown" does not exist

exec-ddl
CREATE TABLE t102909 (
  id INT PRIMARY KEY,
  a INT NULL DEFAULT 1,
  b INT NULL,
  c INT GENERATED ALWAYS AS (a+b) STORED
)
----

# Regression test for #102909. The reference to column a in the computed column
# expression should not be ambiguous when building the expression in a Project.
build
INSERT INTO t102909(id) VALUES (0) ON CONFLICT (id) DO UPDATE SET b = 1
----
upsert t102909
 ├── arbiter indexes: t102909_pkey
 ├── columns: <none>
 ├── canary column: id:11
 ├── fetch columns: id:11 a:12 b:13 c:14
 ├── insert-mapping:
 │    ├── column1:7 => id:1
 │    ├── a_default:8 => a:2
 │    ├── b_default:9 => b:3
 │    └── c_comp:10 => c:4
 ├── update-mapping:
 │    ├── upsert_b:20 => b:3
 │    └── upsert_c:21 => c:4
 └── project
      ├── columns: upsert_id:18 upsert_a:19 upsert_b:20 upsert_c:21 column1:7!null a_default:8!null b_default:9 c_comp:10 id:11 a:12 b:13 c:14 crdb_internal_mvcc_timestamp:15 tableoid:16 c_comp:17
      ├── project
      │    ├── columns: c_comp:17 column1:7!null a_default:8!null b_default:9 c_comp:10 id:11 a:12 b:13 c:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    ├── left-join (hash)
      │    │    ├── columns: column1:7!null a_default:8!null b_default:9 c_comp:10 id:11 a:12 b:13 c:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:7!null a_default:8!null b_default:9 c_comp:10
      │    │    │    ├── grouping columns: column1:7!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: c_comp:10 column1:7!null a_default:8!null b_default:9
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: a_default:8!null b_default:9 column1:7!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:7!null
      │    │    │    │    │    │    └── (0,)
      │    │    │    │    │    └── projections
      │    │    │    │    │         ├── 1 [as=a_default:8]
      │    │    │    │    │         └── NULL::INT8 [as=b_default:9]
      │    │    │    │    └── projections
      │    │    │    │         └── a_default:8 + b_default:9 [as=c_comp:10]
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=a_default:8]
      │    │    │         │    └── a_default:8
      │    │    │         ├── first-agg [as=b_default:9]
      │    │    │         │    └── b_default:9
      │    │    │         └── first-agg [as=c_comp:10]
      │    │    │              └── c_comp:10
      │    │    ├── scan t102909
      │    │    │    ├── columns: id:11!null a:12 b:13 c:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    ├── computed column expressions
      │    │    │    │    └── c:14
      │    │    │    │         └── a:12 + b:13
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:7 = id:11
      │    └── projections
      │         └── a:12 + a_default:8 [as=c_comp:17]
      └── projections
           ├── CASE WHEN id:11 IS NULL THEN column1:7 ELSE id:11 END [as=upsert_id:18]
           ├── CASE WHEN id:11 IS NULL THEN a_default:8 ELSE a:12 END [as=upsert_a:19]
           ├── CASE WHEN id:11 IS NULL THEN b_default:9 ELSE a_default:8 END [as=upsert_b:20]
           └── CASE WHEN id:11 IS NULL THEN c_comp:10 ELSE c_comp:17 END [as=upsert_c:21]


# ------------------------------------------------------------------------------
# Test DO NOTHING.
# ------------------------------------------------------------------------------

# No conflict columns specified (all non-duplicate indexes must be checked).
build
INSERT INTO xyz
VALUES (1, 2, 3), (4, 5, 6)
ON CONFLICT DO NOTHING
----
insert xyz
 ├── arbiter indexes: xyz_pkey xyz_y_z_key xyz_z_y_key
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── column3:8 => z:3
 └── upsert-distinct-on
      ├── columns: column1:6!null column2:7!null column3:8!null
      ├── grouping columns: column2:7!null column3:8!null
      ├── upsert-distinct-on
      │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    ├── grouping columns: column2:7!null column3:8!null
      │    ├── upsert-distinct-on
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── grouping columns: column1:6!null
      │    │    ├── anti-join (hash)
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    ├── anti-join (hash)
      │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    ├── anti-join (hash)
      │    │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    │    │    ├── (1, 2, 3)
      │    │    │    │    │    │    └── (4, 5, 6)
      │    │    │    │    │    ├── scan xyz
      │    │    │    │    │    │    ├── columns: x:9!null y:10 z:11
      │    │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    │    └── filters
      │    │    │    │    │         └── column1:6 = x:9
      │    │    │    │    ├── scan xyz
      │    │    │    │    │    ├── columns: x:14!null y:15 z:16
      │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    └── filters
      │    │    │    │         ├── column2:7 = y:15
      │    │    │    │         └── column3:8 = z:16
      │    │    │    ├── scan xyz
      │    │    │    │    ├── columns: x:19!null y:20 z:21
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         ├── column2:7 = y:20
      │    │    │         └── column3:8 = z:21
      │    │    └── aggregations
      │    │         ├── first-agg [as=column2:7]
      │    │         │    └── column2:7
      │    │         └── first-agg [as=column3:8]
      │    │              └── column3:8
      │    └── aggregations
      │         └── first-agg [as=column1:6]
      │              └── column1:6
      └── aggregations
           └── first-agg [as=column1:6]
                └── column1:6

# Conflict columns are explicitly specified.
build
INSERT INTO xyz
VALUES (1, 2, 3), (4, 5, 6)
ON CONFLICT (y, z) DO NOTHING
----
insert xyz
 ├── arbiter indexes: xyz_y_z_key
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── column3:8 => z:3
 └── upsert-distinct-on
      ├── columns: column1:6!null column2:7!null column3:8!null
      ├── grouping columns: column2:7!null column3:8!null
      ├── anti-join (hash)
      │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    ├── values
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── (1, 2, 3)
      │    │    └── (4, 5, 6)
      │    ├── scan xyz
      │    │    ├── columns: x:9!null y:10 z:11
      │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    └── filters
      │         ├── column2:7 = y:10
      │         └── column3:8 = z:11
      └── aggregations
           └── first-agg [as=column1:6]
                └── column1:6

build
INSERT INTO uniq VALUES ('x2', 'y1', 'z2'), ('x2', 'y2', 'z2'), ('x2', 'y2', 'z2')
ON CONFLICT DO NOTHING
----
insert uniq
 ├── arbiter indexes: uniq_pkey uniq_y_key uniq_z_key
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── column3:8 => z:3
 └── upsert-distinct-on
      ├── columns: column1:6!null column2:7!null column3:8!null
      ├── grouping columns: column3:8!null
      ├── upsert-distinct-on
      │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    ├── grouping columns: column2:7!null
      │    ├── upsert-distinct-on
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── grouping columns: column1:6!null
      │    │    ├── anti-join (hash)
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    ├── anti-join (hash)
      │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    ├── anti-join (hash)
      │    │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    │    │    ├── ('x2', 'y1', 'z2')
      │    │    │    │    │    │    ├── ('x2', 'y2', 'z2')
      │    │    │    │    │    │    └── ('x2', 'y2', 'z2')
      │    │    │    │    │    ├── scan uniq
      │    │    │    │    │    │    ├── columns: x:9!null y:10 z:11
      │    │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    │    └── filters
      │    │    │    │    │         └── column1:6 = x:9
      │    │    │    │    ├── scan uniq
      │    │    │    │    │    ├── columns: x:14!null y:15 z:16
      │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    └── filters
      │    │    │    │         └── column2:7 = y:15
      │    │    │    ├── scan uniq
      │    │    │    │    ├── columns: x:19!null y:20 z:21
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── column3:8 = z:21
      │    │    └── aggregations
      │    │         ├── first-agg [as=column2:7]
      │    │         │    └── column2:7
      │    │         └── first-agg [as=column3:8]
      │    │              └── column3:8
      │    └── aggregations
      │         ├── first-agg [as=column1:6]
      │         │    └── column1:6
      │         └── first-agg [as=column3:8]
      │              └── column3:8
      └── aggregations
           ├── first-agg [as=column1:6]
           │    └── column1:6
           └── first-agg [as=column2:7]
                └── column2:7

# DO NOTHING with index hints.

build
INSERT INTO xyz@xyz_pkey
VALUES (1, 2, 3), (4, 5, 6)
ON CONFLICT DO NOTHING
----
insert xyz
 ├── arbiter indexes: xyz_pkey xyz_y_z_key xyz_z_y_key
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── column3:8 => z:3
 └── upsert-distinct-on
      ├── columns: column1:6!null column2:7!null column3:8!null
      ├── grouping columns: column2:7!null column3:8!null
      ├── upsert-distinct-on
      │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    ├── grouping columns: column2:7!null column3:8!null
      │    ├── upsert-distinct-on
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── grouping columns: column1:6!null
      │    │    ├── anti-join (hash)
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    ├── anti-join (hash)
      │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    ├── anti-join (hash)
      │    │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    │    │    ├── (1, 2, 3)
      │    │    │    │    │    │    └── (4, 5, 6)
      │    │    │    │    │    ├── scan xyz
      │    │    │    │    │    │    ├── columns: x:9!null y:10 z:11
      │    │    │    │    │    │    └── flags: force-index=xyz_pkey avoid-full-scan disabled not visible index feature
      │    │    │    │    │    └── filters
      │    │    │    │    │         └── column1:6 = x:9
      │    │    │    │    ├── scan xyz
      │    │    │    │    │    ├── columns: x:14!null y:15 z:16
      │    │    │    │    │    └── flags: force-index=xyz_pkey avoid-full-scan disabled not visible index feature
      │    │    │    │    └── filters
      │    │    │    │         ├── column2:7 = y:15
      │    │    │    │         └── column3:8 = z:16
      │    │    │    ├── scan xyz
      │    │    │    │    ├── columns: x:19!null y:20 z:21
      │    │    │    │    └── flags: force-index=xyz_pkey avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         ├── column2:7 = y:20
      │    │    │         └── column3:8 = z:21
      │    │    └── aggregations
      │    │         ├── first-agg [as=column2:7]
      │    │         │    └── column2:7
      │    │         └── first-agg [as=column3:8]
      │    │              └── column3:8
      │    └── aggregations
      │         └── first-agg [as=column1:6]
      │              └── column1:6
      └── aggregations
           └── first-agg [as=column1:6]
                └── column1:6

build
INSERT INTO xyz@xyz_y_z_key
VALUES (1, 2, 3), (4, 5, 6)
ON CONFLICT (y, z) DO NOTHING
----
insert xyz
 ├── arbiter indexes: xyz_y_z_key
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── column3:8 => z:3
 └── upsert-distinct-on
      ├── columns: column1:6!null column2:7!null column3:8!null
      ├── grouping columns: column2:7!null column3:8!null
      ├── anti-join (hash)
      │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    ├── values
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── (1, 2, 3)
      │    │    └── (4, 5, 6)
      │    ├── scan xyz
      │    │    ├── columns: x:9!null y:10 z:11
      │    │    └── flags: force-index=xyz_y_z_key avoid-full-scan disabled not visible index feature
      │    └── filters
      │         ├── column2:7 = y:10
      │         └── column3:8 = z:11
      └── aggregations
           └── first-agg [as=column1:6]
                └── column1:6

build
INSERT INTO xyz@{FORCE_INDEX=xyz_y_idx,ASC}
VALUES (1, 2, 3), (4, 5, 6)
ON CONFLICT DO NOTHING
----
insert xyz
 ├── arbiter indexes: xyz_pkey xyz_y_z_key xyz_z_y_key
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── column3:8 => z:3
 └── upsert-distinct-on
      ├── columns: column1:6!null column2:7!null column3:8!null
      ├── grouping columns: column2:7!null column3:8!null
      ├── upsert-distinct-on
      │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    ├── grouping columns: column2:7!null column3:8!null
      │    ├── upsert-distinct-on
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── grouping columns: column1:6!null
      │    │    ├── anti-join (hash)
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    ├── anti-join (hash)
      │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    ├── anti-join (hash)
      │    │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    │    │    ├── (1, 2, 3)
      │    │    │    │    │    │    └── (4, 5, 6)
      │    │    │    │    │    ├── scan xyz
      │    │    │    │    │    │    ├── columns: x:9!null y:10 z:11
      │    │    │    │    │    │    └── flags: force-index=xyz_y_idx,fwd avoid-full-scan disabled not visible index feature
      │    │    │    │    │    └── filters
      │    │    │    │    │         └── column1:6 = x:9
      │    │    │    │    ├── scan xyz
      │    │    │    │    │    ├── columns: x:14!null y:15 z:16
      │    │    │    │    │    └── flags: force-index=xyz_y_idx,fwd avoid-full-scan disabled not visible index feature
      │    │    │    │    └── filters
      │    │    │    │         ├── column2:7 = y:15
      │    │    │    │         └── column3:8 = z:16
      │    │    │    ├── scan xyz
      │    │    │    │    ├── columns: x:19!null y:20 z:21
      │    │    │    │    └── flags: force-index=xyz_y_idx,fwd avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         ├── column2:7 = y:20
      │    │    │         └── column3:8 = z:21
      │    │    └── aggregations
      │    │         ├── first-agg [as=column2:7]
      │    │         │    └── column2:7
      │    │         └── first-agg [as=column3:8]
      │    │              └── column3:8
      │    └── aggregations
      │         └── first-agg [as=column1:6]
      │              └── column1:6
      └── aggregations
           └── first-agg [as=column1:6]
                └── column1:6

build
INSERT INTO xyz@{NO_FULL_SCAN}
VALUES (1, 2, 3), (4, 5, 6)
ON CONFLICT DO NOTHING
----
insert xyz
 ├── arbiter indexes: xyz_pkey xyz_y_z_key xyz_z_y_key
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── column3:8 => z:3
 └── upsert-distinct-on
      ├── columns: column1:6!null column2:7!null column3:8!null
      ├── grouping columns: column2:7!null column3:8!null
      ├── upsert-distinct-on
      │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    ├── grouping columns: column2:7!null column3:8!null
      │    ├── upsert-distinct-on
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── grouping columns: column1:6!null
      │    │    ├── anti-join (hash)
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    ├── anti-join (hash)
      │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    ├── anti-join (hash)
      │    │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    │    │    ├── (1, 2, 3)
      │    │    │    │    │    │    └── (4, 5, 6)
      │    │    │    │    │    ├── scan xyz
      │    │    │    │    │    │    ├── columns: x:9!null y:10 z:11
      │    │    │    │    │    │    └── flags: no-full-scan avoid-full-scan disabled not visible index feature
      │    │    │    │    │    └── filters
      │    │    │    │    │         └── column1:6 = x:9
      │    │    │    │    ├── scan xyz
      │    │    │    │    │    ├── columns: x:14!null y:15 z:16
      │    │    │    │    │    └── flags: no-full-scan avoid-full-scan disabled not visible index feature
      │    │    │    │    └── filters
      │    │    │    │         ├── column2:7 = y:15
      │    │    │    │         └── column3:8 = z:16
      │    │    │    ├── scan xyz
      │    │    │    │    ├── columns: x:19!null y:20 z:21
      │    │    │    │    └── flags: no-full-scan avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         ├── column2:7 = y:20
      │    │    │         └── column3:8 = z:21
      │    │    └── aggregations
      │    │         ├── first-agg [as=column2:7]
      │    │         │    └── column2:7
      │    │         └── first-agg [as=column3:8]
      │    │              └── column3:8
      │    └── aggregations
      │         └── first-agg [as=column1:6]
      │              └── column1:6
      └── aggregations
           └── first-agg [as=column1:6]
                └── column1:6

build
INSERT INTO xyz@bad_idx
VALUES (1, 2, 3), (4, 5, 6)
ON CONFLICT DO NOTHING
----
error (42704): index "bad_idx" not found

# ------------------------------------------------------------------------------
# Test excluded columns.
# ------------------------------------------------------------------------------

build
INSERT INTO xyz
VALUES (1, 2, 3), (-1, -1, -1)
ON CONFLICT (z, y) DO
UPDATE SET x=excluded.x+1, y=excluded.y*xyz.y, z=excluded.x-excluded.z
WHERE excluded.y>xyz.y
RETURNING xyz.x*2, y+z
----
project
 ├── columns: "?column?":20!null "?column?":21
 ├── upsert xyz
 │    ├── columns: x:1!null y:2 z:3!null
 │    ├── arbiter indexes: xyz_y_z_key
 │    ├── canary column: x:9
 │    ├── fetch columns: x:9 y:10 z:11
 │    ├── insert-mapping:
 │    │    ├── column1:6 => x:1
 │    │    ├── column2:7 => y:2
 │    │    └── column3:8 => z:3
 │    ├── update-mapping:
 │    │    ├── upsert_x:17 => x:1
 │    │    ├── upsert_y:18 => y:2
 │    │    └── upsert_z:19 => z:3
 │    ├── return-mapping:
 │    │    ├── upsert_x:17 => x:1
 │    │    ├── upsert_y:18 => y:2
 │    │    └── upsert_z:19 => z:3
 │    └── project
 │         ├── columns: upsert_x:17!null upsert_y:18 upsert_z:19!null column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13 x_new:14!null y_new:15 z_new:16!null
 │         ├── project
 │         │    ├── columns: x_new:14!null y_new:15 z_new:16!null column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
 │         │    ├── select
 │         │    │    ├── columns: column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
 │         │    │    ├── left-join (hash)
 │         │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
 │         │    │    │    ├── ensure-upsert-distinct-on
 │         │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
 │         │    │    │    │    ├── grouping columns: column2:7!null column3:8!null
 │         │    │    │    │    ├── values
 │         │    │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
 │         │    │    │    │    │    ├── (1, 2, 3)
 │         │    │    │    │    │    └── (-1, -1, -1)
 │         │    │    │    │    └── aggregations
 │         │    │    │    │         └── first-agg [as=column1:6]
 │         │    │    │    │              └── column1:6
 │         │    │    │    ├── scan xyz
 │         │    │    │    │    ├── columns: x:9!null y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
 │         │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
 │         │    │    │    └── filters
 │         │    │    │         ├── column2:7 = y:10
 │         │    │    │         └── column3:8 = z:11
 │         │    │    └── filters
 │         │    │         └── (x:9 IS NULL) OR (column2:7 > y:10)
 │         │    └── projections
 │         │         ├── column1:6 + 1 [as=x_new:14]
 │         │         ├── column2:7 * y:10 [as=y_new:15]
 │         │         └── column1:6 - column3:8 [as=z_new:16]
 │         └── projections
 │              ├── CASE WHEN x:9 IS NULL THEN column1:6 ELSE x_new:14 END [as=upsert_x:17]
 │              ├── CASE WHEN x:9 IS NULL THEN column2:7 ELSE y_new:15 END [as=upsert_y:18]
 │              └── CASE WHEN x:9 IS NULL THEN column3:8 ELSE z_new:16 END [as=upsert_z:19]
 └── projections
      ├── x:1 * 2 [as="?column?":20]
      └── y:2 + z:3 [as="?column?":21]

# Try to use excluded in RETURNING.
build
INSERT INTO xyz
VALUES (1, 2, 3)
ON CONFLICT (x) DO
UPDATE SET x=1
RETURNING excluded.x
----
error (42P01): no data source matches prefix: excluded in this context

# Referencing column without "excluded" or "xyz" prefix is not allowed.
build
INSERT INTO xyz
VALUES (1, 2, 3)
ON CONFLICT (x) DO
UPDATE SET x=x+1
----
error (42702): column reference "x" is ambiguous (candidates: excluded.x, xyz.x)

# ------------------------------------------------------------------------------
# Test UPDATE SET expressions.
# ------------------------------------------------------------------------------

# Subquery.
build
INSERT INTO abc
VALUES (1, 2)
ON CONFLICT (a) DO
UPDATE SET (b, a)=(SELECT x, y+excluded.b FROM xyz WHERE x=excluded.a)
----
upsert abc
 ├── arbiter indexes: abc_a_key
 ├── columns: <none>
 ├── canary column: a:11
 ├── fetch columns: a:11 b:12 c:13 rowid:14
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── c_comp:10 => c:3
 │    └── rowid_default:9 => rowid:4
 ├── update-mapping:
 │    ├── upsert_a:24 => a:1
 │    ├── upsert_b:25 => b:2
 │    └── upsert_c:26 => c:3
 └── project
      ├── columns: upsert_a:24 upsert_b:25 upsert_c:26 upsert_rowid:27 column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 abc.crdb_internal_mvcc_timestamp:15 abc.tableoid:16 x:17 "?column?":22 c_comp:23
      ├── project
      │    ├── columns: c_comp:23 column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 abc.crdb_internal_mvcc_timestamp:15 abc.tableoid:16 x:17 "?column?":22
      │    ├── left-join-apply
      │    │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 abc.crdb_internal_mvcc_timestamp:15 abc.tableoid:16 x:17 "?column?":22
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 abc.crdb_internal_mvcc_timestamp:15 abc.tableoid:16
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null
      │    │    │    │    ├── grouping columns: column1:7!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: c_comp:10!null column1:7!null column2:8!null rowid_default:9
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: rowid_default:9 column1:7!null column2:8!null
      │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    ├── columns: column1:7!null column2:8!null
      │    │    │    │    │    │    │    └── (1, 2)
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── unique_rowid() [as=rowid_default:9]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── column2:8 + 1 [as=c_comp:10]
      │    │    │    │    └── aggregations
      │    │    │    │         ├── first-agg [as=column2:8]
      │    │    │    │         │    └── column2:8
      │    │    │    │         ├── first-agg [as=rowid_default:9]
      │    │    │    │         │    └── rowid_default:9
      │    │    │    │         └── first-agg [as=c_comp:10]
      │    │    │    │              └── c_comp:10
      │    │    │    ├── scan abc
      │    │    │    │    ├── columns: a:11!null b:12 c:13 rowid:14!null abc.crdb_internal_mvcc_timestamp:15 abc.tableoid:16
      │    │    │    │    ├── computed column expressions
      │    │    │    │    │    └── c:13
      │    │    │    │    │         └── b:12 + 1
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── column1:7 = a:11
      │    │    ├── max1-row
      │    │    │    ├── columns: x:17!null "?column?":22
      │    │    │    └── project
      │    │    │         ├── columns: "?column?":22 x:17!null
      │    │    │         ├── select
      │    │    │         │    ├── columns: x:17!null y:18 z:19 xyz.crdb_internal_mvcc_timestamp:20 xyz.tableoid:21
      │    │    │         │    ├── scan xyz
      │    │    │         │    │    └── columns: x:17!null y:18 z:19 xyz.crdb_internal_mvcc_timestamp:20 xyz.tableoid:21
      │    │    │         │    └── filters
      │    │    │         │         └── x:17 = column1:7
      │    │    │         └── projections
      │    │    │              └── y:18 + column2:8 [as="?column?":22]
      │    │    └── filters (true)
      │    └── projections
      │         └── x:17 + 1 [as=c_comp:23]
      └── projections
           ├── CASE WHEN a:11 IS NULL THEN column1:7 ELSE "?column?":22 END [as=upsert_a:24]
           ├── CASE WHEN a:11 IS NULL THEN column2:8 ELSE x:17 END [as=upsert_b:25]
           ├── CASE WHEN a:11 IS NULL THEN c_comp:10 ELSE c_comp:23 END [as=upsert_c:26]
           └── CASE WHEN a:11 IS NULL THEN rowid_default:9 ELSE rowid:14 END [as=upsert_rowid:27]

# Default expressions.
build
INSERT INTO abc
VALUES (1, 2)
ON CONFLICT (a) DO
UPDATE SET a=DEFAULT, b=DEFAULT
----
upsert abc
 ├── arbiter indexes: abc_a_key
 ├── columns: <none>
 ├── canary column: a:11
 ├── fetch columns: a:11 b:12 c:13 rowid:14
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── c_comp:10 => c:3
 │    └── rowid_default:9 => rowid:4
 ├── update-mapping:
 │    ├── upsert_a:20 => a:1
 │    ├── upsert_b:21 => b:2
 │    └── upsert_c:22 => c:3
 └── project
      ├── columns: upsert_a:20 upsert_b:21!null upsert_c:22!null upsert_rowid:23 column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16 a_new:17 b_new:18!null c_comp:19!null
      ├── project
      │    ├── columns: c_comp:19!null column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16 a_new:17 b_new:18!null
      │    ├── project
      │    │    ├── columns: a_new:17 b_new:18!null column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null
      │    │    │    │    ├── grouping columns: column1:7!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: c_comp:10!null column1:7!null column2:8!null rowid_default:9
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: rowid_default:9 column1:7!null column2:8!null
      │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    ├── columns: column1:7!null column2:8!null
      │    │    │    │    │    │    │    └── (1, 2)
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── unique_rowid() [as=rowid_default:9]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── column2:8 + 1 [as=c_comp:10]
      │    │    │    │    └── aggregations
      │    │    │    │         ├── first-agg [as=column2:8]
      │    │    │    │         │    └── column2:8
      │    │    │    │         ├── first-agg [as=rowid_default:9]
      │    │    │    │         │    └── rowid_default:9
      │    │    │    │         └── first-agg [as=c_comp:10]
      │    │    │    │              └── c_comp:10
      │    │    │    ├── scan abc
      │    │    │    │    ├── columns: a:11!null b:12 c:13 rowid:14!null crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    │    ├── computed column expressions
      │    │    │    │    │    └── c:13
      │    │    │    │    │         └── b:12 + 1
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── column1:7 = a:11
      │    │    └── projections
      │    │         ├── NULL::INT8 [as=a_new:17]
      │    │         └── 10 [as=b_new:18]
      │    └── projections
      │         └── b_new:18 + 1 [as=c_comp:19]
      └── projections
           ├── CASE WHEN a:11 IS NULL THEN column1:7 ELSE a_new:17 END [as=upsert_a:20]
           ├── CASE WHEN a:11 IS NULL THEN column2:8 ELSE b_new:18 END [as=upsert_b:21]
           ├── CASE WHEN a:11 IS NULL THEN c_comp:10 ELSE c_comp:19 END [as=upsert_c:22]
           └── CASE WHEN a:11 IS NULL THEN rowid_default:9 ELSE rowid:14 END [as=upsert_rowid:23]

# UPDATE SET with index hints.

build
INSERT INTO xyz@xyz_pkey
VALUES (1, 2, 3), (4, 5, 6)
ON CONFLICT (x) DO UPDATE SET x=DEFAULT
----
upsert xyz
 ├── arbiter indexes: xyz_pkey
 ├── columns: <none>
 ├── canary column: x:9
 ├── fetch columns: x:9 y:10 z:11
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── column3:8 => z:3
 ├── update-mapping:
 │    └── upsert_x:15 => x:1
 └── project
      ├── columns: upsert_x:15 upsert_y:16 upsert_z:17 column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13 x_new:14
      ├── project
      │    ├── columns: x_new:14 column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── left-join (hash)
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    ├── grouping columns: column1:6!null
      │    │    │    ├── values
      │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    ├── (1, 2, 3)
      │    │    │    │    └── (4, 5, 6)
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=column2:7]
      │    │    │         │    └── column2:7
      │    │    │         └── first-agg [as=column3:8]
      │    │    │              └── column3:8
      │    │    ├── scan xyz
      │    │    │    ├── columns: x:9!null y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    │    └── flags: force-index=xyz_pkey avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:6 = x:9
      │    └── projections
      │         └── NULL::INT8 [as=x_new:14]
      └── projections
           ├── CASE WHEN x:9 IS NULL THEN column1:6 ELSE x_new:14 END [as=upsert_x:15]
           ├── CASE WHEN x:9 IS NULL THEN column2:7 ELSE y:10 END [as=upsert_y:16]
           └── CASE WHEN x:9 IS NULL THEN column3:8 ELSE z:11 END [as=upsert_z:17]

build
INSERT INTO xyz@xyz_y_idx
VALUES (1, 2, 3), (4, 5, 6)
ON CONFLICT (y, z) DO UPDATE SET x=xyz.x+1
----
upsert xyz
 ├── arbiter indexes: xyz_y_z_key
 ├── columns: <none>
 ├── canary column: x:9
 ├── fetch columns: x:9 y:10 z:11
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── column3:8 => z:3
 ├── update-mapping:
 │    └── upsert_x:15 => x:1
 └── project
      ├── columns: upsert_x:15 upsert_y:16 upsert_z:17 column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13 x_new:14
      ├── project
      │    ├── columns: x_new:14 column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── left-join (hash)
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    ├── grouping columns: column2:7!null column3:8!null
      │    │    │    ├── values
      │    │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    │    ├── (1, 2, 3)
      │    │    │    │    └── (4, 5, 6)
      │    │    │    └── aggregations
      │    │    │         └── first-agg [as=column1:6]
      │    │    │              └── column1:6
      │    │    ├── scan xyz
      │    │    │    ├── columns: x:9!null y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    │    └── flags: force-index=xyz_y_idx avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         ├── column2:7 = y:10
      │    │         └── column3:8 = z:11
      │    └── projections
      │         └── x:9 + 1 [as=x_new:14]
      └── projections
           ├── CASE WHEN x:9 IS NULL THEN column1:6 ELSE x_new:14 END [as=upsert_x:15]
           ├── CASE WHEN x:9 IS NULL THEN column2:7 ELSE y:10 END [as=upsert_y:16]
           └── CASE WHEN x:9 IS NULL THEN column3:8 ELSE z:11 END [as=upsert_z:17]

build
INSERT INTO xyz@{FORCE_INDEX=xyz_z_y_key,ASC}
VALUES (1, 2, 3), (4, 5, 6)
ON CONFLICT (x) DO UPDATE SET x=excluded.y
----
upsert xyz
 ├── arbiter indexes: xyz_pkey
 ├── columns: <none>
 ├── canary column: x:9
 ├── fetch columns: x:9 y:10 z:11
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── column3:8 => z:3
 ├── update-mapping:
 │    └── upsert_x:14 => x:1
 └── project
      ├── columns: upsert_x:14!null upsert_y:15 upsert_z:16 column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      ├── left-join (hash)
      │    ├── columns: column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── ensure-upsert-distinct-on
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── grouping columns: column1:6!null
      │    │    ├── values
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    ├── (1, 2, 3)
      │    │    │    └── (4, 5, 6)
      │    │    └── aggregations
      │    │         ├── first-agg [as=column2:7]
      │    │         │    └── column2:7
      │    │         └── first-agg [as=column3:8]
      │    │              └── column3:8
      │    ├── scan xyz
      │    │    ├── columns: x:9!null y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    └── flags: force-index=xyz_z_y_key,fwd avoid-full-scan disabled not visible index feature
      │    └── filters
      │         └── column1:6 = x:9
      └── projections
           ├── CASE WHEN x:9 IS NULL THEN column1:6 ELSE column2:7 END [as=upsert_x:14]
           ├── CASE WHEN x:9 IS NULL THEN column2:7 ELSE y:10 END [as=upsert_y:15]
           └── CASE WHEN x:9 IS NULL THEN column3:8 ELSE z:11 END [as=upsert_z:16]

build
INSERT INTO xyz@{NO_FULL_SCAN}
VALUES (1, 2, 3), (4, 5, 6)
ON CONFLICT (z, y) DO UPDATE SET y=excluded.x
----
upsert xyz
 ├── arbiter indexes: xyz_y_z_key
 ├── columns: <none>
 ├── canary column: x:9
 ├── fetch columns: x:9 y:10 z:11
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── column3:8 => z:3
 ├── update-mapping:
 │    └── upsert_y:15 => y:2
 └── project
      ├── columns: upsert_x:14 upsert_y:15!null upsert_z:16 column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      ├── left-join (hash)
      │    ├── columns: column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── ensure-upsert-distinct-on
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── grouping columns: column2:7!null column3:8!null
      │    │    ├── values
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    ├── (1, 2, 3)
      │    │    │    └── (4, 5, 6)
      │    │    └── aggregations
      │    │         └── first-agg [as=column1:6]
      │    │              └── column1:6
      │    ├── scan xyz
      │    │    ├── columns: x:9!null y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    └── flags: no-full-scan avoid-full-scan disabled not visible index feature
      │    └── filters
      │         ├── column2:7 = y:10
      │         └── column3:8 = z:11
      └── projections
           ├── CASE WHEN x:9 IS NULL THEN column1:6 ELSE x:9 END [as=upsert_x:14]
           ├── CASE WHEN x:9 IS NULL THEN column2:7 ELSE column1:6 END [as=upsert_y:15]
           └── CASE WHEN x:9 IS NULL THEN column3:8 ELSE z:11 END [as=upsert_z:16]

# ------------------------------------------------------------------------------
# Test mutation columns.
# ------------------------------------------------------------------------------

build
INSERT INTO mutation (m, n)
VALUES (1, 2)
ON CONFLICT (m) DO
UPDATE SET m=mutation.m+1
----
upsert mutation
 ├── arbiter indexes: mutation_pkey
 ├── columns: <none>
 ├── canary column: m:12
 ├── fetch columns: m:12 n:13 o:14 p:15 q:16
 ├── insert-mapping:
 │    ├── column1:8 => m:1
 │    ├── column2:9 => n:2
 │    ├── o_default:10 => o:3
 │    └── p_comp:11 => p:4
 ├── update-mapping:
 │    ├── upsert_m:21 => m:1
 │    ├── o_default:10 => o:3
 │    └── upsert_p:23 => p:4
 ├── check columns: check1:24
 └── project
      ├── columns: check1:24 column1:8!null column2:9!null o_default:10!null p_comp:11!null m:12 n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18 m_new:19 p_comp:20 upsert_m:21 upsert_n:22 upsert_p:23
      ├── project
      │    ├── columns: upsert_m:21 upsert_n:22 upsert_p:23 column1:8!null column2:9!null o_default:10!null p_comp:11!null m:12 n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18 m_new:19 p_comp:20
      │    ├── project
      │    │    ├── columns: p_comp:20 column1:8!null column2:9!null o_default:10!null p_comp:11!null m:12 n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18 m_new:19
      │    │    ├── project
      │    │    │    ├── columns: m_new:19 column1:8!null column2:9!null o_default:10!null p_comp:11!null m:12 n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18
      │    │    │    ├── left-join (hash)
      │    │    │    │    ├── columns: column1:8!null column2:9!null o_default:10!null p_comp:11!null m:12 n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18
      │    │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    │    ├── columns: column1:8!null column2:9!null o_default:10!null p_comp:11!null
      │    │    │    │    │    ├── grouping columns: column1:8!null
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: p_comp:11!null column1:8!null column2:9!null o_default:10!null
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: o_default:10!null column1:8!null column2:9!null
      │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    ├── columns: column1:8!null column2:9!null
      │    │    │    │    │    │    │    │    └── (1, 2)
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── 10 [as=o_default:10]
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── o_default:10 + column2:9 [as=p_comp:11]
      │    │    │    │    │    └── aggregations
      │    │    │    │    │         ├── first-agg [as=column2:9]
      │    │    │    │    │         │    └── column2:9
      │    │    │    │    │         ├── first-agg [as=o_default:10]
      │    │    │    │    │         │    └── o_default:10
      │    │    │    │    │         └── first-agg [as=p_comp:11]
      │    │    │    │    │              └── p_comp:11
      │    │    │    │    ├── scan mutation
      │    │    │    │    │    ├── columns: m:12!null n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18
      │    │    │    │    │    ├── check constraint expressions
      │    │    │    │    │    │    └── m:12 > 0
      │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    └── filters
      │    │    │    │         └── column1:8 = m:12
      │    │    │    └── projections
      │    │    │         └── m:12 + 1 [as=m_new:19]
      │    │    └── projections
      │    │         └── o_default:10 + n:13 [as=p_comp:20]
      │    └── projections
      │         ├── CASE WHEN m:12 IS NULL THEN column1:8 ELSE m_new:19 END [as=upsert_m:21]
      │         ├── CASE WHEN m:12 IS NULL THEN column2:9 ELSE n:13 END [as=upsert_n:22]
      │         └── CASE WHEN m:12 IS NULL THEN p_comp:11 ELSE p_comp:20 END [as=upsert_p:23]
      └── projections
           └── upsert_m:21 > 0 [as=check1:24]

# ------------------------------------------------------------------------------
# Test UPSERT.
# ------------------------------------------------------------------------------

# Single column primary key.
build
UPSERT INTO xyz VALUES (1)
----
upsert xyz
 ├── arbiter indexes: xyz_pkey
 ├── columns: <none>
 ├── canary column: x:8
 ├── fetch columns: x:8 y:9 z:10
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── y_default:7 => y:2
 │    └── y_default:7 => z:3
 ├── update-mapping:
 │    ├── y_default:7 => y:2
 │    └── y_default:7 => z:3
 └── project
      ├── columns: upsert_x:13 column1:6!null y_default:7 x:8 y:9 z:10 crdb_internal_mvcc_timestamp:11 tableoid:12
      ├── left-join (hash)
      │    ├── columns: column1:6!null y_default:7 x:8 y:9 z:10 crdb_internal_mvcc_timestamp:11 tableoid:12
      │    ├── ensure-upsert-distinct-on
      │    │    ├── columns: column1:6!null y_default:7
      │    │    ├── grouping columns: column1:6!null
      │    │    ├── project
      │    │    │    ├── columns: y_default:7 column1:6!null
      │    │    │    ├── values
      │    │    │    │    ├── columns: column1:6!null
      │    │    │    │    └── (1,)
      │    │    │    └── projections
      │    │    │         └── NULL::INT8 [as=y_default:7]
      │    │    └── aggregations
      │    │         └── first-agg [as=y_default:7]
      │    │              └── y_default:7
      │    ├── scan xyz
      │    │    ├── columns: x:8!null y:9 z:10 crdb_internal_mvcc_timestamp:11 tableoid:12
      │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    └── filters
      │         └── column1:6 = x:8
      └── projections
           └── CASE WHEN x:8 IS NULL THEN column1:6 ELSE x:8 END [as=upsert_x:13]

# Test multi-column primary key that contains all columns in table.
build
UPSERT INTO uv VALUES (1, 2) RETURNING *
----
upsert uv
 ├── columns: u:1!null v:2!null
 ├── upsert-mapping:
 │    ├── column1:5 => u:1
 │    └── column2:6 => v:2
 └── values
      ├── columns: column1:5!null column2:6!null
      └── (1, 2)

# Use returning UPSERT as a FROM expression.
build
SELECT * FROM [UPSERT INTO abc VALUES (1, 2) RETURNING *]
----
with &1
 ├── columns: a:18!null b:19!null c:20!null
 ├── project
 │    ├── columns: abc.a:1!null abc.b:2!null abc.c:3!null
 │    └── upsert abc
 │         ├── columns: abc.a:1!null abc.b:2!null abc.c:3!null rowid:4!null
 │         ├── arbiter indexes: abc_pkey
 │         ├── canary column: rowid:14
 │         ├── fetch columns: abc.a:11 abc.b:12 abc.c:13 rowid:14
 │         ├── insert-mapping:
 │         │    ├── column1:7 => abc.a:1
 │         │    ├── column2:8 => abc.b:2
 │         │    ├── c_comp:10 => abc.c:3
 │         │    └── rowid_default:9 => rowid:4
 │         ├── update-mapping:
 │         │    ├── column1:7 => abc.a:1
 │         │    ├── column2:8 => abc.b:2
 │         │    └── c_comp:10 => abc.c:3
 │         ├── return-mapping:
 │         │    ├── column1:7 => abc.a:1
 │         │    ├── column2:8 => abc.b:2
 │         │    ├── c_comp:10 => abc.c:3
 │         │    └── upsert_rowid:17 => rowid:4
 │         └── project
 │              ├── columns: upsert_rowid:17 column1:7!null column2:8!null rowid_default:9 c_comp:10!null abc.a:11 abc.b:12 abc.c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
 │              ├── left-join (hash)
 │              │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null abc.a:11 abc.b:12 abc.c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
 │              │    ├── ensure-upsert-distinct-on
 │              │    │    ├── columns: column1:7!null column2:8!null rowid_default:9 c_comp:10!null
 │              │    │    ├── grouping columns: rowid_default:9
 │              │    │    ├── project
 │              │    │    │    ├── columns: c_comp:10!null column1:7!null column2:8!null rowid_default:9
 │              │    │    │    ├── project
 │              │    │    │    │    ├── columns: rowid_default:9 column1:7!null column2:8!null
 │              │    │    │    │    ├── values
 │              │    │    │    │    │    ├── columns: column1:7!null column2:8!null
 │              │    │    │    │    │    └── (1, 2)
 │              │    │    │    │    └── projections
 │              │    │    │    │         └── unique_rowid() [as=rowid_default:9]
 │              │    │    │    └── projections
 │              │    │    │         └── column2:8 + 1 [as=c_comp:10]
 │              │    │    └── aggregations
 │              │    │         ├── first-agg [as=column1:7]
 │              │    │         │    └── column1:7
 │              │    │         ├── first-agg [as=column2:8]
 │              │    │         │    └── column2:8
 │              │    │         └── first-agg [as=c_comp:10]
 │              │    │              └── c_comp:10
 │              │    ├── scan abc
 │              │    │    ├── columns: abc.a:11!null abc.b:12 abc.c:13 rowid:14!null crdb_internal_mvcc_timestamp:15 tableoid:16
 │              │    │    ├── computed column expressions
 │              │    │    │    └── abc.c:13
 │              │    │    │         └── abc.b:12 + 1
 │              │    │    └── flags: avoid-full-scan disabled not visible index feature
 │              │    └── filters
 │              │         └── rowid_default:9 = rowid:14
 │              └── projections
 │                   └── CASE WHEN rowid:14 IS NULL THEN rowid_default:9 ELSE rowid:14 END [as=upsert_rowid:17]
 └── with-scan &1
      ├── columns: a:18!null b:19!null c:20!null
      └── mapping:
           ├──  abc.a:1 => a:18
           ├──  abc.b:2 => b:19
           └──  abc.c:3 => c:20

# Use explicitly specified column names with secondary indexes present. Existing
# values of other columns need to be fetched to delete existing index rows.
build
UPSERT INTO xyz (z, x, y) VALUES (1, 2, 3)
----
upsert xyz
 ├── arbiter indexes: xyz_pkey
 ├── columns: <none>
 ├── canary column: x:9
 ├── fetch columns: x:9 y:10 z:11
 ├── insert-mapping:
 │    ├── column2:7 => x:1
 │    ├── column3:8 => y:2
 │    └── column1:6 => z:3
 ├── update-mapping:
 │    ├── column3:8 => y:2
 │    └── column1:6 => z:3
 └── project
      ├── columns: upsert_x:14 column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      ├── left-join (hash)
      │    ├── columns: column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── ensure-upsert-distinct-on
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── grouping columns: column2:7!null
      │    │    ├── values
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    └── (1, 2, 3)
      │    │    └── aggregations
      │    │         ├── first-agg [as=column1:6]
      │    │         │    └── column1:6
      │    │         └── first-agg [as=column3:8]
      │    │              └── column3:8
      │    ├── scan xyz
      │    │    ├── columns: x:9!null y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    └── filters
      │         └── column2:7 = x:9
      └── projections
           └── CASE WHEN x:9 IS NULL THEN column2:7 ELSE x:9 END [as=upsert_x:14]

# Use explicitly specified column names with no secondary indexes present.
# Upsert implemented with blind Puts is possible.
build
UPSERT INTO noindex (x, y, z) VALUES (1, 2, 3)
----
upsert noindex
 ├── columns: <none>
 ├── upsert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── column3:8 => z:3
 └── values
      ├── columns: column1:6!null column2:7!null column3:8!null
      └── (1, 2, 3)

# Use subset of explicitly specified column names with no secondary indexes
# present. Existing values of other columns need to be fetched to provide
# update values for unspecified columns.
build
UPSERT INTO checks (a, b, c) VALUES (1, 2, 3)
----
upsert checks
 ├── arbiter indexes: checks_pkey
 ├── columns: <none>
 ├── canary column: a:11
 ├── fetch columns: a:11 b:12 c:13 d:14
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── column3:9 => c:3
 │    └── d_comp:10 => d:4
 ├── update-mapping:
 │    ├── column2:8 => b:2
 │    ├── column3:9 => c:3
 │    └── d_comp:10 => d:4
 ├── check columns: check1:18 check2:19
 └── project
      ├── columns: check1:18!null check2:19 column1:7!null column2:8!null column3:9!null d_comp:10!null a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16 upsert_a:17
      ├── project
      │    ├── columns: upsert_a:17 column1:7!null column2:8!null column3:9!null d_comp:10!null a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    ├── left-join (hash)
      │    │    ├── columns: column1:7!null column2:8!null column3:9!null d_comp:10!null a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:7!null column2:8!null column3:9!null d_comp:10!null
      │    │    │    ├── grouping columns: column1:7!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: d_comp:10!null column1:7!null column2:8!null column3:9!null
      │    │    │    │    ├── values
      │    │    │    │    │    ├── columns: column1:7!null column2:8!null column3:9!null
      │    │    │    │    │    └── (1, 2, 3)
      │    │    │    │    └── projections
      │    │    │    │         └── column3:9 + 1 [as=d_comp:10]
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=column2:8]
      │    │    │         │    └── column2:8
      │    │    │         ├── first-agg [as=column3:9]
      │    │    │         │    └── column3:9
      │    │    │         └── first-agg [as=d_comp:10]
      │    │    │              └── d_comp:10
      │    │    ├── scan checks
      │    │    │    ├── columns: a:11!null b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    ├── check constraint expressions
      │    │    │    │    └── a:11 > 0
      │    │    │    ├── computed column expressions
      │    │    │    │    └── d:14
      │    │    │    │         └── c:13 + 1
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:7 = a:11
      │    └── projections
      │         └── CASE WHEN a:11 IS NULL THEN column1:7 ELSE a:11 END [as=upsert_a:17]
      └── projections
           ├── column2:8 < d_comp:10 [as=check1:18]
           └── upsert_a:17 > 0 [as=check2:19]

# Ensure that mutation columns are set by the insert and update. Use explicit
# target columns.
build
UPSERT INTO mutation (m, n) VALUES (1, 2)
----
upsert mutation
 ├── arbiter indexes: mutation_pkey
 ├── columns: <none>
 ├── canary column: m:12
 ├── fetch columns: m:12 n:13 o:14 p:15 q:16
 ├── insert-mapping:
 │    ├── column1:8 => m:1
 │    ├── column2:9 => n:2
 │    ├── o_default:10 => o:3
 │    └── p_comp:11 => p:4
 ├── update-mapping:
 │    ├── column2:9 => n:2
 │    ├── o_default:10 => o:3
 │    └── p_comp:11 => p:4
 ├── check columns: check1:20
 └── project
      ├── columns: check1:20 column1:8!null column2:9!null o_default:10!null p_comp:11!null m:12 n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18 upsert_m:19
      ├── project
      │    ├── columns: upsert_m:19 column1:8!null column2:9!null o_default:10!null p_comp:11!null m:12 n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18
      │    ├── left-join (hash)
      │    │    ├── columns: column1:8!null column2:9!null o_default:10!null p_comp:11!null m:12 n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:8!null column2:9!null o_default:10!null p_comp:11!null
      │    │    │    ├── grouping columns: column1:8!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: p_comp:11!null column1:8!null column2:9!null o_default:10!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: o_default:10!null column1:8!null column2:9!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:8!null column2:9!null
      │    │    │    │    │    │    └── (1, 2)
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── 10 [as=o_default:10]
      │    │    │    │    └── projections
      │    │    │    │         └── o_default:10 + column2:9 [as=p_comp:11]
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=column2:9]
      │    │    │         │    └── column2:9
      │    │    │         ├── first-agg [as=o_default:10]
      │    │    │         │    └── o_default:10
      │    │    │         └── first-agg [as=p_comp:11]
      │    │    │              └── p_comp:11
      │    │    ├── scan mutation
      │    │    │    ├── columns: m:12!null n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18
      │    │    │    ├── check constraint expressions
      │    │    │    │    └── m:12 > 0
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:8 = m:12
      │    └── projections
      │         └── CASE WHEN m:12 IS NULL THEN column1:8 ELSE m:12 END [as=upsert_m:19]
      └── projections
           └── upsert_m:19 > 0 [as=check1:20]

# Don't directly update mutation columns. However, computed columns do need to
# be updated. Use implicit target columns.
build
UPSERT INTO mutation VALUES (1, 2)
----
upsert mutation
 ├── arbiter indexes: mutation_pkey
 ├── columns: <none>
 ├── canary column: m:12
 ├── fetch columns: m:12 n:13 o:14 p:15 q:16
 ├── insert-mapping:
 │    ├── column1:8 => m:1
 │    ├── column2:9 => n:2
 │    ├── o_default:10 => o:3
 │    └── p_comp:11 => p:4
 ├── update-mapping:
 │    ├── column2:9 => n:2
 │    ├── o_default:10 => o:3
 │    └── p_comp:11 => p:4
 ├── check columns: check1:20
 └── project
      ├── columns: check1:20 column1:8!null column2:9!null o_default:10!null p_comp:11!null m:12 n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18 upsert_m:19
      ├── project
      │    ├── columns: upsert_m:19 column1:8!null column2:9!null o_default:10!null p_comp:11!null m:12 n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18
      │    ├── left-join (hash)
      │    │    ├── columns: column1:8!null column2:9!null o_default:10!null p_comp:11!null m:12 n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:8!null column2:9!null o_default:10!null p_comp:11!null
      │    │    │    ├── grouping columns: column1:8!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: p_comp:11!null column1:8!null column2:9!null o_default:10!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: o_default:10!null column1:8!null column2:9!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:8!null column2:9!null
      │    │    │    │    │    │    └── (1, 2)
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── 10 [as=o_default:10]
      │    │    │    │    └── projections
      │    │    │    │         └── o_default:10 + column2:9 [as=p_comp:11]
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=column2:9]
      │    │    │         │    └── column2:9
      │    │    │         ├── first-agg [as=o_default:10]
      │    │    │         │    └── o_default:10
      │    │    │         └── first-agg [as=p_comp:11]
      │    │    │              └── p_comp:11
      │    │    ├── scan mutation
      │    │    │    ├── columns: m:12!null n:13 o:14 p:15 q:16 crdb_internal_mvcc_timestamp:17 tableoid:18
      │    │    │    ├── check constraint expressions
      │    │    │    │    └── m:12 > 0
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:8 = m:12
      │    └── projections
      │         └── CASE WHEN m:12 IS NULL THEN column1:8 ELSE m:12 END [as=upsert_m:19]
      └── projections
           └── upsert_m:19 > 0 [as=check1:20]

# Use unknown name in upsert column list.
build
UPSERT INTO xyz (x, unknown) VALUES (1)
----
error (42703): column "unknown" does not exist

# UPSERT with index hints.

build
UPSERT INTO xyz@xyz_pkey (z, x, y) VALUES (1, 2, 3)
----
upsert xyz
 ├── arbiter indexes: xyz_pkey
 ├── columns: <none>
 ├── canary column: x:9
 ├── fetch columns: x:9 y:10 z:11
 ├── insert-mapping:
 │    ├── column2:7 => x:1
 │    ├── column3:8 => y:2
 │    └── column1:6 => z:3
 ├── update-mapping:
 │    ├── column3:8 => y:2
 │    └── column1:6 => z:3
 └── project
      ├── columns: upsert_x:14 column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      ├── left-join (hash)
      │    ├── columns: column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── ensure-upsert-distinct-on
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── grouping columns: column2:7!null
      │    │    ├── values
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    └── (1, 2, 3)
      │    │    └── aggregations
      │    │         ├── first-agg [as=column1:6]
      │    │         │    └── column1:6
      │    │         └── first-agg [as=column3:8]
      │    │              └── column3:8
      │    ├── scan xyz
      │    │    ├── columns: x:9!null y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    └── flags: force-index=xyz_pkey avoid-full-scan disabled not visible index feature
      │    └── filters
      │         └── column2:7 = x:9
      └── projections
           └── CASE WHEN x:9 IS NULL THEN column2:7 ELSE x:9 END [as=upsert_x:14]

build
UPSERT INTO xyz@xyz_y_z_key (z, x, y) VALUES (1, 2, 3)
----
upsert xyz
 ├── arbiter indexes: xyz_pkey
 ├── columns: <none>
 ├── canary column: x:9
 ├── fetch columns: x:9 y:10 z:11
 ├── insert-mapping:
 │    ├── column2:7 => x:1
 │    ├── column3:8 => y:2
 │    └── column1:6 => z:3
 ├── update-mapping:
 │    ├── column3:8 => y:2
 │    └── column1:6 => z:3
 └── project
      ├── columns: upsert_x:14 column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      ├── left-join (hash)
      │    ├── columns: column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── ensure-upsert-distinct-on
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── grouping columns: column2:7!null
      │    │    ├── values
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    └── (1, 2, 3)
      │    │    └── aggregations
      │    │         ├── first-agg [as=column1:6]
      │    │         │    └── column1:6
      │    │         └── first-agg [as=column3:8]
      │    │              └── column3:8
      │    ├── scan xyz
      │    │    ├── columns: x:9!null y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    └── flags: force-index=xyz_y_z_key avoid-full-scan disabled not visible index feature
      │    └── filters
      │         └── column2:7 = x:9
      └── projections
           └── CASE WHEN x:9 IS NULL THEN column2:7 ELSE x:9 END [as=upsert_x:14]

build
UPSERT INTO xyz@{NO_FULL_SCAN} (z, x, y) VALUES (1, 2, 3)
----
upsert xyz
 ├── arbiter indexes: xyz_pkey
 ├── columns: <none>
 ├── canary column: x:9
 ├── fetch columns: x:9 y:10 z:11
 ├── insert-mapping:
 │    ├── column2:7 => x:1
 │    ├── column3:8 => y:2
 │    └── column1:6 => z:3
 ├── update-mapping:
 │    ├── column3:8 => y:2
 │    └── column1:6 => z:3
 └── project
      ├── columns: upsert_x:14 column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      ├── left-join (hash)
      │    ├── columns: column1:6!null column2:7!null column3:8!null x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── ensure-upsert-distinct-on
      │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    ├── grouping columns: column2:7!null
      │    │    ├── values
      │    │    │    ├── columns: column1:6!null column2:7!null column3:8!null
      │    │    │    └── (1, 2, 3)
      │    │    └── aggregations
      │    │         ├── first-agg [as=column1:6]
      │    │         │    └── column1:6
      │    │         └── first-agg [as=column3:8]
      │    │              └── column3:8
      │    ├── scan xyz
      │    │    ├── columns: x:9!null y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    └── flags: no-full-scan avoid-full-scan disabled not visible index feature
      │    └── filters
      │         └── column2:7 = x:9
      └── projections
           └── CASE WHEN x:9 IS NULL THEN column2:7 ELSE x:9 END [as=upsert_x:14]

build
UPSERT INTO xyz@bax_idx (z, x, y) VALUES (1, 2, 3)
----
error (42704): index "bax_idx" not found


# ------------------------------------------------------------------------------
# Test check constraints.
# ------------------------------------------------------------------------------

# INSERT..ON CONFLICT
build
INSERT INTO checks (a, b) VALUES (1, 2) ON CONFLICT (a) DO UPDATE SET b=3, c=4
----
upsert checks
 ├── arbiter indexes: checks_pkey
 ├── columns: <none>
 ├── canary column: a:11
 ├── fetch columns: a:11 b:12 c:13 d:14
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── c_default:9 => c:3
 │    └── d_comp:10 => d:4
 ├── update-mapping:
 │    ├── upsert_b:21 => b:2
 │    ├── upsert_c:22 => c:3
 │    └── upsert_d:23 => d:4
 ├── check columns: check1:24 check2:25
 └── project
      ├── columns: check1:24 check2:25 column1:7!null column2:8!null c_default:9 d_comp:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16 b_new:17!null c_new:18!null d_comp:19!null upsert_a:20 upsert_b:21!null upsert_c:22 upsert_d:23
      ├── project
      │    ├── columns: upsert_a:20 upsert_b:21!null upsert_c:22 upsert_d:23 column1:7!null column2:8!null c_default:9 d_comp:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16 b_new:17!null c_new:18!null d_comp:19!null
      │    ├── project
      │    │    ├── columns: d_comp:19!null column1:7!null column2:8!null c_default:9 d_comp:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16 b_new:17!null c_new:18!null
      │    │    ├── project
      │    │    │    ├── columns: b_new:17!null c_new:18!null column1:7!null column2:8!null c_default:9 d_comp:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    ├── left-join (hash)
      │    │    │    │    ├── columns: column1:7!null column2:8!null c_default:9 d_comp:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    │    ├── columns: column1:7!null column2:8!null c_default:9 d_comp:10
      │    │    │    │    │    ├── grouping columns: column1:7!null
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: d_comp:10 column1:7!null column2:8!null c_default:9
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: c_default:9 column1:7!null column2:8!null
      │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    ├── columns: column1:7!null column2:8!null
      │    │    │    │    │    │    │    │    └── (1, 2)
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── NULL::INT8 [as=c_default:9]
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── c_default:9 + 1 [as=d_comp:10]
      │    │    │    │    │    └── aggregations
      │    │    │    │    │         ├── first-agg [as=column2:8]
      │    │    │    │    │         │    └── column2:8
      │    │    │    │    │         ├── first-agg [as=c_default:9]
      │    │    │    │    │         │    └── c_default:9
      │    │    │    │    │         └── first-agg [as=d_comp:10]
      │    │    │    │    │              └── d_comp:10
      │    │    │    │    ├── scan checks
      │    │    │    │    │    ├── columns: a:11!null b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    │    │    ├── check constraint expressions
      │    │    │    │    │    │    └── a:11 > 0
      │    │    │    │    │    ├── computed column expressions
      │    │    │    │    │    │    └── d:14
      │    │    │    │    │    │         └── c:13 + 1
      │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    └── filters
      │    │    │    │         └── column1:7 = a:11
      │    │    │    └── projections
      │    │    │         ├── 3 [as=b_new:17]
      │    │    │         └── 4 [as=c_new:18]
      │    │    └── projections
      │    │         └── c_new:18 + 1 [as=d_comp:19]
      │    └── projections
      │         ├── CASE WHEN a:11 IS NULL THEN column1:7 ELSE a:11 END [as=upsert_a:20]
      │         ├── CASE WHEN a:11 IS NULL THEN column2:8 ELSE b_new:17 END [as=upsert_b:21]
      │         ├── CASE WHEN a:11 IS NULL THEN c_default:9 ELSE c_new:18 END [as=upsert_c:22]
      │         └── CASE WHEN a:11 IS NULL THEN d_comp:10 ELSE d_comp:19 END [as=upsert_d:23]
      └── projections
           ├── upsert_b:21 < upsert_d:23 [as=check1:24]
           └── upsert_a:20 > 0 [as=check2:25]

# INSERT..ON CONFLICT DO NOTHING
build
INSERT INTO checks (a, b) VALUES (1, 2) ON CONFLICT (a) DO NOTHING
----
insert checks
 ├── arbiter indexes: checks_pkey
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── c_default:9 => c:3
 │    └── d_comp:10 => d:4
 ├── check columns: check1:17 check2:18
 └── project
      ├── columns: check1:17 check2:18!null column1:7!null column2:8!null c_default:9 d_comp:10
      ├── upsert-distinct-on
      │    ├── columns: column1:7!null column2:8!null c_default:9 d_comp:10
      │    ├── grouping columns: column1:7!null
      │    ├── anti-join (hash)
      │    │    ├── columns: column1:7!null column2:8!null c_default:9 d_comp:10
      │    │    ├── project
      │    │    │    ├── columns: d_comp:10 column1:7!null column2:8!null c_default:9
      │    │    │    ├── project
      │    │    │    │    ├── columns: c_default:9 column1:7!null column2:8!null
      │    │    │    │    ├── values
      │    │    │    │    │    ├── columns: column1:7!null column2:8!null
      │    │    │    │    │    └── (1, 2)
      │    │    │    │    └── projections
      │    │    │    │         └── NULL::INT8 [as=c_default:9]
      │    │    │    └── projections
      │    │    │         └── c_default:9 + 1 [as=d_comp:10]
      │    │    ├── scan checks
      │    │    │    ├── columns: a:11!null b:12 c:13 d:14
      │    │    │    ├── check constraint expressions
      │    │    │    │    └── a:11 > 0
      │    │    │    ├── computed column expressions
      │    │    │    │    └── d:14
      │    │    │    │         └── c:13 + 1
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:7 = a:11
      │    └── aggregations
      │         ├── first-agg [as=column2:8]
      │         │    └── column2:8
      │         ├── first-agg [as=c_default:9]
      │         │    └── c_default:9
      │         └── first-agg [as=d_comp:10]
      │              └── d_comp:10
      └── projections
           ├── column2:8 < d_comp:10 [as=check1:17]
           └── column1:7 > 0 [as=check2:18]

# UPSERT
build
UPSERT INTO checks (a, b) VALUES (1, 2)
----
upsert checks
 ├── arbiter indexes: checks_pkey
 ├── columns: <none>
 ├── canary column: a:11
 ├── fetch columns: a:11 b:12 c:13 d:14
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── c_default:9 => c:3
 │    └── d_comp:10 => d:4
 ├── update-mapping:
 │    └── column2:8 => b:2
 ├── check columns: check1:21 check2:22
 └── project
      ├── columns: check1:21 check2:22 column1:7!null column2:8!null c_default:9 d_comp:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16 d_comp:17 upsert_a:18 upsert_c:19 upsert_d:20
      ├── project
      │    ├── columns: upsert_a:18 upsert_c:19 upsert_d:20 column1:7!null column2:8!null c_default:9 d_comp:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16 d_comp:17
      │    ├── project
      │    │    ├── columns: d_comp:17 column1:7!null column2:8!null c_default:9 d_comp:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: column1:7!null column2:8!null c_default:9 d_comp:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: column1:7!null column2:8!null c_default:9 d_comp:10
      │    │    │    │    ├── grouping columns: column1:7!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: d_comp:10 column1:7!null column2:8!null c_default:9
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: c_default:9 column1:7!null column2:8!null
      │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    ├── columns: column1:7!null column2:8!null
      │    │    │    │    │    │    │    └── (1, 2)
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── NULL::INT8 [as=c_default:9]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── c_default:9 + 1 [as=d_comp:10]
      │    │    │    │    └── aggregations
      │    │    │    │         ├── first-agg [as=column2:8]
      │    │    │    │         │    └── column2:8
      │    │    │    │         ├── first-agg [as=c_default:9]
      │    │    │    │         │    └── c_default:9
      │    │    │    │         └── first-agg [as=d_comp:10]
      │    │    │    │              └── d_comp:10
      │    │    │    ├── scan checks
      │    │    │    │    ├── columns: a:11!null b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    │    ├── check constraint expressions
      │    │    │    │    │    └── a:11 > 0
      │    │    │    │    ├── computed column expressions
      │    │    │    │    │    └── d:14
      │    │    │    │    │         └── c:13 + 1
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── column1:7 = a:11
      │    │    └── projections
      │    │         └── c:13 + 1 [as=d_comp:17]
      │    └── projections
      │         ├── CASE WHEN a:11 IS NULL THEN column1:7 ELSE a:11 END [as=upsert_a:18]
      │         ├── CASE WHEN a:11 IS NULL THEN c_default:9 ELSE c:13 END [as=upsert_c:19]
      │         └── CASE WHEN a:11 IS NULL THEN d_comp:10 ELSE d:14 END [as=upsert_d:20]
      └── projections
           ├── column2:8 < upsert_d:20 [as=check1:21]
           └── upsert_a:18 > 0 [as=check2:22]

# Use subqueries and excluded.
build
INSERT INTO checks
SELECT a, b FROM abc
ON CONFLICT (a) DO UPDATE SET a=excluded.a, b=(SELECT x FROM xyz WHERE x=checks.a)
----
upsert checks
 ├── arbiter indexes: checks_pkey
 ├── columns: <none>
 ├── canary column: checks.a:15
 ├── fetch columns: checks.a:15 checks.b:16 checks.c:17 d:18
 ├── insert-mapping:
 │    ├── abc.a:7 => checks.a:1
 │    ├── abc.b:8 => checks.b:2
 │    ├── c_default:13 => checks.c:3
 │    └── d_comp:14 => d:4
 ├── update-mapping:
 │    ├── abc.a:7 => checks.a:1
 │    └── upsert_b:28 => checks.b:2
 ├── check columns: check1:31 check2:32
 └── project
      ├── columns: check1:31 check2:32!null abc.a:7!null abc.b:8 c_default:13 d_comp:14 checks.a:15 checks.b:16 checks.c:17 d:18 checks.crdb_internal_mvcc_timestamp:19 checks.tableoid:20 b_new:26 d_comp:27 upsert_b:28 upsert_c:29 upsert_d:30
      ├── project
      │    ├── columns: upsert_b:28 upsert_c:29 upsert_d:30 abc.a:7!null abc.b:8 c_default:13 d_comp:14 checks.a:15 checks.b:16 checks.c:17 d:18 checks.crdb_internal_mvcc_timestamp:19 checks.tableoid:20 b_new:26 d_comp:27
      │    ├── project
      │    │    ├── columns: d_comp:27 abc.a:7!null abc.b:8 c_default:13 d_comp:14 checks.a:15 checks.b:16 checks.c:17 d:18 checks.crdb_internal_mvcc_timestamp:19 checks.tableoid:20 b_new:26
      │    │    ├── project
      │    │    │    ├── columns: b_new:26 abc.a:7!null abc.b:8 c_default:13 d_comp:14 checks.a:15 checks.b:16 checks.c:17 d:18 checks.crdb_internal_mvcc_timestamp:19 checks.tableoid:20
      │    │    │    ├── left-join (hash)
      │    │    │    │    ├── columns: abc.a:7!null abc.b:8 c_default:13 d_comp:14 checks.a:15 checks.b:16 checks.c:17 d:18 checks.crdb_internal_mvcc_timestamp:19 checks.tableoid:20
      │    │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    │    ├── columns: abc.a:7!null abc.b:8 c_default:13 d_comp:14
      │    │    │    │    │    ├── grouping columns: abc.a:7!null
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: d_comp:14 abc.a:7!null abc.b:8 c_default:13
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: c_default:13 abc.a:7!null abc.b:8
      │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    ├── columns: abc.a:7!null abc.b:8
      │    │    │    │    │    │    │    │    └── scan abc
      │    │    │    │    │    │    │    │         ├── columns: abc.a:7!null abc.b:8 abc.c:9 rowid:10!null abc.crdb_internal_mvcc_timestamp:11 abc.tableoid:12
      │    │    │    │    │    │    │    │         └── computed column expressions
      │    │    │    │    │    │    │    │              └── abc.c:9
      │    │    │    │    │    │    │    │                   └── abc.b:8 + 1
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── NULL::INT8 [as=c_default:13]
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── c_default:13 + 1 [as=d_comp:14]
      │    │    │    │    │    └── aggregations
      │    │    │    │    │         ├── first-agg [as=abc.b:8]
      │    │    │    │    │         │    └── abc.b:8
      │    │    │    │    │         ├── first-agg [as=c_default:13]
      │    │    │    │    │         │    └── c_default:13
      │    │    │    │    │         └── first-agg [as=d_comp:14]
      │    │    │    │    │              └── d_comp:14
      │    │    │    │    ├── scan checks
      │    │    │    │    │    ├── columns: checks.a:15!null checks.b:16 checks.c:17 d:18 checks.crdb_internal_mvcc_timestamp:19 checks.tableoid:20
      │    │    │    │    │    ├── check constraint expressions
      │    │    │    │    │    │    └── checks.a:15 > 0
      │    │    │    │    │    ├── computed column expressions
      │    │    │    │    │    │    └── d:18
      │    │    │    │    │    │         └── checks.c:17 + 1
      │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    └── filters
      │    │    │    │         └── abc.a:7 = checks.a:15
      │    │    │    └── projections
      │    │    │         └── subquery [as=b_new:26]
      │    │    │              └── max1-row
      │    │    │                   ├── columns: x:21!null
      │    │    │                   └── project
      │    │    │                        ├── columns: x:21!null
      │    │    │                        └── select
      │    │    │                             ├── columns: x:21!null y:22 z:23 xyz.crdb_internal_mvcc_timestamp:24 xyz.tableoid:25
      │    │    │                             ├── scan xyz
      │    │    │                             │    └── columns: x:21!null y:22 z:23 xyz.crdb_internal_mvcc_timestamp:24 xyz.tableoid:25
      │    │    │                             └── filters
      │    │    │                                  └── x:21 = checks.a:15
      │    │    └── projections
      │    │         └── checks.c:17 + 1 [as=d_comp:27]
      │    └── projections
      │         ├── CASE WHEN checks.a:15 IS NULL THEN abc.b:8 ELSE b_new:26 END [as=upsert_b:28]
      │         ├── CASE WHEN checks.a:15 IS NULL THEN c_default:13 ELSE checks.c:17 END [as=upsert_c:29]
      │         └── CASE WHEN checks.a:15 IS NULL THEN d_comp:14 ELSE d:18 END [as=upsert_d:30]
      └── projections
           ├── upsert_b:28 < upsert_d:30 [as=check1:31]
           └── abc.a:7 > 0 [as=check2:32]

# Use ORDER BY in upsert input (should be ignored and not cause error).
build
INSERT INTO xyz
SELECT a, b, c FROM abc ORDER BY a
ON CONFLICT (z, y) DO UPDATE SET y=5
----
upsert xyz
 ├── arbiter indexes: xyz_y_z_key
 ├── columns: <none>
 ├── canary column: x:12
 ├── fetch columns: x:12 y:13 z:14
 ├── insert-mapping:
 │    ├── a:6 => x:1
 │    ├── b:7 => y:2
 │    └── c:8 => z:3
 ├── update-mapping:
 │    └── upsert_y:19 => y:2
 └── project
      ├── columns: upsert_x:18 upsert_y:19 upsert_z:20 a:6!null b:7 c:8 x:12 y:13 z:14 xyz.crdb_internal_mvcc_timestamp:15 xyz.tableoid:16 y_new:17!null
      ├── project
      │    ├── columns: y_new:17!null a:6!null b:7 c:8 x:12 y:13 z:14 xyz.crdb_internal_mvcc_timestamp:15 xyz.tableoid:16
      │    ├── left-join (hash)
      │    │    ├── columns: a:6!null b:7 c:8 x:12 y:13 z:14 xyz.crdb_internal_mvcc_timestamp:15 xyz.tableoid:16
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: a:6!null b:7 c:8
      │    │    │    ├── grouping columns: b:7 c:8
      │    │    │    ├── project
      │    │    │    │    ├── columns: a:6!null b:7 c:8
      │    │    │    │    └── scan abc
      │    │    │    │         ├── columns: a:6!null b:7 c:8 rowid:9!null abc.crdb_internal_mvcc_timestamp:10 abc.tableoid:11
      │    │    │    │         └── computed column expressions
      │    │    │    │              └── c:8
      │    │    │    │                   └── b:7 + 1
      │    │    │    └── aggregations
      │    │    │         └── first-agg [as=a:6]
      │    │    │              └── a:6
      │    │    ├── scan xyz
      │    │    │    ├── columns: x:12!null y:13 z:14 xyz.crdb_internal_mvcc_timestamp:15 xyz.tableoid:16
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         ├── b:7 = y:13
      │    │         └── c:8 = z:14
      │    └── projections
      │         └── 5 [as=y_new:17]
      └── projections
           ├── CASE WHEN x:12 IS NULL THEN a:6 ELSE x:12 END [as=upsert_x:18]
           ├── CASE WHEN x:12 IS NULL THEN b:7 ELSE y_new:17 END [as=upsert_y:19]
           └── CASE WHEN x:12 IS NULL THEN c:8 ELSE z:14 END [as=upsert_z:20]

# Test multi-column-family checks under read committed. If a check constraint
# contains multiple column families but only some of them are updated, the
# query should fail under read committed.

exec-ddl
CREATE TABLE multi_col_fam_checks (
  a INT PRIMARY KEY,
  b INT,
  c INT,
  d INT UNIQUE,
  UNIQUE (a, b),
  UNIQUE (b, c),
  FAMILY (a),
  FAMILY (b, d),
  FAMILY (c),
  CHECK (b < c)
)
----

exec-ddl
CREATE TABLE multi_col_fam_checks_simple (
  a INT PRIMARY KEY,
  b INT,
  c INT,
  d INT,
  FAMILY (a),
  FAMILY (b),
  FAMILY (c),
  FAMILY (d),
  CHECK (b < c)
)
----

# Both b and c are updated (c is given the default value), so this should
# succeed.
build isolation=ReadCommitted
UPSERT INTO multi_col_fam_checks VALUES (5, 6)
----
upsert multi_col_fam_checks
 ├── arbiter indexes: multi_col_fam_checks_pkey
 ├── columns: <none>
 ├── canary column: a:10
 ├── fetch columns: a:10 b:11 c:12 d:13
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── c_default:9 => c:3
 │    └── c_default:9 => d:4
 ├── update-mapping:
 │    ├── column2:8 => b:2
 │    ├── c_default:9 => c:3
 │    └── c_default:9 => d:4
 ├── check columns: check1:17
 └── project
      ├── columns: check1:17 column1:7!null column2:8!null c_default:9 a:10 b:11 c:12 d:13 crdb_internal_mvcc_timestamp:14 tableoid:15 upsert_a:16
      ├── project
      │    ├── columns: upsert_a:16 column1:7!null column2:8!null c_default:9 a:10 b:11 c:12 d:13 crdb_internal_mvcc_timestamp:14 tableoid:15
      │    ├── left-join (hash)
      │    │    ├── columns: column1:7!null column2:8!null c_default:9 a:10 b:11 c:12 d:13 crdb_internal_mvcc_timestamp:14 tableoid:15
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:7!null column2:8!null c_default:9
      │    │    │    ├── grouping columns: column1:7!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: c_default:9 column1:7!null column2:8!null
      │    │    │    │    ├── values
      │    │    │    │    │    ├── columns: column1:7!null column2:8!null
      │    │    │    │    │    └── (5, 6)
      │    │    │    │    └── projections
      │    │    │    │         └── NULL::INT8 [as=c_default:9]
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=column2:8]
      │    │    │         │    └── column2:8
      │    │    │         └── first-agg [as=c_default:9]
      │    │    │              └── c_default:9
      │    │    ├── scan multi_col_fam_checks
      │    │    │    ├── columns: a:10!null b:11 c:12 d:13 crdb_internal_mvcc_timestamp:14 tableoid:15
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:7 = a:10
      │    └── projections
      │         └── CASE WHEN a:10 IS NULL THEN column1:7 ELSE a:10 END [as=upsert_a:16]
      └── projections
           └── column2:8 < c_default:9 [as=check1:17]

# Fast upsert case. Both b and c are updated (c is given the default value), so
# this should succeed.
build isolation=ReadCommitted
UPSERT INTO multi_col_fam_checks_simple VALUES (1, 2)
----
upsert multi_col_fam_checks_simple
 ├── columns: <none>
 ├── upsert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── c_default:9 => c:3
 │    └── c_default:9 => d:4
 ├── check columns: check1:10
 └── project
      ├── columns: check1:10 column1:7!null column2:8!null c_default:9
      ├── project
      │    ├── columns: c_default:9 column1:7!null column2:8!null
      │    ├── values
      │    │    ├── columns: column1:7!null column2:8!null
      │    │    └── (1, 2)
      │    └── projections
      │         └── NULL::INT8 [as=c_default:9]
      └── projections
           └── column2:8 < c_default:9 [as=check1:10]

# Only b is updated, but c must be read to check the constraint. Should fail
# under read committed.
build isolation=ReadCommitted
UPSERT INTO multi_col_fam_checks (a, b) VALUES (5, 6)
----
error (0A000): unimplemented: multi-column-family check constraints are not yet supported under read committed isolation

# The same query should succeed under serializable isolation.
build isolation=Serializable
UPSERT INTO multi_col_fam_checks (a, b) VALUES (5, 6)
----
upsert multi_col_fam_checks
 ├── arbiter indexes: multi_col_fam_checks_pkey
 ├── columns: <none>
 ├── canary column: a:10
 ├── fetch columns: a:10 b:11 c:12 d:13
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── c_default:9 => c:3
 │    └── c_default:9 => d:4
 ├── update-mapping:
 │    └── column2:8 => b:2
 ├── check columns: check1:19
 └── project
      ├── columns: check1:19 column1:7!null column2:8!null c_default:9 a:10 b:11 c:12 d:13 crdb_internal_mvcc_timestamp:14 tableoid:15 upsert_a:16 upsert_c:17 upsert_d:18
      ├── project
      │    ├── columns: upsert_a:16 upsert_c:17 upsert_d:18 column1:7!null column2:8!null c_default:9 a:10 b:11 c:12 d:13 crdb_internal_mvcc_timestamp:14 tableoid:15
      │    ├── left-join (hash)
      │    │    ├── columns: column1:7!null column2:8!null c_default:9 a:10 b:11 c:12 d:13 crdb_internal_mvcc_timestamp:14 tableoid:15
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:7!null column2:8!null c_default:9
      │    │    │    ├── grouping columns: column1:7!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: c_default:9 column1:7!null column2:8!null
      │    │    │    │    ├── values
      │    │    │    │    │    ├── columns: column1:7!null column2:8!null
      │    │    │    │    │    └── (5, 6)
      │    │    │    │    └── projections
      │    │    │    │         └── NULL::INT8 [as=c_default:9]
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=column2:8]
      │    │    │         │    └── column2:8
      │    │    │         └── first-agg [as=c_default:9]
      │    │    │              └── c_default:9
      │    │    ├── scan multi_col_fam_checks
      │    │    │    ├── columns: a:10!null b:11 c:12 d:13 crdb_internal_mvcc_timestamp:14 tableoid:15
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:7 = a:10
      │    └── projections
      │         ├── CASE WHEN a:10 IS NULL THEN column1:7 ELSE a:10 END [as=upsert_a:16]
      │         ├── CASE WHEN a:10 IS NULL THEN c_default:9 ELSE c:12 END [as=upsert_c:17]
      │         └── CASE WHEN a:10 IS NULL THEN c_default:9 ELSE d:13 END [as=upsert_d:18]
      └── projections
           └── column2:8 < upsert_c:17 [as=check1:19]

# Neither b nor c is updated, so this should succeed.
build isolation=ReadCommitted
UPSERT INTO multi_col_fam_checks (a, d) VALUES (3, 5)
----
upsert multi_col_fam_checks
 ├── arbiter indexes: multi_col_fam_checks_pkey
 ├── columns: <none>
 ├── canary column: a:10
 ├── fetch columns: a:10 b:11 c:12 d:13
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── b_default:9 => b:2
 │    ├── b_default:9 => c:3
 │    └── column2:8 => d:4
 ├── update-mapping:
 │    └── column2:8 => d:4
 ├── check columns: check1:19
 └── project
      ├── columns: check1:19 column1:7!null column2:8!null b_default:9 a:10 b:11 c:12 d:13 crdb_internal_mvcc_timestamp:14 tableoid:15 upsert_a:16 upsert_b:17 upsert_c:18
      ├── project
      │    ├── columns: upsert_a:16 upsert_b:17 upsert_c:18 column1:7!null column2:8!null b_default:9 a:10 b:11 c:12 d:13 crdb_internal_mvcc_timestamp:14 tableoid:15
      │    ├── left-join (hash)
      │    │    ├── columns: column1:7!null column2:8!null b_default:9 a:10 b:11 c:12 d:13 crdb_internal_mvcc_timestamp:14 tableoid:15
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:7!null column2:8!null b_default:9
      │    │    │    ├── grouping columns: column1:7!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: b_default:9 column1:7!null column2:8!null
      │    │    │    │    ├── values
      │    │    │    │    │    ├── columns: column1:7!null column2:8!null
      │    │    │    │    │    └── (3, 5)
      │    │    │    │    └── projections
      │    │    │    │         └── NULL::INT8 [as=b_default:9]
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=column2:8]
      │    │    │         │    └── column2:8
      │    │    │         └── first-agg [as=b_default:9]
      │    │    │              └── b_default:9
      │    │    ├── scan multi_col_fam_checks
      │    │    │    ├── columns: a:10!null b:11 c:12 d:13 crdb_internal_mvcc_timestamp:14 tableoid:15
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:7 = a:10
      │    └── projections
      │         ├── CASE WHEN a:10 IS NULL THEN column1:7 ELSE a:10 END [as=upsert_a:16]
      │         ├── CASE WHEN a:10 IS NULL THEN b_default:9 ELSE b:11 END [as=upsert_b:17]
      │         └── CASE WHEN a:10 IS NULL THEN b_default:9 ELSE c:12 END [as=upsert_c:18]
      └── projections
           └── upsert_b:17 < upsert_c:18 [as=check1:19]

# Only b is updated, but c must be read to check the constraint. Should fail
# under read committed.
build isolation=ReadCommitted
INSERT INTO multi_col_fam_checks VALUES (5) ON CONFLICT (a) DO UPDATE SET b = 5
----
error (0A000): unimplemented: multi-column-family check constraints are not yet supported under read committed isolation

# The same query should succeed under serializable isolation.
build isolation=Serializable
INSERT INTO multi_col_fam_checks VALUES (5) ON CONFLICT (a) DO UPDATE SET b = 5
----
upsert multi_col_fam_checks
 ├── arbiter indexes: multi_col_fam_checks_pkey
 ├── columns: <none>
 ├── canary column: a:9
 ├── fetch columns: a:9 b:10 c:11 d:12
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── b_default:8 => b:2
 │    ├── b_default:8 => c:3
 │    └── b_default:8 => d:4
 ├── update-mapping:
 │    └── upsert_b:17 => b:2
 ├── check columns: check1:20
 └── project
      ├── columns: check1:20 column1:7!null b_default:8 a:9 b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14 b_new:15!null upsert_a:16 upsert_b:17 upsert_c:18 upsert_d:19
      ├── project
      │    ├── columns: upsert_a:16 upsert_b:17 upsert_c:18 upsert_d:19 column1:7!null b_default:8 a:9 b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14 b_new:15!null
      │    ├── project
      │    │    ├── columns: b_new:15!null column1:7!null b_default:8 a:9 b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: column1:7!null b_default:8 a:9 b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: column1:7!null b_default:8
      │    │    │    │    ├── grouping columns: column1:7!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: b_default:8 column1:7!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:7!null
      │    │    │    │    │    │    └── (5,)
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── NULL::INT8 [as=b_default:8]
      │    │    │    │    └── aggregations
      │    │    │    │         └── first-agg [as=b_default:8]
      │    │    │    │              └── b_default:8
      │    │    │    ├── scan multi_col_fam_checks
      │    │    │    │    ├── columns: a:9!null b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── column1:7 = a:9
      │    │    └── projections
      │    │         └── 5 [as=b_new:15]
      │    └── projections
      │         ├── CASE WHEN a:9 IS NULL THEN column1:7 ELSE a:9 END [as=upsert_a:16]
      │         ├── CASE WHEN a:9 IS NULL THEN b_default:8 ELSE b_new:15 END [as=upsert_b:17]
      │         ├── CASE WHEN a:9 IS NULL THEN b_default:8 ELSE c:11 END [as=upsert_c:18]
      │         └── CASE WHEN a:9 IS NULL THEN b_default:8 ELSE d:12 END [as=upsert_d:19]
      └── projections
           └── upsert_b:17 < upsert_c:18 [as=check1:20]

# Only c is updated, but b is read. Should fail under read committed.
build isolation=ReadCommitted
INSERT INTO multi_col_fam_checks SELECT a FROM multi_col_fam_checks WHERE b = 5
ON CONFLICT (d) DO UPDATE SET c = 3
----
error (0A000): unimplemented: multi-column-family check constraints are not yet supported under read committed isolation

# The same query should succeed under serializable isolation.
build isolation=Serializable
INSERT INTO multi_col_fam_checks SELECT a FROM multi_col_fam_checks WHERE b = 5
ON CONFLICT (d) DO UPDATE SET c = 3
----
upsert multi_col_fam_checks
 ├── arbiter indexes: multi_col_fam_checks_d_key
 ├── columns: <none>
 ├── canary column: a:14
 ├── fetch columns: a:14 b:15 c:16 d:17
 ├── insert-mapping:
 │    ├── a:7 => a:1
 │    ├── b_default:13 => b:2
 │    ├── b_default:13 => c:3
 │    └── b_default:13 => d:4
 ├── update-mapping:
 │    └── upsert_c:23 => c:3
 ├── check columns: check1:25
 └── project
      ├── columns: check1:25 a:7!null b_default:13 a:14 b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19 c_new:20!null upsert_a:21 upsert_b:22 upsert_c:23 upsert_d:24
      ├── project
      │    ├── columns: upsert_a:21 upsert_b:22 upsert_c:23 upsert_d:24 a:7!null b_default:13 a:14 b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19 c_new:20!null
      │    ├── project
      │    │    ├── columns: c_new:20!null a:7!null b_default:13 a:14 b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: a:7!null b_default:13 a:14 b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: a:7!null b_default:13
      │    │    │    │    ├── grouping columns: b_default:13
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: b_default:13 a:7!null
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: a:7!null
      │    │    │    │    │    │    └── select
      │    │    │    │    │    │         ├── columns: a:7!null b:8!null c:9 d:10 crdb_internal_mvcc_timestamp:11 tableoid:12
      │    │    │    │    │    │         ├── scan multi_col_fam_checks
      │    │    │    │    │    │         │    └── columns: a:7!null b:8 c:9 d:10 crdb_internal_mvcc_timestamp:11 tableoid:12
      │    │    │    │    │    │         └── filters
      │    │    │    │    │    │              └── b:8 = 5
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── NULL::INT8 [as=b_default:13]
      │    │    │    │    └── aggregations
      │    │    │    │         └── first-agg [as=a:7]
      │    │    │    │              └── a:7
      │    │    │    ├── scan multi_col_fam_checks
      │    │    │    │    ├── columns: a:14!null b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── b_default:13 = d:17
      │    │    └── projections
      │    │         └── 3 [as=c_new:20]
      │    └── projections
      │         ├── CASE WHEN a:14 IS NULL THEN a:7 ELSE a:14 END [as=upsert_a:21]
      │         ├── CASE WHEN a:14 IS NULL THEN b_default:13 ELSE b:15 END [as=upsert_b:22]
      │         ├── CASE WHEN a:14 IS NULL THEN b_default:13 ELSE c_new:20 END [as=upsert_c:23]
      │         └── CASE WHEN a:14 IS NULL THEN b_default:13 ELSE d:17 END [as=upsert_d:24]
      └── projections
           └── upsert_b:22 < upsert_c:23 [as=check1:25]

# Both b and c are updated, so this should succeed.
build isolation=ReadCommitted
INSERT INTO multi_col_fam_checks VALUES (1, 2, 3)
ON CONFLICT (a, b) DO UPDATE SET b = 3, c = 4
----
upsert multi_col_fam_checks
 ├── arbiter indexes: multi_col_fam_checks_a_b_key
 ├── columns: <none>
 ├── canary column: a:11
 ├── fetch columns: a:11 b:12 c:13 d:14
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── column3:9 => c:3
 │    └── d_default:10 => d:4
 ├── update-mapping:
 │    ├── upsert_b:20 => b:2
 │    └── upsert_c:21 => c:3
 ├── check columns: check1:23
 └── project
      ├── columns: check1:23!null column1:7!null column2:8!null column3:9!null d_default:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16 b_new:17!null c_new:18!null upsert_a:19 upsert_b:20!null upsert_c:21!null upsert_d:22
      ├── project
      │    ├── columns: upsert_a:19 upsert_b:20!null upsert_c:21!null upsert_d:22 column1:7!null column2:8!null column3:9!null d_default:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16 b_new:17!null c_new:18!null
      │    ├── project
      │    │    ├── columns: b_new:17!null c_new:18!null column1:7!null column2:8!null column3:9!null d_default:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: column1:7!null column2:8!null column3:9!null d_default:10 a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: column1:7!null column2:8!null column3:9!null d_default:10
      │    │    │    │    ├── grouping columns: column1:7!null column2:8!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: d_default:10 column1:7!null column2:8!null column3:9!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:7!null column2:8!null column3:9!null
      │    │    │    │    │    │    └── (1, 2, 3)
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── NULL::INT8 [as=d_default:10]
      │    │    │    │    └── aggregations
      │    │    │    │         ├── first-agg [as=column3:9]
      │    │    │    │         │    └── column3:9
      │    │    │    │         └── first-agg [as=d_default:10]
      │    │    │    │              └── d_default:10
      │    │    │    ├── scan multi_col_fam_checks
      │    │    │    │    ├── columns: a:11!null b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         ├── column1:7 = a:11
      │    │    │         └── column2:8 = b:12
      │    │    └── projections
      │    │         ├── 3 [as=b_new:17]
      │    │         └── 4 [as=c_new:18]
      │    └── projections
      │         ├── CASE WHEN a:11 IS NULL THEN column1:7 ELSE a:11 END [as=upsert_a:19]
      │         ├── CASE WHEN a:11 IS NULL THEN column2:8 ELSE b_new:17 END [as=upsert_b:20]
      │         ├── CASE WHEN a:11 IS NULL THEN column3:9 ELSE c_new:18 END [as=upsert_c:21]
      │         └── CASE WHEN a:11 IS NULL THEN d_default:10 ELSE d:14 END [as=upsert_d:22]
      └── projections
           └── upsert_b:20 < upsert_c:21 [as=check1:23]

# Both b and c are updated, so this should succeed.
build isolation=ReadCommitted
INSERT INTO multi_col_fam_checks (a) VALUES (1)
ON CONFLICT (a) DO UPDATE SET b = 3, c = 4, d = 5
----
upsert multi_col_fam_checks
 ├── arbiter indexes: multi_col_fam_checks_pkey
 ├── columns: <none>
 ├── canary column: a:9
 ├── fetch columns: a:9 b:10 c:11 d:12
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── b_default:8 => b:2
 │    ├── b_default:8 => c:3
 │    └── b_default:8 => d:4
 ├── update-mapping:
 │    ├── upsert_b:19 => b:2
 │    ├── upsert_c:20 => c:3
 │    └── upsert_d:21 => d:4
 ├── check columns: check1:22
 └── project
      ├── columns: check1:22 column1:7!null b_default:8 a:9 b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14 b_new:15!null c_new:16!null d_new:17!null upsert_a:18 upsert_b:19 upsert_c:20 upsert_d:21
      ├── project
      │    ├── columns: upsert_a:18 upsert_b:19 upsert_c:20 upsert_d:21 column1:7!null b_default:8 a:9 b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14 b_new:15!null c_new:16!null d_new:17!null
      │    ├── project
      │    │    ├── columns: b_new:15!null c_new:16!null d_new:17!null column1:7!null b_default:8 a:9 b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: column1:7!null b_default:8 a:9 b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: column1:7!null b_default:8
      │    │    │    │    ├── grouping columns: column1:7!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: b_default:8 column1:7!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:7!null
      │    │    │    │    │    │    └── (1,)
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── NULL::INT8 [as=b_default:8]
      │    │    │    │    └── aggregations
      │    │    │    │         └── first-agg [as=b_default:8]
      │    │    │    │              └── b_default:8
      │    │    │    ├── scan multi_col_fam_checks
      │    │    │    │    ├── columns: a:9!null b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── column1:7 = a:9
      │    │    └── projections
      │    │         ├── 3 [as=b_new:15]
      │    │         ├── 4 [as=c_new:16]
      │    │         └── 5 [as=d_new:17]
      │    └── projections
      │         ├── CASE WHEN a:9 IS NULL THEN column1:7 ELSE a:9 END [as=upsert_a:18]
      │         ├── CASE WHEN a:9 IS NULL THEN b_default:8 ELSE b_new:15 END [as=upsert_b:19]
      │         ├── CASE WHEN a:9 IS NULL THEN b_default:8 ELSE c_new:16 END [as=upsert_c:20]
      │         └── CASE WHEN a:9 IS NULL THEN b_default:8 ELSE d_new:17 END [as=upsert_d:21]
      └── projections
           └── upsert_b:19 < upsert_c:20 [as=check1:22]

# Neither b nor c is updated, so this should succeed.
build isolation=ReadCommitted
INSERT INTO multi_col_fam_checks (a, b, c, d) VALUES (1, 2, 3, 4)
ON CONFLICT (b, c) DO UPDATE SET a = 5
----
upsert multi_col_fam_checks
 ├── arbiter indexes: multi_col_fam_checks_b_c_key
 ├── columns: <none>
 ├── canary column: a:11
 ├── fetch columns: a:11 b:12 c:13 d:14
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── column2:8 => b:2
 │    ├── column3:9 => c:3
 │    └── column4:10 => d:4
 ├── update-mapping:
 │    └── upsert_a:18 => a:1
 ├── check columns: check1:22
 └── project
      ├── columns: check1:22 column1:7!null column2:8!null column3:9!null column4:10!null a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16 a_new:17!null upsert_a:18!null upsert_b:19 upsert_c:20 upsert_d:21
      ├── project
      │    ├── columns: upsert_a:18!null upsert_b:19 upsert_c:20 upsert_d:21 column1:7!null column2:8!null column3:9!null column4:10!null a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16 a_new:17!null
      │    ├── project
      │    │    ├── columns: a_new:17!null column1:7!null column2:8!null column3:9!null column4:10!null a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: column1:7!null column2:8!null column3:9!null column4:10!null a:11 b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: column1:7!null column2:8!null column3:9!null column4:10!null
      │    │    │    │    ├── grouping columns: column2:8!null column3:9!null
      │    │    │    │    ├── values
      │    │    │    │    │    ├── columns: column1:7!null column2:8!null column3:9!null column4:10!null
      │    │    │    │    │    └── (1, 2, 3, 4)
      │    │    │    │    └── aggregations
      │    │    │    │         ├── first-agg [as=column1:7]
      │    │    │    │         │    └── column1:7
      │    │    │    │         └── first-agg [as=column4:10]
      │    │    │    │              └── column4:10
      │    │    │    ├── scan multi_col_fam_checks
      │    │    │    │    ├── columns: a:11!null b:12 c:13 d:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         ├── column2:8 = b:12
      │    │    │         └── column3:9 = c:13
      │    │    └── projections
      │    │         └── 5 [as=a_new:17]
      │    └── projections
      │         ├── CASE WHEN a:11 IS NULL THEN column1:7 ELSE a_new:17 END [as=upsert_a:18]
      │         ├── CASE WHEN a:11 IS NULL THEN column2:8 ELSE b:12 END [as=upsert_b:19]
      │         ├── CASE WHEN a:11 IS NULL THEN column3:9 ELSE c:13 END [as=upsert_c:20]
      │         └── CASE WHEN a:11 IS NULL THEN column4:10 ELSE d:14 END [as=upsert_d:21]
      └── projections
           └── upsert_b:19 < upsert_c:20 [as=check1:22]

# Neither b nor c is updated, so this should succeed.
build isolation=ReadCommitted
INSERT INTO multi_col_fam_checks (a) VALUES (1)
ON CONFLICT (a) DO UPDATE SET d = 5
----
upsert multi_col_fam_checks
 ├── arbiter indexes: multi_col_fam_checks_pkey
 ├── columns: <none>
 ├── canary column: a:9
 ├── fetch columns: a:9 b:10 c:11 d:12
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── b_default:8 => b:2
 │    ├── b_default:8 => c:3
 │    └── b_default:8 => d:4
 ├── update-mapping:
 │    └── upsert_d:19 => d:4
 ├── check columns: check1:20
 └── project
      ├── columns: check1:20 column1:7!null b_default:8 a:9 b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14 d_new:15!null upsert_a:16 upsert_b:17 upsert_c:18 upsert_d:19
      ├── project
      │    ├── columns: upsert_a:16 upsert_b:17 upsert_c:18 upsert_d:19 column1:7!null b_default:8 a:9 b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14 d_new:15!null
      │    ├── project
      │    │    ├── columns: d_new:15!null column1:7!null b_default:8 a:9 b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: column1:7!null b_default:8 a:9 b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: column1:7!null b_default:8
      │    │    │    │    ├── grouping columns: column1:7!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: b_default:8 column1:7!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:7!null
      │    │    │    │    │    │    └── (1,)
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── NULL::INT8 [as=b_default:8]
      │    │    │    │    └── aggregations
      │    │    │    │         └── first-agg [as=b_default:8]
      │    │    │    │              └── b_default:8
      │    │    │    ├── scan multi_col_fam_checks
      │    │    │    │    ├── columns: a:9!null b:10 c:11 d:12 crdb_internal_mvcc_timestamp:13 tableoid:14
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── column1:7 = a:9
      │    │    └── projections
      │    │         └── 5 [as=d_new:15]
      │    └── projections
      │         ├── CASE WHEN a:9 IS NULL THEN column1:7 ELSE a:9 END [as=upsert_a:16]
      │         ├── CASE WHEN a:9 IS NULL THEN b_default:8 ELSE b:10 END [as=upsert_b:17]
      │         ├── CASE WHEN a:9 IS NULL THEN b_default:8 ELSE c:11 END [as=upsert_c:18]
      │         └── CASE WHEN a:9 IS NULL THEN b_default:8 ELSE d_new:15 END [as=upsert_d:19]
      └── projections
           └── upsert_b:17 < upsert_c:18 [as=check1:20]

# ------------------------------------------------------------------------------
# Test assignment casts.
# ------------------------------------------------------------------------------

# Fast UPSERT case.
build
UPSERT INTO decimals (a, b) VALUES (1.1, ARRAY[0.95])
----
upsert decimals
 ├── arbiter indexes: decimals_pkey
 ├── columns: <none>
 ├── canary column: a:15
 ├── fetch columns: a:15 b:16 c:17 d:18
 ├── insert-mapping:
 │    ├── a_cast:9 => a:1
 │    ├── b_cast:10 => b:2
 │    ├── c_cast:12 => c:3
 │    └── d_cast:14 => d:4
 ├── update-mapping:
 │    └── b_cast:10 => b:2
 ├── check columns: check1:25 check2:26
 └── project
      ├── columns: check1:25 check2:26 a_cast:9!null b_cast:10 c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20 d_comp:21 upsert_a:22 upsert_c:23 upsert_d:24
      ├── project
      │    ├── columns: upsert_a:22 upsert_c:23 upsert_d:24 a_cast:9!null b_cast:10 c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20 d_comp:21
      │    ├── project
      │    │    ├── columns: d_comp:21 a_cast:9!null b_cast:10 c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: a_cast:9!null b_cast:10 c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: a_cast:9!null b_cast:10 c_cast:12!null d_cast:14!null
      │    │    │    │    ├── grouping columns: a_cast:9!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: d_cast:14!null a_cast:9!null b_cast:10 c_cast:12!null
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: d_comp:13!null a_cast:9!null b_cast:10 c_cast:12!null
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: c_cast:12!null a_cast:9!null b_cast:10
      │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    ├── columns: c_default:11!null a_cast:9!null b_cast:10
      │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    ├── columns: a_cast:9!null b_cast:10
      │    │    │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    │    │    ├── columns: column1:7!null column2:8
      │    │    │    │    │    │    │    │    │    │    └── (1.1, ARRAY[0.95])
      │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │         ├── assignment-cast: DECIMAL(10) [as=a_cast:9]
      │    │    │    │    │    │    │    │    │         │    └── column1:7
      │    │    │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(5,1)[] [as=b_cast:10]
      │    │    │    │    │    │    │    │    │              └── column2:8
      │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │         └── 1.23 [as=c_default:11]
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10,1) [as=c_cast:12]
      │    │    │    │    │    │    │              └── c_default:11
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── a_cast:9 + c_cast:12 [as=d_comp:13]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── assignment-cast: DECIMAL(10,1) [as=d_cast:14]
      │    │    │    │    │              └── d_comp:13
      │    │    │    │    └── aggregations
      │    │    │    │         ├── first-agg [as=b_cast:10]
      │    │    │    │         │    └── b_cast:10
      │    │    │    │         ├── first-agg [as=c_cast:12]
      │    │    │    │         │    └── c_cast:12
      │    │    │    │         └── first-agg [as=d_cast:14]
      │    │    │    │              └── d_cast:14
      │    │    │    ├── scan decimals
      │    │    │    │    ├── columns: a:15!null b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20
      │    │    │    │    ├── computed column expressions
      │    │    │    │    │    └── d:18
      │    │    │    │    │         └── assignment-cast: DECIMAL(10,1)
      │    │    │    │    │              └── a:15 + c:17
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── a_cast:9 = a:15
      │    │    └── projections
      │    │         └── a:15 + c:17 [as=d_comp:21]
      │    └── projections
      │         ├── CASE WHEN a:15 IS NULL THEN a_cast:9 ELSE a:15 END [as=upsert_a:22]
      │         ├── CASE WHEN a:15 IS NULL THEN c_cast:12 ELSE c:17 END [as=upsert_c:23]
      │         └── CASE WHEN a:15 IS NULL THEN d_cast:14 ELSE d:18 END [as=upsert_d:24]
      └── projections
           ├── round(upsert_a:22) = upsert_a:22 [as=check1:25]
           └── b_cast:10[0] > 1 [as=check2:26]

# Regular UPSERT case.
build
UPSERT INTO decimals (a) VALUES (1.1)
----
upsert decimals
 ├── arbiter indexes: decimals_pkey
 ├── columns: <none>
 ├── canary column: a:14
 ├── fetch columns: a:14 b:15 c:16 d:17
 ├── insert-mapping:
 │    ├── a_cast:8 => a:1
 │    ├── b_default:9 => b:2
 │    ├── c_cast:11 => c:3
 │    └── d_cast:13 => d:4
 ├── check columns: check1:25 check2:26
 └── project
      ├── columns: check1:25 check2:26 a_cast:8!null b_default:9 c_cast:11!null d_cast:13!null a:14 b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19 d_comp:20 upsert_a:21 upsert_b:22 upsert_c:23 upsert_d:24
      ├── project
      │    ├── columns: upsert_a:21 upsert_b:22 upsert_c:23 upsert_d:24 a_cast:8!null b_default:9 c_cast:11!null d_cast:13!null a:14 b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19 d_comp:20
      │    ├── project
      │    │    ├── columns: d_comp:20 a_cast:8!null b_default:9 c_cast:11!null d_cast:13!null a:14 b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: a_cast:8!null b_default:9 c_cast:11!null d_cast:13!null a:14 b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: a_cast:8!null b_default:9 c_cast:11!null d_cast:13!null
      │    │    │    │    ├── grouping columns: a_cast:8!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: d_cast:13!null a_cast:8!null b_default:9 c_cast:11!null
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: d_comp:12!null a_cast:8!null b_default:9 c_cast:11!null
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: c_cast:11!null a_cast:8!null b_default:9
      │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    ├── columns: b_default:9 c_default:10!null a_cast:8!null
      │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    ├── columns: a_cast:8!null
      │    │    │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    │    │    ├── columns: column1:7!null
      │    │    │    │    │    │    │    │    │    │    └── (1.1,)
      │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10) [as=a_cast:8]
      │    │    │    │    │    │    │    │    │              └── column1:7
      │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │         ├── NULL::DECIMAL(5,1)[] [as=b_default:9]
      │    │    │    │    │    │    │    │         └── 1.23 [as=c_default:10]
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10,1) [as=c_cast:11]
      │    │    │    │    │    │    │              └── c_default:10
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── a_cast:8 + c_cast:11 [as=d_comp:12]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── assignment-cast: DECIMAL(10,1) [as=d_cast:13]
      │    │    │    │    │              └── d_comp:12
      │    │    │    │    └── aggregations
      │    │    │    │         ├── first-agg [as=b_default:9]
      │    │    │    │         │    └── b_default:9
      │    │    │    │         ├── first-agg [as=c_cast:11]
      │    │    │    │         │    └── c_cast:11
      │    │    │    │         └── first-agg [as=d_cast:13]
      │    │    │    │              └── d_cast:13
      │    │    │    ├── scan decimals
      │    │    │    │    ├── columns: a:14!null b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19
      │    │    │    │    ├── computed column expressions
      │    │    │    │    │    └── d:17
      │    │    │    │    │         └── assignment-cast: DECIMAL(10,1)
      │    │    │    │    │              └── a:14 + c:16
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── a_cast:8 = a:14
      │    │    └── projections
      │    │         └── a:14 + c:16 [as=d_comp:20]
      │    └── projections
      │         ├── CASE WHEN a:14 IS NULL THEN a_cast:8 ELSE a:14 END [as=upsert_a:21]
      │         ├── CASE WHEN a:14 IS NULL THEN b_default:9 ELSE b:15 END [as=upsert_b:22]
      │         ├── CASE WHEN a:14 IS NULL THEN c_cast:11 ELSE c:16 END [as=upsert_c:23]
      │         └── CASE WHEN a:14 IS NULL THEN d_cast:13 ELSE d:17 END [as=upsert_d:24]
      └── projections
           ├── round(upsert_a:21) = upsert_a:21 [as=check1:25]
           └── upsert_b:22[0] > 1 [as=check2:26]

# Regular UPSERT case as a prepared statement.
assign-placeholders-build query-args=(1.1)
UPSERT INTO decimals (a) VALUES ($1)
----
upsert decimals
 ├── arbiter indexes: decimals_pkey
 ├── columns: <none>
 ├── canary column: a:14
 ├── fetch columns: a:14 b:15 c:16 d:17
 ├── insert-mapping:
 │    ├── a_cast:8 => a:1
 │    ├── b_default:9 => b:2
 │    ├── c_cast:11 => c:3
 │    └── d_cast:13 => d:4
 ├── check columns: check1:25 check2:26
 └── project
      ├── columns: check1:25 check2:26 a_cast:8!null b_default:9 c_cast:11!null d_cast:13!null a:14 b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19 d_comp:20 upsert_a:21 upsert_b:22 upsert_c:23 upsert_d:24
      ├── project
      │    ├── columns: upsert_a:21 upsert_b:22 upsert_c:23 upsert_d:24 a_cast:8!null b_default:9 c_cast:11!null d_cast:13!null a:14 b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19 d_comp:20
      │    ├── project
      │    │    ├── columns: d_comp:20 a_cast:8!null b_default:9 c_cast:11!null d_cast:13!null a:14 b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: a_cast:8!null b_default:9 c_cast:11!null d_cast:13!null a:14 b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: a_cast:8!null b_default:9 c_cast:11!null d_cast:13!null
      │    │    │    │    ├── grouping columns: a_cast:8!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: d_cast:13!null a_cast:8!null b_default:9 c_cast:11!null
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: d_comp:12!null a_cast:8!null b_default:9 c_cast:11!null
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: c_cast:11!null a_cast:8!null b_default:9
      │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    ├── columns: b_default:9 c_default:10!null a_cast:8!null
      │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    ├── columns: a_cast:8!null
      │    │    │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    │    │    ├── columns: column1:7!null
      │    │    │    │    │    │    │    │    │    │    └── (1.1,)
      │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10) [as=a_cast:8]
      │    │    │    │    │    │    │    │    │              └── column1:7
      │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │         ├── NULL::DECIMAL(5,1)[] [as=b_default:9]
      │    │    │    │    │    │    │    │         └── 1.23 [as=c_default:10]
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10,1) [as=c_cast:11]
      │    │    │    │    │    │    │              └── c_default:10
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── a_cast:8 + c_cast:11 [as=d_comp:12]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── assignment-cast: DECIMAL(10,1) [as=d_cast:13]
      │    │    │    │    │              └── d_comp:12
      │    │    │    │    └── aggregations
      │    │    │    │         ├── first-agg [as=b_default:9]
      │    │    │    │         │    └── b_default:9
      │    │    │    │         ├── first-agg [as=c_cast:11]
      │    │    │    │         │    └── c_cast:11
      │    │    │    │         └── first-agg [as=d_cast:13]
      │    │    │    │              └── d_cast:13
      │    │    │    ├── scan decimals
      │    │    │    │    ├── columns: a:14!null b:15 c:16 d:17 crdb_internal_mvcc_timestamp:18 tableoid:19
      │    │    │    │    ├── computed column expressions
      │    │    │    │    │    └── d:17
      │    │    │    │    │         └── assignment-cast: DECIMAL(10,1)
      │    │    │    │    │              └── a:14 + c:16
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── a_cast:8 = a:14
      │    │    └── projections
      │    │         └── a:14 + c:16 [as=d_comp:20]
      │    └── projections
      │         ├── CASE WHEN a:14 IS NULL THEN a_cast:8 ELSE a:14 END [as=upsert_a:21]
      │         ├── CASE WHEN a:14 IS NULL THEN b_default:9 ELSE b:15 END [as=upsert_b:22]
      │         ├── CASE WHEN a:14 IS NULL THEN c_cast:11 ELSE c:16 END [as=upsert_c:23]
      │         └── CASE WHEN a:14 IS NULL THEN d_cast:13 ELSE d:17 END [as=upsert_d:24]
      └── projections
           ├── round(upsert_a:21) = upsert_a:21 [as=check1:25]
           └── upsert_b:22[0] > 1 [as=check2:26]

# INSERT...ON CONFLICT case.
build
INSERT INTO decimals (a, b) VALUES (1.1, ARRAY[0.95])
ON CONFLICT (a)
DO UPDATE SET b=ARRAY[0.99]
----
upsert decimals
 ├── arbiter indexes: decimals_pkey
 ├── columns: <none>
 ├── canary column: a:15
 ├── fetch columns: a:15 b:16 c:17 d:18
 ├── insert-mapping:
 │    ├── a_cast:9 => a:1
 │    ├── b_cast:10 => b:2
 │    ├── c_cast:12 => c:3
 │    └── d_cast:14 => d:4
 ├── update-mapping:
 │    └── upsert_b:25 => b:2
 ├── check columns: check1:28 check2:29
 └── project
      ├── columns: check1:28 check2:29 a_cast:9!null b_cast:10 c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20 b_cast:22!null d_comp:23 upsert_a:24 upsert_b:25 upsert_c:26 upsert_d:27
      ├── project
      │    ├── columns: upsert_a:24 upsert_b:25 upsert_c:26 upsert_d:27 a_cast:9!null b_cast:10 c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20 b_cast:22!null d_comp:23
      │    ├── project
      │    │    ├── columns: d_comp:23 a_cast:9!null b_cast:10 c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20 b_cast:22!null
      │    │    ├── project
      │    │    │    ├── columns: b_cast:22!null a_cast:9!null b_cast:10 c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20
      │    │    │    ├── project
      │    │    │    │    ├── columns: b_new:21!null a_cast:9!null b_cast:10 c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20
      │    │    │    │    ├── left-join (hash)
      │    │    │    │    │    ├── columns: a_cast:9!null b_cast:10 c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20
      │    │    │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    │    │    ├── columns: a_cast:9!null b_cast:10 c_cast:12!null d_cast:14!null
      │    │    │    │    │    │    ├── grouping columns: a_cast:9!null
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: d_cast:14!null a_cast:9!null b_cast:10 c_cast:12!null
      │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    ├── columns: d_comp:13!null a_cast:9!null b_cast:10 c_cast:12!null
      │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    ├── columns: c_cast:12!null a_cast:9!null b_cast:10
      │    │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    │    ├── columns: c_default:11!null a_cast:9!null b_cast:10
      │    │    │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    │    │    ├── columns: a_cast:9!null b_cast:10
      │    │    │    │    │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    │    │    │    │    ├── columns: column1:7!null column2:8
      │    │    │    │    │    │    │    │    │    │    │    │    └── (1.1, ARRAY[0.95])
      │    │    │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │    │    │         ├── assignment-cast: DECIMAL(10) [as=a_cast:9]
      │    │    │    │    │    │    │    │    │    │    │         │    └── column1:7
      │    │    │    │    │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(5,1)[] [as=b_cast:10]
      │    │    │    │    │    │    │    │    │    │    │              └── column2:8
      │    │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │    │         └── 1.23 [as=c_default:11]
      │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10,1) [as=c_cast:12]
      │    │    │    │    │    │    │    │    │              └── c_default:11
      │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │         └── a_cast:9 + c_cast:12 [as=d_comp:13]
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10,1) [as=d_cast:14]
      │    │    │    │    │    │    │              └── d_comp:13
      │    │    │    │    │    │    └── aggregations
      │    │    │    │    │    │         ├── first-agg [as=b_cast:10]
      │    │    │    │    │    │         │    └── b_cast:10
      │    │    │    │    │    │         ├── first-agg [as=c_cast:12]
      │    │    │    │    │    │         │    └── c_cast:12
      │    │    │    │    │    │         └── first-agg [as=d_cast:14]
      │    │    │    │    │    │              └── d_cast:14
      │    │    │    │    │    ├── scan decimals
      │    │    │    │    │    │    ├── columns: a:15!null b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20
      │    │    │    │    │    │    ├── computed column expressions
      │    │    │    │    │    │    │    └── d:18
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10,1)
      │    │    │    │    │    │    │              └── a:15 + c:17
      │    │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    │    └── filters
      │    │    │    │    │         └── a_cast:9 = a:15
      │    │    │    │    └── projections
      │    │    │    │         └── ARRAY[0.99] [as=b_new:21]
      │    │    │    └── projections
      │    │    │         └── assignment-cast: DECIMAL(5,1)[] [as=b_cast:22]
      │    │    │              └── b_new:21
      │    │    └── projections
      │    │         └── a:15 + c:17 [as=d_comp:23]
      │    └── projections
      │         ├── CASE WHEN a:15 IS NULL THEN a_cast:9 ELSE a:15 END [as=upsert_a:24]
      │         ├── CASE WHEN a:15 IS NULL THEN b_cast:10 ELSE b_cast:22 END [as=upsert_b:25]
      │         ├── CASE WHEN a:15 IS NULL THEN c_cast:12 ELSE c:17 END [as=upsert_c:26]
      │         └── CASE WHEN a:15 IS NULL THEN d_cast:14 ELSE d:18 END [as=upsert_d:27]
      └── projections
           ├── round(upsert_a:24) = upsert_a:24 [as=check1:28]
           └── upsert_b:25[0] > 1 [as=check2:29]

# INSERT...ON CONFLICT case as a prepared statement.
assign-placeholders-build query-args=(1.1, ARRAY[0.95], ARRAY[0.99])
INSERT INTO decimals (a, b) VALUES ($1, $2)
ON CONFLICT (a)
DO UPDATE SET b=$3
----
upsert decimals
 ├── arbiter indexes: decimals_pkey
 ├── columns: <none>
 ├── canary column: a:15
 ├── fetch columns: a:15 b:16 c:17 d:18
 ├── insert-mapping:
 │    ├── a_cast:9 => a:1
 │    ├── b_cast:10 => b:2
 │    ├── c_cast:12 => c:3
 │    └── d_cast:14 => d:4
 ├── update-mapping:
 │    └── upsert_b:25 => b:2
 ├── check columns: check1:28 check2:29
 └── project
      ├── columns: check1:28 check2:29 a_cast:9!null b_cast:10!null c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20 b_cast:22!null d_comp:23 upsert_a:24 upsert_b:25!null upsert_c:26 upsert_d:27
      ├── project
      │    ├── columns: upsert_a:24 upsert_b:25!null upsert_c:26 upsert_d:27 a_cast:9!null b_cast:10!null c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20 b_cast:22!null d_comp:23
      │    ├── project
      │    │    ├── columns: d_comp:23 a_cast:9!null b_cast:10!null c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20 b_cast:22!null
      │    │    ├── project
      │    │    │    ├── columns: b_cast:22!null a_cast:9!null b_cast:10!null c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20
      │    │    │    ├── project
      │    │    │    │    ├── columns: b_new:21!null a_cast:9!null b_cast:10!null c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20
      │    │    │    │    ├── left-join (hash)
      │    │    │    │    │    ├── columns: a_cast:9!null b_cast:10!null c_cast:12!null d_cast:14!null a:15 b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20
      │    │    │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    │    │    ├── columns: a_cast:9!null b_cast:10!null c_cast:12!null d_cast:14!null
      │    │    │    │    │    │    ├── grouping columns: a_cast:9!null
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: d_cast:14!null a_cast:9!null b_cast:10!null c_cast:12!null
      │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    ├── columns: d_comp:13!null a_cast:9!null b_cast:10!null c_cast:12!null
      │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    ├── columns: c_cast:12!null a_cast:9!null b_cast:10!null
      │    │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    │    ├── columns: c_default:11!null a_cast:9!null b_cast:10!null
      │    │    │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    │    │    ├── columns: a_cast:9!null b_cast:10!null
      │    │    │    │    │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    │    │    │    │    ├── columns: column1:7!null column2:8!null
      │    │    │    │    │    │    │    │    │    │    │    │    └── (1.1, ARRAY[0.95])
      │    │    │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │    │    │         ├── assignment-cast: DECIMAL(10) [as=a_cast:9]
      │    │    │    │    │    │    │    │    │    │    │         │    └── column1:7
      │    │    │    │    │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(5,1)[] [as=b_cast:10]
      │    │    │    │    │    │    │    │    │    │    │              └── column2:8
      │    │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │    │         └── 1.23 [as=c_default:11]
      │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10,1) [as=c_cast:12]
      │    │    │    │    │    │    │    │    │              └── c_default:11
      │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │         └── a_cast:9 + c_cast:12 [as=d_comp:13]
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10,1) [as=d_cast:14]
      │    │    │    │    │    │    │              └── d_comp:13
      │    │    │    │    │    │    └── aggregations
      │    │    │    │    │    │         ├── first-agg [as=b_cast:10]
      │    │    │    │    │    │         │    └── b_cast:10
      │    │    │    │    │    │         ├── first-agg [as=c_cast:12]
      │    │    │    │    │    │         │    └── c_cast:12
      │    │    │    │    │    │         └── first-agg [as=d_cast:14]
      │    │    │    │    │    │              └── d_cast:14
      │    │    │    │    │    ├── scan decimals
      │    │    │    │    │    │    ├── columns: a:15!null b:16 c:17 d:18 crdb_internal_mvcc_timestamp:19 tableoid:20
      │    │    │    │    │    │    ├── computed column expressions
      │    │    │    │    │    │    │    └── d:18
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10,1)
      │    │    │    │    │    │    │              └── a:15 + c:17
      │    │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    │    └── filters
      │    │    │    │    │         └── a_cast:9 = a:15
      │    │    │    │    └── projections
      │    │    │    │         └── ARRAY[0.99] [as=b_new:21]
      │    │    │    └── projections
      │    │    │         └── assignment-cast: DECIMAL(5,1)[] [as=b_cast:22]
      │    │    │              └── b_new:21
      │    │    └── projections
      │    │         └── a:15 + c:17 [as=d_comp:23]
      │    └── projections
      │         ├── CASE WHEN a:15 IS NULL THEN a_cast:9 ELSE a:15 END [as=upsert_a:24]
      │         ├── CASE WHEN a:15 IS NULL THEN b_cast:10 ELSE b_cast:22 END [as=upsert_b:25]
      │         ├── CASE WHEN a:15 IS NULL THEN c_cast:12 ELSE c:17 END [as=upsert_c:26]
      │         └── CASE WHEN a:15 IS NULL THEN d_cast:14 ELSE d:18 END [as=upsert_d:27]
      └── projections
           ├── round(upsert_a:24) = upsert_a:24 [as=check1:28]
           └── upsert_b:25[0] > 1 [as=check2:29]

# Test standard upsert with some types that require assignment casts.
build
UPSERT INTO assn_cast (k, c, qc, i, s) VALUES (1.0::DECIMAL, ' ', 'foo', '1', 2)
----
upsert assn_cast
 ├── arbiter indexes: assn_cast_pkey
 ├── columns: <none>
 ├── canary column: k:22
 ├── fetch columns: k:22 c:23 qc:24 i:25 s:26 d:27 d_comp:28
 ├── insert-mapping:
 │    ├── k_cast:15 => k:1
 │    ├── c_cast:16 => c:2
 │    ├── qc_cast:17 => qc:3
 │    ├── column4:13 => i:4
 │    ├── s_cast:18 => s:5
 │    ├── d_default:19 => d:6
 │    └── d_comp_cast:21 => d_comp:7
 ├── update-mapping:
 │    ├── c_cast:16 => c:2
 │    ├── qc_cast:17 => qc:3
 │    ├── column4:13 => i:4
 │    └── s_cast:18 => s:5
 └── project
      ├── columns: upsert_k:32 upsert_d:33 upsert_d_comp:34 column4:13!null k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null d_default:19 d_comp_cast:21 k:22 c:23 qc:24 i:25 s:26 d:27 d_comp:28 crdb_internal_mvcc_timestamp:29 tableoid:30 d_comp_comp:31
      ├── project
      │    ├── columns: d_comp_comp:31 column4:13!null k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null d_default:19 d_comp_cast:21 k:22 c:23 qc:24 i:25 s:26 d:27 d_comp:28 crdb_internal_mvcc_timestamp:29 tableoid:30
      │    ├── left-join (hash)
      │    │    ├── columns: column4:13!null k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null d_default:19 d_comp_cast:21 k:22 c:23 qc:24 i:25 s:26 d:27 d_comp:28 crdb_internal_mvcc_timestamp:29 tableoid:30
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column4:13!null k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null d_default:19 d_comp_cast:21
      │    │    │    ├── grouping columns: k_cast:15!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: d_comp_cast:21 column4:13!null k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null d_default:19
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: d_comp_comp:20 column4:13!null k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null d_default:19
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: d_default:19 column4:13!null k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null column4:13!null
      │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    ├── columns: column1:10!null column2:11!null column3:12!null column4:13!null column5:14!null
      │    │    │    │    │    │    │    │    └── (1.0, '', 'foo', 1, 2)
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         ├── assignment-cast: INT8 [as=k_cast:15]
      │    │    │    │    │    │    │         │    └── column1:10
      │    │    │    │    │    │    │         ├── assignment-cast: CHAR [as=c_cast:16]
      │    │    │    │    │    │    │         │    └── column2:11
      │    │    │    │    │    │    │         ├── assignment-cast: "char" [as=qc_cast:17]
      │    │    │    │    │    │    │         │    └── column3:12
      │    │    │    │    │    │    │         └── assignment-cast: STRING [as=s_cast:18]
      │    │    │    │    │    │    │              └── column5:14
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── NULL::DECIMAL(10) [as=d_default:19]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── d_default:19 + 10.0 [as=d_comp_comp:20]
      │    │    │    │    └── projections
      │    │    │    │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:21]
      │    │    │    │              └── d_comp_comp:20
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=c_cast:16]
      │    │    │         │    └── c_cast:16
      │    │    │         ├── first-agg [as=qc_cast:17]
      │    │    │         │    └── qc_cast:17
      │    │    │         ├── first-agg [as=column4:13]
      │    │    │         │    └── column4:13
      │    │    │         ├── first-agg [as=s_cast:18]
      │    │    │         │    └── s_cast:18
      │    │    │         ├── first-agg [as=d_default:19]
      │    │    │         │    └── d_default:19
      │    │    │         └── first-agg [as=d_comp_cast:21]
      │    │    │              └── d_comp_cast:21
      │    │    ├── scan assn_cast
      │    │    │    ├── columns: k:22!null c:23 qc:24 i:25 s:26 d:27 d_comp:28 crdb_internal_mvcc_timestamp:29 tableoid:30
      │    │    │    ├── computed column expressions
      │    │    │    │    └── d_comp:28
      │    │    │    │         └── assignment-cast: DECIMAL(10)
      │    │    │    │              └── d:27 + 10.0
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── k_cast:15 = k:22
      │    └── projections
      │         └── d:27 + 10.0 [as=d_comp_comp:31]
      └── projections
           ├── CASE WHEN k:22 IS NULL THEN k_cast:15 ELSE k:22 END [as=upsert_k:32]
           ├── CASE WHEN k:22 IS NULL THEN d_default:19 ELSE d:27 END [as=upsert_d:33]
           └── CASE WHEN k:22 IS NULL THEN d_comp_cast:21 ELSE d_comp:28 END [as=upsert_d_comp:34]

# Test standard prepared upsert with some types that require assignment casts.
assign-placeholders-build query-args=(1.0, ' ', 'foo', '1')
UPSERT INTO assn_cast (k, c, qc, i) VALUES ($1::DECIMAL, $2, $3, $4)
----
upsert assn_cast
 ├── arbiter indexes: assn_cast_pkey
 ├── columns: <none>
 ├── canary column: k:21
 ├── fetch columns: k:21 c:22 qc:23 i:24 s:25 d:26 d_comp:27
 ├── insert-mapping:
 │    ├── k_cast:14 => k:1
 │    ├── c_cast:15 => c:2
 │    ├── qc_cast:16 => qc:3
 │    ├── column4:13 => i:4
 │    ├── s_default:17 => s:5
 │    ├── d_default:18 => d:6
 │    └── d_comp_cast:20 => d_comp:7
 ├── update-mapping:
 │    ├── c_cast:15 => c:2
 │    ├── qc_cast:16 => qc:3
 │    └── column4:13 => i:4
 └── project
      ├── columns: upsert_k:31 upsert_s:32 upsert_d:33 upsert_d_comp:34 column4:13!null k_cast:14 c_cast:15!null qc_cast:16!null s_default:17 d_default:18 d_comp_cast:20 k:21 c:22 qc:23 i:24 s:25 d:26 d_comp:27 crdb_internal_mvcc_timestamp:28 tableoid:29 d_comp_comp:30
      ├── project
      │    ├── columns: d_comp_comp:30 column4:13!null k_cast:14 c_cast:15!null qc_cast:16!null s_default:17 d_default:18 d_comp_cast:20 k:21 c:22 qc:23 i:24 s:25 d:26 d_comp:27 crdb_internal_mvcc_timestamp:28 tableoid:29
      │    ├── left-join (hash)
      │    │    ├── columns: column4:13!null k_cast:14 c_cast:15!null qc_cast:16!null s_default:17 d_default:18 d_comp_cast:20 k:21 c:22 qc:23 i:24 s:25 d:26 d_comp:27 crdb_internal_mvcc_timestamp:28 tableoid:29
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column4:13!null k_cast:14 c_cast:15!null qc_cast:16!null s_default:17 d_default:18 d_comp_cast:20
      │    │    │    ├── grouping columns: k_cast:14
      │    │    │    ├── project
      │    │    │    │    ├── columns: d_comp_cast:20 column4:13!null k_cast:14 c_cast:15!null qc_cast:16!null s_default:17 d_default:18
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: d_comp_comp:19 column4:13!null k_cast:14 c_cast:15!null qc_cast:16!null s_default:17 d_default:18
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: s_default:17 d_default:18 column4:13!null k_cast:14 c_cast:15!null qc_cast:16!null
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: k_cast:14 c_cast:15!null qc_cast:16!null column4:13!null
      │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    ├── columns: column1:10 column2:11!null column3:12!null column4:13!null
      │    │    │    │    │    │    │    │    └── (1.0::DECIMAL, '', 'foo', 1)
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         ├── assignment-cast: INT8 [as=k_cast:14]
      │    │    │    │    │    │    │         │    └── column1:10
      │    │    │    │    │    │    │         ├── assignment-cast: CHAR [as=c_cast:15]
      │    │    │    │    │    │    │         │    └── column2:11
      │    │    │    │    │    │    │         └── assignment-cast: "char" [as=qc_cast:16]
      │    │    │    │    │    │    │              └── column3:12
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         ├── NULL::STRING [as=s_default:17]
      │    │    │    │    │    │         └── NULL::DECIMAL(10) [as=d_default:18]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── d_default:18 + 10.0 [as=d_comp_comp:19]
      │    │    │    │    └── projections
      │    │    │    │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:20]
      │    │    │    │              └── d_comp_comp:19
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=c_cast:15]
      │    │    │         │    └── c_cast:15
      │    │    │         ├── first-agg [as=qc_cast:16]
      │    │    │         │    └── qc_cast:16
      │    │    │         ├── first-agg [as=column4:13]
      │    │    │         │    └── column4:13
      │    │    │         ├── first-agg [as=s_default:17]
      │    │    │         │    └── s_default:17
      │    │    │         ├── first-agg [as=d_default:18]
      │    │    │         │    └── d_default:18
      │    │    │         └── first-agg [as=d_comp_cast:20]
      │    │    │              └── d_comp_cast:20
      │    │    ├── scan assn_cast
      │    │    │    ├── columns: k:21!null c:22 qc:23 i:24 s:25 d:26 d_comp:27 crdb_internal_mvcc_timestamp:28 tableoid:29
      │    │    │    ├── computed column expressions
      │    │    │    │    └── d_comp:27
      │    │    │    │         └── assignment-cast: DECIMAL(10)
      │    │    │    │              └── d:26 + 10.0
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── k_cast:14 = k:21
      │    └── projections
      │         └── d:26 + 10.0 [as=d_comp_comp:30]
      └── projections
           ├── CASE WHEN k:21 IS NULL THEN k_cast:14 ELSE k:21 END [as=upsert_k:31]
           ├── CASE WHEN k:21 IS NULL THEN s_default:17 ELSE s:25 END [as=upsert_s:32]
           ├── CASE WHEN k:21 IS NULL THEN d_default:18 ELSE d:26 END [as=upsert_d:33]
           └── CASE WHEN k:21 IS NULL THEN d_comp_cast:20 ELSE d_comp:27 END [as=upsert_d_comp:34]

# Test standard insert-do-nothing with some types that require assignment casts.
build
INSERT INTO assn_cast (k, c, qc, i, s) VALUES (1.0::DECIMAL, ' ', 'foo', '1', 2) ON CONFLICT DO NOTHING
----
insert assn_cast
 ├── arbiter indexes: assn_cast_pkey
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── k_cast:15 => k:1
 │    ├── c_cast:16 => c:2
 │    ├── qc_cast:17 => qc:3
 │    ├── column4:13 => i:4
 │    ├── s_cast:18 => s:5
 │    ├── d_default:19 => d:6
 │    └── d_comp_cast:21 => d_comp:7
 └── upsert-distinct-on
      ├── columns: column4:13!null k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null d_default:19 d_comp_cast:21
      ├── grouping columns: k_cast:15!null
      ├── anti-join (hash)
      │    ├── columns: column4:13!null k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null d_default:19 d_comp_cast:21
      │    ├── project
      │    │    ├── columns: d_comp_cast:21 column4:13!null k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null d_default:19
      │    │    ├── project
      │    │    │    ├── columns: d_comp_comp:20 column4:13!null k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null d_default:19
      │    │    │    ├── project
      │    │    │    │    ├── columns: d_default:19 column4:13!null k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: k_cast:15!null c_cast:16!null qc_cast:17!null s_cast:18!null column4:13!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:10!null column2:11!null column3:12!null column4:13!null column5:14!null
      │    │    │    │    │    │    └── (1.0, '', 'foo', 1, 2)
      │    │    │    │    │    └── projections
      │    │    │    │    │         ├── assignment-cast: INT8 [as=k_cast:15]
      │    │    │    │    │         │    └── column1:10
      │    │    │    │    │         ├── assignment-cast: CHAR [as=c_cast:16]
      │    │    │    │    │         │    └── column2:11
      │    │    │    │    │         ├── assignment-cast: "char" [as=qc_cast:17]
      │    │    │    │    │         │    └── column3:12
      │    │    │    │    │         └── assignment-cast: STRING [as=s_cast:18]
      │    │    │    │    │              └── column5:14
      │    │    │    │    └── projections
      │    │    │    │         └── NULL::DECIMAL(10) [as=d_default:19]
      │    │    │    └── projections
      │    │    │         └── d_default:19 + 10.0 [as=d_comp_comp:20]
      │    │    └── projections
      │    │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:21]
      │    │              └── d_comp_comp:20
      │    ├── scan assn_cast
      │    │    ├── columns: k:22!null c:23 qc:24 i:25 s:26 d:27 d_comp:28
      │    │    ├── computed column expressions
      │    │    │    └── d_comp:28
      │    │    │         └── assignment-cast: DECIMAL(10)
      │    │    │              └── d:27 + 10.0
      │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    └── filters
      │         └── k_cast:15 = k:22
      └── aggregations
           ├── first-agg [as=c_cast:16]
           │    └── c_cast:16
           ├── first-agg [as=qc_cast:17]
           │    └── qc_cast:17
           ├── first-agg [as=column4:13]
           │    └── column4:13
           ├── first-agg [as=s_cast:18]
           │    └── s_cast:18
           ├── first-agg [as=d_default:19]
           │    └── d_default:19
           └── first-agg [as=d_comp_cast:21]
                └── d_comp_cast:21

# Test standard insert-do-update with some types that require assignment casts.
build
INSERT INTO assn_cast (k, c, qc, i, s) VALUES (1.0::DECIMAL, 'a', 'b', 1, 'c')
ON CONFLICT (k) DO UPDATE SET c = ' ', qc = 'foo', i = '1', s = 2
----
upsert assn_cast
 ├── arbiter indexes: assn_cast_pkey
 ├── columns: <none>
 ├── canary column: k:21
 ├── fetch columns: k:21 c:22 qc:23 i:24 s:25 d:26 d_comp:27
 ├── insert-mapping:
 │    ├── k_cast:15 => k:1
 │    ├── c_cast:16 => c:2
 │    ├── qc_cast:17 => qc:3
 │    ├── column4:13 => i:4
 │    ├── column5:14 => s:5
 │    ├── d_default:18 => d:6
 │    └── d_comp_cast:20 => d_comp:7
 ├── update-mapping:
 │    ├── upsert_c:39 => c:2
 │    ├── upsert_qc:40 => qc:3
 │    ├── upsert_i:41 => i:4
 │    └── upsert_s:42 => s:5
 └── project
      ├── columns: upsert_k:38 upsert_c:39!null upsert_qc:40!null upsert_i:41!null upsert_s:42!null upsert_d:43 upsert_d_comp:44 column4:13!null column5:14!null k_cast:15!null c_cast:16!null qc_cast:17!null d_default:18 d_comp_cast:20 k:21 c:22 qc:23 i:24 s:25 d:26 d_comp:27 crdb_internal_mvcc_timestamp:28 tableoid:29 i_new:32!null c_cast:34!null qc_cast:35!null s_cast:36!null d_comp_comp:37
      ├── project
      │    ├── columns: d_comp_comp:37 column4:13!null column5:14!null k_cast:15!null c_cast:16!null qc_cast:17!null d_default:18 d_comp_cast:20 k:21 c:22 qc:23 i:24 s:25 d:26 d_comp:27 crdb_internal_mvcc_timestamp:28 tableoid:29 i_new:32!null c_cast:34!null qc_cast:35!null s_cast:36!null
      │    ├── project
      │    │    ├── columns: c_cast:34!null qc_cast:35!null s_cast:36!null column4:13!null column5:14!null k_cast:15!null c_cast:16!null qc_cast:17!null d_default:18 d_comp_cast:20 k:21 c:22 qc:23 i:24 s:25 d:26 d_comp:27 crdb_internal_mvcc_timestamp:28 tableoid:29 i_new:32!null
      │    │    ├── project
      │    │    │    ├── columns: c_new:30!null qc_new:31!null i_new:32!null s_new:33!null column4:13!null column5:14!null k_cast:15!null c_cast:16!null qc_cast:17!null d_default:18 d_comp_cast:20 k:21 c:22 qc:23 i:24 s:25 d:26 d_comp:27 crdb_internal_mvcc_timestamp:28 tableoid:29
      │    │    │    ├── left-join (hash)
      │    │    │    │    ├── columns: column4:13!null column5:14!null k_cast:15!null c_cast:16!null qc_cast:17!null d_default:18 d_comp_cast:20 k:21 c:22 qc:23 i:24 s:25 d:26 d_comp:27 crdb_internal_mvcc_timestamp:28 tableoid:29
      │    │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    │    ├── columns: column4:13!null column5:14!null k_cast:15!null c_cast:16!null qc_cast:17!null d_default:18 d_comp_cast:20
      │    │    │    │    │    ├── grouping columns: k_cast:15!null
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: d_comp_cast:20 column4:13!null column5:14!null k_cast:15!null c_cast:16!null qc_cast:17!null d_default:18
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: d_comp_comp:19 column4:13!null column5:14!null k_cast:15!null c_cast:16!null qc_cast:17!null d_default:18
      │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    ├── columns: d_default:18 column4:13!null column5:14!null k_cast:15!null c_cast:16!null qc_cast:17!null
      │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    ├── columns: k_cast:15!null c_cast:16!null qc_cast:17!null column4:13!null column5:14!null
      │    │    │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    │    │    ├── columns: column1:10!null column2:11!null column3:12!null column4:13!null column5:14!null
      │    │    │    │    │    │    │    │    │    │    └── (1.0, 'a', 'b', 1, 'c')
      │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │         ├── assignment-cast: INT8 [as=k_cast:15]
      │    │    │    │    │    │    │    │    │         │    └── column1:10
      │    │    │    │    │    │    │    │    │         ├── assignment-cast: CHAR [as=c_cast:16]
      │    │    │    │    │    │    │    │    │         │    └── column2:11
      │    │    │    │    │    │    │    │    │         └── assignment-cast: "char" [as=qc_cast:17]
      │    │    │    │    │    │    │    │    │              └── column3:12
      │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │         └── NULL::DECIMAL(10) [as=d_default:18]
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── d_default:18 + 10.0 [as=d_comp_comp:19]
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:20]
      │    │    │    │    │    │              └── d_comp_comp:19
      │    │    │    │    │    └── aggregations
      │    │    │    │    │         ├── first-agg [as=c_cast:16]
      │    │    │    │    │         │    └── c_cast:16
      │    │    │    │    │         ├── first-agg [as=qc_cast:17]
      │    │    │    │    │         │    └── qc_cast:17
      │    │    │    │    │         ├── first-agg [as=column4:13]
      │    │    │    │    │         │    └── column4:13
      │    │    │    │    │         ├── first-agg [as=column5:14]
      │    │    │    │    │         │    └── column5:14
      │    │    │    │    │         ├── first-agg [as=d_default:18]
      │    │    │    │    │         │    └── d_default:18
      │    │    │    │    │         └── first-agg [as=d_comp_cast:20]
      │    │    │    │    │              └── d_comp_cast:20
      │    │    │    │    ├── scan assn_cast
      │    │    │    │    │    ├── columns: k:21!null c:22 qc:23 i:24 s:25 d:26 d_comp:27 crdb_internal_mvcc_timestamp:28 tableoid:29
      │    │    │    │    │    ├── computed column expressions
      │    │    │    │    │    │    └── d_comp:27
      │    │    │    │    │    │         └── assignment-cast: DECIMAL(10)
      │    │    │    │    │    │              └── d:26 + 10.0
      │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    └── filters
      │    │    │    │         └── k_cast:15 = k:21
      │    │    │    └── projections
      │    │    │         ├── '' [as=c_new:30]
      │    │    │         ├── 'foo' [as=qc_new:31]
      │    │    │         ├── 1 [as=i_new:32]
      │    │    │         └── 2 [as=s_new:33]
      │    │    └── projections
      │    │         ├── assignment-cast: CHAR [as=c_cast:34]
      │    │         │    └── c_new:30
      │    │         ├── assignment-cast: "char" [as=qc_cast:35]
      │    │         │    └── qc_new:31
      │    │         └── assignment-cast: STRING [as=s_cast:36]
      │    │              └── s_new:33
      │    └── projections
      │         └── d:26 + 10.0 [as=d_comp_comp:37]
      └── projections
           ├── CASE WHEN k:21 IS NULL THEN k_cast:15 ELSE k:21 END [as=upsert_k:38]
           ├── CASE WHEN k:21 IS NULL THEN c_cast:16 ELSE c_cast:34 END [as=upsert_c:39]
           ├── CASE WHEN k:21 IS NULL THEN qc_cast:17 ELSE qc_cast:35 END [as=upsert_qc:40]
           ├── CASE WHEN k:21 IS NULL THEN column4:13 ELSE i_new:32 END [as=upsert_i:41]
           ├── CASE WHEN k:21 IS NULL THEN column5:14 ELSE s_cast:36 END [as=upsert_s:42]
           ├── CASE WHEN k:21 IS NULL THEN d_default:18 ELSE d:26 END [as=upsert_d:43]
           └── CASE WHEN k:21 IS NULL THEN d_comp_cast:20 ELSE d_comp:27 END [as=upsert_d_comp:44]

# Test upsert to DEFAULT that requires an assignment cast.
build
UPSERT INTO assn_cast (k, i) VALUES (1, DEFAULT)
----
upsert assn_cast
 ├── arbiter indexes: assn_cast_pkey
 ├── columns: <none>
 ├── canary column: k:19
 ├── fetch columns: k:19 c:20 qc:21 i:22 s:23 d:24 d_comp:25
 ├── insert-mapping:
 │    ├── column1:10 => k:1
 │    ├── c_default:13 => c:2
 │    ├── qc_default:14 => qc:3
 │    ├── i_cast:12 => i:4
 │    ├── s_default:15 => s:5
 │    ├── d_default:16 => d:6
 │    └── d_comp_cast:18 => d_comp:7
 ├── update-mapping:
 │    └── i_cast:12 => i:4
 └── project
      ├── columns: upsert_k:29 upsert_c:30 upsert_qc:31 upsert_s:32 upsert_d:33 upsert_d_comp:34 column1:10!null i_cast:12 c_default:13 qc_default:14 s_default:15 d_default:16 d_comp_cast:18 k:19 c:20 qc:21 i:22 s:23 d:24 d_comp:25 crdb_internal_mvcc_timestamp:26 tableoid:27 d_comp_comp:28
      ├── project
      │    ├── columns: d_comp_comp:28 column1:10!null i_cast:12 c_default:13 qc_default:14 s_default:15 d_default:16 d_comp_cast:18 k:19 c:20 qc:21 i:22 s:23 d:24 d_comp:25 crdb_internal_mvcc_timestamp:26 tableoid:27
      │    ├── left-join (hash)
      │    │    ├── columns: column1:10!null i_cast:12 c_default:13 qc_default:14 s_default:15 d_default:16 d_comp_cast:18 k:19 c:20 qc:21 i:22 s:23 d:24 d_comp:25 crdb_internal_mvcc_timestamp:26 tableoid:27
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:10!null i_cast:12 c_default:13 qc_default:14 s_default:15 d_default:16 d_comp_cast:18
      │    │    │    ├── grouping columns: column1:10!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: d_comp_cast:18 column1:10!null i_cast:12 c_default:13 qc_default:14 s_default:15 d_default:16
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: d_comp_comp:17 column1:10!null i_cast:12 c_default:13 qc_default:14 s_default:15 d_default:16
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: c_default:13 qc_default:14 s_default:15 d_default:16 column1:10!null i_cast:12
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: i_cast:12 column1:10!null
      │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    ├── columns: column1:10!null column2:11
      │    │    │    │    │    │    │    │    └── (1, 10::INT2)
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── assignment-cast: INT8 [as=i_cast:12]
      │    │    │    │    │    │    │              └── column2:11
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         ├── NULL::CHAR [as=c_default:13]
      │    │    │    │    │    │         ├── NULL::"char" [as=qc_default:14]
      │    │    │    │    │    │         ├── NULL::STRING [as=s_default:15]
      │    │    │    │    │    │         └── NULL::DECIMAL(10) [as=d_default:16]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── d_default:16 + 10.0 [as=d_comp_comp:17]
      │    │    │    │    └── projections
      │    │    │    │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:18]
      │    │    │    │              └── d_comp_comp:17
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=i_cast:12]
      │    │    │         │    └── i_cast:12
      │    │    │         ├── first-agg [as=c_default:13]
      │    │    │         │    └── c_default:13
      │    │    │         ├── first-agg [as=qc_default:14]
      │    │    │         │    └── qc_default:14
      │    │    │         ├── first-agg [as=s_default:15]
      │    │    │         │    └── s_default:15
      │    │    │         ├── first-agg [as=d_default:16]
      │    │    │         │    └── d_default:16
      │    │    │         └── first-agg [as=d_comp_cast:18]
      │    │    │              └── d_comp_cast:18
      │    │    ├── scan assn_cast
      │    │    │    ├── columns: k:19!null c:20 qc:21 i:22 s:23 d:24 d_comp:25 crdb_internal_mvcc_timestamp:26 tableoid:27
      │    │    │    ├── computed column expressions
      │    │    │    │    └── d_comp:25
      │    │    │    │         └── assignment-cast: DECIMAL(10)
      │    │    │    │              └── d:24 + 10.0
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:10 = k:19
      │    └── projections
      │         └── d:24 + 10.0 [as=d_comp_comp:28]
      └── projections
           ├── CASE WHEN k:19 IS NULL THEN column1:10 ELSE k:19 END [as=upsert_k:29]
           ├── CASE WHEN k:19 IS NULL THEN c_default:13 ELSE c:20 END [as=upsert_c:30]
           ├── CASE WHEN k:19 IS NULL THEN qc_default:14 ELSE qc:21 END [as=upsert_qc:31]
           ├── CASE WHEN k:19 IS NULL THEN s_default:15 ELSE s:23 END [as=upsert_s:32]
           ├── CASE WHEN k:19 IS NULL THEN d_default:16 ELSE d:24 END [as=upsert_d:33]
           └── CASE WHEN k:19 IS NULL THEN d_comp_cast:18 ELSE d_comp:25 END [as=upsert_d_comp:34]

# Test insert-do-update to DEFAULT that requires an assignment cast.
build
INSERT INTO assn_cast (k, i) VALUES (1, 2) ON CONFLICT (k) DO UPDATE SET i = DEFAULT
----
upsert assn_cast
 ├── arbiter indexes: assn_cast_pkey
 ├── columns: <none>
 ├── canary column: k:18
 ├── fetch columns: k:18 c:19 qc:20 i:21 s:22 d:23 d_comp:24
 ├── insert-mapping:
 │    ├── column1:10 => k:1
 │    ├── c_default:12 => c:2
 │    ├── qc_default:13 => qc:3
 │    ├── column2:11 => i:4
 │    ├── s_default:14 => s:5
 │    ├── d_default:15 => d:6
 │    └── d_comp_cast:17 => d_comp:7
 ├── update-mapping:
 │    └── upsert_i:33 => i:4
 └── project
      ├── columns: upsert_k:30 upsert_c:31 upsert_qc:32 upsert_i:33!null upsert_s:34 upsert_d:35 upsert_d_comp:36 column1:10!null column2:11!null c_default:12 qc_default:13 s_default:14 d_default:15 d_comp_cast:17 k:18 c:19 qc:20 i:21 s:22 d:23 d_comp:24 crdb_internal_mvcc_timestamp:25 tableoid:26 i_cast:28!null d_comp_comp:29
      ├── project
      │    ├── columns: d_comp_comp:29 column1:10!null column2:11!null c_default:12 qc_default:13 s_default:14 d_default:15 d_comp_cast:17 k:18 c:19 qc:20 i:21 s:22 d:23 d_comp:24 crdb_internal_mvcc_timestamp:25 tableoid:26 i_cast:28!null
      │    ├── project
      │    │    ├── columns: i_cast:28!null column1:10!null column2:11!null c_default:12 qc_default:13 s_default:14 d_default:15 d_comp_cast:17 k:18 c:19 qc:20 i:21 s:22 d:23 d_comp:24 crdb_internal_mvcc_timestamp:25 tableoid:26
      │    │    ├── project
      │    │    │    ├── columns: i_new:27!null column1:10!null column2:11!null c_default:12 qc_default:13 s_default:14 d_default:15 d_comp_cast:17 k:18 c:19 qc:20 i:21 s:22 d:23 d_comp:24 crdb_internal_mvcc_timestamp:25 tableoid:26
      │    │    │    ├── left-join (hash)
      │    │    │    │    ├── columns: column1:10!null column2:11!null c_default:12 qc_default:13 s_default:14 d_default:15 d_comp_cast:17 k:18 c:19 qc:20 i:21 s:22 d:23 d_comp:24 crdb_internal_mvcc_timestamp:25 tableoid:26
      │    │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    │    ├── columns: column1:10!null column2:11!null c_default:12 qc_default:13 s_default:14 d_default:15 d_comp_cast:17
      │    │    │    │    │    ├── grouping columns: column1:10!null
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: d_comp_cast:17 column1:10!null column2:11!null c_default:12 qc_default:13 s_default:14 d_default:15
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: d_comp_comp:16 column1:10!null column2:11!null c_default:12 qc_default:13 s_default:14 d_default:15
      │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    ├── columns: c_default:12 qc_default:13 s_default:14 d_default:15 column1:10!null column2:11!null
      │    │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    │    ├── columns: column1:10!null column2:11!null
      │    │    │    │    │    │    │    │    │    └── (1, 2)
      │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │         ├── NULL::CHAR [as=c_default:12]
      │    │    │    │    │    │    │    │         ├── NULL::"char" [as=qc_default:13]
      │    │    │    │    │    │    │    │         ├── NULL::STRING [as=s_default:14]
      │    │    │    │    │    │    │    │         └── NULL::DECIMAL(10) [as=d_default:15]
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── d_default:15 + 10.0 [as=d_comp_comp:16]
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:17]
      │    │    │    │    │    │              └── d_comp_comp:16
      │    │    │    │    │    └── aggregations
      │    │    │    │    │         ├── first-agg [as=column2:11]
      │    │    │    │    │         │    └── column2:11
      │    │    │    │    │         ├── first-agg [as=c_default:12]
      │    │    │    │    │         │    └── c_default:12
      │    │    │    │    │         ├── first-agg [as=qc_default:13]
      │    │    │    │    │         │    └── qc_default:13
      │    │    │    │    │         ├── first-agg [as=s_default:14]
      │    │    │    │    │         │    └── s_default:14
      │    │    │    │    │         ├── first-agg [as=d_default:15]
      │    │    │    │    │         │    └── d_default:15
      │    │    │    │    │         └── first-agg [as=d_comp_cast:17]
      │    │    │    │    │              └── d_comp_cast:17
      │    │    │    │    ├── scan assn_cast
      │    │    │    │    │    ├── columns: k:18!null c:19 qc:20 i:21 s:22 d:23 d_comp:24 crdb_internal_mvcc_timestamp:25 tableoid:26
      │    │    │    │    │    ├── computed column expressions
      │    │    │    │    │    │    └── d_comp:24
      │    │    │    │    │    │         └── assignment-cast: DECIMAL(10)
      │    │    │    │    │    │              └── d:23 + 10.0
      │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    └── filters
      │    │    │    │         └── column1:10 = k:18
      │    │    │    └── projections
      │    │    │         └── 10::INT2 [as=i_new:27]
      │    │    └── projections
      │    │         └── assignment-cast: INT8 [as=i_cast:28]
      │    │              └── i_new:27
      │    └── projections
      │         └── d:23 + 10.0 [as=d_comp_comp:29]
      └── projections
           ├── CASE WHEN k:18 IS NULL THEN column1:10 ELSE k:18 END [as=upsert_k:30]
           ├── CASE WHEN k:18 IS NULL THEN c_default:12 ELSE c:19 END [as=upsert_c:31]
           ├── CASE WHEN k:18 IS NULL THEN qc_default:13 ELSE qc:20 END [as=upsert_qc:32]
           ├── CASE WHEN k:18 IS NULL THEN column2:11 ELSE i_cast:28 END [as=upsert_i:33]
           ├── CASE WHEN k:18 IS NULL THEN s_default:14 ELSE s:22 END [as=upsert_s:34]
           ├── CASE WHEN k:18 IS NULL THEN d_default:15 ELSE d:23 END [as=upsert_d:35]
           └── CASE WHEN k:18 IS NULL THEN d_comp_cast:17 ELSE d_comp:24 END [as=upsert_d_comp:36]

# Test upsert to a column that requires an assignment cast and a computed column
# that depends on the new value.
build
UPSERT INTO assn_cast (k, d) VALUES (1, 1.45::DECIMAL(10, 2))
----
upsert assn_cast
 ├── arbiter indexes: assn_cast_pkey
 ├── columns: <none>
 ├── canary column: k:20
 ├── fetch columns: k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26
 ├── insert-mapping:
 │    ├── column1:10 => k:1
 │    ├── c_default:13 => c:2
 │    ├── qc_default:14 => qc:3
 │    ├── i_cast:17 => i:4
 │    ├── s_default:16 => s:5
 │    ├── d_cast:12 => d:6
 │    └── d_comp_cast:19 => d_comp:7
 ├── update-mapping:
 │    ├── d_cast:12 => d:6
 │    └── d_comp_cast:19 => d_comp:7
 └── project
      ├── columns: upsert_k:29 upsert_c:30 upsert_qc:31 upsert_i:32 upsert_s:33 column1:10!null d_cast:12 c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19 k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28
      ├── left-join (hash)
      │    ├── columns: column1:10!null d_cast:12 c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19 k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28
      │    ├── ensure-upsert-distinct-on
      │    │    ├── columns: column1:10!null d_cast:12 c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19
      │    │    ├── grouping columns: column1:10!null
      │    │    ├── project
      │    │    │    ├── columns: d_comp_cast:19 column1:10!null d_cast:12 c_default:13 qc_default:14 s_default:16 i_cast:17!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: d_comp_comp:18 column1:10!null d_cast:12 c_default:13 qc_default:14 s_default:16 i_cast:17!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: i_cast:17!null column1:10!null d_cast:12 c_default:13 qc_default:14 s_default:16
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: c_default:13 qc_default:14 i_default:15!null s_default:16 column1:10!null d_cast:12
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: d_cast:12 column1:10!null
      │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    ├── columns: column1:10!null column2:11
      │    │    │    │    │    │    │    │    └── (1, 1.45::DECIMAL(10,2))
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10) [as=d_cast:12]
      │    │    │    │    │    │    │              └── column2:11
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         ├── NULL::CHAR [as=c_default:13]
      │    │    │    │    │    │         ├── NULL::"char" [as=qc_default:14]
      │    │    │    │    │    │         ├── 10::INT2 [as=i_default:15]
      │    │    │    │    │    │         └── NULL::STRING [as=s_default:16]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── assignment-cast: INT8 [as=i_cast:17]
      │    │    │    │    │              └── i_default:15
      │    │    │    │    └── projections
      │    │    │    │         └── d_cast:12 + 10.0 [as=d_comp_comp:18]
      │    │    │    └── projections
      │    │    │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:19]
      │    │    │              └── d_comp_comp:18
      │    │    └── aggregations
      │    │         ├── first-agg [as=d_cast:12]
      │    │         │    └── d_cast:12
      │    │         ├── first-agg [as=c_default:13]
      │    │         │    └── c_default:13
      │    │         ├── first-agg [as=qc_default:14]
      │    │         │    └── qc_default:14
      │    │         ├── first-agg [as=i_cast:17]
      │    │         │    └── i_cast:17
      │    │         ├── first-agg [as=s_default:16]
      │    │         │    └── s_default:16
      │    │         └── first-agg [as=d_comp_cast:19]
      │    │              └── d_comp_cast:19
      │    ├── scan assn_cast
      │    │    ├── columns: k:20!null c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28
      │    │    ├── computed column expressions
      │    │    │    └── d_comp:26
      │    │    │         └── assignment-cast: DECIMAL(10)
      │    │    │              └── d:25 + 10.0
      │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    └── filters
      │         └── column1:10 = k:20
      └── projections
           ├── CASE WHEN k:20 IS NULL THEN column1:10 ELSE k:20 END [as=upsert_k:29]
           ├── CASE WHEN k:20 IS NULL THEN c_default:13 ELSE c:21 END [as=upsert_c:30]
           ├── CASE WHEN k:20 IS NULL THEN qc_default:14 ELSE qc:22 END [as=upsert_qc:31]
           ├── CASE WHEN k:20 IS NULL THEN i_cast:17 ELSE i:23 END [as=upsert_i:32]
           └── CASE WHEN k:20 IS NULL THEN s_default:16 ELSE s:24 END [as=upsert_s:33]

# Test prepared upsert to a column that requires an assignment cast and a
# computed column that depends on the new value.
assign-placeholders-build query-args=(1.45::DECIMAL(10, 2))
UPSERT INTO assn_cast (k, d) VALUES (1, $1)
----
upsert assn_cast
 ├── arbiter indexes: assn_cast_pkey
 ├── columns: <none>
 ├── canary column: k:20
 ├── fetch columns: k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26
 ├── insert-mapping:
 │    ├── column1:10 => k:1
 │    ├── c_default:13 => c:2
 │    ├── qc_default:14 => qc:3
 │    ├── i_cast:17 => i:4
 │    ├── s_default:16 => s:5
 │    ├── d_cast:12 => d:6
 │    └── d_comp_cast:19 => d_comp:7
 ├── update-mapping:
 │    ├── d_cast:12 => d:6
 │    └── d_comp_cast:19 => d_comp:7
 └── project
      ├── columns: upsert_k:29 upsert_c:30 upsert_qc:31 upsert_i:32 upsert_s:33 column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19!null k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28
      ├── left-join (hash)
      │    ├── columns: column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19!null k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28
      │    ├── ensure-upsert-distinct-on
      │    │    ├── columns: column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19!null
      │    │    ├── grouping columns: column1:10!null
      │    │    ├── project
      │    │    │    ├── columns: d_comp_cast:19!null column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: d_comp_comp:18!null column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: i_cast:17!null column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: c_default:13 qc_default:14 i_default:15!null s_default:16 column1:10!null d_cast:12!null
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: d_cast:12!null column1:10!null
      │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    ├── columns: column1:10!null column2:11!null
      │    │    │    │    │    │    │    │    └── (1, 1.45)
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10) [as=d_cast:12]
      │    │    │    │    │    │    │              └── column2:11
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         ├── NULL::CHAR [as=c_default:13]
      │    │    │    │    │    │         ├── NULL::"char" [as=qc_default:14]
      │    │    │    │    │    │         ├── 10::INT2 [as=i_default:15]
      │    │    │    │    │    │         └── NULL::STRING [as=s_default:16]
      │    │    │    │    │    └── projections
      │    │    │    │    │         └── assignment-cast: INT8 [as=i_cast:17]
      │    │    │    │    │              └── i_default:15
      │    │    │    │    └── projections
      │    │    │    │         └── d_cast:12 + 10.0 [as=d_comp_comp:18]
      │    │    │    └── projections
      │    │    │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:19]
      │    │    │              └── d_comp_comp:18
      │    │    └── aggregations
      │    │         ├── first-agg [as=d_cast:12]
      │    │         │    └── d_cast:12
      │    │         ├── first-agg [as=c_default:13]
      │    │         │    └── c_default:13
      │    │         ├── first-agg [as=qc_default:14]
      │    │         │    └── qc_default:14
      │    │         ├── first-agg [as=i_cast:17]
      │    │         │    └── i_cast:17
      │    │         ├── first-agg [as=s_default:16]
      │    │         │    └── s_default:16
      │    │         └── first-agg [as=d_comp_cast:19]
      │    │              └── d_comp_cast:19
      │    ├── scan assn_cast
      │    │    ├── columns: k:20!null c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28
      │    │    ├── computed column expressions
      │    │    │    └── d_comp:26
      │    │    │         └── assignment-cast: DECIMAL(10)
      │    │    │              └── d:25 + 10.0
      │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    └── filters
      │         └── column1:10 = k:20
      └── projections
           ├── CASE WHEN k:20 IS NULL THEN column1:10 ELSE k:20 END [as=upsert_k:29]
           ├── CASE WHEN k:20 IS NULL THEN c_default:13 ELSE c:21 END [as=upsert_c:30]
           ├── CASE WHEN k:20 IS NULL THEN qc_default:14 ELSE qc:22 END [as=upsert_qc:31]
           ├── CASE WHEN k:20 IS NULL THEN i_cast:17 ELSE i:23 END [as=upsert_i:32]
           └── CASE WHEN k:20 IS NULL THEN s_default:16 ELSE s:24 END [as=upsert_s:33]

# Test insert-do-nothing to a column that requires an assignment cast and a
# computed column that depends on the new value.
build
INSERT INTO assn_cast (k, d) VALUES (1, 1.45::DECIMAL(10, 2)) ON CONFLICT DO NOTHING
----
insert assn_cast
 ├── arbiter indexes: assn_cast_pkey
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── column1:10 => k:1
 │    ├── c_default:13 => c:2
 │    ├── qc_default:14 => qc:3
 │    ├── i_cast:17 => i:4
 │    ├── s_default:16 => s:5
 │    ├── d_cast:12 => d:6
 │    └── d_comp_cast:19 => d_comp:7
 └── upsert-distinct-on
      ├── columns: column1:10!null d_cast:12 c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19
      ├── grouping columns: column1:10!null
      ├── anti-join (hash)
      │    ├── columns: column1:10!null d_cast:12 c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19
      │    ├── project
      │    │    ├── columns: d_comp_cast:19 column1:10!null d_cast:12 c_default:13 qc_default:14 s_default:16 i_cast:17!null
      │    │    ├── project
      │    │    │    ├── columns: d_comp_comp:18 column1:10!null d_cast:12 c_default:13 qc_default:14 s_default:16 i_cast:17!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: i_cast:17!null column1:10!null d_cast:12 c_default:13 qc_default:14 s_default:16
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: c_default:13 qc_default:14 i_default:15!null s_default:16 column1:10!null d_cast:12
      │    │    │    │    │    ├── project
      │    │    │    │    │    │    ├── columns: d_cast:12 column1:10!null
      │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    ├── columns: column1:10!null column2:11
      │    │    │    │    │    │    │    └── (1, 1.45::DECIMAL(10,2))
      │    │    │    │    │    │    └── projections
      │    │    │    │    │    │         └── assignment-cast: DECIMAL(10) [as=d_cast:12]
      │    │    │    │    │    │              └── column2:11
      │    │    │    │    │    └── projections
      │    │    │    │    │         ├── NULL::CHAR [as=c_default:13]
      │    │    │    │    │         ├── NULL::"char" [as=qc_default:14]
      │    │    │    │    │         ├── 10::INT2 [as=i_default:15]
      │    │    │    │    │         └── NULL::STRING [as=s_default:16]
      │    │    │    │    └── projections
      │    │    │    │         └── assignment-cast: INT8 [as=i_cast:17]
      │    │    │    │              └── i_default:15
      │    │    │    └── projections
      │    │    │         └── d_cast:12 + 10.0 [as=d_comp_comp:18]
      │    │    └── projections
      │    │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:19]
      │    │              └── d_comp_comp:18
      │    ├── scan assn_cast
      │    │    ├── columns: k:20!null c:21 qc:22 i:23 s:24 d:25 d_comp:26
      │    │    ├── computed column expressions
      │    │    │    └── d_comp:26
      │    │    │         └── assignment-cast: DECIMAL(10)
      │    │    │              └── d:25 + 10.0
      │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    └── filters
      │         └── column1:10 = k:20
      └── aggregations
           ├── first-agg [as=d_cast:12]
           │    └── d_cast:12
           ├── first-agg [as=c_default:13]
           │    └── c_default:13
           ├── first-agg [as=qc_default:14]
           │    └── qc_default:14
           ├── first-agg [as=i_cast:17]
           │    └── i_cast:17
           ├── first-agg [as=s_default:16]
           │    └── s_default:16
           └── first-agg [as=d_comp_cast:19]
                └── d_comp_cast:19

# Test insert-do-update to a column that requires an assignment cast and a
# computed column that depends on the new value.
build
INSERT INTO assn_cast (k, d) VALUES (1, 1.45) ON CONFLICT (k) DO UPDATE SET d = 2.67::DECIMAL(10, 2)
----
upsert assn_cast
 ├── arbiter indexes: assn_cast_pkey
 ├── columns: <none>
 ├── canary column: k:20
 ├── fetch columns: k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26
 ├── insert-mapping:
 │    ├── column1:10 => k:1
 │    ├── c_default:13 => c:2
 │    ├── qc_default:14 => qc:3
 │    ├── i_cast:17 => i:4
 │    ├── s_default:16 => s:5
 │    ├── d_cast:12 => d:6
 │    └── d_comp_cast:19 => d_comp:7
 ├── update-mapping:
 │    ├── upsert_d:38 => d:6
 │    └── upsert_d_comp:39 => d_comp:7
 └── project
      ├── columns: upsert_k:33 upsert_c:34 upsert_qc:35 upsert_i:36 upsert_s:37 upsert_d:38!null upsert_d_comp:39!null column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19!null k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28 d_cast:30!null d_comp_cast:32!null
      ├── project
      │    ├── columns: d_comp_cast:32!null column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19!null k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28 d_cast:30!null
      │    ├── project
      │    │    ├── columns: d_comp_comp:31!null column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19!null k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28 d_cast:30!null
      │    │    ├── project
      │    │    │    ├── columns: d_cast:30!null column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19!null k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28
      │    │    │    ├── project
      │    │    │    │    ├── columns: d_new:29!null column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19!null k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28
      │    │    │    │    ├── left-join (hash)
      │    │    │    │    │    ├── columns: column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19!null k:20 c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28
      │    │    │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    │    │    ├── columns: column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null d_comp_cast:19!null
      │    │    │    │    │    │    ├── grouping columns: column1:10!null
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: d_comp_cast:19!null column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null
      │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    ├── columns: d_comp_comp:18!null column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16 i_cast:17!null
      │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    ├── columns: i_cast:17!null column1:10!null d_cast:12!null c_default:13 qc_default:14 s_default:16
      │    │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    │    ├── columns: c_default:13 qc_default:14 i_default:15!null s_default:16 column1:10!null d_cast:12!null
      │    │    │    │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    │    │    │    ├── columns: d_cast:12!null column1:10!null
      │    │    │    │    │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    │    │    │    │    ├── columns: column1:10!null column2:11!null
      │    │    │    │    │    │    │    │    │    │    │    │    └── (1, 1.45)
      │    │    │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10) [as=d_cast:12]
      │    │    │    │    │    │    │    │    │    │    │              └── column2:11
      │    │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │    │         ├── NULL::CHAR [as=c_default:13]
      │    │    │    │    │    │    │    │    │    │         ├── NULL::"char" [as=qc_default:14]
      │    │    │    │    │    │    │    │    │    │         ├── 10::INT2 [as=i_default:15]
      │    │    │    │    │    │    │    │    │    │         └── NULL::STRING [as=s_default:16]
      │    │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │    │         └── assignment-cast: INT8 [as=i_cast:17]
      │    │    │    │    │    │    │    │    │              └── i_default:15
      │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │         └── d_cast:12 + 10.0 [as=d_comp_comp:18]
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:19]
      │    │    │    │    │    │    │              └── d_comp_comp:18
      │    │    │    │    │    │    └── aggregations
      │    │    │    │    │    │         ├── first-agg [as=d_cast:12]
      │    │    │    │    │    │         │    └── d_cast:12
      │    │    │    │    │    │         ├── first-agg [as=c_default:13]
      │    │    │    │    │    │         │    └── c_default:13
      │    │    │    │    │    │         ├── first-agg [as=qc_default:14]
      │    │    │    │    │    │         │    └── qc_default:14
      │    │    │    │    │    │         ├── first-agg [as=i_cast:17]
      │    │    │    │    │    │         │    └── i_cast:17
      │    │    │    │    │    │         ├── first-agg [as=s_default:16]
      │    │    │    │    │    │         │    └── s_default:16
      │    │    │    │    │    │         └── first-agg [as=d_comp_cast:19]
      │    │    │    │    │    │              └── d_comp_cast:19
      │    │    │    │    │    ├── scan assn_cast
      │    │    │    │    │    │    ├── columns: k:20!null c:21 qc:22 i:23 s:24 d:25 d_comp:26 crdb_internal_mvcc_timestamp:27 tableoid:28
      │    │    │    │    │    │    ├── computed column expressions
      │    │    │    │    │    │    │    └── d_comp:26
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10)
      │    │    │    │    │    │    │              └── d:25 + 10.0
      │    │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    │    └── filters
      │    │    │    │    │         └── column1:10 = k:20
      │    │    │    │    └── projections
      │    │    │    │         └── 2.67::DECIMAL(10,2) [as=d_new:29]
      │    │    │    └── projections
      │    │    │         └── assignment-cast: DECIMAL(10) [as=d_cast:30]
      │    │    │              └── d_new:29
      │    │    └── projections
      │    │         └── d_cast:30 + 10.0 [as=d_comp_comp:31]
      │    └── projections
      │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:32]
      │              └── d_comp_comp:31
      └── projections
           ├── CASE WHEN k:20 IS NULL THEN column1:10 ELSE k:20 END [as=upsert_k:33]
           ├── CASE WHEN k:20 IS NULL THEN c_default:13 ELSE c:21 END [as=upsert_c:34]
           ├── CASE WHEN k:20 IS NULL THEN qc_default:14 ELSE qc:22 END [as=upsert_qc:35]
           ├── CASE WHEN k:20 IS NULL THEN i_cast:17 ELSE i:23 END [as=upsert_i:36]
           ├── CASE WHEN k:20 IS NULL THEN s_default:16 ELSE s:24 END [as=upsert_s:37]
           ├── CASE WHEN k:20 IS NULL THEN d_cast:12 ELSE d_cast:30 END [as=upsert_d:38]
           └── CASE WHEN k:20 IS NULL THEN d_comp_cast:19 ELSE d_comp_cast:32 END [as=upsert_d_comp:39]

# Test ON UPDATE columns that require assignment casts.
build
INSERT INTO assn_cast_on_update (k, i) VALUES (1, 2) ON CONFLICT (k) DO UPDATE SET i = 3
----
upsert assn_cast_on_update
 ├── arbiter indexes: assn_cast_on_update_pkey
 ├── columns: <none>
 ├── canary column: k:12
 ├── fetch columns: k:12 i:13 d:14 d2:15 d_comp:16
 ├── insert-mapping:
 │    ├── column1:8 => k:1
 │    ├── column2:9 => i:2
 │    ├── d_default:10 => d:3
 │    ├── d_default:10 => d2:4
 │    └── d_comp_cast:11 => d_comp:5
 ├── update-mapping:
 │    ├── upsert_i:26 => i:2
 │    ├── upsert_d:27 => d:3
 │    ├── upsert_d2:28 => d2:4
 │    └── upsert_d_comp:29 => d_comp:5
 └── project
      ├── columns: upsert_k:25 upsert_i:26!null upsert_d:27 upsert_d2:28 upsert_d_comp:29 column1:8!null column2:9!null d_default:10 d_comp_cast:11 k:12 i:13 d:14 d2:15 d_comp:16 crdb_internal_mvcc_timestamp:17 tableoid:18 i_new:19!null d_cast:22!null d2_cast:23!null d_comp_cast:24!null
      ├── project
      │    ├── columns: d_comp_cast:24!null column1:8!null column2:9!null d_default:10 d_comp_cast:11 k:12 i:13 d:14 d2:15 d_comp:16 crdb_internal_mvcc_timestamp:17 tableoid:18 i_new:19!null d_cast:22!null d2_cast:23!null
      │    ├── project
      │    │    ├── columns: d_cast:22!null d2_cast:23!null column1:8!null column2:9!null d_default:10 d_comp_cast:11 k:12 i:13 d:14 d2:15 d_comp:16 crdb_internal_mvcc_timestamp:17 tableoid:18 i_new:19!null
      │    │    ├── project
      │    │    │    ├── columns: d_on_update:20!null d2_on_update:21!null column1:8!null column2:9!null d_default:10 d_comp_cast:11 k:12 i:13 d:14 d2:15 d_comp:16 crdb_internal_mvcc_timestamp:17 tableoid:18 i_new:19!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: i_new:19!null column1:8!null column2:9!null d_default:10 d_comp_cast:11 k:12 i:13 d:14 d2:15 d_comp:16 crdb_internal_mvcc_timestamp:17 tableoid:18
      │    │    │    │    ├── left-join (hash)
      │    │    │    │    │    ├── columns: column1:8!null column2:9!null d_default:10 d_comp_cast:11 k:12 i:13 d:14 d2:15 d_comp:16 crdb_internal_mvcc_timestamp:17 tableoid:18
      │    │    │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    │    │    ├── columns: column1:8!null column2:9!null d_default:10 d_comp_cast:11
      │    │    │    │    │    │    ├── grouping columns: column1:8!null
      │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    ├── columns: d_comp_cast:11 column1:8!null column2:9!null d_default:10
      │    │    │    │    │    │    │    ├── project
      │    │    │    │    │    │    │    │    ├── columns: d_default:10 column1:8!null column2:9!null
      │    │    │    │    │    │    │    │    ├── values
      │    │    │    │    │    │    │    │    │    ├── columns: column1:8!null column2:9!null
      │    │    │    │    │    │    │    │    │    └── (1, 2)
      │    │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │    │         └── NULL::DECIMAL(10,1) [as=d_default:10]
      │    │    │    │    │    │    │    └── projections
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:11]
      │    │    │    │    │    │    │              └── d_default:10
      │    │    │    │    │    │    └── aggregations
      │    │    │    │    │    │         ├── first-agg [as=column2:9]
      │    │    │    │    │    │         │    └── column2:9
      │    │    │    │    │    │         ├── first-agg [as=d_default:10]
      │    │    │    │    │    │         │    └── d_default:10
      │    │    │    │    │    │         └── first-agg [as=d_comp_cast:11]
      │    │    │    │    │    │              └── d_comp_cast:11
      │    │    │    │    │    ├── scan assn_cast_on_update
      │    │    │    │    │    │    ├── columns: k:12!null i:13 d:14 d2:15 d_comp:16 crdb_internal_mvcc_timestamp:17 tableoid:18
      │    │    │    │    │    │    ├── computed column expressions
      │    │    │    │    │    │    │    └── d_comp:16
      │    │    │    │    │    │    │         └── assignment-cast: DECIMAL(10)
      │    │    │    │    │    │    │              └── d:14
      │    │    │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    │    │    └── filters
      │    │    │    │    │         └── column1:8 = k:12
      │    │    │    │    └── projections
      │    │    │    │         └── 3 [as=i_new:19]
      │    │    │    └── projections
      │    │    │         ├── 1.23 [as=d_on_update:20]
      │    │    │         └── 1.23::DECIMAL(10,2) [as=d2_on_update:21]
      │    │    └── projections
      │    │         ├── assignment-cast: DECIMAL(10,1) [as=d_cast:22]
      │    │         │    └── d_on_update:20
      │    │         └── assignment-cast: DECIMAL(10,1) [as=d2_cast:23]
      │    │              └── d2_on_update:21
      │    └── projections
      │         └── assignment-cast: DECIMAL(10) [as=d_comp_cast:24]
      │              └── d_cast:22
      └── projections
           ├── CASE WHEN k:12 IS NULL THEN column1:8 ELSE k:12 END [as=upsert_k:25]
           ├── CASE WHEN k:12 IS NULL THEN column2:9 ELSE i_new:19 END [as=upsert_i:26]
           ├── CASE WHEN k:12 IS NULL THEN d_default:10 ELSE d_cast:22 END [as=upsert_d:27]
           ├── CASE WHEN k:12 IS NULL THEN d_default:10 ELSE d2_cast:23 END [as=upsert_d2:28]
           └── CASE WHEN k:12 IS NULL THEN d_comp_cast:11 ELSE d_comp_cast:24 END [as=upsert_d_comp:29]


# Regression test for #67100. Do not prune check columns for UPSERTs even if the
# expression does not reference any mutating columns.

exec-ddl
CREATE TABLE t67100 (
  a INT,
  CHECK (false),
  CHECK (0 > 1),
  CHECK (true AND 0 > 1)
)
----

build
UPSERT INTO t67100 VALUES (1)
----
upsert t67100
 ├── columns: <none>
 ├── upsert-mapping:
 │    ├── column1:5 => a:1
 │    └── rowid_default:6 => rowid:2
 ├── check columns: check1:7 check2:8 check3:9
 └── project
      ├── columns: check1:7!null check2:8!null check3:9!null column1:5!null rowid_default:6
      ├── project
      │    ├── columns: rowid_default:6 column1:5!null
      │    ├── values
      │    │    ├── columns: column1:5!null
      │    │    └── (1,)
      │    └── projections
      │         └── unique_rowid() [as=rowid_default:6]
      └── projections
           ├── false [as=check1:7]
           ├── 0 > 1 [as=check2:8]
           └── true AND (0 > 1) [as=check3:9]

build
UPSERT INTO on_update_bare VALUES (1, 2)
----
upsert on_update_bare
 ├── columns: <none>
 ├── upsert-mapping:
 │    ├── column1:5 => a:1
 │    └── column2:6 => v:2
 └── values
      ├── columns: column1:5!null column2:6!null
      └── (1, 2)

# If there are no explicit target columns, UPSERT sets all columns to their
# DEFAULT value. Therefore, the ON UPDATE value is not used. See #73984.
build
UPSERT INTO on_update_bare VALUES (1)
----
upsert on_update_bare
 ├── columns: <none>
 ├── upsert-mapping:
 │    ├── column1:5 => a:1
 │    └── v_default:6 => v:2
 └── project
      ├── columns: v_default:6 column1:5!null
      ├── values
      │    ├── columns: column1:5!null
      │    └── (1,)
      └── projections
           └── NULL::INT8 [as=v_default:6]

build
UPSERT INTO on_update_bare (a) VALUES (1)
----
upsert on_update_bare
 ├── arbiter indexes: on_update_bare_pkey
 ├── columns: <none>
 ├── canary column: a:7
 ├── fetch columns: a:7 v:8
 ├── insert-mapping:
 │    ├── column1:5 => a:1
 │    └── v_default:6 => v:2
 ├── update-mapping:
 │    └── upsert_v:13 => v:2
 └── project
      ├── columns: upsert_a:12 upsert_v:13 column1:5!null v_default:6 a:7 v:8 crdb_internal_mvcc_timestamp:9 tableoid:10 v_on_update:11!null
      ├── project
      │    ├── columns: v_on_update:11!null column1:5!null v_default:6 a:7 v:8 crdb_internal_mvcc_timestamp:9 tableoid:10
      │    ├── left-join (hash)
      │    │    ├── columns: column1:5!null v_default:6 a:7 v:8 crdb_internal_mvcc_timestamp:9 tableoid:10
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:5!null v_default:6
      │    │    │    ├── grouping columns: column1:5!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: v_default:6 column1:5!null
      │    │    │    │    ├── values
      │    │    │    │    │    ├── columns: column1:5!null
      │    │    │    │    │    └── (1,)
      │    │    │    │    └── projections
      │    │    │    │         └── NULL::INT8 [as=v_default:6]
      │    │    │    └── aggregations
      │    │    │         └── first-agg [as=v_default:6]
      │    │    │              └── v_default:6
      │    │    ├── scan on_update_bare
      │    │    │    ├── columns: a:7!null v:8 crdb_internal_mvcc_timestamp:9 tableoid:10
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:5 = a:7
      │    └── projections
      │         └── 5 [as=v_on_update:11]
      └── projections
           ├── CASE WHEN a:7 IS NULL THEN column1:5 ELSE a:7 END [as=upsert_a:12]
           └── CASE WHEN a:7 IS NULL THEN v_default:6 ELSE v_on_update:11 END [as=upsert_v:13]

build
UPSERT INTO on_update_with_default (a, b, v) VALUES (1, 2, 3)
----
upsert on_update_with_default
 ├── columns: <none>
 ├── upsert-mapping:
 │    ├── column1:6 => a:1
 │    ├── column2:7 => b:2
 │    └── column3:8 => v:3
 └── values
      ├── columns: column1:6!null column2:7!null column3:8!null
      └── (1, 2, 3)

build
UPSERT INTO on_update_with_default (a, b) VALUES (1, 2)
----
upsert on_update_with_default
 ├── arbiter indexes: on_update_with_default_pkey
 ├── columns: <none>
 ├── canary column: a:9
 ├── fetch columns: a:9 b:10 v:11
 ├── insert-mapping:
 │    ├── column1:6 => a:1
 │    ├── column2:7 => b:2
 │    └── v_default:8 => v:3
 ├── update-mapping:
 │    ├── column2:7 => b:2
 │    └── upsert_v:16 => v:3
 └── project
      ├── columns: upsert_a:15 upsert_v:16!null column1:6!null column2:7!null v_default:8!null a:9 b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13 v_on_update:14!null
      ├── project
      │    ├── columns: v_on_update:14!null column1:6!null column2:7!null v_default:8!null a:9 b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── left-join (hash)
      │    │    ├── columns: column1:6!null column2:7!null v_default:8!null a:9 b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:6!null column2:7!null v_default:8!null
      │    │    │    ├── grouping columns: column1:6!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: v_default:8!null column1:6!null column2:7!null
      │    │    │    │    ├── values
      │    │    │    │    │    ├── columns: column1:6!null column2:7!null
      │    │    │    │    │    └── (1, 2)
      │    │    │    │    └── projections
      │    │    │    │         └── 4 [as=v_default:8]
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=column2:7]
      │    │    │         │    └── column2:7
      │    │    │         └── first-agg [as=v_default:8]
      │    │    │              └── v_default:8
      │    │    ├── scan on_update_with_default
      │    │    │    ├── columns: a:9!null b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:6 = a:9
      │    └── projections
      │         └── 10 [as=v_on_update:14]
      └── projections
           ├── CASE WHEN a:9 IS NULL THEN column1:6 ELSE a:9 END [as=upsert_a:15]
           └── CASE WHEN a:9 IS NULL THEN v_default:8 ELSE v_on_update:14 END [as=upsert_v:16]

build
INSERT INTO on_update_with_default (a, v)
VALUES (1, 2)
ON CONFLICT (a) DO
UPDATE SET (a, v) = (5, 4)
----
upsert on_update_with_default
 ├── arbiter indexes: on_update_with_default_pkey
 ├── columns: <none>
 ├── canary column: a:9
 ├── fetch columns: a:9 b:10 v:11
 ├── insert-mapping:
 │    ├── column1:6 => a:1
 │    ├── b_default:8 => b:2
 │    └── column2:7 => v:3
 ├── update-mapping:
 │    ├── upsert_a:16 => a:1
 │    └── upsert_v:18 => v:3
 └── project
      ├── columns: upsert_a:16!null upsert_b:17 upsert_v:18!null column1:6!null column2:7!null b_default:8 a:9 b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13 a_new:14!null v_new:15!null
      ├── project
      │    ├── columns: a_new:14!null v_new:15!null column1:6!null column2:7!null b_default:8 a:9 b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── left-join (hash)
      │    │    ├── columns: column1:6!null column2:7!null b_default:8 a:9 b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:6!null column2:7!null b_default:8
      │    │    │    ├── grouping columns: column1:6!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: b_default:8 column1:6!null column2:7!null
      │    │    │    │    ├── values
      │    │    │    │    │    ├── columns: column1:6!null column2:7!null
      │    │    │    │    │    └── (1, 2)
      │    │    │    │    └── projections
      │    │    │    │         └── NULL::INT8 [as=b_default:8]
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=column2:7]
      │    │    │         │    └── column2:7
      │    │    │         └── first-agg [as=b_default:8]
      │    │    │              └── b_default:8
      │    │    ├── scan on_update_with_default
      │    │    │    ├── columns: a:9!null b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:6 = a:9
      │    └── projections
      │         ├── 5 [as=a_new:14]
      │         └── 4 [as=v_new:15]
      └── projections
           ├── CASE WHEN a:9 IS NULL THEN column1:6 ELSE a_new:14 END [as=upsert_a:16]
           ├── CASE WHEN a:9 IS NULL THEN b_default:8 ELSE b:10 END [as=upsert_b:17]
           └── CASE WHEN a:9 IS NULL THEN column2:7 ELSE v_new:15 END [as=upsert_v:18]

build
INSERT INTO on_update_with_default (a)
VALUES (1)
ON CONFLICT (a) DO
UPDATE SET a=5
----
upsert on_update_with_default
 ├── arbiter indexes: on_update_with_default_pkey
 ├── columns: <none>
 ├── canary column: a:9
 ├── fetch columns: a:9 b:10 v:11
 ├── insert-mapping:
 │    ├── column1:6 => a:1
 │    ├── b_default:7 => b:2
 │    └── v_default:8 => v:3
 ├── update-mapping:
 │    ├── upsert_a:16 => a:1
 │    └── upsert_v:18 => v:3
 └── project
      ├── columns: upsert_a:16!null upsert_b:17 upsert_v:18!null column1:6!null b_default:7 v_default:8!null a:9 b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13 a_new:14!null v_on_update:15!null
      ├── project
      │    ├── columns: v_on_update:15!null column1:6!null b_default:7 v_default:8!null a:9 b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13 a_new:14!null
      │    ├── project
      │    │    ├── columns: a_new:14!null column1:6!null b_default:7 v_default:8!null a:9 b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: column1:6!null b_default:7 v_default:8!null a:9 b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    │    ├── ensure-upsert-distinct-on
      │    │    │    │    ├── columns: column1:6!null b_default:7 v_default:8!null
      │    │    │    │    ├── grouping columns: column1:6!null
      │    │    │    │    ├── project
      │    │    │    │    │    ├── columns: b_default:7 v_default:8!null column1:6!null
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── columns: column1:6!null
      │    │    │    │    │    │    └── (1,)
      │    │    │    │    │    └── projections
      │    │    │    │    │         ├── NULL::INT8 [as=b_default:7]
      │    │    │    │    │         └── 4 [as=v_default:8]
      │    │    │    │    └── aggregations
      │    │    │    │         ├── first-agg [as=b_default:7]
      │    │    │    │         │    └── b_default:7
      │    │    │    │         └── first-agg [as=v_default:8]
      │    │    │    │              └── v_default:8
      │    │    │    ├── scan on_update_with_default
      │    │    │    │    ├── columns: a:9!null b:10 v:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    │    └── filters
      │    │    │         └── column1:6 = a:9
      │    │    └── projections
      │    │         └── 5 [as=a_new:14]
      │    └── projections
      │         └── 10 [as=v_on_update:15]
      └── projections
           ├── CASE WHEN a:9 IS NULL THEN column1:6 ELSE a_new:14 END [as=upsert_a:16]
           ├── CASE WHEN a:9 IS NULL THEN b_default:7 ELSE b:10 END [as=upsert_b:17]
           └── CASE WHEN a:9 IS NULL THEN v_default:8 ELSE v_on_update:15 END [as=upsert_v:18]

# Test with columns created with GENERATED AS IDENTITY syntax.
exec-ddl
CREATE TABLE generated_as_identity (
  a INT UNIQUE,
  b INT GENERATED ALWAYS AS IDENTITY,
  c INT GENERATED BY DEFAULT AS IDENTITY
)
----

build
INSERT INTO generated_as_identity (a, b) VALUES (1, 10) ON CONFLICT DO NOTHING
----
error (428C9): cannot insert into column "b"

build
INSERT INTO generated_as_identity (a, b) VALUES (2, 20) ON CONFLICT (b) DO UPDATE SET b=DEFAULT;
----
error (428C9): cannot insert into column "b"

build
INSERT INTO generated_as_identity (a, b) VALUES (2, 20) ON CONFLICT (b) DO UPDATE SET b=(1+1);
----
error (428C9): cannot insert into column "b"

build
UPSERT INTO generated_as_identity (a) VALUES (2)
----
upsert generated_as_identity
 ├── arbiter indexes: generated_as_identity_pkey
 ├── columns: <none>
 ├── canary column: rowid:14
 ├── fetch columns: a:11 b:12 c:13 rowid:14
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── b_default:8 => b:2
 │    ├── c_default:9 => c:3
 │    └── rowid_default:10 => rowid:4
 ├── update-mapping:
 │    └── column1:7 => a:1
 └── project
      ├── columns: upsert_b:17 upsert_c:18 upsert_rowid:19 column1:7!null b_default:8 c_default:9 rowid_default:10 a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      ├── left-join (hash)
      │    ├── columns: column1:7!null b_default:8 c_default:9 rowid_default:10 a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    ├── ensure-upsert-distinct-on
      │    │    ├── columns: column1:7!null b_default:8 c_default:9 rowid_default:10
      │    │    ├── grouping columns: rowid_default:10
      │    │    ├── project
      │    │    │    ├── columns: b_default:8 c_default:9 rowid_default:10 column1:7!null
      │    │    │    ├── values
      │    │    │    │    ├── columns: column1:7!null
      │    │    │    │    └── (2,)
      │    │    │    └── projections
      │    │    │         ├── nextval('t.public.generated_as_identity_b_seq') [as=b_default:8]
      │    │    │         ├── nextval('t.public.generated_as_identity_c_seq') [as=c_default:9]
      │    │    │         └── unique_rowid() [as=rowid_default:10]
      │    │    └── aggregations
      │    │         ├── first-agg [as=column1:7]
      │    │         │    └── column1:7
      │    │         ├── first-agg [as=b_default:8]
      │    │         │    └── b_default:8
      │    │         └── first-agg [as=c_default:9]
      │    │              └── c_default:9
      │    ├── scan generated_as_identity
      │    │    ├── columns: a:11 b:12!null c:13!null rowid:14!null crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    └── filters
      │         └── rowid_default:10 = rowid:14
      └── projections
           ├── CASE WHEN rowid:14 IS NULL THEN b_default:8 ELSE b:12 END [as=upsert_b:17]
           ├── CASE WHEN rowid:14 IS NULL THEN c_default:9 ELSE c:13 END [as=upsert_c:18]
           └── CASE WHEN rowid:14 IS NULL THEN rowid_default:10 ELSE rowid:14 END [as=upsert_rowid:19]

build
UPSERT INTO generated_as_identity (a, b) VALUES (3, 30)
----
error (428C9): cannot insert into column "b"

build
UPSERT INTO generated_as_identity (a, c) VALUES (3, 30)
----
upsert generated_as_identity
 ├── arbiter indexes: generated_as_identity_pkey
 ├── columns: <none>
 ├── canary column: rowid:14
 ├── fetch columns: a:11 b:12 c:13 rowid:14
 ├── insert-mapping:
 │    ├── column1:7 => a:1
 │    ├── b_default:9 => b:2
 │    ├── column2:8 => c:3
 │    └── rowid_default:10 => rowid:4
 ├── update-mapping:
 │    ├── column1:7 => a:1
 │    └── column2:8 => c:3
 └── project
      ├── columns: upsert_b:17 upsert_rowid:18 column1:7!null column2:8!null b_default:9 rowid_default:10 a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      ├── left-join (hash)
      │    ├── columns: column1:7!null column2:8!null b_default:9 rowid_default:10 a:11 b:12 c:13 rowid:14 crdb_internal_mvcc_timestamp:15 tableoid:16
      │    ├── ensure-upsert-distinct-on
      │    │    ├── columns: column1:7!null column2:8!null b_default:9 rowid_default:10
      │    │    ├── grouping columns: rowid_default:10
      │    │    ├── project
      │    │    │    ├── columns: b_default:9 rowid_default:10 column1:7!null column2:8!null
      │    │    │    ├── values
      │    │    │    │    ├── columns: column1:7!null column2:8!null
      │    │    │    │    └── (3, 30)
      │    │    │    └── projections
      │    │    │         ├── nextval('t.public.generated_as_identity_b_seq') [as=b_default:9]
      │    │    │         └── unique_rowid() [as=rowid_default:10]
      │    │    └── aggregations
      │    │         ├── first-agg [as=column1:7]
      │    │         │    └── column1:7
      │    │         ├── first-agg [as=column2:8]
      │    │         │    └── column2:8
      │    │         └── first-agg [as=b_default:9]
      │    │              └── b_default:9
      │    ├── scan generated_as_identity
      │    │    ├── columns: a:11 b:12!null c:13!null rowid:14!null crdb_internal_mvcc_timestamp:15 tableoid:16
      │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    └── filters
      │         └── rowid_default:10 = rowid:14
      └── projections
           ├── CASE WHEN rowid:14 IS NULL THEN b_default:9 ELSE b:12 END [as=upsert_b:17]
           └── CASE WHEN rowid:14 IS NULL THEN rowid_default:10 ELSE rowid:14 END [as=upsert_rowid:18]

# ------------------------------------------------------------------------------
# Test ON CONSTRAINT arbiter index selection.
# ------------------------------------------------------------------------------
build
INSERT INTO xyz (x, y)
VALUES (1, 2)
ON CONFLICT ON CONSTRAINT xyz_pkey DO
UPDATE SET x=5
----
upsert xyz
 ├── arbiter indexes: xyz_pkey
 ├── columns: <none>
 ├── canary column: x:9
 ├── fetch columns: x:9 y:10 z:11
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── z_default:8 => z:3
 ├── update-mapping:
 │    └── upsert_x:15 => x:1
 └── project
      ├── columns: upsert_x:15!null upsert_y:16 upsert_z:17 column1:6!null column2:7!null z_default:8 x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13 x_new:14!null
      ├── project
      │    ├── columns: x_new:14!null column1:6!null column2:7!null z_default:8 x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── left-join (hash)
      │    │    ├── columns: column1:6!null column2:7!null z_default:8 x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:6!null column2:7!null z_default:8
      │    │    │    ├── grouping columns: column1:6!null
      │    │    │    ├── project
      │    │    │    │    ├── columns: z_default:8 column1:6!null column2:7!null
      │    │    │    │    ├── values
      │    │    │    │    │    ├── columns: column1:6!null column2:7!null
      │    │    │    │    │    └── (1, 2)
      │    │    │    │    └── projections
      │    │    │    │         └── NULL::INT8 [as=z_default:8]
      │    │    │    └── aggregations
      │    │    │         ├── first-agg [as=column2:7]
      │    │    │         │    └── column2:7
      │    │    │         └── first-agg [as=z_default:8]
      │    │    │              └── z_default:8
      │    │    ├── scan xyz
      │    │    │    ├── columns: x:9!null y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         └── column1:6 = x:9
      │    └── projections
      │         └── 5 [as=x_new:14]
      └── projections
           ├── CASE WHEN x:9 IS NULL THEN column1:6 ELSE x_new:14 END [as=upsert_x:15]
           ├── CASE WHEN x:9 IS NULL THEN column2:7 ELSE y:10 END [as=upsert_y:16]
           └── CASE WHEN x:9 IS NULL THEN z_default:8 ELSE z:11 END [as=upsert_z:17]

build
INSERT INTO xyz (x, y)
VALUES (1, 2)
ON CONFLICT ON CONSTRAINT xyz_y_z_key DO
UPDATE SET x=5
----
upsert xyz
 ├── arbiter indexes: xyz_y_z_key
 ├── columns: <none>
 ├── canary column: x:9
 ├── fetch columns: x:9 y:10 z:11
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── z_default:8 => z:3
 ├── update-mapping:
 │    └── upsert_x:15 => x:1
 └── project
      ├── columns: upsert_x:15!null upsert_y:16 upsert_z:17 column1:6!null column2:7!null z_default:8 x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13 x_new:14!null
      ├── project
      │    ├── columns: x_new:14!null column1:6!null column2:7!null z_default:8 x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── left-join (hash)
      │    │    ├── columns: column1:6!null column2:7!null z_default:8 x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:6!null column2:7!null z_default:8
      │    │    │    ├── grouping columns: column2:7!null z_default:8
      │    │    │    ├── project
      │    │    │    │    ├── columns: z_default:8 column1:6!null column2:7!null
      │    │    │    │    ├── values
      │    │    │    │    │    ├── columns: column1:6!null column2:7!null
      │    │    │    │    │    └── (1, 2)
      │    │    │    │    └── projections
      │    │    │    │         └── NULL::INT8 [as=z_default:8]
      │    │    │    └── aggregations
      │    │    │         └── first-agg [as=column1:6]
      │    │    │              └── column1:6
      │    │    ├── scan xyz
      │    │    │    ├── columns: x:9!null y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         ├── column2:7 = y:10
      │    │         └── z_default:8 = z:11
      │    └── projections
      │         └── 5 [as=x_new:14]
      └── projections
           ├── CASE WHEN x:9 IS NULL THEN column1:6 ELSE x_new:14 END [as=upsert_x:15]
           ├── CASE WHEN x:9 IS NULL THEN column2:7 ELSE y:10 END [as=upsert_y:16]
           └── CASE WHEN x:9 IS NULL THEN z_default:8 ELSE z:11 END [as=upsert_z:17]

build
INSERT INTO xyz (x, y)
VALUES (1, 2)
ON CONFLICT ON CONSTRAINT xyz_z_y_key DO
UPDATE SET x=5
----
upsert xyz
 ├── arbiter indexes: xyz_z_y_key
 ├── columns: <none>
 ├── canary column: x:9
 ├── fetch columns: x:9 y:10 z:11
 ├── insert-mapping:
 │    ├── column1:6 => x:1
 │    ├── column2:7 => y:2
 │    └── z_default:8 => z:3
 ├── update-mapping:
 │    └── upsert_x:15 => x:1
 └── project
      ├── columns: upsert_x:15!null upsert_y:16 upsert_z:17 column1:6!null column2:7!null z_default:8 x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13 x_new:14!null
      ├── project
      │    ├── columns: x_new:14!null column1:6!null column2:7!null z_default:8 x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    ├── left-join (hash)
      │    │    ├── columns: column1:6!null column2:7!null z_default:8 x:9 y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    ├── ensure-upsert-distinct-on
      │    │    │    ├── columns: column1:6!null column2:7!null z_default:8
      │    │    │    ├── grouping columns: column2:7!null z_default:8
      │    │    │    ├── project
      │    │    │    │    ├── columns: z_default:8 column1:6!null column2:7!null
      │    │    │    │    ├── values
      │    │    │    │    │    ├── columns: column1:6!null column2:7!null
      │    │    │    │    │    └── (1, 2)
      │    │    │    │    └── projections
      │    │    │    │         └── NULL::INT8 [as=z_default:8]
      │    │    │    └── aggregations
      │    │    │         └── first-agg [as=column1:6]
      │    │    │              └── column1:6
      │    │    ├── scan xyz
      │    │    │    ├── columns: x:9!null y:10 z:11 crdb_internal_mvcc_timestamp:12 tableoid:13
      │    │    │    └── flags: avoid-full-scan disabled not visible index feature
      │    │    └── filters
      │    │         ├── column2:7 = y:10
      │    │         └── z_default:8 = z:11
      │    └── projections
      │         └── 5 [as=x_new:14]
      └── projections
           ├── CASE WHEN x:9 IS NULL THEN column1:6 ELSE x_new:14 END [as=upsert_x:15]
           ├── CASE WHEN x:9 IS NULL THEN column2:7 ELSE y:10 END [as=upsert_y:16]
           └── CASE WHEN x:9 IS NULL THEN z_default:8 ELSE z:11 END [as=upsert_z:17]

build
INSERT INTO xyz (x, y)
VALUES (1, 2)
ON CONFLICT ON CONSTRAINT no_such_constraint DO
UPDATE SET x=5
----
error (42704): constraint "no_such_constraint" for table "xyz" does not exist

exec-ddl
CREATE TABLE min_arbiters (
  a INT,
  b INT,
  c INT,
  UNIQUE WITHOUT INDEX (b),
  UNIQUE WITHOUT INDEX (c) WHERE a > 0
)
----

exec-ddl
CREATE UNIQUE INDEX redundant ON min_arbiters (a, b)
----

build format=hide-all
INSERT INTO min_arbiters (a) VALUES (1) ON CONFLICT DO NOTHING
----
insert min_arbiters
 ├── arbiter indexes: min_arbiters_pkey
 ├── arbiter constraints: unique_b unique_c
 └── project
      └── upsert-distinct-on
           ├── project
           │    ├── upsert-distinct-on
           │    │    ├── upsert-distinct-on
           │    │    │    ├── anti-join (hash)
           │    │    │    │    ├── anti-join (hash)
           │    │    │    │    │    ├── anti-join (hash)
           │    │    │    │    │    │    ├── project
           │    │    │    │    │    │    │    ├── values
           │    │    │    │    │    │    │    │    └── (1,)
           │    │    │    │    │    │    │    └── projections
           │    │    │    │    │    │    │         ├── NULL::INT8
           │    │    │    │    │    │    │         └── unique_rowid()
           │    │    │    │    │    │    ├── scan min_arbiters
           │    │    │    │    │    │    │    └── flags: avoid-full-scan
           │    │    │    │    │    │    └── filters
           │    │    │    │    │    │         └── rowid_default = rowid
           │    │    │    │    │    ├── scan min_arbiters
           │    │    │    │    │    │    └── flags: avoid-full-scan
           │    │    │    │    │    └── filters
           │    │    │    │    │         └── b_default = b
           │    │    │    │    ├── select
           │    │    │    │    │    ├── scan min_arbiters
           │    │    │    │    │    │    └── flags: avoid-full-scan
           │    │    │    │    │    └── filters
           │    │    │    │    │         └── a > 0
           │    │    │    │    └── filters
           │    │    │    │         ├── b_default = c
           │    │    │    │         └── column1 > 0
           │    │    │    └── aggregations
           │    │    │         ├── first-agg
           │    │    │         │    └── column1
           │    │    │         └── first-agg
           │    │    │              └── b_default
           │    │    └── aggregations
           │    │         ├── first-agg
           │    │         │    └── column1
           │    │         └── first-agg
           │    │              └── rowid_default
           │    └── projections
           │         └── (column1 > 0) OR NULL::BOOL
           └── aggregations
                ├── first-agg
                │    └── column1
                └── first-agg
                     └── rowid_default

exec-ddl
DROP INDEX redundant
----

exec-ddl
CREATE UNIQUE INDEX redundant ON min_arbiters (a, b, c)
----

build format=hide-all
INSERT INTO min_arbiters (a) VALUES (1) ON CONFLICT DO NOTHING
----
insert min_arbiters
 ├── arbiter indexes: min_arbiters_pkey
 ├── arbiter constraints: unique_b unique_c
 └── project
      └── upsert-distinct-on
           ├── project
           │    ├── upsert-distinct-on
           │    │    ├── upsert-distinct-on
           │    │    │    ├── anti-join (hash)
           │    │    │    │    ├── anti-join (hash)
           │    │    │    │    │    ├── anti-join (hash)
           │    │    │    │    │    │    ├── project
           │    │    │    │    │    │    │    ├── values
           │    │    │    │    │    │    │    │    └── (1,)
           │    │    │    │    │    │    │    └── projections
           │    │    │    │    │    │    │         ├── NULL::INT8
           │    │    │    │    │    │    │         └── unique_rowid()
           │    │    │    │    │    │    ├── scan min_arbiters
           │    │    │    │    │    │    │    └── flags: avoid-full-scan
           │    │    │    │    │    │    └── filters
           │    │    │    │    │    │         └── rowid_default = rowid
           │    │    │    │    │    ├── scan min_arbiters
           │    │    │    │    │    │    └── flags: avoid-full-scan
           │    │    │    │    │    └── filters
           │    │    │    │    │         └── b_default = b
           │    │    │    │    ├── select
           │    │    │    │    │    ├── scan min_arbiters
           │    │    │    │    │    │    └── flags: avoid-full-scan
           │    │    │    │    │    └── filters
           │    │    │    │    │         └── a > 0
           │    │    │    │    └── filters
           │    │    │    │         ├── b_default = c
           │    │    │    │         └── column1 > 0
           │    │    │    └── aggregations
           │    │    │         ├── first-agg
           │    │    │         │    └── column1
           │    │    │         └── first-agg
           │    │    │              └── b_default
           │    │    └── aggregations
           │    │         ├── first-agg
           │    │         │    └── column1
           │    │         └── first-agg
           │    │              └── rowid_default
           │    └── projections
           │         └── (column1 > 0) OR NULL::BOOL
           └── aggregations
                ├── first-agg
                │    └── column1
                └── first-agg
                     └── rowid_default

exec-ddl
DROP INDEX redundant
----

exec-ddl
CREATE UNIQUE INDEX redundant ON min_arbiters (b, c) WHERE a > 0
----

build format=hide-all
INSERT INTO min_arbiters (a) VALUES (1) ON CONFLICT DO NOTHING
----
insert min_arbiters
 ├── arbiter indexes: min_arbiters_pkey
 ├── arbiter constraints: unique_b unique_c
 └── project
      ├── project
      │    └── upsert-distinct-on
      │         ├── project
      │         │    ├── upsert-distinct-on
      │         │    │    ├── upsert-distinct-on
      │         │    │    │    ├── anti-join (hash)
      │         │    │    │    │    ├── anti-join (hash)
      │         │    │    │    │    │    ├── anti-join (hash)
      │         │    │    │    │    │    │    ├── project
      │         │    │    │    │    │    │    │    ├── values
      │         │    │    │    │    │    │    │    │    └── (1,)
      │         │    │    │    │    │    │    │    └── projections
      │         │    │    │    │    │    │    │         ├── NULL::INT8
      │         │    │    │    │    │    │    │         └── unique_rowid()
      │         │    │    │    │    │    │    ├── scan min_arbiters
      │         │    │    │    │    │    │    │    ├── partial index predicates
      │         │    │    │    │    │    │    │    │    └── redundant: filters
      │         │    │    │    │    │    │    │    │         └── a > 0
      │         │    │    │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │    │    │    │    └── filters
      │         │    │    │    │    │    │         └── rowid_default = rowid
      │         │    │    │    │    │    ├── scan min_arbiters
      │         │    │    │    │    │    │    ├── partial index predicates
      │         │    │    │    │    │    │    │    └── redundant: filters
      │         │    │    │    │    │    │    │         └── a > 0
      │         │    │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │    │    │    └── filters
      │         │    │    │    │    │         └── b_default = b
      │         │    │    │    │    ├── select
      │         │    │    │    │    │    ├── scan min_arbiters
      │         │    │    │    │    │    │    ├── partial index predicates
      │         │    │    │    │    │    │    │    └── redundant: filters
      │         │    │    │    │    │    │    │         └── a > 0
      │         │    │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │    │    │    └── filters
      │         │    │    │    │    │         └── a > 0
      │         │    │    │    │    └── filters
      │         │    │    │    │         ├── b_default = c
      │         │    │    │    │         └── column1 > 0
      │         │    │    │    └── aggregations
      │         │    │    │         ├── first-agg
      │         │    │    │         │    └── column1
      │         │    │    │         └── first-agg
      │         │    │    │              └── b_default
      │         │    │    └── aggregations
      │         │    │         ├── first-agg
      │         │    │         │    └── column1
      │         │    │         └── first-agg
      │         │    │              └── rowid_default
      │         │    └── projections
      │         │         └── (column1 > 0) OR NULL::BOOL
      │         └── aggregations
      │              ├── first-agg
      │              │    └── column1
      │              └── first-agg
      │                   └── rowid_default
      └── projections
           └── column1 > 0

exec-ddl
DROP INDEX redundant
----

exec-ddl
CREATE UNIQUE INDEX redundant ON min_arbiters (b, c) WHERE a > 0
----

build format=hide-all
INSERT INTO min_arbiters (a) VALUES (1) ON CONFLICT DO NOTHING
----
insert min_arbiters
 ├── arbiter indexes: min_arbiters_pkey
 ├── arbiter constraints: unique_b unique_c
 └── project
      ├── project
      │    └── upsert-distinct-on
      │         ├── project
      │         │    ├── upsert-distinct-on
      │         │    │    ├── upsert-distinct-on
      │         │    │    │    ├── anti-join (hash)
      │         │    │    │    │    ├── anti-join (hash)
      │         │    │    │    │    │    ├── anti-join (hash)
      │         │    │    │    │    │    │    ├── project
      │         │    │    │    │    │    │    │    ├── values
      │         │    │    │    │    │    │    │    │    └── (1,)
      │         │    │    │    │    │    │    │    └── projections
      │         │    │    │    │    │    │    │         ├── NULL::INT8
      │         │    │    │    │    │    │    │         └── unique_rowid()
      │         │    │    │    │    │    │    ├── scan min_arbiters
      │         │    │    │    │    │    │    │    ├── partial index predicates
      │         │    │    │    │    │    │    │    │    └── redundant: filters
      │         │    │    │    │    │    │    │    │         └── a > 0
      │         │    │    │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │    │    │    │    └── filters
      │         │    │    │    │    │    │         └── rowid_default = rowid
      │         │    │    │    │    │    ├── scan min_arbiters
      │         │    │    │    │    │    │    ├── partial index predicates
      │         │    │    │    │    │    │    │    └── redundant: filters
      │         │    │    │    │    │    │    │         └── a > 0
      │         │    │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │    │    │    └── filters
      │         │    │    │    │    │         └── b_default = b
      │         │    │    │    │    ├── select
      │         │    │    │    │    │    ├── scan min_arbiters
      │         │    │    │    │    │    │    ├── partial index predicates
      │         │    │    │    │    │    │    │    └── redundant: filters
      │         │    │    │    │    │    │    │         └── a > 0
      │         │    │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │    │    │    └── filters
      │         │    │    │    │    │         └── a > 0
      │         │    │    │    │    └── filters
      │         │    │    │    │         ├── b_default = c
      │         │    │    │    │         └── column1 > 0
      │         │    │    │    └── aggregations
      │         │    │    │         ├── first-agg
      │         │    │    │         │    └── column1
      │         │    │    │         └── first-agg
      │         │    │    │              └── b_default
      │         │    │    └── aggregations
      │         │    │         ├── first-agg
      │         │    │         │    └── column1
      │         │    │         └── first-agg
      │         │    │              └── rowid_default
      │         │    └── projections
      │         │         └── (column1 > 0) OR NULL::BOOL
      │         └── aggregations
      │              ├── first-agg
      │              │    └── column1
      │              └── first-agg
      │                   └── rowid_default
      └── projections
           └── column1 > 0

exec-ddl
DROP INDEX redundant
----

exec-ddl
CREATE UNIQUE INDEX not_redundant ON min_arbiters (b, c) WHERE a > 10
----

build format=hide-all
INSERT INTO min_arbiters (a) VALUES (1) ON CONFLICT DO NOTHING
----
insert min_arbiters
 ├── arbiter indexes: min_arbiters_pkey not_redundant
 ├── arbiter constraints: unique_b unique_c
 └── project
      ├── project
      │    └── upsert-distinct-on
      │         ├── project
      │         │    ├── upsert-distinct-on
      │         │    │    ├── project
      │         │    │    │    └── upsert-distinct-on
      │         │    │    │         ├── project
      │         │    │    │         │    ├── upsert-distinct-on
      │         │    │    │         │    │    ├── anti-join (hash)
      │         │    │    │         │    │    │    ├── anti-join (hash)
      │         │    │    │         │    │    │    │    ├── anti-join (hash)
      │         │    │    │         │    │    │    │    │    ├── anti-join (hash)
      │         │    │    │         │    │    │    │    │    │    ├── project
      │         │    │    │         │    │    │    │    │    │    │    ├── values
      │         │    │    │         │    │    │    │    │    │    │    │    └── (1,)
      │         │    │    │         │    │    │    │    │    │    │    └── projections
      │         │    │    │         │    │    │    │    │    │    │         ├── NULL::INT8
      │         │    │    │         │    │    │    │    │    │    │         └── unique_rowid()
      │         │    │    │         │    │    │    │    │    │    ├── scan min_arbiters
      │         │    │    │         │    │    │    │    │    │    │    ├── partial index predicates
      │         │    │    │         │    │    │    │    │    │    │    │    └── not_redundant: filters
      │         │    │    │         │    │    │    │    │    │    │    │         └── a > 10
      │         │    │    │         │    │    │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │         │    │    │    │    │    │    └── filters
      │         │    │    │         │    │    │    │    │    │         └── rowid_default = rowid
      │         │    │    │         │    │    │    │    │    ├── select
      │         │    │    │         │    │    │    │    │    │    ├── scan min_arbiters
      │         │    │    │         │    │    │    │    │    │    │    ├── partial index predicates
      │         │    │    │         │    │    │    │    │    │    │    │    └── not_redundant: filters
      │         │    │    │         │    │    │    │    │    │    │    │         └── a > 10
      │         │    │    │         │    │    │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │         │    │    │    │    │    │    └── filters
      │         │    │    │         │    │    │    │    │    │         └── a > 10
      │         │    │    │         │    │    │    │    │    └── filters
      │         │    │    │         │    │    │    │    │         ├── b_default = b
      │         │    │    │         │    │    │    │    │         ├── b_default = c
      │         │    │    │         │    │    │    │    │         └── column1 > 10
      │         │    │    │         │    │    │    │    ├── scan min_arbiters
      │         │    │    │         │    │    │    │    │    ├── partial index predicates
      │         │    │    │         │    │    │    │    │    │    └── not_redundant: filters
      │         │    │    │         │    │    │    │    │    │         └── a > 10
      │         │    │    │         │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │         │    │    │    │    └── filters
      │         │    │    │         │    │    │    │         └── b_default = b
      │         │    │    │         │    │    │    ├── select
      │         │    │    │         │    │    │    │    ├── scan min_arbiters
      │         │    │    │         │    │    │    │    │    ├── partial index predicates
      │         │    │    │         │    │    │    │    │    │    └── not_redundant: filters
      │         │    │    │         │    │    │    │    │    │         └── a > 10
      │         │    │    │         │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │         │    │    │    │    └── filters
      │         │    │    │         │    │    │    │         └── a > 0
      │         │    │    │         │    │    │    └── filters
      │         │    │    │         │    │    │         ├── b_default = c
      │         │    │    │         │    │    │         └── column1 > 0
      │         │    │    │         │    │    └── aggregations
      │         │    │    │         │    │         ├── first-agg
      │         │    │    │         │    │         │    └── column1
      │         │    │    │         │    │         └── first-agg
      │         │    │    │         │    │              └── b_default
      │         │    │    │         │    └── projections
      │         │    │    │         │         └── (column1 > 10) OR NULL::BOOL
      │         │    │    │         └── aggregations
      │         │    │    │              ├── first-agg
      │         │    │    │              │    └── column1
      │         │    │    │              └── first-agg
      │         │    │    │                   └── rowid_default
      │         │    │    └── aggregations
      │         │    │         ├── first-agg
      │         │    │         │    └── column1
      │         │    │         └── first-agg
      │         │    │              └── rowid_default
      │         │    └── projections
      │         │         └── (column1 > 0) OR NULL::BOOL
      │         └── aggregations
      │              ├── first-agg
      │              │    └── column1
      │              └── first-agg
      │                   └── rowid_default
      └── projections
           └── column1 > 10

exec-ddl
DROP INDEX not_redundant
----

exec-ddl
CREATE UNIQUE INDEX not_redundant ON min_arbiters (a, b) WHERE c > 0
----

build format=hide-all
INSERT INTO min_arbiters (a) VALUES (1) ON CONFLICT DO NOTHING
----
insert min_arbiters
 ├── arbiter indexes: min_arbiters_pkey not_redundant
 ├── arbiter constraints: unique_b unique_c
 └── project
      ├── project
      │    └── upsert-distinct-on
      │         ├── project
      │         │    ├── upsert-distinct-on
      │         │    │    ├── project
      │         │    │    │    └── upsert-distinct-on
      │         │    │    │         ├── project
      │         │    │    │         │    ├── upsert-distinct-on
      │         │    │    │         │    │    ├── anti-join (hash)
      │         │    │    │         │    │    │    ├── anti-join (hash)
      │         │    │    │         │    │    │    │    ├── anti-join (hash)
      │         │    │    │         │    │    │    │    │    ├── anti-join (hash)
      │         │    │    │         │    │    │    │    │    │    ├── project
      │         │    │    │         │    │    │    │    │    │    │    ├── values
      │         │    │    │         │    │    │    │    │    │    │    │    └── (1,)
      │         │    │    │         │    │    │    │    │    │    │    └── projections
      │         │    │    │         │    │    │    │    │    │    │         ├── NULL::INT8
      │         │    │    │         │    │    │    │    │    │    │         └── unique_rowid()
      │         │    │    │         │    │    │    │    │    │    ├── scan min_arbiters
      │         │    │    │         │    │    │    │    │    │    │    ├── partial index predicates
      │         │    │    │         │    │    │    │    │    │    │    │    └── not_redundant: filters
      │         │    │    │         │    │    │    │    │    │    │    │         └── c > 0
      │         │    │    │         │    │    │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │         │    │    │    │    │    │    └── filters
      │         │    │    │         │    │    │    │    │    │         └── rowid_default = rowid
      │         │    │    │         │    │    │    │    │    ├── select
      │         │    │    │         │    │    │    │    │    │    ├── scan min_arbiters
      │         │    │    │         │    │    │    │    │    │    │    ├── partial index predicates
      │         │    │    │         │    │    │    │    │    │    │    │    └── not_redundant: filters
      │         │    │    │         │    │    │    │    │    │    │    │         └── c > 0
      │         │    │    │         │    │    │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │         │    │    │    │    │    │    └── filters
      │         │    │    │         │    │    │    │    │    │         └── c > 0
      │         │    │    │         │    │    │    │    │    └── filters
      │         │    │    │         │    │    │    │    │         ├── column1 = a
      │         │    │    │         │    │    │    │    │         ├── b_default = b
      │         │    │    │         │    │    │    │    │         └── b_default > 0
      │         │    │    │         │    │    │    │    ├── scan min_arbiters
      │         │    │    │         │    │    │    │    │    ├── partial index predicates
      │         │    │    │         │    │    │    │    │    │    └── not_redundant: filters
      │         │    │    │         │    │    │    │    │    │         └── c > 0
      │         │    │    │         │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │         │    │    │    │    └── filters
      │         │    │    │         │    │    │    │         └── b_default = b
      │         │    │    │         │    │    │    ├── select
      │         │    │    │         │    │    │    │    ├── scan min_arbiters
      │         │    │    │         │    │    │    │    │    ├── partial index predicates
      │         │    │    │         │    │    │    │    │    │    └── not_redundant: filters
      │         │    │    │         │    │    │    │    │    │         └── c > 0
      │         │    │    │         │    │    │    │    │    └── flags: avoid-full-scan
      │         │    │    │         │    │    │    │    └── filters
      │         │    │    │         │    │    │    │         └── a > 0
      │         │    │    │         │    │    │    └── filters
      │         │    │    │         │    │    │         ├── b_default = c
      │         │    │    │         │    │    │         └── column1 > 0
      │         │    │    │         │    │    └── aggregations
      │         │    │    │         │    │         ├── first-agg
      │         │    │    │         │    │         │    └── column1
      │         │    │    │         │    │         └── first-agg
      │         │    │    │         │    │              └── b_default
      │         │    │    │         │    └── projections
      │         │    │    │         │         └── (b_default > 0) OR NULL::BOOL
      │         │    │    │         └── aggregations
      │         │    │    │              └── first-agg
      │         │    │    │                   └── rowid_default
      │         │    │    └── aggregations
      │         │    │         ├── first-agg
      │         │    │         │    └── column1
      │         │    │         └── first-agg
      │         │    │              └── rowid_default
      │         │    └── projections
      │         │         └── (column1 > 0) OR NULL::BOOL
      │         └── aggregations
      │              ├── first-agg
      │              │    └── column1
      │              └── first-agg
      │                   └── rowid_default
      └── projections
           └── b_default > 0

exec-ddl
DROP INDEX not_redundant
----

exec-ddl
CREATE UNIQUE INDEX not_redundant ON min_arbiters (a, c)
----

build format=hide-all
INSERT INTO min_arbiters (a) VALUES (1) ON CONFLICT DO NOTHING
----
insert min_arbiters
 ├── arbiter indexes: min_arbiters_pkey not_redundant
 ├── arbiter constraints: unique_b unique_c
 └── project
      └── upsert-distinct-on
           ├── project
           │    ├── upsert-distinct-on
           │    │    ├── upsert-distinct-on
           │    │    │    ├── upsert-distinct-on
           │    │    │    │    ├── anti-join (hash)
           │    │    │    │    │    ├── anti-join (hash)
           │    │    │    │    │    │    ├── anti-join (hash)
           │    │    │    │    │    │    │    ├── anti-join (hash)
           │    │    │    │    │    │    │    │    ├── project
           │    │    │    │    │    │    │    │    │    ├── values
           │    │    │    │    │    │    │    │    │    │    └── (1,)
           │    │    │    │    │    │    │    │    │    └── projections
           │    │    │    │    │    │    │    │    │         ├── NULL::INT8
           │    │    │    │    │    │    │    │    │         └── unique_rowid()
           │    │    │    │    │    │    │    │    ├── scan min_arbiters
           │    │    │    │    │    │    │    │    │    └── flags: avoid-full-scan
           │    │    │    │    │    │    │    │    └── filters
           │    │    │    │    │    │    │    │         └── rowid_default = rowid
           │    │    │    │    │    │    │    ├── scan min_arbiters
           │    │    │    │    │    │    │    │    └── flags: avoid-full-scan
           │    │    │    │    │    │    │    └── filters
           │    │    │    │    │    │    │         ├── column1 = a
           │    │    │    │    │    │    │         └── b_default = c
           │    │    │    │    │    │    ├── scan min_arbiters
           │    │    │    │    │    │    │    └── flags: avoid-full-scan
           │    │    │    │    │    │    └── filters
           │    │    │    │    │    │         └── b_default = b
           │    │    │    │    │    ├── select
           │    │    │    │    │    │    ├── scan min_arbiters
           │    │    │    │    │    │    │    └── flags: avoid-full-scan
           │    │    │    │    │    │    └── filters
           │    │    │    │    │    │         └── a > 0
           │    │    │    │    │    └── filters
           │    │    │    │    │         ├── b_default = c
           │    │    │    │    │         └── column1 > 0
           │    │    │    │    └── aggregations
           │    │    │    │         ├── first-agg
           │    │    │    │         │    └── column1
           │    │    │    │         └── first-agg
           │    │    │    │              └── b_default
           │    │    │    └── aggregations
           │    │    │         └── first-agg
           │    │    │              └── rowid_default
           │    │    └── aggregations
           │    │         ├── first-agg
           │    │         │    └── column1
           │    │         └── first-agg
           │    │              └── rowid_default
           │    └── projections
           │         └── (column1 > 0) OR NULL::BOOL
           └── aggregations
                ├── first-agg
                │    └── column1
                └── first-agg
                     └── rowid_default
