# LogicTest: local

# Prepare a trace to be inspected below.

statement ok
SET tracing = on; BEGIN; SELECT 1; COMMIT; SELECT 2; SET tracing = off

# Inspect the trace: we exclude messages containing newlines as these
# may contain non-deterministic txn object descriptions.
# This also checks that the span column properly reports separate
# SQL transactions.
# We replace the command position because the values depend on exactly
# how many commands we ran in the session.
query ITT
SELECT
  span, regexp_replace(message, 'pos:[0-9]*', 'pos:?'), operation
FROM [SHOW TRACE FOR SESSION]
WHERE message LIKE '%SPAN START%' OR message LIKE '%pos%executing%';
----
0                         === SPAN START: session recording ===                session recording
0                         [NoTxn pos:?] executing ExecStmt: BEGIN TRANSACTION  session recording
1                         === SPAN START: sql txn ===                          sql txn
1                         [Open pos:?] executing ExecStmt: SELECT 1            sql txn
2                         === SPAN START: consuming rows ===                   consuming rows
3                         === SPAN START: flow ===                             flow
7                         === SPAN START: values ===
cockroach.processorid: 0  values
1                         [Open pos:?] executing ExecStmt: COMMIT TRANSACTION  sql txn
0                         [NoTxn pos:?] executing ExecStmt: SELECT 2           session recording
4                         === SPAN START: sql txn ===                          sql txn
4                         [Open pos:?] executing ExecStmt: SELECT 2            sql txn
5                         === SPAN START: consuming rows ===                   consuming rows
6                         === SPAN START: flow ===                             flow
8                         === SPAN START: values ===
cockroach.processorid: 0  values
0                         [NoTxn pos:?] executing ExecStmt: SET TRACING = off  session recording

# ------------------------------------------------------------------------------
# Test with storing columns.
# ------------------------------------------------------------------------------
statement ok
CREATE TABLE t (
  a INT PRIMARY KEY,
  b INT,
  c INT,
  d INT,
  INDEX b_idx (b) STORING (c, d),
  UNIQUE INDEX c_idx (c) STORING (b, d)
)

query TTBITTBB colnames
SHOW INDEXES FROM t
----
table_name  index_name  non_unique  seq_in_index  column_name  direction  storing  implicit
t           primary     false       1             a            ASC        false    false
t           b_idx       true        1             b            ASC        false    false
t           b_idx       true        2             c            N/A        true     false
t           b_idx       true        3             d            N/A        true     false
t           b_idx       true        4             a            ASC        false    true
t           c_idx       false       1             c            ASC        false    false
t           c_idx       false       2             b            N/A        true     false
t           c_idx       false       3             d            N/A        true     false
t           c_idx       false       4             a            ASC        false    true

statement ok
INSERT INTO t VALUES (1, 2, 3, 4)

statement ok
SET tracing = on,kv,results; SELECT * FROM t@b_idx; SET tracing = off

query T
SELECT message FROM [SHOW KV TRACE FOR SESSION]
 WHERE message LIKE 'fetched:%' OR message LIKE 'output row%'
----
fetched: /t/b_idx/2/1/c/d -> /3/4
output row: [1 2 3 4]

statement ok
SET tracing = on,kv,results; SELECT * FROM t@c_idx; SET tracing = off

query T
SELECT message FROM [SHOW KV TRACE FOR SESSION]
 WHERE message LIKE 'fetched:%' OR message LIKE 'output row%'
----
fetched: /t/c_idx/3/b/d -> /2/4
output row: [1 2 3 4]

# Test index backfill for UNIQUE and non-UNIQUE indexes with STORING columns.

statement ok
CREATE INDEX d_idx ON t (d) STORING (b)

statement ok
SET tracing = on,kv,results; SELECT a, b, d FROM t@d_idx; SET tracing = off

query T
SELECT message FROM [SHOW KV TRACE FOR SESSION]
 WHERE message LIKE 'fetched:%' OR message LIKE 'output row%'
----
fetched: /t/d_idx/4/1/b -> /2
output row: [1 2 4]

statement ok
CREATE UNIQUE INDEX a_idx ON t (a) STORING (b)

statement ok
SET tracing = on,kv,results; SELECT a, b FROM t@a_idx; SET tracing = off

query T
SELECT message FROM [SHOW KV TRACE FOR SESSION]
 WHERE message LIKE 'fetched:%' OR message LIKE 'output row%'
----
fetched: /t/a_idx/1/b -> /2
output row: [1 2]

# Test that unspecified storing values are treated like NULL values.
statement ok
INSERT INTO t (a) VALUES (2)

statement ok
INSERT INTO t VALUES (3)

statement ok
SET tracing = on,kv,results; SELECT * FROM t@b_idx; SET tracing = off

query T
SELECT message FROM [SHOW KV TRACE FOR SESSION]
 WHERE message LIKE 'fetched:%' OR message LIKE 'output row%'
----
fetched: /t/b_idx/NULL/2 -> NULL
output row: [2 NULL NULL NULL]
fetched: /t/b_idx/NULL/3 -> NULL
output row: [3 NULL NULL NULL]
fetched: /t/b_idx/2/1/c/d -> /3/4
output row: [1 2 3 4]

# Regression test for #14601.

statement ok
CREATE TABLE t14601 (a STRING, b BOOL)

statement ok
CREATE INDEX i14601 ON t14601 (a) STORING (b)

query TTT
EXPLAIN SELECT a FROM t14601 ORDER BY a
----
render     ·      ·
 └── scan  ·      ·
·          table  t14601@i14601
·          spans  ALL

# Updates were broken too.

statement ok
CREATE TABLE t14601a (
  a STRING,
  b BOOL,
  c INT,
  FAMILY f1 (a),
  FAMILY f2 (b),
  FAMILY f3 (c)
)

statement ok
CREATE INDEX i14601a ON t14601a (a) STORING (b, c)

query TTT
EXPLAIN SELECT a, b FROM t14601a ORDER BY a
----
render     ·      ·
 └── scan  ·      ·
·          table  t14601a@i14601a
·          spans  ALL

statement ok
DROP index i14601a

statement ok
CREATE UNIQUE INDEX i14601a ON t14601a (a) STORING (b)

query TTT
EXPLAIN SELECT a, b FROM t14601a ORDER BY a
----
render     ·      ·
 └── scan  ·      ·
·          table  t14601a@i14601a
·          spans  ALL

statement ok
DROP TABLE t; DROP TABLE t14601; DROP TABLE t14601a

# ------------------------------------------------------------------------------
# Ensure that correct index is used when indexed column has collation.
# ------------------------------------------------------------------------------
statement ok
CREATE TABLE coll (
  a STRING COLLATE da,
  b INT,
  c BOOL,
  PRIMARY KEY (a, b),
  INDEX (b, a) STORING (c)
)

query TTT
EXPLAIN SELECT a, b FROM coll ORDER BY a, b
----
render     ·      ·
 └── scan  ·      ·
·          table  coll@primary
·          spans  ALL

query TTT
EXPLAIN SELECT b, a FROM coll ORDER BY b, a
----
render     ·      ·
 └── scan  ·      ·
·          table  coll@coll_b_a_idx
·          spans  ALL

# ------------------------------------------------------------------------------
# Ensure correct index is used when indexed column is computed.
# ------------------------------------------------------------------------------
statement ok
CREATE TABLE computed (
  k INT PRIMARY KEY,
  a JSON,
  b TEXT AS (a->>'q') STORED,
  INDEX (b)
)

query TTT
EXPLAIN SELECT b FROM computed ORDER BY b
----
render     ·      ·
 └── scan  ·      ·
·          table  computed@computed_b_idx
·          spans  ALL

# ------------------------------------------------------------------------------
# Ensure that Select filter probes expected date/time key/values that are in
# different column families.
# ------------------------------------------------------------------------------
statement ok
CREATE TABLE dt (
  a TIMESTAMP PRIMARY KEY,
  b DATE,
  c INTERVAL,
  UNIQUE (b),
  UNIQUE (c),
  FAMILY (a),
  FAMILY (b),
  FAMILY (c)
)

statement ok
INSERT INTO dt VALUES
  ('2015-08-30 03:34:45.34567', '2015-08-30', '34h2s'),
  ('2015-08-25 04:45:45.53453', '2015-08-25', '2h45m2s234ms'),
  ('2015-08-29 23:10:09.98763', '2015-08-29', '234h45m2s234ms')

statement ok
SET tracing = on,kv,results; SELECT * FROM dt WHERE a = '2015-08-25 04:45:45.53453+02:00'::timestamp; SET tracing = off

query T
SELECT message FROM [SHOW KV TRACE FOR SESSION]
 WHERE message LIKE 'fetched:%' OR message LIKE 'output row%'
----
fetched: /dt/primary/'2015-08-25 04:45:45.53453+00:00' -> NULL
fetched: /dt/primary/'2015-08-25 04:45:45.53453+00:00'/b -> '2015-08-25'
fetched: /dt/primary/'2015-08-25 04:45:45.53453+00:00'/c -> '02:45:02.234'
output row: ['2015-08-25 04:45:45.53453+00:00' '2015-08-25' '02:45:02.234']

statement ok
SET tracing = on,kv,results; SELECT b FROM dt WHERE b < '2015-08-29'::date; SET tracing = off

query T
SELECT message FROM [SHOW KV TRACE FOR SESSION]
 WHERE message LIKE 'fetched:%' OR message LIKE 'output row%'
----
fetched: /dt/dt_b_key/'2015-08-25' -> /'2015-08-25 04:45:45.53453+00:00'
output row: ['2015-08-25']

statement ok
SET tracing = on,kv,results; SELECT c FROM dt WHERE c < '234h45m2s234ms'::interval; SET tracing = off

query T
SELECT message FROM [SHOW KV TRACE FOR SESSION]
 WHERE message LIKE 'fetched:%' OR message LIKE 'output row%'
----
fetched: /dt/dt_c_key/'02:45:02.234' -> /'2015-08-25 04:45:45.53453+00:00'
output row: ['02:45:02.234']
fetched: /dt/dt_c_key/'34:00:02' -> /'2015-08-30 03:34:45.34567+00:00'
output row: ['34:00:02']

# ------------------------------------------------------------------------------
# Ensure that decimal values result in correct scan spans.
# ------------------------------------------------------------------------------
statement ok
CREATE TABLE dec (d decimal, v decimal(3, 1), primary key (d, v))

query TTT
EXPLAIN SELECT * FROM dec WHERE d IS NaN and v IS NaN
----
scan  ·      ·
·     table  dec@primary
·     spans  /NaN/NaN-/NaN/NaN/#

# The NaN suffix is decimalNaNDesc, not decimalNaN(Asc).
query TTT
EXPLAIN SELECT * FROM dec WHERE d = 'Infinity' and v = 'Infinity'
----
scan  ·      ·
·     table  dec@primary
·     spans  /Infinity/Infinity-/Infinity/Infinity/#

query TTT
EXPLAIN SELECT * FROM dec WHERE d = '-Infinity' and v = '-Infinity'
----
scan  ·      ·
·     table  dec@primary
·     spans  /-Infinity/-Infinity-/-Infinity/-Infinity/#

# Test composite encoding of DECIMAL type in indexes.
statement ok
CREATE TABLE c (
  a INT PRIMARY KEY,
  b DECIMAL(2,2),
  INDEX b_idx (b)
)

statement ok
INSERT INTO c VALUES(1, 0.4)

# Test that unspecifying b is like specifying NULL.
statement ok
INSERT INTO c (a) VALUES(2)

statement ok
INSERT INTO c VALUES(3)

statement ok
SET tracing = on,kv,results; SELECT * FROM c@b_idx; SET tracing = off

query T
SELECT message FROM [SHOW KV TRACE FOR SESSION]
 WHERE message LIKE 'fetched:%' OR message LIKE 'output row%'
----
fetched: /c/b_idx/NULL/2 -> NULL
output row: [2 NULL]
fetched: /c/b_idx/NULL/3 -> NULL
output row: [3 NULL]
fetched: /c/b_idx/0.4/1/b -> /0.40
output row: [1 0.40]

# ------------------------------------------------------------------------------
# Verify that lookups for Decimal NaN use indices when possible:
# - `WHERE d IS NaN` should perform a point lookup.
# - `WHERE d = 'NaN'` should also perform a point lookup.
# - `WHERE isnan(d)` is a function so it can't perform a point lookup.
# ------------------------------------------------------------------------------
statement ok
CREATE TABLE dec2 (d decimal null, index (d))

query TTT
EXPLAIN SELECT * FROM dec2 WHERE d IS NaN
----
render     ·      ·
 └── scan  ·      ·
·          table  dec2@dec2_d_idx
·          spans  /NaN-/-Infinity

query TTT
EXPLAIN SELECT * FROM dec2 WHERE d = 'NaN'
----
render     ·      ·
 └── scan  ·      ·
·          table  dec2@dec2_d_idx
·          spans  /NaN-/-Infinity

query TTT
EXPLAIN SELECT * FROM dec2 WHERE isnan(d)
----
render     ·       ·
 └── scan  ·       ·
·          table   dec2@dec2_d_idx
·          spans   ALL
·          filter  isnan(d)

# ------------------------------------------------------------------------------
# Verify that lookups for Float NaN use indices when possible:
# - `WHERE f IS NaN` should perform a point lookup.
# - `WHERE f = 'NaN'` should also perform a point lookup.
# - `WHERE isnan(f)` is a function so it can't perform a point lookup.
# ------------------------------------------------------------------------------
statement ok
CREATE TABLE flt (f float null, unique index (f))

query TTT
EXPLAIN SELECT * FROM flt WHERE f IS NaN
----
render     ·      ·
 └── scan  ·      ·
·          table  flt@flt_f_key
·          spans  /NaN-/NaN/PrefixEnd

query TTT
EXPLAIN SELECT * FROM flt WHERE f = 'NaN'
----
render     ·      ·
 └── scan  ·      ·
·          table  flt@flt_f_key
·          spans  /NaN-/NaN/PrefixEnd

query TTT
EXPLAIN SELECT * FROM flt WHERE isnan(f)
----
render     ·       ·
 └── scan  ·       ·
·          table   flt@flt_f_key
·          spans   ALL
·          filter  isnan(f)

# ------------------------------------------------------
# Verify that multi-span point lookups are parallelized.
# ------------------------------------------------------
statement ok
CREATE TABLE a (a INT PRIMARY KEY, item STRING, price FLOAT, UNIQUE INDEX item (item), UNIQUE INDEX p (price))

statement ok
CREATE TABLE b (a INT, b INT, c INT NULL, d INT NULL, PRIMARY KEY (a, b))

# No parallel line printed out for single-span selects.
query TTT
EXPLAIN SELECT * FROM a WHERE a = 10
----
scan  ·         ·
·     table     a@primary
·     spans     /10-/10/#

query TTT
EXPLAIN SELECT * FROM a WHERE a = 10 OR a = 20
----
scan  ·         ·
·     table     a@primary
·     spans     /10-/10/# /20-/20/#
·     parallel  ·

query TTT
EXPLAIN SELECT * FROM a WHERE a IN (10, 20)
----
scan  ·         ·
·     table     a@primary
·     spans     /10-/10/# /20-/20/#
·     parallel  ·

# Verify that consolidated point spans are still parallelized.
query TTT
EXPLAIN SELECT * FROM a WHERE a in (10, 11)
----
scan  ·         ·
·     table     a@primary
·     spans     /10-/11/#
·     parallel  ·

query TTT
EXPLAIN SELECT * FROM a WHERE a > 10 AND a < 20
----
scan  ·         ·
·     table     a@primary
·     spans     /11-/19/#
·     parallel  ·

# This ticks all the boxes for parallelization apart from the fact that there
# is no end key in the span.
query TTT
EXPLAIN SELECT * FROM a WHERE a > 10
----
scan  ·         ·
·     table     a@primary
·     spans     /11-

# Test non-int types.

# Point queries on non-int types are parallel.
query TTT
EXPLAIN SELECT price FROM a WHERE item IN ('sock', 'ball')
----
render           ·         ·
 └── index-join  ·         ·
      │          table     a@primary
      └── scan   ·         ·
·                table     a@item
·                spans     /"ball"-/"ball"/PrefixEnd /"sock"-/"sock"/PrefixEnd
·                parallel  ·

# Range queries on non-int types are not parallel due to unbounded number of
# results.
query TTT
EXPLAIN SELECT item FROM a WHERE price > 5 AND price < 10 OR price > 20 AND price < 40
----
render           ·       ·
 └── index-join  ·       ·
      │          table   a@primary
      └── scan   ·       ·
·                table   a@p
·                spans   /5.000000000000001-/9.999999999999998/PrefixEnd /20.000000000000004-/39.99999999999999/PrefixEnd
·                filter  (price < 10.0) OR (price > 20.0)

# TODO(radu): fix this testcase after #31614 is resolved. There should be no filter left.
query TTT
EXPLAIN SELECT * FROM b WHERE (a = 10 AND b = 10) OR (a = 20 AND b = 20)
----
scan  ·         ·
·     table     b@primary
·     spans     /10/10-/10/10/# /20/20-/20/20/#
·     parallel  ·
·     filter    ((a = 10) AND (b = 10)) OR ((a = 20) AND (b = 20))

# This one isn't parallelizable because it's not a point lookup - only part of
# the primary key is specified.
query TTT
EXPLAIN SELECT * FROM b WHERE a = 10 OR a = 20
----
scan  ·      ·
·     table  b@primary
·     spans  /10-/11 /20-/21

# This one isn't parallelizable because it has a LIMIT clause.
query TTT
EXPLAIN SELECT * FROM a WHERE a = 10 OR a = 20 LIMIT 1
----
limit      ·      ·
 │         count  1
 └── scan  ·      ·
·          table  a@primary
·          spans  /10-/10/# /20-/20/#
·          limit  1

statement ok
CREATE INDEX on b(b) STORING (c)

# This one isn't parallelizable because its index isn't unique.
query TTT
EXPLAIN SELECT b FROM b WHERE b = 10 OR b = 20
----
render     ·      ·
 └── scan  ·      ·
·          table  b@b_b_idx
·          spans  /10-/11 /20-/21

statement ok
CREATE UNIQUE INDEX on b(c)

# If the index has nullable values, parallelize only when the spans do not
# specify any nulls.
query TTT
EXPLAIN SELECT c FROM b WHERE c = 10 OR c = 20
----
render     ·         ·
 └── scan  ·         ·
·          table     b@b_c_key
·          spans     /10-/11 /20-/21
·          parallel  ·

query TTT
EXPLAIN SELECT c FROM b WHERE c = 10 OR c < 2
----
render     ·      ·
 └── scan  ·      ·
·          table  b@b_c_key
·          spans  /!NULL-/2 /10-/11

statement ok
CREATE UNIQUE INDEX on b(d DESC)

# This scan is not parallelizable because the second span has a null in its end
# key.
query TTT
EXPLAIN SELECT d FROM b WHERE d = 10 OR d < 2
----
render     ·       ·
 └── scan  ·       ·
·          table   b@b_d_key
·          spans  /10-/9 /1-/NULL

statement ok
CREATE UNIQUE INDEX ON b(c, d)

# This scan is not parallelizable because although the second column is
# constrained, the first column is null.
# TODO(radu): fix this testcase after #31614 is resolved. There should be no filter left.
query TTT
EXPLAIN SELECT d FROM b WHERE c = 10 AND d = 10 OR c IS NULL AND d > 0 AND d < 2
----
render     ·       ·
 └── scan  ·       ·
·          table   b@b_c_d_key
·          spans   /NULL/1-/NULL/2 /10/10-/10/11
·          filter  ((c = 10) AND (d = 10)) OR ((c IS NULL) AND (d < 2))

statement ok
CREATE UNIQUE INDEX on b(b) STORING (c)

# This one is parallelizable because its index is unique and non-null.
query TTT
EXPLAIN SELECT b FROM b WHERE b = 10 OR b = 20
----
render     ·         ·
 └── scan  ·         ·
·          table     b@b_b_key
·          spans     /10-/11 /20-/21
·          parallel  ·

statement ok
ALTER TABLE a SPLIT AT VALUES(5)

# Run a select to prime the range cache to simplify the trace below.
statement ok
SELECT * FROM a

# Make sure that the scan actually gets parallelized.
statement ok
SET tracing = on; SELECT * FROM a WHERE a = 0 OR a = 10; SET tracing = off

# The span "sending partial batch" means that the scan was parallelized.
# If this test is failing and doesn't have that span, it means that the scanNode
# was improperly configured to add a limit to the ScanRequest batch.
# See #30943 for more details.
query T
SELECT message FROM [SHOW TRACE FOR SESSION] WHERE message IN 
    ('querying next range at /Table/63/1/0',
     'querying next range at /Table/63/1/10',
     '=== SPAN START: kv.DistSender: sending partial batch ==='
    )
----
querying next range at /Table/63/1/0
=== SPAN START: kv.DistSender: sending partial batch ===
querying next range at /Table/63/1/10
