Skip to content
Permalink
Browse files

sql: print comments in SHOW CREATE TABLE

Fixes #42875

Release note (sql change): SHOW CREATE TABLE now also emits the COMMENT statements sufficient to populate the table's user-defined comments, if any, alongside the CREATE statement proper.
  • Loading branch information
hueypark committed Jan 20, 2020
1 parent 3bb1834 commit 08be7c42415af6009836da5444889eb43382103e
@@ -55,7 +55,7 @@ func showBackupPlanHook(
case tree.BackupFileDetails:
shower = backupShowerFiles
default:
shower = backupShowerDefault(ctx, backup.ShouldIncludeSchemas)
shower = backupShowerDefault(ctx, p, backup.ShouldIncludeSchemas)
}

fn := func(ctx context.Context, _ []sql.PlanNode, resultsCh chan<- tree.Datums) error {
@@ -112,7 +112,7 @@ func backupShowerHeaders(showSchemas bool) sqlbase.ResultColumns {
return baseHeaders
}

func backupShowerDefault(ctx context.Context, showSchemas bool) backupShower {
func backupShowerDefault(ctx context.Context, p sql.PlanHookState, showSchemas bool) backupShower {
return backupShower{
header: backupShowerHeaders(showSchemas),
fn: func(desc BackupDescriptor) []tree.Datums {
@@ -157,7 +157,7 @@ func backupShowerDefault(ctx context.Context, showSchemas bool) backupShower {
tree.NewDInt(tree.DInt(descSizes[table.ID].Rows)),
}
if showSchemas {
schema, err := sql.ShowCreate(ctx, dbName, desc.Descriptors, table, sql.OmitMissingFKClausesFromCreate)
schema, err := p.ShowCreate(ctx, dbName, desc.Descriptors, table, sql.OmitMissingFKClausesFromCreate)
if err != nil {
continue
}
@@ -119,17 +119,24 @@ func TestShowBackup(t *testing.T) {
// Test that tables, views and sequences are all supported.
{
viewTableSeq := localFoo + "/tableviewseq"
sqlDB.Exec(t, `CREATE TABLE data.tableA (a int primary key, b int)`)
sqlDB.Exec(t, `CREATE TABLE data.tableA (a int primary key, b int, INDEX tableA_b_idx (b ASC))`)
sqlDB.Exec(t, `COMMENT ON TABLE data.tableA IS 'table'`)
sqlDB.Exec(t, `COMMENT ON COLUMN data.tableA.a IS 'column'`)
sqlDB.Exec(t, `COMMENT ON INDEX data.tableA_b_idx IS 'index'`)
sqlDB.Exec(t, `CREATE VIEW data.viewA AS SELECT a from data.tableA`)
sqlDB.Exec(t, `CREATE SEQUENCE data.seqA START 1 INCREMENT 2 MAXVALUE 20`)
sqlDB.Exec(t, `BACKUP data.tableA, data.viewA, data.seqA TO $1;`, viewTableSeq)

expectedCreateTable := `CREATE TABLE tablea (
a INT8 NOT NULL,
b INT8 NULL,
CONSTRAINT "primary" PRIMARY KEY (a ASC),
FAMILY "primary" (a, b)
)`
a INT8 NOT NULL,
b INT8 NULL,
CONSTRAINT "primary" PRIMARY KEY (a ASC),
INDEX tablea_b_idx (b ASC),
FAMILY "primary" (a, b)
);
COMMENT ON TABLE tablea IS 'table';
COMMENT ON COLUMN tablea.a IS 'column';
COMMENT ON INDEX tablea_b_idx IS 'index'`
expectedCreateView := `CREATE VIEW viewa (a) AS SELECT a FROM data.public.tablea`
expectedCreateSeq := `CREATE SEQUENCE seqa MINVALUE 1 MAXVALUE 20 INCREMENT 2 START 1`

@@ -1260,15 +1260,15 @@ CREATE TABLE crdb_internal.create_statements (
} else {
descType = typeTable
tn := (*tree.Name)(&table.Name)
createNofk, err = ShowCreateTable(ctx, tn, contextName, table, lCtx, OmitFKClausesFromCreate)
createNofk, err = ShowCreateTable(ctx, p, tn, contextName, table, lCtx, OmitFKClausesFromCreate)
if err != nil {
return err
}
allIdx := append(table.Indexes, table.PrimaryIndex)
if err := showAlterStatementWithInterleave(ctx, tn, contextName, lCtx, allIdx, table, alterStmts, validateStmts); err != nil {
return err
}
stmt, err = ShowCreateTable(ctx, tn, contextName, table, lCtx, IncludeFkClausesInCreate)
stmt, err = ShowCreateTable(ctx, p, tn, contextName, table, lCtx, IncludeFkClausesInCreate)
}
if err != nil {
return err
@@ -10,6 +10,24 @@ CREATE TABLE v (
FAMILY "primary" ("'", s, rowid)
)

statement ok
CREATE TABLE c (
a INT NOT NULL,
b INT NULL,
INDEX c_a_b_idx (a ASC, b ASC),
FAMILY fam_0_a_rowid (a, rowid),
FAMILY fam_1_b (b)
)

statement ok
COMMENT ON TABLE c IS 'table'

statement ok
COMMENT ON COLUMN c.a IS 'column'

statement ok
COMMENT ON INDEX c_a_b_idx IS 'index'

query TTTT colnames
SELECT create_statement, create_nofks, alter_statements, validate_statements FROM crdb_internal.create_statements WHERE database_name = 'test'
----
@@ -39,6 +57,25 @@ CREATE TABLE v (
INDEX "v_auto_index_fk_'_ref_t" ("'" ASC),
FAMILY "primary" ("'", s, rowid)
) {"ALTER TABLE v ADD CONSTRAINT \"fk_'_ref_t\" FOREIGN KEY (\"'\") REFERENCES t(rowid)","ALTER TABLE v ADD CONSTRAINT fk_s_ref_v FOREIGN KEY (s) REFERENCES v(s)"} {"ALTER TABLE v VALIDATE CONSTRAINT \"fk_'_ref_t\"","ALTER TABLE v VALIDATE CONSTRAINT fk_s_ref_v"}
CREATE TABLE c (
a INT8 NOT NULL,
b INT8 NULL,
INDEX c_a_b_idx (a ASC, b ASC),
FAMILY fam_0_a_rowid (a, rowid),
FAMILY fam_1_b (b)
);
COMMENT ON TABLE c IS 'table';
COMMENT ON COLUMN c.a IS 'column';
COMMENT ON INDEX c_a_b_idx IS 'index' CREATE TABLE c (
a INT8 NOT NULL,
b INT8 NULL,
INDEX c_a_b_idx (a ASC, b ASC),
FAMILY fam_0_a_rowid (a, rowid),
FAMILY fam_1_b (b)
);
COMMENT ON TABLE c IS 'table';
COMMENT ON COLUMN c.a IS 'column';
COMMENT ON INDEX c_a_b_idx IS 'index' {} {}

statement error invalid storage parameter "foo"
CREATE TABLE a (b INT) WITH (foo=100);
@@ -0,0 +1,33 @@
statement ok
CREATE TABLE c (
a INT NOT NULL,
b INT NULL,
INDEX c_a_b_idx (a ASC, b ASC),
FAMILY fam_0_a_rowid (a, rowid),
FAMILY fam_1_b (b)
)

statement ok
COMMENT ON TABLE c IS 'table'

statement ok
COMMENT ON COLUMN c.a IS 'column'

statement ok
COMMENT ON INDEX c_a_b_idx IS 'index'

query TT colnames
SHOW CREATE c
----
table_name create_statement
c CREATE TABLE c (
a INT8 NOT NULL,
b INT8 NULL,
INDEX c_a_b_idx (a ASC, b ASC),
FAMILY fam_0_a_rowid (a, rowid),
FAMILY fam_1_b (b)
);
COMMENT ON TABLE c IS 'table';
COMMENT ON COLUMN c.a IS 'column';
COMMENT ON INDEX c_a_b_idx IS 'index'

@@ -97,6 +97,9 @@ type PlanHookState interface {
ResolveMutableTableDescriptor(
ctx context.Context, tn *ObjectName, required bool, requiredType ResolveRequiredType,
) (table *MutableTableDescriptor, err error)
ShowCreate(
ctx context.Context, dbPrefix string, allDescs []sqlbase.Descriptor, desc *sqlbase.TableDescriptor, ignoreFKs shouldOmitFKClausesFromCreate,
) (string, error)
}

// AddPlanHook adds a hook used to short-circuit creating a planNode from a
@@ -47,6 +47,7 @@ const (
// current database.
func ShowCreateTable(
ctx context.Context,
p PlanHookState,
tn *tree.Name,
dbPrefix string,
desc *sqlbase.TableDescriptor,
@@ -150,6 +151,10 @@ func ShowCreateTable(
return "", err
}

if err := showComments(desc, selectComment(ctx, p, desc.ID), &f.Buffer); err != nil {
return "", err
}

return f.CloseAndGetString(), nil
}

@@ -173,7 +178,7 @@ func formatQuoteNames(buf *bytes.Buffer, names ...string) {
// unless it is equal to the given dbPrefix. This allows us to elide
// the prefix when the given table references other tables in the
// current database.
func ShowCreate(
func (p *planner) ShowCreate(
ctx context.Context,
dbPrefix string,
allDescs []sqlbase.Descriptor,
@@ -189,7 +194,7 @@ func ShowCreate(
stmt, err = ShowCreateSequence(ctx, tn, desc)
} else {
lCtx := newInternalLookupCtxFromDescriptors(allDescs, nil /* want all tables */)
stmt, err = ShowCreateTable(ctx, tn, dbPrefix, desc, lCtx, ignoreFKs)
stmt, err = ShowCreateTable(ctx, p, tn, dbPrefix, desc, lCtx, ignoreFKs)
}

return stmt, err
@@ -16,11 +16,61 @@ import (
"fmt"
"strings"

"github.com/cockroachdb/cockroach/pkg/keys"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
"github.com/cockroachdb/cockroach/pkg/util/log"
"github.com/pkg/errors"
)

// tableComments stores the comment data for a table.
type tableComments struct {
comment *string
columns []comment
indexes []comment
}

type comment struct {
subID int
comment string
}

// selectComment retrieves all the comments pertaining to a table (comments on the table
// itself but also column and index comments.)
func selectComment(ctx context.Context, p PlanHookState, tableID sqlbase.ID) (tc *tableComments) {
query := fmt.Sprintf("SELECT type, object_id, sub_id, comment FROM system.comments WHERE object_id = %d", tableID)

commentRows, err := p.ExtendedEvalContext().ExecCfg.InternalExecutor.Query(
ctx, "show-tables-with-comment", p.Txn(), query)
if err != nil {
log.VEventf(ctx, 1, "%q", err)
} else {
for _, row := range commentRows {
commentType := int(tree.MustBeDInt(row[0]))
switch commentType {
case keys.TableCommentType, keys.ColumnCommentType, keys.IndexCommentType:
subID := int(tree.MustBeDInt(row[2]))
cmt := string(tree.MustBeDString(row[3]))

if tc == nil {
tc = &tableComments{}
}

switch commentType {
case keys.TableCommentType:
tc.comment = &cmt
case keys.ColumnCommentType:
tc.columns = append(tc.columns, comment{subID, cmt})
case keys.IndexCommentType:
tc.indexes = append(tc.indexes, comment{subID, cmt})
}
}
}
}

return tc
}

// ShowCreateView returns a valid SQL representation of the CREATE VIEW
// statement used to create the given view. It is used in the implementation of
// the crdb_internal.create_statements virtual table.
@@ -42,6 +92,41 @@ func ShowCreateView(
return f.CloseAndGetString(), nil
}

// showComments prints out the COMMENT statements sufficient to populate a
// table's comments, including its index and column comments.
func showComments(table *sqlbase.TableDescriptor, tc *tableComments, buf *bytes.Buffer) error {
if tc == nil {
return nil
}

if tc.comment != nil {
buf.WriteString(";\n")
buf.WriteString(fmt.Sprintf("COMMENT ON TABLE %s IS '%s'", table.Name, *tc.comment))
}

for _, columnComment := range tc.columns {
col, err := table.FindColumnByID(sqlbase.ColumnID(columnComment.subID))
if err != nil {
return err
}

buf.WriteString(";\n")
buf.WriteString(fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", table.Name, col.Name, columnComment.comment))
}

for _, indexComment := range tc.indexes {
idx, err := table.FindIndexByID(sqlbase.IndexID(indexComment.subID))
if err != nil {
return err
}

buf.WriteString(";\n")
buf.WriteString(fmt.Sprintf("COMMENT ON INDEX %s IS '%s'", idx.Name, indexComment.comment))
}

return nil
}

// showForeignKeyConstraint returns a valid SQL representation of a FOREIGN KEY
// clause for a given index.
func showForeignKeyConstraint(

This file was deleted.

0 comments on commit 08be7c4

Please sign in to comment.
You can’t perform that action at this time.