From 4fb24656360beee0e6f8e50f965fd09f4dd87b9f Mon Sep 17 00:00:00 2001 From: Vivek Yadav Date: Fri, 22 Nov 2024 18:05:54 +0530 Subject: [PATCH 1/2] added the mysql dump flow --- sources/mysql/mysqldump.go | 47 ++++++++++++++++++++----- sources/mysql/mysqldump_test.go | 61 +++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 8 deletions(-) diff --git a/sources/mysql/mysqldump.go b/sources/mysql/mysqldump.go index 6145aee27..7246a6912 100644 --- a/sources/mysql/mysqldump.go +++ b/sources/mysql/mysqldump.go @@ -28,6 +28,7 @@ import ( "github.com/GoogleCloudPlatform/spanner-migration-tool/sources/common" "github.com/pingcap/tidb/parser" "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/parser/format" "github.com/pingcap/tidb/parser/opcode" "github.com/pingcap/tidb/types" driver "github.com/pingcap/tidb/types/parser_driver" @@ -282,16 +283,19 @@ func processCreateTable(conv *internal.Conv, stmt *ast.CreateTableStmt) { }) } } + CheckConstraints := toCheckConstraints(stmt.Constraints) + conv.SchemaStatement(NodeType(stmt)) conv.SrcSchema[tableId] = schema.Table{ - Id: tableId, - Name: tableName, - ColIds: colIds, - ColNameIdMap: colNameIdMap, - ColDefs: colDef, - PrimaryKeys: keys, - ForeignKeys: fkeys, - Indexes: index} + Id: tableId, + Name: tableName, + ColIds: colIds, + ColNameIdMap: colNameIdMap, + CheckConstraints: CheckConstraints, + ColDefs: colDef, + PrimaryKeys: keys, + ForeignKeys: fkeys, + Indexes: index} for _, constraint := range stmt.Constraints { processConstraint(conv, tableId, constraint, "CREATE TABLE", conv.SrcSchema[tableId].ColNameIdMap) } @@ -325,6 +329,33 @@ func processConstraint(conv *internal.Conv, tableId string, constraint *ast.Cons conv.SrcSchema[tableId] = st } +func toCheckConstraints(ckArray []*ast.Constraint) (cks []schema.CheckConstraints) { + for _, ck := range ckArray { + if ck.Tp == ast.ConstraintCheck { + exp := expressionToString(ck.Expr) + exp = strings.ReplaceAll(exp, "_UTF8MB4", "") + checkConstraint := schema.CheckConstraints{ + Name: ck.Name, + Expr: exp, + Id: internal.GenerateCheckConstrainstId(), + } + cks = append(cks, checkConstraint) + } + } + return cks +} + +// converts an AST expression node to its string representation. +func expressionToString(expr ast.Node) string { + var sb strings.Builder + restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, &sb) + if err := expr.Restore(restoreCtx); err != nil { + fmt.Errorf("Error restoring expression: %v\n", err) + return "" + } + return sb.String() +} + // toSchemaKeys converts a string list of MySQL keys to schema keys. // Note that we map all MySQL keys to ascending ordered schema keys. // For primary keys: this is fine because MySQL primary keys are always ascending. diff --git a/sources/mysql/mysqldump_test.go b/sources/mysql/mysqldump_test.go index 26a8d863a..a9ca41f6c 100644 --- a/sources/mysql/mysqldump_test.go +++ b/sources/mysql/mysqldump_test.go @@ -972,3 +972,64 @@ func stripSchemaComments(spSchema map[string]ddl.CreateTable) map[string]ddl.Cre func bitReverse(i int64) int64 { return int64(bits.Reverse64(uint64(i))) } + +func TestCheckConstraint(t *testing.T) { + cases := []struct { + name string + input string + expectedSchema map[string]ddl.CreateTable + }{ + { + name: "orders", + input: `CREATE TABLE orders ( + order_id int NOT NULL, + total_amount decimal(10,2) NOT NULL, + order_status enum('pending','completed','cancelled') DEFAULT 'pending', + PRIMARY KEY (order_id), + CONSTRAINT orders_chk_1 CHECK ((total_amount >= 0)), + CONSTRAINT orders_chk_2 CHECK (((order_status = 'completed' and total_amount > 0) or (order_status <> 'completed'))) + ); + `, + expectedSchema: map[string]ddl.CreateTable{ + "t1": ddl.CreateTable{ + Name: "orders", + ColIds: []string{"c2", "c3", "c4"}, + ColDefs: map[string]ddl.ColumnDef{ + "c2": ddl.ColumnDef{Name: "order_id", T: ddl.Type{Name: ddl.Int64}}, + "c3": ddl.ColumnDef{Name: "total_amount", T: ddl.Type{Name: ddl.Numeric, Len: ddl.MaxLength}}, + "c4": ddl.ColumnDef{Name: "order_status", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}}, + }, + CheckConstraint: []ddl.Checkconstraint{{Id: "ck1", Name: "orders_chk_1", Expr: "(`total_amount`>=0)"}, {Id: "ck1", Name: "orders_chk_2", Expr: "((`order_status`='completed' AND `total_amount`>0) OR (`order_status`!='completed'))"}}, + }, + }, + }, + { + name: "orders", + input: `CREATE TABLE orders ( + order_id int NOT NULL, + total_amount decimal(10,2) NOT NULL, + order_status enum('pending','completed','cancelled') DEFAULT 'pending', + PRIMARY KEY (order_id), + ); + `, + expectedSchema: map[string]ddl.CreateTable{ + "t1": ddl.CreateTable{ + Name: "orders", + ColIds: []string{"c2", "c3", "c4"}, + ColDefs: map[string]ddl.ColumnDef{ + "c2": ddl.ColumnDef{Name: "order_id", T: ddl.Type{Name: ddl.Int64}}, + "c3": ddl.ColumnDef{Name: "total_amount", T: ddl.Type{Name: ddl.Numeric, Len: ddl.MaxLength}}, + "c4": ddl.ColumnDef{Name: "order_status", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}}, + }, + CheckConstraint: nil, + }, + }, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + conv, _ := runProcessMySQLDump(tc.input) + assert.Equal(t, tc.expectedSchema["t1"].CheckConstraint, conv.SpSchema["t1"].CheckConstraint) + }) + } +} From 06d9876a52af322c9c9f597994995790065b003b Mon Sep 17 00:00:00 2001 From: Vivek Yadav Date: Fri, 22 Nov 2024 18:08:47 +0530 Subject: [PATCH 2/2] rename the variable --- sources/mysql/mysqldump_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/mysql/mysqldump_test.go b/sources/mysql/mysqldump_test.go index a9ca41f6c..4ba738044 100644 --- a/sources/mysql/mysqldump_test.go +++ b/sources/mysql/mysqldump_test.go @@ -999,7 +999,7 @@ func TestCheckConstraint(t *testing.T) { "c3": ddl.ColumnDef{Name: "total_amount", T: ddl.Type{Name: ddl.Numeric, Len: ddl.MaxLength}}, "c4": ddl.ColumnDef{Name: "order_status", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}}, }, - CheckConstraint: []ddl.Checkconstraint{{Id: "ck1", Name: "orders_chk_1", Expr: "(`total_amount`>=0)"}, {Id: "ck1", Name: "orders_chk_2", Expr: "((`order_status`='completed' AND `total_amount`>0) OR (`order_status`!='completed'))"}}, + CheckConstraint: []ddl.CheckConstraints{{Id: "ck1", Name: "orders_chk_1", Expr: "(`total_amount`>=0)"}, {Id: "ck1", Name: "orders_chk_2", Expr: "((`order_status`='completed' AND `total_amount`>0) OR (`order_status`!='completed'))"}}, }, }, },