# LogicTest: local

statement ok
CREATE TABLE t(x INT PRIMARY KEY)

statement ok
CREATE TABLE t2(x INT PRIMARY KEY)

# Check that if a mutation uses further processing, a spool is added.
query T
EXPLAIN WITH a AS (INSERT INTO t SELECT * FROM t2 RETURNING x)
        SELECT * FROM a LIMIT 1
----
distribution: local
vectorized: true
·
• root
│
├── • limit
│   │ count: 1
│   │
│   └── • scan buffer
│         label: buffer 1 (a)
│
└── • subquery
    │ id: @S1
    │ original sql: INSERT INTO t SELECT * FROM t2 RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1 (a)
        │
        └── • insert
            │ into: t(x)
            │
            └── • scan
                  missing stats
                  table: t2@t2_pkey
                  spans: FULL SCAN

query T
EXPLAIN WITH a AS (DELETE FROM t RETURNING x)
        SELECT * FROM a LIMIT 1
----
distribution: local
vectorized: true
·
• root
│
├── • limit
│   │ count: 1
│   │
│   └── • scan buffer
│         label: buffer 1 (a)
│
└── • subquery
    │ id: @S1
    │ original sql: DELETE FROM t RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1 (a)
        │
        └── • delete
            │ from: t
            │
            └── • scan
                  missing stats
                  table: t@t_pkey
                  spans: FULL SCAN
                  locking strength: for update


query T
EXPLAIN WITH a AS (UPDATE t SET x = x + 1 RETURNING x)
        SELECT * FROM a LIMIT 1
----
distribution: local
vectorized: true
·
• root
│
├── • limit
│   │ count: 1
│   │
│   └── • scan buffer
│         label: buffer 1 (a)
│
└── • subquery
    │ id: @S1
    │ original sql: UPDATE t SET x = x + 1 RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1 (a)
        │
        └── • update
            │ table: t
            │ set: x
            │
            └── • render
                │
                └── • scan
                      missing stats
                      table: t@t_pkey
                      spans: FULL SCAN
                      locking strength: for update

query T
EXPLAIN WITH a AS (UPSERT INTO t VALUES (2), (3) RETURNING x)
        SELECT * FROM a LIMIT 1
----
distribution: local
vectorized: true
·
• root
│
├── • limit
│   │ count: 1
│   │
│   └── • scan buffer
│         estimated row count: 2
│         label: buffer 1 (a)
│
└── • subquery
    │ id: @S1
    │ original sql: UPSERT INTO t VALUES (2), (3) RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1 (a)
        │
        └── • upsert
            │ estimated row count: 2
            │ into: t(x)
            │
            └── • values
                  size: 1 column, 2 rows

# Ditto all mutations, with the statement source syntax.
query T
EXPLAIN SELECT * FROM [INSERT INTO t SELECT * FROM t2 RETURNING x] LIMIT 1
----
distribution: local
vectorized: true
·
• root
│
├── • limit
│   │ count: 1
│   │
│   └── • scan buffer
│         label: buffer 1
│
└── • subquery
    │ id: @S1
    │ original sql: INSERT INTO t SELECT * FROM t2 RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1
        │
        └── • insert
            │ into: t(x)
            │
            └── • scan
                  missing stats
                  table: t2@t2_pkey
                  spans: FULL SCAN

query T
EXPLAIN SELECT * FROM [DELETE FROM t RETURNING x] LIMIT 1
----
distribution: local
vectorized: true
·
• root
│
├── • limit
│   │ count: 1
│   │
│   └── • scan buffer
│         label: buffer 1
│
└── • subquery
    │ id: @S1
    │ original sql: DELETE FROM t RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1
        │
        └── • delete
            │ from: t
            │
            └── • scan
                  missing stats
                  table: t@t_pkey
                  spans: FULL SCAN
                  locking strength: for update

query T
EXPLAIN SELECT * FROM [UPDATE t SET x = x + 1 RETURNING x] LIMIT 1
----
distribution: local
vectorized: true
·
• root
│
├── • limit
│   │ count: 1
│   │
│   └── • scan buffer
│         label: buffer 1
│
└── • subquery
    │ id: @S1
    │ original sql: UPDATE t SET x = x + 1 RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1
        │
        └── • update
            │ table: t
            │ set: x
            │
            └── • render
                │
                └── • scan
                      missing stats
                      table: t@t_pkey
                      spans: FULL SCAN
                      locking strength: for update

query T
EXPLAIN SELECT * FROM [UPSERT INTO t VALUES (2), (3) RETURNING x] LIMIT 1
----
distribution: local
vectorized: true
·
• root
│
├── • limit
│   │ count: 1
│   │
│   └── • scan buffer
│         estimated row count: 2
│         label: buffer 1
│
└── • subquery
    │ id: @S1
    │ original sql: UPSERT INTO t VALUES (2), (3) RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1
        │
        └── • upsert
            │ estimated row count: 2
            │ into: t(x)
            │
            └── • values
                  size: 1 column, 2 rows

# Check that a spool is also inserted for other processings than LIMIT.
query T
EXPLAIN SELECT count(*) FROM [INSERT INTO t SELECT * FROM t2 RETURNING x]
----
distribution: local
vectorized: true
·
• root
│
├── • group (scalar)
│   │
│   └── • scan buffer
│         label: buffer 1
│
└── • subquery
    │ id: @S1
    │ original sql: INSERT INTO t SELECT * FROM t2 RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1
        │
        └── • insert
            │ into: t(x)
            │
            └── • scan
                  missing stats
                  table: t2@t2_pkey
                  spans: FULL SCAN

query T
EXPLAIN SELECT * FROM [INSERT INTO t SELECT * FROM t2 RETURNING x], t
----
distribution: local
vectorized: true
·
• root
│
├── • cross join
│   │
│   ├── • scan buffer
│   │     label: buffer 1
│   │
│   └── • scan
│         missing stats
│         table: t@t_pkey
│         spans: FULL SCAN
│
└── • subquery
    │ id: @S1
    │ original sql: INSERT INTO t SELECT * FROM t2 RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1
        │
        └── • insert
            │ into: t(x)
            │
            └── • scan
                  missing stats
                  table: t2@t2_pkey
                  spans: FULL SCAN

# Check that if a spool is already added at some level, then it is not added
# again at levels below.
# TODO(andyk): This optimization is not part of CBO yet.
query T
EXPLAIN WITH a AS (INSERT INTO t SELECT * FROM t2 RETURNING x),
             b AS (INSERT INTO t SELECT x+1 FROM a RETURNING x)
        SELECT * FROM b LIMIT 1
----
distribution: local
vectorized: true
·
• root
│
├── • limit
│   │ count: 1
│   │
│   └── • scan buffer
│         label: buffer 2 (b)
│
├── • subquery
│   │ id: @S1
│   │ original sql: INSERT INTO t SELECT * FROM t2 RETURNING x
│   │ exec mode: discard all rows
│   │
│   └── • buffer
│       │ label: buffer 1 (a)
│       │
│       └── • insert
│           │ into: t(x)
│           │
│           └── • scan
│                 missing stats
│                 table: t2@t2_pkey
│                 spans: FULL SCAN
│
└── • subquery
    │ id: @S2
    │ original sql: INSERT INTO t SELECT x + 1 FROM a RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 2 (b)
        │
        └── • insert
            │ into: t(x)
            │
            └── • render
                │
                └── • scan buffer
                      label: buffer 1 (a)

# Check that no spool is inserted if a top-level render is elided.
query T
EXPLAIN SELECT * FROM [INSERT INTO t SELECT * FROM t2 RETURNING x]
----
distribution: local
vectorized: true
·
• root
│
├── • scan buffer
│     label: buffer 1
│
└── • subquery
    │ id: @S1
    │ original sql: INSERT INTO t SELECT * FROM t2 RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1
        │
        └── • insert
            │ into: t(x)
            │
            └── • scan
                  missing stats
                  table: t2@t2_pkey
                  spans: FULL SCAN

# Check that no spool is used for a top-level INSERT, but
# sub-INSERTs still get a spool.
query T
EXPLAIN INSERT INTO t SELECT x+1 FROM [INSERT INTO t SELECT * FROM t2 RETURNING x]
----
distribution: local
vectorized: true
·
• root
│
├── • insert
│   │ into: t(x)
│   │
│   └── • render
│       │
│       └── • scan buffer
│             label: buffer 1
│
└── • subquery
    │ id: @S1
    │ original sql: INSERT INTO t SELECT * FROM t2 RETURNING x
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1
        │
        └── • insert
            │ into: t(x)
            │
            └── • scan
                  missing stats
                  table: t2@t2_pkey
                  spans: FULL SCAN

# Check that simple computations using RETURNING get their spool pulled up.
query T
EXPLAIN SELECT * FROM [INSERT INTO t SELECT * FROM t2 RETURNING x+10 AS r] WHERE r < 3 LIMIT 10
----
distribution: local
vectorized: true
·
• root
│
├── • limit
│   │ count: 10
│   │
│   └── • filter
│       │ filter: r < 3
│       │
│       └── • scan buffer
│             label: buffer 1
│
└── • subquery
    │ id: @S1
    │ original sql: INSERT INTO t SELECT * FROM t2 RETURNING x + 10 AS r
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1
        │
        └── • render
            │
            └── • insert
                │ into: t(x)
                │
                └── • scan
                      missing stats
                      table: t2@t2_pkey
                      spans: FULL SCAN

# Check that a pulled up spool gets elided at the top level.
query T
EXPLAIN SELECT * FROM [INSERT INTO t SELECT * FROM t2 RETURNING x+10 AS r] WHERE r < 3
----
distribution: local
vectorized: true
·
• root
│
├── • filter
│   │ filter: r < 3
│   │
│   └── • scan buffer
│         label: buffer 1
│
└── • subquery
    │ id: @S1
    │ original sql: INSERT INTO t SELECT * FROM t2 RETURNING x + 10 AS r
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1
        │
        └── • render
            │
            └── • insert
                │ into: t(x)
                │
                └── • scan
                      missing stats
                      table: t2@t2_pkey
                      spans: FULL SCAN
