github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/syncer/dml_test.go (about) 1 // Copyright 2019 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package syncer 15 16 import ( 17 "math" 18 "testing" 19 20 tiddl "github.com/pingcap/tidb/pkg/ddl" 21 "github.com/pingcap/tidb/pkg/parser" 22 "github.com/pingcap/tidb/pkg/parser/ast" 23 "github.com/pingcap/tidb/pkg/parser/model" 24 "github.com/pingcap/tidb/pkg/parser/mysql" 25 "github.com/pingcap/tidb/pkg/parser/types" 26 "github.com/pingcap/tidb/pkg/sessionctx" 27 "github.com/pingcap/tidb/pkg/util/mock" 28 cdcmodel "github.com/pingcap/tiflow/cdc/model" 29 "github.com/pingcap/tiflow/dm/pkg/binlog" 30 "github.com/pingcap/tiflow/pkg/sqlmodel" 31 "github.com/stretchr/testify/require" 32 ) 33 34 var ( 35 location = binlog.Location{ 36 Position: binlog.MinPosition, 37 } 38 ec = &eventContext{startLocation: location, endLocation: location, lastLocation: location} 39 ecWithSafeMode = &eventContext{startLocation: location, endLocation: location, lastLocation: location, safeMode: true} 40 ) 41 42 func TestCastUnsigned(t *testing.T) { 43 t.Parallel() 44 45 // ref: https://dev.mysql.com/doc/refman/5.7/en/integer-types.html 46 cases := []struct { 47 data interface{} 48 unsigned bool 49 Type byte 50 expected interface{} 51 }{ 52 {int8(-math.Exp2(7)), false, mysql.TypeTiny, int8(-math.Exp2(7))}, // TINYINT 53 {int8(-math.Exp2(7)), true, mysql.TypeTiny, uint8(math.Exp2(7))}, 54 {int16(-math.Exp2(15)), false, mysql.TypeShort, int16(-math.Exp2(15))}, // SMALLINT 55 {int16(-math.Exp2(15)), true, mysql.TypeShort, uint16(math.Exp2(15))}, 56 {int32(-math.Exp2(23)), false, mysql.TypeInt24, int32(-math.Exp2(23))}, // MEDIUMINT 57 {int32(-math.Exp2(23)), true, mysql.TypeInt24, uint32(math.Exp2(23))}, 58 {int32(-math.Exp2(31)), false, mysql.TypeLong, int32(-math.Exp2(31))}, // INT 59 {int32(-math.Exp2(31)), true, mysql.TypeLong, uint32(math.Exp2(31))}, 60 {int64(-math.Exp2(63)), false, mysql.TypeLonglong, int64(-math.Exp2(63))}, // BIGINT 61 {int64(-math.Exp2(63)), true, mysql.TypeLonglong, uint64(math.Exp2(63))}, 62 } 63 for _, cs := range cases { 64 ft := types.NewFieldType(cs.Type) 65 if cs.unsigned { 66 ft.AddFlag(mysql.UnsignedFlag) 67 } 68 obtained := castUnsigned(cs.data, ft) 69 require.Equal(t, cs.expected, obtained) 70 } 71 } 72 73 func createTableInfo(p *parser.Parser, se sessionctx.Context, tableID int64, sql string) (*model.TableInfo, error) { 74 node, err := p.ParseOneStmt(sql, "utf8mb4", "utf8mb4_bin") 75 if err != nil { 76 return nil, err 77 } 78 return tiddl.MockTableInfo(se, node.(*ast.CreateTableStmt), tableID) 79 } 80 81 func TestGenDMLWithSameOp(t *testing.T) { 82 t.Parallel() 83 84 targetTable1 := &cdcmodel.TableName{Schema: "db1", Table: "tb1"} 85 targetTable2 := &cdcmodel.TableName{Schema: "db2", Table: "tb2"} 86 sourceTable11 := &cdcmodel.TableName{Schema: "dba", Table: "tba"} 87 sourceTable12 := &cdcmodel.TableName{Schema: "dba", Table: "tbb"} 88 sourceTable21 := &cdcmodel.TableName{Schema: "dbb", Table: "tba"} 89 sourceTable22 := &cdcmodel.TableName{Schema: "dbb", Table: "tbb"} 90 91 tableInfo11 := mockTableInfo(t, "create table db.tb(id int primary key, col1 int unique not null, name varchar(24))") 92 tableInfo12 := mockTableInfo(t, "create table db.tb(id int primary key, col1 int unique not null, name varchar(24))") 93 tableInfo21 := mockTableInfo(t, "create table db.tb(id int primary key, col2 int unique not null, name varchar(24))") 94 tableInfo22 := mockTableInfo(t, "create table db.tb(id int primary key, col3 int unique not null, name varchar(24))") 95 96 dmls := []*job{ 97 // insert 98 newDMLJob( 99 sqlmodel.NewRowChange(sourceTable11, targetTable1, nil, []interface{}{1, 1, "a"}, tableInfo11, nil, nil), 100 ecWithSafeMode, 101 ), 102 newDMLJob( 103 sqlmodel.NewRowChange(sourceTable11, targetTable1, nil, []interface{}{2, 2, "b"}, tableInfo11, nil, nil), 104 ecWithSafeMode, 105 ), 106 newDMLJob( 107 sqlmodel.NewRowChange(sourceTable12, targetTable1, nil, []interface{}{3, 3, "c"}, tableInfo12, nil, nil), 108 ecWithSafeMode, 109 ), 110 111 // update no index but safemode 112 newDMLJob( 113 sqlmodel.NewRowChange(sourceTable11, targetTable1, []interface{}{1, 1, "a"}, []interface{}{1, 1, "aa"}, tableInfo11, nil, nil), 114 ecWithSafeMode, 115 ), 116 newDMLJob( 117 sqlmodel.NewRowChange(sourceTable11, targetTable1, []interface{}{2, 2, "b"}, []interface{}{2, 2, "bb"}, tableInfo11, nil, nil), 118 ecWithSafeMode, 119 ), 120 newDMLJob( 121 sqlmodel.NewRowChange(sourceTable12, targetTable1, []interface{}{3, 3, "c"}, []interface{}{3, 3, "cc"}, tableInfo12, nil, nil), 122 ecWithSafeMode, 123 ), 124 125 // update uk 126 newDMLJob( 127 sqlmodel.NewRowChange(sourceTable11, targetTable1, []interface{}{1, 1, "aa"}, []interface{}{1, 4, "aa"}, tableInfo11, nil, nil), 128 ecWithSafeMode, 129 ), 130 newDMLJob( 131 sqlmodel.NewRowChange(sourceTable11, targetTable1, []interface{}{2, 2, "bb"}, []interface{}{2, 5, "bb"}, tableInfo11, nil, nil), 132 ecWithSafeMode, 133 ), 134 newDMLJob( 135 sqlmodel.NewRowChange(sourceTable12, targetTable1, []interface{}{3, 3, "cc"}, []interface{}{3, 6, "cc"}, tableInfo12, nil, nil), 136 ecWithSafeMode, 137 ), 138 139 // update pk 140 newDMLJob( 141 sqlmodel.NewRowChange(sourceTable11, targetTable1, []interface{}{1, 4, "aa"}, []interface{}{4, 4, "aa"}, tableInfo11, nil, nil), 142 ecWithSafeMode, 143 ), 144 newDMLJob( 145 sqlmodel.NewRowChange(sourceTable11, targetTable1, []interface{}{2, 5, "bb"}, []interface{}{5, 5, "bb"}, tableInfo11, nil, nil), 146 ecWithSafeMode, 147 ), 148 newDMLJob( 149 sqlmodel.NewRowChange(sourceTable12, targetTable1, []interface{}{3, 6, "cc"}, []interface{}{6, 6, "cc"}, tableInfo12, nil, nil), 150 ecWithSafeMode, 151 ), 152 // delete 153 newDMLJob( 154 sqlmodel.NewRowChange(sourceTable11, targetTable1, []interface{}{4, 4, "aa"}, nil, tableInfo11, nil, nil), 155 ecWithSafeMode, 156 ), 157 newDMLJob( 158 sqlmodel.NewRowChange(sourceTable11, targetTable1, []interface{}{5, 5, "bb"}, nil, tableInfo11, nil, nil), 159 ecWithSafeMode, 160 ), 161 newDMLJob( 162 sqlmodel.NewRowChange(sourceTable12, targetTable1, []interface{}{6, 6, "cc"}, nil, tableInfo12, nil, nil), 163 ecWithSafeMode, 164 ), 165 166 // target table 2 167 // insert 168 newDMLJob( 169 sqlmodel.NewRowChange(sourceTable21, targetTable2, nil, []interface{}{1, 1, "a"}, tableInfo21, nil, nil), 170 ecWithSafeMode, 171 ), 172 newDMLJob( 173 sqlmodel.NewRowChange(sourceTable21, targetTable2, nil, []interface{}{2, 2, "b"}, tableInfo21, nil, nil), 174 ec, 175 ), 176 newDMLJob( 177 sqlmodel.NewRowChange(sourceTable22, targetTable2, nil, []interface{}{3, 3, "c"}, tableInfo22, nil, nil), 178 ec, 179 ), 180 181 // update no index 182 newDMLJob( 183 sqlmodel.NewRowChange(sourceTable21, targetTable2, []interface{}{1, 1, "a"}, []interface{}{1, 1, "aa"}, tableInfo21, nil, nil), 184 ec, 185 ), 186 newDMLJob( 187 sqlmodel.NewRowChange(sourceTable21, targetTable2, []interface{}{2, 2, "b"}, []interface{}{2, 2, "bb"}, tableInfo21, nil, nil), 188 ec, 189 ), 190 newDMLJob( 191 sqlmodel.NewRowChange(sourceTable22, targetTable2, []interface{}{3, 3, "c"}, []interface{}{3, 3, "cc"}, tableInfo22, nil, nil), 192 ec, 193 ), 194 195 // update uk 196 newDMLJob( 197 sqlmodel.NewRowChange(sourceTable21, targetTable2, []interface{}{1, 1, "aa"}, []interface{}{1, 4, "aa"}, tableInfo21, nil, nil), 198 ec, 199 ), 200 newDMLJob( 201 sqlmodel.NewRowChange(sourceTable21, targetTable2, []interface{}{2, 2, "bb"}, []interface{}{2, 5, "bb"}, tableInfo21, nil, nil), 202 ec, 203 ), 204 newDMLJob( 205 sqlmodel.NewRowChange(sourceTable22, targetTable2, []interface{}{3, 3, "cc"}, []interface{}{3, 6, "cc"}, tableInfo22, nil, nil), 206 ec, 207 ), 208 209 // update pk 210 newDMLJob( 211 sqlmodel.NewRowChange(sourceTable21, targetTable2, []interface{}{1, 4, "aa"}, []interface{}{4, 4, "aa"}, tableInfo21, nil, nil), 212 ec, 213 ), 214 newDMLJob( 215 sqlmodel.NewRowChange(sourceTable21, targetTable2, []interface{}{2, 5, "bb"}, []interface{}{5, 5, "bb"}, tableInfo21, nil, nil), 216 ec, 217 ), 218 newDMLJob( 219 sqlmodel.NewRowChange(sourceTable22, targetTable2, []interface{}{3, 6, "cc"}, []interface{}{6, 6, "cc"}, tableInfo22, nil, nil), 220 ec, 221 ), 222 223 // delete 224 newDMLJob( 225 sqlmodel.NewRowChange(sourceTable21, targetTable2, []interface{}{4, 4, "aa"}, nil, tableInfo21, nil, nil), 226 ec, 227 ), 228 newDMLJob( 229 sqlmodel.NewRowChange(sourceTable21, targetTable2, []interface{}{5, 5, "bb"}, nil, tableInfo21, nil, nil), 230 ec, 231 ), 232 newDMLJob( 233 sqlmodel.NewRowChange(sourceTable22, targetTable2, []interface{}{6, 6, "cc"}, nil, tableInfo22, nil, nil), 234 ec, 235 ), 236 237 // table1 238 // detele 239 newDMLJob( 240 sqlmodel.NewRowChange(sourceTable11, targetTable1, []interface{}{44, 44, "aaa"}, nil, tableInfo11, nil, nil), 241 ec, 242 ), 243 newDMLJob( 244 sqlmodel.NewRowChange(sourceTable11, targetTable1, []interface{}{55, 55, "bbb"}, nil, tableInfo11, nil, nil), 245 ec, 246 ), 247 newDMLJob( 248 sqlmodel.NewRowChange(sourceTable12, targetTable1, []interface{}{66, 66, "ccc"}, nil, tableInfo12, nil, nil), 249 ec, 250 ), 251 } 252 253 expectQueries := []string{ 254 // table1 255 "REPLACE INTO `db1`.`tb1` (`id`,`col1`,`name`) VALUES (?,?,?),(?,?,?),(?,?,?)", 256 "DELETE FROM `db1`.`tb1` WHERE `id` = ? LIMIT 1", 257 "REPLACE INTO `db1`.`tb1` (`id`,`col1`,`name`) VALUES (?,?,?)", 258 "DELETE FROM `db1`.`tb1` WHERE `id` = ? LIMIT 1", 259 "REPLACE INTO `db1`.`tb1` (`id`,`col1`,`name`) VALUES (?,?,?)", 260 "DELETE FROM `db1`.`tb1` WHERE `id` = ? LIMIT 1", 261 "REPLACE INTO `db1`.`tb1` (`id`,`col1`,`name`) VALUES (?,?,?)", 262 "DELETE FROM `db1`.`tb1` WHERE `id` = ? LIMIT 1", 263 "REPLACE INTO `db1`.`tb1` (`id`,`col1`,`name`) VALUES (?,?,?)", 264 "DELETE FROM `db1`.`tb1` WHERE `id` = ? LIMIT 1", 265 "REPLACE INTO `db1`.`tb1` (`id`,`col1`,`name`) VALUES (?,?,?)", 266 "DELETE FROM `db1`.`tb1` WHERE `id` = ? LIMIT 1", 267 "REPLACE INTO `db1`.`tb1` (`id`,`col1`,`name`) VALUES (?,?,?)", 268 "DELETE FROM `db1`.`tb1` WHERE `id` = ? LIMIT 1", 269 "REPLACE INTO `db1`.`tb1` (`id`,`col1`,`name`) VALUES (?,?,?)", 270 "DELETE FROM `db1`.`tb1` WHERE `id` = ? LIMIT 1", 271 "REPLACE INTO `db1`.`tb1` (`id`,`col1`,`name`) VALUES (?,?,?)", 272 "DELETE FROM `db1`.`tb1` WHERE `id` = ? LIMIT 1", 273 "REPLACE INTO `db1`.`tb1` (`id`,`col1`,`name`) VALUES (?,?,?)", 274 "DELETE FROM `db1`.`tb1` WHERE (`id` = ?) OR (`id` = ?) OR (`id` = ?)", 275 276 // table2 277 "REPLACE INTO `db2`.`tb2` (`id`,`col2`,`name`) VALUES (?,?,?)", 278 "INSERT INTO `db2`.`tb2` (`id`,`col2`,`name`) VALUES (?,?,?)", 279 "INSERT INTO `db2`.`tb2` (`id`,`col3`,`name`) VALUES (?,?,?)", 280 "INSERT INTO `db2`.`tb2` (`id`,`col2`,`name`) VALUES (?,?,?),(?,?,?) ON DUPLICATE KEY UPDATE `id`=VALUES(`id`),`col2`=VALUES(`col2`),`name`=VALUES(`name`)", 281 "INSERT INTO `db2`.`tb2` (`id`,`col3`,`name`) VALUES (?,?,?) ON DUPLICATE KEY UPDATE `id`=VALUES(`id`),`col3`=VALUES(`col3`),`name`=VALUES(`name`)", 282 "INSERT INTO `db2`.`tb2` (`id`,`col2`,`name`) VALUES (?,?,?),(?,?,?) ON DUPLICATE KEY UPDATE `id`=VALUES(`id`),`col2`=VALUES(`col2`),`name`=VALUES(`name`)", 283 "INSERT INTO `db2`.`tb2` (`id`,`col3`,`name`) VALUES (?,?,?) ON DUPLICATE KEY UPDATE `id`=VALUES(`id`),`col3`=VALUES(`col3`),`name`=VALUES(`name`)", 284 "UPDATE `db2`.`tb2` SET `id`=CASE WHEN `id` = ? THEN ? WHEN `id` = ? THEN ? END, `col2`=CASE WHEN `id` = ? THEN ? WHEN `id` = ? THEN ? END, `name`=CASE WHEN `id` = ? THEN ? WHEN `id` = ? THEN ? END WHERE (`id` = ?) OR (`id` = ?)", 285 "UPDATE `db2`.`tb2` SET `id`=CASE WHEN `id` = ? THEN ? END, `col3`=CASE WHEN `id` = ? THEN ? END, `name`=CASE WHEN `id` = ? THEN ? END WHERE (`id` = ?)", 286 "DELETE FROM `db2`.`tb2` WHERE (`id` = ?) OR (`id` = ?) OR (`id` = ?)", 287 288 // table1 289 "DELETE FROM `db1`.`tb1` WHERE (`id` = ?) OR (`id` = ?) OR (`id` = ?)", 290 } 291 292 expectArgs := [][]interface{}{ 293 // table1 294 {1, 1, "a", 2, 2, "b", 3, 3, "c"}, 295 {1}, 296 {1, 1, "aa"}, 297 {2}, 298 {2, 2, "bb"}, 299 {3}, 300 {3, 3, "cc"}, 301 {1}, 302 {1, 4, "aa"}, 303 {2}, 304 {2, 5, "bb"}, 305 {3}, 306 {3, 6, "cc"}, 307 {1}, 308 {4, 4, "aa"}, 309 {2}, 310 {5, 5, "bb"}, 311 {3}, 312 {6, 6, "cc"}, 313 {4, 5, 6}, 314 315 // table2 316 {1, 1, "a"}, 317 {2, 2, "b"}, 318 {3, 3, "c"}, 319 {1, 1, "aa", 2, 2, "bb"}, 320 {3, 3, "cc"}, 321 {1, 4, "aa", 2, 5, "bb"}, 322 {3, 6, "cc"}, 323 {1, 4, 2, 5, 1, 4, 2, 5, 1, "aa", 2, "bb", 1, 2}, 324 {3, 6, 3, 6, 3, "cc", 3}, 325 {4, 5, 6}, 326 327 // table1 328 {44, 55, 66}, 329 } 330 331 queries, args := genDMLsWithSameOp(dmls) 332 require.Equal(t, expectQueries, queries) 333 require.Equal(t, expectArgs, args) 334 } 335 336 func TestGBKExtractValueFromData(t *testing.T) { 337 t.Parallel() 338 339 table := `CREATE TABLE t (c INT PRIMARY KEY, d VARCHAR(20) CHARSET GBK);` 340 se := mock.NewContext() 341 p := parser.New() 342 ti, err := createTableInfo(p, se, 0, table) 343 require.NoError(t, err) 344 345 row := []interface{}{1, "\xc4\xe3\xba\xc3"} 346 expect := []interface{}{1, []byte("\xc4\xe3\xba\xc3")} 347 got, err := adjustValueFromBinlogData(row, ti) 348 require.NoError(t, err) 349 require.Equal(t, expect, got) 350 }