vitess.io/vitess@v0.16.2/go/vt/schemadiff/schema.go (about) 1 /* 2 Copyright 2022 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package schemadiff 18 19 import ( 20 "bytes" 21 "errors" 22 "fmt" 23 "io" 24 "sort" 25 "strings" 26 27 "vitess.io/vitess/go/vt/sqlparser" 28 ) 29 30 // Schema represents a database schema, which may contain entities such as tables and views. 31 // Schema is not in itself an Entity, since it is more of a collection of entities. 32 type Schema struct { 33 tables []*CreateTableEntity 34 views []*CreateViewEntity 35 36 named map[string]Entity 37 sorted []Entity 38 39 foreignKeyParents []*CreateTableEntity // subset of tables 40 foreignKeyChildren []*CreateTableEntity // subset of tables 41 } 42 43 // newEmptySchema is used internally to initialize a Schema object 44 func newEmptySchema() *Schema { 45 schema := &Schema{ 46 tables: []*CreateTableEntity{}, 47 views: []*CreateViewEntity{}, 48 named: map[string]Entity{}, 49 sorted: []Entity{}, 50 51 foreignKeyParents: []*CreateTableEntity{}, 52 foreignKeyChildren: []*CreateTableEntity{}, 53 } 54 return schema 55 } 56 57 // NewSchemaFromEntities creates a valid and normalized schema based on list of entities 58 func NewSchemaFromEntities(entities []Entity) (*Schema, error) { 59 schema := newEmptySchema() 60 for _, e := range entities { 61 switch c := e.(type) { 62 case *CreateTableEntity: 63 schema.tables = append(schema.tables, c) 64 case *CreateViewEntity: 65 schema.views = append(schema.views, c) 66 default: 67 return nil, &UnsupportedEntityError{Entity: c.Name(), Statement: c.Create().CanonicalStatementString()} 68 } 69 } 70 if err := schema.normalize(); err != nil { 71 return nil, err 72 } 73 return schema, nil 74 } 75 76 // NewSchemaFromStatements creates a valid and normalized schema based on list of valid statements 77 func NewSchemaFromStatements(statements []sqlparser.Statement) (*Schema, error) { 78 entities := make([]Entity, 0, len(statements)) 79 for _, s := range statements { 80 switch stmt := s.(type) { 81 case *sqlparser.CreateTable: 82 c, err := NewCreateTableEntity(stmt) 83 if err != nil { 84 return nil, err 85 } 86 entities = append(entities, c) 87 case *sqlparser.CreateView: 88 v, err := NewCreateViewEntity(stmt) 89 if err != nil { 90 return nil, err 91 } 92 entities = append(entities, v) 93 default: 94 return nil, &UnsupportedStatementError{Statement: sqlparser.CanonicalString(s)} 95 } 96 } 97 return NewSchemaFromEntities(entities) 98 } 99 100 // NewSchemaFromQueries creates a valid and normalized schema based on list of queries 101 func NewSchemaFromQueries(queries []string) (*Schema, error) { 102 statements := make([]sqlparser.Statement, 0, len(queries)) 103 for _, q := range queries { 104 stmt, err := sqlparser.ParseStrictDDL(q) 105 if err != nil { 106 return nil, err 107 } 108 statements = append(statements, stmt) 109 } 110 return NewSchemaFromStatements(statements) 111 } 112 113 // NewSchemaFromSQL creates a valid and normalized schema based on a SQL blob that contains 114 // CREATE statements for various objects (tables, views) 115 func NewSchemaFromSQL(sql string) (*Schema, error) { 116 var statements []sqlparser.Statement 117 tokenizer := sqlparser.NewStringTokenizer(sql) 118 for { 119 stmt, err := sqlparser.ParseNextStrictDDL(tokenizer) 120 if err != nil { 121 if errors.Is(err, io.EOF) { 122 break 123 } 124 return nil, fmt.Errorf("could not parse statement in SQL: %v: %w", sql, err) 125 } 126 statements = append(statements, stmt) 127 } 128 return NewSchemaFromStatements(statements) 129 } 130 131 // getForeignKeyParentTableNames analyzes a CREATE TABLE definition and extracts all referened foreign key tables names. 132 // A table name may appear twice in the result output, it it is referenced by more than one foreign key 133 func getForeignKeyParentTableNames(createTable *sqlparser.CreateTable) (names []string, err error) { 134 for _, cs := range createTable.TableSpec.Constraints { 135 if check, ok := cs.Details.(*sqlparser.ForeignKeyDefinition); ok { 136 parentTableName := check.ReferenceDefinition.ReferencedTable.Name.String() 137 names = append(names, parentTableName) 138 } 139 } 140 return names, err 141 } 142 143 // getViewDependentTableNames analyzes a CREATE VIEW definition and extracts all tables/views read by this view 144 func getViewDependentTableNames(createView *sqlparser.CreateView) (names []string, err error) { 145 err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { 146 switch node := node.(type) { 147 case *sqlparser.TableName: 148 names = append(names, node.Name.String()) 149 case *sqlparser.AliasedTableExpr: 150 if tableName, ok := node.Expr.(sqlparser.TableName); ok { 151 names = append(names, tableName.Name.String()) 152 } 153 // or, this could be a more complex expression, like a derived table `(select * from v1) as derived`, 154 // in which case further Walk-ing will eventually find the "real" table name 155 } 156 return true, nil 157 }, createView) 158 return names, err 159 } 160 161 // normalize is called as part of Schema creation process. The user may only get a hold of normalized schema. 162 // It validates some cross-entity constraints, and orders entity based on dependencies (e.g. tables, views that read from tables, 2nd level views, etc.) 163 func (s *Schema) normalize() error { 164 s.named = make(map[string]Entity, len(s.tables)+len(s.views)) 165 s.sorted = make([]Entity, 0, len(s.tables)+len(s.views)) 166 // Verify no two entities share same name 167 for _, t := range s.tables { 168 name := t.Name() 169 if _, ok := s.named[name]; ok { 170 return &ApplyDuplicateEntityError{Entity: name} 171 } 172 s.named[name] = t 173 } 174 for _, v := range s.views { 175 name := v.Name() 176 if _, ok := s.named[name]; ok { 177 return &ApplyDuplicateEntityError{Entity: name} 178 } 179 s.named[name] = v 180 } 181 182 // Generally speaking, we want tables and views to be sorted alphabetically 183 sort.SliceStable(s.tables, func(i, j int) bool { 184 return s.tables[i].Name() < s.tables[j].Name() 185 }) 186 sort.SliceStable(s.views, func(i, j int) bool { 187 return s.views[i].Name() < s.views[j].Name() 188 }) 189 190 // More importantly, we want tables and views to be sorted in applicable order. 191 // For example, if a view v reads from table t, then t must be defined before v. 192 // We actually prioritise all tables first, then views. 193 // If a view v1 depends on v2, then v2 must come before v1, even though v1 194 // precedes v2 alphabetically 195 dependencyLevels := make(map[string]int, len(s.tables)+len(s.views)) 196 197 allNamesFoundInLowerLevel := func(names []string, level int) bool { 198 for _, name := range names { 199 dependencyLevel, ok := dependencyLevels[name] 200 if !ok { 201 if strings.ToLower(name) != "dual" { 202 // named table is not yet handled. This means this view cannot be defined yet. 203 return false 204 } 205 } 206 if dependencyLevel >= level { 207 // named table/view is in same dependency level as this view; we want to postpone this 208 // view for s future iteration because we want to first maintain alphabetical ordering. 209 return false 210 } 211 } 212 return true 213 } 214 215 // We now iterate all tables. We iterate "dependency levels": 216 // - first we want all tables that don't have foreign keys or which only reference themselves 217 // - then we only want tables that reference 1st level tables. these are 2nd level tables 218 // - etc. 219 // we stop when we have been unable to find a table in an iteration. 220 fkParents := map[string]bool{} 221 iterationLevel := 0 222 for { 223 handledAnyTablesInIteration := false 224 for _, t := range s.tables { 225 name := t.Name() 226 if _, ok := dependencyLevels[name]; ok { 227 // already handled; skip 228 continue 229 } 230 // Not handled. Is this view dependent on already handled objects? 231 referencedTableNames, err := getForeignKeyParentTableNames(t.CreateTable) 232 if err != nil { 233 return err 234 } 235 if len(referencedTableNames) > 0 { 236 s.foreignKeyChildren = append(s.foreignKeyChildren, t) 237 } 238 nonSelfReferenceNames := []string{} 239 for _, referencedTableName := range referencedTableNames { 240 if referencedTableName != name { 241 nonSelfReferenceNames = append(nonSelfReferenceNames, referencedTableName) 242 } 243 fkParents[referencedTableName] = true 244 } 245 if allNamesFoundInLowerLevel(nonSelfReferenceNames, iterationLevel) { 246 s.sorted = append(s.sorted, t) 247 dependencyLevels[t.Name()] = iterationLevel 248 handledAnyTablesInIteration = true 249 } 250 } 251 if !handledAnyTablesInIteration { 252 break 253 } 254 iterationLevel++ 255 } 256 for _, t := range s.tables { 257 if fkParents[t.Name()] { 258 s.foreignKeyParents = append(s.foreignKeyParents, t) 259 } 260 } 261 // We now iterate all views. We iterate "dependency levels": 262 // - first we want all views that only depend on tables. These are 1st level views. 263 // - then we only want views that depend on 1st level views or on tables. These are 2nd level views. 264 // - etc. 265 // we stop when we have been unable to find a view in an iteration. 266 267 // It's possible that there's never been any tables in this schema. Which means 268 // iterationLevel remains zero. 269 // To deal with views, we must have iterationLevel at least 1. This is because any view reads 270 // from _something_: at the very least it reads from DUAL (inplicitly or explicitly). Which 271 // puts the view at a higher level. 272 if iterationLevel < 1 { 273 iterationLevel = 1 274 } 275 for { 276 handledAnyViewsInIteration := false 277 for _, v := range s.views { 278 name := v.Name() 279 if _, ok := dependencyLevels[name]; ok { 280 // already handled; skip 281 continue 282 } 283 // Not handled. Is this view dependent on already handled objects? 284 dependentNames, err := getViewDependentTableNames(v.CreateView) 285 if err != nil { 286 return err 287 } 288 if allNamesFoundInLowerLevel(dependentNames, iterationLevel) { 289 s.sorted = append(s.sorted, v) 290 dependencyLevels[v.Name()] = iterationLevel 291 handledAnyViewsInIteration = true 292 } 293 } 294 if !handledAnyViewsInIteration { 295 break 296 } 297 iterationLevel++ 298 } 299 if len(s.sorted) != len(s.tables)+len(s.views) { 300 // We have leftover tables or views. This can happen if the schema definition is invalid: 301 // - a table's foreign key references a nonexistent table 302 // - two or more tables have circular FK dependency 303 // - a view depends on a nonexistent table 304 // - two or more views have a circular dependency 305 for _, t := range s.tables { 306 if _, ok := dependencyLevels[t.Name()]; !ok { 307 // We _know_ that in this iteration, at least one view is found unassigned a dependency level. 308 // We return the first one. 309 return &ForeignKeyDependencyUnresolvedError{Table: t.Name()} 310 } 311 } 312 for _, v := range s.views { 313 if _, ok := dependencyLevels[v.Name()]; !ok { 314 // We _know_ that in this iteration, at least one view is found unassigned a dependency level. 315 // We return the first one. 316 return &ViewDependencyUnresolvedError{View: v.ViewName.Name.String()} 317 } 318 } 319 } 320 321 // Validate table definitions 322 for _, t := range s.tables { 323 if err := t.validate(); err != nil { 324 return err 325 } 326 } 327 colTypeEqualForForeignKey := func(a, b *sqlparser.ColumnType) bool { 328 return a.Type == b.Type && 329 a.Unsigned == b.Unsigned && 330 a.Zerofill == b.Zerofill && 331 sqlparser.Equals.ColumnCharset(a.Charset, b.Charset) && 332 sqlparser.Equals.SliceOfString(a.EnumValues, b.EnumValues) 333 } 334 335 // Now validate foreign key columns: 336 // - referenced table columns must exist 337 // - foreign key columns must match in count and type to referenced table columns 338 // - referenced table has an appropriate index over referenced columns 339 for _, t := range s.tables { 340 if len(t.TableSpec.Constraints) == 0 { 341 continue 342 } 343 344 tableColumns := map[string]*sqlparser.ColumnDefinition{} 345 for _, col := range t.CreateTable.TableSpec.Columns { 346 colName := col.Name.Lowered() 347 tableColumns[colName] = col 348 } 349 350 for _, cs := range t.TableSpec.Constraints { 351 check, ok := cs.Details.(*sqlparser.ForeignKeyDefinition) 352 if !ok { 353 continue 354 } 355 referencedTableName := check.ReferenceDefinition.ReferencedTable.Name.String() 356 referencedTable := s.Table(referencedTableName) // we know this exists because we validated foreign key dependencies earlier on 357 358 referencedColumns := map[string]*sqlparser.ColumnDefinition{} 359 for _, col := range referencedTable.CreateTable.TableSpec.Columns { 360 colName := col.Name.Lowered() 361 referencedColumns[colName] = col 362 } 363 // Thanks to table validation, we already know the foreign key covered columns count is equal to the 364 // referenced table column count. Now ensure their types are identical 365 for i, col := range check.Source { 366 coveredColumn, ok := tableColumns[col.Lowered()] 367 if !ok { 368 return &InvalidColumnInForeignKeyConstraintError{Table: t.Name(), Constraint: cs.Name.String(), Column: col.String()} 369 } 370 referencedColumnName := check.ReferenceDefinition.ReferencedColumns[i].Lowered() 371 referencedColumn, ok := referencedColumns[referencedColumnName] 372 if !ok { 373 return &InvalidReferencedColumnInForeignKeyConstraintError{Table: t.Name(), Constraint: cs.Name.String(), ReferencedTable: referencedTableName, ReferencedColumn: referencedColumnName} 374 } 375 if !colTypeEqualForForeignKey(coveredColumn.Type, referencedColumn.Type) { 376 return &ForeignKeyColumnTypeMismatchError{Table: t.Name(), Constraint: cs.Name.String(), Column: coveredColumn.Name.String(), ReferencedTable: referencedTableName, ReferencedColumn: referencedColumnName} 377 } 378 } 379 380 if !referencedTable.columnsCoveredByInOrderIndex(check.ReferenceDefinition.ReferencedColumns) { 381 return &MissingForeignKeyReferencedIndexError{Table: t.Name(), Constraint: cs.Name.String(), ReferencedTable: referencedTableName} 382 } 383 } 384 } 385 return nil 386 } 387 388 // Entities returns this schema's entities in good order (may be applied without error) 389 func (s *Schema) Entities() []Entity { 390 return s.sorted 391 } 392 393 // EntityNames is a convenience function that returns just the names of entities, in good order 394 func (s *Schema) EntityNames() []string { 395 var names []string 396 for _, e := range s.Entities() { 397 names = append(names, e.Name()) 398 } 399 return names 400 } 401 402 // Tables returns this schema's tables in good order (may be applied without error) 403 func (s *Schema) Tables() []*CreateTableEntity { 404 var tables []*CreateTableEntity 405 for _, entity := range s.sorted { 406 if table, ok := entity.(*CreateTableEntity); ok { 407 tables = append(tables, table) 408 } 409 } 410 return tables 411 } 412 413 // TableNames is a convenience function that returns just the names of tables, in good order 414 func (s *Schema) TableNames() []string { 415 var names []string 416 for _, e := range s.Tables() { 417 names = append(names, e.Name()) 418 } 419 return names 420 } 421 422 // Views returns this schema's views in good order (may be applied without error) 423 func (s *Schema) Views() []*CreateViewEntity { 424 var views []*CreateViewEntity 425 for _, entity := range s.sorted { 426 if view, ok := entity.(*CreateViewEntity); ok { 427 views = append(views, view) 428 } 429 } 430 return views 431 } 432 433 // ViewNames is a convenience function that returns just the names of views, in good order 434 func (s *Schema) ViewNames() []string { 435 var names []string 436 for _, e := range s.Views() { 437 names = append(names, e.Name()) 438 } 439 return names 440 } 441 442 // Diff compares this schema with another schema, and sees what it takes to make this schema look 443 // like the other. It returns a list of diffs. 444 func (s *Schema) Diff(other *Schema, hints *DiffHints) (diffs []EntityDiff, err error) { 445 // dropped entities 446 var dropDiffs []EntityDiff 447 for _, e := range s.Entities() { 448 if _, ok := other.named[e.Name()]; !ok { 449 // other schema does not have the entity 450 dropDiffs = append(dropDiffs, e.Drop()) 451 } 452 } 453 // We iterate by order of "other" schema because we need to construct queries that will be valid 454 // for that schema (we need to maintain view dependencies according to target, not according to source) 455 var alterDiffs []EntityDiff 456 var createDiffs []EntityDiff 457 for _, e := range other.Entities() { 458 if fromEntity, ok := s.named[e.Name()]; ok { 459 // entities exist by same name in both schemas. Let's diff them. 460 diff, err := fromEntity.Diff(e, hints) 461 462 switch { 463 case err != nil && errors.Is(err, ErrEntityTypeMismatch): 464 // e.g. comparing a table with a view 465 // there's no single "diff", ie no single ALTER statement to convert from one to another, 466 // hence the error. 467 // But in our schema context, we know better. We know we should DROP the one, CREATE the other. 468 // We proceed to do that, and implicitly ignore the error 469 dropDiffs = append(dropDiffs, fromEntity.Drop()) 470 createDiffs = append(createDiffs, e.Create()) 471 // And we're good. We can move on to comparing next entity. 472 case err != nil: 473 // Any other kind of error 474 return nil, err 475 default: 476 // No error, let's check the diff: 477 if diff != nil && !diff.IsEmpty() { 478 alterDiffs = append(alterDiffs, diff) 479 } 480 } 481 } else { // !ok 482 // Added entity 483 // this schema does not have the entity 484 createDiffs = append(createDiffs, e.Create()) 485 } 486 } 487 dropDiffs, createDiffs, renameDiffs := s.heuristicallyDetectTableRenames(dropDiffs, createDiffs, hints) 488 diffs = append(diffs, dropDiffs...) 489 diffs = append(diffs, alterDiffs...) 490 diffs = append(diffs, createDiffs...) 491 diffs = append(diffs, renameDiffs...) 492 493 return diffs, err 494 } 495 496 func (s *Schema) heuristicallyDetectTableRenames( 497 dropDiffs []EntityDiff, 498 createDiffs []EntityDiff, 499 hints *DiffHints, 500 ) ( 501 updatedDropDiffs []EntityDiff, 502 updatedCreateDiffs []EntityDiff, 503 renameDiffs []EntityDiff, 504 ) { 505 renameDiffs = []EntityDiff{} 506 507 findRenamedTable := func() bool { 508 // What we're doing next is to try and identify a table RENAME. 509 // We do so by cross-referencing dropped and created tables. 510 // The check is heuristic, and looks like this: 511 // We consider a table renamed iff: 512 // - the DROP and CREATE table definitions are identical other than the table name 513 // In the case where multiple dropped tables have identical schema, and likewise multiple created tables 514 // have identical schemas, schemadiff makes an arbitrary match. 515 // Once we heuristically decide that we found a RENAME, we cancel the DROP, 516 // cancel the CREATE, and inject a RENAME in place of both. 517 518 // findRenamedTable cross-references dropped and created tables to find a single renamed table. If such is found: 519 // we remove the entry from DROPped tables, remove the entry from CREATEd tables, add an entry for RENAMEd tables, 520 // and return 'true'. 521 // Successive calls to this function will then find the next heuristic RENAMEs. 522 // the function returns 'false' if it is unable to heuristically find a RENAME. 523 for iDrop, drop1 := range dropDiffs { 524 for iCreate, create2 := range createDiffs { 525 dropTableDiff, ok := drop1.(*DropTableEntityDiff) 526 if !ok { 527 continue 528 } 529 createTableDiff, ok := create2.(*CreateTableEntityDiff) 530 if !ok { 531 continue 532 } 533 if !dropTableDiff.from.identicalOtherThanName(createTableDiff.to) { 534 continue 535 } 536 // Yes, it looks like those tables have the exact same spec, just with different names. 537 dropDiffs = append(dropDiffs[0:iDrop], dropDiffs[iDrop+1:]...) 538 createDiffs = append(createDiffs[0:iCreate], createDiffs[iCreate+1:]...) 539 renameTable := &sqlparser.RenameTable{ 540 TablePairs: []*sqlparser.RenameTablePair{ 541 {FromTable: dropTableDiff.from.Table, ToTable: createTableDiff.to.Table}, 542 }, 543 } 544 renameTableEntityDiff := &RenameTableEntityDiff{ 545 from: dropTableDiff.from, 546 to: createTableDiff.to, 547 renameTable: renameTable, 548 } 549 renameDiffs = append(renameDiffs, renameTableEntityDiff) 550 return true 551 } 552 } 553 return false 554 } 555 switch hints.TableRenameStrategy { 556 case TableRenameAssumeDifferent: 557 // do nothing 558 case TableRenameHeuristicStatement: 559 for findRenamedTable() { 560 // Iteratively detect all RENAMEs 561 } 562 } 563 564 return dropDiffs, createDiffs, renameDiffs 565 } 566 567 // Entity returns an entity by name, or nil if nonexistent 568 func (s *Schema) Entity(name string) Entity { 569 return s.named[name] 570 } 571 572 // Table returns a table by name, or nil if nonexistent 573 func (s *Schema) Table(name string) *CreateTableEntity { 574 if table, ok := s.named[name].(*CreateTableEntity); ok { 575 return table 576 } 577 return nil 578 } 579 580 // View returns a view by name, or nil if nonexistent 581 func (s *Schema) View(name string) *CreateViewEntity { 582 if view, ok := s.named[name].(*CreateViewEntity); ok { 583 return view 584 } 585 return nil 586 } 587 588 // ToStatements returns an ordered list of statements which can be applied to create the schema 589 func (s *Schema) ToStatements() []sqlparser.Statement { 590 stmts := make([]sqlparser.Statement, 0, len(s.Entities())) 591 for _, e := range s.Entities() { 592 stmts = append(stmts, e.Create().Statement()) 593 } 594 return stmts 595 } 596 597 // ToQueries returns an ordered list of queries which can be applied to create the schema 598 func (s *Schema) ToQueries() []string { 599 queries := make([]string, 0, len(s.Entities())) 600 for _, e := range s.Entities() { 601 queries = append(queries, e.Create().CanonicalStatementString()) 602 } 603 return queries 604 } 605 606 // ToSQL returns a SQL blob with ordered sequence of queries which can be applied to create the schema 607 func (s *Schema) ToSQL() string { 608 var buf bytes.Buffer 609 for _, query := range s.ToQueries() { 610 buf.WriteString(query) 611 buf.WriteString(";\n") 612 } 613 return buf.String() 614 } 615 616 // copy returns a shallow copy of the schema. This is used when applying changes for example. 617 // applying changes will ensure we copy new entities themselves separately. 618 func (s *Schema) copy() *Schema { 619 dup := newEmptySchema() 620 dup.tables = make([]*CreateTableEntity, len(s.tables)) 621 copy(dup.tables, s.tables) 622 dup.views = make([]*CreateViewEntity, len(s.views)) 623 copy(dup.views, s.views) 624 dup.named = make(map[string]Entity, len(s.named)) 625 for k, v := range s.named { 626 dup.named[k] = v 627 } 628 dup.sorted = make([]Entity, len(s.sorted)) 629 copy(dup.sorted, s.sorted) 630 return dup 631 } 632 633 // apply attempts to apply given list of diffs to this object. 634 // These diffs are CREATE/DROP/ALTER TABLE/VIEW. 635 func (s *Schema) apply(diffs []EntityDiff) error { 636 for _, diff := range diffs { 637 switch diff := diff.(type) { 638 case *CreateTableEntityDiff: 639 // We expect the table to not exist 640 name := diff.createTable.Table.Name.String() 641 if _, ok := s.named[name]; ok { 642 return &ApplyDuplicateEntityError{Entity: name} 643 } 644 s.tables = append(s.tables, &CreateTableEntity{CreateTable: diff.createTable}) 645 _, s.named[name] = diff.Entities() 646 case *CreateViewEntityDiff: 647 // We expect the view to not exist 648 name := diff.createView.ViewName.Name.String() 649 if _, ok := s.named[name]; ok { 650 return &ApplyDuplicateEntityError{Entity: name} 651 } 652 s.views = append(s.views, &CreateViewEntity{CreateView: diff.createView}) 653 _, s.named[name] = diff.Entities() 654 case *DropTableEntityDiff: 655 // We expect the table to exist 656 found := false 657 for i, t := range s.tables { 658 if name := t.Table.Name.String(); name == diff.from.Table.Name.String() { 659 s.tables = append(s.tables[0:i], s.tables[i+1:]...) 660 delete(s.named, name) 661 found = true 662 break 663 } 664 } 665 if !found { 666 return &ApplyTableNotFoundError{Table: diff.from.Table.Name.String()} 667 } 668 case *DropViewEntityDiff: 669 // We expect the view to exist 670 found := false 671 for i, v := range s.views { 672 if name := v.ViewName.Name.String(); name == diff.from.ViewName.Name.String() { 673 s.views = append(s.views[0:i], s.views[i+1:]...) 674 delete(s.named, name) 675 found = true 676 break 677 } 678 } 679 if !found { 680 return &ApplyViewNotFoundError{View: diff.from.ViewName.Name.String()} 681 } 682 case *AlterTableEntityDiff: 683 // We expect the table to exist 684 found := false 685 for i, t := range s.tables { 686 if name := t.Table.Name.String(); name == diff.from.Table.Name.String() { 687 to, err := t.Apply(diff) 688 if err != nil { 689 return err 690 } 691 toCreateTableEntity, ok := to.(*CreateTableEntity) 692 if !ok { 693 return ErrEntityTypeMismatch 694 } 695 s.tables[i] = toCreateTableEntity 696 s.named[name] = toCreateTableEntity 697 found = true 698 break 699 } 700 } 701 if !found { 702 return &ApplyTableNotFoundError{Table: diff.from.Table.Name.String()} 703 } 704 case *AlterViewEntityDiff: 705 // We expect the view to exist 706 found := false 707 for i, v := range s.views { 708 if name := v.ViewName.Name.String(); name == diff.from.ViewName.Name.String() { 709 to, err := v.Apply(diff) 710 if err != nil { 711 return err 712 } 713 toCreateViewEntity, ok := to.(*CreateViewEntity) 714 if !ok { 715 return ErrEntityTypeMismatch 716 } 717 s.views[i] = toCreateViewEntity 718 s.named[name] = toCreateViewEntity 719 found = true 720 break 721 } 722 } 723 if !found { 724 return &ApplyViewNotFoundError{View: diff.from.ViewName.Name.String()} 725 } 726 case *RenameTableEntityDiff: 727 // We expect the table to exist 728 found := false 729 for i, t := range s.tables { 730 if name := t.Table.Name.String(); name == diff.from.Table.Name.String() { 731 s.tables[i] = diff.to 732 delete(s.named, name) 733 s.named[diff.to.Table.Name.String()] = diff.to 734 found = true 735 break 736 } 737 } 738 if !found { 739 return &ApplyTableNotFoundError{Table: diff.from.Table.Name.String()} 740 } 741 default: 742 return &UnsupportedApplyOperationError{Statement: diff.CanonicalStatementString()} 743 } 744 } 745 if err := s.normalize(); err != nil { 746 return err 747 } 748 return nil 749 } 750 751 // Apply attempts to apply given list of diffs to the schema described by this object. 752 // These diffs are CREATE/DROP/ALTER TABLE/VIEW. 753 // The operation does not modify this object. Instead, if successful, a new (modified) Schema is returned. 754 func (s *Schema) Apply(diffs []EntityDiff) (*Schema, error) { 755 dup := s.copy() 756 for k, v := range s.named { 757 dup.named[k] = v 758 } 759 if err := dup.apply(diffs); err != nil { 760 return nil, err 761 } 762 return dup, nil 763 }