
# Tests for creating a hash sharded primary key
statement ok
CREATE TABLE sharded_primary (a INT PRIMARY KEY USING HASH WITH (bucket_count=10))

query TT
SHOW CREATE TABLE sharded_primary
----
sharded_primary  CREATE TABLE public.sharded_primary (
                   crdb_internal_a_shard_10 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 10:::INT8)) VIRTUAL,
                   a INT8 NOT NULL,
                   CONSTRAINT sharded_primary_pkey PRIMARY KEY (a ASC) USING HASH WITH (bucket_count=10)
                 )

statement error pgcode 22023 hash sharded index bucket count must be in range \[2, 2048\], got -1
CREATE TABLE invalid_bucket_count (k INT PRIMARY KEY USING HASH WITH (bucket_count=-1))

statement error pgcode 22023 hash sharded index bucket count must be in range \[2, 2048\], got 999999999
CREATE TABLE invalid_bucket_count (k INT PRIMARY KEY USING HASH WITH (bucket_count=999999999))

statement error pgcode 22023 hash sharded index bucket count must be in range \[2, 2048\], got 1
CREATE TABLE invalid_bucket_count (k INT PRIMARY KEY USING HASH WITH (bucket_count=1))

statement error expected BUCKET_COUNT expression to have type int, but '2.32' has type decimal
CREATE TABLE fractional_bucket_count (k INT PRIMARY KEY USING HASH WITH (bucket_count=2.32))

statement error variable sub-expressions are not allowed in BUCKET_COUNT
CREATE TABLE invalid_bucket_count (k INT PRIMARY KEY USING HASH WITH (bucket_count=(SELECT 1)))

# Ensure that this is round-tripable
statement ok
DROP TABLE sharded_primary

statement ok
CREATE TABLE sharded_primary (
                a INT8 NOT NULL,
                CONSTRAINT "primary" PRIMARY KEY (a ASC) USING HASH WITH (bucket_count=10),
                FAMILY "primary" (a)
)

query TT
SHOW CREATE TABLE sharded_primary
----
sharded_primary  CREATE TABLE public.sharded_primary (
                   a INT8 NOT NULL,
                   crdb_internal_a_shard_10 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 10:::INT8)) VIRTUAL,
                   CONSTRAINT "primary" PRIMARY KEY (a ASC) USING HASH WITH (bucket_count=10)
                 )

query TTT colnames
SELECT
  tablename, indexname, indexdef
FROM pg_indexes
WHERE tablename = 'sharded_primary'
ORDER BY 1, 2, 3
----
tablename        indexname  indexdef
sharded_primary  primary    CREATE UNIQUE INDEX "primary" ON test.public.sharded_primary USING btree (a ASC) USING HASH WITH (bucket_count=10)

query TTB
SELECT index_name, column_name, implicit FROM [SHOW INDEXES FROM sharded_primary]
ORDER BY index_name, seq_in_index
----
primary  crdb_internal_a_shard_10  true
primary  a                         false

query TTB colnames
SELECT index_name, column_name, implicit FROM crdb_internal.index_columns
WHERE descriptor_name = 'sharded_primary' AND column_type = 'key'
ORDER BY 1, 2
----
index_name  column_name               implicit
primary     a                         false
primary     crdb_internal_a_shard_10  true

statement ok
INSERT INTO sharded_primary values (1), (2), (3)

query error pq: duplicate key value violates unique constraint "primary"\nDETAIL: Key \(a\)=\(1\) already exists\.
INSERT INTO sharded_primary values (1)

# Ensure that the shard column is assigned into the column family of the first column in
# the index column set.
statement ok
CREATE TABLE specific_family (
    a INT,
    b INT,
    INDEX (b) USING HASH WITH (bucket_count=10),
    FAMILY "a_family" (a),
    FAMILY "b_family" (b)
)

query TT
SHOW CREATE TABLE specific_family
----
specific_family  CREATE TABLE public.specific_family (
                   a INT8 NULL,
                   b INT8 NULL,
                   crdb_internal_b_shard_10 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(b))), 10:::INT8)) VIRTUAL,
                   rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
                   CONSTRAINT specific_family_pkey PRIMARY KEY (rowid ASC),
                   INDEX specific_family_b_idx (b ASC) USING HASH WITH (bucket_count=10),
                   FAMILY a_family (a, rowid),
                   FAMILY b_family (b)
                 )

# Tests for secondary sharded indexes
statement ok
CREATE TABLE sharded_secondary (a INT, INDEX (a) USING HASH WITH (bucket_count=4))

query TT
SHOW CREATE TABLE sharded_secondary
----
sharded_secondary  CREATE TABLE public.sharded_secondary (
                     a INT8 NULL,
                     crdb_internal_a_shard_4 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 4:::INT8)) VIRTUAL,
                     rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
                     CONSTRAINT sharded_secondary_pkey PRIMARY KEY (rowid ASC),
                     INDEX sharded_secondary_a_idx (a ASC) USING HASH WITH (bucket_count=4)
                   )

statement ok
DROP TABLE sharded_secondary

statement ok
CREATE TABLE sharded_secondary (
                        a INT8 NULL,
                        INDEX sharded_secondary_crdb_internal_a_shard_4_a_idx (a ASC) USING HASH WITH (bucket_count=4),
                        FAMILY "primary" (a, rowid)
)

query TT
SHOW CREATE TABLE sharded_secondary
----
sharded_secondary  CREATE TABLE public.sharded_secondary (
                     a INT8 NULL,
                     crdb_internal_a_shard_4 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 4:::INT8)) VIRTUAL,
                     rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
                     CONSTRAINT sharded_secondary_pkey PRIMARY KEY (rowid ASC),
                     INDEX sharded_secondary_crdb_internal_a_shard_4_a_idx (a ASC) USING HASH WITH (bucket_count=4)
                   )

statement ok
INSERT INTO sharded_secondary values (1), (2), (1)

statement ok
DROP TABLE sharded_secondary

statement ok
CREATE TABLE sharded_secondary (
    a INT
)

statement ok
CREATE INDEX ON sharded_secondary (a) USING HASH WITH (bucket_count=10)

statement ok
INSERT INTO sharded_secondary values (1), (2), (1)

query TT
SHOW CREATE TABLE sharded_secondary
----
sharded_secondary  CREATE TABLE public.sharded_secondary (
                     a INT8 NULL,
                     rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
                     crdb_internal_a_shard_10 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 10:::INT8)) VIRTUAL,
                     CONSTRAINT sharded_secondary_pkey PRIMARY KEY (rowid ASC),
                     INDEX sharded_secondary_a_idx (a ASC) USING HASH WITH (bucket_count=10)
                   )

statement ok
INSERT INTO sharded_secondary values (3), (2), (1)

# Test multiple indexes on the same column set
statement ok
CREATE INDEX ON sharded_secondary (a) USING HASH WITH (bucket_count=4)

query TT
SHOW CREATE TABLE sharded_secondary
----
sharded_secondary  CREATE TABLE public.sharded_secondary (
                     a INT8 NULL,
                     rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
                     crdb_internal_a_shard_10 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 10:::INT8)) VIRTUAL,
                     crdb_internal_a_shard_4 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 4:::INT8)) VIRTUAL,
                     CONSTRAINT sharded_secondary_pkey PRIMARY KEY (rowid ASC),
                     INDEX sharded_secondary_a_idx (a ASC) USING HASH WITH (bucket_count=10),
                     INDEX sharded_secondary_a_idx1 (a ASC) USING HASH WITH (bucket_count=4)
                   )

# Drop a sharded index and ensure that the shard column is dropped with it.
statement ok
DROP INDEX sharded_secondary_a_idx

query TT
SHOW CREATE TABLE sharded_secondary
----
sharded_secondary  CREATE TABLE public.sharded_secondary (
                     a INT8 NULL,
                     rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
                     crdb_internal_a_shard_4 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 4:::INT8)) VIRTUAL,
                     CONSTRAINT sharded_secondary_pkey PRIMARY KEY (rowid ASC),
                     INDEX sharded_secondary_a_idx1 (a ASC) USING HASH WITH (bucket_count=4)
                   )

statement ok
DROP INDEX sharded_secondary_a_idx1


query TT
SHOW CREATE TABLE sharded_secondary
----
sharded_secondary  CREATE TABLE public.sharded_secondary (
                     a INT8 NULL,
                     rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
                     CONSTRAINT sharded_secondary_pkey PRIMARY KEY (rowid ASC)
                   )

# Ensure that the shard column cannot be used in the same txn if its dropped along with
# the sharded index.
statement ok
CREATE INDEX idx on sharded_secondary (a) USING HASH WITH (bucket_count=3)

# Use high priority to decrease the likelihood of the transaction being aborted.
statement ok
BEGIN TRANSACTION PRIORITY HIGH ISOLATION LEVEL SERIALIZABLE

statement ok
SELECT crdb_internal_a_shard_3 FROM sharded_secondary

statement ok
DROP INDEX sharded_secondary@idx

statement error pq: column "crdb_internal_a_shard_3" does not exist
SELECT crdb_internal_a_shard_3 FROM sharded_secondary

statement ok
ROLLBACK

statement ok
DROP INDEX sharded_secondary@idx

# Ensure that multiple (> 2) identical indexes can be created.
statement ok
CREATE INDEX ON sharded_secondary (a) USING HASH WITH (bucket_count=10)

statement ok
CREATE INDEX ON sharded_secondary (a) USING HASH WITH (bucket_count=10)

statement ok
CREATE INDEX ON sharded_secondary (a) USING HASH WITH (bucket_count=10)

query TT
SHOW CREATE TABLE sharded_secondary
----
sharded_secondary  CREATE TABLE public.sharded_secondary (
                     a INT8 NULL,
                     rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
                     crdb_internal_a_shard_10 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 10:::INT8)) VIRTUAL,
                     CONSTRAINT sharded_secondary_pkey PRIMARY KEY (rowid ASC),
                     INDEX sharded_secondary_a_idx (a ASC) USING HASH WITH (bucket_count=10),
                     INDEX sharded_secondary_a_idx1 (a ASC) USING HASH WITH (bucket_count=10),
                     INDEX sharded_secondary_a_idx2 (a ASC) USING HASH WITH (bucket_count=10)
                   )


# Ensure that the table descriptor was left in a "valid" state
query I
SELECT count(*) FROM sharded_secondary
----
6

statement ok
CREATE INDEX ON sharded_primary (a) USING HASH WITH (bucket_count=4);

query TT
SHOW CREATE TABLE sharded_primary
----
sharded_primary  CREATE TABLE public.sharded_primary (
                   a INT8 NOT NULL,
                   crdb_internal_a_shard_10 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 10:::INT8)) VIRTUAL,
                   crdb_internal_a_shard_4 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 4:::INT8)) VIRTUAL,
                   CONSTRAINT "primary" PRIMARY KEY (a ASC) USING HASH WITH (bucket_count=10),
                   INDEX sharded_primary_a_idx (a ASC) USING HASH WITH (bucket_count=4)
                 )

statement ok
DROP INDEX sharded_primary_a_idx

statement ok
SELECT count(*) FROM sharded_primary

query TT
SHOW CREATE TABLE sharded_primary
----
sharded_primary  CREATE TABLE public.sharded_primary (
                   a INT8 NOT NULL,
                   crdb_internal_a_shard_10 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 10:::INT8)) VIRTUAL,
                   CONSTRAINT "primary" PRIMARY KEY (a ASC) USING HASH WITH (bucket_count=10)
                 )

statement ok
CREATE INDEX on sharded_primary (a) USING HASH WITH (bucket_count=10);

query TT
SHOW CREATE TABLE sharded_primary
----
sharded_primary  CREATE TABLE public.sharded_primary (
                   a INT8 NOT NULL,
                   crdb_internal_a_shard_10 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 10:::INT8)) VIRTUAL,
                   CONSTRAINT "primary" PRIMARY KEY (a ASC) USING HASH WITH (bucket_count=10),
                   INDEX sharded_primary_a_idx (a ASC) USING HASH WITH (bucket_count=10)
                 )

statement ok
DROP INDEX sharded_primary_a_idx

# Ensure that the table descriptor was left in a "valid" state
statement ok
SELECT count(*) FROM sharded_primary

statement ok
DROP TABLE sharded_secondary

statement ok
CREATE TABLE sharded_secondary (a INT8, INDEX (a) USING HASH WITH (bucket_count=12))

# Ensure that hash sharded indexes can be created on columns that are added in the same
# statement, just like non-sharded indexes.
statement ok
BEGIN TRANSACTION

statement ok
ALTER TABLE sharded_secondary ADD COLUMN b INT

statement ok
CREATE INDEX ON sharded_secondary (a, b) USING HASH WITH (bucket_count=12)

statement ok
COMMIT TRANSACTION

# Ensure that sharded indexes cannot be created on computed columns
statement ok
ALTER TABLE sharded_secondary ADD COLUMN c INT AS (mod(a, 100)) STORED

statement error cannot create a sharded index on a computed column
CREATE INDEX ON sharded_secondary (a, c) USING HASH WITH (bucket_count=12);

# Ensure that sharded indexes cannot be created on computed columns
# in the same txn
statement error cannot create a sharded index on a computed column
CREATE TABLE shard_on_computed_column (
    a INT,
    b INT AS (a % 5) STORED,
    INDEX (b) USING HASH WITH (bucket_count=10)
)

statement ok
BEGIN TRANSACTION

statement ok
ALTER TABLE sharded_secondary ADD COLUMN d INT AS (mod(a, 100)) STORED

statement error cannot create a sharded index on a computed column
CREATE INDEX ON sharded_secondary (a, d) USING HASH WITH (bucket_count=12);

statement ok
ROLLBACK TRANSACTION

# Ensure that the shard column isn't dropped even if its being used by a non-sharded index
statement ok
CREATE TABLE column_used_on_unsharded (
    a INT,
    INDEX foo (a) USING HASH WITH (bucket_count=10)
)

statement ok
CREATE INDEX on column_used_on_unsharded (crdb_internal_a_shard_10)

statement ok
DROP INDEX column_used_on_unsharded@foo

query TT
SHOW CREATE TABLE column_used_on_unsharded
----
column_used_on_unsharded  CREATE TABLE public.column_used_on_unsharded (
                            a INT8 NULL,
                            crdb_internal_a_shard_10 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 10:::INT8)) VIRTUAL,
                            rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
                            CONSTRAINT column_used_on_unsharded_pkey PRIMARY KEY (rowid ASC),
                            INDEX column_used_on_unsharded_crdb_internal_a_shard_10_idx (crdb_internal_a_shard_10 ASC)
                          )

statement ok
DROP INDEX column_used_on_unsharded_crdb_internal_a_shard_10_idx

statement ok
CREATE TABLE column_used_on_unsharded_create_table (
    a INT,
    INDEX foo (a) USING HASH WITH (bucket_count=10),
    INDEX (crdb_internal_a_shard_10)
)

statement ok
DROP INDEX column_used_on_unsharded_create_table@foo

query TT
SHOW CREATE TABLE column_used_on_unsharded_create_table
----
column_used_on_unsharded_create_table  CREATE TABLE public.column_used_on_unsharded_create_table (
                                         a INT8 NULL,
                                         crdb_internal_a_shard_10 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 10:::INT8)) VIRTUAL,
                                         rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
                                         CONSTRAINT column_used_on_unsharded_create_table_pkey PRIMARY KEY (rowid ASC),
                                         INDEX column_used_on_unsharded_create_table_crdb_internal_a_shard_10_idx (crdb_internal_a_shard_10 ASC)
                                       )

statement ok
DROP INDEX column_used_on_unsharded_create_table_crdb_internal_a_shard_10_idx

statement ok
DROP TABLE sharded_primary

# Ensure everything works with weird column names
statement ok
CREATE TABLE weird_names (
    "I am a column with spaces" INT PRIMARY KEY USING HASH WITH (bucket_count=12),
    "'quotes' in the column's name" INT,
    FAMILY "primary" ("I am a column with spaces", "'quotes' in the column's name")
    )

statement ok
CREATE INDEX foo on weird_names ("'quotes' in the column's name") USING HASH WITH (bucket_count=4)

statement ok
INSERT INTO weird_names VALUES (1, 2)

query I
SELECT count(*) from weird_names WHERE "'quotes' in the column's name" = 2
----
1

query TT
SHOW CREATE TABLE weird_names
----
weird_names  CREATE TABLE public.weird_names (
               "crdb_internal_I am a column with spaces_shard_12" INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes("I am a column with spaces"))), 12:::INT8)) VIRTUAL,
               "I am a column with spaces" INT8 NOT NULL,
               "'quotes' in the column's name" INT8 NULL,
               "crdb_internal_'quotes' in the column's name_shard_4" INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes("'quotes' in the column's name"))), 4:::INT8)) VIRTUAL,
               CONSTRAINT weird_names_pkey PRIMARY KEY ("I am a column with spaces" ASC) USING HASH WITH (bucket_count=12),
               INDEX foo ("'quotes' in the column's name" ASC) USING HASH WITH (bucket_count=4)
             )

subtest column_does_not_exist

statement ok
CREATE TABLE t0();

statement error column "c0" does not exist
CREATE INDEX ON t0 (c0) USING HASH WITH (bucket_count=8);

statement ok
DROP TABLE t0;

# Test that creating an index on a column which is currently being dropped
# causes an error.
subtest create_hash_index_on_dropping_column

statement ok
CREATE TABLE create_idx_drop_column (c0 INT PRIMARY KEY, c1 INT);

statement ok
begin; ALTER TABLE create_idx_drop_column DROP COLUMN c1;

statement error column "c1" does not exist
CREATE INDEX idx_create_idx_drop_column ON create_idx_drop_column (c1) USING HASH WITH (bucket_count=8);

statement ok
ROLLBACK;

statement ok
DROP TABLE create_idx_drop_column;

# Test that NULL values can be a part of a hash-sharded index.
subtest null_values_in_sharded_columns

statement ok
CREATE TABLE sharded_index_with_nulls (
     a INT8 PRIMARY KEY,
     b INT8,
     INDEX (b) USING HASH WITH (bucket_count=8)
)

statement ok
INSERT INTO sharded_index_with_nulls VALUES (1, NULL);

statement ok
DROP TABLE sharded_index_with_nulls;

# Test that renaming a column which is a member of a hash sharded index works.
subtest rename_column

statement ok
CREATE TABLE rename_column (
    c0 INT,
    c1 INT,
    c2 INT,
    PRIMARY KEY (c0, c1) USING HASH WITH (bucket_count=8),
    INDEX (c2) USING HASH WITH (bucket_count=8),
    FAMILY "primary" (c0, c1, c2)
);

statement ok
INSERT INTO rename_column VALUES (1, 2, 3);

query TT
SHOW CREATE TABLE rename_column
----
rename_column  CREATE TABLE public.rename_column (
                 c0 INT8 NOT NULL,
                 c1 INT8 NOT NULL,
                 c2 INT8 NULL,
                 crdb_internal_c0_c1_shard_8 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(c0, c1))), 8:::INT8)) VIRTUAL,
                 crdb_internal_c2_shard_8 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(c2))), 8:::INT8)) VIRTUAL,
                 CONSTRAINT rename_column_pkey PRIMARY KEY (c0 ASC, c1 ASC) USING HASH WITH (bucket_count=8),
                 INDEX rename_column_c2_idx (c2 ASC) USING HASH WITH (bucket_count=8)
               )

statement ok
ALTER TABLE rename_column RENAME c2 TO c3;

# Test mucking with primary key columns.
statement ok
ALTER TABLE rename_column RENAME c1 TO c2;

statement ok
ALTER TABLE rename_column RENAME c0 TO c1;

query TT
SHOW CREATE TABLE rename_column
----
rename_column  CREATE TABLE public.rename_column (
                 c1 INT8 NOT NULL,
                 c2 INT8 NOT NULL,
                 c3 INT8 NULL,
                 crdb_internal_c1_c2_shard_8 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(c1, c2))), 8:::INT8)) VIRTUAL,
                 crdb_internal_c3_shard_8 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(c3))), 8:::INT8)) VIRTUAL,
                 CONSTRAINT rename_column_pkey PRIMARY KEY (c1 ASC, c2 ASC) USING HASH WITH (bucket_count=8),
                 INDEX rename_column_c2_idx (c3 ASC) USING HASH WITH (bucket_count=8)
               )

query III
SELECT c3, c2, c1 FROM rename_column
----
3 2 1

# Test both at the same time.
statement ok
ALTER TABLE rename_column RENAME c1 TO c0, RENAME c2 TO c1, RENAME c3 TO c2;

query TT
SHOW CREATE TABLE rename_column
----
rename_column  CREATE TABLE public.rename_column (
                 c0 INT8 NOT NULL,
                 c1 INT8 NOT NULL,
                 c2 INT8 NULL,
                 crdb_internal_c0_c1_shard_8 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(c0, c1))), 8:::INT8)) VIRTUAL,
                 crdb_internal_c2_shard_8 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(c2))), 8:::INT8)) VIRTUAL,
                 CONSTRAINT rename_column_pkey PRIMARY KEY (c0 ASC, c1 ASC) USING HASH WITH (bucket_count=8),
                 INDEX rename_column_c2_idx (c2 ASC) USING HASH WITH (bucket_count=8)
               )

query III
SELECT c2, c1, c0 FROM rename_column
----
3 2 1

# Ensure that renaming a shard column fails.
statement error cannot rename shard column
ALTER TABLE rename_column RENAME crdb_internal_c2_shard_8 TO foo;

statement ok
DROP TABLE rename_column;

# This is a regression test for a bug whereby the dropping of a hash column
# could result in an invalid descriptor and would fail. The underlying bug was
# due to a column descriptor pointer to a slice being clobbered. See #55766.
subtest drop_earlier_column_due_to_hash_sharded_index

statement ok
CREATE TABLE IF NOT EXISTS drop_earlier_hash_column (
    i INT PRIMARY KEY,
    j INT,
    k INT
);

statement ok
CREATE INDEX h1 ON drop_earlier_hash_column(j) USING HASH WITH (bucket_count=8)

statement ok
CREATE INDEX h2 ON drop_earlier_hash_column(k) USING HASH WITH (bucket_count=8)

statement ok
DROP INDEX h1

subtest test_table_indexes

statement ok
CREATE TABLE poor_t (a INT PRIMARY KEY, b INT, INDEX t_idx_b (b) USING HASH WITH (bucket_count=8))

query ITITTBBBI colnames,rowsort
SELECT descriptor_id, descriptor_name, index_id, index_name, index_type, is_unique, is_inverted, is_sharded, shard_bucket_count
  FROM crdb_internal.table_indexes
 WHERE descriptor_name = 'poor_t'
----
descriptor_id  descriptor_name  index_id  index_name   index_type  is_unique  is_inverted  is_sharded  shard_bucket_count
121            poor_t           1         poor_t_pkey  primary     true       false        false       NULL
121            poor_t           2         t_idx_b      secondary   false      false        true        8

statement ok
DROP TABLE poor_t

subtest fk_reference_shard_pk

statement ok
DROP TABLE IF EXISTS child

statement ok
DROP TABLE IF EXISTS parent

statement ok
CREATE TABLE parent (
    id INT PRIMARY KEY USING HASH WITH (bucket_count=8)
);

statement ok
CREATE TABLE child (
    id INT PRIMARY KEY,
    pid INT,
    CONSTRAINT fk_child_pid FOREIGN KEY (pid) REFERENCES parent(id) ON DELETE CASCADE
);

statement error pq: insert on table "child" violates foreign key constraint "fk_child_pid"
INSERT INTO child VALUES (1,1)

statement ok
INSERT INTO parent VALUES (1)

statement ok
INSERT INTO child VALUES (1,1)

subtest fk_reference_shard_index

statement ok
DROP TABLE IF EXISTS child

statement ok
DROP TABLE IF EXISTS parent

statement ok
CREATE TABLE parent (
    id INT PRIMARY KEY,
    oid INT
);

statement ok
CREATE UNIQUE INDEX t_idx_oid ON parent(oid) USING HASH WITH (bucket_count=8)

statement ok
CREATE TABLE child (
    id INT PRIMARY KEY,
    poid INT,
    CONSTRAINT fk_child_pid FOREIGN KEY (poid) REFERENCES parent(oid) ON DELETE CASCADE
);

statement error pq: insert on table "child" violates foreign key constraint "fk_child_pid"
INSERT INTO child VALUES (1,11)

statement ok
INSERT INTO parent VALUES (1,11)

statement ok
INSERT INTO child VALUES (1,11)

subtest fk_reference_shard_pk_multi_col

statement ok
DROP TABLE IF EXISTS child

statement ok
DROP TABLE IF EXISTS parent

statement ok
CREATE TABLE parent (
    a INT NOT NULL,
    b INT NOT NULL,
    PRIMARY KEY (a, b) USING HASH WITH (bucket_count=8)
);

statement ok
CREATE TABLE child (
    ca INT PRIMARY KEY,
    cb INT,
    CONSTRAINT fk_child_ca_cb FOREIGN KEY (ca, cb) REFERENCES parent(a, b) ON DELETE CASCADE
);

statement error pq: insert on table "child" violates foreign key constraint "fk_child_ca_cb"
INSERT INTO child VALUES (1,1)

statement ok
INSERT INTO parent VALUES (1,1)

statement ok
INSERT INTO child VALUES (1,1)

subtest fk_reference_shard_index_multi_col

statement ok
DROP TABLE IF EXISTS child

statement ok
DROP TABLE IF EXISTS parent

statement ok
CREATE TABLE parent (
    a INT,
    b INT
);

statement ok
CREATE UNIQUE INDEX t_idx_a_b ON parent(a, b) USING HASH WITH (bucket_count=8)


statement ok
CREATE TABLE child (
    ca INT PRIMARY KEY,
    cb INT,
    CONSTRAINT fk_child_ca_cb FOREIGN KEY (ca, cb) REFERENCES parent(a, b) ON DELETE CASCADE
);

statement error pq: insert on table "child" violates foreign key constraint "fk_child_ca_cb"
INSERT INTO child VALUES (1,1)

statement ok
INSERT INTO parent VALUES (1,1)

statement ok
INSERT INTO child VALUES (1,1)

# Test creating tables with output of `SHOW CREATE TABLE` from table with
# hash-sharded index has same round trip.
subtest create_with_show_create

statement ok
DROP TABLE IF EXISTS t

statement ok
CREATE TABLE t (
    a INT PRIMARY KEY USING HASH WITH (bucket_count=8)
);

let $create_statement
SELECT create_statement FROM [SHOW CREATE TABLE t]

statement ok
DROP TABLE t

statement ok
$create_statement

query T
SELECT create_statement FROM [SHOW CREATE TABLE t]
----
CREATE TABLE public.t (
  crdb_internal_a_shard_8 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 8:::INT8)) VIRTUAL,
  a INT8 NOT NULL,
  CONSTRAINT t_pkey PRIMARY KEY (a ASC) USING HASH WITH (bucket_count=8)
)

# Make sure user defined constraint is used if it's equivalent to the shard
# column constraint would have been created.
statement ok
DROP TABLE t

statement ok
CREATE TABLE public.t (
    crdb_internal_a_shard_8 INT4 NOT VISIBLE NOT NULL AS (mod(fnv32(crdb_internal.datums_to_bytes(a)), 8:::INT8)) VIRTUAL,
    a INT8 NOT NULL,
    CONSTRAINT t_pkey PRIMARY KEY (a ASC) USING HASH WITH (bucket_count=8),
    FAMILY "primary" (a),
    CONSTRAINT check_crdb_internal_a_shard_8 CHECK (crdb_internal_a_shard_8 IN (0:::INT8, 1:::INT8, 2:::INT8, 3:::INT8, 4:::INT8, 5:::INT8, 6:::INT8, 7:::INT8))
)

query T
SELECT create_statement FROM [SHOW CREATE TABLE t]
----
CREATE TABLE public.t (
  crdb_internal_a_shard_8 INT4 NOT VISIBLE NOT NULL AS (mod(fnv32(crdb_internal.datums_to_bytes(a)), 8:::INT8)) VIRTUAL,
  a INT8 NOT NULL,
  CONSTRAINT t_pkey PRIMARY KEY (a ASC) USING HASH WITH (bucket_count=8),
  CONSTRAINT check_crdb_internal_a_shard_8 CHECK (crdb_internal_a_shard_8 IN (0:::INT8, 1:::INT8, 2:::INT8, 3:::INT8, 4:::INT8, 5:::INT8, 6:::INT8, 7:::INT8))
)

subtest test_hash_index_presplit

statement ok
CREATE TABLE t_hash_pre_split (
  a INT PRIMARY KEY,
  b INT
);

skipif config 3node-tenant-default-configs
query TITTT retry,rowsort
SELECT table_name, table_id, index_name, start_key, end_key
  FROM [SHOW RANGES FROM DATABASE test WITH INDEXES]
 WHERE table_name = 't_hash_pre_split'
----
t_hash_pre_split  133  t_hash_pre_split_pkey  /Table/128/3/7  /Max

statement ok
CREATE INDEX t_hash_pre_split_idx_b ON t_hash_pre_split (b) USING HASH WITH (bucket_count=8);

skipif config 3node-tenant-default-configs
query TITTT colnames,retry,rowsort
SELECT table_name, table_id, index_name, start_key, end_key
  FROM [SHOW RANGES FROM DATABASE test WITH INDEXES]
 WHERE table_name = 't_hash_pre_split'
----
table_name        table_id  index_name              start_key       end_key
t_hash_pre_split  133       t_hash_pre_split_pkey   /Table/128/3/7  /Table/133/2
t_hash_pre_split  133       t_hash_pre_split_idx_b  /Table/133/2    /Table/133/2/0
t_hash_pre_split  133       t_hash_pre_split_idx_b  /Table/133/2/0  /Table/133/2/1
t_hash_pre_split  133       t_hash_pre_split_idx_b  /Table/133/2/1  /Table/133/2/2
t_hash_pre_split  133       t_hash_pre_split_idx_b  /Table/133/2/2  /Table/133/2/3
t_hash_pre_split  133       t_hash_pre_split_idx_b  /Table/133/2/3  /Table/133/2/4
t_hash_pre_split  133       t_hash_pre_split_idx_b  /Table/133/2/4  /Table/133/2/5
t_hash_pre_split  133       t_hash_pre_split_idx_b  /Table/133/2/5  /Table/133/2/6
t_hash_pre_split  133       t_hash_pre_split_idx_b  /Table/133/2/6  /Table/133/2/7
t_hash_pre_split  133       t_hash_pre_split_idx_b  /Table/133/2/7  /Table/133/3

subtest test_default_bucket_count

statement ok
CREATE TABLE t_default_bucket_16 (
  a INT PRIMARY KEY USING HASH,
  b INT,
  c INT,
  INDEX idx_t_default_bucket_16_b (b) USING HASH,
  INDEX idx_t_default_bucket_16_c (c) USING HASH WITH (bucket_count=4),
  FAMILY fam_0_a (a),
  FAMILY fam_1_c_b (c, b)
);

query T
SELECT create_statement FROM [SHOW CREATE TABLE t_default_bucket_16]
----
CREATE TABLE public.t_default_bucket_16 (
  crdb_internal_a_shard_16 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 16:::INT8)) VIRTUAL,
  a INT8 NOT NULL,
  b INT8 NULL,
  c INT8 NULL,
  crdb_internal_b_shard_16 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(b))), 16:::INT8)) VIRTUAL,
  crdb_internal_c_shard_4 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(c))), 4:::INT8)) VIRTUAL,
  CONSTRAINT t_default_bucket_16_pkey PRIMARY KEY (a ASC) USING HASH WITH (bucket_count=16),
  INDEX idx_t_default_bucket_16_b (b ASC) USING HASH WITH (bucket_count=16),
  INDEX idx_t_default_bucket_16_c (c ASC) USING HASH WITH (bucket_count=4),
  FAMILY fam_0_a (a),
  FAMILY fam_1_c_b (c, b)
)

statement ok
SET CLUSTER SETTING sql.defaults.default_hash_sharded_index_bucket_count = 8

statement ok
CREATE TABLE t_default_bucket_8 (a INT PRIMARY KEY USING HASH);

query T
SELECT create_statement FROM [SHOW CREATE TABLE t_default_bucket_8]
----
CREATE TABLE public.t_default_bucket_8 (
  crdb_internal_a_shard_8 INT8 NOT VISIBLE NOT NULL AS (mod(fnv32(md5(crdb_internal.datums_to_bytes(a))), 8:::INT8)) VIRTUAL,
  a INT8 NOT NULL,
  CONSTRAINT t_default_bucket_8_pkey PRIMARY KEY (a ASC) USING HASH WITH (bucket_count=8)
)

# Make sure that uniqueness is guaranteed with hash index.
subtest unique_hash_sharded_index

statement ok
DROP TABLE IF EXISTS t

statement ok
CREATE TABLE t (
    x INT PRIMARY KEY,
    y INT,
    z INT,
    duped BOOL DEFAULT false
)

statement ok
CREATE UNIQUE INDEX t_uniq_idx_y_z ON t(y, z) USING HASH WITH (bucket_count=8)

statement ok
INSERT INTO t (x, y, z) VALUES (1, 11, 111) ON CONFLICT (y, z) DO UPDATE SET duped = true

query IIIB colnames
SELECT * FROM t
----
x  y   z    duped
1  11  111  false

statement ok
INSERT INTO t (x, y, z) VALUES (2, 22, 222) ON CONFLICT (y, z) DO UPDATE SET duped = true

query IIIB colnames,rowsort
SELECT * FROM t
----
x  y   z    duped
1  11  111  false
2  22  222  false

statement ok
INSERT INTO t (x, y, z) VALUES (1, 11, 111) ON CONFLICT (y, z) DO UPDATE SET duped = true

query IIIB colnames,rowsort
SELECT * FROM t
----
x  y   z    duped
1  11  111  true
2  22  222  false

statement error pq: duplicate key value violates unique constraint "t_uniq_idx_y_z"
INSERT INTO t (x, y, z) VALUES (3, 11, 111)

statement error pq: duplicate key value violates unique constraint "t_uniq_idx_y_z"
INSERT INTO t (x, y, z) VALUES (3, 11, 111) ON CONFLICT (y, z) DO UPDATE SET y = 22, z = 222

statement ok
INSERT INTO t (x, y, z) VALUES (3, 11, 111) ON CONFLICT (y, z) DO NOTHING

statement ok
INSERT INTO t (x, y, z) VALUES (3, 11, 111) ON CONFLICT DO NOTHING

query IIIB colnames,rowsort
SELECT * FROM t
----
x  y   z    duped
1  11  111  true
2  22  222  false

# Make sure uniqueness check works properly on hash sharded primary key.
statement ok
DROP TABLE IF EXISTS t

statement ok
CREATE TABLE t (
    x INT PRIMARY KEY USING HASH WITH (bucket_count=8),
    duped BOOL DEFAULT false
)

statement ok
INSERT INTO t (x) VALUES (1) ON CONFLICT (x) DO UPDATE SET duped = true

query IB colnames
SELECT * FROM t
----
x  duped
1  false

statement ok
INSERT INTO t (x) VALUES (2) ON CONFLICT (x) DO UPDATE SET duped = true

query IB colnames
SELECT * FROM t ORDER BY x
----
x  duped
1  false
2  false

statement ok
INSERT INTO t (x) VALUES (1) ON CONFLICT (x) DO UPDATE SET duped = true

query IB colnames
SELECT * FROM t ORDER BY x
----
x  duped
1  true
2  false

statement ok
INSERT INTO t (x) VALUES (2) ON CONFLICT (x) DO UPDATE SET duped = true

query IB colnames
SELECT * FROM t ORDER BY x
----
x  duped
1  true
2  true

statement error pq: duplicate key value violates unique constraint "t_pkey"
INSERT INTO t (x) VALUES (1)

statement error pq: duplicate key value violates unique constraint "t_pkey"
INSERT INTO t (x) VALUES (1) ON CONFLICT (x) DO UPDATE SET x = 2

statement ok
INSERT INTO t (x) VALUES (1) ON CONFLICT (x) DO NOTHING

statement ok
INSERT INTO t (x) VALUES (1) ON CONFLICT DO NOTHING

query IB colnames
SELECT * FROM t ORDER BY x
----
x  duped
1  true
2  true

# NOTE: Please keep this statement at the end this file.
# This session variable has noop and is just kept for backward compatibility.
statement ok
set experimental_enable_hash_sharded_indexes = on


# This is a regression test for a bug whereby the virtual shard column would
# not be included in the fetch columns when it was a key in the primary index.
# This caused the column backfiller to return an error and fail permanently and
# irrecoverably in the case that the a column backfill were to occur.
subtest add_non_null_column_to_hash_sharded_primary_key

statement ok
CREATE TABLE products (
    id INT8 PRIMARY KEY USING HASH DEFAULT unique_rowid(),
    title VARCHAR(150) NOT NULL,
    price INT8 NOT NULL
);

statement ok
INSERT INTO products (title, price) VALUES ('Test Product', '55');
INSERT INTO products (title, price) VALUES ('Test Product B', '60');


statement ok
ALTER TABLE products ADD COLUMN description STRING NOT NULL DEFAULT '';

statement ok
ALTER TABLE products DROP COLUMN description;

statement ok
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET LOCAL use_declarative_schema_changer = off;
ALTER TABLE products ADD COLUMN description STRING NOT NULL DEFAULT '';
COMMIT;

statement ok
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET LOCAL use_declarative_schema_changer = off;
ALTER TABLE products DROP COLUMN description;
COMMIT;

statement ok
DROP TABLE products;

statement ok
CREATE TABLE t103755 (c0 INT4, c1 INT);

statement ok
CREATE INDEX ON t103755(c0) USING HASH WITH BUCKET_COUNT=794;

statement ok
INSERT INTO t103755 (c0, c1) VALUES(-10, 1), (-20, 1);

# No predicates should be derived on the hash-sharded bucket column and this
# should return 2 rows.
query I
SELECT c0 FROM t103755 WHERE c0 < 0 OR c0 IN (VALUES (2)) ORDER BY c0;
----
-20
-10

# No predicates should be derived on the hash-sharded bucket column and this
# should return 2 rows.
query I
SELECT c0 FROM t103755 WHERE c0 < 0 OR (c0 IN (VALUES (2)) AND c1 IN (6,8,9)) ORDER BY c0
----
-20
-10

statement ok
CREATE TABLE t104484 (c0 VARBIT(10) AS (B'1') STORED)

# We use '$' to match the end of the error text, since we want to assert
# that the error does not include the schema plan details.
statement error cannot create a sharded index on a computed column$
CREATE INDEX ON t104484(c0 DESC) USING HASH

# Regression test for #91109 to make sure decimals are properly distributed.
subtest evenly_distributed_decimals

statement ok
CREATE TABLE decimals (
  d DECIMAL NOT NULL PRIMARY KEY USING HASH WITH (bucket_count=16),
  id INT8
)

statement ok
INSERT INTO decimals SELECT generate_series(0, 1023), 101;

# Crude check to make sure that no shard has "too few" rows in it.
query IB
SELECT crdb_internal_d_shard_16 AS shard, count(*) < 20 FROM decimals GROUP BY 1 ORDER BY 1
----
0   false
1   false
2   false
3   false
4   false
5   false
6   false
7   false
8   false
9   false
10  false
11  false
12  false
13  false
14  false
15  false

subtest end
