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  }