// CopySchemaShard copies the schema from a source tablet to the // specified shard. The schema is applied directly on the master of // the destination shard, and is propogated to the replicas through // binlogs. func (wr *Wrangler) CopySchemaShard(ctx context.Context, sourceTabletAlias *topodatapb.TabletAlias, tables, excludeTables []string, includeViews bool, destKeyspace, destShard string) error { destShardInfo, err := wr.ts.GetShard(ctx, destKeyspace, destShard) if err != nil { return err } sourceSd, err := wr.GetSchema(ctx, sourceTabletAlias, tables, excludeTables, includeViews) if err != nil { return err } destSd, err := wr.GetSchema(ctx, destShardInfo.MasterAlias, tables, excludeTables, includeViews) if err != nil { destSd = nil } if destSd != nil { diffs := tmutils.DiffSchemaToArray("source", sourceSd, "dest", destSd) if diffs == nil { // Return early because dest has already the same schema as source. return nil } } createSQL := tmutils.SchemaDefinitionToSQLStrings(sourceSd) destTabletInfo, err := wr.ts.GetTablet(ctx, destShardInfo.MasterAlias) if err != nil { return err } for i, sqlLine := range createSQL { err = wr.applySQLShard(ctx, destTabletInfo, sqlLine, i == len(createSQL)-1) if err != nil { return err } } return nil }
// CopySchemaShard copies the schema from a source tablet to the // specified shard. The schema is applied directly on the master of // the destination shard, and is propogated to the replicas through // binlogs. func (wr *Wrangler) CopySchemaShard(ctx context.Context, sourceTabletAlias *topodatapb.TabletAlias, tables, excludeTables []string, includeViews bool, destKeyspace, destShard string, waitSlaveTimeout time.Duration) error { destShardInfo, err := wr.ts.GetShard(ctx, destKeyspace, destShard) if err != nil { return err } diffs, err := wr.compareSchemas(ctx, sourceTabletAlias, destShardInfo.MasterAlias, tables, excludeTables, includeViews) if err != nil { return fmt.Errorf("CopySchemaShard failed because schemas could not be compared initially: %v", err) } if diffs == nil { // Return early because dest has already the same schema as source. return nil } sourceSd, err := wr.GetSchema(ctx, sourceTabletAlias, tables, excludeTables, includeViews) if err != nil { return err } createSQL := tmutils.SchemaDefinitionToSQLStrings(sourceSd) destTabletInfo, err := wr.ts.GetTablet(ctx, destShardInfo.MasterAlias) if err != nil { return err } for i, sqlLine := range createSQL { err = wr.applySQLShard(ctx, destTabletInfo, sqlLine, i == len(createSQL)-1) if err != nil { return err } } // Remember the replication position after all the above were applied. destMasterPos, err := wr.tmc.MasterPosition(ctx, destTabletInfo.Tablet) if err != nil { return fmt.Errorf("CopySchemaShard: can't get replication position after schema applied: %v", err) } // Although the copy was successful, we have to verify it to catch the case // where the database already existed on the destination, but with different // options e.g. a different character set. // In that case, MySQL would have skipped our CREATE DATABASE IF NOT EXISTS // statement. We want to fail early in this case because vtworker SplitDiff // fails in case of such an inconsistency as well. diffs, err = wr.compareSchemas(ctx, sourceTabletAlias, destShardInfo.MasterAlias, tables, excludeTables, includeViews) if err != nil { return fmt.Errorf("CopySchemaShard failed because schemas could not be compared finally: %v", err) } if diffs != nil { return fmt.Errorf("CopySchemaShard was not successful because the schemas between the two tablets %v and %v differ: %v", sourceTabletAlias, destShardInfo.MasterAlias, diffs) } // Notify slaves to reload schema. This is best-effort. reloadCtx, cancel := context.WithTimeout(ctx, waitSlaveTimeout) defer cancel() wr.ReloadSchemaShard(reloadCtx, destKeyspace, destShard, destMasterPos) return nil }