# LogicTest: local

subtest example

# The following test demonstrates that the function can create
# databases, schemas and tables.

query I
SELECT count(*) FROM crdb_internal.databases
WHERE name NOT IN ('system','defaultdb','postgres','test')
----
0

query I
SELECT count(*) FROM crdb_internal.tables
WHERE database_name NOT IN ('system','defaultdb','postgres','test')
----
0

query T
SELECT crdb_internal.generate_test_objects('a.b.c',array[2,3,5])->'generated_counts'
----
{"databases": 2, "schemas": 8, "tables": 30}

query I
SELECT count(*) FROM crdb_internal.databases
WHERE name NOT IN ('system','defaultdb','postgres','test')
----
2

query I
SELECT count(*) FROM crdb_internal.tables
WHERE database_name NOT IN ('system','defaultdb','postgres','test')
----
30

subtest show_names

# The following test demonstrates how the new objects are named by default.
# We use a fixed seed to make the output deterministic.

query T
SELECT crdb_internal.generate_test_objects('{"names":"zz.b.c","counts":[2,2,2],"seed":123}'::jsonb)->'generated_counts'
----
{"databases": 2, "schemas": 6, "tables": 8}

query TTT
SELECT quote_ident(database_name), quote_ident(schema_name), quote_ident(name)
FROM "".crdb_internal.tables WHERE database_name LIKE '%z%z%'
ORDER BY database_name, schema_name, name
----
"""z z_1"  b_1      c_1
"""z z_1"  b_1      c_2
"""z z_1"  b̷_2     c_1
"""z z_1"  b̷_2     c_2
"zz%q_2"   "b%p_2"  "%c'_2"
"zz%q_2"   "b%p_2"  "c""
_1"
"zz%q_2"  b_1  c_1
"zz%q_2"  b_1  c̕_2

# Show number placement inside the output identifier, without added noise.
query T
SELECT crdb_internal.generate_test_objects('{"names":"\"z#y\".b.c","counts":[2,2,2],"seed":123,"name_gen":{"noise":false}}'::jsonb)->'generated_counts'
----
{"databases": 2, "schemas": 6, "tables": 8}

query TTT rowsort
SELECT quote_ident(database_name), quote_ident(schema_name), quote_ident(name)
FROM "".crdb_internal.tables WHERE database_name LIKE '%z%y%'
----
z1y  b_1  c_1
z1y  b_1  c_2
z1y  b_2  c_1
z1y  b_2  c_2
z2y  b_1  c_1
z2y  b_1  c_2
z2y  b_2  c_1
z2y  b_2  c_2

subtest randomize_columns

query T
SELECT crdb_internal.generate_test_objects('{"seed":123,"randomize_columns":true,"counts":[3]}'::jsonb)->'generated_counts'
----
{"databases": 0, "schemas": 0, "tables": 3}

query TT
SELECT quote_ident(descriptor_name), quote_ident(column_name) FROM crdb_internal.table_columns
WHERE descriptor_name ILIKE '%t%e%s%t%'
ORDER BY descriptor_name, column_name
----
"test(_3"  address
"test(_3"  name
"test(_3"  rowid
test_1     "ad""dress%"
test_1     name😤
test_1     rowid
test_2     address
test_2     "name%80"
test_2     rowid

subtest templates/more_tables_generated_than_templates

statement ok
CREATE DATABASE base; CREATE TABLE base.t(x NUMERIC); CREATE TABLE base.u(y STRING);
CREATE DATABASE newdb; SET database = newdb

# More tables requested than there are templates: the count
# rotates around the templates.
query T
SELECT crdb_internal.generate_test_objects('{"seed":123,"counts":[10],"table_templates":["base.*"]}'::JSONB)->'generated_counts'
----
{"databases": 0, "schemas": 0, "tables": 10}

# The column names are also randomized by default.
query TTT
SELECT quote_ident(table_name), quote_ident(column_name), data_type FROM "".information_schema.columns
WHERE table_catalog = 'newdb' AND table_schema = 'public'
ORDER BY table_name, column_name
----
"                 u_3"     rowid    bigint
"                 u_3"     y        text
"*t""_1"          "%56x"   numeric
"*t""_1"          rowid    bigint
"\\U00051024u_4"  rowid    bigint
"\\U00051024u_4"  "y "     text
"t%q_3"           rowid    bigint
"t%q_3"           x        numeric
t_2               rowid    bigint
t_2               "x""%q"  numeric
t_4               """%vx"  numeric
t_4               rowid    bigint
t̷_5              "%vx"    numeric
t̷_5              rowid    bigint
u_1               rowid    bigint
u_1               y        text
u_2               rowid    bigint
u_2               "|y"     text
u_5               rowid    bigint
u_5               y        text

# As well as index names.
query TT
SELECT quote_ident(table_name), quote_ident(constraint_name) FROM "".information_schema.table_constraints
WHERE table_catalog = 'newdb' AND table_schema = 'public' AND constraint_type = 'PRIMARY KEY'
ORDER BY table_name, constraint_name
----
"                 u_3"         pr̫imary
"*t""_1"          "PrimaRy̧"
"\\U00051024u_4"  "primary"
"t%q_3"           "primary"
t_2               "primary"
t_4               "primar'y"
t̷_5              primary̕
u_1               "primary"
u_2               "primary"
u_5               "primar%vy"

subtest templates/fewer_tables_generated_than_templates

statement ok
CREATE DATABASE newdb2; SET database = newdb2

# More templates than tables requested: we get a random
# selection of the templates; there is no numbering.
query T
SELECT crdb_internal.generate_test_objects('{"seed":1234,"counts":[10],"table_templates":["system.*"]}'::JSONB)->'generated_counts'
----
{"databases": 0, "schemas": 0, "tables": 10}

# Regression test for not ignoring virtual columns.
query T
SELECT crdb_internal.generate_test_objects('{"seed":1234,"counts":[1],"table_templates":["system.statement_statistics"]}'::JSONB)->'generated_counts'
----
{"databases": 0, "schemas": 0, "tables": 1}

query T
SELECT table_name FROM [SHOW TABLES]
ORDER BY table_name
----
prepared_transactions
protected_ts_reco"rds
ro😪l|e_options
set                             tings
span_stats'_bucke ts
statement_diagnostics_requests
statement_statisti😍cs
sta😣tement_statistics
tenant_tasks
tran\\u9A2Esaction_statistics
us ers

# Again, the column names are randomized.
query TTT
SELECT quote_ident(table_name), quote_ident(column_name), data_type FROM "".information_schema.columns
WHERE table_catalog = 'newdb2' AND table_schema = 'public'
ORDER BY table_name, column_name
LIMIT 20
----
prepared_transactions     database         text
prepared_transactions     global_id        text
prepared_transactions     heuristic        text
prepared_transactions     prepared         timestamp with time zone
prepared_transactions     rowid            bigint
prepared_transactions     transaction_id_  uuid
prepared_transactions     transaction_key  bytea
prepared_transactions     "😠owne\gr"       text
"protected_ts_reco""rds"  "*meta"          bytea
"protected_ts_reco""rds"  id               uuid
"protected_ts_reco""rds"  "meta_typ%ve"    text
"protected_ts_reco""rds"  num_spans        bigint
"protected_ts_reco""rds"  rowid            bigint
"protected_ts_reco""rds"  spans            bytea
"protected_ts_reco""rds"  target           bytea
"protected_ts_reco""rds"  ts               numeric
"protected_ts_reco""rds"  "veri!fied"      boolean
"ro😪l|e_options"          option           text
"ro😪l|e_options"          rowid            bigint
"ro😪l|e_options"          "u\\xe6s̠er_id"  oid

subtest templates/different_templates_in_each_db

# When there are more templates than the requested number of tables
# per database, a different subset of templates is selected in each
# new database.

query T
SELECT crdb_internal.generate_test_objects('{"seed":123,"names":"dbt._","counts":[3,0,3],"table_templates":["system.*"]}'::JSONB)->'generated_counts'
----
{"databases": 3, "schemas": 3, "tables": 9}

query TTT
SELECT quote_ident(database_name), quote_ident(schema_name), quote_ident(name)
FROM "".crdb_internal.tables WHERE database_name ILIKE '%d%b%t%'
ORDER BY database_name, schema_name, name
----
"d%qbt_1"  public  "external_connect ions"
"d%qbt_1"  public  reports_meta
"d%qbt_1"  public  "sta""tement_bundle_chunks"
"d%qbt_2"  public  "job_progres s"
"d%qbt_2"  public  web_sessions
"d%qbt_2"  public  zones
dbt_3      public  "namespa ce"
dbt_3      public  "rolE_members"
dbt_3      public  span_stats_tenant_boundaries


statement ok
SET database = test

subtest show_config

# The following tests shows how the default config is applied.

# Default parameters.
# We erase the seed to make the output deterministic.
query T
SELECT crdb_internal.generate_test_objects('{"dry_run":true}'::JSONB)#-array['seed']
----
{"batch_size": 1000, "counts": [10], "dry_run": true, "generated_counts": {"databases": 0, "schemas": 0, "tables": 10}, "name_gen": {"capitals": 0.08, "diacritic_depth": 1, "diacritics": 0.08, "emote": 0.08, "escapes": 0.08, "fmt": 0.08, "noise": true, "punctuate": 0.08, "quote": 0.08, "space": 0.08, "suffix": true, "whitespace": 0.08}, "names": "_", "randomize_columns": true}

# Manual seed.
query T
SELECT crdb_internal.generate_test_objects('{"dry_run":true,"seed":123}'::JSONB)#-array['generated_counts']
----
{"batch_size": 1000, "counts": [10], "dry_run": true, "name_gen": {"capitals": 0.08, "diacritic_depth": 1, "diacritics": 0.08, "emote": 0.08, "escapes": 0.08, "fmt": 0.08, "noise": true, "punctuate": 0.08, "quote": 0.08, "space": 0.08, "suffix": true, "whitespace": 0.08}, "names": "_", "randomize_columns": true, "seed": 123}

# Noise disabled.
query T
SELECT crdb_internal.generate_test_objects('{"dry_run":true,"seed":123,"name_gen":{"noise":false}}'::JSONB)#-array['generated_counts']
----
{"batch_size": 1000, "counts": [10], "dry_run": true, "name_gen": {"noise": false, "suffix": true}, "names": "_", "randomize_columns": true, "seed": 123}

# Numbers disabled.
query T
SELECT crdb_internal.generate_test_objects('{"dry_run":true,"seed":123,"name_gen":{"suffix":false}}'::JSONB)#-array['generated_counts']
----
{"batch_size": 1000, "counts": [10], "dry_run": true, "name_gen": {"capitals": 0.08, "diacritic_depth": 1, "diacritics": 0.08, "emote": 0.08, "escapes": 0.08, "fmt": 0.08, "noise": true, "punctuate": 0.08, "quote": 0.08, "space": 0.08, "suffix": false, "whitespace": 0.08}, "names": "_", "randomize_columns": true, "seed": 123}

# Numbers and noise disabled.
query error name generation needs variability to generate objects
SELECT crdb_internal.generate_test_objects('{"dry_run":true,"seed":123,"name_gen":{"suffix":false,"noise":false}}'::JSONB)#-array['generated_counts']

# Numbers and noise disabled, but some extra variability.
query T
SELECT crdb_internal.generate_test_objects('{"dry_run":true,"seed":123,"name_gen":{"suffix":false,"noise":false,"quote":1}}'::JSONB)#-array['generated_counts']
----
{"batch_size": 1000, "counts": [10], "dry_run": true, "name_gen": {"noise": false, "quote": 1, "suffix": false}, "names": "_", "randomize_columns": true, "seed": 123}

# Zalgo mode enabled.
query T
SELECT crdb_internal.generate_test_objects('{"dry_run":true,"seed":123,"name_gen":{"noise":false,"zalgo":true}}'::JSONB)#-array['generated_counts']
----
{"batch_size": 1000, "counts": [10], "dry_run": true, "name_gen": {"diacritic_depth": 20, "diacritics": 1000, "noise": false, "suffix": true, "zalgo": true}, "names": "_", "randomize_columns": true, "seed": 123}

query T
SELECT crdb_internal.generate_test_objects('{"dry_run":true,"seed":123,"name_gen":{"noise":true,"zalgo":true}}'::JSONB)#-array['generated_counts']
----
{"batch_size": 1000, "counts": [10], "dry_run": true, "name_gen": {"capitals": 0.08, "diacritic_depth": 20, "diacritics": 1000, "emote": 0.08, "escapes": 0.08, "fmt": 0.08, "noise": true, "punctuate": 0.08, "quote": 0.08, "space": 0.08, "suffix": true, "whitespace": 0.08, "zalgo": true}, "names": "_", "randomize_columns": true, "seed": 123}

subtest zero_dbs

# If the requested number of databases is zero, then no objects are ever created.
query T
SELECT crdb_internal.generate_test_objects('foo.bar.baz', ARRAY[0,10,20])->'generated_counts'
----
{"databases": 0, "schemas": 0, "tables": 0}


subtest nonzero_dbs_zero_schemas

# If the requested number of schemas is zero, but the number of
# databases is non-zero, then dbs are created but no schemas.

query T
SELECT crdb_internal.generate_test_objects('{"names":"dba.bar.baz", "counts":[2,0,10], "name_gen":{"noise":false}}'::JSONB)->'generated_counts'
----
{"databases": 2, "schemas": 2, "tables": 0}

query I
SELECT count(*) FROM [SHOW TABLES FROM dba_1]
----
0

query I
SELECT count(*) FROM [SHOW TABLES FROM dba_2]
----
0

subtest nonzero_dbs_nonzero_schemas_zero_tables

# If the requested number of tables is zero, but the number of
# databases/schemas is non-zero, then schemas/dbs are created but no
# tables.

query T
SELECT crdb_internal.generate_test_objects('{"names":"dbb.bar.baz", "counts":[1,1,0], "name_gen":{"noise":false}}'::JSONB)->'generated_counts'
----
{"databases": 1, "schemas": 2, "tables": 0}

query T rowsort
SELECT schema_name FROM [SHOW SCHEMAS FROM dbb_1]
----
bar_1
public
crdb_internal
information_schema
pg_catalog
pg_extension

query I
SELECT count(*) FROM [SHOW TABLES FROM dbb_1.bar_1]
----
0


subtest implicit_db

# Without a db qualification, the creation targets the current db.

statement ok
CREATE SCHEMA myschema

query T
SELECT crdb_internal.generate_test_objects('{"names":"myschema.foo", "counts":[2], "name_gen":{"noise":false}}'::JSONB)->'generated_counts'
----
{"databases": 0, "schemas": 0, "tables": 2}

query TT rowsort
SELECT schema_name, table_name FROM [SHOW TABLES FROM test.myschema]
----
myschema  foo_1
myschema  foo_2

subtest implicit_db/zero_schemas

query T
SELECT crdb_internal.generate_test_objects('myschema.foo', ARRAY[0, 2])->'generated_counts'
----
{"databases": 0, "schemas": 0, "tables": 0}

subtest implicit_db/zero_tables

query T
SELECT crdb_internal.generate_test_objects('myschema.foo', ARRAY[0])->'generated_counts'
----
{"databases": 0, "schemas": 0, "tables": 0}

subtest implicit_db/nonzero_schemas_zero_tables

query T
SELECT crdb_internal.generate_test_objects('{"names":"scgen.foo", "counts":[2,0], "name_gen":{"noise":false}}'::JSONB)->'generated_counts'
----
{"databases": 0, "schemas": 2, "tables": 0}

query T rowsort
SELECT schema_name FROM [SHOW SCHEMAS] WHERE schema_name LIKE 'scgen%'
----
scgen_1
scgen_2

query I
SELECT count(*) FROM [SHOW TABLES FROM scgen_1]
----
0

query I
SELECT count(*) FROM [SHOW TABLES FROM scgen_2]
----
0

subtest implicit_schema

# Without a schema qualification, the creation targets the first valid
# schema in the search path.

statement ok
CREATE SCHEMA otherschema;
SET search_path=invalidschema,otherschema,public

query T
SELECT crdb_internal.generate_test_objects('{"names":"foo", "counts":[2], "name_gen":{"noise":false}}'::JSONB)->'generated_counts'
----
{"databases": 0, "schemas": 0, "tables": 2}

query TT rowsort
SELECT schema_name, table_name FROM [SHOW TABLES FROM test.otherschema]
----
otherschema  foo_1
otherschema  foo_2

statement ok
RESET search_path

subtest gen_dbs_and_public_tables_but_no_schema

# This test checks that if db creation is requested (with 3 sizes),
# and there are just 2 name components, the prefix is used as db name
# and the tables get created in schema 'public'.

query T
SELECT crdb_internal.generate_test_objects('{"names":"dbfoo.baz", "counts":[1,0,2], "name_gen":{"noise":false}}'::JSONB)->'generated_counts'
----
{"databases": 1, "schemas": 1, "tables": 2}

query TT rowsort
SELECT schema_name, table_name FROM [SHOW TABLES FROM dbfoo_1]
----
public  baz_1
public  baz_2

subtest missing_pattern

query error missing database name pattern
SELECT crdb_internal.generate_test_objects('foo', ARRAY[10,10,10])

query error missing schema name pattern
SELECT crdb_internal.generate_test_objects('foo', ARRAY[10,10])

subtest target_check_on_zero_count

query error database "nonexistent" does not exist
SELECT crdb_internal.generate_test_objects('nonexistent.foo.bar', 0)

query error unknown schema "nonexistent"
SELECT crdb_internal.generate_test_objects('nonexistent.bar', 0)

query error unknown schema "nonexistent"
SELECT crdb_internal.generate_test_objects('test.nonexistent.bar', 0)

subtest inv_name

query error unacceptable schema name "pg_foo"
SELECT crdb_internal.generate_test_objects('pg_foo.bar', ARRAY[10,10])

query error unacceptable schema name "pg_foo"
SELECT crdb_internal.generate_test_objects('test.pg_foo.bar', ARRAY[10,10])

subtest inv_privs

query error does not have CREATE privilege on database system
SELECT crdb_internal.generate_test_objects('system.public.foo', 10)

query error pg_catalog is a virtual schema and cannot be modified
SELECT crdb_internal.generate_test_objects('pg_catalog.foo', 10)

statement ok
SET search_path=pg_catalog,public

query error pg_catalog is a virtual schema and cannot be modified
SELECT crdb_internal.generate_test_objects('foo', 10)

statement ok
RESET search_path

statement ok
CREATE DATABASE rootonly;
CREATE TABLE rootonly.foo(x int);
CREATE TABLE rootonly.bar(x int)

user testuser

query error  must have admin role to generate objects
SELECT crdb_internal.generate_test_objects('foo._._', ARRAY[1,0,0])

user root

statement ok
SET CLUSTER SETTING sql.schema.test_object_generator.non_admin.enabled = true

user testuser

query error permission denied to create database
SELECT crdb_internal.generate_test_objects('foo._._', ARRAY[1,0,0])

query error user has no privileges on foo
SELECT crdb_internal.generate_test_objects('{"table_templates":["rootonly.foo"]}'::jsonb)

query error template name expansion did not find any usable tables
SELECT crdb_internal.generate_test_objects('{"table_templates":["rootonly.*"]}'::jsonb)

user root

statement ok
ALTER USER testuser CREATEDB

user testuser

statement ok
SELECT crdb_internal.generate_test_objects('custom._._', ARRAY[1,0,0])

subtest inv_privs/many_descriptors

query error only admin users can generate more than 10000 descriptors at a time
SELECT crdb_internal.generate_test_objects('a.b.c', ARRAY[10000, 1,1])

query error only admin users can generate more than 10000 descriptors at a time
SELECT crdb_internal.generate_test_objects('a.b.c', ARRAY[1,10000,1])

query error only admin users can generate more than 10000 descriptors at a time
SELECT crdb_internal.generate_test_objects('a.b.c', ARRAY[1,1,10000])

user root

statement ok
RESET CLUSTER SETTING sql.schema.test_object_generator.non_admin.enabled

subtest disable_feature

statement ok
SET CLUSTER SETTING sql.schema.test_object_generator.enabled = false

query error generation disabled by configuration
SELECT crdb_internal.generate_test_objects('foo', 10)

statement ok
RESET CLUSTER SETTING sql.schema.test_object_generator.enabled

subtest max_counts

query error invalid count
SELECT crdb_internal.generate_test_objects('foo', 100000000)

query error invalid count
SELECT crdb_internal.generate_test_objects('{"names":"a.b.c","counts":[100000000,0,0]}'::jsonb)

query error invalid count
SELECT crdb_internal.generate_test_objects('{"names":"a.b.c","counts":[0,100000000,0]}'::jsonb)

query error invalid count
SELECT crdb_internal.generate_test_objects('{"names":"a.b.c","counts":[0,0,100000000]}'::jsonb)

query error too many objects generated
SELECT crdb_internal.generate_test_objects('{"names":"a.b.c","counts":[10000000,10000000,10000000]}'::jsonb)

subtest invalid_count

query error invalid count
SELECT crdb_internal.generate_test_objects('foo', ARRAY[]::INT8[])

query error invalid count
SELECT crdb_internal.generate_test_objects('foo', ARRAY[1, 2, 3, 4, 5]::INT8[])

subtest temp_schema

# Force create the temp schema.
statement ok
SET experimental_enable_temp_tables = 'on';
CREATE TEMP TABLE test(x INT)

# Create some tables in it.
query T
SELECT crdb_internal.generate_test_objects('{"names":"pg_temp.foo", "counts":[3], "name_gen":{"noise":false}}'::JSONB)->'generated_counts'
----
{"databases": 0, "schemas": 0, "tables": 3}

# List them.
query T rowsort
SELECT table_name FROM [SHOW TABLES FROM pg_temp]
----
test
foo_1
foo_2
foo_3
