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 }