diff --git a/docs/generated/sql/functions.md b/docs/generated/sql/functions.md index ca4b99c6b1a9..c53a0b9981da 100644 --- a/docs/generated/sql/functions.md +++ b/docs/generated/sql/functions.md @@ -2371,9 +2371,13 @@ SQL statement omitting multi-region related zone configuration fields. If the CONFIGURE ZONE statement can be inferred by the database’s or table’s zone configuration this will return NULL.

-crdb_internal.show_create_all_tables(dbName: string) → string

Returns a flat log of CREATE table statements followed by -ALTER table statements that add table constraints. The flat log can be used -to recreate a database.’

+crdb_internal.show_create_all_tables(database_name: string) → string

Returns rows of CREATE table statements followed by +ALTER table statements that add table constraints. The rows are ordered +by dependencies. All foreign keys are added after the creation of the table +in the alter statements. +It is not recommended to perform this operation on a database with many +tables. +The output can be used to recreate a database.’

decode(text: string, format: string) → bytes

Decodes data using format (hex / escape / base64).

diff --git a/pkg/sql/logictest/testdata/logic_test/show_create_all_tables b/pkg/sql/logictest/testdata/logic_test/show_create_all_tables index a5fe4e7ff8f2..007ac9b7fe4d 100644 --- a/pkg/sql/logictest/testdata/logic_test/show_create_all_tables +++ b/pkg/sql/logictest/testdata/logic_test/show_create_all_tables @@ -8,7 +8,6 @@ query T colnames SHOW CREATE ALL TABLES ---- create_statement -· statement ok CREATE TABLE d.parent ( @@ -47,35 +46,32 @@ SHOW CREATE ALL TABLES ---- create_statement CREATE TABLE public.parent ( - x INT8 NULL, - y INT8 NULL, - z INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - UNIQUE INDEX parent_x_y_z_key (x ASC, y ASC, z ASC), - UNIQUE INDEX parent_x_key (x ASC), - FAMILY f1 (x, y, z, rowid) + x INT8 NULL, + y INT8 NULL, + z INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + UNIQUE INDEX parent_x_y_z_key (x ASC, y ASC, z ASC), + UNIQUE INDEX parent_x_key (x ASC), + FAMILY f1 (x, y, z, rowid) ); CREATE TABLE public.full_test ( - x INT8 NULL, - y INT8 NULL, - z INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - UNIQUE INDEX full_test_x_key (x ASC), - FAMILY f1 (x, y, z, rowid) + x INT8 NULL, + y INT8 NULL, + z INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + UNIQUE INDEX full_test_x_key (x ASC), + FAMILY f1 (x, y, z, rowid) ); -CREATE SEQUENCE public.s MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1; CREATE VIEW public.vx ("?column?") AS SELECT 1; +CREATE SEQUENCE public.s MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1; ALTER TABLE public.full_test ADD CONSTRAINT fk_x_ref_parent FOREIGN KEY (x, y, z) REFERENCES public.parent(x, y, z) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE public.full_test ADD CONSTRAINT test_fk FOREIGN KEY (x) REFERENCES public.parent(x) ON DELETE CASCADE; -- Validate foreign key constraints. These can fail if there was unvalidated data during the SHOW CREATE ALL TABLES ALTER TABLE public.full_test VALIDATE CONSTRAINT fk_x_ref_parent; ALTER TABLE public.full_test VALIDATE CONSTRAINT test_fk; - - - # testuser does not have CONNECT on database d and cannot see any tables. user testuser @@ -83,7 +79,6 @@ query T colnames SHOW CREATE ALL TABLES ---- create_statement -· user root @@ -99,35 +94,32 @@ SHOW CREATE ALL TABLES ---- create_statement CREATE TABLE public.parent ( - x INT8 NULL, - y INT8 NULL, - z INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - UNIQUE INDEX parent_x_y_z_key (x ASC, y ASC, z ASC), - UNIQUE INDEX parent_x_key (x ASC), - FAMILY f1 (x, y, z, rowid) + x INT8 NULL, + y INT8 NULL, + z INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + UNIQUE INDEX parent_x_y_z_key (x ASC, y ASC, z ASC), + UNIQUE INDEX parent_x_key (x ASC), + FAMILY f1 (x, y, z, rowid) ); CREATE TABLE public.full_test ( - x INT8 NULL, - y INT8 NULL, - z INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - UNIQUE INDEX full_test_x_key (x ASC), - FAMILY f1 (x, y, z, rowid) + x INT8 NULL, + y INT8 NULL, + z INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + UNIQUE INDEX full_test_x_key (x ASC), + FAMILY f1 (x, y, z, rowid) ); -CREATE SEQUENCE public.s MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1; CREATE VIEW public.vx ("?column?") AS SELECT 1; +CREATE SEQUENCE public.s MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1; ALTER TABLE public.full_test ADD CONSTRAINT fk_x_ref_parent FOREIGN KEY (x, y, z) REFERENCES public.parent(x, y, z) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE public.full_test ADD CONSTRAINT test_fk FOREIGN KEY (x) REFERENCES public.parent(x) ON DELETE CASCADE; -- Validate foreign key constraints. These can fail if there was unvalidated data during the SHOW CREATE ALL TABLES ALTER TABLE public.full_test VALIDATE CONSTRAINT fk_x_ref_parent; ALTER TABLE public.full_test VALIDATE CONSTRAINT test_fk; - - - user root # Make sure temp tables don't show up in crdb_internal.show_create_all_tables. @@ -147,7 +139,6 @@ query T colnames SHOW CREATE ALL TABLES ---- create_statement -· # Test that a database with foreign keys has the right order. statement ok @@ -170,57 +161,57 @@ CREATE TABLE c (i int REFERENCES d); CREATE SEQUENCE s; CREATE TABLE s_tbl (id INT PRIMARY KEY DEFAULT nextval('s'), v INT, FAMILY f1 (id, v)); -# Table order should be B, A, E, G, F, D, C, sequence s, s_tbl. +# Table order should be B, A, G, F, E, D, C, sequence s, s_tbl. query T colnames SHOW CREATE ALL TABLES ---- create_statement CREATE TABLE public.b ( - i INT8 NOT NULL, - CONSTRAINT "primary" PRIMARY KEY (i ASC), - FAMILY "primary" (i) + i INT8 NOT NULL, + CONSTRAINT "primary" PRIMARY KEY (i ASC), + FAMILY "primary" (i) ); CREATE TABLE public.a ( - i INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - FAMILY "primary" (i, rowid) -); -CREATE TABLE public.e ( - i INT8 NOT NULL, - CONSTRAINT "primary" PRIMARY KEY (i ASC), - FAMILY "primary" (i) + i INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + FAMILY "primary" (i, rowid) ); CREATE TABLE public.g ( - i INT8 NOT NULL, - CONSTRAINT "primary" PRIMARY KEY (i ASC), - FAMILY "primary" (i) + i INT8 NOT NULL, + CONSTRAINT "primary" PRIMARY KEY (i ASC), + FAMILY "primary" (i) ); CREATE TABLE public.f ( - i INT8 NOT NULL, - g INT8 NULL, - CONSTRAINT "primary" PRIMARY KEY (i ASC), - FAMILY f1 (i, g) + i INT8 NOT NULL, + g INT8 NULL, + CONSTRAINT "primary" PRIMARY KEY (i ASC), + FAMILY f1 (i, g) +); +CREATE TABLE public.e ( + i INT8 NOT NULL, + CONSTRAINT "primary" PRIMARY KEY (i ASC), + FAMILY "primary" (i) ); CREATE TABLE public.d ( - i INT8 NOT NULL, - e INT8 NULL, - f INT8 NULL, - CONSTRAINT "primary" PRIMARY KEY (i ASC), - FAMILY f1 (i, e, f) + i INT8 NOT NULL, + e INT8 NULL, + f INT8 NULL, + CONSTRAINT "primary" PRIMARY KEY (i ASC), + FAMILY f1 (i, e, f) ); CREATE TABLE public.c ( - i INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - FAMILY "primary" (i, rowid) + i INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + FAMILY "primary" (i, rowid) ); CREATE SEQUENCE public.s MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1; CREATE TABLE public.s_tbl ( - id INT8 NOT NULL DEFAULT nextval('test_fk_order.public.s':::STRING::REGCLASS), - v INT8 NULL, - CONSTRAINT "primary" PRIMARY KEY (id ASC), - FAMILY f1 (id, v) + id INT8 NOT NULL DEFAULT nextval('test_fk_order.public.s':::STRING::REGCLASS), + v INT8 NULL, + CONSTRAINT "primary" PRIMARY KEY (id ASC), + FAMILY f1 (id, v) ); ALTER TABLE public.a ADD CONSTRAINT fk_i_ref_b FOREIGN KEY (i) REFERENCES public.b(i); ALTER TABLE public.f ADD CONSTRAINT fk_g_ref_g FOREIGN KEY (g) REFERENCES public.g(i); @@ -234,9 +225,6 @@ ALTER TABLE public.d VALIDATE CONSTRAINT fk_e_ref_e; ALTER TABLE public.d VALIDATE CONSTRAINT fk_f_ref_f; ALTER TABLE public.c VALIDATE CONSTRAINT fk_i_ref_d; - - - # Test that a cycle between two tables is handled correctly. statement ok CREATE DATABASE test_cycle; @@ -278,9 +266,6 @@ ALTER TABLE public.loop_a ADD CONSTRAINT b_id_delete_constraint FOREIGN KEY (b_i ALTER TABLE public.loop_b VALIDATE CONSTRAINT fk_a_id_ref_loop_a; ALTER TABLE public.loop_a VALIDATE CONSTRAINT b_id_delete_constraint; - - - # Test that a primary key with a non-default name works. statement ok CREATE DATABASE test_primary_key; @@ -295,13 +280,11 @@ SHOW CREATE ALL TABLES ---- create_statement CREATE TABLE public.t ( - i INT8 NOT NULL, - CONSTRAINT pk_name PRIMARY KEY (i ASC), - FAMILY "primary" (i) + i INT8 NOT NULL, + CONSTRAINT pk_name PRIMARY KEY (i ASC), + FAMILY "primary" (i) ); - - # Test that computed columns are shown correctly. statement ok CREATE DATABASE test_computed_column; @@ -317,15 +300,12 @@ SHOW CREATE ALL TABLES ---- create_statement CREATE TABLE public.t ( - a INT8 NOT NULL, - b INT8 NULL AS (a + 1:::INT8) STORED, - CONSTRAINT "primary" PRIMARY KEY (a ASC), - FAMILY f1 (a, b) + a INT8 NOT NULL, + b INT8 NULL AS (a + 1:::INT8) STORED, + CONSTRAINT "primary" PRIMARY KEY (a ASC), + FAMILY f1 (a, b) ); - - - # Test showing a table with a semicolon in the table, index, and # column names properly escapes. statement ok @@ -339,16 +319,13 @@ SHOW CREATE ALL TABLES ---- create_statement CREATE TABLE public.";" ( - ";" INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - INDEX ";_;_idx" (";" ASC), - FAMILY "primary" (";", rowid) + ";" INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + INDEX ";_;_idx" (";" ASC), + FAMILY "primary" (";", rowid) ); - - - # Ensure quotes in comments are properly escaped, also that the object names # are properly escaped in the output of the COMMENT statements. statement ok @@ -372,8 +349,6 @@ COMMENT ON TABLE public."t t" IS e'has \' quotes'; COMMENT ON COLUMN public."t t"."x'" IS e'i \' just \' love \' quotes'; COMMENT ON INDEX public."t t"@primary IS e'has \' more \' quotes'; - - # Ensure schemas are shown correctly. statement ok CREATE DATABASE test_schema; @@ -388,21 +363,18 @@ SHOW CREATE ALL TABLES ---- create_statement CREATE TABLE sc1.t ( - x INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - FAMILY "primary" (x, rowid) + x INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + FAMILY "primary" (x, rowid) ); CREATE TABLE sc2.t ( - x INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - FAMILY "primary" (x, rowid) + x INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + FAMILY "primary" (x, rowid) ); - - - # Ensure sequences are shown correctly. statement ok CREATE DATABASE test_sequence; diff --git a/pkg/sql/logictest/testdata/logic_test/show_create_all_tables_builtin b/pkg/sql/logictest/testdata/logic_test/show_create_all_tables_builtin index b36f033f7486..092d55b6e5e7 100644 --- a/pkg/sql/logictest/testdata/logic_test/show_create_all_tables_builtin +++ b/pkg/sql/logictest/testdata/logic_test/show_create_all_tables_builtin @@ -1,7 +1,5 @@ -query T +statement error pq: crdb_internal.show_create_all_tables: database "d" does not exist SELECT crdb_internal.show_create_all_tables('d') ----- -· statement ok CREATE DATABASE d @@ -9,7 +7,6 @@ CREATE DATABASE d query T SELECT crdb_internal.show_create_all_tables('d') ---- -· statement ok CREATE TABLE d.parent ( @@ -47,40 +44,38 @@ query T SELECT crdb_internal.show_create_all_tables('d') ---- CREATE TABLE public.parent ( - x INT8 NULL, - y INT8 NULL, - z INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - UNIQUE INDEX parent_x_y_z_key (x ASC, y ASC, z ASC), - UNIQUE INDEX parent_x_key (x ASC), - FAMILY f1 (x, y, z, rowid) + x INT8 NULL, + y INT8 NULL, + z INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + UNIQUE INDEX parent_x_y_z_key (x ASC, y ASC, z ASC), + UNIQUE INDEX parent_x_key (x ASC), + FAMILY f1 (x, y, z, rowid) ); CREATE TABLE public.full_test ( - x INT8 NULL, - y INT8 NULL, - z INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - UNIQUE INDEX full_test_x_key (x ASC), - FAMILY f1 (x, y, z, rowid) + x INT8 NULL, + y INT8 NULL, + z INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + UNIQUE INDEX full_test_x_key (x ASC), + FAMILY f1 (x, y, z, rowid) ); -CREATE SEQUENCE public.s MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1; CREATE VIEW public.vx ("?column?") AS SELECT 1; +CREATE SEQUENCE public.s MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1; ALTER TABLE public.full_test ADD CONSTRAINT fk_x_ref_parent FOREIGN KEY (x, y, z) REFERENCES public.parent(x, y, z) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE public.full_test ADD CONSTRAINT test_fk FOREIGN KEY (x) REFERENCES public.parent(x) ON DELETE CASCADE; -- Validate foreign key constraints. These can fail if there was unvalidated data during the SHOW CREATE ALL TABLES ALTER TABLE public.full_test VALIDATE CONSTRAINT fk_x_ref_parent; ALTER TABLE public.full_test VALIDATE CONSTRAINT test_fk; - # testuser does not have CONNECT on database d and cannot see any tables. user testuser query T SELECT crdb_internal.show_create_all_tables('d') ---- -· user root @@ -95,33 +90,32 @@ query T SELECT crdb_internal.show_create_all_tables('d') ---- CREATE TABLE public.parent ( - x INT8 NULL, - y INT8 NULL, - z INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - UNIQUE INDEX parent_x_y_z_key (x ASC, y ASC, z ASC), - UNIQUE INDEX parent_x_key (x ASC), - FAMILY f1 (x, y, z, rowid) + x INT8 NULL, + y INT8 NULL, + z INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + UNIQUE INDEX parent_x_y_z_key (x ASC, y ASC, z ASC), + UNIQUE INDEX parent_x_key (x ASC), + FAMILY f1 (x, y, z, rowid) ); CREATE TABLE public.full_test ( - x INT8 NULL, - y INT8 NULL, - z INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - UNIQUE INDEX full_test_x_key (x ASC), - FAMILY f1 (x, y, z, rowid) + x INT8 NULL, + y INT8 NULL, + z INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + UNIQUE INDEX full_test_x_key (x ASC), + FAMILY f1 (x, y, z, rowid) ); -CREATE SEQUENCE public.s MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1; CREATE VIEW public.vx ("?column?") AS SELECT 1; +CREATE SEQUENCE public.s MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1; ALTER TABLE public.full_test ADD CONSTRAINT fk_x_ref_parent FOREIGN KEY (x, y, z) REFERENCES public.parent(x, y, z) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE public.full_test ADD CONSTRAINT test_fk FOREIGN KEY (x) REFERENCES public.parent(x) ON DELETE CASCADE; -- Validate foreign key constraints. These can fail if there was unvalidated data during the SHOW CREATE ALL TABLES ALTER TABLE public.full_test VALIDATE CONSTRAINT fk_x_ref_parent; ALTER TABLE public.full_test VALIDATE CONSTRAINT test_fk; - user root # Make sure temp tables don't show up in crdb_internal.show_create_all_tables. @@ -140,7 +134,6 @@ CREATE TEMPORARY TABLE t() query T SELECT crdb_internal.show_create_all_tables('temp_test') ---- -· # Test that a database with foreign keys has the right order. statement ok @@ -168,51 +161,51 @@ query T SELECT crdb_internal.show_create_all_tables('test_fk_order') ---- CREATE TABLE public.b ( - i INT8 NOT NULL, - CONSTRAINT "primary" PRIMARY KEY (i ASC), - FAMILY "primary" (i) + i INT8 NOT NULL, + CONSTRAINT "primary" PRIMARY KEY (i ASC), + FAMILY "primary" (i) ); CREATE TABLE public.a ( - i INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - FAMILY "primary" (i, rowid) -); -CREATE TABLE public.e ( - i INT8 NOT NULL, - CONSTRAINT "primary" PRIMARY KEY (i ASC), - FAMILY "primary" (i) + i INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + FAMILY "primary" (i, rowid) ); CREATE TABLE public.g ( - i INT8 NOT NULL, - CONSTRAINT "primary" PRIMARY KEY (i ASC), - FAMILY "primary" (i) + i INT8 NOT NULL, + CONSTRAINT "primary" PRIMARY KEY (i ASC), + FAMILY "primary" (i) ); CREATE TABLE public.f ( - i INT8 NOT NULL, - g INT8 NULL, - CONSTRAINT "primary" PRIMARY KEY (i ASC), - FAMILY f1 (i, g) + i INT8 NOT NULL, + g INT8 NULL, + CONSTRAINT "primary" PRIMARY KEY (i ASC), + FAMILY f1 (i, g) +); +CREATE TABLE public.e ( + i INT8 NOT NULL, + CONSTRAINT "primary" PRIMARY KEY (i ASC), + FAMILY "primary" (i) ); CREATE TABLE public.d ( - i INT8 NOT NULL, - e INT8 NULL, - f INT8 NULL, - CONSTRAINT "primary" PRIMARY KEY (i ASC), - FAMILY f1 (i, e, f) + i INT8 NOT NULL, + e INT8 NULL, + f INT8 NULL, + CONSTRAINT "primary" PRIMARY KEY (i ASC), + FAMILY f1 (i, e, f) ); CREATE TABLE public.c ( - i INT8 NULL, - rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), - CONSTRAINT "primary" PRIMARY KEY (rowid ASC), - FAMILY "primary" (i, rowid) + i INT8 NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT "primary" PRIMARY KEY (rowid ASC), + FAMILY "primary" (i, rowid) ); CREATE SEQUENCE public.s MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1; CREATE TABLE public.s_tbl ( - id INT8 NOT NULL DEFAULT nextval('test_fk_order.public.s':::STRING::REGCLASS), - v INT8 NULL, - CONSTRAINT "primary" PRIMARY KEY (id ASC), - FAMILY f1 (id, v) + id INT8 NOT NULL DEFAULT nextval('test_fk_order.public.s':::STRING::REGCLASS), + v INT8 NULL, + CONSTRAINT "primary" PRIMARY KEY (id ASC), + FAMILY f1 (id, v) ); ALTER TABLE public.a ADD CONSTRAINT fk_i_ref_b FOREIGN KEY (i) REFERENCES public.b(i); ALTER TABLE public.f ADD CONSTRAINT fk_g_ref_g FOREIGN KEY (g) REFERENCES public.g(i); @@ -226,7 +219,6 @@ ALTER TABLE public.d VALIDATE CONSTRAINT fk_e_ref_e; ALTER TABLE public.d VALIDATE CONSTRAINT fk_f_ref_f; ALTER TABLE public.c VALIDATE CONSTRAINT fk_i_ref_d; - # Test that a cycle between two tables is handled correctly. statement ok CREATE DATABASE test_cycle; @@ -267,7 +259,6 @@ ALTER TABLE public.loop_a ADD CONSTRAINT b_id_delete_constraint FOREIGN KEY (b_i ALTER TABLE public.loop_b VALIDATE CONSTRAINT fk_a_id_ref_loop_a; ALTER TABLE public.loop_a VALIDATE CONSTRAINT b_id_delete_constraint; - # Test that a primary key with a non-default name works. statement ok CREATE DATABASE test_primary_key; @@ -304,7 +295,6 @@ CREATE TABLE public.t ( FAMILY f1 (a, b) ); - # Test showing a table with a semicolon in the table, index, and # column names properly escapes. statement ok @@ -323,7 +313,6 @@ CREATE TABLE public.";" ( FAMILY "primary" (";", rowid) ); - # Ensure quotes in comments are properly escaped, also that the object names # are properly escaped in the output of the COMMENT statements. statement ok @@ -370,7 +359,6 @@ CREATE TABLE sc2.t ( FAMILY "primary" (x, rowid) ); - # Ensure sequences are shown correctly. statement ok CREATE DATABASE test_sequence; diff --git a/pkg/sql/opt/norm/project_set_funcs.go b/pkg/sql/opt/norm/project_set_funcs.go index 733185423a5c..4dec3e777083 100644 --- a/pkg/sql/opt/norm/project_set_funcs.go +++ b/pkg/sql/opt/norm/project_set_funcs.go @@ -141,7 +141,7 @@ func (c *CustomFuncs) ConstructValuesFromZips(zip memo.ZipExpr) memo.RelExpr { val := c.f.ConstructConstVal(vals[0], vals[0].ResolvedType()) addValToOutRows(val, j, i) } - generator.Close() + generator.Close(c.f.evalCtx.Context) default: panic(errors.AssertionFailedf("invalid parameter type")) diff --git a/pkg/sql/rowexec/project_set.go b/pkg/sql/rowexec/project_set.go index 293d9181ae2e..53e61eef3f1d 100644 --- a/pkg/sql/rowexec/project_set.go +++ b/pkg/sql/rowexec/project_set.go @@ -92,7 +92,13 @@ func newProjectSetProcessor( processorID, output, nil, /* memMonitor */ - execinfra.ProcStateOpts{InputsToDrain: []execinfra.RowSource{ps.input}}, + execinfra.ProcStateOpts{ + InputsToDrain: []execinfra.RowSource{ps.input}, + TrailingMetaCallback: func() []execinfrapb.ProducerMetadata { + ps.close() + return nil + }, + }, ); err != nil { return nil, err } @@ -278,6 +284,21 @@ func (ps *projectSetProcessor) toEncDatum(d tree.Datum, colIdx int) rowenc.EncDa return rowenc.DatumToEncDatum(ctyp, d) } +func (ps *projectSetProcessor) close() { + if ps.InternalClose() { + for _, gen := range ps.gens { + if gen != nil { + gen.Close(ps.Ctx) + } + } + } +} + +// ConsumerClosed is part of the RowSource interface. +func (ps *projectSetProcessor) ConsumerClosed() { + ps.close() +} + // ChildCount is part of the execinfra.OpNode interface. func (ps *projectSetProcessor) ChildCount(verbose bool) int { if _, ok := ps.input.(execinfra.OpNode); ok { diff --git a/pkg/sql/sem/builtins/builtins.go b/pkg/sql/sem/builtins/builtins.go index e9007aaafa8e..97f1b2e0058b 100644 --- a/pkg/sql/sem/builtins/builtins.go +++ b/pkg/sql/sem/builtins/builtins.go @@ -5052,21 +5052,6 @@ table's zone configuration this will return NULL.`, tree.VolatilityStable, ), ), - - "crdb_internal.show_create_all_tables": makeBuiltin( - tree.FunctionProperties{}, - tree.Overload{ - Types: tree.ArgTypes{ - {"dbName", types.String}, - }, - ReturnType: tree.FixedReturnType(types.String), - Fn: showCreateAllTablesBuiltin, - Info: `Returns a flat log of CREATE table statements followed by -ALTER table statements that add table constraints. The flat log can be used -to recreate a database.'`, - Volatility: tree.VolatilityStable, - }, - ), } var lengthImpls = func(incBitOverload bool) builtinDefinition { diff --git a/pkg/sql/sem/builtins/generator_builtins.go b/pkg/sql/sem/builtins/generator_builtins.go index c3bfd379abdc..79c6fd6023b7 100644 --- a/pkg/sql/sem/builtins/generator_builtins.go +++ b/pkg/sql/sem/builtins/generator_builtins.go @@ -32,6 +32,8 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/duration" "github.com/cockroachdb/cockroach/pkg/util/errorutil" "github.com/cockroachdb/cockroach/pkg/util/json" + "github.com/cockroachdb/cockroach/pkg/util/mon" + "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/cockroach/pkg/util/tracing" "github.com/cockroachdb/errors" pbtypes "github.com/gogo/protobuf/types" @@ -84,7 +86,7 @@ type aclexplodeGenerator struct{} func (aclexplodeGenerator) ResolvedType() *types.T { return aclexplodeGeneratorType } func (aclexplodeGenerator) Start(_ context.Context, _ *kv.Txn) error { return nil } -func (aclexplodeGenerator) Close() {} +func (aclexplodeGenerator) Close(_ context.Context) {} func (aclexplodeGenerator) Next(_ context.Context) (bool, error) { return false, nil } func (aclexplodeGenerator) Values() (tree.Datums, error) { return nil, nil } @@ -348,7 +350,6 @@ var generators = map[string]builtinDefinition{ tree.VolatilityVolatile, ), ), - "crdb_internal.payloads_for_trace": makeBuiltin( tree.FunctionProperties{ Class: tree.GeneratorClass, @@ -364,6 +365,27 @@ var generators = map[string]builtinDefinition{ tree.VolatilityVolatile, ), ), + "crdb_internal.show_create_all_tables": makeBuiltin( + tree.FunctionProperties{ + Class: tree.GeneratorClass, + }, + makeGeneratorOverload( + tree.ArgTypes{ + {"database_name", types.String}, + }, + showCreateAllTablesGeneratorType, + makeShowCreateAllTablesGenerator, + `Returns rows of CREATE table statements followed by +ALTER table statements that add table constraints. The rows are ordered +by dependencies. All foreign keys are added after the creation of the table +in the alter statements. +It is not recommended to perform this operation on a database with many +tables. +The output can be used to recreate a database.' +`, + tree.VolatilityVolatile, + ), + ), } func makeGeneratorOverload( @@ -420,7 +442,7 @@ func makeRegexpSplitToTableGeneratorFactory(hasFlags bool) tree.GeneratorFactory func (*regexpSplitToTableGenerator) ResolvedType() *types.T { return types.String } // Close implements the tree.ValueGenerator interface. -func (*regexpSplitToTableGenerator) Close() {} +func (*regexpSplitToTableGenerator) Close(_ context.Context) {} // Start implements the tree.ValueGenerator interface. func (g *regexpSplitToTableGenerator) Start(_ context.Context, _ *kv.Txn) error { @@ -457,7 +479,7 @@ func makeKeywordsGenerator(_ *tree.EvalContext, _ tree.Datums) (tree.ValueGenera func (*keywordsValueGenerator) ResolvedType() *types.T { return keywordsValueGeneratorType } // Close implements the tree.ValueGenerator interface. -func (*keywordsValueGenerator) Close() {} +func (*keywordsValueGenerator) Close(_ context.Context) {} // Start implements the tree.ValueGenerator interface. func (k *keywordsValueGenerator) Start(_ context.Context, _ *kv.Txn) error { @@ -609,7 +631,7 @@ func (s *seriesValueGenerator) Start(_ context.Context, _ *kv.Txn) error { } // Close implements the tree.ValueGenerator interface. -func (s *seriesValueGenerator) Close() {} +func (s *seriesValueGenerator) Close(_ context.Context) {} // Next implements the tree.ValueGenerator interface. func (s *seriesValueGenerator) Next(_ context.Context) (bool, error) { @@ -660,7 +682,7 @@ func (s *multipleArrayValueGenerator) Start(_ context.Context, _ *kv.Txn) error } // Close implements the tree.ValueGenerator interface. -func (s *multipleArrayValueGenerator) Close() {} +func (s *multipleArrayValueGenerator) Close(_ context.Context) {} // Next implements the tree.ValueGenerator interface. func (s *multipleArrayValueGenerator) Next(_ context.Context) (bool, error) { @@ -709,7 +731,7 @@ func (s *arrayValueGenerator) Start(_ context.Context, _ *kv.Txn) error { } // Close implements the tree.ValueGenerator interface. -func (s *arrayValueGenerator) Close() {} +func (s *arrayValueGenerator) Close(_ context.Context) {} // Next implements the tree.ValueGenerator interface. func (s *arrayValueGenerator) Next(_ context.Context) (bool, error) { @@ -758,7 +780,7 @@ func (s *expandArrayValueGenerator) Start(_ context.Context, _ *kv.Txn) error { } // Close implements the tree.ValueGenerator interface. -func (s *expandArrayValueGenerator) Close() {} +func (s *expandArrayValueGenerator) Close(_ context.Context) {} // Next implements the tree.ValueGenerator interface. func (s *expandArrayValueGenerator) Next(_ context.Context) (bool, error) { @@ -831,7 +853,7 @@ func (s *subscriptsValueGenerator) Start(_ context.Context, _ *kv.Txn) error { } // Close implements the tree.ValueGenerator interface. -func (s *subscriptsValueGenerator) Close() {} +func (s *subscriptsValueGenerator) Close(_ context.Context) {} // Next implements the tree.ValueGenerator interface. func (s *subscriptsValueGenerator) Next(_ context.Context) (bool, error) { @@ -876,7 +898,7 @@ func (s *unaryValueGenerator) Start(_ context.Context, _ *kv.Txn) error { } // Close implements the tree.ValueGenerator interface. -func (s *unaryValueGenerator) Close() {} +func (s *unaryValueGenerator) Close(_ context.Context) {} // Next implements the tree.ValueGenerator interface. func (s *unaryValueGenerator) Next(_ context.Context) (bool, error) { @@ -981,7 +1003,7 @@ func (g *jsonArrayGenerator) Start(_ context.Context, _ *kv.Txn) error { } // Close implements the tree.ValueGenerator interface. -func (g *jsonArrayGenerator) Close() {} +func (g *jsonArrayGenerator) Close(_ context.Context) {} // Next implements the tree.ValueGenerator interface. func (g *jsonArrayGenerator) Next(_ context.Context) (bool, error) { @@ -1050,7 +1072,7 @@ func (g *jsonObjectKeysGenerator) ResolvedType() *types.T { func (g *jsonObjectKeysGenerator) Start(_ context.Context, _ *kv.Txn) error { return nil } // Close implements the tree.ValueGenerator interface. -func (g *jsonObjectKeysGenerator) Close() {} +func (g *jsonObjectKeysGenerator) Close(_ context.Context) {} // Next implements the tree.ValueGenerator interface. func (g *jsonObjectKeysGenerator) Next(_ context.Context) (bool, error) { @@ -1146,7 +1168,7 @@ func (g *jsonEachGenerator) Start(_ context.Context, _ *kv.Txn) error { } // Close implements the tree.ValueGenerator interface. -func (g *jsonEachGenerator) Close() {} +func (g *jsonEachGenerator) Close(_ context.Context) {} // Next implements the tree.ValueGenerator interface. func (g *jsonEachGenerator) Next(_ context.Context) (bool, error) { @@ -1278,7 +1300,7 @@ func (c *checkConsistencyGenerator) Values() (tree.Datums, error) { } // Close is part of the tree.ValueGenerator interface. -func (c *checkConsistencyGenerator) Close() {} +func (c *checkConsistencyGenerator) Close(_ context.Context) {} // rangeKeyIteratorChunkSize is the number of K/V pairs that the // rangeKeyIterator requests at a time. If this changes, make sure @@ -1400,7 +1422,7 @@ func (rk *rangeKeyIterator) Values() (tree.Datums, error) { } // Close implements the tree.ValueGenerator interface. -func (rk *rangeKeyIterator) Close() {} +func (rk *rangeKeyIterator) Close(_ context.Context) {} var payloadsForSpanGeneratorLabels = []string{"payload_type", "payload_jsonb"} @@ -1530,7 +1552,7 @@ func (p *payloadsForSpanGenerator) Values() (tree.Datums, error) { } // Close implements the tree.ValueGenerator interface. -func (p *payloadsForSpanGenerator) Close() {} +func (p *payloadsForSpanGenerator) Close(_ context.Context) {} var payloadsForTraceGeneratorLabels = []string{"span_id", "payload_type", "payload_jsonb"} @@ -1607,10 +1629,184 @@ func (p *payloadsForTraceGenerator) Values() (tree.Datums, error) { } // Close implements the tree.ValueGenerator interface. -func (p *payloadsForTraceGenerator) Close() { +func (p *payloadsForTraceGenerator) Close(_ context.Context) { err := p.it.Close() if err != nil { // TODO(angelapwen, yuzefovich): The iterator's error should be surfaced here. return } } + +var showCreateAllTablesGeneratorType = types.String + +// Phase is used to determine if CREATE statements or ALTER statements +// are being generated for showCreateAllTables. +type Phase int + +const ( + create Phase = iota + alterAddFks + alterValidateFks +) + +// showCreateAllTablesGenerator supports the execution of +// crdb_internal.show_create_all_tables(dbName). +type showCreateAllTablesGenerator struct { + ie sqlutil.InternalExecutor + txn *kv.Txn + timestamp string + ids []int64 + dbName string + acc mon.BoundAccount + + // The following variables are updated during + // calls to Next() and change throughout the lifecycle of + // showCreateAllTablesGenerator. + curr tree.Datum + idx int + shouldValidate bool + alterArr tree.Datums + alterArrIdx int + phase Phase +} + +// ResolvedType implements the tree.ValueGenerator interface. +func (s *showCreateAllTablesGenerator) ResolvedType() *types.T { + return showCreateAllTablesGeneratorType +} + +// Start implements the tree.ValueGenerator interface. +func (s *showCreateAllTablesGenerator) Start(ctx context.Context, txn *kv.Txn) error { + // Note: All the table ids are accumulated in ram before the generator + // starts generating values. + // This is reasonable under the assumption that: + // This uses approximately the same amount of memory as required when + // generating the vtable crdb_internal.show_create_statements. If generating + // and reading from the vtable succeeds which we do to retrieve the ids, then + // it is reasonable to use the same amount of memory to hold the ids in + // ram during the lifecycle of showCreateAllTablesGenerator. + // + // We also account for the memory in the BoundAccount memory monitor in + // showCreateAllTablesGenerator. + ids, err := getTopologicallySortedTableIDs( + ctx, s.ie, txn, s.dbName, s.timestamp, &s.acc, + ) + if err != nil { + return err + } + + s.ids = ids + + s.txn = txn + s.idx = -1 + s.phase = create + return nil +} + +func (s *showCreateAllTablesGenerator) Next(ctx context.Context) (bool, error) { + switch s.phase { + case create: + s.idx++ + if s.idx >= len(s.ids) { + // Were done generating the create statements, start generating alters. + s.phase = alterAddFks + s.idx = -1 + return s.Next(ctx) + } + + createStmt, err := getCreateStatement( + ctx, s.ie, s.txn, s.ids[s.idx], s.timestamp, s.dbName, + ) + if err != nil { + return false, err + } + createStmtStr := string(tree.MustBeDString(createStmt)) + s.curr = tree.NewDString(createStmtStr + ";") + case alterAddFks, alterValidateFks: + // We have existing alter statements to generate for the current + // table id. + s.alterArrIdx++ + if s.alterArrIdx < len(s.alterArr) { + alterStmtStr := string(tree.MustBeDString(s.alterArr[s.alterArrIdx])) + s.curr = tree.NewDString(alterStmtStr + ";") + + // At least one FK was added, we must validate the FK. + s.shouldValidate = true + return true, nil + } + // We need to generate the alter statements for the next table. + s.idx++ + if s.idx >= len(s.ids) { + if s.phase == alterAddFks { + // Were done generating the alter fk statements, + // start generating alter validate fk statements. + s.phase = alterValidateFks + s.idx = -1 + + if s.shouldValidate { + // Add a warning about the possibility of foreign key + // validation failing. + s.curr = tree.NewDString(foreignKeyValidationWarning) + return true, nil + } + return s.Next(ctx) + } + // We're done if were on phase alterValidateFks and we + // finish going through all the table ids. + return false, nil + } + + statementType := alterAddFKStatements + if s.phase == alterValidateFks { + statementType = alterValidateFKStatements + } + alterStmt, err := getAlterStatements( + ctx, s.ie, s.txn, s.ids[s.idx], s.timestamp, s.dbName, statementType, + ) + if err != nil { + return false, err + } + if alterStmt == nil { + // There can be no ALTER statements for a given id, in this case + // we go next. + return s.Next(ctx) + } + s.alterArr = tree.MustBeDArray(alterStmt).Array + s.alterArrIdx = -1 + return s.Next(ctx) + } + + return true, nil +} + +// Values implements the tree.ValueGenerator interface. +func (s *showCreateAllTablesGenerator) Values() (tree.Datums, error) { + return tree.Datums{s.curr}, nil +} + +// Close implements the tree.ValueGenerator interface. +func (s *showCreateAllTablesGenerator) Close(ctx context.Context) { + s.acc.Close(ctx) +} + +// makeShowCreateAllTablesGenerator creates a generator to support the +// crdb_internal.show_create_all_tables(dbName) builtin. +// We use the timestamp of when the generator is created as the +// timestamp to pass to AS OF SYSTEM TIME for looking up the create table +// and alter table statements. +func makeShowCreateAllTablesGenerator( + ctx *tree.EvalContext, args tree.Datums, +) (tree.ValueGenerator, error) { + dbName := string(tree.MustBeDString(args[0])) + tsI, err := tree.MakeDTimestamp(timeutil.Now(), time.Microsecond) + if err != nil { + return nil, err + } + ts := tsI.String() + return &showCreateAllTablesGenerator{ + timestamp: ts, + dbName: dbName, + ie: ctx.InternalExecutor.(sqlutil.InternalExecutor), + acc: ctx.Mon.MakeBoundAccount(), + }, nil +} diff --git a/pkg/sql/sem/builtins/geo_builtins.go b/pkg/sql/sem/builtins/geo_builtins.go index bd465d246552..ba541bafc84d 100644 --- a/pkg/sql/sem/builtins/geo_builtins.go +++ b/pkg/sql/sem/builtins/geo_builtins.go @@ -409,7 +409,7 @@ func (m *minimumBoundRadiusGen) Values() (tree.Datums, error) { tree.NewDFloat(tree.DFloat(m.radius))}, nil } -func (m *minimumBoundRadiusGen) Close() {} +func (m *minimumBoundRadiusGen) Close(_ context.Context) {} func makeSubdividedGeometriesGeneratorFactory(expectMaxVerticesArg bool) tree.GeneratorFactory { return func( @@ -441,7 +441,7 @@ type subdividedGeometriesGen struct { func (s *subdividedGeometriesGen) ResolvedType() *types.T { return types.Geometry } -func (s *subdividedGeometriesGen) Close() {} +func (s *subdividedGeometriesGen) Close(_ context.Context) {} func (s *subdividedGeometriesGen) Start(_ context.Context, _ *kv.Txn) error { s.curr = -1 diff --git a/pkg/sql/sem/builtins/show_create_all_tables_builtin.go b/pkg/sql/sem/builtins/show_create_all_tables_builtin.go index cacde442c115..5d1b5cb32c5b 100644 --- a/pkg/sql/sem/builtins/show_create_all_tables_builtin.go +++ b/pkg/sql/sem/builtins/show_create_all_tables_builtin.go @@ -11,300 +11,296 @@ package builtins import ( + "context" "fmt" "sort" - "strings" - "time" + "unsafe" + "github.com/cockroachdb/cockroach/pkg/kv" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" "github.com/cockroachdb/cockroach/pkg/sql/sqlutil" - "github.com/cockroachdb/cockroach/pkg/util/timeutil" + "github.com/cockroachdb/cockroach/pkg/util/mon" "github.com/cockroachdb/errors" ) -type tableMetadata struct { - ID int64 - name *tree.TableName - createStmt string - dependsOn []int64 - alter []string - validate []string -} +const sizeOfInt64 = int64(unsafe.Sizeof(int64(0))) -type tableWithSchema struct { - schema string - table string -} +// mapEntryOverhead is a guess on how much space (in bytes) +// each item added to a map takes. +// More explanation in cockroach/pkg/sql/rowexec/aggregator.go variable +// hashAggregatorSizeOfBucketsItem +const mapEntryOverhead = 64 + +// alterAddFKStatements represents the column name for alter_statements in +// crdb_internal.create_statements. +const alterAddFKStatements = "alter_statements" -// showCreateAllTablesBuiltin presents CREATE TABLE statements followed by -// ALTER TABLE statements for a database. The CREATE TABLE / ALTER TABLE -// statements are sorted such that the tables are ordered topologically to -// account for dependencies and references. The output can be used to -// recreate a database. -// The reason for ALTER TABLE statements coming after all CREATE TABLE -// statements is that we want to add constraints such as foreign keys -// after all the referenced tables are created. -func showCreateAllTablesBuiltin(evalCtx *tree.EvalContext, arg tree.Datums) (tree.Datum, error) { - mds, err := getMetadataForTablesInDB(evalCtx, arg) +// alterValidateFKStatements represents the column name for validate_statements in +// crdb_internal.create_statements. +const alterValidateFKStatements = "validate_statements" + +// foreignKeyValidationWarning is a warning letting the user know that +// the validate foreign key constraints may fail. +const foreignKeyValidationWarning = "-- Validate foreign key constraints. These can fail if there was unvalidated data during the SHOW CREATE ALL TABLES" + +// getTopologicallySortedTableIDs returns the set of table ids sorted +// first by table id, then topologically ordered such that dependencies are +// ordered before tables that depend on them. (ie, sequences will appear before +// the table that uses the sequence). +// The tables are sorted by table id first to guarantee stable ordering. +func getTopologicallySortedTableIDs( + ctx context.Context, + ie sqlutil.InternalExecutor, + txn *kv.Txn, + dbName string, + ts string, + acc *mon.BoundAccount, +) ([]int64, error) { + ids, err := getTableIDs(ctx, ie, txn, ts, dbName, acc) if err != nil { return nil, err } - if len(mds) == 0 { - return tree.NewDString(""), nil + if len(ids) == 0 { + return nil, nil } - byID := make(map[int64]tableMetadata) - for _, md := range mds { - byID[md.ID] = md + sizeOfMap := int64(0) + // dependsOnIDs maps an id of a table to the ids it depends on. + // We perform the topological sort on dependsOnIDs instead of on the + // byID map to reduce memory usage. + dependsOnIDs := make(map[int64][]int64) + for _, tid := range ids { + query := fmt.Sprintf(` + SELECT dependson_id + FROM %s.crdb_internal.backward_dependencies + AS OF SYSTEM TIME %s + WHERE descriptor_id = $1 + `, dbName, ts) + it, err := ie.QueryIteratorEx( + ctx, + "crdb_internal.show_create_all_tables", + txn, + sessiondata.NoSessionDataOverride, + query, + tid, + ) + if err != nil { + return nil, err + } + + var refs []int64 + var ok bool + for ok, err = it.Next(ctx); ok; ok, err = it.Next(ctx) { + id := tree.MustBeDInt(it.Cur()[0]) + refs = append(refs, int64(id)) + } + if err != nil { + return nil, err + } + + // Account for memory of map. + sizeOfKeyValue := int64(unsafe.Sizeof(tid)) + int64(len(refs))*sizeOfInt64 + sizeOfMap += sizeOfKeyValue + mapEntryOverhead + if err = acc.Grow(ctx, sizeOfKeyValue+mapEntryOverhead); err != nil { + return nil, err + } + + dependsOnIDs[tid] = refs } - // First sort by name to guarantee stable output. - sort.Slice(mds, func(i, j int) bool { - return mds[i].name.String() < mds[j].name.String() + // First sort by ids to guarantee stable output. + sort.Slice(ids, func(i, j int) bool { + return ids[i] < ids[j] }) // Collect transitive dependencies in topological order into collected. // The topological order is essential here since it captures dependencies // for views and sequences creation, hence simple alphabetical sort won't // be enough. - var collected []int64 - seen := make(map[int64]bool) - for _, md := range mds { - collect(md.ID, byID, seen, &collected) - } - // collectOrder maps a table ID to its collection index. This is needed - // instead of just using range over collected because collected may contain - // table IDs not present in the dump spec. It is simpler to sort mds correctly - // to skip over these referenced-but-not-dumped tables. - collectOrder := make(map[int64]int) - for i, id := range collected { - collectOrder[id] = i - } - - // Second sort dumped tables by dependency order. - sort.SliceStable(mds, func(i, j int) bool { - return collectOrder[mds[i].ID] < collectOrder[mds[j].ID] - }) - - var out []string - for _, md := range mds { - out = append(out, md.createStmt+";\n") - } + var topologicallyOrderedIDs []int64 - hasRefs := false - for _, md := range mds { - for _, alter := range md.alter { - if !hasRefs { - hasRefs = true - } - out = append(out, fmt.Sprintf("%s;\n", alter)) - } - } - if hasRefs { - const alterValidateMessage = `-- Validate foreign key constraints. These can fail if there was unvalidated data during the SHOW CREATE ALL TABLES` - out = append(out, alterValidateMessage+"\n") - for _, md := range mds { - for _, validate := range md.validate { - out = append(out, fmt.Sprintf("%s;\n", validate)) - - } - } - } - - result := tree.NewDString(strings.Join(out, "")) - return result, nil -} - -// getMetadataForTablesInDB finds all the table names in a given database and -// populates the tableMetadata information for all the tables. -func getMetadataForTablesInDB(evalCtx *tree.EvalContext, arg tree.Datums) ([]tableMetadata, error) { - tsI, err := tree.MakeDTimestamp(timeutil.Now(), time.Microsecond) - if err != nil { - return nil, err - } - ts := tsI.String() - dbName := string(tree.MustBeDString(arg[0])) - tableMDs, err := getTableNames(evalCtx, dbName, ts) - if err != nil { + // The sort relies on creating a new array for the ids. + if err = acc.Grow(ctx, int64(len(ids))*sizeOfInt64); err != nil { return nil, err } - - mds := make([]tableMetadata, len(tableMDs)) - for i, dumpTable := range tableMDs { - tableMD, err := getTableMetadata(evalCtx, dbName, dumpTable, ts) - if err != nil { + seen := make(map[int64]struct{}) + for _, id := range ids { + if err := topologicalSort( + ctx, id, dependsOnIDs, seen, &topologicallyOrderedIDs, acc, + ); err != nil { return nil, err } - mds[i] = tableMD } - return mds, nil + // Clear memory used by the seen map. + sizeOfSeen := len(seen) + seen = nil + acc.Shrink(ctx, int64(sizeOfSeen)*(sizeOfInt64+mapEntryOverhead)) + + // The lengths should match. This is also important for memory accounting, + // the two arrays should have the same length. + if len(ids) != len(topologicallyOrderedIDs) { + return nil, errors.AssertionFailedf("show_create_all_tables_builtin failed. "+ + "len(ids):% d not equal to len(topologicallySortedIDs): %d", + len(ids), len(topologicallyOrderedIDs)) + } + + // Shrink the memory we used for the original ids array. + acc.Shrink(ctx, int64(len(ids))*sizeOfInt64) + acc.Shrink(ctx, sizeOfMap) + return topologicallyOrderedIDs, nil } -// getTableMetadata populates the metadata for a given table by querying -// crdb_internal.create_statements. -func getTableMetadata( - evalCtx *tree.EvalContext, dbName string, table tableWithSchema, ts string, -) (tableMetadata, error) { - tn := tree.MakeTableNameWithSchema(tree.Name(dbName), tree.Name(table.schema), tree.Name(table.table)) - // Fetch table ID. +// getTableIDs returns the set of table ids from +// crdb_internal.show_create_all_tables for a specified database. +func getTableIDs( + ctx context.Context, + ie sqlutil.InternalExecutor, + txn *kv.Txn, + ts string, + dbName string, + acc *mon.BoundAccount, +) ([]int64, error) { query := fmt.Sprintf(` - SELECT - schema_name, - descriptor_id, - create_nofks, - alter_statements, - validate_statements + SELECT descriptor_id FROM %s.crdb_internal.create_statements AS OF SYSTEM TIME %s - WHERE database_name = $1 - AND schema_name = $2 - AND descriptor_name = $3 - `, dbName, ts) - ie := evalCtx.InternalExecutor.(sqlutil.InternalExecutor) - vals, err := ie.QueryRowEx( - evalCtx.Context, + WHERE database_name = $1 + AND is_virtual = FALSE + AND is_temporary = FALSE + `, dbName, ts) + it, err := ie.QueryIteratorEx( + ctx, "crdb_internal.show_create_all_tables", - evalCtx.Txn, + txn, sessiondata.NoSessionDataOverride, query, dbName, - table.schema, - table.table, ) if err != nil { - return tableMetadata{}, err + return nil, err } - if len(vals) == 0 { - return tableMetadata{}, nil + var tableIDs []int64 + + var ok bool + for ok, err = it.Next(ctx); ok; ok, err = it.Next(ctx) { + tid := tree.MustBeDInt(it.Cur()[0]) + + tableIDs = append(tableIDs, int64(tid)) + if err = acc.Grow(ctx, int64(unsafe.Sizeof(tid))); err != nil { + return nil, err + } + } + if err != nil { + return tableIDs, err } - // Check the schema to disallow dumping temp tables, views and sequences. This - // will only be triggered if a user explicitly specifies a temp construct as - // one of the arguments to the `cockroach dump` command. When no table names - // are specified on the CLI, we ignore temp tables at the stage where we read - // all table names in getTableNames. - schemaName := string(tree.MustBeDString(vals[0])) - if strings.HasPrefix(schemaName, sessiondata.PgTempSchemaName) { - return tableMetadata{}, errors.Newf("cannot dump temp table %s", tn.String()) + return tableIDs, nil +} + +// topologicalSort sorts transitive dependencies in topological order into +// collected. +// The topological order is essential here since it captures dependencies +// for views and sequences creation, hence simple alphabetical sort won't +// be enough. +func topologicalSort( + ctx context.Context, + tid int64, + dependsOnIDs map[int64][]int64, + seen map[int64]struct{}, + collected *[]int64, + acc *mon.BoundAccount, +) error { + // has this table already been collected previously? + // We need this check because a table could be traversed to multiple times + // if it is referenced. + // For example, if a table references itself, without this check + // collect would infinitely recurse. + if _, isPresent := seen[tid]; isPresent { + return nil + } + + // Account for memory of map. + // The key value entry into the map is only the memory of an int64 since + // the value stuct{}{} uses no memory. + if err := acc.Grow(ctx, sizeOfInt64+mapEntryOverhead); err != nil { + return err + } + seen[tid] = struct{}{} + for _, dep := range dependsOnIDs[tid] { + if err := topologicalSort(ctx, dep, dependsOnIDs, seen, collected, acc); err != nil { + return err + } } - id := int64(tree.MustBeDInt(vals[1])) - createStatement := string(tree.MustBeDString(vals[2])) - alterStatements := extractArray(vals[3]) - validateStatements := extractArray(vals[4]) + if err := acc.Grow(ctx, int64(unsafe.Sizeof(tid))); err != nil { + return err + } + *collected = append(*collected, tid) - // Get dependencies. - query = fmt.Sprintf(` - SELECT dependson_id - FROM %s.crdb_internal.backward_dependencies + return nil +} + +// getCreateStatement gets the create statement to recreate a table (ignoring fks) +// for a given table id in a database. +func getCreateStatement( + ctx context.Context, ie sqlutil.InternalExecutor, txn *kv.Txn, id int64, ts string, dbName string, +) (tree.Datum, error) { + query := fmt.Sprintf(` + SELECT + create_nofks + FROM %s.crdb_internal.create_statements AS OF SYSTEM TIME %s WHERE descriptor_id = $1 - `, dbName, ts) - it, err := ie.QueryIteratorEx( - evalCtx.Context, + `, dbName, ts) + row, err := ie.QueryRowEx( + ctx, "crdb_internal.show_create_all_tables", - evalCtx.Txn, + txn, sessiondata.NoSessionDataOverride, query, id, ) - if err != nil { - return tableMetadata{}, err - } - var refs []int64 - var ok bool - for ok, err = it.Next(evalCtx.Context); ok; ok, err = it.Next(evalCtx.Context) { - id := tree.MustBeDInt(it.Cur()[0]) - refs = append(refs, int64(id)) - } if err != nil { - return tableMetadata{}, err - } - md := tableMetadata{ - ID: id, - name: &tn, - createStmt: createStatement, - dependsOn: refs, - alter: alterStatements, - validate: validateStatements, - } - - return md, nil -} - -// extractArray ensures that a tree.Datum is a DArray and converts -// the DArray into a list of strings. -func extractArray(val tree.Datum) []string { - arr := tree.MustBeDArray(val) - res := make([]string, len(arr.Array)) - for i, v := range arr.Array { - res[i] = string(*v.(*tree.DString)) + return nil, err } - return res + return row[0], nil } -// getTableNames retrieves all tables names in the given database. Following -// pg_dump, we ignore all descriptors which are part of the temp schema. This -// includes tables, views and sequences. -func getTableNames(evalCtx *tree.EvalContext, dbName string, ts string) ([]tableWithSchema, error) { +// getAlterStatements gets the set of alter statements that add and validate +// foreign keys for a given table id in a database. +func getAlterStatements( + ctx context.Context, + ie sqlutil.InternalExecutor, + txn *kv.Txn, + id int64, + ts string, + dbName string, + statementType string, +) (tree.Datum, error) { query := fmt.Sprintf(` - SELECT schema_name, descriptor_name - FROM "".crdb_internal.create_statements + SELECT + %s + FROM %s.crdb_internal.create_statements AS OF SYSTEM TIME %s - WHERE database_name = $1 AND schema_name NOT LIKE $2 - `, ts) - ie := evalCtx.InternalExecutor.(sqlutil.InternalExecutor) - it, err := ie.QueryIteratorEx( - evalCtx.Ctx(), + WHERE descriptor_id = $1 + `, statementType, dbName, ts) + row, err := ie.QueryRowEx( + ctx, "crdb_internal.show_create_all_tables", - evalCtx.Txn, + txn, sessiondata.NoSessionDataOverride, query, - dbName, - sessiondata.PgTempSchemaName+"%", + id, ) - if err != nil { - return nil, err - } - - var tableNames []tableWithSchema - var ok bool - for ok, err = it.Next(evalCtx.Context); ok; ok, err = it.Next(evalCtx.Context) { - schema := string(tree.MustBeDString(it.Cur()[0])) - table := string(tree.MustBeDString(it.Cur()[1])) - - tableNames = append(tableNames, tableWithSchema{table: table, schema: schema}) - } if err != nil { - return tableNames, err + return nil, err } - return tableNames, nil -} - -// collect maps a table id to it's tableMetadata and ensures tables are only -// mapped once. -func collect(tid int64, byID map[int64]tableMetadata, seen map[int64]bool, collected *[]int64) { - // has this table already been collected previously? - // We need this check because a table could be multiple times - // if it is referenced. - // For example, if a table references itself, without this check - // collect would infinitely recurse. - if seen[tid] { - return - } - // no: mark it as seen. - seen[tid] = true - for _, dep := range byID[tid].dependsOn { - // depth-first collection of dependencies - collect(dep, byID, seen, collected) - } - // Only add it after its dependencies. - *collected = append(*collected, tid) + return row[0], nil } diff --git a/pkg/sql/sem/tree/eval.go b/pkg/sql/sem/tree/eval.go index f0fd91b393cb..e2987cff7343 100644 --- a/pkg/sql/sem/tree/eval.go +++ b/pkg/sql/sem/tree/eval.go @@ -5408,7 +5408,7 @@ func (c *CallbackValueGenerator) Values() (Datums, error) { } // Close is part of the ValueGenerator interface. -func (c *CallbackValueGenerator) Close() {} +func (c *CallbackValueGenerator) Close(_ context.Context) {} // Sqrt returns the square root of x. func Sqrt(x float64) (*DFloat, error) { diff --git a/pkg/sql/sem/tree/generators.go b/pkg/sql/sem/tree/generators.go index bc7fc9fbd5de..5ca4e23348db 100644 --- a/pkg/sql/sem/tree/generators.go +++ b/pkg/sql/sem/tree/generators.go @@ -59,7 +59,7 @@ type ValueGenerator interface { // Close must be called after Start() before disposing of the // ValueGenerator. It does not need to be called if Start() has not // been called yet. It must not be called in-between restarts. - Close() + Close(ctx context.Context) } // GeneratorFactory is the type of constructor functions for