github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/show_create_clauses.go (about)

     1  // Copyright 2019 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package sql
    12  
    13  import (
    14  	"bytes"
    15  	"context"
    16  	"fmt"
    17  	"strings"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/keys"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/schemaexpr"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    23  	"github.com/cockroachdb/cockroach/pkg/util/log"
    24  	"github.com/cockroachdb/errors"
    25  )
    26  
    27  // tableComments stores the comment data for a table.
    28  type tableComments struct {
    29  	comment *string
    30  	columns []comment
    31  	indexes []comment
    32  }
    33  
    34  type comment struct {
    35  	subID   int
    36  	comment string
    37  }
    38  
    39  // selectComment retrieves all the comments pertaining to a table (comments on the table
    40  // itself but also column and index comments.)
    41  func selectComment(ctx context.Context, p PlanHookState, tableID sqlbase.ID) (tc *tableComments) {
    42  	query := fmt.Sprintf("SELECT type, object_id, sub_id, comment FROM system.comments WHERE object_id = %d", tableID)
    43  
    44  	commentRows, err := p.ExtendedEvalContext().ExecCfg.InternalExecutor.Query(
    45  		ctx, "show-tables-with-comment", p.Txn(), query)
    46  	if err != nil {
    47  		log.VEventf(ctx, 1, "%q", err)
    48  	} else {
    49  		for _, row := range commentRows {
    50  			commentType := int(tree.MustBeDInt(row[0]))
    51  			switch commentType {
    52  			case keys.TableCommentType, keys.ColumnCommentType, keys.IndexCommentType:
    53  				subID := int(tree.MustBeDInt(row[2]))
    54  				cmt := string(tree.MustBeDString(row[3]))
    55  
    56  				if tc == nil {
    57  					tc = &tableComments{}
    58  				}
    59  
    60  				switch commentType {
    61  				case keys.TableCommentType:
    62  					tc.comment = &cmt
    63  				case keys.ColumnCommentType:
    64  					tc.columns = append(tc.columns, comment{subID, cmt})
    65  				case keys.IndexCommentType:
    66  					tc.indexes = append(tc.indexes, comment{subID, cmt})
    67  				}
    68  			}
    69  		}
    70  	}
    71  
    72  	return tc
    73  }
    74  
    75  // ShowCreateView returns a valid SQL representation of the CREATE VIEW
    76  // statement used to create the given view. It is used in the implementation of
    77  // the crdb_internal.create_statements virtual table.
    78  func ShowCreateView(
    79  	ctx context.Context, tn *tree.Name, desc *sqlbase.TableDescriptor,
    80  ) (string, error) {
    81  	f := tree.NewFmtCtx(tree.FmtSimple)
    82  	f.WriteString("CREATE ")
    83  	if desc.Temporary {
    84  		f.WriteString("TEMP ")
    85  	}
    86  	f.WriteString("VIEW ")
    87  	f.FormatNode(tn)
    88  	f.WriteString(" (")
    89  	for i := range desc.Columns {
    90  		if i > 0 {
    91  			f.WriteString(", ")
    92  		}
    93  		f.FormatNameP(&desc.Columns[i].Name)
    94  	}
    95  	f.WriteString(") AS ")
    96  	f.WriteString(desc.ViewQuery)
    97  	return f.CloseAndGetString(), nil
    98  }
    99  
   100  // showComments prints out the COMMENT statements sufficient to populate a
   101  // table's comments, including its index and column comments.
   102  func showComments(table *sqlbase.TableDescriptor, tc *tableComments, buf *bytes.Buffer) error {
   103  	if tc == nil {
   104  		return nil
   105  	}
   106  
   107  	if tc.comment != nil {
   108  		buf.WriteString(";\n")
   109  		buf.WriteString(fmt.Sprintf("COMMENT ON TABLE %s IS '%s'", table.Name, *tc.comment))
   110  	}
   111  
   112  	for _, columnComment := range tc.columns {
   113  		col, err := table.FindColumnByID(sqlbase.ColumnID(columnComment.subID))
   114  		if err != nil {
   115  			return err
   116  		}
   117  
   118  		buf.WriteString(";\n")
   119  		buf.WriteString(fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", table.Name, col.Name, columnComment.comment))
   120  	}
   121  
   122  	for _, indexComment := range tc.indexes {
   123  		idx, err := table.FindIndexByID(sqlbase.IndexID(indexComment.subID))
   124  		if err != nil {
   125  			return err
   126  		}
   127  
   128  		buf.WriteString(";\n")
   129  		buf.WriteString(fmt.Sprintf("COMMENT ON INDEX %s IS '%s'", idx.Name, indexComment.comment))
   130  	}
   131  
   132  	return nil
   133  }
   134  
   135  // showForeignKeyConstraint returns a valid SQL representation of a FOREIGN KEY
   136  // clause for a given index.
   137  func showForeignKeyConstraint(
   138  	buf *bytes.Buffer,
   139  	dbPrefix string,
   140  	originTable *sqlbase.TableDescriptor,
   141  	fk *sqlbase.ForeignKeyConstraint,
   142  	lCtx simpleSchemaResolver,
   143  ) error {
   144  	var refNames []string
   145  	var originNames []string
   146  	var fkTableName tree.TableName
   147  	if lCtx != nil {
   148  		fkTable, err := lCtx.getTableByID(fk.ReferencedTableID)
   149  		if err != nil {
   150  			return err
   151  		}
   152  		fkDb, err := lCtx.getDatabaseByID(fkTable.ParentID)
   153  		if err != nil {
   154  			return err
   155  		}
   156  		refNames, err = fkTable.NamesForColumnIDs(fk.ReferencedColumnIDs)
   157  		if err != nil {
   158  			return err
   159  		}
   160  		fkTableName = tree.MakeTableName(tree.Name(fkDb.Name), tree.Name(fkTable.Name))
   161  		fkTableName.ExplicitSchema = fkDb.Name != dbPrefix
   162  		originNames, err = originTable.NamesForColumnIDs(fk.OriginColumnIDs)
   163  		if err != nil {
   164  			return err
   165  		}
   166  	} else {
   167  		refNames = []string{"???"}
   168  		originNames = []string{"???"}
   169  		fkTableName = tree.MakeTableName(tree.Name(""), tree.Name(fmt.Sprintf("[%d as ref]", fk.ReferencedTableID)))
   170  		fkTableName.ExplicitSchema = false
   171  	}
   172  	buf.WriteString("FOREIGN KEY (")
   173  	formatQuoteNames(buf, originNames...)
   174  	buf.WriteString(") REFERENCES ")
   175  	fmtCtx := tree.NewFmtCtx(tree.FmtSimple)
   176  	fmtCtx.FormatNode(&fkTableName)
   177  	buf.WriteString(fmtCtx.CloseAndGetString())
   178  	buf.WriteString("(")
   179  	formatQuoteNames(buf, refNames...)
   180  	buf.WriteByte(')')
   181  	// We omit MATCH SIMPLE because it is the default.
   182  	if fk.Match != sqlbase.ForeignKeyReference_SIMPLE {
   183  		buf.WriteByte(' ')
   184  		buf.WriteString(fk.Match.String())
   185  	}
   186  	if fk.OnDelete != sqlbase.ForeignKeyReference_NO_ACTION {
   187  		buf.WriteString(" ON DELETE ")
   188  		buf.WriteString(fk.OnDelete.String())
   189  	}
   190  	if fk.OnUpdate != sqlbase.ForeignKeyReference_NO_ACTION {
   191  		buf.WriteString(" ON UPDATE ")
   192  		buf.WriteString(fk.OnUpdate.String())
   193  	}
   194  	return nil
   195  }
   196  
   197  // ShowCreateSequence returns a valid SQL representation of the
   198  // CREATE SEQUENCE statement used to create the given sequence.
   199  func ShowCreateSequence(
   200  	ctx context.Context, tn *tree.Name, desc *sqlbase.TableDescriptor,
   201  ) (string, error) {
   202  	f := tree.NewFmtCtx(tree.FmtSimple)
   203  	f.WriteString("CREATE ")
   204  	if desc.Temporary {
   205  		f.WriteString("TEMP ")
   206  	}
   207  	f.WriteString("SEQUENCE ")
   208  	f.FormatNode(tn)
   209  	opts := desc.SequenceOpts
   210  	f.Printf(" MINVALUE %d", opts.MinValue)
   211  	f.Printf(" MAXVALUE %d", opts.MaxValue)
   212  	f.Printf(" INCREMENT %d", opts.Increment)
   213  	f.Printf(" START %d", opts.Start)
   214  	if opts.Virtual {
   215  		f.Printf(" VIRTUAL")
   216  	}
   217  	return f.CloseAndGetString(), nil
   218  }
   219  
   220  // showFamilyClause creates the FAMILY clauses for a CREATE statement, writing them
   221  // to tree.FmtCtx f
   222  func showFamilyClause(desc *sqlbase.TableDescriptor, f *tree.FmtCtx) {
   223  	for _, fam := range desc.Families {
   224  		activeColumnNames := make([]string, 0, len(fam.ColumnNames))
   225  		for i, colID := range fam.ColumnIDs {
   226  			if _, err := desc.FindActiveColumnByID(colID); err == nil {
   227  				activeColumnNames = append(activeColumnNames, fam.ColumnNames[i])
   228  			}
   229  		}
   230  		if len(desc.VisibleColumns()) == 0 {
   231  			f.WriteString("FAMILY ")
   232  		} else {
   233  			f.WriteString(",\n\tFAMILY ")
   234  		}
   235  		formatQuoteNames(&f.Buffer, fam.Name)
   236  		f.WriteString(" (")
   237  		formatQuoteNames(&f.Buffer, activeColumnNames...)
   238  		f.WriteString(")")
   239  	}
   240  }
   241  
   242  // showCreateInterleave returns an INTERLEAVE IN PARENT clause for the specified
   243  // index, if applicable.
   244  //
   245  // The name of the parent table is prefixed by its database name unless
   246  // it is equal to the given dbPrefix. This allows us to elide the prefix
   247  // when the given index is interleaved in a table of the current database.
   248  func showCreateInterleave(
   249  	idx *sqlbase.IndexDescriptor, buf *bytes.Buffer, dbPrefix string, lCtx simpleSchemaResolver,
   250  ) error {
   251  	if len(idx.Interleave.Ancestors) == 0 {
   252  		return nil
   253  	}
   254  	intl := idx.Interleave
   255  	parentTableID := intl.Ancestors[len(intl.Ancestors)-1].TableID
   256  	var err error
   257  	var parentName tree.TableName
   258  	if lCtx != nil {
   259  		parentName, err = getParentAsTableName(lCtx, parentTableID, dbPrefix)
   260  		if err != nil {
   261  			return err
   262  		}
   263  	} else {
   264  		parentName = tree.MakeTableName(tree.Name(""), tree.Name(fmt.Sprintf("[%d as parent]", parentTableID)))
   265  		parentName.ExplicitCatalog = false
   266  		parentName.ExplicitSchema = false
   267  	}
   268  	var sharedPrefixLen int
   269  	for _, ancestor := range intl.Ancestors {
   270  		sharedPrefixLen += int(ancestor.SharedPrefixLen)
   271  	}
   272  	buf.WriteString(" INTERLEAVE IN PARENT ")
   273  	fmtCtx := tree.NewFmtCtx(tree.FmtSimple)
   274  	fmtCtx.FormatNode(&parentName)
   275  	buf.WriteString(fmtCtx.CloseAndGetString())
   276  	buf.WriteString(" (")
   277  	formatQuoteNames(buf, idx.ColumnNames[:sharedPrefixLen]...)
   278  	buf.WriteString(")")
   279  	return nil
   280  }
   281  
   282  // ShowCreatePartitioning returns a PARTITION BY clause for the specified
   283  // index, if applicable.
   284  func ShowCreatePartitioning(
   285  	a *sqlbase.DatumAlloc,
   286  	codec keys.SQLCodec,
   287  	tableDesc *sqlbase.TableDescriptor,
   288  	idxDesc *sqlbase.IndexDescriptor,
   289  	partDesc *sqlbase.PartitioningDescriptor,
   290  	buf *bytes.Buffer,
   291  	indent int,
   292  	colOffset int,
   293  ) error {
   294  	if partDesc.NumColumns == 0 {
   295  		return nil
   296  	}
   297  
   298  	// We don't need real prefixes in the DecodePartitionTuple calls because we
   299  	// only use the tree.Datums part of the output.
   300  	fakePrefixDatums := make([]tree.Datum, colOffset)
   301  	for i := range fakePrefixDatums {
   302  		fakePrefixDatums[i] = tree.DNull
   303  	}
   304  
   305  	indentStr := strings.Repeat("\t", indent)
   306  	buf.WriteString(` PARTITION BY `)
   307  	if len(partDesc.List) > 0 {
   308  		buf.WriteString(`LIST`)
   309  	} else if len(partDesc.Range) > 0 {
   310  		buf.WriteString(`RANGE`)
   311  	} else {
   312  		return errors.Errorf(`invalid partition descriptor: %v`, partDesc)
   313  	}
   314  	buf.WriteString(` (`)
   315  	for i := 0; i < int(partDesc.NumColumns); i++ {
   316  		if i != 0 {
   317  			buf.WriteString(", ")
   318  		}
   319  		buf.WriteString(idxDesc.ColumnNames[colOffset+i])
   320  	}
   321  	buf.WriteString(`) (`)
   322  	fmtCtx := tree.NewFmtCtx(tree.FmtSimple)
   323  	for i := range partDesc.List {
   324  		part := &partDesc.List[i]
   325  		if i != 0 {
   326  			buf.WriteString(`, `)
   327  		}
   328  		buf.WriteString("\n")
   329  		buf.WriteString(indentStr)
   330  		buf.WriteString("\tPARTITION ")
   331  		fmtCtx.FormatNameP(&part.Name)
   332  		_, _ = fmtCtx.Buffer.WriteTo(buf)
   333  		buf.WriteString(` VALUES IN (`)
   334  		for j, values := range part.Values {
   335  			if j != 0 {
   336  				buf.WriteString(`, `)
   337  			}
   338  			tuple, _, err := sqlbase.DecodePartitionTuple(
   339  				a, codec, tableDesc, idxDesc, partDesc, values, fakePrefixDatums)
   340  			if err != nil {
   341  				return err
   342  			}
   343  			buf.WriteString(tuple.String())
   344  		}
   345  		buf.WriteString(`)`)
   346  		if err := ShowCreatePartitioning(
   347  			a, codec, tableDesc, idxDesc, &part.Subpartitioning, buf, indent+1,
   348  			colOffset+int(partDesc.NumColumns),
   349  		); err != nil {
   350  			return err
   351  		}
   352  	}
   353  	for i, part := range partDesc.Range {
   354  		if i != 0 {
   355  			buf.WriteString(`, `)
   356  		}
   357  		buf.WriteString("\n")
   358  		buf.WriteString(indentStr)
   359  		buf.WriteString("\tPARTITION ")
   360  		buf.WriteString(part.Name)
   361  		buf.WriteString(" VALUES FROM ")
   362  		fromTuple, _, err := sqlbase.DecodePartitionTuple(
   363  			a, codec, tableDesc, idxDesc, partDesc, part.FromInclusive, fakePrefixDatums)
   364  		if err != nil {
   365  			return err
   366  		}
   367  		buf.WriteString(fromTuple.String())
   368  		buf.WriteString(" TO ")
   369  		toTuple, _, err := sqlbase.DecodePartitionTuple(
   370  			a, codec, tableDesc, idxDesc, partDesc, part.ToExclusive, fakePrefixDatums)
   371  		if err != nil {
   372  			return err
   373  		}
   374  		buf.WriteString(toTuple.String())
   375  	}
   376  	buf.WriteString("\n")
   377  	buf.WriteString(indentStr)
   378  	buf.WriteString(")")
   379  	return nil
   380  }
   381  
   382  // showConstraintClause creates the CONSTRAINT clauses for a CREATE statement,
   383  // writing them to tree.FmtCtx f
   384  func showConstraintClause(
   385  	ctx context.Context, desc *sqlbase.TableDescriptor, semaCtx *tree.SemaContext, f *tree.FmtCtx,
   386  ) error {
   387  	for _, e := range desc.AllActiveAndInactiveChecks() {
   388  		if e.Hidden {
   389  			continue
   390  		}
   391  		f.WriteString(",\n\t")
   392  		if len(e.Name) > 0 {
   393  			f.WriteString("CONSTRAINT ")
   394  			formatQuoteNames(&f.Buffer, e.Name)
   395  			f.WriteString(" ")
   396  		}
   397  		f.WriteString("CHECK (")
   398  		typed, err := schemaexpr.DeserializeTableDescExpr(ctx, semaCtx, desc, e.Expr)
   399  		if err != nil {
   400  			return err
   401  		}
   402  		f.WriteString(tree.SerializeForDisplay(typed))
   403  		f.WriteString(")")
   404  	}
   405  	f.WriteString("\n)")
   406  	return nil
   407  }