statement ok
CREATE TABLE t (
  k INT PRIMARY KEY,
  a INT,
  b INT,
  c STRING,
  j JSON,
  comp INT AS (a + 10) VIRTUAL,
  INDEX t_a_plus_b_idx ((a + b)),
  INDEX t_lower_c_a_plus_b_idx (lower(c), (a + b)),
  INVERTED INDEX t_b_j_a_asc (b DESC, (j->'a') ASC),
  FAMILY (k, a, b, c, j)
)

query T
SELECT create_statement FROM [SHOW CREATE TABLE t]
----
CREATE TABLE public.t (
  k INT8 NOT NULL,
  a INT8 NULL,
  b INT8 NULL,
  c STRING NULL,
  j JSONB NULL,
  comp INT8 NULL AS (a + 10:::INT8) VIRTUAL,
  CONSTRAINT t_pkey PRIMARY KEY (k ASC),
  INDEX t_a_plus_b_idx ((a + b) ASC),
  INDEX t_lower_c_a_plus_b_idx (lower(c) ASC, (a + b) ASC),
  INVERTED INDEX t_b_j_a_asc (b DESC, (j->'a':::STRING)),
  FAMILY fam_0_k_a_b_c_j (k, a, b, c, j)
)

query T
SELECT create_statement FROM [SHOW CREATE TABLE t WITH REDACT]
----
CREATE TABLE public.t (
  k INT8 NOT NULL,
  a INT8 NULL,
  b INT8 NULL,
  c STRING NULL,
  j JSONB NULL,
  comp INT8 NULL AS (a + ‹×›:::INT8) VIRTUAL,
  CONSTRAINT t_pkey PRIMARY KEY (k ASC),
  INDEX t_a_plus_b_idx ((a + b) ASC),
  INDEX t_lower_c_a_plus_b_idx (lower(c) ASC, (a + b) ASC),
  INVERTED INDEX t_b_j_a_asc (b DESC, (j->‹×›:::STRING)),
  FAMILY fam_0_k_a_b_c_j (k, a, b, c, j)
)

query TTBTTTB colnames
SELECT * FROM [SHOW COLUMNS FROM t] ORDER BY column_name
----
column_name  data_type  is_nullable  column_default  generation_expression  indices                                                     is_hidden
a            INT8       true         NULL            ·                      {t_pkey}                                                    false
b            INT8       true         NULL            ·                      {t_b_j_a_asc,t_pkey}                                        false
c            STRING     true         NULL            ·                      {t_pkey}                                                    false
comp         INT8       true         NULL            a + 10                 {}                                                          false
j            JSONB      true         NULL            ·                      {t_pkey}                                                    false
k            INT8       false        NULL            ·                      {t_a_plus_b_idx,t_b_j_a_asc,t_lower_c_a_plus_b_idx,t_pkey}  false

# Referencing an inaccessible column in a CHECK constraint is not allowed.
statement error column \"crdb_internal_idx_expr\" is inaccessible and cannot be referenced
CREATE TABLE err (a INT, INDEX ((a + 10)), CHECK (crdb_internal_idx_expr > 0))

# Referencing an inaccessible column in a UNIQUE constraint is not allowed.
statement error column \"crdb_internal_idx_expr\" is inaccessible and cannot be referenced
CREATE TABLE err (a INT, INDEX ((a + 10)), UNIQUE (crdb_internal_idx_expr))

# Referencing an inaccessible column in a computed column expression is not
# allowed.
statement error column \"crdb_internal_idx_expr\" does not exist
CREATE TABLE err (a INT, INDEX ((a + 10)), comp INT AS (crdb_internal_idx_expr + 10) STORED)

# Referencing an inaccessible column in an index is not allowed.
statement error column \"crdb_internal_idx_expr\" is inaccessible and cannot be referenced
CREATE TABLE err (a INT, INDEX ((a + 10)), INDEX (crdb_internal_idx_expr))

# Referencing an inaccessible column in a partial index predicate expression is
# not allowed.
statement error column \"crdb_internal_idx_expr\" is inaccessible and cannot be referenced
CREATE TABLE err (a INT, INDEX ((a + 10)), INDEX (a) WHERE crdb_internal_idx_expr > 0)

# Referencing an inaccessible column in a FK on the child side is not allowed.
statement error column \"crdb_internal_idx_expr\" is inaccessible and cannot reference a foreign key
CREATE TABLE err (a INT, INDEX ((a + 10)), FOREIGN KEY (crdb_internal_idx_expr) REFERENCES t(a))

# A unique column name will be selected for expression index columns.
statement ok
CREATE TABLE name_collision (a INT, INDEX ((a + 10)), crdb_internal_idx_expr INT)

query T
SELECT * FROM (
  SELECT json_array_elements(
    crdb_internal.pb_to_json('cockroach.sql.sqlbase.Descriptor', descriptor, false)->'table'->'columns'
  ) AS desc FROM system.descriptor WHERE id = 'name_collision'::REGCLASS
) AS cols WHERE cols.desc->'name' = '"crdb_internal_idx_expr_1"'
----
{"computeExpr": "a + 10:::INT8", "id": 3, "inaccessible": true, "name": "crdb_internal_idx_expr_1", "nullable": true, "type": {"family": "IntFamily", "oid": 20, "width": 64}, "virtual": true}

statement error random\(\): volatile functions are not allowed in EXPRESSION INDEX ELEMENT
CREATE TABLE err (a INT, INDEX ((a + random()::INT)))

statement error column \"z\" does not exist
CREATE TABLE err (a INT, INDEX ((a + z)))

statement error EXPRESSION INDEX ELEMENT expression cannot reference computed columns
CREATE TABLE err (a INT, comp INT AS (a + 10) STORED, INDEX ((comp + 100)))

statement error type of index element NULL is ambiguous.*\nHINT: consider adding a type cast to the expression
CREATE TABLE err (a INT, INDEX (a, (NULL)))

statement ok
CREATE TABLE t_null_cast (a INT, INDEX (a, (NULL::TEXT)))

statement error index element \(a, b\) of type record is not indexable
CREATE TABLE err (a INT, b INT, INDEX (a, (row(a, b))))

statement error index element a \+ b of type int is not allowed as the last column in an inverted index.*\nHINT: see the documentation for more information about inverted indexes: https://www.cockroachlabs.com/docs/.*/inverted-indexes.html
CREATE TABLE err (a INT, b INT, INVERTED INDEX (a, (a + b)))

statement error index element \(a, b\) of type record is not allowed as the last column in an inverted index.*\nHINT: see the documentation for more information about inverted indexes: https://www.cockroachlabs.com/docs/.*/inverted-indexes.html
CREATE TABLE err (a INT, b INT, INVERTED INDEX (a, (row(a, b))))

statement error the last column in an inverted index cannot have the DESC option
CREATE TABLE err (a INT, j JSON, INVERTED INDEX (a, (j->'a') DESC))

statement error random\(\): volatile functions are not allowed in EXPRESSION INDEX ELEMENT
CREATE TABLE err (a INT, INDEX ((a + random()::INT)))

statement error column \"z\" does not exist
CREATE TABLE err (a INT, INDEX ((a + z)))

statement error EXPRESSION INDEX ELEMENT expression cannot reference computed columns
CREATE TABLE err (a INT, comp INT AS (a + 10) STORED, INDEX ((comp + 10)))

# TODO(mgartner): Postgres does not allow unique constraints with expressions,
# but does allow unique indexes with expressions. We may want to err in this
# case to be consistent with Postgres. See #65825.
statement ok
CREATE TABLE err (a INT, UNIQUE ((a + 10)))

# TODO(mgartner): Postgres does not allow unique constraints with expressions,
# but does allow unique indexes with expressions. We may want to err in this
# case to be consistent with Postgres. See #65825.
statement ok
ALTER TABLE t ADD CONSTRAINT err UNIQUE ((a + 10))

statement ok
DROP TABLE t

statement ok
CREATE TABLE t (
  k INT PRIMARY KEY,
  a INT,
  b INT,
  c STRING,
  j JSON,
  comp INT AS (a + 10) VIRTUAL,
  FAMILY (k, a, b, c, j)
)

statement ok
CREATE INDEX t_a_plus_b_idx ON t ((a + b))

statement ok
CREATE INDEX t_lower_c_idx ON t (lower(c))

statement ok
CREATE INDEX t_lower_c_a_plus_b_idx ON t (lower(c), (a + b))

statement ok
CREATE INDEX t_a_plus_ten_idx ON t ((a + 10))

statement error index \"err\" contains duplicate expression
CREATE INDEX err ON t ((a + 10), (a + 10))

query T
SELECT create_statement FROM [SHOW CREATE TABLE t]
----
CREATE TABLE public.t (
  k INT8 NOT NULL,
  a INT8 NULL,
  b INT8 NULL,
  c STRING NULL,
  j JSONB NULL,
  comp INT8 NULL AS (a + 10:::INT8) VIRTUAL,
  CONSTRAINT t_pkey PRIMARY KEY (k ASC),
  INDEX t_a_plus_b_idx ((a + b) ASC),
  INDEX t_lower_c_idx (lower(c) ASC),
  INDEX t_lower_c_a_plus_b_idx (lower(c) ASC, (a + b) ASC),
  INDEX t_a_plus_ten_idx ((a + 10:::INT8) ASC),
  FAMILY fam_0_k_a_b_c_j (k, a, b, c, j)
)

# Referencing an inaccessible column in a CHECK constraint is not allowed.
statement error column \"crdb_internal_idx_expr_2\" is inaccessible and cannot be referenced
ALTER TABLE t ADD CONSTRAINT err CHECK (crdb_internal_idx_expr_2 > 0)

# Referencing an inaccessible column in a NOT NULL constraint is not allowed.
statement error column \"crdb_internal_idx_expr_2\" is inaccessible and cannot be referenced
ALTER TABLE t ALTER COLUMN crdb_internal_idx_expr_2 SET NOT NULL

# Referencing an inaccessible column in a UNIQUE constraint is not allowed.
statement error column \"crdb_internal_idx_expr_2\" is inaccessible and cannot be referenced
ALTER TABLE t ADD CONSTRAINT err UNIQUE (crdb_internal_idx_expr_2)

# Referencing an inaccessible column in a computed column expression is not
# allowed.
statement error column \"crdb_internal_idx_expr_2\" is inaccessible and cannot be referenced in a computed column expression
ALTER TABLE t ADD COLUMN err INT AS (crdb_internal_idx_expr_2 + 10) STORED

# Referencing an inaccessible column in an index is not allowed.
statement error column \"crdb_internal_idx_expr_2\" is inaccessible and cannot be referenced
CREATE INDEX err ON t (crdb_internal_idx_expr_2)

# Referencing an inaccessible column in a partial index predicate expression is
# not allowed.
statement error column \"crdb_internal_idx_expr_2\" is inaccessible and cannot be referenced
CREATE INDEX err ON t (a) WHERE crdb_internal_idx_expr_2 > 0

# Referencing an inaccessible column in a FK is not allowed.
statement error column \"crdb_internal_idx_expr_2\" is inaccessible and cannot be referenced by a foreign key
CREATE TABLE child (a INT REFERENCES t(crdb_internal_idx_expr_2))

statement ok
CREATE TABLE child (a INT)

statement error column \"crdb_internal_idx_expr_2\" is inaccessible and cannot be referenced by a foreign key
ALTER TABLE child ADD CONSTRAINT err FOREIGN KEY (a) REFERENCES t(crdb_internal_idx_expr_2)

statement error column \"crdb_internal_idx_expr_2\" is inaccessible and cannot reference a foreign key
ALTER TABLE t ADD CONSTRAINT err FOREIGN KEY (crdb_internal_idx_expr_2) REFERENCES child(a)

# Dropping an inaccessible column is not allowed.
statement error cannot drop inaccessible column \"crdb_internal_idx_expr_2\"
ALTER TABLE t DROP COLUMN crdb_internal_idx_expr_2

# Renaming a column to the same name as one of the inaccessible expression index
# columns is not allowed.
statement error column \"crdb_internal_idx_expr_2\" of relation \"t\" already exists
ALTER TABLE t RENAME COLUMN a TO crdb_internal_idx_expr_2

# Renaming an inaccessible expression index column is not allowed.
statement error column \"crdb_internal_idx_expr_2\" is inaccessible and cannot be renamed
ALTER TABLE t RENAME COLUMN crdb_internal_idx_expr_2 TO err

# Adding a column with the same name as one of the inaccessible columns created
# for an expression index is not allowed.
statement error column \"crdb_internal_idx_expr_2\" of relation \"t\" already exists
ALTER TABLE t ADD COLUMN crdb_internal_idx_expr_2 INT

onlyif config local-legacy-schema-changer
query T
SELECT * FROM (
  SELECT json_array_elements(
    crdb_internal.pb_to_json('cockroach.sql.sqlbase.Descriptor', descriptor, false)->'table'->'columns'
  ) AS desc FROM system.descriptor WHERE id = 't'::REGCLASS
) AS cols WHERE cols.desc->'name' = '"crdb_internal_idx_expr_2"'
----
{"computeExpr": "a + 10:::INT8", "id": 9, "inaccessible": true, "name": "crdb_internal_idx_expr_2", "nullable": true, "type": {"family": "IntFamily", "oid": 20, "width": 64}, "virtual": true}

skipif config local-legacy-schema-changer
query T
SELECT * FROM (
  SELECT json_array_elements(
    crdb_internal.pb_to_json('cockroach.sql.sqlbase.Descriptor', descriptor, false)->'table'->'columns'
  ) AS desc FROM system.descriptor WHERE id = 't'::REGCLASS
) AS cols WHERE cols.desc->'name' = '"crdb_internal_idx_expr_2"'
----
{"computeExpr": "a + 10:::INT8", "id": 9, "inaccessible": true, "name": "crdb_internal_idx_expr_2", "nullable": true, "type": {"family": "IntFamily", "oid": 20, "width": 64}, "virtual": true}

statement ok
DROP INDEX t_lower_c_a_plus_b_idx

# Verify that the inaccessible columns in t_lower_c_a_plus_b_idx are not dropped
# because other indexes contain them.
# exists in the descriptor.
skipif config local-legacy-schema-changer
query T
SELECT cols.desc FROM (
  SELECT id, json_array_elements(
    crdb_internal.pb_to_json('cockroach.sql.sqlbase.Descriptor', descriptor, false)->'table'->'columns'
  ) AS desc FROM system.descriptor WHERE id = 't'::REGCLASS
) AS cols WHERE cols.desc->>'name' IN ('crdb_internal_idx_expr', 'crdb_internal_idx_expr_1')
ORDER BY id
----
{"computeExpr": "a + b", "id": 7, "inaccessible": true, "name": "crdb_internal_idx_expr", "nullable": true, "type": {"family": "IntFamily", "oid": 20, "width": 64}, "virtual": true}
{"computeExpr": "lower(c)", "id": 8, "inaccessible": true, "name": "crdb_internal_idx_expr_1", "nullable": true, "type": {"family": "StringFamily", "oid": 25}, "virtual": true}

onlyif config local-legacy-schema-changer
query T
SELECT cols.desc FROM (
  SELECT id, json_array_elements(
    crdb_internal.pb_to_json('cockroach.sql.sqlbase.Descriptor', descriptor, false)->'table'->'columns'
  ) AS desc FROM system.descriptor WHERE id = 't'::REGCLASS
) AS cols WHERE cols.desc->>'name' IN ('crdb_internal_idx_expr', 'crdb_internal_idx_expr_1')
ORDER BY id
----
{"computeExpr": "a + b", "id": 7, "inaccessible": true, "name": "crdb_internal_idx_expr", "nullable": true, "type": {"family": "IntFamily", "oid": 20, "width": 64}, "virtual": true}
{"computeExpr": "lower(c)", "id": 8, "inaccessible": true, "name": "crdb_internal_idx_expr_1", "nullable": true, "type": {"family": "StringFamily", "oid": 25}, "virtual": true}

statement ok
DROP INDEX t_a_plus_b_idx

# Verify that the inaccessible column created for t_a_plus_b_idx no longer
# exists in the descriptor.
query T
SELECT * FROM (
  SELECT json_array_elements(
    crdb_internal.pb_to_json('cockroach.sql.sqlbase.Descriptor', descriptor, false)->'table'->'columns'
  ) AS desc FROM system.descriptor WHERE id = 't'::REGCLASS
) AS cols WHERE cols.desc->>'name' = 'crdb_internal_idx_expr'
----

# Adding a column with the same name as one of the inaccessible columns created
# for an expression index is allowed after the index has been dropped.
statement ok
ALTER TABLE t ADD COLUMN crdb_internal_idx_expr INT

statement ok
ALTER TABLE t DROP COLUMN crdb_internal_idx_expr

statement error random\(\): volatile functions are not allowed in EXPRESSION INDEX ELEMENT
CREATE INDEX err ON t ((a + random()::INT))

statement error column \"z\" does not exist
CREATE INDEX err ON t ((a + z))

statement error EXPRESSION INDEX ELEMENT expression cannot reference computed columns
CREATE INDEX err ON t ((comp + 10))

statement error type of index element NULL is ambiguous.*\nHINT: consider adding a type cast to the expression
CREATE INDEX err ON t (a, (NULL), b)

statement ok
CREATE INDEX t_cast_idx ON t (a, (NULL::TEXT), b)

statement ok
CREATE INDEX err ON t (a, (j->'a'));

statement ok
DROP INDEX err

statement error index element \(a, b\) of type record is not indexable
CREATE INDEX err ON t (a, (row(a, b)));

statement ok
CREATE INVERTED INDEX err ON t ((j->'a'), j);

statement ok
DROP INDEX err

statement error index element a \+ b of type int is not allowed as the last column in an inverted index.*\nHINT: see the documentation for more information about inverted indexes: https://www.cockroachlabs.com/docs/.*/inverted-indexes.html
CREATE INVERTED INDEX err ON t (a, (a + b));

statement error index element \(a, b\) of type record is not allowed as the last column in an inverted index.*\nHINT: see the documentation for more information about inverted indexes: https://www.cockroachlabs.com/docs/.*/inverted-indexes.html
CREATE INVERTED INDEX err ON t (a, (row(a, b)));

statement error the last column in an inverted index cannot have the DESC option
CREATE INVERTED INDEX err ON t (a, (j->'a') DESC);

statement ok
CREATE INVERTED INDEX ON t (a, (j->'a') ASC);

statement ok
CREATE TABLE other (
  a INT
)

# An expression index cannot reference columns in other tables.
statement error no data source matches prefix: t in this context
CREATE INDEX err ON other ((t.a + 10))

# An inaccessible column used in an expression index cannot be referenced in
# queries.
statement error column \"crdb_internal_idx_expr\" does not exist
SELECT crdb_internal_idx_expr FROM t

statement error column \"crdb_internal_idx_expr\" does not exist
SELECT * FROM t WHERE crdb_internal_idx_expr > 0

statement error column \"t.crdb_internal_idx_expr\" does not exist
SELECT t.crdb_internal_idx_expr FROM t

statement error column \"t.crdb_internal_idx_expr\" does not exist
SELECT * FROM t WHERE t.crdb_internal_idx_expr > 0

# An inaccessible column cannot be used in a view.
statement error column \"crdb_internal_idx_expr\" does not exist
CREATE VIEW v(a) AS SELECT crdb_internal_idx_expr FROM t

statement ok
CREATE TABLE pk (
  k INT PRIMARY KEY,
  UNIQUE INDEX ((k + 10))
)

statement error cannot use inaccessible column \"crdb_internal_idx_expr\" in primary key
ALTER TABLE pk ALTER PRIMARY KEY USING COLUMNS (crdb_internal_idx_expr)

# Tests for CREATE TABLE ... LIKE.

statement ok
CREATE TABLE src (
  k INT PRIMARY KEY,
  a INT,
  b INT,
  j JSON,
  comp INT8 AS (1 + 10) VIRTUAL,
  INDEX ((a + b)),
  INDEX named_idx ((a + 1)),
  UNIQUE INDEX ((a + 10)),
  INVERTED INDEX ((a + b), (j->'a'))
);
CREATE TABLE copy (LIKE src);
CREATE TABLE copy_generated (LIKE src INCLUDING GENERATED);
CREATE TABLE copy_indexes (LIKE src INCLUDING INDEXES);
CREATE TABLE copy_all (LIKE src INCLUDING ALL)

query T
SELECT create_statement FROM [SHOW CREATE TABLE copy]
----
CREATE TABLE public.copy (
  k INT8 NOT NULL,
  a INT8 NULL,
  b INT8 NULL,
  j JSONB NULL,
  comp INT8 NULL,
  rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
  CONSTRAINT copy_pkey PRIMARY KEY (rowid ASC)
)

# Inaccessible expression index columns should not be copied if the indexes are
# not copied. copy should not have any crdb_internal_idx_expr columns.
query I
SELECT count(*) FROM (
  SELECT json_array_elements(
    crdb_internal.pb_to_json('cockroach.sql.sqlbase.Descriptor', descriptor, false)->'table'->'columns'
  ) AS desc FROM system.descriptor WHERE id = 'copy'::REGCLASS
) AS cols WHERE cols.desc->>'name' LIKE 'crdb_internal_idx_expr%'
----
0

query T
SELECT create_statement FROM [SHOW CREATE TABLE copy_generated]
----
CREATE TABLE public.copy_generated (
  k INT8 NOT NULL,
  a INT8 NULL,
  b INT8 NULL,
  j JSONB NULL,
  comp INT8 NULL AS (1:::INT8 + 10:::INT8) VIRTUAL,
  rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
  CONSTRAINT copy_generated_pkey PRIMARY KEY (rowid ASC)
)

# Inaccessible expression index columns should not be copied if the indexes are
# not copied. copy_generated should not have any crdb_internal_idx_expr columns.
query I
SELECT count(*) FROM (
  SELECT json_array_elements(
    crdb_internal.pb_to_json('cockroach.sql.sqlbase.Descriptor', descriptor, false)->'table'->'columns'
  ) AS desc FROM system.descriptor WHERE id = 'copy_generated'::REGCLASS
) AS cols WHERE cols.desc->>'name' LIKE 'crdb_internal_idx_expr%'
----
0

query T
SELECT create_statement FROM [SHOW CREATE TABLE copy_indexes]
----
CREATE TABLE public.copy_indexes (
  k INT8 NOT NULL,
  a INT8 NULL,
  b INT8 NULL,
  j JSONB NULL,
  comp INT8 NULL,
  CONSTRAINT src_pkey PRIMARY KEY (k ASC),
  INDEX src_expr_idx ((a + b) ASC),
  INDEX named_idx ((a + 1:::INT8) ASC),
  UNIQUE INDEX src_expr_key ((a + 10:::INT8) ASC),
  INVERTED INDEX src_expr_expr1_idx ((a + b) ASC, (j->'a':::STRING))
)

# Inaccessible expression index columns should be copied if the indexes are
# copied. copy_indexes should have crdb_internal_idx_expr columns.
query I
SELECT count(*) FROM (
  SELECT json_array_elements(
    crdb_internal.pb_to_json('cockroach.sql.sqlbase.Descriptor', descriptor, false)->'table'->'columns'
  ) AS desc FROM system.descriptor WHERE id = 'copy_indexes'::REGCLASS
) AS cols WHERE cols.desc->>'name' LIKE 'crdb_internal_idx_expr%'
----
4

query T
SELECT create_statement FROM [SHOW CREATE TABLE copy_all]
----
CREATE TABLE public.copy_all (
  k INT8 NOT NULL,
  a INT8 NULL,
  b INT8 NULL,
  j JSONB NULL,
  comp INT8 NULL AS (1:::INT8 + 10:::INT8) VIRTUAL,
  CONSTRAINT src_pkey PRIMARY KEY (k ASC),
  INDEX src_expr_idx ((a + b) ASC),
  INDEX named_idx ((a + 1:::INT8) ASC),
  UNIQUE INDEX src_expr_key ((a + 10:::INT8) ASC),
  INVERTED INDEX src_expr_expr1_idx ((a + b) ASC, (j->'a':::STRING))
)

# Inaccessible expression index columns should be copied if the indexes are
# copied. copy_all should have crdb_internal_idx_expr columns.
query I
SELECT count(*) FROM (
  SELECT json_array_elements(
    crdb_internal.pb_to_json('cockroach.sql.sqlbase.Descriptor', descriptor, false)->'table'->'columns'
  ) AS desc FROM system.descriptor WHERE id = 'copy_all'::REGCLASS
) AS cols WHERE cols.desc->>'name' LIKE 'crdb_internal_idx_expr%'
----
4

# Test anonymous index name generation.

statement ok
CREATE TABLE anon (
  k INT PRIMARY KEY,
  a INT,
  b INT,
  c STRING,
  FAMILY (k, a, b, c)
);
CREATE INDEX ON anon ((a + b));
CREATE INDEX ON anon ((a + 10), b);
CREATE UNIQUE INDEX ON anon (lower(c), b);
CREATE INDEX ON anon ((a + 10), b, lower(c));
CREATE INDEX ON anon ((a + 10), (b + 100), lower(c));

query T
SELECT create_statement FROM [SHOW CREATE TABLE anon]
----
CREATE TABLE public.anon (
  k INT8 NOT NULL,
  a INT8 NULL,
  b INT8 NULL,
  c STRING NULL,
  CONSTRAINT anon_pkey PRIMARY KEY (k ASC),
  INDEX anon_expr_idx ((a + b) ASC),
  INDEX anon_expr_b_idx ((a + 10:::INT8) ASC, b ASC),
  UNIQUE INDEX anon_expr_b_key (lower(c) ASC, b ASC),
  INDEX anon_expr_b_expr1_idx ((a + 10:::INT8) ASC, b ASC, lower(c) ASC),
  INDEX anon_expr_expr1_expr2_idx ((a + 10:::INT8) ASC, (b + 100:::INT8) ASC, lower(c) ASC),
  FAMILY fam_0_k_a_b_c (k, a, b, c)
)

statement ok
DROP TABLE anon;

statement ok
CREATE TABLE anon (
  k INT PRIMARY KEY,
  a INT,
  b INT,
  c STRING,
  INDEX ((a + b)),
  INDEX ((a + 10), b),
  UNIQUE INDEX (lower(c), b),
  INDEX ((a + 10), b, lower(c)),
  INDEX ((a + 10), (b + 100), lower(c)),
  FAMILY (k, a, b, c)
)

query T
SELECT create_statement FROM [SHOW CREATE TABLE anon]
----
CREATE TABLE public.anon (
  k INT8 NOT NULL,
  a INT8 NULL,
  b INT8 NULL,
  c STRING NULL,
  CONSTRAINT anon_pkey PRIMARY KEY (k ASC),
  INDEX anon_expr_idx ((a + b) ASC),
  INDEX anon_expr_b_idx ((a + 10:::INT8) ASC, b ASC),
  UNIQUE INDEX anon_expr_b_key (lower(c) ASC, b ASC),
  INDEX anon_expr_b_expr1_idx ((a + 10:::INT8) ASC, b ASC, lower(c) ASC),
  INDEX anon_expr_expr1_expr2_idx ((a + 10:::INT8) ASC, (b + 100:::INT8) ASC, lower(c) ASC),
  FAMILY fam_0_k_a_b_c (k, a, b, c)
)

# Querying expression indexes.

statement ok
CREATE INDEX t_a_plus_b_idx ON t ((a + b));

statement ok
CREATE INDEX t_lower_c_a_plus_b_idx ON t (lower(c), (a + b))

statement ok
INSERT INTO t VALUES
  (1, 10, 100, 'Foo'),
  (2, 20, 200, 'FOO'),
  (3, 10, 100, 'foo'),
  (4, 40, 400, 'BAR'),
  (5, 100, 10, 'Food')

query IIITI colnames,rowsort
SELECT k, a, b, c, comp FROM t@t_a_plus_b_idx WHERE a + b = 110
----
k  a    b    c     comp
1  10   100  Foo   20
3  10   100  foo   20
5  100  10   Food  110

query IIITI colnames,rowsort
SELECT k, a, b, c, comp FROM t@t_a_plus_b_idx WHERE a + b > 110
----
k  a   b    c    comp
2  20  200  FOO  30
4  40  400  BAR  50

query IIITI colnames,rowsort
SELECT k, a, b, c, comp FROM t@t_lower_c_idx WHERE lower(c) = 'foo'
----
k  a   b    c    comp
1  10  100  Foo  20
2  20  200  FOO  30
3  10  100  foo  20

query IIITI colnames,rowsort
SELECT k, a, b, c, comp FROM t@t_lower_c_idx WHERE lower(c) LIKE 'foo%'
----
k  a    b    c     comp
1  10   100  Foo   20
2  20   200  FOO   30
3  10   100  foo   20
5  100  10   Food  110

query IIITI colnames,rowsort
SELECT k, a, b, c, comp FROM t@t_lower_c_a_plus_b_idx WHERE lower(c) = 'foo' AND a + b > 110
----
k  a   b    c    comp
2  20  200  FOO  30

statement ok
CREATE TABLE d (a INT, j JSON);
CREATE INDEX json_expr_index on d ((j->'a'))

statement ok
INSERT INTO d VALUES
        (1, '{"a": "hello"}'),
        (2, '{"a": "b"}'),
        (3, '{"a": "bye"}'),
        (4, '{"a": "json"}')

query IT
SELECT a, j from d@json_expr_index where j->'a' = '"b"' ORDER BY a
----
2  {"a": "b"}

query IT
SELECT a, j from d@json_expr_index where j->'a' = '"b"' OR j->'a' = '"bye"' ORDER BY a
----
2  {"a": "b"}
3  {"a": "bye"}

query IT
SELECT a, j from d@json_expr_index where j->'a' > '"a"' ORDER BY a
----
1  {"a": "hello"}
2  {"a": "b"}
3  {"a": "bye"}
4  {"a": "json"}

query IT
SELECT a, j from d@json_expr_index where j->'a' <= '"hello"' ORDER BY a
----
1  {"a": "hello"}
2  {"a": "b"}
3  {"a": "bye"}


statement ok
INSERT INTO d VALUES
        (5, '{"a": "forward", "json": "inverted"}'),
        (6, '{"a": "c", "b": "d"}')


statement ok
CREATE INVERTED INDEX json_inv on d ((j->'a'), j)

query IT
SELECT a, j from d@json_inv where j->'a' = '"forward"' AND j->'json' = '"inverted"' ORDER BY a
----
5  {"a": "forward", "json": "inverted"}

query IT
SELECT a, j from d@json_inv where j->'a' = '"c"' AND j->'json' = '"inverted"' ORDER BY a
----

query IT
SELECT a, j from d@json_inv where j->'a' = '"c"' AND j->'b' = '"d"' ORDER BY a
----
6  {"a": "c", "b": "d"}


# Backfilling expression indexes.

statement ok
CREATE INDEX t_a_times_two_idx ON t ((a * 2))

query IIITI colnames,rowsort
SELECT k, a, b, c, comp FROM t@t_a_times_two_idx WHERE a * 2 = 20
----
k  a   b    c    comp
1  10  100  Foo  20
3  10  100  foo  20

query IIITI colnames,rowsort
SELECT k, a, b, c, comp FROM t@t_a_times_two_idx WHERE a * 2 < 100
----
k  a   b    c    comp
1  10  100  Foo  20
2  20  200  FOO  30
3  10  100  foo  20
4  40  400  BAR  50

# Backfill an expression index when a new table is created in the same
# transaction.

statement ok
BEGIN

statement ok
CREATE TABLE bf (a INT, b INT)

statement ok
INSERT INTO bf VALUES (1, 10), (6, 60)

statement ok
CREATE INDEX a_plus_b ON bf ((a + b))

statement ok
COMMIT

query II colnames,rowsort
SELECT * FROM bf@a_plus_b WHERE a + b = 66
----
a  b
6  60

statement ok
DROP TABLE bf

# Cannot create an expression index that references a column added in the same
# transaction. This is a limitation of virtual computed columns.

statement ok
CREATE TABLE bf (a INT)

statement ok
BEGIN

statement ok
ALTER TABLE bf ADD COLUMN b INT DEFAULT 10

statement error pgcode 0A000 index element expression referencing columns \("b"\) added in the current transaction
CREATE INDEX a_plus_b ON bf ((a + b))

statement ok
ABORT

statement ok
DROP TABLE bf

# Backfill a expression index with a user defined type.

statement ok
CREATE TYPE enum AS ENUM ('Foo', 'BAR', 'baz')

statement ok
CREATE TABLE bf (a enum)

statement ok
INSERT INTO bf VALUES ('Foo'), ('BAR'), ('baz')

statement ok
CREATE INDEX lower_a ON bf (lower(a::STRING))

query T rowsort
SELECT a FROM bf@lower_a WHERE lower(a::STRING) IN ('foo', 'bar', 'baz')
----
Foo
BAR
baz

statement ok
DROP TABLE bf

# Backfill a expression index with a user defined type when a new table is
# created in the same transaction.

statement ok
BEGIN

statement ok
CREATE TABLE bf (a enum)

statement ok
INSERT INTO bf VALUES ('Foo'), ('BAR'), ('baz')

statement ok
CREATE INDEX lower_a ON bf (lower(a::STRING))

statement ok
COMMIT

query T rowsort
SELECT a FROM bf@lower_a WHERE lower(a::STRING) IN ('foo', 'bar', 'baz')
----
Foo
BAR
baz

statement ok
DROP TABLE bf

# Add a primary key to a table with a expression index.

statement ok
CREATE TABLE bf (k INT NOT NULL, a INT, INDEX a_plus_10 ((a + 10)))

statement ok
INSERT INTO bf VALUES (1, 1), (6, 6)

statement ok
ALTER TABLE bf ADD PRIMARY KEY (k)

query II rowsort
SELECT * FROM bf@a_plus_10 WHERE a + 10 IN (11, 16)
----
1  1
6  6

statement ok
DROP TABLE bf

# Truncate "removes" all entries from an expression index (technically a new
# table is created). The expression index is preserved correctly in the new
# table.

statement ok
CREATE TABLE l (
    a INT PRIMARY KEY,
    b INT,
    INDEX a_plus_b ((a + b))
)

statement ok
INSERT INTO l VALUES (1, 1), (6, 6)

statement ok
TRUNCATE l

query II rowsort
SELECT * FROM l@a_plus_b
----

statement ok
INSERT INTO l VALUES (1, 1), (7, 7)

query II rowsort
SELECT * FROM l@a_plus_b
----
1  1
7  7

# Querying inverted expression indexes.

statement ok
CREATE TABLE inv (
  k INT PRIMARY KEY,
  i INT,
  j JSON,
  INVERTED INDEX j_a ((j->'a')),
  INVERTED INDEX j_a_b ((j->'a'->'b')),
  INVERTED INDEX i_j_a (i, (j->'a')),
  INVERTED INDEX i_plus_100_j_a ((i+100), (j->'a'))
);

statement ok
INSERT INTO inv VALUES
  (1, 1, 'null'),
  (2, 1, 'true'),
  (3, 1, '1'),
  (4, 1, '""'),
  (5, 1, '"x"'),
  (6, 1, '{}'),
  (7, 1, '[]'),
  (8, 1, '{"a": null}'),
  (9, 1, '{"a": true}'),
  (10, 1, '{"a": 1}'),
  (11, 1, '{"a": ""}'),
  (12, 1, '{"a": "x"}'),
  (13, 1, '{"a": []}'),
  (14, 1, '{"a": [null, 1, true, ""]}'),
  (15, 1, '{"a": ["x", "y", "z"]}'),
  (16, 2, '{"a": ["x", "y", "z"]}'),
  (17, 1, '{"a": ["p", "q"]}'),
  (18, 1, '{"a": [1, "x"]}'),
  (19, 2, '{"a": [1, "x"]}'),
  (20, 1, '{"a": {}}'),
  (21, 1, '{"a": {"b": null}}'),
  (22, 1, '{"a": {"b": true}}'),
  (23, 1, '{"a": {"b": 1}}'),
  (24, 1, '{"a": {"b": ""}}'),
  (25, 1, '{"a": {"b": "x"}}'),
  (26, 1, '{"a": {"b": []}}'),
  (27, 1, '{"a": {"b": [null, 1, true, ""]}}'),
  (28, 1, '{"a": {"b": ["x", "y", "z"]}}'),
  (29, 1, '{"a": {"b": ["p", "q"]}}'),
  (30, 1, '{"a": {"b": [1, "x"]}}'),
  (31, 1, '{"a": {"b": {}}}'),
  (32, 1, '{"a": {"b": {"x": "y"}}}'),
  (33, 1, '{"a": {"b": {"p": "q"}}}')

query T
SELECT j FROM inv@j_a WHERE j->'a' @> '"x"' ORDER BY k
----
{"a": "x"}
{"a": ["x", "y", "z"]}
{"a": ["x", "y", "z"]}
{"a": [1, "x"]}
{"a": [1, "x"]}

query T
SELECT j FROM inv@j_a_b WHERE j->'a'->'b' @> '"x"' ORDER BY k
----
{"a": {"b": "x"}}
{"a": {"b": ["x", "y", "z"]}}
{"a": {"b": [1, "x"]}}

query IT
SELECT i, j FROM inv@i_j_a WHERE i = 1 AND j->'a' @> '"x"' ORDER BY k
----
1  {"a": "x"}
1  {"a": ["x", "y", "z"]}
1  {"a": [1, "x"]}

query IT
SELECT i, j FROM inv@i_plus_100_j_a WHERE i+100 = 101 AND j->'a' @> '"x"' ORDER BY k
----
1  {"a": "x"}
1  {"a": ["x", "y", "z"]}
1  {"a": [1, "x"]}

# Backfilling inverted expression indexes.

statement ok
DROP INDEX j_a;
DROP INDEX j_a_b;
DROP INDEX i_j_a

statement ok
CREATE INVERTED INDEX j_a ON inv ((j->'a'));
CREATE INVERTED INDEX j_a_b ON inv ((j->'a'->'b'));
CREATE INVERTED INDEX i_j_a ON inv (i, (j->'a'))

query T
SELECT j FROM inv@j_a WHERE j->'a' @> '"x"' ORDER BY k
----
{"a": "x"}
{"a": ["x", "y", "z"]}
{"a": ["x", "y", "z"]}
{"a": [1, "x"]}
{"a": [1, "x"]}

query T
SELECT j FROM inv@j_a_b WHERE j->'a'->'b' @> '"x"' ORDER BY k
----
{"a": {"b": "x"}}
{"a": {"b": ["x", "y", "z"]}}
{"a": {"b": [1, "x"]}}

query IT
SELECT i, j FROM inv@i_j_a WHERE i = 1 AND j->'a' @> '"x"' ORDER BY k
----
1  {"a": "x"}
1  {"a": ["x", "y", "z"]}
1  {"a": [1, "x"]}

query IT
SELECT i, j FROM inv@i_plus_100_j_a WHERE i+100 = 101 AND j->'a' @> '"x"' ORDER BY k
----
1  {"a": "x"}
1  {"a": ["x", "y", "z"]}
1  {"a": [1, "x"]}

# Backfilling JSON expression indexes

statement ok
CREATE TABLE json_backfill (
  k INT PRIMARY KEY,
  j JSON,
  INDEX forward_expr ((j->'a')),
  INDEX forward (j)
)

statement ok
INSERT INTO json_backfill VALUES
  (1, '[1, 2, 3]'),
  (2, '{"a": [1, 2, 3], "b": [4, 5, 6]}'),
  (3, '{"a": {"a": "b"}, "d": {"e": [1, 2, 3]}}'),
  (4, '{"a": [4, 5]}')

query T
SELECT j from json_backfill@forward_expr where j->'a' IN ('[1, 2, 3]', '[4,5]') ORDER BY k
----
{"a": [1, 2, 3], "b": [4, 5, 6]}
{"a": [4, 5]}

query T
SELECT j from json_backfill@forward_expr where j->'a' = '{"a": "b"}' ORDER BY k
----
{"a": {"a": "b"}, "d": {"e": [1, 2, 3]}}

query T
SELECT j from json_backfill@forward_expr where j->'a' > '{"a": "b"}' ORDER BY k
----

query T
SELECT j from json_backfill@forward_expr where j->'a' < '{"a": "b"}' ORDER BY k
----
{"a": [1, 2, 3], "b": [4, 5, 6]}
{"a": [4, 5]}

query T
SELECT j from json_backfill@forward where j = '[1, 2, 3]' ORDER BY k
----
[1, 2, 3]

query T
SELECT j from json_backfill@forward where j = '{"a": [4, 5]}' OR j = '[1, 2, 3]' ORDER BY k
----
[1, 2, 3]
{"a": [4, 5]}


statement ok
DROP INDEX forward_expr;
DROP INDEX forward;

statement ok
CREATE INDEX forward_expr on json_backfill ((j->'a'));
CREATE INDEX forward on json_backfill (j);

query T
SELECT j from json_backfill@forward_expr where j->'a' IN ('[1, 2, 3]', '[4,5]') ORDER BY k
----
{"a": [1, 2, 3], "b": [4, 5, 6]}
{"a": [4, 5]}

query T
SELECT j from json_backfill@forward_expr where j->'a' = '{"a": "b"}' ORDER BY k
----
{"a": {"a": "b"}, "d": {"e": [1, 2, 3]}}

query T
SELECT j from json_backfill@forward_expr where j->'a' > '{"a": "b"}' ORDER BY k
----

query T
SELECT j from json_backfill@forward_expr where j->'a' < '{"a": "b"}' ORDER BY k
----
{"a": [1, 2, 3], "b": [4, 5, 6]}
{"a": [4, 5]}

query T
SELECT j from json_backfill@forward where j = '[1, 2, 3]' ORDER BY k
----
[1, 2, 3]

query T
SELECT j from json_backfill@forward where j = '{"a": [4, 5]}' OR j = '[1, 2, 3]' ORDER BY k
----
[1, 2, 3]
{"a": [4, 5]}


# Unique expression indexes.

statement ok
CREATE TABLE uniq (
  k INT PRIMARY KEY,
  a INT,
  b INT,
  UNIQUE INDEX ((a + b))
)

statement ok
INSERT INTO uniq VALUES (1, 10, 100), (2, 20, 200)

statement error duplicate key value violates unique constraint \"uniq_idx\"
CREATE UNIQUE INDEX uniq_idx ON uniq ((a > 0))

statement error duplicate key value violates unique constraint \"uniq_expr_key\"\nDETAIL: Key \(a \+ b\)=\(110\) already exists
INSERT INTO uniq VALUES (3, 1, 109)

statement ok
INSERT INTO uniq VALUES (3, 1, 109) ON CONFLICT DO NOTHING

# Expressions as ON CONFLICT targets are not yet allowed.
# See https://github.com/cockroachdb/cockroach/issues/67893.
statement error syntax error
INSERT INTO uniq VALUES (3, 1, 109) ON CONFLICT ((a + b)) DO NOTHING

# Expressions as ON CONFLICT targets are not yet allowed.
# See https://github.com/cockroachdb/cockroach/issues/67893.
statement error syntax error
INSERT INTO uniq VALUES (4, 1, 219) ON CONFLICT ((a + b)) DO UPDATE SET b = 90

# Regression test for #72012. CHECK CONSTRAINT should not check inaccessible columns.

statement ok
CREATE TABLE t72012 (col integer);

statement ok
CREATE INDEX t72012_idx ON t72012 ((abs(col)));

statement ok
ALTER TABLE t72012 ALTER COLUMN col SET NOT NULL

query TT
SHOW CREATE TABLE t72012
----
t72012  CREATE TABLE public.t72012 (
          col INT8 NOT NULL,
          rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
          CONSTRAINT t72012_pkey PRIMARY KEY (rowid ASC),
          INDEX t72012_idx (abs(col) ASC)
        )

statement ok
ALTER TABLE t72012 ALTER COLUMN col DROP NOT NULL;

statement ok
INSERT INTO t72012 VALUES (NULL)

statement error (.*validation of CHECK "col IS NOT NULL" failed on row: col=NULL, rowid=\d+|pq: validation of column "col" NOT NULL failed on row: col=NULL, rowid=\d+)
ALTER TABLE t72012 ALTER COLUMN col SET NOT NULL

# Regression test for #74216. Do not allow expressions with mutable casts
# between string types and reg* types.
subtest 74216

statement error regtype::string: context-dependent operators are not allowed in EXPRESSION INDEX ELEMENT
CREATE TABLE t74216 (
    a REGTYPE,
    UNIQUE ((a::STRING))
)

# create index using an expression works even when retried
statement ok
BEGIN;
CREATE INDEX t_a_times_three_idx ON t ((a * 3));
SELECT crdb_internal.force_retry('10ms');
COMMIT

# JSON Expression Unique Indexes

statement ok
CREATE TABLE uniq_json (
  k INT PRIMARY KEY,
  j JSON,
  UNIQUE INDEX ((j->'a'))
)

statement error duplicate key value violates unique constraint "uniq_json_expr_key"
INSERT INTO uniq_json VALUES
  (1, '{"a": "b"}'),
  (2, '{"a": "b"}')
