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  }