# LogicTest: local-opt

statement ok
CREATE TABLE kv (
  k INT PRIMARY KEY,
  v INT
)

# Use implicit target columns (which can use blind KV Put).
query TTT
SELECT tree, field, description FROM [
EXPLAIN (VERBOSE) UPSERT INTO kv TABLE kv ORDER BY v DESC LIMIT 2
]
----
count                          ·         ·
 └── upsert                    ·         ·
      │                        into      kv(k, v)
      │                        strategy  opt upserter
      └── render               ·         ·
           │                   render 0  k
           │                   render 1  v
           │                   render 2  v
           └── limit           ·         ·
                │              count     2
                └── sort       ·         ·
                     │         order     -v
                     └── scan  ·         ·
·                              table     kv@primary
·                              spans     ALL

# Use explicit target columns (which can use blind KV Put).
query TTT
SELECT tree, field, description FROM [
EXPLAIN (VERBOSE) UPSERT INTO kv (k, v) TABLE kv ORDER BY v DESC LIMIT 2
]
----
count                          ·         ·
 └── upsert                    ·         ·
      │                        into      kv(k, v)
      │                        strategy  opt upserter
      └── render               ·         ·
           │                   render 0  k
           │                   render 1  v
           │                   render 2  v
           └── limit           ·         ·
                │              count     2
                └── sort       ·         ·
                     │         order     -v
                     └── scan  ·         ·
·                              table     kv@primary
·                              spans     ALL

# Add RETURNING clause (should still use blind KV Put).
query TTT
SELECT tree, field, description FROM [
EXPLAIN (VERBOSE) UPSERT INTO kv (k, v) TABLE kv ORDER BY v DESC LIMIT 2 RETURNING *
]
----
run                            ·         ·
 └── upsert                    ·         ·
      │                        into      kv(k, v)
      │                        strategy  opt upserter
      └── render               ·         ·
           │                   render 0  k
           │                   render 1  v
           │                   render 2  v
           └── limit           ·         ·
                │              count     2
                └── sort       ·         ·
                     │         order     -v
                     └── scan  ·         ·
·                              table     kv@primary
·                              spans     ALL

# Use subset of explicit target columns (which cannot use blind KV Put).
query TTT
SELECT tree, field, description FROM [
EXPLAIN (VERBOSE) UPSERT INTO kv (k) SELECT k FROM kv ORDER BY v DESC LIMIT 2
]
----
count                                    ·         ·
 └── upsert                              ·         ·
      │                                  into      kv(k, v)
      │                                  strategy  opt upserter
      └── render                         ·         ·
           │                             render 0  k
           │                             render 1  column5
           │                             render 2  k
           └── lookup-join               ·         ·
                │                        table     kv@primary
                │                        type      inner
                └── render               ·         ·
                     │                   render 0  CAST(NULL AS INT8)
                     │                   render 1  k
                     └── limit           ·         ·
                          │              count     2
                          └── sort       ·         ·
                               │         order     -v
                               └── scan  ·         ·
·                                        table     kv@primary
·                                        spans     ALL

# Use Upsert with indexed table, default columns, computed columns, and check
# columns.
statement ok
CREATE TABLE indexed (
  a INT PRIMARY KEY,
  b INT,
  c INT DEFAULT(10),
  d INT AS (a + c) STORED,
  UNIQUE INDEX secondary (d, b),
  CHECK (c > 0)
)

# Should fetch existing values since there is a secondary index.
query TTT
SELECT tree, field, description FROM [
EXPLAIN (VERBOSE) UPSERT INTO indexed VALUES (1)
]
----
count                            ·              ·
 └── upsert                      ·              ·
      │                          into           indexed(a, b, c, d)
      │                          strategy       opt upserter
      └── render                 ·              ·
           │                     render 0       column1
           │                     render 1       column6
           │                     render 2       column7
           │                     render 3       column8
           │                     render 4       a
           │                     render 5       b
           │                     render 6       c
           │                     render 7       d
           │                     render 8       column6
           │                     render 9       column7
           │                     render 10      column8
           │                     render 11      a
           │                     render 12      check1
           └── render            ·              ·
                │                render 0       column7 > 0
                │                render 1       column1
                │                render 2       column6
                │                render 3       column7
                │                render 4       column8
                │                render 5       a
                │                render 6       b
                │                render 7       c
                │                render 8       d
                └── hash-join    ·              ·
                     │           type           left outer
                     ├── values  ·              ·
                     │           size           4 columns, 1 row
                     │           row 0, expr 0  1
                     │           row 0, expr 1  CAST(NULL AS INT8)
                     │           row 0, expr 2  10
                     │           row 0, expr 3  11
                     └── scan    ·              ·
·                                table          indexed@primary
·                                spans          /1-/1/#

# Drop index and verify that existing values no longer need to be fetched.
statement ok
DROP INDEX indexed@secondary CASCADE

query TTT
SELECT tree, field, description FROM [
EXPLAIN (VERBOSE) UPSERT INTO indexed VALUES (1) RETURNING *
]
----
run                    ·              ·
 └── upsert            ·              ·
      │                into           indexed(a, b, c, d)
      │                strategy       opt upserter
      └── render       ·              ·
           │           render 0       column1
           │           render 1       column6
           │           render 2       column7
           │           render 3       column8
           │           render 4       column6
           │           render 5       column7
           │           render 6       column8
           │           render 7       check1
           └── values  ·              ·
·                      size           5 columns, 1 row
·                      row 0, expr 0  1
·                      row 0, expr 1  CAST(NULL AS INT8)
·                      row 0, expr 2  10
·                      row 0, expr 3  11
·                      row 0, expr 4  true

# Regression test for #25726.
# UPSERT over tables with column families, on the fast path, use the
# INSERT logic. This has special casing for column families of 1
# column, and another special casing for column families of 2+
# columns. The special casing is only for families that do not include
# the primary key. So we need a table with 3 families: 1 for the PK, 1
# with just 1 col, and 1 with 2+ cols.
statement ok
CREATE TABLE tu (a INT PRIMARY KEY, b INT, c INT, d INT, FAMILY (a), FAMILY (b), FAMILY (c,d));
  INSERT INTO tu VALUES (1, 2, 3, 4)

statement ok
SET tracing = on,kv,results; UPSERT INTO tu VALUES (1, NULL, NULL, NULL); SET tracing = off

query T
SELECT message FROM [SHOW KV TRACE FOR SESSION]
 WHERE operation != 'dist sender send'
----
Put /Table/55/1/1/0 -> /TUPLE/
Del /Table/55/1/1/1/1
Del /Table/55/1/1/2/1
fast path completed
rows affected: 1

# KV operations.
statement ok
CREATE DATABASE t; CREATE TABLE t.kv(k INT PRIMARY KEY, v INT)

statement ok
CREATE UNIQUE INDEX woo ON t.kv(v)

statement ok
SET tracing = on,kv,results; UPSERT INTO t.kv(k, v) VALUES (2,3); SET tracing = off

query TT
SELECT operation, message FROM [SHOW KV TRACE FOR SESSION]
 WHERE operation != 'dist sender send'
----
table reader  Scan /Table/57/1/2{-/#}
flow          CPut /Table/57/1/2/0 -> /TUPLE/2:2:Int/3
flow          InitPut /Table/57/2/3/0 -> /BYTES/0x8a
flow          fast path completed
sql txn       rows affected: 1

statement ok
SET tracing = on,kv,results; UPSERT INTO t.kv(k, v) VALUES (1,2); SET tracing = off

query TT
SELECT operation, message FROM [SHOW KV TRACE FOR SESSION]
 WHERE operation != 'dist sender send'
----
table reader  Scan /Table/57/1/1{-/#}
flow          CPut /Table/57/1/1/0 -> /TUPLE/2:2:Int/2
flow          InitPut /Table/57/2/2/0 -> /BYTES/0x89
flow          fast path completed
sql txn       rows affected: 1

statement error duplicate key value
SET tracing = on,kv,results; UPSERT INTO t.kv(k, v) VALUES (2,2); SET tracing = off

query TT
set tracing=off;
SELECT operation, message FROM [SHOW KV TRACE FOR SESSION]
 WHERE operation != 'dist sender send'
----
table reader  Scan /Table/57/1/2{-/#}
table reader  fetched: /kv/primary/2/v -> /3
flow          Put /Table/57/1/2/0 -> /TUPLE/2:2:Int/2
flow          Del /Table/57/2/3/0
flow          CPut /Table/57/2/2/0 -> /BYTES/0x8a (expecting does not exist)
sql txn       execution failed after 0 rows: duplicate key value (v)=(2) violates unique constraint "woo"


subtest regression_32473

statement ok
CREATE TABLE customers (
  customer_id serial PRIMARY KEY,
  name VARCHAR UNIQUE,
  email VARCHAR NOT NULL
);

statement ok
INSERT INTO customers (name, email) VALUES ('bob', 'bob@email.com') ON CONFLICT (name)
  DO UPDATE SET (name, email) = (
    SELECT 'bob', 'otherbob@email.com'
  )

query TT
SELECT name, email FROM customers
----
bob  bob@email.com

# This statement only works with the optimizer enabled.
statement ok
INSERT INTO customers (name, email) VALUES ('bob', 'bob@email.com') ON CONFLICT (name)
  DO UPDATE SET (name, email) = (
    SELECT 'bob2', 'otherbob@email.com'
  )

query TT
SELECT name, email FROM customers
----
bob2  otherbob@email.com

statement ok
DROP TABLE customers

# The CBO behaves differently than the HP and PG in this case. It only checks
# constraints if an insert or update actually occurs. In this case, the DO
# NOTHING clause skips the update, so there is no need to check the constraint.
statement ok
CREATE TABLE t5 (k INT PRIMARY KEY, a INT, b int CHECK (a > b))

statement ok
INSERT INTO t5 VALUES (1, 10, 9) ON CONFLICT (k) DO NOTHING

statement ok
INSERT INTO t5 VALUES (1, 10, 20) ON CONFLICT (k) DO NOTHING

# Regression test for #35564: make sure we use the Upsert's input required
# ordering for the internal projection.

statement ok
CREATE TABLE abc (a INT, b INT, c INT, INDEX(c) STORING(a,b))

statement ok
CREATE TABLE xyz (x INT, y INT, z INT)

query TTTTT
EXPLAIN (VERBOSE) SELECT * FROM [UPSERT INTO xyz SELECT a, b, c FROM abc RETURNING z] ORDER BY z
----
render                         ·         ·                    (z)                          +z
 │                             render 0  z                    ·                            ·
 └── run                       ·         ·                    (x, y, z, rowid[hidden])     ·
      └── upsert               ·         ·                    (x, y, z, rowid[hidden])     ·
           │                   into      xyz(x, y, z, rowid)  ·                            ·
           │                   strategy  opt upserter         ·                            ·
           └── render          ·         ·                    (a, b, c, column9, a, b, c)  +c
                │              render 0  a                    ·                            ·
                │              render 1  b                    ·                            ·
                │              render 2  c                    ·                            ·
                │              render 3  column9              ·                            ·
                │              render 4  a                    ·                            ·
                │              render 5  b                    ·                            ·
                │              render 6  c                    ·                            ·
                └── render     ·         ·                    (column9, a, b, c)           +c
                     │         render 0  unique_rowid()       ·                            ·
                     │         render 1  a                    ·                            ·
                     │         render 2  b                    ·                            ·
                     │         render 3  c                    ·                            ·
                     └── scan  ·         ·                    (a, b, c)                    +c
·                              table     abc@abc_c_idx        ·                            ·
·                              spans     ALL                  ·                            ·

# ------------------------------------------------------------------------------
# Regression for #35364. This tests behavior that is different between the CBO
# and the HP. The CBO will (deliberately) round any input columns *before*
# evaluating any computed columns, as well as rounding the output.
# ------------------------------------------------------------------------------

statement ok
CREATE TABLE t35364(
    x DECIMAL(10,0) CHECK(round(x) = x) PRIMARY KEY,
    y DECIMAL(10,0) DEFAULT (1.5),
    z DECIMAL(10,0) AS (x+y+2.5) STORED CHECK(z >= 7)
)

query TTT
UPSERT INTO t35364 (x) VALUES (1.5) RETURNING *
----
2  2  7

query TTT
UPSERT INTO t35364 (x, y) VALUES (1.5, 2.5) RETURNING *
----
2  3  8

query TTT
INSERT INTO t35364 (x) VALUES (1.5) ON CONFLICT (x) DO UPDATE SET x=2.5 RETURNING *
----
3  3  9

statement error pq: failed to satisfy CHECK constraint \(z >= 7\)
UPSERT INTO t35364 (x) VALUES (0)
