# LogicTest: local

query T
EXPLAIN (PLAN) SELECT 1 FROM system.jobs WHERE FALSE
----
distribution: local
vectorized: true
·
• norows

query T
EXPLAIN (PLAN) SELECT 1 FROM system.jobs WHERE NULL
----
distribution: local
vectorized: true
·
• norows

query T
EXPLAIN (PLAN, VERBOSE) SELECT 1 a
----
distribution: local
vectorized: true
·
• values
  columns: (a)
  size: 1 column, 1 row
  row 0, expr 0: 1

query T
EXPLAIN (VERBOSE,PLAN) SELECT 1 a
----
distribution: local
vectorized: true
·
• values
  columns: (a)
  size: 1 column, 1 row
  row 0, expr 0: 1


query T
EXPLAIN (TYPES) SELECT 1 a
----
distribution: local
vectorized: true
·
• values
  columns: (a int)
  size: 1 column, 1 row
  row 0, expr 0: (1)[int]

statement error cannot set EXPLAIN mode more than once
EXPLAIN (PLAN,PLAN) SELECT 1

statement error cannot set EXPLAIN mode more than once
EXPLAIN (PLAN,DISTSQL) SELECT 1

statement error unsupported EXPLAIN option
EXPLAIN (PLAN,UNKNOWN) SELECT 1

statement error could not determine data type of placeholder \$1
EXPLAIN (TYPES) SELECT $1

# TODO(radu): we don't support placeholders with no values.
#query TTTTT colnames
#EXPLAIN (TYPES) SELECT $1::INT AS a
#----
#Tree           Field     Description               Columns          Ordering
#render         ·         ·                         (a int)          a=CONST
# │             render 0  (($1)[string]::INT)[int]  ·                ·
# └── emptyrow  ·         ·                         ()               ·


# Ensure that all relevant statement types can be explained
query T
EXPLAIN CREATE DATABASE foo
----
distribution: local
vectorized: true
·
• create database

query T
EXPLAIN CREATE TABLE foo (x INT)
----
distribution: local
vectorized: true
·
• create table

statement ok
CREATE TABLE foo (x INT)

query T
EXPLAIN CREATE INDEX a ON foo(x)
----
distribution: local
vectorized: true
·
• create index

statement ok
CREATE DATABASE foo

query T
EXPLAIN DROP DATABASE foo
----
distribution: local
vectorized: true
·
• drop database

# explain SHOW JOBS - beware to test this before the CREATE INDEX
# below, otherwise the result becomes non-deterministic.
# Migrations with backfill will affect the number of rows.
query T
SELECT * FROM [EXPLAIN SHOW JOBS] WHERE info NOT LIKE '%size%' AND info NOT LIKE '%filter%'
----
distribution: local
vectorized: true
·
• sort
│ order: -column26,-started
│
└── • render
    │
        │
        └── • virtual table
              table: jobs@primary

statement ok
CREATE INDEX a ON foo(x)

query T
EXPLAIN DROP INDEX foo@a
----
distribution: local
vectorized: true
·
• drop index

query T
EXPLAIN ALTER TABLE foo ADD COLUMN y INT
----
distribution: local
vectorized: true
·
• alter table

query T
EXPLAIN (VERBOSE) ALTER TABLE foo SPLIT AT VALUES (42)
----
distribution: local
vectorized: true
·
• split
│ columns: (key, pretty, split_enforced_until)
│ estimated row count: 10 (missing stats)
│ index: foo@foo_pkey
│ expiry: CAST(NULL AS STRING)
│
└── • values
      columns: (column1)
      size: 1 column, 1 row
      row 0, expr 0: 42

query T
EXPLAIN (VERBOSE) ALTER TABLE foo SPLIT AT VALUES (42) WITH EXPIRATION '2200-01-01 00:00:00.0'
----
distribution: local
vectorized: true
·
• split
│ columns: (key, pretty, split_enforced_until)
│ estimated row count: 10 (missing stats)
│ index: foo@foo_pkey
│ expiry: '2200-01-01 00:00:00.0'
│
└── • values
      columns: (column1)
      size: 1 column, 1 row
      row 0, expr 0: 42

query T
EXPLAIN ALTER TABLE foo RELOCATE VALUES (ARRAY[1], 1)
----
distribution: local
vectorized: true
·
• relocate table
│ index: foo@foo_pkey
│
└── • values
      size: 2 columns, 1 row

query T
EXPLAIN (VERBOSE) ALTER TABLE foo RELOCATE VALUES (ARRAY[1], 1)
----
distribution: local
vectorized: true
·
• relocate table
│ columns: (key, pretty)
│ estimated row count: 10 (missing stats)
│ index: foo@foo_pkey
│
└── • values
      columns: (column1, column2)
      size: 2 columns, 1 row
      row 0, expr 0: ARRAY[1]
      row 0, expr 1: 1

query T
EXPLAIN ALTER INDEX foo@a RELOCATE VALUES (ARRAY[1], 1)
----
distribution: local
vectorized: true
·
• relocate table
│ index: foo@a
│
└── • values
      size: 2 columns, 1 row

query T
EXPLAIN (VERBOSE) ALTER INDEX foo@a RELOCATE VALUES (ARRAY[1], 1)
----
distribution: local
vectorized: true
·
• relocate table
│ columns: (key, pretty)
│ estimated row count: 10 (missing stats)
│ index: foo@a
│
└── • values
      columns: (column1, column2)
      size: 2 columns, 1 row
      row 0, expr 0: ARRAY[1]
      row 0, expr 1: 1

query T
EXPLAIN DROP TABLE foo
----
distribution: local
vectorized: true
·
• drop table

query T
EXPLAIN ALTER RANGE 20+2 RELOCATE FROM 30+3 TO 40+4
----
distribution: local
vectorized: true
·
• relocate range
│ replicas: VOTERS
│ to: 44
│ from: 33
│
└── • values
      size: 1 column, 1 row

query T
EXPLAIN (VERBOSE) ALTER RANGE 20+2 RELOCATE LEASE TO 40+4
----
distribution: local
vectorized: true
·
• relocate range
│ columns: (range_id, pretty, result)
│ estimated row count: 10 (missing stats)
│ replicas: LEASE
│ to: 44
│
└── • values
      columns: (column1)
      size: 1 column, 1 row
      row 0, expr 0: 22

query T
EXPLAIN (VERBOSE) ALTER RANGE 20+2 RELOCATE FROM 30+3 TO 40+4
----
distribution: local
vectorized: true
·
• relocate range
│ columns: (range_id, pretty, result)
│ estimated row count: 10 (missing stats)
│ replicas: VOTERS
│ to: 44
│ from: 33
│
└── • values
      columns: (column1)
      size: 1 column, 1 row
      row 0, expr 0: 22

query T
EXPLAIN ALTER RANGE RELOCATE FROM 30+3 TO 40+4 FOR SELECT 20+2
----
distribution: local
vectorized: true
·
• relocate range
│ replicas: VOTERS
│ to: 44
│ from: 33
│
└── • values
      size: 1 column, 1 row

query T
EXPLAIN (VERBOSE) ALTER RANGE RELOCATE FROM 30+3 TO 40+4 FOR SELECT 20+2
----
distribution: local
vectorized: true
·
• relocate range
│ columns: (range_id, pretty, result)
│ estimated row count: 10 (missing stats)
│ replicas: VOTERS
│ to: 44
│ from: 33
│
└── • values
      columns: ("?column?")
      size: 1 column, 1 row
      row 0, expr 0: 22

query T
EXPLAIN SHOW DATABASES
----
distribution: local
vectorized: true
·
• sort
│ order: +name
│
└── • virtual table
      table: databases@primary

query T
SELECT * FROM [EXPLAIN SHOW TABLES] WHERE info NOT LIKE '%size%'
----
distribution: local
vectorized: true
·
• sort
│ order: +nspname,+relname
│
└── • render
    │
    └── • hash join (left outer)
        │ equality: (column80) = (table_id)
        │
        ├── • render
        │   │
        │   └── • hash join (left outer)
        │       │ equality: (column62) = (table_id)
        │       │
        │       ├── • render
        │       │   │
        │       │   └── • hash join (right outer)
        │       │       │ equality: (oid) = (relowner)
        │       │       │
        │       │       ├── • virtual table
        │       │       │     table: pg_roles@primary
        │       │       │
        │       │       └── • hash join
        │       │           │ equality: (oid) = (relnamespace)
        │       │           │
        │       │           ├── • filter
        │       │           │   │ filter: nspname NOT IN ('crdb_internal', 'information_schema', __more1_10__, 'pg_extension')
        │       │           │   │
        │       │           │   └── • virtual table
        │       │           │         table: pg_namespace@primary
        │       │           │
        │       │           └── • filter
        │       │               │ filter: relkind IN ('S', 'm', __more1_10__, 'v')
        │       │               │
        │       │               └── • virtual table
        │       │                     table: pg_class@primary
        │       │
        │       └── • virtual table
        │             table: table_row_statistics@primary
        │
        └── • virtual table
              table: tables@tables_database_name_idx (partial index)
              spans: [/'test' - /'test']


query T
SELECT * FROM [EXPLAIN SHOW TABLES WITH COMMENT] WHERE info NOT LIKE '%size%'
----
distribution: local
vectorized: true
·
• sort
│ order: +nspname,+relname
│
└── • render
    │
    └── • hash join (left outer)
        │ equality: (column95) = (table_id)
        │
        ├── • render
        │   │
        │   └── • hash join (left outer)
        │       │ equality: (column77) = (table_id)
        │       │
        │       ├── • render
        │       │   │
        │       │   └── • hash join (right outer)
        │       │       │ equality: (oid) = (relowner)
        │       │       │
        │       │       ├── • virtual table
        │       │       │     table: pg_roles@primary
        │       │       │
        │       │       └── • hash join
        │       │           │ equality: (oid) = (relnamespace)
        │       │           │
        │       │           ├── • filter
        │       │           │   │ filter: nspname NOT IN ('crdb_internal', 'information_schema', __more1_10__, 'pg_extension')
        │       │           │   │
        │       │           │   └── • virtual table
        │       │           │         table: pg_namespace@primary
        │       │           │
        │       │           └── • merge join (left outer)
        │       │               │ equality: (oid) = (objoid)
        │       │               │
        │       │               ├── • filter
        │       │               │   │ filter: relkind IN ('S', 'm', __more1_10__, 'v')
        │       │               │   │
        │       │               │   └── • virtual table
        │       │               │         table: pg_class@pg_class_oid_idx
        │       │               │
        │       │               └── • sort
        │       │                   │ order: +objoid
        │       │                   │
        │       │                   └── • render
        │       │                       │
        │       │                       └── • filter
        │       │                           │ filter: objsubid = 0
        │       │                           │
        │       │                           └── • render
        │       │                               │
        │       │                               └── • filter
        │       │                                   │ filter: classoid = 4294967083
        │       │                                   │
        │       │                                   └── • virtual table
        │       │                                         table: kv_catalog_comments@primary
        │       │
        │       └── • virtual table
        │             table: table_row_statistics@primary
        │
        └── • virtual table
              table: tables@tables_database_name_idx (partial index)
              spans: [/'test' - /'test']

query T
SELECT * FROM [EXPLAIN SHOW DATABASE] WHERE info NOT LIKE '%size%'
----
distribution: local
vectorized: true
·
• filter
│ filter: variable = 'database'
│
└── • virtual table
      table: session_variables@primary

query T
SELECT * FROM [EXPLAIN SHOW SCHEMAS] WHERE info NOT LIKE '%size%'
----
distribution: local
vectorized: true
·
• sort
│ order: +nspname
│
└── • hash join (right outer)
    │ equality: (oid) = (nspowner)
    │
    ├── • virtual table
    │     table: pg_roles@primary
    │
    └── • hash join
        │ equality: (nspname) = (schema_name)
        │
        ├── • virtual table
        │     table: pg_namespace@primary
        │
        └── • filter
            │ filter: catalog_name = 'test'
            │
            └── • virtual table
                  table: schemata@primary

query T
SELECT * FROM [EXPLAIN SHOW TIME ZONE] WHERE info NOT LIKE '%size%'
----
distribution: local
vectorized: true
·
• show

query T
SELECT * FROM [EXPLAIN SHOW DEFAULT_TRANSACTION_ISOLATION] WHERE info NOT LIKE '%size%'
----
distribution: local
vectorized: true
·
• show

query T
SELECT * FROM [EXPLAIN SHOW DEFAULT_TRANSACTION_PRIORITY] WHERE info NOT LIKE '%size%'
----
distribution: local
vectorized: true
·
• show

query T
SELECT * FROM [EXPLAIN SHOW DEFAULT_TRANSACTION_USE_FOLLOWER_READS] WHERE info NOT LIKE '%size%'
----
distribution: local
vectorized: true
·
• show

query T
SELECT * FROM [EXPLAIN SHOW TRANSACTION ISOLATION LEVEL] WHERE info NOT LIKE '%size%'
----
distribution: local
vectorized: true
·
• show

query T
SELECT * FROM [EXPLAIN SHOW TRANSACTION PRIORITY] WHERE info NOT LIKE '%size%'
----
distribution: local
vectorized: true
·
• show

query T
EXPLAIN SHOW COLUMNS FROM foo
----
distribution: local
vectorized: true
·
• sort
│ order: +ordinal_position,+column_name,+crdb_sql_type,+is_nullable,+column_default,+generation_expression,+indices,+is_hidden
│
└── • render
    │
    └── • group (hash)
        │ group by: column_name, ordinal_position, column_default, is_nullable, generation_expression, is_hidden, crdb_sql_type
        │
        └── • sort
            │ order: +index_name
            │
            └── • hash join (left outer)
                │ equality: (column_name) = (column_name)
                │
                ├── • filter
                │   │ filter: ((table_catalog = 'test') AND (table_schema = 'public')) AND (table_name = 'foo')
                │   │
                │   └── • virtual table
                │         table: columns@primary
                │
                └── • filter
                    │ filter: ((table_catalog = 'test') AND (table_schema = 'public')) AND (table_name = 'foo')
                    │
                    └── • virtual table
                          table: statistics@primary

query T
SELECT * FROM [EXPLAIN SHOW GRANTS ON foo] WHERE info NOT LIKE '%size%'
----
distribution: local
vectorized: true
·
• root
│
├── • sort
│   │ order: +database_name,+schema_name,+table_name,+grantee,+privilege_type,+is_grantable
│   │
│   └── • distinct
│       │ distinct on: database_name, schema_name, table_name, grantee, privilege_type, is_grantable
│       │
│       └── • render
│           │
│           └── • union all
│               │
│               ├── • hash join
│               │   │ equality: (role) = (grantee)
│               │   │
│               │   ├── • virtual table
│               │   │     table: kv_inherited_role_members@primary
│               │   │
│               │   └── • scan buffer
│               │         label: buffer 1 (r)
│               │
│               └── • scan buffer
│                     label: buffer 1 (r)
│
└── • subquery
    │ id: @S1
    │ original sql: SELECT * FROM (SELECT tp.table_catalog AS database_name, tp.table_schema AS schema_name, tp.table_name, tp.grantee, tp.privilege_type, tp.is_grantable::BOOL, CASE WHEN s.sequence_name IS NOT NULL THEN 'sequence' ELSE 'table' END AS object_type FROM "".information_schema.table_privileges AS tp LEFT JOIN "".information_schema.sequences AS s ON (((tp.table_catalog = s.sequence_catalog) AND (tp.table_schema = s.sequence_schema)) AND (tp.table_name = s.sequence_name))) WHERE (database_name, schema_name, table_name) IN (('test', 'public', 'foo'),)
    │ exec mode: discard all rows
    │
    └── • buffer
        │ label: buffer 1 (r)
        │
        └── • render
            │
            └── • merge join (left outer)
                │ equality: (table_catalog, table_schema, table_name) = (sequence_catalog, sequence_schema, sequence_name)
                │
                ├── • filter
                │   │ filter: ((table_catalog = 'test') AND (table_schema = 'public')) AND (table_name = 'foo')
                │   │
                │   └── • virtual table
                │         table: table_privileges@primary
                │
                └── • filter
                    │ filter: ((sequence_catalog = 'test') AND (sequence_schema = 'public')) AND (sequence_name = 'foo')
                    │
                    └── • virtual table
                          table: sequences@primary

query T
EXPLAIN SHOW INDEX FROM foo
----
distribution: local
vectorized: true
·
• sort
│ order: +index_name,+seq_in_index
│
└── • render
    │
    └── • virtual table lookup join
        │ table: pg_namespace@pg_namespace_oid_idx
        │ equality: (relnamespace) = (oid)
        │ pred: nspname = index_schema
        │
        └── • hash join
            │ equality: (indexrelid, indrelid) = (oid, oid)
            │
            ├── • virtual table
            │     table: pg_index@primary
            │
            └── • hash join
                │ equality: (relname, relnamespace) = (index_name, relnamespace)
                │
                ├── • virtual table
                │     table: pg_class@primary
                │
                └── • merge join
                    │ equality: (relname) = (table_name)
                    │
                    ├── • filter
                    │   │ filter: relname = 'foo'
                    │   │
                    │   └── • virtual table
                    │         table: pg_class@primary
                    │
                    └── • filter
                        │ filter: ((table_name = 'foo') AND (table_catalog = 'test')) AND (table_schema = 'public')
                        │
                        └── • virtual table
                              table: statistics@primary

query T
EXPLAIN SHOW CONSTRAINTS FROM foo
----
distribution: local
vectorized: true
·
• sort
│ order: +conname,+constraint_type,+condef,+convalidated
│
└── • render
    │
    └── • hash join
        │ equality: (relnamespace) = (oid)
        │
        ├── • virtual table lookup join
        │   │ table: pg_constraint@pg_constraint_conrelid_idx
        │   │ equality: (oid) = (conrelid)
        │   │
        │   └── • filter
        │       │ filter: relname = 'foo'
        │       │
        │       └── • virtual table
        │             table: pg_class@primary
        │
        └── • filter
            │ filter: nspname = 'public'
            │
            └── • virtual table
                  table: pg_namespace@primary

statement OK
ANALYZE system.users

statement OK
ANALYZE system.role_options

statement OK
ANALYZE system.role_members

query T retry
EXPLAIN SHOW USERS
----
distribution: local
vectorized: true
·
• sort
│ estimated row count: 3
│ order: +username
│
└── • render
    │
    └── • group (hash)
        │ estimated row count: 3
        │ group by: username
        │
        └── • sort
            │ estimated row count: 3
            │ order: +"role"
            │
            └── • merge join (right outer)
                │ estimated row count: 3
                │ equality: (member) = (username)
                │ right cols are key
                │
                ├── • scan
                │     estimated row count: 1 (100% of the table; stats collected <hidden> ago)
                │     table: role_members@role_members_member_idx
                │     spans: FULL SCAN
                │
                └── • group (streaming)
                    │ estimated row count: 3
                    │ group by: username
                    │ ordered: +username
                    │
                    └── • sort
                        │ estimated row count: 3
                        │ order: +username,+option
                        │
                        └── • render
                            │
                            └── • hash join (left outer)
                                │ estimated row count: 3
                                │ equality: (username) = (username)
                                │ left cols are key
                                │
                                ├── • scan
                                │     estimated row count: 3 (100% of the table; stats collected <hidden> ago)
                                │     table: users@users_user_id_idx
                                │     spans: FULL SCAN
                                │
                                └── • scan
                                      estimated row count: 1 (100% of the table; stats collected <hidden> ago)
                                      table: role_options@primary
                                      spans: FULL SCAN

# EXPLAIN selecting from a sequence.
statement ok
CREATE SEQUENCE select_test

query T
EXPLAIN (VERBOSE) SELECT * FROM select_test
----
distribution: local
vectorized: true
·
• sequence select
  columns: (last_value, log_cnt, is_called)
  estimated row count: 1

query T
EXPLAIN (VERBOSE) SELECT last_value FROM select_test
----
distribution: local
vectorized: true
·
• project
│ columns: (last_value)
│
└── • sequence select
      columns: (last_value, log_cnt, is_called)
      estimated row count: 1

statement ok
CREATE TABLE t (
  k INT PRIMARY KEY,
  v INT,
  FAMILY "primary" (k, v)
)

query T
EXPLAIN INSERT INTO t VALUES (1, 2)
----
distribution: local
vectorized: true
·
• insert fast path
  into: t(k, v)
  auto commit
  size: 2 columns, 1 row

statement ok
INSERT INTO t VALUES (1, 2)

query T
EXPLAIN SELECT * FROM t
----
distribution: local
vectorized: true
·
• scan
  missing stats
  table: t@t_pkey
  spans: FULL SCAN

query T
EXPLAIN (VERBOSE) SELECT * FROM t
----
distribution: local
vectorized: true
·
• scan
  columns: (k, v)
  estimated row count: 1,000 (missing stats)
  table: t@t_pkey
  spans: FULL SCAN

query T
EXPLAIN SELECT * FROM t WHERE k = 1 OR k = 3
----
distribution: local
vectorized: true
·
• scan
  missing stats
  table: t@t_pkey
  spans: [/1 - /1] [/3 - /3]

statement ok
CREATE TABLE t2 (x INT PRIMARY KEY)

query T
EXPLAIN (PLAN) SELECT * FROM t INNER LOOKUP JOIN t2 ON t.k = t2.x
----
distribution: local
vectorized: true
·
• lookup join
│ table: t2@t2_pkey
│ equality: (k) = (x)
│ equality cols are key
│
└── • scan
      missing stats
      table: t@t_pkey
      spans: FULL SCAN

query T
EXPLAIN (VERBOSE) SELECT * FROM t WHERE k % 2 = 0
----
distribution: local
vectorized: true
·
• filter
│ columns: (k, v)
│ estimated row count: 333 (missing stats)
│ filter: (k % 2) = 0
│
└── • scan
      columns: (k, v)
      estimated row count: 1,000 (missing stats)
      table: t@t_pkey
      spans: FULL SCAN

query T
EXPLAIN VALUES (1, 2, 3), (4, 5, 6)
----
distribution: local
vectorized: true
·
• values
  size: 3 columns, 2 rows

query T
EXPLAIN VALUES (1)
----
distribution: local
vectorized: true
·
• values
  size: 1 column, 1 row

query T
EXPLAIN (VERBOSE) SELECT * FROM t WITH ORDINALITY LIMIT 1 OFFSET 1
----
distribution: local
vectorized: true
·
• limit
│ columns: (k, v, "ordinality")
│ offset: 1
│
└── • ordinality
    │ columns: (k, v, "ordinality")
    │ estimated row count: 2 (missing stats)
    │
    └── • scan
          columns: (k, v)
          estimated row count: 2 (missing stats)
          table: t@t_pkey
          spans: LIMITED SCAN
          limit: 2

query T
EXPLAIN SELECT DISTINCT v FROM t
----
distribution: local
vectorized: true
·
• distinct
│ distinct on: v
│
└── • scan
      missing stats
      table: t@t_pkey
      spans: FULL SCAN

query T
EXPLAIN (VERBOSE) SELECT DISTINCT v FROM t LIMIT 1 OFFSET 1
----
distribution: local
vectorized: true
·
• limit
│ columns: (v)
│ offset: 1
│
└── • limit
    │ columns: (v)
    │ count: 2
    │
    └── • distinct
        │ columns: (v)
        │ estimated row count: 100 (missing stats)
        │ distinct on: v
        │
        └── • scan
              columns: (v)
              estimated row count: 1,000 (missing stats)
              table: t@t_pkey
              spans: FULL SCAN (SOFT LIMIT)

statement ok
CREATE TABLE tc (a INT, b INT, INDEX c(a), FAMILY "primary" (a, b, rowid))

query T
EXPLAIN (VERBOSE) SELECT * FROM tc WHERE a = 10 ORDER BY b
----
distribution: local
vectorized: true
·
• sort
│ columns: (a, b)
│ estimated row count: 10 (missing stats)
│ order: +b
│
└── • index join
    │ columns: (a, b)
    │ estimated row count: 10 (missing stats)
    │ table: tc@tc_pkey
    │ key columns: rowid
    │
    └── • scan
          columns: (a, rowid)
          estimated row count: 10 (missing stats)
          table: tc@c
          spans: /10-/11

# Ensure that the query above no longer has index recommendations if they are
# disabled.
statement ok
SET index_recommendations_enabled = 'false'

query T
EXPLAIN (VERBOSE) SELECT * FROM tc WHERE a = 10 ORDER BY b
----
distribution: local
vectorized: true
·
• sort
│ columns: (a, b)
│ estimated row count: 10 (missing stats)
│ order: +b
│
└── • index join
    │ columns: (a, b)
    │ estimated row count: 10 (missing stats)
    │ table: tc@tc_pkey
    │ key columns: rowid
    │
    └── • scan
          columns: (a, rowid)
          estimated row count: 10 (missing stats)
          table: tc@c
          spans: /10-/11

# Reset index_recommendations_enabled to its default setting.
statement ok
SET index_recommendations_enabled = 'true'

# Regression test for #80910. There should be no index recommendations for
# system tables.
query T
EXPLAIN SELECT * FROM system.table_statistics WHERE name = 'foo'
----
distribution: local
vectorized: true
·
• filter
│ filter: name = 'foo'
│
└── • scan
      missing stats
      table: table_statistics@primary
      spans: FULL SCAN

query T
EXPLAIN (TYPES) INSERT INTO t VALUES (1, 2)
----
distribution: local
vectorized: true
·
• insert fast path
  columns: ()
  estimated row count: 0 (missing stats)
  into: t(k, v)
  auto commit
  size: 2 columns, 1 row
  row 0, expr 0: (1)[int]
  row 0, expr 1: (2)[int]

query T
EXPLAIN (TYPES) SELECT 42 AS a
----
distribution: local
vectorized: true
·
• values
  columns: (a int)
  size: 1 column, 1 row
  row 0, expr 0: (42)[int]

query T
EXPLAIN (TYPES) SELECT * FROM t
----
distribution: local
vectorized: true
·
• scan
  columns: (k int, v int)
  estimated row count: 1,000 (missing stats)
  table: t@t_pkey
  spans: FULL SCAN

query T
EXPLAIN (TYPES,VERBOSE) SELECT k FROM t
----
distribution: local
vectorized: true
·
• scan
  columns: (k int)
  estimated row count: 1,000 (missing stats)
  table: t@t_pkey
  spans: FULL SCAN

query T
EXPLAIN (TYPES) SELECT * FROM t WHERE v > 123
----
distribution: local
vectorized: true
·
• filter
│ columns: (k int, v int)
│ estimated row count: 333 (missing stats)
│ filter: ((v)[int] > (123)[int])[bool]
│
└── • scan
      columns: (k int, v int)
      estimated row count: 1,000 (missing stats)
      table: t@t_pkey
      spans: FULL SCAN
·
index recommendations: 1
1. type: index creation
   SQL command: CREATE INDEX ON test.public.t (v);

query T
EXPLAIN (TYPES) VALUES (1, 2, 3), (4, 5, 6)
----
distribution: local
vectorized: true
·
• values
  columns: (column1 int, column2 int, column3 int)
  size: 3 columns, 2 rows
  row 0, expr 0: (1)[int]
  row 0, expr 1: (2)[int]
  row 0, expr 2: (3)[int]
  row 1, expr 0: (4)[int]
  row 1, expr 1: (5)[int]
  row 1, expr 2: (6)[int]

query T
EXPLAIN (TYPES) SELECT 2*count(k) as z, v FROM t WHERE v>123 GROUP BY v HAVING v<2 AND count(k)>1
----
distribution: local
vectorized: true
·
• render
│ columns: (z int, v int)
│ render z: ((count)[int] * (2)[int])[int]
│ render v: (v)[int]
│
└── • norows
      columns: (v int, count int)

query T
EXPLAIN (TYPES) DELETE FROM t WHERE v > 1
----
distribution: local
vectorized: true
·
• delete
│ columns: ()
│ estimated row count: 0 (missing stats)
│ from: t
│ auto commit
│
└── • project
    │ columns: (k int)
    │
    └── • filter
        │ columns: (k int, v int)
        │ estimated row count: 333 (missing stats)
        │ filter: ((v)[int] > (1)[int])[bool]
        │
        └── • scan
              columns: (k int, v int)
              estimated row count: 1,000 (missing stats)
              table: t@t_pkey
              spans: FULL SCAN
·
index recommendations: 1
1. type: index creation
   SQL command: CREATE INDEX ON test.public.t (v);

query T
EXPLAIN (TYPES) UPDATE t SET v = k + 1 WHERE v > 123
----
distribution: local
vectorized: true
·
• update
│ columns: ()
│ estimated row count: 0 (missing stats)
│ table: t
│ set: v
│ auto commit
│
└── • render
    │ columns: (k int, v int, v_new int)
    │ render v_new: ((k)[int] + (1)[int])[int]
    │ render k: (k)[int]
    │ render v: (v)[int]
    │
    └── • filter
        │ columns: (k int, v int)
        │ estimated row count: 333 (missing stats)
        │ filter: ((v)[int] > (123)[int])[bool]
        │
        └── • scan
              columns: (k int, v int)
              estimated row count: 1,000 (missing stats)
              table: t@t_pkey
              spans: FULL SCAN
·
index recommendations: 1
1. type: index creation
   SQL command: CREATE INDEX ON test.public.t (v);

query T
EXPLAIN (TYPES) VALUES (1) UNION VALUES (2)
----
distribution: local
vectorized: true
·
• union
│ columns: (column1 int)
│ estimated row count: 2
│
├── • values
│     columns: (column1 int)
│     size: 1 column, 1 row
│     row 0, expr 0: (1)[int]
│
└── • values
      columns: (column1 int)
      size: 1 column, 1 row
      row 0, expr 0: (2)[int]

query T
EXPLAIN (TYPES) SELECT DISTINCT k FROM t
----
distribution: local
vectorized: true
·
• scan
  columns: (k int)
  estimated row count: 1,000 (missing stats)
  table: t@t_pkey
  spans: FULL SCAN

query T
EXPLAIN (TYPES) SELECT v FROM t ORDER BY v
----
distribution: local
vectorized: true
·
• sort
│ columns: (v int)
│ estimated row count: 1,000 (missing stats)
│ order: +v
│
└── • scan
      columns: (v int)
      estimated row count: 1,000 (missing stats)
      table: t@t_pkey
      spans: FULL SCAN
·
index recommendations: 1
1. type: index creation
   SQL command: CREATE INDEX ON test.public.t (v);

query T
EXPLAIN (TYPES) SELECT v FROM t LIMIT 1
----
distribution: local
vectorized: true
·
• scan
  columns: (v int)
  estimated row count: 1 (missing stats)
  table: t@t_pkey
  spans: LIMITED SCAN
  limit: 1

statement ok
CREATE TABLE tt (x INT, y INT, INDEX a(x), INDEX b(y))

query T
EXPLAIN (TYPES) SELECT * FROM tt WHERE x < 10 AND y > 10
----
distribution: local
vectorized: true
·
• filter
│ columns: (x int, y int)
│ estimated row count: 311 (missing stats)
│ filter: ((((x)[int] < (10)[int])[bool]) AND (((y)[int] > (10)[int])[bool]))[bool]
│
└── • scan
      columns: (x int, y int)
      estimated row count: 1,000 (missing stats)
      table: tt@tt_pkey
      spans: FULL SCAN
·
index recommendations: 1
1. type: index replacement
   SQL commands: CREATE INDEX ON test.public.tt (x) STORING (y); DROP INDEX test.public.tt@a;

statement ok
CREATE DATABASE another_db;
USE another_db;
CREATE SCHEMA sc;

statement ok
CREATE TABLE another_db.sc.t (
  k INT PRIMARY KEY,
  a INT
)

query T
EXPLAIN SELECT * FROM another_db.sc.t WHERE a = 1
----
distribution: local
vectorized: true
·
• filter
│ filter: a = 1
│
└── • scan
      missing stats
      table: t@t_pkey
      spans: FULL SCAN
·
index recommendations: 1
1. type: index creation
   SQL command: CREATE INDEX ON another_db.sc.t (a);

statement ok
USE test;

# TODO(radu): we don't support placeholders with no values.
#query TTTTT
#EXPLAIN (TYPES) SELECT $1 + 2 AS a
#----
#render         ·         ·                            (a int)  a=CONST
# │             render 0  (($1)[int] + (2)[int])[int]  ·        ·
# └── emptyrow  ·         ·                            ()       ·

query T
EXPLAIN (TYPES) SELECT abs(2-3) AS a
----
distribution: local
vectorized: true
·
• values
  columns: (a int)
  size: 1 column, 1 row
  row 0, expr 0: (1)[int]

# Check array subscripts (#13811)
query T
EXPLAIN (TYPES) SELECT x[1] FROM (SELECT ARRAY[1,2,3] AS x)
----
distribution: local
vectorized: true
·
• values
  columns: (x int)
  size: 1 column, 1 row
  row 0, expr 0: (1)[int]

query T
EXPLAIN (OPT) SELECT 1 AS r
----
values
 └── (1,)

query T
EXPLAIN (OPT,REDACT) SELECT 1 AS r
----
values
 └── ‹(‹×›,)›

query T
EXPLAIN (OPT,VERBOSE) SELECT 1 AS r
----
values
 ├── columns: r:1
 ├── cardinality: [1 - 1]
 ├── stats: [rows=1]
 ├── cost: 0.02
 ├── key: ()
 ├── fd: ()-->(1)
 ├── distribution: test
 ├── prune: (1)
 └── (1,)

query T
EXPLAIN (OPT,VERBOSE,REDACT) SELECT 1 AS r
----
values
 ├── columns: r:1
 ├── cardinality: [1 - 1]
 ├── stats: [rows=1]
 ├── cost: 0.02
 ├── key: ()
 ├── fd: ()-->(1)
 ├── distribution: test
 ├── prune: (1)
 └── ‹(‹×›,)›

query T
EXPLAIN (OPT,TYPES) SELECT 1 AS r
----
values
 ├── columns: r:1(int!null)
 ├── cardinality: [1 - 1]
 ├── stats: [rows=1]
 ├── cost: 0.02
 ├── key: ()
 ├── fd: ()-->(1)
 ├── distribution: test
 ├── prune: (1)
 └── tuple [type=tuple{int}]
      └── const: 1 [type=int]

query T
EXPLAIN (OPT,TYPES,REDACT) SELECT 1 AS r
----
values
 ├── columns: r:1(int!null)
 ├── cardinality: [1 - 1]
 ├── stats: [rows=1]
 ├── cost: 0.02
 ├── key: ()
 ├── fd: ()-->(1)
 ├── distribution: test
 ├── prune: (1)
 └── tuple [type=tuple{int}]
      └── const: ‹×› [type=int]

query T
EXPLAIN (OPT) SELECT * FROM tc WHERE a = 10 ORDER BY b
----
distribute
 └── sort
      └── index-join tc
           └── scan tc@c
                └── constraint: /1/3: [/10 - /10]

query T
EXPLAIN (OPT,REDACT) SELECT * FROM tc WHERE a = 10 ORDER BY b
----
distribute
 └── sort
      └── index-join tc
           └── scan tc@c
                └── constraint: /1/3: ‹×›

query T
EXPLAIN (OPT,VERBOSE) SELECT * FROM tc WHERE a = 10 ORDER BY b
----
distribute
 ├── columns: a:1 b:2
 ├── stats: [rows=10, distinct(1)=1, null(1)=0]
 ├── cost: 290.844386
 ├── fd: ()-->(1)
 ├── ordering: +2 opt(1) [actual: +2]
 ├── distribution: test
 ├── input distribution:
 ├── prune: (2)
 └── sort
      ├── columns: a:1 b:2
      ├── stats: [rows=10, distinct(1)=1, null(1)=0]
      ├── cost: 90.8243864
      ├── fd: ()-->(1)
      ├── ordering: +2 opt(1) [actual: +2]
      ├── prune: (2)
      └── index-join tc
           ├── columns: a:1 b:2
           ├── stats: [rows=10, distinct(1)=1, null(1)=0]
           ├── cost: 89.7400007
           ├── fd: ()-->(1)
           ├── prune: (2)
           └── scan tc@c
                ├── columns: a:1 rowid:3
                ├── constraint: /1/3: [/10 - /10]
                ├── stats: [rows=10, distinct(1)=1, null(1)=0]
                ├── cost: 28.8200001
                ├── key: (3)
                └── fd: ()-->(1)

query T
EXPLAIN (OPT,VERBOSE,REDACT) SELECT * FROM tc WHERE a = 10 ORDER BY b
----
distribute
 ├── columns: a:1 b:2
 ├── stats: [rows=10, distinct(1)=1, null(1)=0]
 ├── cost: 290.844386
 ├── fd: ()-->(1)
 ├── ordering: +2 opt(1) [actual: +2]
 ├── distribution: test
 ├── input distribution:
 ├── prune: (2)
 └── sort
      ├── columns: a:1 b:2
      ├── stats: [rows=10, distinct(1)=1, null(1)=0]
      ├── cost: 90.8243864
      ├── fd: ()-->(1)
      ├── ordering: +2 opt(1) [actual: +2]
      ├── prune: (2)
      └── index-join tc
           ├── columns: a:1 b:2
           ├── stats: [rows=10, distinct(1)=1, null(1)=0]
           ├── cost: 89.7400007
           ├── fd: ()-->(1)
           ├── prune: (2)
           └── scan tc@c
                ├── columns: a:1 rowid:3
                ├── constraint: /1/3: ‹×›
                ├── stats: [rows=10, distinct(1)=1, null(1)=0]
                ├── cost: 28.8200001
                ├── key: (3)
                └── fd: ()-->(1)

query T
EXPLAIN (OPT,TYPES) SELECT * FROM tc WHERE a = 10 ORDER BY b
----
distribute
 ├── columns: a:1(int!null) b:2(int)
 ├── stats: [rows=10, distinct(1)=1, null(1)=0]
 ├── cost: 290.844386
 ├── fd: ()-->(1)
 ├── ordering: +2 opt(1) [actual: +2]
 ├── distribution: test
 ├── input distribution:
 ├── prune: (2)
 └── sort
      ├── columns: a:1(int!null) b:2(int)
      ├── stats: [rows=10, distinct(1)=1, null(1)=0]
      ├── cost: 90.8243864
      ├── fd: ()-->(1)
      ├── ordering: +2 opt(1) [actual: +2]
      ├── prune: (2)
      └── index-join tc
           ├── columns: a:1(int!null) b:2(int)
           ├── stats: [rows=10, distinct(1)=1, null(1)=0]
           ├── cost: 89.7400007
           ├── fd: ()-->(1)
           ├── prune: (2)
           └── scan tc@c
                ├── columns: a:1(int!null) rowid:3(int!null)
                ├── constraint: /1/3: [/10 - /10]
                ├── stats: [rows=10, distinct(1)=1, null(1)=0]
                ├── cost: 28.8200001
                ├── key: (3)
                └── fd: ()-->(1)

query T
EXPLAIN (OPT,TYPES,REDACT) SELECT * FROM tc WHERE a = 10 ORDER BY b
----
distribute
 ├── columns: a:1(int!null) b:2(int)
 ├── stats: [rows=10, distinct(1)=1, null(1)=0]
 ├── cost: 290.844386
 ├── fd: ()-->(1)
 ├── ordering: +2 opt(1) [actual: +2]
 ├── distribution: test
 ├── input distribution:
 ├── prune: (2)
 └── sort
      ├── columns: a:1(int!null) b:2(int)
      ├── stats: [rows=10, distinct(1)=1, null(1)=0]
      ├── cost: 90.8243864
      ├── fd: ()-->(1)
      ├── ordering: +2 opt(1) [actual: +2]
      ├── prune: (2)
      └── index-join tc
           ├── columns: a:1(int!null) b:2(int)
           ├── stats: [rows=10, distinct(1)=1, null(1)=0]
           ├── cost: 89.7400007
           ├── fd: ()-->(1)
           ├── prune: (2)
           └── scan tc@c
                ├── columns: a:1(int!null) rowid:3(int!null)
                ├── constraint: /1/3: ‹×›
                ├── stats: [rows=10, distinct(1)=1, null(1)=0]
                ├── cost: 28.8200001
                ├── key: (3)
                └── fd: ()-->(1)

query T
EXPLAIN (OPT,CATALOG) SELECT * FROM tc WHERE a = 10 ORDER BY b
----
TABLE tc
 ├── a int
 ├── b int
 ├── rowid int not null default (unique_rowid()) [hidden]
 ├── crdb_internal_mvcc_timestamp decimal [hidden] [system]
 ├── tableoid oid [hidden] [system]
 ├── crdb_internal_origin_id int4 [hidden] [system]
 ├── crdb_internal_origin_timestamp decimal [hidden] [system]
 ├── PRIMARY INDEX tc_pkey
 │    └── rowid int not null default (unique_rowid()) [hidden]
 └── INDEX c
      ├── a int
      └── rowid int not null default (unique_rowid()) [hidden]
distribute
 └── sort
      └── index-join tc
           └── scan tc@c
                └── constraint: /1/3: [/10 - /10]

query T
EXPLAIN (OPT,CATALOG,REDACT) SELECT * FROM tc WHERE a = 10 ORDER BY b
----
TABLE tc
 ├── a int
 ├── b int
 ├── rowid int not null default (‹×›) [hidden]
 ├── crdb_internal_mvcc_timestamp decimal [hidden] [system]
 ├── tableoid oid [hidden] [system]
 ├── crdb_internal_origin_id int4 [hidden] [system]
 ├── crdb_internal_origin_timestamp decimal [hidden] [system]
 ├── PRIMARY INDEX tc_pkey
 │    └── rowid int not null default (‹×›) [hidden]
 └── INDEX c
      ├── a int
      └── rowid int not null default (‹×›) [hidden]
distribute
 └── sort
      └── index-join tc
           └── scan tc@c
                └── constraint: /1/3: ‹×›

query T
EXPLAIN (OPT,VERBOSE,CATALOG) SELECT * FROM tc JOIN t ON k=a
----
TABLE tc
 ├── a int
 ├── b int
 ├── rowid int not null default (unique_rowid()) [hidden]
 ├── crdb_internal_mvcc_timestamp decimal [hidden] [system]
 ├── tableoid oid [hidden] [system]
 ├── crdb_internal_origin_id int4 [hidden] [system]
 ├── crdb_internal_origin_timestamp decimal [hidden] [system]
 ├── PRIMARY INDEX tc_pkey
 │    └── rowid int not null default (unique_rowid()) [hidden]
 └── INDEX c
      ├── a int
      └── rowid int not null default (unique_rowid()) [hidden]
TABLE t
 ├── k int not null
 ├── v int
 ├── crdb_internal_mvcc_timestamp decimal [hidden] [system]
 ├── tableoid oid [hidden] [system]
 ├── crdb_internal_origin_id int4 [hidden] [system]
 ├── crdb_internal_origin_timestamp decimal [hidden] [system]
 └── PRIMARY INDEX t_pkey
      └── k int not null
inner-join (hash)
 ├── columns: a:1 b:2 k:8 v:9
 ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 ├── stats: [rows=990, distinct(1)=99, null(1)=0, distinct(8)=99, null(8)=0]
 ├── cost: 2277.90625
 ├── fd: (8)-->(9), (1)==(8), (8)==(1)
 ├── distribution: test
 ├── prune: (2,9)
 ├── scan tc
 │    ├── columns: a:1 b:2
 │    ├── stats: [rows=1000, distinct(1)=100, null(1)=10]
 │    ├── cost: 1129.02
 │    ├── distribution: test
 │    ├── prune: (1,2)
 │    ├── interesting orderings: (+1)
 │    └── unfiltered-cols: (1-7)
 ├── scan t
 │    ├── columns: k:8 v:9
 │    ├── stats: [rows=1000, distinct(8)=1000, null(8)=0]
 │    ├── cost: 1108.82
 │    ├── key: (8)
 │    ├── fd: (8)-->(9)
 │    ├── distribution: test
 │    ├── prune: (8,9)
 │    ├── interesting orderings: (+8)
 │    └── unfiltered-cols: (8-13)
 └── filters
      └── k:8 = a:1 [outer=(1,8), constraints=(/1: (/NULL - ]; /8: (/NULL - ]), fd=(1)==(8), (8)==(1)]

query T
EXPLAIN (OPT,VERBOSE,CATALOG,REDACT) SELECT * FROM tc JOIN t ON k=a
----
TABLE tc
 ├── a int
 ├── b int
 ├── rowid int not null default (‹×›) [hidden]
 ├── crdb_internal_mvcc_timestamp decimal [hidden] [system]
 ├── tableoid oid [hidden] [system]
 ├── crdb_internal_origin_id int4 [hidden] [system]
 ├── crdb_internal_origin_timestamp decimal [hidden] [system]
 ├── PRIMARY INDEX tc_pkey
 │    └── rowid int not null default (‹×›) [hidden]
 └── INDEX c
      ├── a int
      └── rowid int not null default (‹×›) [hidden]
TABLE t
 ├── k int not null
 ├── v int
 ├── crdb_internal_mvcc_timestamp decimal [hidden] [system]
 ├── tableoid oid [hidden] [system]
 ├── crdb_internal_origin_id int4 [hidden] [system]
 ├── crdb_internal_origin_timestamp decimal [hidden] [system]
 └── PRIMARY INDEX t_pkey
      └── k int not null
inner-join (hash)
 ├── columns: a:1 b:2 k:8 v:9
 ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 ├── stats: [rows=990, distinct(1)=99, null(1)=0, distinct(8)=99, null(8)=0]
 ├── cost: 2277.90625
 ├── fd: (8)-->(9), (1)==(8), (8)==(1)
 ├── distribution: test
 ├── prune: (2,9)
 ├── scan tc
 │    ├── columns: a:1 b:2
 │    ├── stats: [rows=1000, distinct(1)=100, null(1)=10]
 │    ├── cost: 1129.02
 │    ├── distribution: test
 │    ├── prune: (1,2)
 │    ├── interesting orderings: (+1)
 │    └── unfiltered-cols: (1-7)
 ├── scan t
 │    ├── columns: k:8 v:9
 │    ├── stats: [rows=1000, distinct(8)=1000, null(8)=0]
 │    ├── cost: 1108.82
 │    ├── key: (8)
 │    ├── fd: (8)-->(9)
 │    ├── distribution: test
 │    ├── prune: (8,9)
 │    ├── interesting orderings: (+8)
 │    └── unfiltered-cols: (8-13)
 └── filters
      └── k:8 = a:1 [outer=(1,8), constraints=(‹×›), fd=(1)==(8), (8)==(1)]

query T
EXPLAIN (OPT) SELECT * FROM tc WHERE a + 2 * b > 1 ORDER BY a*b
----
distribute
 └── sort
      └── project
           ├── select
           │    ├── scan tc
           │    └── filters
           │         └── (a + (b * 2)) > 1
           └── projections
                └── a * b

query T
EXPLAIN (OPT, REDACT) SELECT * FROM tc WHERE a + 2 * b > 1 ORDER BY a*b
----
distribute
 └── sort
      └── project
           ├── select
           │    ├── scan tc
           │    └── filters
           │         └── (a + (b * ‹×›)) > ‹×›
           └── projections
                └── a * b

query T
EXPLAIN (OPT, VERBOSE) SELECT * FROM tc WHERE a + 2 * b > 1 ORDER BY a*b
----
distribute
 ├── columns: a:1 b:2  [hidden: column8:8]
 ├── immutable
 ├── stats: [rows=333.3333]
 ├── cost: 1418.32951
 ├── fd: (1,2)-->(8)
 ├── ordering: +8
 ├── distribution: test
 ├── input distribution:
 ├── prune: (1,2,8)
 ├── interesting orderings: (+1)
 └── sort
      ├── columns: a:1 b:2 column8:8
      ├── immutable
      ├── stats: [rows=333.3333]
      ├── cost: 1218.30951
      ├── fd: (1,2)-->(8)
      ├── ordering: +8
      ├── prune: (1,2,8)
      ├── interesting orderings: (+1)
      └── project
           ├── columns: column8:8 a:1 b:2
           ├── immutable
           ├── stats: [rows=333.3333]
           ├── cost: 1145.73667
           ├── fd: (1,2)-->(8)
           ├── prune: (1,2,8)
           ├── interesting orderings: (+1)
           ├── select
           │    ├── columns: a:1 b:2
           │    ├── immutable
           │    ├── stats: [rows=333.3333]
           │    ├── cost: 1139.05
           │    ├── interesting orderings: (+1)
           │    ├── scan tc
           │    │    ├── columns: a:1 b:2
           │    │    ├── stats: [rows=1000]
           │    │    ├── cost: 1129.02
           │    │    ├── prune: (1,2)
           │    │    └── interesting orderings: (+1)
           │    └── filters
           │         └── (a:1 + (b:2 * 2)) > 1 [outer=(1,2), immutable]
           └── projections
                └── a:1 * b:2 [as=column8:8, outer=(1,2), immutable]

query T
EXPLAIN (OPT, VERBOSE, REDACT) SELECT * FROM tc WHERE a + 2 * b > 1 ORDER BY a*b
----
distribute
 ├── columns: a:1 b:2  [hidden: column8:8]
 ├── immutable
 ├── stats: [rows=333.3333]
 ├── cost: 1418.32951
 ├── fd: (1,2)-->(8)
 ├── ordering: +8
 ├── distribution: test
 ├── input distribution:
 ├── prune: (1,2,8)
 ├── interesting orderings: (+1)
 └── sort
      ├── columns: a:1 b:2 column8:8
      ├── immutable
      ├── stats: [rows=333.3333]
      ├── cost: 1218.30951
      ├── fd: (1,2)-->(8)
      ├── ordering: +8
      ├── prune: (1,2,8)
      ├── interesting orderings: (+1)
      └── project
           ├── columns: column8:8 a:1 b:2
           ├── immutable
           ├── stats: [rows=333.3333]
           ├── cost: 1145.73667
           ├── fd: (1,2)-->(8)
           ├── prune: (1,2,8)
           ├── interesting orderings: (+1)
           ├── select
           │    ├── columns: a:1 b:2
           │    ├── immutable
           │    ├── stats: [rows=333.3333]
           │    ├── cost: 1139.05
           │    ├── interesting orderings: (+1)
           │    ├── scan tc
           │    │    ├── columns: a:1 b:2
           │    │    ├── stats: [rows=1000]
           │    │    ├── cost: 1129.02
           │    │    ├── prune: (1,2)
           │    │    └── interesting orderings: (+1)
           │    └── filters
           │         └── (a:1 + (b:2 * ‹×›)) > ‹×› [outer=(1,2), immutable]
           └── projections
                └── a:1 * b:2 [as=column8:8, outer=(1,2), immutable]

query T
EXPLAIN (OPT, TYPES) SELECT * FROM tc WHERE a + 2 * b > 1 ORDER BY a*b
----
distribute
 ├── columns: a:1(int) b:2(int)  [hidden: column8:8(int)]
 ├── immutable
 ├── stats: [rows=333.3333]
 ├── cost: 1418.32951
 ├── fd: (1,2)-->(8)
 ├── ordering: +8
 ├── distribution: test
 ├── input distribution:
 ├── prune: (1,2,8)
 ├── interesting orderings: (+1)
 └── sort
      ├── columns: a:1(int) b:2(int) column8:8(int)
      ├── immutable
      ├── stats: [rows=333.3333]
      ├── cost: 1218.30951
      ├── fd: (1,2)-->(8)
      ├── ordering: +8
      ├── prune: (1,2,8)
      ├── interesting orderings: (+1)
      └── project
           ├── columns: column8:8(int) a:1(int) b:2(int)
           ├── immutable
           ├── stats: [rows=333.3333]
           ├── cost: 1145.73667
           ├── fd: (1,2)-->(8)
           ├── prune: (1,2,8)
           ├── interesting orderings: (+1)
           ├── select
           │    ├── columns: a:1(int) b:2(int)
           │    ├── immutable
           │    ├── stats: [rows=333.3333]
           │    ├── cost: 1139.05
           │    ├── interesting orderings: (+1)
           │    ├── scan tc
           │    │    ├── columns: a:1(int) b:2(int)
           │    │    ├── stats: [rows=1000]
           │    │    ├── cost: 1129.02
           │    │    ├── prune: (1,2)
           │    │    └── interesting orderings: (+1)
           │    └── filters
           │         └── gt [type=bool, outer=(1,2), immutable]
           │              ├── plus [type=int]
           │              │    ├── variable: a:1 [type=int]
           │              │    └── mult [type=int]
           │              │         ├── variable: b:2 [type=int]
           │              │         └── const: 2 [type=int]
           │              └── const: 1 [type=int]
           └── projections
                └── mult [as=column8:8, type=int, outer=(1,2), immutable]
                     ├── variable: a:1 [type=int]
                     └── variable: b:2 [type=int]

query T
EXPLAIN (OPT, TYPES, REDACT) SELECT * FROM tc WHERE a + 2 * b > 1 ORDER BY a*b
----
distribute
 ├── columns: a:1(int) b:2(int)  [hidden: column8:8(int)]
 ├── immutable
 ├── stats: [rows=333.3333]
 ├── cost: 1418.32951
 ├── fd: (1,2)-->(8)
 ├── ordering: +8
 ├── distribution: test
 ├── input distribution:
 ├── prune: (1,2,8)
 ├── interesting orderings: (+1)
 └── sort
      ├── columns: a:1(int) b:2(int) column8:8(int)
      ├── immutable
      ├── stats: [rows=333.3333]
      ├── cost: 1218.30951
      ├── fd: (1,2)-->(8)
      ├── ordering: +8
      ├── prune: (1,2,8)
      ├── interesting orderings: (+1)
      └── project
           ├── columns: column8:8(int) a:1(int) b:2(int)
           ├── immutable
           ├── stats: [rows=333.3333]
           ├── cost: 1145.73667
           ├── fd: (1,2)-->(8)
           ├── prune: (1,2,8)
           ├── interesting orderings: (+1)
           ├── select
           │    ├── columns: a:1(int) b:2(int)
           │    ├── immutable
           │    ├── stats: [rows=333.3333]
           │    ├── cost: 1139.05
           │    ├── interesting orderings: (+1)
           │    ├── scan tc
           │    │    ├── columns: a:1(int) b:2(int)
           │    │    ├── stats: [rows=1000]
           │    │    ├── cost: 1129.02
           │    │    ├── prune: (1,2)
           │    │    └── interesting orderings: (+1)
           │    └── filters
           │         └── gt [type=bool, outer=(1,2), immutable]
           │              ├── plus [type=int]
           │              │    ├── variable: a:1 [type=int]
           │              │    └── mult [type=int]
           │              │         ├── variable: b:2 [type=int]
           │              │         └── const: ‹×› [type=int]
           │              └── const: ‹×› [type=int]
           └── projections
                └── mult [as=column8:8, type=int, outer=(1,2), immutable]
                     ├── variable: a:1 [type=int]
                     └── variable: b:2 [type=int]

query T
EXPLAIN SELECT string_agg(x, y) FROM (VALUES ('foo', 'foo'), ('bar', 'bar')) t(x, y)
----
distribution: local
vectorized: true
·
• group (scalar)
│ estimated row count: 1
│
└── • values
      size: 2 columns, 2 rows

query T
EXPLAIN SELECT corr(a, b) FROM tc;
----
distribution: local
vectorized: true
·
• group (scalar)
│
└── • scan
      missing stats
      table: tc@tc_pkey
      spans: FULL SCAN

# Verify that subqueries are handled correctly, namely that we can have
# subqueries outside of the EXPLAINed query.
query IT
WITH
  a AS MATERIALIZED (SELECT max(k) FROM t WHERE k > (SELECT v FROM t WHERE v = k + 1)),
  b AS MATERIALIZED (EXPLAIN SELECT k, v FROM t)
SELECT * FROM a, b
----
NULL  distribution: local
NULL  vectorized: true
NULL  ·
NULL  • scan
NULL    missing stats
NULL    table: t@t_pkey
NULL    spans: FULL SCAN

# Tests with EXPLAIN inside EXPLAIN.
query T
EXPLAIN EXPLAIN SELECT 1
----
distribution: local
vectorized: true
·
• explain

statement error EXPLAIN ANALYZE can only be used as a top-level statement
EXPLAIN (VERBOSE) EXPLAIN ANALYZE (DISTSQL) SELECT 1

# Test a case with many spans.
query T
EXPLAIN SELECT * FROM t WHERE k IN (10, 20, 30, 40, 50, 60, 70, 80)
----
distribution: local
vectorized: true
·
• scan
  missing stats
  table: t@t_pkey
  spans: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] … (4 more)


# UPSERT and INSERT ON CONFLICT include arbiters.

statement ok
CREATE TABLE u (
  k INT PRIMARY KEY,
  v INT,
  UNIQUE INDEX (v),
  FAMILY "primary" (k, v)
)

query T
EXPLAIN UPSERT INTO u VALUES (1, 1)
----
distribution: local
vectorized: true
·
• upsert
│ into: u(k, v)
│ auto commit
│ arbiter indexes: u_pkey
│
└── • cross join (left outer)
    │
    ├── • values
    │     size: 2 columns, 1 row
    │
    └── • scan
          missing stats
          table: u@u_pkey
          spans: [/1 - /1]
          locking strength: for update

query T
EXPLAIN INSERT INTO u VALUES (1, 1) ON CONFLICT DO NOTHING
----
distribution: local
vectorized: true
·
• insert
│ into: u(k, v)
│ auto commit
│ arbiter indexes: u_pkey, u_v_key
│
└── • lookup join (anti)
    │ table: u@u_v_key
    │ equality: (column2) = (v)
    │ equality cols are key
    │
    └── • cross join (anti)
        │
        ├── • values
        │     size: 2 columns, 1 row
        │
        └── • scan
              missing stats
              table: u@u_pkey
              spans: [/1 - /1]

# Make sure EXPLAIN (VERBOSE) works when there is a constrained scan of a
# virtual table (#58193).
statement ok
CREATE TABLE ab (a INT, b INT)

query T
EXPLAIN (VERBOSE) SELECT attnum FROM pg_attribute WHERE attrelid = 'ab'::regclass AND attname = 'b';
----
distribution: local
vectorized: true
·
• project
│ columns: (attnum)
│
└── • filter
    │ columns: (attrelid, attname, attnum)
    │ estimated row count: 1 (missing stats)
    │ filter: attname = 'b'
    │
    └── • virtual table
          columns: (attrelid, attname, attnum)
          estimated row count: 10 (missing stats)
          table: pg_attribute@pg_attribute_attrelid_idx
          spans: [/ab - /ab]


# Verify the output for the percentage of table that is scanned.
statement ok
CREATE TABLE percent (a INT PRIMARY KEY, b INT)

statement ok
ALTER TABLE percent INJECT STATISTICS '[
  {
    "columns": ["a"],
    "created_at": "2018-01-01 1:00:00.00000+00:00",
    "row_count": 1000000,
    "distinct_count": 1000000,
    "histo_buckets": []
  }
]'

query T
EXPLAIN SELECT * FROM percent
----
distribution: local
vectorized: true
·
• scan
  estimated row count: 1,000,000 (100% of the table; stats collected <hidden> ago)
  table: percent@percent_pkey
  spans: FULL SCAN

query T
EXPLAIN SELECT * FROM percent WHERE a BETWEEN 1 AND 150000
----
distribution: local
vectorized: true
·
• scan
  estimated row count: 150,000 (15% of the table; stats collected <hidden> ago)
  table: percent@percent_pkey
  spans: [/1 - /150000]

query T
EXPLAIN SELECT * FROM percent WHERE a BETWEEN 1 AND 20000
----
distribution: local
vectorized: true
·
• scan
  estimated row count: 20,000 (2.0% of the table; stats collected <hidden> ago)
  table: percent@percent_pkey
  spans: [/1 - /20000]

query T
EXPLAIN SELECT * FROM percent WHERE a BETWEEN 1 AND 1234
----
distribution: local
vectorized: true
·
• scan
  estimated row count: 1,234 (0.12% of the table; stats collected <hidden> ago)
  table: percent@percent_pkey
  spans: [/1 - /1234]

query T
EXPLAIN SELECT * FROM percent WHERE a BETWEEN 1 AND 980
----
distribution: local
vectorized: true
·
• scan
  estimated row count: 980 (0.10% of the table; stats collected <hidden> ago)
  table: percent@percent_pkey
  spans: [/1 - /980]

query T
EXPLAIN SELECT * FROM percent WHERE a BETWEEN 1 and 100
----
distribution: local
vectorized: true
·
• scan
  estimated row count: 100 (0.01% of the table; stats collected <hidden> ago)
  table: percent@percent_pkey
  spans: [/1 - /100]

query T
EXPLAIN SELECT * FROM percent WHERE a BETWEEN 1 and 99
----
distribution: local
vectorized: true
·
• scan
  estimated row count: 99 (<0.01% of the table; stats collected <hidden> ago)
  table: percent@percent_pkey
  spans: [/1 - /99]

query T
EXPLAIN SELECT * FROM percent WHERE a = 1
----
distribution: local
vectorized: true
·
• scan
  estimated row count: 1 (<0.01% of the table; stats collected <hidden> ago)
  table: percent@percent_pkey
  spans: [/1 - /1]

# Regression test for #61248: insert fast path with no rows.
query T
EXPLAIN INSERT INTO t2 SELECT x FROM t2 WHERE false
----
distribution: local
vectorized: true
·
• insert fast path
  into: t2(x)
  auto commit

statement ok
PREPARE prep AS SELECT $1 + 1

statement error EXPLAIN EXECUTE is not supported
EXPLAIN EXECUTE prep(1)

statement error EXPLAIN EXECUTE is not supported
SELECT 1 FROM [ EXPLAIN EXECUTE prep(1) ]

query T
EXPLAIN ANALYZE EXECUTE prep(1)
----
planning time: 10µs
execution time: 100µs
distribution: <hidden>
vectorized: <hidden>
plan type: custom
maximum memory usage: <hidden>
network usage: <hidden>
regions: <hidden>
isolation level: serializable
priority: normal
quality of service: regular
·
• values
  sql nodes: <hidden>
  regions: <hidden>
  actual row count: 1
  size: 1 column, 1 row

# Tests for EXPLAIN (OPT, MEMO).
query T
EXPLAIN (OPT, MEMO) SELECT * FROM tc JOIN t ON k=a
----
memo (optimized, ~19KB, required=[presentation: info:14] [distribution: test])
 ├── G1: (explain G2 [presentation: a:1,b:2,k:8,v:9] [distribution: test])
 │    └── [presentation: info:14] [distribution: test]
 │         ├── best: (explain G2="[presentation: a:1,b:2,k:8,v:9] [distribution: test]" [presentation: a:1,b:2,k:8,v:9] [distribution: test])
 │         └── cost: 2277.93
 ├── G2: (inner-join G3 G4 G5) (inner-join G4 G3 G5) (merge-join G3 G4 G6 inner-join,+1,+8) (lookup-join G3 G6 t,keyCols=[1],outCols=(1,2,8,9)) (merge-join G4 G3 G6 inner-join,+8,+1) (lookup-join G7 G6 tc,keyCols=[3],outCols=(1,2,8,9))
 │    ├── [presentation: a:1,b:2,k:8,v:9] [distribution: test]
 │    │    ├── best: (inner-join G3="[distribution: test]" G4="[distribution: test]" G5)
 │    │    └── cost: 2277.91
 │    └── []
 │         ├── best: (inner-join G3 G4 G5)
 │         └── cost: 2277.91
 ├── G3: (scan tc,cols=(1,2))
 │    ├── [distribution: test]
 │    │    ├── best: (scan tc,cols=(1,2))
 │    │    └── cost: 1129.02
 │    ├── [ordering: +1]
 │    │    ├── best: (sort G3)
 │    │    └── cost: 1368.50
 │    ├── [ordering: +1] [distribution: test]
 │    │    ├── best: (distribute G3="[ordering: +1]")
 │    │    └── cost: 1568.52
 │    └── []
 │         ├── best: (scan tc,cols=(1,2))
 │         └── cost: 1129.02
 ├── G4: (scan t,cols=(8,9))
 │    ├── [distribution: test]
 │    │    ├── best: (scan t,cols=(8,9))
 │    │    └── cost: 1108.82
 │    ├── [ordering: +8]
 │    │    ├── best: (scan t,cols=(8,9))
 │    │    └── cost: 1108.82
 │    ├── [ordering: +8] [distribution: test]
 │    │    ├── best: (scan t,cols=(8,9))
 │    │    └── cost: 1108.82
 │    └── []
 │         ├── best: (scan t,cols=(8,9))
 │         └── cost: 1108.82
 ├── G5: (filters G8)
 ├── G6: (filters)
 ├── G7: (lookup-join G4 G6 tc@c,keyCols=[8],outCols=(1,3,8,9))
 │    ├── [distribution: test]
 │    │    ├── best: (lookup-join G4="[distribution: test]" G6 tc@c,keyCols=[8],outCols=(1,3,8,9))
 │    │    └── cost: 23181.94
 │    └── []
 │         ├── best: (lookup-join G4 G6 tc@c,keyCols=[8],outCols=(1,3,8,9))
 │         └── cost: 23181.94
 ├── G8: (eq G9 G10)
 ├── G9: (variable k)
 └── G10: (variable a)
inner-join (hash)
 ├── scan tc
 ├── scan t
 └── filters
      └── k = a

query T
EXPLAIN (OPT, MEMO, VERBOSE, CATALOG) SELECT * FROM tc JOIN t ON k=a
----
TABLE tc
 ├── a int
 ├── b int
 ├── rowid int not null default (unique_rowid()) [hidden]
 ├── crdb_internal_mvcc_timestamp decimal [hidden] [system]
 ├── tableoid oid [hidden] [system]
 ├── crdb_internal_origin_id int4 [hidden] [system]
 ├── crdb_internal_origin_timestamp decimal [hidden] [system]
 ├── PRIMARY INDEX tc_pkey
 │    └── rowid int not null default (unique_rowid()) [hidden]
 └── INDEX c
      ├── a int
      └── rowid int not null default (unique_rowid()) [hidden]
TABLE t
 ├── k int not null
 ├── v int
 ├── crdb_internal_mvcc_timestamp decimal [hidden] [system]
 ├── tableoid oid [hidden] [system]
 ├── crdb_internal_origin_id int4 [hidden] [system]
 ├── crdb_internal_origin_timestamp decimal [hidden] [system]
 └── PRIMARY INDEX t_pkey
      └── k int not null
memo (optimized, ~19KB, required=[presentation: info:14] [distribution: test])
 ├── G1: (explain G2 [presentation: a:1,b:2,k:8,v:9] [distribution: test])
 │    └── [presentation: info:14] [distribution: test]
 │         ├── best: (explain G2="[presentation: a:1,b:2,k:8,v:9] [distribution: test]" [presentation: a:1,b:2,k:8,v:9] [distribution: test])
 │         └── cost: 2277.93
 ├── G2: (inner-join G3 G4 G5) (inner-join G4 G3 G5) (merge-join G3 G4 G6 inner-join,+1,+8) (lookup-join G3 G6 t,keyCols=[1],outCols=(1,2,8,9)) (merge-join G4 G3 G6 inner-join,+8,+1) (lookup-join G7 G6 tc,keyCols=[3],outCols=(1,2,8,9))
 │    ├── [presentation: a:1,b:2,k:8,v:9] [distribution: test]
 │    │    ├── best: (inner-join G3="[distribution: test]" G4="[distribution: test]" G5)
 │    │    └── cost: 2277.91
 │    └── []
 │         ├── best: (inner-join G3 G4 G5)
 │         └── cost: 2277.91
 ├── G3: (scan tc,cols=(1,2))
 │    ├── [distribution: test]
 │    │    ├── best: (scan tc,cols=(1,2))
 │    │    └── cost: 1129.02
 │    ├── [ordering: +1]
 │    │    ├── best: (sort G3)
 │    │    └── cost: 1368.50
 │    ├── [ordering: +1] [distribution: test]
 │    │    ├── best: (distribute G3="[ordering: +1]")
 │    │    └── cost: 1568.52
 │    └── []
 │         ├── best: (scan tc,cols=(1,2))
 │         └── cost: 1129.02
 ├── G4: (scan t,cols=(8,9))
 │    ├── [distribution: test]
 │    │    ├── best: (scan t,cols=(8,9))
 │    │    └── cost: 1108.82
 │    ├── [ordering: +8]
 │    │    ├── best: (scan t,cols=(8,9))
 │    │    └── cost: 1108.82
 │    ├── [ordering: +8] [distribution: test]
 │    │    ├── best: (scan t,cols=(8,9))
 │    │    └── cost: 1108.82
 │    └── []
 │         ├── best: (scan t,cols=(8,9))
 │         └── cost: 1108.82
 ├── G5: (filters G8)
 ├── G6: (filters)
 ├── G7: (lookup-join G4 G6 tc@c,keyCols=[8],outCols=(1,3,8,9))
 │    ├── [distribution: test]
 │    │    ├── best: (lookup-join G4="[distribution: test]" G6 tc@c,keyCols=[8],outCols=(1,3,8,9))
 │    │    └── cost: 23181.94
 │    └── []
 │         ├── best: (lookup-join G4 G6 tc@c,keyCols=[8],outCols=(1,3,8,9))
 │         └── cost: 23181.94
 ├── G8: (eq G9 G10)
 ├── G9: (variable k)
 └── G10: (variable a)
inner-join (hash)
 ├── columns: a:1 b:2 k:8 v:9
 ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 ├── stats: [rows=990, distinct(1)=99, null(1)=0, distinct(8)=99, null(8)=0]
 ├── cost: 2277.90625
 ├── fd: (8)-->(9), (1)==(8), (8)==(1)
 ├── distribution: test
 ├── prune: (2,9)
 ├── scan tc
 │    ├── columns: a:1 b:2
 │    ├── stats: [rows=1000, distinct(1)=100, null(1)=10]
 │    ├── cost: 1129.02
 │    ├── distribution: test
 │    ├── prune: (1,2)
 │    ├── interesting orderings: (+1)
 │    └── unfiltered-cols: (1-7)
 ├── scan t
 │    ├── columns: k:8 v:9
 │    ├── stats: [rows=1000, distinct(8)=1000, null(8)=0]
 │    ├── cost: 1108.82
 │    ├── key: (8)
 │    ├── fd: (8)-->(9)
 │    ├── distribution: test
 │    ├── prune: (8,9)
 │    ├── interesting orderings: (+8)
 │    └── unfiltered-cols: (8-13)
 └── filters
      └── k:8 = a:1 [outer=(1,8), constraints=(/1: (/NULL - ]; /8: (/NULL - ]), fd=(1)==(8), (8)==(1)]

# Regression test for overflow when printing out the estimated row count.
statement ok
CREATE TABLE very_large_table (k INT PRIMARY KEY);

statement ok
ALTER TABLE very_large_table INJECT STATISTICS '[
  {
    "columns": ["k"],
    "created_at": "2024-01-01 1:00:00.00000+00:00",
    "row_count": 1000000000000,
    "distinct_count": 1000000000000
  }
]'

query T
EXPLAIN SELECT t1.k FROM very_large_table AS t1, very_large_table AS t2;
----
distribution: local
vectorized: true
·
• cross join
│ estimated row count: 9,223,372,036,854,775,807
│
├── • scan
│     estimated row count: 1,000,000,000,000 (100% of the table; stats collected <hidden> ago)
│     table: very_large_table@very_large_table_pkey
│     spans: FULL SCAN
│
└── • scan
      estimated row count: 1,000,000,000,000 (100% of the table; stats collected <hidden> ago)
      table: very_large_table@very_large_table_pkey
      spans: FULL SCAN
