github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/cdc/model/codec/codec_test.go (about) 1 // Copyright 2023 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 codec 15 16 import ( 17 "testing" 18 19 "github.com/pingcap/tidb/pkg/parser/charset" 20 timodel "github.com/pingcap/tidb/pkg/parser/model" 21 "github.com/pingcap/tidb/pkg/parser/mysql" 22 "github.com/pingcap/tiflow/cdc/model" 23 codecv1 "github.com/pingcap/tiflow/cdc/model/codec/v1" 24 "github.com/stretchr/testify/require" 25 ) 26 27 func TestV1toV2(t *testing.T) { 28 var msg1 []byte 29 var rv1 *codecv1.RedoLog 30 var rv2, rv2Gen *model.RedoLog 31 var err error 32 33 rv1 = &codecv1.RedoLog{ 34 RedoRow: &codecv1.RedoRowChangedEvent{ 35 Row: &codecv1.RowChangedEvent{ 36 StartTs: 1, 37 CommitTs: 2, 38 Table: &codecv1.TableName{ 39 Schema: "schema", 40 Table: "table", 41 TableID: 1, 42 IsPartition: false, 43 }, 44 TableInfo: nil, 45 Columns: []*codecv1.Column{ 46 { 47 Name: "column", 48 Flag: model.BinaryFlag, 49 }, 50 }, 51 PreColumns: []*codecv1.Column{ 52 { 53 Name: "column", 54 Flag: model.BinaryFlag, 55 }, 56 }, 57 IndexColumns: [][]int{{1}}, 58 }, 59 }, 60 RedoDDL: &codecv1.RedoDDLEvent{ 61 DDL: &codecv1.DDLEvent{ 62 StartTs: 1, 63 CommitTs: 2, 64 Type: timodel.ActionCreateTable, 65 }, 66 }, 67 } 68 69 rv2 = &model.RedoLog{ 70 RedoRow: model.RedoRowChangedEvent{ 71 Row: &model.RowChangedEventInRedoLog{ 72 StartTs: 1, 73 CommitTs: 2, 74 Table: &model.TableName{ 75 Schema: "schema", 76 Table: "table", 77 TableID: 1, 78 IsPartition: false, 79 }, 80 Columns: []*model.Column{ 81 { 82 Name: "column", 83 Flag: model.BinaryFlag, 84 }, 85 }, 86 PreColumns: []*model.Column{ 87 { 88 Name: "column", 89 Flag: model.BinaryFlag, 90 }, 91 }, 92 IndexColumns: [][]int{{1}}, 93 }, 94 }, 95 RedoDDL: model.RedoDDLEvent{ 96 DDL: &model.DDLEvent{ 97 StartTs: 1, 98 CommitTs: 2, 99 Type: timodel.ActionCreateTable, 100 }, 101 }, 102 } 103 104 // Unmarshal from v1, []byte{} will be transformed into "". 105 rv1.RedoRow.Row.Columns[0].Value = []byte{} 106 rv1.RedoRow.Row.PreColumns[0].Value = []byte{} 107 rv2.RedoRow.Row.Columns[0].Value = "" 108 rv2.RedoRow.Row.PreColumns[0].Value = "" 109 110 // Marshal v1 into bytes. 111 codecv1.PreMarshal(rv1) 112 msg1, err = rv1.MarshalMsg(nil) 113 require.Nil(t, err) 114 115 // Unmarshal v2 from v1 bytes. 116 rv2Gen, msg1, err = UnmarshalRedoLog(msg1) 117 require.Nil(t, err) 118 require.Zero(t, len(msg1)) 119 require.Equal(t, rv2.RedoRow.Row, rv2Gen.RedoRow.Row) 120 121 // For v2, []byte{} will be kept same in marshal and unmarshal. 122 rv2.RedoRow.Row.Columns[0].Value = []byte{} 123 rv2.RedoRow.Row.PreColumns[0].Value = []byte{} 124 rv2Gen.RedoRow.Row.Columns[0].Value = []byte{} 125 rv2Gen.RedoRow.Row.PreColumns[0].Value = []byte{} 126 127 msg1, err = MarshalRedoLog(rv2Gen, nil) 128 require.Nil(t, err) 129 rv2Gen, msg1, err = UnmarshalRedoLog(msg1) 130 require.Nil(t, err) 131 require.Zero(t, len(msg1)) 132 require.Equal(t, rv2.RedoRow.Row, rv2Gen.RedoRow.Row) 133 } 134 135 func TestRowRedoConvert(t *testing.T) { 136 t.Parallel() 137 138 row := &model.RowChangedEventInRedoLog{ 139 StartTs: 100, 140 CommitTs: 120, 141 Table: &model.TableName{Schema: "test", Table: "table1", TableID: 57}, 142 PreColumns: []*model.Column{{ 143 Name: "a1", 144 Type: mysql.TypeLong, 145 Flag: model.BinaryFlag | model.MultipleKeyFlag | model.HandleKeyFlag, 146 Value: int64(1), 147 }, { 148 Name: "a2", 149 Type: mysql.TypeVarchar, 150 Value: []byte("char"), 151 }, { 152 Name: "a3", 153 Type: mysql.TypeLong, 154 Flag: model.BinaryFlag | model.MultipleKeyFlag | model.HandleKeyFlag, 155 Value: int64(1), 156 }, { 157 Name: "a4", 158 Type: mysql.TypeTinyBlob, 159 Charset: charset.CharsetGBK, 160 Value: []byte("你好"), 161 }, nil}, 162 Columns: []*model.Column{{ 163 Name: "a1", 164 Type: mysql.TypeLong, 165 Flag: model.BinaryFlag | model.MultipleKeyFlag | model.HandleKeyFlag, 166 Value: int64(2), 167 }, { 168 Name: "a2", 169 Type: mysql.TypeVarchar, 170 Value: []byte("char-updated"), 171 }, { 172 Name: "a3", 173 Type: mysql.TypeLong, 174 Flag: model.BinaryFlag | model.MultipleKeyFlag | model.HandleKeyFlag, 175 Value: int64(2), 176 }, { 177 Name: "a4", 178 Type: mysql.TypeTinyBlob, 179 Charset: charset.CharsetGBK, 180 Value: []byte("世界"), 181 }, nil}, 182 IndexColumns: [][]int{{1, 3}}, 183 } 184 185 redoLog := &model.RedoLog{RedoRow: model.RedoRowChangedEvent{Row: row}} 186 data, err := MarshalRedoLog(redoLog, nil) 187 require.Nil(t, err) 188 189 redoLog2, data, err := UnmarshalRedoLog(data) 190 require.Nil(t, err) 191 require.Zero(t, len(data)) 192 require.Equal(t, row, redoLog2.RedoRow.Row) 193 } 194 195 func TestRowRedoConvertWithEmptySlice(t *testing.T) { 196 t.Parallel() 197 198 row := &model.RowChangedEventInRedoLog{ 199 StartTs: 100, 200 CommitTs: 120, 201 Table: &model.TableName{Schema: "test", Table: "table1", TableID: 57}, 202 PreColumns: []*model.Column{{ 203 Name: "a1", 204 Type: mysql.TypeLong, 205 Flag: model.BinaryFlag | model.MultipleKeyFlag | model.HandleKeyFlag, 206 Value: int64(1), 207 }, { 208 Name: "a2", 209 Type: mysql.TypeVarchar, 210 Value: []byte(""), // empty slice should be marshal and unmarshal safely 211 }}, 212 Columns: []*model.Column{{ 213 Name: "a1", 214 Type: mysql.TypeLong, 215 Flag: model.BinaryFlag | model.MultipleKeyFlag | model.HandleKeyFlag, 216 Value: int64(2), 217 }, { 218 Name: "a2", 219 Type: mysql.TypeVarchar, 220 Value: []byte(""), 221 }}, 222 IndexColumns: [][]int{{1}}, 223 } 224 225 redoLog := &model.RedoLog{RedoRow: model.RedoRowChangedEvent{Row: row}} 226 data, err := MarshalRedoLog(redoLog, nil) 227 require.Nil(t, err) 228 229 redoLog2, data, err := UnmarshalRedoLog(data) 230 require.Nil(t, err) 231 require.Zero(t, len(data)) 232 require.Equal(t, row, redoLog2.RedoRow.Row) 233 } 234 235 func TestDDLRedoConvert(t *testing.T) { 236 t.Parallel() 237 238 ddl := &model.DDLEvent{ 239 StartTs: 1020, 240 CommitTs: 1030, 241 TableInfo: &model.TableInfo{}, 242 Type: timodel.ActionAddColumn, 243 Query: "ALTER TABLE test.t1 ADD COLUMN a int", 244 } 245 246 redoLog := &model.RedoLog{RedoDDL: model.RedoDDLEvent{DDL: ddl}} 247 data, err := MarshalRedoLog(redoLog, nil) 248 require.Nil(t, err) 249 250 redoLog2, data, err := UnmarshalRedoLog(data) 251 require.Nil(t, err) 252 require.Zero(t, len(data)) 253 require.Equal(t, ddl, redoLog2.RedoDDL.DDL) 254 }