github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/entry/mounter_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 entry 15 16 import ( 17 "context" 18 "strings" 19 "time" 20 21 "github.com/pingcap/check" 22 "github.com/pingcap/log" 23 "github.com/pingcap/parser/mysql" 24 "github.com/pingcap/ticdc/cdc/model" 25 "github.com/pingcap/ticdc/pkg/regionspan" 26 "github.com/pingcap/ticdc/pkg/util/testleak" 27 ticonfig "github.com/pingcap/tidb/config" 28 tidbkv "github.com/pingcap/tidb/kv" 29 "github.com/pingcap/tidb/session" 30 "github.com/pingcap/tidb/store/mockstore" 31 "github.com/pingcap/tidb/store/tikv/oracle" 32 "github.com/pingcap/tidb/util/testkit" 33 "go.uber.org/zap" 34 ) 35 36 type mountTxnsSuite struct{} 37 38 var _ = check.Suite(&mountTxnsSuite{}) 39 40 func (s *mountTxnsSuite) TestMounterDisableOldValue(c *check.C) { 41 defer testleak.AfterTest(c)() 42 testCases := []struct { 43 tableName string 44 createTableDDL string 45 values [][]interface{} 46 }{{ 47 tableName: "simple", 48 createTableDDL: "create table simple(id int primary key)", 49 values: [][]interface{}{{1}, {2}, {3}, {4}, {5}}, 50 }, { 51 tableName: "no_pk", 52 createTableDDL: "create table no_pk(id int not null unique key)", 53 values: [][]interface{}{{1}, {2}, {3}, {4}, {5}}, 54 }, { 55 tableName: "many_index", 56 createTableDDL: "create table many_index(id int not null unique key, c1 int unique key, c2 int, INDEX (c2))", 57 values: [][]interface{}{{1, 1, 1}, {2, 2, 2}, {3, 3, 3}, {4, 4, 4}, {5, 5, 5}}, 58 }, { 59 tableName: "default_value", 60 createTableDDL: "create table default_value(id int primary key, c1 int, c2 int not null default 5, c3 varchar(20), c4 varchar(20) not null default '666')", 61 values: [][]interface{}{{1}, {2}, {3}, {4}, {5}}, 62 }, { 63 tableName: "partition_table", 64 createTableDDL: `CREATE TABLE partition_table ( 65 id INT NOT NULL AUTO_INCREMENT UNIQUE KEY, 66 fname VARCHAR(25) NOT NULL, 67 lname VARCHAR(25) NOT NULL, 68 store_id INT NOT NULL, 69 department_id INT NOT NULL, 70 INDEX (department_id) 71 ) 72 73 PARTITION BY RANGE(id) ( 74 PARTITION p0 VALUES LESS THAN (5), 75 PARTITION p1 VALUES LESS THAN (10), 76 PARTITION p2 VALUES LESS THAN (15), 77 PARTITION p3 VALUES LESS THAN (20) 78 )`, 79 values: [][]interface{}{ 80 {1, "aa", "bb", 12, 12}, 81 {6, "aac", "bab", 51, 51}, 82 {11, "aad", "bsb", 71, 61}, 83 {18, "aae", "bbf", 21, 14}, 84 {15, "afa", "bbc", 11, 12}, 85 }, 86 }, { 87 tableName: "tp_int", 88 createTableDDL: `create table tp_int 89 ( 90 id int auto_increment, 91 c_tinyint tinyint null, 92 c_smallint smallint null, 93 c_mediumint mediumint null, 94 c_int int null, 95 c_bigint bigint null, 96 constraint pk 97 primary key (id) 98 );`, 99 values: [][]interface{}{ 100 {1, 1, 2, 3, 4, 5}, 101 {2}, 102 {3, 3, 4, 5, 6, 7}, 103 {4, 127, 32767, 8388607, 2147483647, 9223372036854775807}, 104 {5, -128, -32768, -8388608, -2147483648, -9223372036854775808}, 105 }, 106 }, { 107 tableName: "tp_text", 108 createTableDDL: `create table tp_text 109 ( 110 id int auto_increment, 111 c_tinytext tinytext null, 112 c_text text null, 113 c_mediumtext mediumtext null, 114 c_longtext longtext null, 115 c_varchar varchar(16) null, 116 c_char char(16) null, 117 c_tinyblob tinyblob null, 118 c_blob blob null, 119 c_mediumblob mediumblob null, 120 c_longblob longblob null, 121 c_binary binary(16) null, 122 c_varbinary varbinary(16) null, 123 constraint pk 124 primary key (id) 125 );`, 126 values: [][]interface{}{ 127 {1}, 128 { 129 2, "89504E470D0A1A0A", "89504E470D0A1A0A", "89504E470D0A1A0A", "89504E470D0A1A0A", "89504E470D0A1A0A", 130 "89504E470D0A1A0A", 131 []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, 132 []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, 133 []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, 134 []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, 135 []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, 136 []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, 137 }, 138 { 139 3, "bug free", "bug free", "bug free", "bug free", "bug free", "bug free", "bug free", "bug free", 140 "bug free", "bug free", "bug free", "bug free", 141 }, 142 {4, "", "", "", "", "", "", "", "", "", "", "", ""}, 143 {5, "你好", "我好", "大家好", "道路", "千万条", "安全", "第一条", "行车", "不规范", "亲人", "两行泪", "!"}, 144 {6, "😀", "😃", "😄", "😁", "😆", "😅", "😂", "🤣", "☺️", "😊", "😇", "🙂"}, 145 }, 146 }, { 147 tableName: "tp_time", 148 createTableDDL: `create table tp_time 149 ( 150 id int auto_increment, 151 c_date date null, 152 c_datetime datetime null, 153 c_timestamp timestamp null, 154 c_time time null, 155 c_year year null, 156 constraint pk 157 primary key (id) 158 );`, 159 values: [][]interface{}{ 160 {1}, 161 {2, "2020-02-20", "2020-02-20 02:20:20", "2020-02-20 02:20:20", "02:20:20", "2020"}, 162 }, 163 }, { 164 tableName: "tp_real", 165 createTableDDL: `create table tp_real 166 ( 167 id int auto_increment, 168 c_float float null, 169 c_double double null, 170 c_decimal decimal null, 171 constraint pk 172 primary key (id) 173 );`, 174 values: [][]interface{}{ 175 {1}, 176 {2, "2020.0202", "2020.0303", "2020.0404"}, 177 }, 178 }, { 179 tableName: "tp_other", 180 createTableDDL: `create table tp_other 181 ( 182 id int auto_increment, 183 c_enum enum ('a','b','c') null, 184 c_set set ('a','b','c') null, 185 c_bit bit(64) null, 186 c_json json null, 187 constraint pk 188 primary key (id) 189 );`, 190 values: [][]interface{}{ 191 {1}, 192 {2, "a", "a,c", 888, `{"aa":"bb"}`}, 193 }, 194 }, { 195 tableName: "clustered_index1", 196 createTableDDL: "CREATE TABLE clustered_index1 (id VARCHAR(255) PRIMARY KEY, data INT);", 197 values: [][]interface{}{ 198 {"hhh"}, 199 {"你好😘", 666}, 200 {"世界🤪", 888}, 201 }, 202 }, { 203 tableName: "clustered_index2", 204 createTableDDL: "CREATE TABLE clustered_index2 (id VARCHAR(255), data INT, ddaa date, PRIMARY KEY (id, data, ddaa), UNIQUE KEY (id, data, ddaa));", 205 values: [][]interface{}{ 206 {"你好😘", 666, "2020-11-20"}, 207 {"世界🤪", 888, "2020-05-12"}, 208 }, 209 }} 210 for _, tc := range testCases { 211 testMounterDisableOldValue(c, tc) 212 } 213 } 214 215 func testMounterDisableOldValue(c *check.C, tc struct { 216 tableName string 217 createTableDDL string 218 values [][]interface{} 219 }) { 220 store, err := mockstore.NewMockStore() 221 c.Assert(err, check.IsNil) 222 defer store.Close() //nolint:errcheck 223 ticonfig.UpdateGlobal(func(conf *ticonfig.Config) { 224 // we can update the tidb config here 225 }) 226 session.SetSchemaLease(0) 227 session.DisableStats4Test() 228 domain, err := session.BootstrapSession(store) 229 c.Assert(err, check.IsNil) 230 defer domain.Close() 231 domain.SetStatsUpdating(true) 232 tk := testkit.NewTestKit(c, store) 233 tk.MustExec("set @@tidb_enable_clustered_index=1;") 234 tk.MustExec("use test;") 235 236 tk.MustExec(tc.createTableDDL) 237 238 jobs, err := getAllHistoryDDLJob(store) 239 c.Assert(err, check.IsNil) 240 scheamStorage, err := NewSchemaStorage(nil, 0, nil, false) 241 c.Assert(err, check.IsNil) 242 for _, job := range jobs { 243 err := scheamStorage.HandleDDLJob(job) 244 c.Assert(err, check.IsNil) 245 } 246 tableInfo, ok := scheamStorage.GetLastSnapshot().GetTableByName("test", tc.tableName) 247 c.Assert(ok, check.IsTrue) 248 if tableInfo.IsCommonHandle { 249 // we can check this log to make sure if the clustered-index is enabled 250 log.Info("this table is enable the clustered index", zap.String("tableName", tableInfo.Name.L)) 251 } 252 253 for _, params := range tc.values { 254 insertSQL := prepareInsertSQL(c, tableInfo, len(params)) 255 tk.MustExec(insertSQL, params...) 256 } 257 258 ver, err := store.CurrentVersion(oracle.GlobalTxnScope) 259 c.Assert(err, check.IsNil) 260 scheamStorage.AdvanceResolvedTs(ver.Ver) 261 mounter := NewMounter(scheamStorage, 1, false).(*mounterImpl) 262 mounter.tz = time.Local 263 ctx := context.Background() 264 265 mountAndCheckRowInTable := func(tableID int64, f func(key []byte, value []byte) *model.RawKVEntry) int { 266 var rows int 267 walkTableSpanInStore(c, store, tableID, func(key []byte, value []byte) { 268 rawKV := f(key, value) 269 row, err := mounter.unmarshalAndMountRowChanged(ctx, rawKV) 270 c.Assert(err, check.IsNil) 271 if row == nil { 272 return 273 } 274 rows++ 275 c.Assert(row.Table.Table, check.Equals, tc.tableName) 276 c.Assert(row.Table.Schema, check.Equals, "test") 277 // TODO: test column flag, column type and index columns 278 if len(row.Columns) != 0 { 279 checkSQL, params := prepareCheckSQL(c, tc.tableName, row.Columns) 280 result := tk.MustQuery(checkSQL, params...) 281 result.Check([][]interface{}{{"1"}}) 282 } 283 if len(row.PreColumns) != 0 { 284 checkSQL, params := prepareCheckSQL(c, tc.tableName, row.PreColumns) 285 result := tk.MustQuery(checkSQL, params...) 286 result.Check([][]interface{}{{"1"}}) 287 } 288 }) 289 return rows 290 } 291 292 mountAndCheckRow := func(f func(key []byte, value []byte) *model.RawKVEntry) int { 293 partitionInfo := tableInfo.GetPartitionInfo() 294 if partitionInfo == nil { 295 return mountAndCheckRowInTable(tableInfo.ID, f) 296 } 297 var rows int 298 for _, p := range partitionInfo.Definitions { 299 rows += mountAndCheckRowInTable(p.ID, f) 300 } 301 return rows 302 } 303 304 rows := mountAndCheckRow(func(key []byte, value []byte) *model.RawKVEntry { 305 return &model.RawKVEntry{ 306 OpType: model.OpTypePut, 307 Key: key, 308 Value: value, 309 StartTs: ver.Ver - 1, 310 CRTs: ver.Ver, 311 } 312 }) 313 c.Assert(rows, check.Equals, len(tc.values)) 314 315 rows = mountAndCheckRow(func(key []byte, value []byte) *model.RawKVEntry { 316 return &model.RawKVEntry{ 317 OpType: model.OpTypeDelete, 318 Key: key, 319 Value: nil, // delete event doesn't include a value when old-value is disabled 320 StartTs: ver.Ver - 1, 321 CRTs: ver.Ver, 322 } 323 }) 324 c.Assert(rows, check.Equals, len(tc.values)) 325 } 326 327 func prepareInsertSQL(c *check.C, tableInfo *model.TableInfo, columnLens int) string { 328 var sb strings.Builder 329 _, err := sb.WriteString("INSERT INTO " + tableInfo.Name.O + "(") 330 c.Assert(err, check.IsNil) 331 for i := 0; i < columnLens; i++ { 332 col := tableInfo.Columns[i] 333 if i != 0 { 334 _, err = sb.WriteString(", ") 335 c.Assert(err, check.IsNil) 336 } 337 _, err = sb.WriteString(col.Name.O) 338 c.Assert(err, check.IsNil) 339 } 340 _, err = sb.WriteString(") VALUES (") 341 c.Assert(err, check.IsNil) 342 for i := 0; i < columnLens; i++ { 343 if i != 0 { 344 _, err = sb.WriteString(", ") 345 c.Assert(err, check.IsNil) 346 } 347 _, err = sb.WriteString("?") 348 c.Assert(err, check.IsNil) 349 } 350 _, err = sb.WriteString(")") 351 c.Assert(err, check.IsNil) 352 return sb.String() 353 } 354 355 func prepareCheckSQL(c *check.C, tableName string, cols []*model.Column) (string, []interface{}) { 356 var sb strings.Builder 357 _, err := sb.WriteString("SELECT count(1) FROM " + tableName + " WHERE ") 358 c.Assert(err, check.IsNil) 359 params := make([]interface{}, 0, len(cols)) 360 for i, col := range cols { 361 if col == nil { 362 continue 363 } 364 if i != 0 { 365 _, err = sb.WriteString(" AND ") 366 c.Assert(err, check.IsNil) 367 } 368 if col.Value == nil { 369 _, err = sb.WriteString(col.Name + " IS NULL") 370 c.Assert(err, check.IsNil) 371 continue 372 } 373 params = append(params, col.Value) 374 if col.Type == mysql.TypeJSON { 375 _, err = sb.WriteString(col.Name + " = CAST(? AS JSON)") 376 } else { 377 _, err = sb.WriteString(col.Name + " = ?") 378 } 379 c.Assert(err, check.IsNil) 380 } 381 return sb.String(), params 382 } 383 384 func walkTableSpanInStore(c *check.C, store tidbkv.Storage, tableID int64, f func(key []byte, value []byte)) { 385 txn, err := store.Begin() 386 c.Assert(err, check.IsNil) 387 defer txn.Rollback() //nolint:errcheck 388 tableSpan := regionspan.GetTableSpan(tableID) 389 kvIter, err := txn.Iter(tableSpan.Start, tableSpan.End) 390 c.Assert(err, check.IsNil) 391 defer kvIter.Close() 392 for kvIter.Valid() { 393 f(kvIter.Key(), kvIter.Value()) 394 err = kvIter.Next() 395 c.Assert(err, check.IsNil) 396 } 397 }