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

     1  // Copyright 2017 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  
    17  	"github.com/cockroachdb/cockroach/pkg/sql/schemaexpr"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    20  )
    21  
    22  type shouldOmitFKClausesFromCreate int
    23  
    24  const (
    25  	_ shouldOmitFKClausesFromCreate = iota
    26  	// OmitFKClausesFromCreate will not include any foreign key information in the
    27  	// create statement.
    28  	OmitFKClausesFromCreate
    29  	// IncludeFkClausesInCreate will include foreign key information in the create
    30  	// statement, and error if a FK cannot be resolved.
    31  	IncludeFkClausesInCreate
    32  	// OmitMissingFKClausesFromCreate will include foreign key information only if they
    33  	// can be resolved. If not, it will ignore those constraints.
    34  	// This is used in the case when showing the create statement for
    35  	// tables stored in backups. Not all relevant tables may have been
    36  	// included in the back up, so some foreign key information may be
    37  	// impossible to retrieve.
    38  	OmitMissingFKClausesFromCreate
    39  )
    40  
    41  // ShowCreateDisplayOptions is a container struct holding the options that
    42  // ShowCreate uses to determine how much information should be included in the
    43  // CREATE statement.
    44  type ShowCreateDisplayOptions struct {
    45  	FKDisplayMode shouldOmitFKClausesFromCreate
    46  	// Comment resolution requires looking up table data from system.comments
    47  	// table. This is sometimes not possible. For example, in the context of a
    48  	// SHOW BACKUP which may resolve the create statement, there is no mechanism
    49  	// to read any table data from the backup (nor is there a guarantee that the
    50  	// system.comments table is included in the backup at all).
    51  	IgnoreComments bool
    52  }
    53  
    54  // ShowCreateTable returns a valid SQL representation of the CREATE
    55  // TABLE statement used to create the given table.
    56  //
    57  // The names of the tables references by foreign keys, and the
    58  // interleaved parent if any, are prefixed by their own database name
    59  // unless it is equal to the given dbPrefix. This allows us to elide
    60  // the prefix when the given table references other tables in the
    61  // current database.
    62  func ShowCreateTable(
    63  	ctx context.Context,
    64  	p PlanHookState,
    65  	tn *tree.Name,
    66  	dbPrefix string,
    67  	desc *sqlbase.TableDescriptor,
    68  	lCtx simpleSchemaResolver,
    69  	displayOptions ShowCreateDisplayOptions,
    70  ) (string, error) {
    71  	a := &sqlbase.DatumAlloc{}
    72  
    73  	f := tree.NewFmtCtx(tree.FmtSimple)
    74  	f.WriteString("CREATE ")
    75  	if desc.Temporary {
    76  		f.WriteString("TEMP ")
    77  	}
    78  	f.WriteString("TABLE ")
    79  	f.FormatNode(tn)
    80  	f.WriteString(" (")
    81  	primaryKeyIsOnVisibleColumn := false
    82  	visibleCols := desc.VisibleColumns()
    83  	for i := range visibleCols {
    84  		col := &visibleCols[i]
    85  		if i != 0 {
    86  			f.WriteString(",")
    87  		}
    88  		f.WriteString("\n\t")
    89  		colstr, err := schemaexpr.FormatColumnForDisplay(ctx, &p.RunParams(ctx).p.semaCtx, desc, col)
    90  		if err != nil {
    91  			return "", err
    92  		}
    93  		f.WriteString(colstr)
    94  		if desc.IsPhysicalTable() && desc.PrimaryIndex.ColumnIDs[0] == col.ID {
    95  			// Only set primaryKeyIsOnVisibleColumn to true if the primary key
    96  			// is on a visible column (not rowid).
    97  			primaryKeyIsOnVisibleColumn = true
    98  		}
    99  	}
   100  	if primaryKeyIsOnVisibleColumn ||
   101  		(desc.IsPhysicalTable() && desc.PrimaryIndex.IsSharded()) {
   102  		f.WriteString(",\n\tCONSTRAINT ")
   103  		formatQuoteNames(&f.Buffer, desc.PrimaryIndex.Name)
   104  		f.WriteString(" ")
   105  		f.WriteString(desc.PrimaryKeyString())
   106  	}
   107  	// TODO (lucy): Possibly include FKs in the mutations list here, or else
   108  	// exclude check mutations below, for consistency.
   109  	if displayOptions.FKDisplayMode != OmitFKClausesFromCreate {
   110  		for i := range desc.OutboundFKs {
   111  			fkCtx := tree.NewFmtCtx(tree.FmtSimple)
   112  			fk := &desc.OutboundFKs[i]
   113  			fkCtx.WriteString(",\n\tCONSTRAINT ")
   114  			fkCtx.FormatNameP(&fk.Name)
   115  			fkCtx.WriteString(" ")
   116  			if err := showForeignKeyConstraint(&fkCtx.Buffer, dbPrefix, desc, fk, lCtx); err != nil {
   117  				if displayOptions.FKDisplayMode == OmitMissingFKClausesFromCreate {
   118  					continue
   119  				} else { // When FKDisplayMode == IncludeFkClausesInCreate.
   120  					return "", err
   121  				}
   122  			}
   123  			f.WriteString(fkCtx.String())
   124  		}
   125  	}
   126  	allIdx := append(desc.Indexes, desc.PrimaryIndex)
   127  	for i := range allIdx {
   128  		idx := &allIdx[i]
   129  		// Only add indexes to the create_statement column, and not to the
   130  		// create_nofks column if they are not associated with an INTERLEAVE
   131  		// statement.
   132  		// Initialize to false if Interleave has no ancestors, indicating that the
   133  		// index is not interleaved at all.
   134  		includeInterleaveClause := len(idx.Interleave.Ancestors) == 0
   135  		if displayOptions.FKDisplayMode != OmitFKClausesFromCreate {
   136  			// The caller is instructing us to not omit FK clauses from inside the CREATE.
   137  			// (i.e. the caller does not want them as separate DDL.)
   138  			// Since we're including FK clauses, we need to also include the PARTITION and INTERLEAVE
   139  			// clauses as well.
   140  			includeInterleaveClause = true
   141  		}
   142  		if idx.ID != desc.PrimaryIndex.ID && includeInterleaveClause {
   143  			// Showing the primary index is handled above.
   144  			f.WriteString(",\n\t")
   145  			f.WriteString(idx.SQLString(&sqlbase.AnonymousTable))
   146  			// Showing the INTERLEAVE and PARTITION BY for the primary index are
   147  			// handled last.
   148  
   149  			// Add interleave or Foreign Key indexes only to the create_table columns,
   150  			// and not the create_nofks column.
   151  			if includeInterleaveClause {
   152  				if err := showCreateInterleave(idx, &f.Buffer, dbPrefix, lCtx); err != nil {
   153  					return "", err
   154  				}
   155  			}
   156  			if err := ShowCreatePartitioning(
   157  				a, p.ExecCfg().Codec, desc, idx, &idx.Partitioning, &f.Buffer, 1 /* indent */, 0, /* colOffset */
   158  			); err != nil {
   159  				return "", err
   160  			}
   161  		}
   162  	}
   163  
   164  	// Create the FAMILY and CONSTRAINTs of the CREATE statement
   165  	showFamilyClause(desc, f)
   166  	if err := showConstraintClause(ctx, desc, &p.RunParams(ctx).p.semaCtx, f); err != nil {
   167  		return "", err
   168  	}
   169  
   170  	if err := showCreateInterleave(&desc.PrimaryIndex, &f.Buffer, dbPrefix, lCtx); err != nil {
   171  		return "", err
   172  	}
   173  	if err := ShowCreatePartitioning(
   174  		a, p.ExecCfg().Codec, desc, &desc.PrimaryIndex, &desc.PrimaryIndex.Partitioning, &f.Buffer, 0 /* indent */, 0, /* colOffset */
   175  	); err != nil {
   176  		return "", err
   177  	}
   178  
   179  	if !displayOptions.IgnoreComments {
   180  		if err := showComments(desc, selectComment(ctx, p, desc.ID), &f.Buffer); err != nil {
   181  			return "", err
   182  		}
   183  	}
   184  
   185  	return f.CloseAndGetString(), nil
   186  }
   187  
   188  // formatQuoteNames quotes and adds commas between names.
   189  func formatQuoteNames(buf *bytes.Buffer, names ...string) {
   190  	f := tree.NewFmtCtx(tree.FmtSimple)
   191  	for i := range names {
   192  		if i > 0 {
   193  			f.WriteString(", ")
   194  		}
   195  		f.FormatNameP(&names[i])
   196  	}
   197  	buf.WriteString(f.CloseAndGetString())
   198  }
   199  
   200  // ShowCreate returns a valid SQL representation of the CREATE
   201  // statement used to create the descriptor passed in. The
   202  //
   203  // The names of the tables references by foreign keys, and the
   204  // interleaved parent if any, are prefixed by their own database name
   205  // unless it is equal to the given dbPrefix. This allows us to elide
   206  // the prefix when the given table references other tables in the
   207  // current database.
   208  func (p *planner) ShowCreate(
   209  	ctx context.Context,
   210  	dbPrefix string,
   211  	allDescs []sqlbase.Descriptor,
   212  	desc *sqlbase.TableDescriptor,
   213  	displayOptions ShowCreateDisplayOptions,
   214  ) (string, error) {
   215  	var stmt string
   216  	var err error
   217  	tn := (*tree.Name)(&desc.Name)
   218  	if desc.IsView() {
   219  		stmt, err = ShowCreateView(ctx, tn, desc)
   220  	} else if desc.IsSequence() {
   221  		stmt, err = ShowCreateSequence(ctx, tn, desc)
   222  	} else {
   223  		lCtx := newInternalLookupCtxFromDescriptors(allDescs, nil /* want all tables */)
   224  		stmt, err = ShowCreateTable(ctx, p, tn, dbPrefix, desc, lCtx, displayOptions)
   225  	}
   226  
   227  	return stmt, err
   228  }