diff --git a/go/vt/wrangler/materializer.go b/go/vt/wrangler/materializer.go index 302ae4a0eac..67f4476adbb 100644 --- a/go/vt/wrangler/materializer.go +++ b/go/vt/wrangler/materializer.go @@ -53,7 +53,8 @@ type materializer struct { } const ( - createDDLAsCopy = "copy" + createDDLAsCopy = "copy" + createDDLAsCopyDropConstraint = "copy:drop_constraint" ) // MoveTables initiates moving table(s) over to another keyspace @@ -642,7 +643,7 @@ func (mz *materializer) deploySchema(ctx context.Context) error { return fmt.Errorf("target table %v does not exist and there is no create ddl defined", ts.TargetTable) } createDDL := ts.CreateDdl - if createDDL == createDDLAsCopy { + if createDDL == createDDLAsCopy || createDDL == createDDLAsCopyDropConstraint { if ts.SourceExpression != "" { // Check for table if non-empty SourceExpression. sourceTableName, err := sqlparser.TableFromStatement(ts.SourceExpression) @@ -653,13 +654,21 @@ func (mz *materializer) deploySchema(ctx context.Context) error { return fmt.Errorf("source and target table names must match for copying schema: %v vs %v", sqlparser.String(sourceTableName), ts.TargetTable) } - } ddl, ok := sourceDDL[ts.TargetTable] if !ok { return fmt.Errorf("source table %v does not exist", ts.TargetTable) } + + if createDDL == createDDLAsCopyDropConstraint { + strippedDDL, err := stripTableConstraints(ddl) + if err != nil { + return err + } + + ddl = strippedDDL + } createDDL = ddl } @@ -667,10 +676,9 @@ func (mz *materializer) deploySchema(ctx context.Context) error { } if len(applyDDLs) > 0 { - sql := strings.Join(applyDDLs, ";\n") - log.Infof("applying schema to target tablet %v...", target.MasterAlias) + log.Infof("applying schema to target tablet %v, sql: %s", target.MasterAlias, sql) _, err = mz.wr.tmc.ApplySchema(ctx, targetTablet.Tablet, &tmutils.SchemaChange{ SQL: sql, Force: false, @@ -686,6 +694,28 @@ func (mz *materializer) deploySchema(ctx context.Context) error { }) } +func stripTableConstraints(ddl string) (string, error) { + ast, err := sqlparser.ParseStrictDDL(ddl) + if err != nil { + return "", err + } + + stripConstraints := func(cursor *sqlparser.Cursor) bool { + switch node := cursor.Node().(type) { + case *sqlparser.DDL: + if node.TableSpec != nil { + node.TableSpec.Constraints = nil + } + } + return true + } + + noConstraintAST := sqlparser.Rewrite(ast, stripConstraints, nil) + newDDL := sqlparser.String(noConstraintAST) + + return newDDL, nil +} + func (mz *materializer) generateInserts(ctx context.Context) (string, error) { ig := vreplication.NewInsertGenerator(binlogplayer.BlpStopped, "{{.dbname}}") diff --git a/go/vt/wrangler/materializer_test.go b/go/vt/wrangler/materializer_test.go index 6e8b5496029..2b7f88f665d 100644 --- a/go/vt/wrangler/materializer_test.go +++ b/go/vt/wrangler/materializer_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/logutil" querypb "vitess.io/vitess/go/vt/proto/query" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" @@ -2182,3 +2183,75 @@ func TestMaterializerNoVindexInExpression(t *testing.T) { err := env.wr.Materialize(context.Background(), ms) assert.EqualError(t, err, "could not find vindex column c1") } + +func TestStripConstraints(t *testing.T) { + tcs := []struct { + desc string + ddl string + + hasErr bool + newDDL string + }{ + { + desc: "constraints", + ddl: "CREATE TABLE `table1` (\n" + + "`id` int(11) NOT NULL AUTO_INCREMENT,\n" + + "`foreign_id` int(11) NOT NULL,\n" + + "`user_id` int(11) NOT NULL,\n" + + "PRIMARY KEY (`id`),\n" + + "KEY `fk_table1_ref_foreign_id` (`foreign_id`),\n" + + "KEY `fk_table1_ref_user_id` (`user_id`),\n" + + "CONSTRAINT `fk_table1_ref_foreign_id` FOREIGN KEY (`foreign_id`) REFERENCES `foreign` (`id`),\n" + + "CONSTRAINT `fk_table1_ref_user_id` FOREIGN KEY (`user_id`) REFERENCES `core_user` (`id`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=latin1;", + + newDDL: "create table table1 (\n" + + "\tid int(11) not null auto_increment,\n" + + "\tforeign_id int(11) not null,\n" + + "\tuser_id int(11) not null,\n" + + "\tPRIMARY KEY (id),\n" + + "\tKEY fk_table1_ref_foreign_id (foreign_id),\n" + + "\tKEY fk_table1_ref_user_id (user_id)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=latin1", + + hasErr: false, + }, + { + desc: "no constraints", + ddl: "CREATE TABLE `table1` (\n" + + "`id` int(11) NOT NULL AUTO_INCREMENT,\n" + + "`foreign_id` int(11) NOT NULL,\n" + + "`user_id` int(11) NOT NULL,\n" + + "PRIMARY KEY (`id`),\n" + + "KEY `fk_table1_ref_foreign_id` (`foreign_id`),\n" + + "KEY `fk_table1_ref_user_id` (`user_id`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=latin1;", + + newDDL: "create table table1 (\n" + + "\tid int(11) not null auto_increment,\n" + + "\tforeign_id int(11) not null,\n" + + "\tuser_id int(11) not null,\n" + + "\tPRIMARY KEY (id),\n" + + "\tKEY fk_table1_ref_foreign_id (foreign_id),\n" + + "\tKEY fk_table1_ref_user_id (user_id)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=latin1", + }, + { + desc: "bad ddl has error", + ddl: "bad ddl", + + hasErr: true, + }, + } + + for _, tc := range tcs { + newDDL, err := stripTableConstraints(tc.ddl) + if tc.hasErr != (err != nil) { + t.Fatalf("hasErr does not match: err: %v, tc: %+v", err, tc) + } + + if newDDL != tc.newDDL { + utils.MustMatch(t, tc.newDDL, newDDL, fmt.Sprintf("newDDL does not match. tc: %+v", tc)) + } + } +}