github.com/pingcap/tidb-lightning@v5.0.0-rc.0.20210428090220-84b649866577+incompatible/lightning/backend/sql2kv_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 backend 15 16 import ( 17 "errors" 18 19 . "github.com/pingcap/check" 20 "github.com/pingcap/parser" 21 "github.com/pingcap/parser/ast" 22 "github.com/pingcap/parser/model" 23 "github.com/pingcap/parser/mysql" 24 "github.com/pingcap/tidb/ddl" 25 "github.com/pingcap/tidb/kv" 26 "github.com/pingcap/tidb/meta/autoid" 27 "github.com/pingcap/tidb/sessionctx" 28 "github.com/pingcap/tidb/table" 29 "github.com/pingcap/tidb/table/tables" 30 "github.com/pingcap/tidb/types" 31 "github.com/pingcap/tidb/util/mock" 32 "go.uber.org/zap" 33 "go.uber.org/zap/zapcore" 34 35 "github.com/pingcap/tidb-lightning/lightning/common" 36 "github.com/pingcap/tidb-lightning/lightning/log" 37 "github.com/pingcap/tidb-lightning/lightning/verification" 38 ) 39 40 func (s *kvSuite) TestMarshal(c *C) { 41 nullDatum := types.Datum{} 42 nullDatum.SetNull() 43 minNotNull := types.Datum{} 44 minNotNull.SetMinNotNull() 45 encoder := zapcore.NewMapObjectEncoder() 46 err := encoder.AddArray("test", rowArrayMarshaler{types.NewStringDatum("1"), nullDatum, minNotNull, types.MaxValueDatum()}) 47 c.Assert(err, IsNil) 48 c.Assert(encoder.Fields["test"], DeepEquals, []interface{}{ 49 map[string]interface{}{"kind": "string", "val": "1"}, 50 map[string]interface{}{"kind": "null", "val": "NULL"}, 51 map[string]interface{}{"kind": "min", "val": "-inf"}, 52 map[string]interface{}{"kind": "max", "val": "+inf"}, 53 }) 54 55 invalid := types.Datum{} 56 invalid.SetInterface(1) 57 err = encoder.AddArray("bad-test", rowArrayMarshaler{minNotNull, invalid}) 58 c.Assert(err, ErrorMatches, "cannot convert.*") 59 c.Assert(encoder.Fields["bad-test"], DeepEquals, []interface{}{ 60 map[string]interface{}{"kind": "min", "val": "-inf"}, 61 }) 62 } 63 64 type mockTable struct { 65 table.Table 66 } 67 68 func (mockTable) AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { 69 return kv.IntHandle(-1), errors.New("mock error") 70 } 71 72 func (s *kvSuite) TestEncode(c *C) { 73 c1 := &model.ColumnInfo{ID: 1, Name: model.NewCIStr("c1"), State: model.StatePublic, Offset: 0, FieldType: *types.NewFieldType(mysql.TypeTiny)} 74 cols := []*model.ColumnInfo{c1} 75 tblInfo := &model.TableInfo{ID: 1, Columns: cols, PKIsHandle: false, State: model.StatePublic} 76 tbl, err := tables.TableFromMeta(NewPanickingAllocators(0), tblInfo) 77 c.Assert(err, IsNil) 78 79 logger := log.Logger{Logger: zap.NewNop()} 80 rows := []types.Datum{ 81 types.NewIntDatum(10000000), 82 } 83 84 // Strict mode 85 strictMode, err := NewTableKVEncoder(tbl, &SessionOptions{ 86 SQLMode: mysql.ModeStrictAllTables, 87 Timestamp: 1234567890, 88 }) 89 c.Assert(err, IsNil) 90 pairs, err := strictMode.Encode(logger, rows, 1, []int{0, 1}) 91 c.Assert(err, ErrorMatches, "failed to cast value as tinyint\\(4\\) for column `c1` \\(#1\\):.*overflows tinyint") 92 c.Assert(pairs, IsNil) 93 94 rowsWithPk := []types.Datum{ 95 types.NewIntDatum(1), 96 types.NewStringDatum("invalid-pk"), 97 } 98 pairs, err = strictMode.Encode(logger, rowsWithPk, 2, []int{0, 1}) 99 c.Assert(err, ErrorMatches, "failed to cast value as bigint\\(20\\) for column `_tidb_rowid`.*Truncated.*") 100 101 rowsWithPk2 := []types.Datum{ 102 types.NewIntDatum(1), 103 types.NewStringDatum("1"), 104 } 105 pairs, err = strictMode.Encode(logger, rowsWithPk2, 2, []int{0, 1}) 106 c.Assert(err, IsNil) 107 c.Assert(pairs, DeepEquals, kvPairs([]common.KvPair{ 108 { 109 Key: []uint8{0x74, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f, 0x72, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1}, 110 Val: []uint8{0x8, 0x2, 0x8, 0x2}, 111 }, 112 })) 113 114 // Mock add record error 115 mockTbl := &mockTable{Table: tbl} 116 mockMode, err := NewTableKVEncoder(mockTbl, &SessionOptions{ 117 SQLMode: mysql.ModeStrictAllTables, 118 Timestamp: 1234567891, 119 }) 120 c.Assert(err, IsNil) 121 pairs, err = mockMode.Encode(logger, rowsWithPk2, 2, []int{0, 1}) 122 c.Assert(err, ErrorMatches, "mock error") 123 124 // Non-strict mode 125 noneMode, err := NewTableKVEncoder(tbl, &SessionOptions{ 126 SQLMode: mysql.ModeNone, 127 Timestamp: 1234567892, 128 SysVars: map[string]string{"tidb_row_format_version": "1"}, 129 }) 130 c.Assert(err, IsNil) 131 pairs, err = noneMode.Encode(logger, rows, 1, []int{0, 1}) 132 c.Assert(err, IsNil) 133 c.Assert(pairs, DeepEquals, kvPairs([]common.KvPair{ 134 { 135 Key: []uint8{0x74, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f, 0x72, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1}, 136 Val: []uint8{0x8, 0x2, 0x8, 0xfe, 0x1}, 137 }, 138 })) 139 } 140 141 func (s *kvSuite) TestEncodeRowFormatV2(c *C) { 142 // Test encoding in row format v2, as described in <https://github.com/pingcap/tidb/blob/master/docs/design/2018-07-19-row-format.md>. 143 144 c1 := &model.ColumnInfo{ID: 1, Name: model.NewCIStr("c1"), State: model.StatePublic, Offset: 0, FieldType: *types.NewFieldType(mysql.TypeTiny)} 145 cols := []*model.ColumnInfo{c1} 146 tblInfo := &model.TableInfo{ID: 1, Columns: cols, PKIsHandle: false, State: model.StatePublic} 147 tbl, err := tables.TableFromMeta(NewPanickingAllocators(0), tblInfo) 148 c.Assert(err, IsNil) 149 150 logger := log.Logger{Logger: zap.NewNop()} 151 rows := []types.Datum{ 152 types.NewIntDatum(10000000), 153 } 154 155 noneMode, err := NewTableKVEncoder(tbl, &SessionOptions{ 156 SQLMode: mysql.ModeNone, 157 Timestamp: 1234567892, 158 SysVars: map[string]string{"tidb_row_format_version": "2"}, 159 }) 160 c.Assert(err, IsNil) 161 pairs, err := noneMode.Encode(logger, rows, 1, []int{0, 1}) 162 c.Assert(err, IsNil) 163 c.Assert(pairs, DeepEquals, kvPairs([]common.KvPair{ 164 { 165 // the key should be the same as TestEncode() 166 Key: []uint8{0x74, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f, 0x72, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1}, 167 Val: []uint8{ 168 0x80, // version 169 0x0, // flag = 0 = not big 170 0x1, 0x0, // number of not null columns = 1 171 0x0, 0x0, // number of null columns = 0 172 0x1, // column IDs = [1] 173 0x1, 0x0, // not null offsets = [1] 174 0x7f, // column version = 127 (10000000 clamped to TINYINT) 175 }, 176 }, 177 })) 178 } 179 180 func (s *kvSuite) TestEncodeTimestamp(c *C) { 181 ty := *types.NewFieldType(mysql.TypeDatetime) 182 ty.Flag |= mysql.NotNullFlag 183 c1 := &model.ColumnInfo{ 184 ID: 1, 185 Name: model.NewCIStr("c1"), 186 State: model.StatePublic, 187 Offset: 0, 188 FieldType: ty, 189 DefaultValue: "CURRENT_TIMESTAMP", 190 Version: 1, 191 } 192 cols := []*model.ColumnInfo{c1} 193 tblInfo := &model.TableInfo{ID: 1, Columns: cols, PKIsHandle: false, State: model.StatePublic} 194 tbl, err := tables.TableFromMeta(NewPanickingAllocators(0), tblInfo) 195 c.Assert(err, IsNil) 196 197 logger := log.Logger{Logger: zap.NewNop()} 198 199 encoder, err := NewTableKVEncoder(tbl, &SessionOptions{ 200 SQLMode: mysql.ModeStrictAllTables, 201 Timestamp: 1234567893, 202 SysVars: map[string]string{ 203 "tidb_row_format_version": "1", 204 "time_zone": "+08:00", 205 }, 206 }) 207 c.Assert(err, IsNil) 208 pairs, err := encoder.Encode(logger, nil, 70, []int{-1, 1}) 209 c.Assert(err, IsNil) 210 c.Assert(pairs, DeepEquals, kvPairs([]common.KvPair{ 211 { 212 Key: []uint8{0x74, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f, 0x72, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46}, 213 Val: []uint8{0x8, 0x2, 0x9, 0x80, 0x80, 0x80, 0xf0, 0xfd, 0x8e, 0xf7, 0xc0, 0x19}, 214 }, 215 })) 216 } 217 218 func mockTableInfo(c *C, createSql string) *model.TableInfo { 219 parser := parser.New() 220 node, err := parser.ParseOneStmt(createSql, "", "") 221 c.Assert(err, IsNil) 222 sctx := mock.NewContext() 223 info, err := ddl.MockTableInfo(sctx, node.(*ast.CreateTableStmt), 1) 224 c.Assert(err, IsNil) 225 info.State = model.StatePublic 226 return info 227 } 228 229 func (s *kvSuite) TestDefaultAutoRandoms(c *C) { 230 tblInfo := mockTableInfo(c, "create table t (id bigint unsigned NOT NULL auto_random primary key, a varchar(100));") 231 // seems parser can't parse auto_random properly. 232 tblInfo.AutoRandomBits = 5 233 tbl, err := tables.TableFromMeta(NewPanickingAllocators(0), tblInfo) 234 c.Assert(err, IsNil) 235 encoder, err := NewTableKVEncoder(tbl, &SessionOptions{ 236 SQLMode: mysql.ModeStrictAllTables, 237 Timestamp: 1234567893, 238 SysVars: map[string]string{"tidb_row_format_version": "2"}, 239 AutoRandomSeed: 456, 240 }) 241 c.Assert(err, IsNil) 242 logger := log.Logger{Logger: zap.NewNop()} 243 pairs, err := encoder.Encode(logger, []types.Datum{types.NewStringDatum("")}, 70, []int{-1, 0}) 244 c.Assert(err, IsNil) 245 c.Assert(pairs, DeepEquals, kvPairs([]common.KvPair{ 246 { 247 Key: []uint8{0x74, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f, 0x72, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46}, 248 Val: []uint8{0x80, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0}, 249 }, 250 })) 251 c.Assert(tbl.Allocators(encoder.(*tableKVEncoder).se).Get(autoid.AutoRandomType).Base(), Equals, int64(70)) 252 253 pairs, err = encoder.Encode(logger, []types.Datum{types.NewStringDatum("")}, 71, []int{-1, 0}) 254 c.Assert(err, IsNil) 255 c.Assert(pairs, DeepEquals, kvPairs([]common.KvPair{ 256 { 257 Key: []uint8{0x74, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f, 0x72, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x47}, 258 Val: []uint8{0x80, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0}, 259 }, 260 })) 261 c.Assert(tbl.Allocators(encoder.(*tableKVEncoder).se).Get(autoid.AutoRandomType).Base(), Equals, int64(71)) 262 } 263 264 func (s *kvSuite) TestSplitIntoChunks(c *C) { 265 pairs := []common.KvPair{ 266 { 267 Key: []byte{1, 2, 3}, 268 Val: []byte{4, 5, 6}, 269 }, 270 { 271 Key: []byte{7, 8}, 272 Val: []byte{9, 0}, 273 }, 274 { 275 Key: []byte{1, 2, 3, 4}, 276 Val: []byte{5, 6, 7, 8}, 277 }, 278 { 279 Key: []byte{9, 0}, 280 Val: []byte{1, 2}, 281 }, 282 } 283 284 splitBy10 := MakeRowsFromKvPairs(pairs).SplitIntoChunks(10) 285 c.Assert(splitBy10, DeepEquals, []Rows{ 286 MakeRowsFromKvPairs(pairs[0:2]), 287 MakeRowsFromKvPairs(pairs[2:3]), 288 MakeRowsFromKvPairs(pairs[3:4]), 289 }) 290 291 splitBy12 := MakeRowsFromKvPairs(pairs).SplitIntoChunks(12) 292 c.Assert(splitBy12, DeepEquals, []Rows{ 293 MakeRowsFromKvPairs(pairs[0:2]), 294 MakeRowsFromKvPairs(pairs[2:4]), 295 }) 296 297 splitBy1000 := MakeRowsFromKvPairs(pairs).SplitIntoChunks(1000) 298 c.Assert(splitBy1000, DeepEquals, []Rows{ 299 MakeRowsFromKvPairs(pairs[0:4]), 300 }) 301 302 splitBy1 := MakeRowsFromKvPairs(pairs).SplitIntoChunks(1) 303 c.Assert(splitBy1, DeepEquals, []Rows{ 304 MakeRowsFromKvPairs(pairs[0:1]), 305 MakeRowsFromKvPairs(pairs[1:2]), 306 MakeRowsFromKvPairs(pairs[2:3]), 307 MakeRowsFromKvPairs(pairs[3:4]), 308 }) 309 } 310 311 func (s *kvSuite) TestClassifyAndAppend(c *C) { 312 kvs := MakeRowFromKvPairs([]common.KvPair{ 313 { 314 Key: []byte("txxxxxxxx_ryyyyyyyy"), 315 Val: []byte("value1"), 316 }, 317 { 318 Key: []byte("txxxxxxxx_rwwwwwwww"), 319 Val: []byte("value2"), 320 }, 321 { 322 Key: []byte("txxxxxxxx_izzzzzzzz"), 323 Val: []byte("index1"), 324 }, 325 }) 326 327 data := MakeRowsFromKvPairs(nil) 328 indices := MakeRowsFromKvPairs(nil) 329 dataChecksum := verification.MakeKVChecksum(0, 0, 0) 330 indexChecksum := verification.MakeKVChecksum(0, 0, 0) 331 332 kvs.ClassifyAndAppend(&data, &dataChecksum, &indices, &indexChecksum) 333 334 c.Assert(data, DeepEquals, MakeRowsFromKvPairs([]common.KvPair{ 335 { 336 Key: []byte("txxxxxxxx_ryyyyyyyy"), 337 Val: []byte("value1"), 338 }, 339 { 340 Key: []byte("txxxxxxxx_rwwwwwwww"), 341 Val: []byte("value2"), 342 }, 343 })) 344 c.Assert(indices, DeepEquals, MakeRowsFromKvPairs([]common.KvPair{ 345 { 346 Key: []byte("txxxxxxxx_izzzzzzzz"), 347 Val: []byte("index1"), 348 }, 349 })) 350 c.Assert(dataChecksum.SumKVS(), Equals, uint64(2)) 351 c.Assert(indexChecksum.SumKVS(), Equals, uint64(1)) 352 } 353 354 type benchSQL2KVSuite struct { 355 row []types.Datum 356 colPerm []int 357 encoder Encoder 358 logger log.Logger 359 } 360 361 var _ = Suite(&benchSQL2KVSuite{}) 362 363 func (s *benchSQL2KVSuite) SetUpTest(c *C) { 364 // First, create the table info corresponding to TPC-C's "CUSTOMER" table. 365 p := parser.New() 366 se := mock.NewContext() 367 node, err := p.ParseOneStmt(` 368 create table bmsql_customer( 369 c_w_id integer not null, 370 c_d_id integer not null, 371 c_id integer not null, 372 c_discount decimal(4,4), 373 c_credit char(2), 374 c_last varchar(16), 375 c_first varchar(16), 376 c_credit_lim decimal(12,2), 377 c_balance decimal(12,2), 378 c_ytd_payment decimal(12,2), 379 c_payment_cnt integer, 380 c_delivery_cnt integer, 381 c_street_1 varchar(20), 382 c_street_2 varchar(20), 383 c_city varchar(20), 384 c_state char(2), 385 c_zip char(9), 386 c_phone char(16), 387 c_since timestamp, 388 c_middle char(2), 389 c_data varchar(500), 390 primary key (c_w_id, c_d_id, c_id) 391 ); 392 `, "", "") 393 c.Assert(err, IsNil) 394 tableInfo, err := ddl.MockTableInfo(se, node.(*ast.CreateTableStmt), 123456) 395 c.Assert(err, IsNil) 396 tableInfo.State = model.StatePublic 397 398 // Construct the corresponding KV encoder. 399 tbl, err := tables.TableFromMeta(NewPanickingAllocators(0), tableInfo) 400 c.Assert(err, IsNil) 401 s.encoder, err = NewTableKVEncoder(tbl, &SessionOptions{SysVars: map[string]string{"tidb_row_format_version": "2"}}) 402 s.logger = log.Logger{Logger: zap.NewNop()} 403 404 // Prepare the row to insert. 405 s.row = []types.Datum{ 406 types.NewIntDatum(15), 407 types.NewIntDatum(10), 408 types.NewIntDatum(3000), 409 types.NewStringDatum("0.3646"), 410 types.NewStringDatum("GC"), 411 types.NewStringDatum("CALLYPRIANTI"), 412 types.NewStringDatum("Rg6mDFlVnP5yh"), 413 types.NewStringDatum("50000.0"), 414 types.NewStringDatum("-10.0"), 415 types.NewStringDatum("10.0"), 416 types.NewIntDatum(1), 417 types.NewIntDatum(0), 418 types.NewStringDatum("aJK7CuRnE0NUxNHSX"), 419 types.NewStringDatum("Q1rps77cXYoj"), 420 types.NewStringDatum("MigXbS6UoUS"), 421 types.NewStringDatum("UJ"), 422 types.NewStringDatum("638611111"), 423 types.NewStringDatum("7743262784364376"), 424 types.NewStringDatum("2020-02-05 19:29:58.903970"), 425 types.NewStringDatum("OE"), 426 types.NewStringDatum("H5p3dpjp7uu8n1l3j0o1buecfV6FngNNgftpNALDhOzJaSzMCMlrQwXuvLAFPIFg215D3wAYB62kiixIuasfbD729oq8TwgKzPPsx8kHE1b4AdhHwpCml3ELKiwuNGQl7CcBQOiq6aFEMMHzjGwQyXwGey0wutjp2KP3Nd4qj3FHtmHbsD8cJ0pH9TswNmdQBgXsFPZeJJhsG3rTimQpS9Tmn3vNeI9fFas3ClDZuQtBjqoTJlyzmBIYT8HeV3TuS93TNFDaXZpQqh8HsvlPq4uTTLOO9CguiY29zlSmIjkZYtva3iscG3YDOQVLeGpP9dtqEJwlRvJ4oe9jWkvRMlCeslSNEuzLxjUBtJBnGRFAzJF6RMlIWCkdCpIhcnIy3jUEsxTuiAU3hsZxUjLg2dnOG62h5qR"), 427 } 428 s.colPerm = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, -1} 429 } 430 431 // Run `go test github.com/pingcap/tidb-lightning/lightning/backend -check.b -test.v` to get benchmark result. 432 func (s *benchSQL2KVSuite) BenchmarkSQL2KV(c *C) { 433 for i := 0; i < c.N; i++ { 434 rows, err := s.encoder.Encode(s.logger, s.row, 1, s.colPerm) 435 c.Assert(err, IsNil) 436 c.Assert(rows, HasLen, 2) 437 } 438 }