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 }