github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/cdc/model/sink_test.go (about)

     1  // Copyright 2020 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 model
    15  
    16  import (
    17  	"sort"
    18  	"testing"
    19  
    20  	timodel "github.com/pingcap/tidb/pkg/parser/model"
    21  	"github.com/pingcap/tidb/pkg/parser/mysql"
    22  	"github.com/pingcap/tidb/pkg/parser/types"
    23  	"github.com/pingcap/tiflow/pkg/sink"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  func TestSetFlag(t *testing.T) {
    29  	t.Parallel()
    30  
    31  	var flag ColumnFlagType
    32  	flag.SetIsBinary()
    33  	flag.SetIsGeneratedColumn()
    34  	require.True(t, flag.IsBinary())
    35  	require.False(t, flag.IsHandleKey())
    36  	require.True(t, flag.IsGeneratedColumn())
    37  	flag.UnsetIsBinary()
    38  	require.False(t, flag.IsBinary())
    39  	flag.SetIsMultipleKey()
    40  	flag.SetIsUniqueKey()
    41  	require.True(t, flag.IsMultipleKey() && flag.IsUniqueKey())
    42  	flag.UnsetIsUniqueKey()
    43  	flag.UnsetIsGeneratedColumn()
    44  	flag.UnsetIsMultipleKey()
    45  	require.False(t, flag.IsUniqueKey() || flag.IsGeneratedColumn() || flag.IsMultipleKey())
    46  
    47  	flag = ColumnFlagType(0)
    48  	flag.SetIsHandleKey()
    49  	flag.SetIsPrimaryKey()
    50  	flag.SetIsUnsigned()
    51  	require.True(t, flag.IsHandleKey() && flag.IsPrimaryKey() && flag.IsUnsigned())
    52  	flag.UnsetIsHandleKey()
    53  	flag.UnsetIsPrimaryKey()
    54  	flag.UnsetIsUnsigned()
    55  	require.False(t, flag.IsHandleKey() || flag.IsPrimaryKey() || flag.IsUnsigned())
    56  	flag.SetIsNullable()
    57  	require.True(t, flag.IsNullable())
    58  	flag.UnsetIsNullable()
    59  	require.False(t, flag.IsNullable())
    60  }
    61  
    62  func TestFlagValue(t *testing.T) {
    63  	t.Parallel()
    64  
    65  	require.Equal(t, ColumnFlagType(0b1), BinaryFlag)
    66  	require.Equal(t, ColumnFlagType(0b1), BinaryFlag)
    67  	require.Equal(t, ColumnFlagType(0b10), HandleKeyFlag)
    68  	require.Equal(t, ColumnFlagType(0b100), GeneratedColumnFlag)
    69  	require.Equal(t, ColumnFlagType(0b1000), PrimaryKeyFlag)
    70  	require.Equal(t, ColumnFlagType(0b10000), UniqueKeyFlag)
    71  	require.Equal(t, ColumnFlagType(0b100000), MultipleKeyFlag)
    72  	require.Equal(t, ColumnFlagType(0b1000000), NullableFlag)
    73  }
    74  
    75  func TestTableNameFuncs(t *testing.T) {
    76  	t.Parallel()
    77  	tbl := &TableName{
    78  		Schema:  "test",
    79  		Table:   "t1",
    80  		TableID: 1071,
    81  	}
    82  	require.Equal(t, "test.t1", tbl.String())
    83  	require.Equal(t, "`test`.`t1`", tbl.QuoteString())
    84  	require.Equal(t, "test", tbl.GetSchema())
    85  	require.Equal(t, "t1", tbl.GetTable())
    86  	require.Equal(t, int64(1071), tbl.GetTableID())
    87  }
    88  
    89  func TestRowChangedEventFuncs(t *testing.T) {
    90  	t.Parallel()
    91  	deleteRow := &RowChangedEvent{
    92  		TableInfo: &TableInfo{
    93  			TableName: TableName{
    94  				Schema: "test",
    95  				Table:  "t1",
    96  			},
    97  		},
    98  		PreColumns: []*ColumnData{
    99  			{
   100  				ColumnID: 1,
   101  				Value:    1,
   102  			}, {
   103  				ColumnID: 2,
   104  				Value:    2,
   105  			},
   106  		},
   107  	}
   108  	require.True(t, deleteRow.IsDelete())
   109  }
   110  
   111  func TestColumnValueString(t *testing.T) {
   112  	t.Parallel()
   113  	testCases := []struct {
   114  		val      interface{}
   115  		expected string
   116  	}{
   117  		{interface{}(nil), "null"},
   118  		{interface{}(true), "1"},
   119  		{interface{}(false), "0"},
   120  		{interface{}(123), "123"},
   121  		{interface{}(int8(-123)), "-123"},
   122  		{interface{}(int16(-123)), "-123"},
   123  		{interface{}(int32(-123)), "-123"},
   124  		{interface{}(int64(-123)), "-123"},
   125  		{interface{}(uint8(123)), "123"},
   126  		{interface{}(uint16(123)), "123"},
   127  		{interface{}(uint32(123)), "123"},
   128  		{interface{}(uint64(123)), "123"},
   129  		{interface{}(float32(123.01)), "123.01"},
   130  		{interface{}(float64(123.01)), "123.01"},
   131  		{interface{}("123.01"), "123.01"},
   132  		{interface{}([]byte("123.01")), "123.01"},
   133  		{interface{}(complex(1, 2)), "(1+2i)"},
   134  	}
   135  	for _, tc := range testCases {
   136  		s := ColumnValueString(tc.val)
   137  		require.Equal(t, tc.expected, s)
   138  	}
   139  }
   140  
   141  func TestDDLEventFromJob(t *testing.T) {
   142  	t.Parallel()
   143  	ft := types.NewFieldType(mysql.TypeUnspecified)
   144  	ft.SetFlag(mysql.PriKeyFlag)
   145  	job := &timodel.Job{
   146  		ID:         1071,
   147  		TableID:    49,
   148  		SchemaName: "test",
   149  		Type:       timodel.ActionAddColumn,
   150  		StartTS:    420536581131337731,
   151  		Query:      "alter table t1 add column a int",
   152  		BinlogInfo: &timodel.HistoryInfo{
   153  			TableInfo: &timodel.TableInfo{
   154  				ID:   49,
   155  				Name: timodel.CIStr{O: "t1"},
   156  				Columns: []*timodel.ColumnInfo{
   157  					{ID: 1, Name: timodel.CIStr{O: "id"}, FieldType: *ft, State: timodel.StatePublic},
   158  					{ID: 2, Name: timodel.CIStr{O: "a"}, FieldType: types.FieldType{}, State: timodel.StatePublic},
   159  				},
   160  			},
   161  			FinishedTS: 420536581196873729,
   162  		},
   163  	}
   164  	preTableInfo := &TableInfo{
   165  		TableName: TableName{
   166  			Schema:  "test",
   167  			Table:   "t1",
   168  			TableID: 49,
   169  		},
   170  		TableInfo: &timodel.TableInfo{
   171  			ID:   49,
   172  			Name: timodel.CIStr{O: "t1"},
   173  			Columns: []*timodel.ColumnInfo{
   174  				{ID: 1, Name: timodel.CIStr{O: "id"}, FieldType: *ft, State: timodel.StatePublic},
   175  			},
   176  		},
   177  	}
   178  	tableInfo := WrapTableInfo(job.SchemaID, job.SchemaName, job.BinlogInfo.FinishedTS, job.BinlogInfo.TableInfo)
   179  	event := &DDLEvent{}
   180  	event.FromJob(job, preTableInfo, tableInfo)
   181  	require.Equal(t, uint64(420536581131337731), event.StartTs)
   182  	require.Equal(t, int64(49), event.TableInfo.TableName.TableID)
   183  	require.Equal(t, 1, len(event.PreTableInfo.TableInfo.Columns))
   184  
   185  	event = &DDLEvent{}
   186  	event.FromJob(job, nil, nil)
   187  	require.Nil(t, event.PreTableInfo)
   188  }
   189  
   190  func TestRenameTables(t *testing.T) {
   191  	ft := types.NewFieldType(mysql.TypeUnspecified)
   192  	ft.SetFlag(mysql.PriKeyFlag | mysql.UniqueFlag)
   193  	job := &timodel.Job{
   194  		ID:         71,
   195  		TableID:    69,
   196  		SchemaName: "test1",
   197  		Type:       timodel.ActionRenameTables,
   198  		StartTS:    432853521879007233,
   199  		Query:      "rename table test1.t1 to test1.t10, test1.t2 to test1.t20",
   200  		BinlogInfo: &timodel.HistoryInfo{
   201  			FinishedTS: 432853521879007238,
   202  			MultipleTableInfos: []*timodel.TableInfo{
   203  				{
   204  					ID:   67,
   205  					Name: timodel.CIStr{O: "t10"},
   206  					Columns: []*timodel.ColumnInfo{
   207  						{
   208  							ID:        1,
   209  							Name:      timodel.CIStr{O: "id"},
   210  							FieldType: *ft,
   211  							State:     timodel.StatePublic,
   212  						},
   213  					},
   214  				},
   215  				{
   216  					ID:   69,
   217  					Name: timodel.CIStr{O: "t20"},
   218  					Columns: []*timodel.ColumnInfo{
   219  						{
   220  							ID:        1,
   221  							Name:      timodel.CIStr{O: "id"},
   222  							FieldType: *ft,
   223  							State:     timodel.StatePublic,
   224  						},
   225  					},
   226  				},
   227  			},
   228  		},
   229  	}
   230  
   231  	preTableInfo := &TableInfo{
   232  		TableName: TableName{
   233  			Schema:  "test1",
   234  			Table:   "t1",
   235  			TableID: 67,
   236  		},
   237  		TableInfo: &timodel.TableInfo{
   238  			ID:   67,
   239  			Name: timodel.CIStr{O: "t1"},
   240  			Columns: []*timodel.ColumnInfo{
   241  				{
   242  					ID:        1,
   243  					Name:      timodel.CIStr{O: "id"},
   244  					FieldType: *ft,
   245  					State:     timodel.StatePublic,
   246  				},
   247  			},
   248  		},
   249  	}
   250  
   251  	tableInfo := &TableInfo{
   252  		TableName: TableName{
   253  			Schema:  "test1",
   254  			Table:   "t10",
   255  			TableID: 67,
   256  		},
   257  		TableInfo: &timodel.TableInfo{
   258  			ID:   67,
   259  			Name: timodel.CIStr{O: "t10"},
   260  			Columns: []*timodel.ColumnInfo{
   261  				{
   262  					ID:        1,
   263  					Name:      timodel.CIStr{O: "id"},
   264  					FieldType: *ft,
   265  					State:     timodel.StatePublic,
   266  				},
   267  			},
   268  		},
   269  	}
   270  
   271  	event := &DDLEvent{}
   272  	event.FromJobWithArgs(job, preTableInfo, tableInfo, "test1", "test1")
   273  	require.Equal(t, event.PreTableInfo.TableName.TableID, int64(67))
   274  	require.Equal(t, event.PreTableInfo.TableName.Table, "t1")
   275  	require.Len(t, event.PreTableInfo.TableInfo.Columns, 1)
   276  	require.Equal(t, event.TableInfo.TableName.TableID, int64(67))
   277  	require.Equal(t, event.TableInfo.TableName.Table, "t10")
   278  	require.Len(t, event.TableInfo.TableInfo.Columns, 1)
   279  	require.Equal(t, event.Query, "RENAME TABLE `test1`.`t1` TO `test1`.`t10`")
   280  	require.Equal(t, event.Type, timodel.ActionRenameTable)
   281  
   282  	preTableInfo = &TableInfo{
   283  		TableName: TableName{
   284  			Schema:  "test1",
   285  			Table:   "t2",
   286  			TableID: 69,
   287  		},
   288  		TableInfo: &timodel.TableInfo{
   289  			ID:   69,
   290  			Name: timodel.CIStr{O: "t2"},
   291  			Columns: []*timodel.ColumnInfo{
   292  				{
   293  					ID:        1,
   294  					Name:      timodel.CIStr{O: "id"},
   295  					FieldType: *ft,
   296  					State:     timodel.StatePublic,
   297  				},
   298  			},
   299  		},
   300  	}
   301  
   302  	tableInfo = &TableInfo{
   303  		TableName: TableName{
   304  			Schema:  "test1",
   305  			Table:   "t20",
   306  			TableID: 69,
   307  		},
   308  		TableInfo: &timodel.TableInfo{
   309  			ID:   69,
   310  			Name: timodel.CIStr{O: "t20"},
   311  			Columns: []*timodel.ColumnInfo{
   312  				{
   313  					ID:        1,
   314  					Name:      timodel.CIStr{O: "id"},
   315  					FieldType: *ft,
   316  					State:     timodel.StatePublic,
   317  				},
   318  			},
   319  		},
   320  	}
   321  
   322  	event = &DDLEvent{}
   323  	event.FromJobWithArgs(job, preTableInfo, tableInfo, "test1", "test1")
   324  	require.Equal(t, event.PreTableInfo.TableName.TableID, int64(69))
   325  	require.Equal(t, event.PreTableInfo.TableName.Table, "t2")
   326  	require.Len(t, event.PreTableInfo.TableInfo.Columns, 1)
   327  	require.Equal(t, event.TableInfo.TableName.TableID, int64(69))
   328  	require.Equal(t, event.TableInfo.TableName.Table, "t20")
   329  	require.Len(t, event.TableInfo.TableInfo.Columns, 1)
   330  	require.Equal(t, event.Query, "RENAME TABLE `test1`.`t2` TO `test1`.`t20`")
   331  	require.Equal(t, event.Type, timodel.ActionRenameTable)
   332  }
   333  
   334  func TestExchangeTablePartition(t *testing.T) {
   335  	ft := types.NewFieldType(mysql.TypeUnspecified)
   336  	ft.SetFlag(mysql.PriKeyFlag | mysql.UniqueFlag)
   337  	job := &timodel.Job{
   338  		ID:         71,
   339  		TableID:    69,
   340  		SchemaName: "test1",
   341  		Type:       timodel.ActionExchangeTablePartition,
   342  		StartTS:    432853521879007233,
   343  		Query:      "alter table t1 exchange partition p0 with table t2",
   344  		BinlogInfo: &timodel.HistoryInfo{
   345  			FinishedTS: 432853521879007238,
   346  		},
   347  	}
   348  
   349  	// source table
   350  	preTableInfo := &TableInfo{
   351  		TableName: TableName{
   352  			Schema:  "test2",
   353  			Table:   "t2",
   354  			TableID: 67,
   355  		},
   356  		TableInfo: &timodel.TableInfo{
   357  			ID:   67,
   358  			Name: timodel.CIStr{O: "t1"},
   359  			Columns: []*timodel.ColumnInfo{
   360  				{
   361  					ID:        1,
   362  					Name:      timodel.CIStr{O: "id"},
   363  					FieldType: *ft,
   364  					State:     timodel.StatePublic,
   365  				},
   366  			},
   367  		},
   368  	}
   369  
   370  	// target table
   371  	tableInfo := &TableInfo{
   372  		TableName: TableName{
   373  			Schema:  "test1",
   374  			Table:   "t1",
   375  			TableID: 69,
   376  		},
   377  		TableInfo: &timodel.TableInfo{
   378  			ID:   69,
   379  			Name: timodel.CIStr{O: "t10"},
   380  			Columns: []*timodel.ColumnInfo{
   381  				{
   382  					ID:        1,
   383  					Name:      timodel.CIStr{O: "id"},
   384  					FieldType: *ft,
   385  					State:     timodel.StatePublic,
   386  				},
   387  			},
   388  		},
   389  	}
   390  
   391  	event := &DDLEvent{}
   392  	event.FromJob(job, preTableInfo, tableInfo)
   393  	require.Equal(t, event.PreTableInfo.TableName.TableID, int64(67))
   394  	require.Equal(t, event.PreTableInfo.TableName.Table, "t2")
   395  	require.Len(t, event.PreTableInfo.TableInfo.Columns, 1)
   396  	require.Equal(t, event.TableInfo.TableName.TableID, int64(69))
   397  	require.Equal(t, event.TableInfo.TableName.Table, "t1")
   398  	require.Len(t, event.TableInfo.TableInfo.Columns, 1)
   399  	require.Equal(t, "ALTER TABLE `test1`.`t1` EXCHANGE PARTITION `p0` WITH TABLE `test2`.`t2`", event.Query)
   400  	require.Equal(t, event.Type, timodel.ActionExchangeTablePartition)
   401  }
   402  
   403  func TestSortRowChangedEvent(t *testing.T) {
   404  	events := []*RowChangedEvent{
   405  		{
   406  			PreColumns: []*ColumnData{{}},
   407  			Columns:    []*ColumnData{{}},
   408  		},
   409  		{
   410  			Columns: []*ColumnData{{}},
   411  		},
   412  		{
   413  			PreColumns: []*ColumnData{{}},
   414  		},
   415  	}
   416  	assert.True(t, events[0].IsUpdate())
   417  	assert.True(t, events[1].IsInsert())
   418  	assert.True(t, events[2].IsDelete())
   419  	sort.Sort(txnRows(events))
   420  	assert.True(t, events[0].IsDelete())
   421  	assert.True(t, events[1].IsUpdate())
   422  	assert.True(t, events[2].IsInsert())
   423  }
   424  
   425  func TestTrySplitAndSortUpdateEventNil(t *testing.T) {
   426  	t.Parallel()
   427  
   428  	events := []*RowChangedEvent{nil}
   429  	result, err := trySplitAndSortUpdateEvent(events)
   430  	require.NoError(t, err)
   431  	require.Equal(t, 0, len(result))
   432  }
   433  
   434  func TestTrySplitAndSortUpdateEventEmpty(t *testing.T) {
   435  	t.Parallel()
   436  
   437  	events := []*RowChangedEvent{
   438  		{
   439  			StartTs:  1,
   440  			CommitTs: 2,
   441  		},
   442  	}
   443  	result, err := trySplitAndSortUpdateEvent(events)
   444  	require.NoError(t, err)
   445  	require.Equal(t, 0, len(result))
   446  }
   447  
   448  func TestTrySplitAndSortUpdateEvent(t *testing.T) {
   449  	t.Parallel()
   450  
   451  	// Update primary key.
   452  	tableInfoWithPrimaryKey := BuildTableInfo("test", "t", []*Column{
   453  		{
   454  			Name: "col1",
   455  			Flag: BinaryFlag,
   456  		},
   457  		{
   458  			Name: "col2",
   459  			Flag: HandleKeyFlag | PrimaryKeyFlag,
   460  		},
   461  	}, [][]int{{1}})
   462  	events := []*RowChangedEvent{
   463  		{
   464  			CommitTs:  1,
   465  			TableInfo: tableInfoWithPrimaryKey,
   466  			Columns: Columns2ColumnDatas([]*Column{
   467  				{
   468  					Name:  "col1",
   469  					Flag:  BinaryFlag,
   470  					Value: "col1-value-updated",
   471  				},
   472  				{
   473  					Name:  "col2",
   474  					Flag:  HandleKeyFlag | PrimaryKeyFlag,
   475  					Value: "col2-value-updated",
   476  				},
   477  			}, tableInfoWithPrimaryKey),
   478  			PreColumns: Columns2ColumnDatas([]*Column{
   479  				{
   480  					Name:  "col1",
   481  					Value: "col1-value",
   482  				},
   483  				{
   484  					Name:  "col2",
   485  					Value: "col2-value",
   486  				},
   487  			}, tableInfoWithPrimaryKey),
   488  		},
   489  	}
   490  	result, err := trySplitAndSortUpdateEvent(events)
   491  	require.NoError(t, err)
   492  	require.Equal(t, 2, len(result))
   493  	require.True(t, result[0].IsDelete())
   494  	require.True(t, result[1].IsInsert())
   495  
   496  	// Update unique key.
   497  	tableInfoWithUniqueKey := BuildTableInfo("test", "t", []*Column{
   498  		{
   499  			Name: "col1",
   500  			Flag: BinaryFlag,
   501  		},
   502  		{
   503  			Name: "col2",
   504  			Flag: UniqueKeyFlag | NullableFlag,
   505  		},
   506  	}, [][]int{{1}})
   507  	events = []*RowChangedEvent{
   508  		{
   509  			CommitTs:  1,
   510  			TableInfo: tableInfoWithUniqueKey,
   511  			Columns: Columns2ColumnDatas([]*Column{
   512  				{
   513  					Name:  "col1",
   514  					Value: "col1-value-updated",
   515  				},
   516  				{
   517  					Name:  "col2",
   518  					Value: "col2-value-updated",
   519  				},
   520  			}, tableInfoWithUniqueKey),
   521  			PreColumns: Columns2ColumnDatas([]*Column{
   522  				{
   523  					Name:  "col1",
   524  					Value: "col1-value",
   525  				},
   526  				{
   527  					Name:  "col2",
   528  					Value: "col2-value",
   529  				},
   530  			}, tableInfoWithUniqueKey),
   531  		},
   532  	}
   533  	result, err = trySplitAndSortUpdateEvent(events)
   534  	require.NoError(t, err)
   535  	require.Equal(t, 2, len(result))
   536  	require.True(t, result[0].IsDelete())
   537  	require.True(t, result[0].IsDelete())
   538  	require.True(t, result[1].IsInsert())
   539  
   540  	// Update non-handle key.
   541  	events = []*RowChangedEvent{
   542  		{
   543  			CommitTs:  1,
   544  			TableInfo: tableInfoWithPrimaryKey,
   545  			Columns: Columns2ColumnDatas([]*Column{
   546  				{
   547  					Name:  "col1",
   548  					Value: "col1-value-updated",
   549  				},
   550  				{
   551  					Name:  "col2",
   552  					Value: "col2-value",
   553  				},
   554  			}, tableInfoWithPrimaryKey),
   555  			PreColumns: Columns2ColumnDatas([]*Column{
   556  				{
   557  					Name:  "col1",
   558  					Value: "col1-value",
   559  				},
   560  				{
   561  					Name:  "col2",
   562  					Value: "col2-value",
   563  				},
   564  			}, tableInfoWithPrimaryKey),
   565  		},
   566  	}
   567  	result, err = trySplitAndSortUpdateEvent(events)
   568  	require.NoError(t, err)
   569  	require.Equal(t, 1, len(result))
   570  }
   571  
   572  func TestTxnTrySplitAndSortUpdateEvent(t *testing.T) {
   573  	columns := []*Column{
   574  		{
   575  			Name:  "col1",
   576  			Flag:  BinaryFlag,
   577  			Value: "col1-value",
   578  		},
   579  		{
   580  			Name:  "col2",
   581  			Flag:  HandleKeyFlag | UniqueKeyFlag | PrimaryKeyFlag,
   582  			Value: "col2-value-updated",
   583  		},
   584  	}
   585  	preColumns := []*Column{
   586  		{
   587  			Name:  "col1",
   588  			Flag:  BinaryFlag,
   589  			Value: "col1-value",
   590  		},
   591  		{
   592  			Name:  "col2",
   593  			Flag:  HandleKeyFlag | UniqueKeyFlag | PrimaryKeyFlag,
   594  			Value: "col2-value",
   595  		},
   596  	}
   597  	tableInfo := BuildTableInfo("test", "t", columns, [][]int{{1}})
   598  	ukUpdatedEvent := &RowChangedEvent{
   599  		TableInfo:  tableInfo,
   600  		PreColumns: Columns2ColumnDatas(preColumns, tableInfo),
   601  		Columns:    Columns2ColumnDatas(columns, tableInfo),
   602  	}
   603  	txn := &SingleTableTxn{
   604  		Rows: []*RowChangedEvent{ukUpdatedEvent},
   605  	}
   606  
   607  	err := txn.TrySplitAndSortUpdateEvent(sink.KafkaScheme)
   608  	require.NoError(t, err)
   609  	require.Len(t, txn.Rows, 2)
   610  
   611  	txn = &SingleTableTxn{
   612  		Rows: []*RowChangedEvent{ukUpdatedEvent},
   613  	}
   614  	err = txn.TrySplitAndSortUpdateEvent(sink.MySQLScheme)
   615  	require.NoError(t, err)
   616  	require.Len(t, txn.Rows, 1)
   617  
   618  	txn2 := &SingleTableTxn{
   619  		Rows: []*RowChangedEvent{ukUpdatedEvent, ukUpdatedEvent},
   620  	}
   621  	err = txn.TrySplitAndSortUpdateEvent(sink.MySQLScheme)
   622  	require.NoError(t, err)
   623  	require.Len(t, txn2.Rows, 2)
   624  }
   625  
   626  func TestToRedoLog(t *testing.T) {
   627  	cols := []*Column{
   628  		{
   629  			Name: "col1",
   630  			Flag: BinaryFlag,
   631  		},
   632  		{
   633  			Name: "col2",
   634  			Flag: HandleKeyFlag | UniqueKeyFlag,
   635  		},
   636  	}
   637  	tableInfo := BuildTableInfo("test", "t", cols, [][]int{{1}})
   638  	event := &RowChangedEvent{
   639  		StartTs:         100,
   640  		CommitTs:        1000,
   641  		PhysicalTableID: 1,
   642  		TableInfo:       tableInfo,
   643  		Columns: Columns2ColumnDatas([]*Column{
   644  			{
   645  				Name:  "col1",
   646  				Value: "col1-value",
   647  			},
   648  			{
   649  				Name:  "col2",
   650  				Value: "col2-value-updated",
   651  			},
   652  		}, tableInfo),
   653  	}
   654  	eventInRedoLog := event.ToRedoLog()
   655  	require.Equal(t, event.StartTs, eventInRedoLog.RedoRow.Row.StartTs)
   656  	require.Equal(t, event.CommitTs, eventInRedoLog.RedoRow.Row.CommitTs)
   657  	require.Equal(t, event.PhysicalTableID, eventInRedoLog.RedoRow.Row.Table.TableID)
   658  	require.Equal(t, event.TableInfo.GetSchemaName(), eventInRedoLog.RedoRow.Row.Table.Schema)
   659  	require.Equal(t, event.TableInfo.GetTableName(), eventInRedoLog.RedoRow.Row.Table.Table)
   660  	require.Equal(t, event.Columns, Columns2ColumnDatas(eventInRedoLog.RedoRow.Row.Columns, tableInfo))
   661  }