github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/lightning/restore/tidb_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 restore 15 16 import ( 17 "context" 18 "testing" 19 20 "github.com/DATA-DOG/go-sqlmock" 21 "github.com/go-sql-driver/mysql" 22 . "github.com/pingcap/check" 23 "github.com/pingcap/errors" 24 "github.com/pingcap/parser/ast" 25 "github.com/pingcap/parser/model" 26 tmysql "github.com/pingcap/parser/mysql" 27 "github.com/pingcap/tidb/ddl" 28 "github.com/pingcap/tidb/util/mock" 29 30 "github.com/pingcap/br/pkg/lightning/checkpoints" 31 "github.com/pingcap/br/pkg/lightning/glue" 32 "github.com/pingcap/br/pkg/lightning/metric" 33 "github.com/pingcap/br/pkg/lightning/mydump" 34 ) 35 36 var _ = Suite(&tidbSuite{}) 37 38 type tidbSuite struct { 39 mockDB sqlmock.Sqlmock 40 timgr *TiDBManager 41 tiGlue glue.Glue 42 } 43 44 func TestTiDB(t *testing.T) { 45 TestingT(t) 46 } 47 48 func (s *tidbSuite) SetUpTest(c *C) { 49 db, mock, err := sqlmock.New() 50 c.Assert(err, IsNil) 51 52 s.mockDB = mock 53 defaultSQLMode, err := tmysql.GetSQLMode(tmysql.DefaultSQLMode) 54 c.Assert(err, IsNil) 55 56 s.timgr = NewTiDBManagerWithDB(db, defaultSQLMode) 57 s.tiGlue = glue.NewExternalTiDBGlue(db, defaultSQLMode) 58 } 59 60 func (s *tidbSuite) TearDownTest(c *C) { 61 s.timgr.Close() 62 c.Assert(s.mockDB.ExpectationsWereMet(), IsNil) 63 } 64 65 func (s *tidbSuite) TestCreateTableIfNotExistsStmt(c *C) { 66 dbName := "testdb" 67 createTableIfNotExistsStmt := func(createTable, tableName string) []string { 68 res, err := createTableIfNotExistsStmt(s.tiGlue.GetParser(), createTable, dbName, tableName) 69 c.Assert(err, IsNil) 70 return res 71 } 72 73 c.Assert( 74 createTableIfNotExistsStmt("CREATE TABLE `foo`(`bar` TINYINT(1));", "foo"), 75 DeepEquals, 76 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`foo` (`bar` TINYINT(1));"}, 77 ) 78 79 c.Assert( 80 createTableIfNotExistsStmt("CREATE TABLE IF NOT EXISTS `foo`(`bar` TINYINT(1));", "foo"), 81 DeepEquals, 82 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`foo` (`bar` TINYINT(1));"}, 83 ) 84 85 // case insensitive 86 c.Assert( 87 createTableIfNotExistsStmt("/* cOmmEnt */ creAte tablE `fOo`(`bar` TinyinT(1));", "fOo"), 88 DeepEquals, 89 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`fOo` (`bar` TINYINT(1));"}, 90 ) 91 92 c.Assert( 93 createTableIfNotExistsStmt("/* coMMenT */ crEatE tAble If not EXISts `FoO`(`bAR` tiNyInT(1));", "FoO"), 94 DeepEquals, 95 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`FoO` (`bAR` TINYINT(1));"}, 96 ) 97 98 // only one "CREATE TABLE" is replaced 99 c.Assert( 100 createTableIfNotExistsStmt("CREATE TABLE `foo`(`bar` INT(1) COMMENT 'CREATE TABLE');", "foo"), 101 DeepEquals, 102 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`foo` (`bar` INT(1) COMMENT 'CREATE TABLE');"}, 103 ) 104 105 // test clustered index consistency 106 c.Assert( 107 createTableIfNotExistsStmt("CREATE TABLE `foo`(`bar` INT(1) PRIMARY KEY CLUSTERED COMMENT 'CREATE TABLE');", "foo"), 108 DeepEquals, 109 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`foo` (`bar` INT(1) PRIMARY KEY /*T![clustered_index] CLUSTERED */ COMMENT 'CREATE TABLE');"}, 110 ) 111 c.Assert( 112 createTableIfNotExistsStmt("CREATE TABLE `foo`(`bar` INT(1) COMMENT 'CREATE TABLE', PRIMARY KEY (`bar`) NONCLUSTERED);", "foo"), 113 DeepEquals, 114 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`foo` (`bar` INT(1) COMMENT 'CREATE TABLE',PRIMARY KEY(`bar`) /*T![clustered_index] NONCLUSTERED */);"}, 115 ) 116 c.Assert( 117 createTableIfNotExistsStmt("CREATE TABLE `foo`(`bar` INT(1) PRIMARY KEY /*T![clustered_index] NONCLUSTERED */ COMMENT 'CREATE TABLE');", "foo"), 118 DeepEquals, 119 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`foo` (`bar` INT(1) PRIMARY KEY /*T![clustered_index] NONCLUSTERED */ COMMENT 'CREATE TABLE');"}, 120 ) 121 c.Assert( 122 createTableIfNotExistsStmt("CREATE TABLE `foo`(`bar` INT(1) COMMENT 'CREATE TABLE', PRIMARY KEY (`bar`) /*T![clustered_index] CLUSTERED */);", "foo"), 123 DeepEquals, 124 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`foo` (`bar` INT(1) COMMENT 'CREATE TABLE',PRIMARY KEY(`bar`) /*T![clustered_index] CLUSTERED */);"}, 125 ) 126 127 c.Assert( 128 createTableIfNotExistsStmt("CREATE TABLE `foo`(`bar` INT(1) PRIMARY KEY AUTO_RANDOM(2) COMMENT 'CREATE TABLE');", "foo"), 129 DeepEquals, 130 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`foo` (`bar` INT(1) PRIMARY KEY /*T![auto_rand] AUTO_RANDOM(2) */ COMMENT 'CREATE TABLE');"}, 131 ) 132 133 // upper case becomes shorter 134 c.Assert( 135 createTableIfNotExistsStmt("CREATE TABLE `ſ`(`ı` TINYINT(1));", "ſ"), 136 DeepEquals, 137 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`ſ` (`ı` TINYINT(1));"}, 138 ) 139 140 // upper case becomes longer 141 c.Assert( 142 createTableIfNotExistsStmt("CREATE TABLE `ɑ`(`ȿ` TINYINT(1));", "ɑ"), 143 DeepEquals, 144 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`ɑ` (`ȿ` TINYINT(1));"}, 145 ) 146 147 // non-utf-8 148 c.Assert( 149 createTableIfNotExistsStmt("CREATE TABLE `\xcc\xcc\xcc`(`\xdd\xdd\xdd` TINYINT(1));", "\xcc\xcc\xcc"), 150 DeepEquals, 151 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`\xcc\xcc\xcc` (`ÝÝÝ` TINYINT(1));"}, 152 ) 153 154 // renaming a table 155 c.Assert( 156 createTableIfNotExistsStmt("create table foo(x int);", "ba`r"), 157 DeepEquals, 158 []string{"CREATE TABLE IF NOT EXISTS `testdb`.`ba``r` (`x` INT);"}, 159 ) 160 161 // conditional comments 162 c.Assert( 163 createTableIfNotExistsStmt(` 164 /*!40101 SET NAMES binary*/; 165 /*!40014 SET FOREIGN_KEY_CHECKS=0*/; 166 CREATE TABLE x.y (z double) ENGINE=InnoDB AUTO_INCREMENT=8343230 DEFAULT CHARSET=utf8; 167 `, "m"), 168 DeepEquals, 169 []string{ 170 "SET NAMES 'binary';", 171 "SET @@SESSION.`FOREIGN_KEY_CHECKS`=0;", 172 "CREATE TABLE IF NOT EXISTS `testdb`.`m` (`z` DOUBLE) ENGINE = InnoDB AUTO_INCREMENT = 8343230 DEFAULT CHARACTER SET = UTF8;", 173 }, 174 ) 175 176 // create view 177 c.Assert( 178 createTableIfNotExistsStmt(` 179 /*!40101 SET NAMES binary*/; 180 DROP TABLE IF EXISTS v2; 181 DROP VIEW IF EXISTS v2; 182 SET @PREV_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT; 183 SET @PREV_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS; 184 SET @PREV_COLLATION_CONNECTION=@@COLLATION_CONNECTION; 185 SET character_set_client = utf8; 186 SET character_set_results = utf8; 187 SET collation_connection = utf8_general_ci; 188 CREATE ALGORITHM=UNDEFINED DEFINER=root@192.168.198.178 SQL SECURITY DEFINER VIEW v2 (s) AS SELECT s FROM db1.v1 WHERE i<2; 189 SET character_set_client = @PREV_CHARACTER_SET_CLIENT; 190 SET character_set_results = @PREV_CHARACTER_SET_RESULTS; 191 SET collation_connection = @PREV_COLLATION_CONNECTION; 192 `, "m"), 193 DeepEquals, 194 []string{ 195 "SET NAMES 'binary';", 196 "DROP TABLE IF EXISTS `testdb`.`m`;", 197 "DROP VIEW IF EXISTS `testdb`.`m`;", 198 "SET @`PREV_CHARACTER_SET_CLIENT`=@@`character_set_client`;", 199 "SET @`PREV_CHARACTER_SET_RESULTS`=@@`character_set_results`;", 200 "SET @`PREV_COLLATION_CONNECTION`=@@`collation_connection`;", 201 "SET @@SESSION.`character_set_client`=`utf8`;", 202 "SET @@SESSION.`character_set_results`=`utf8`;", 203 "SET @@SESSION.`collation_connection`=`utf8_general_ci`;", 204 "CREATE ALGORITHM = UNDEFINED DEFINER = `root`@`192.168.198.178` SQL SECURITY DEFINER VIEW `testdb`.`m` (`s`) AS SELECT `s` FROM `db1`.`v1` WHERE `i`<2;", 205 "SET @@SESSION.`character_set_client`=@`PREV_CHARACTER_SET_CLIENT`;", 206 "SET @@SESSION.`character_set_results`=@`PREV_CHARACTER_SET_RESULTS`;", 207 "SET @@SESSION.`collation_connection`=@`PREV_COLLATION_CONNECTION`;", 208 }, 209 ) 210 } 211 212 func (s *tidbSuite) TestInitSchema(c *C) { 213 ctx := context.Background() 214 215 s.mockDB. 216 ExpectExec("CREATE DATABASE IF NOT EXISTS `db`"). 217 WillReturnResult(sqlmock.NewResult(1, 1)) 218 s.mockDB. 219 ExpectExec("\\QCREATE TABLE IF NOT EXISTS `db`.`t1` (`a` INT PRIMARY KEY,`b` VARCHAR(200));\\E"). 220 WillReturnResult(sqlmock.NewResult(2, 1)) 221 s.mockDB. 222 ExpectExec("\\QSET @@SESSION.`FOREIGN_KEY_CHECKS`=0;\\E"). 223 WillReturnResult(sqlmock.NewResult(0, 0)) 224 s.mockDB. 225 ExpectExec("\\QCREATE TABLE IF NOT EXISTS `db`.`t2` (`xx` TEXT) AUTO_INCREMENT = 11203;\\E"). 226 WillReturnResult(sqlmock.NewResult(2, 1)) 227 s.mockDB. 228 ExpectClose() 229 230 s.mockDB.MatchExpectationsInOrder(false) // maps are unordered. 231 err := InitSchema(ctx, s.tiGlue, "db", map[string]string{ 232 "t1": "create table t1 (a int primary key, b varchar(200));", 233 "t2": "/*!40014 SET FOREIGN_KEY_CHECKS=0*/;CREATE TABLE `db`.`t2` (xx TEXT) AUTO_INCREMENT=11203;", 234 }) 235 s.mockDB.MatchExpectationsInOrder(true) 236 c.Assert(err, IsNil) 237 } 238 239 func (s *tidbSuite) TestInitSchemaSyntaxError(c *C) { 240 ctx := context.Background() 241 242 s.mockDB. 243 ExpectExec("CREATE DATABASE IF NOT EXISTS `db`"). 244 WillReturnResult(sqlmock.NewResult(1, 1)) 245 s.mockDB. 246 ExpectClose() 247 248 err := InitSchema(ctx, s.tiGlue, "db", map[string]string{ 249 "t1": "create table `t1` with invalid syntax;", 250 }) 251 c.Assert(err, NotNil) 252 } 253 254 func (s *tidbSuite) TestInitSchemaErrorLost(c *C) { 255 ctx := context.Background() 256 257 s.mockDB. 258 ExpectExec("CREATE DATABASE IF NOT EXISTS `db`"). 259 WillReturnResult(sqlmock.NewResult(1, 1)) 260 261 s.mockDB. 262 ExpectExec("CREATE TABLE IF NOT EXISTS.*"). 263 WillReturnError(&mysql.MySQLError{ 264 Number: tmysql.ErrTooBigFieldlength, 265 Message: "Column length too big", 266 }) 267 268 s.mockDB. 269 ExpectClose() 270 271 err := InitSchema(ctx, s.tiGlue, "db", map[string]string{ 272 "t1": "create table `t1` (a int);", 273 "t2": "create table t2 (a int primary key, b varchar(200));", 274 }) 275 c.Assert(err, ErrorMatches, ".*Column length too big.*") 276 } 277 278 func (s *tidbSuite) TestInitSchemaUnsupportedSchemaError(c *C) { 279 ctx := context.Background() 280 281 s.mockDB. 282 ExpectExec("CREATE DATABASE IF NOT EXISTS `db`"). 283 WillReturnResult(sqlmock.NewResult(1, 1)) 284 s.mockDB. 285 ExpectExec("CREATE TABLE IF NOT EXISTS `db`.`t1`.*"). 286 WillReturnError(&mysql.MySQLError{ 287 Number: tmysql.ErrTooBigFieldlength, 288 Message: "Column length too big", 289 }) 290 s.mockDB. 291 ExpectClose() 292 293 err := InitSchema(ctx, s.tiGlue, "db", map[string]string{ 294 "t1": "create table `t1` (a VARCHAR(999999999));", 295 }) 296 c.Assert(err, ErrorMatches, ".*Column length too big.*") 297 } 298 299 func (s *tidbSuite) TestDropTable(c *C) { 300 ctx := context.Background() 301 302 s.mockDB. 303 ExpectExec("DROP TABLE `db`.`table`"). 304 WillReturnResult(sqlmock.NewResult(1, 1)) 305 s.mockDB. 306 ExpectClose() 307 308 err := s.timgr.DropTable(ctx, "`db`.`table`") 309 c.Assert(err, IsNil) 310 } 311 312 func (s *tidbSuite) TestLoadSchemaInfo(c *C) { 313 ctx := context.Background() 314 315 tableCntBefore := metric.ReadCounter(metric.TableCounter.WithLabelValues(metric.TableStatePending, metric.TableResultSuccess)) 316 317 // Prepare the mock reply. 318 nodes, _, err := s.timgr.parser.Parse( 319 "CREATE TABLE `t1` (`a` INT PRIMARY KEY);"+ 320 "CREATE TABLE `t2` (`b` VARCHAR(20), `c` BOOL, KEY (`b`, `c`));"+ 321 // an extra table that not exists in dbMetas 322 "CREATE TABLE `t3` (`d` VARCHAR(20), `e` BOOL);", 323 "", "") 324 c.Assert(err, IsNil) 325 tableInfos := make([]*model.TableInfo, 0, len(nodes)) 326 sctx := mock.NewContext() 327 for i, node := range nodes { 328 c.Assert(node, FitsTypeOf, &ast.CreateTableStmt{}) 329 info, err := ddl.MockTableInfo(sctx, node.(*ast.CreateTableStmt), int64(i+100)) 330 c.Assert(err, IsNil) 331 info.State = model.StatePublic 332 tableInfos = append(tableInfos, info) 333 } 334 335 dbMetas := []*mydump.MDDatabaseMeta{ 336 { 337 Name: "db", 338 Tables: []*mydump.MDTableMeta{ 339 { 340 DB: "db", 341 Name: "t1", 342 }, 343 { 344 DB: "db", 345 Name: "t2", 346 }, 347 }, 348 }, 349 } 350 351 loaded, err := LoadSchemaInfo(ctx, dbMetas, func(ctx context.Context, schema string) ([]*model.TableInfo, error) { 352 c.Assert(schema, Equals, "db") 353 return tableInfos, nil 354 }) 355 c.Assert(err, IsNil) 356 c.Assert(loaded, DeepEquals, map[string]*checkpoints.TidbDBInfo{ 357 "db": { 358 Name: "db", 359 Tables: map[string]*checkpoints.TidbTableInfo{ 360 "t1": { 361 ID: 100, 362 DB: "db", 363 Name: "t1", 364 Core: tableInfos[0], 365 }, 366 "t2": { 367 ID: 101, 368 DB: "db", 369 Name: "t2", 370 Core: tableInfos[1], 371 }, 372 }, 373 }, 374 }) 375 376 tableCntAfter := metric.ReadCounter(metric.TableCounter.WithLabelValues(metric.TableStatePending, metric.TableResultSuccess)) 377 378 c.Assert(tableCntAfter-tableCntBefore, Equals, 2.0) 379 } 380 381 func (s *tidbSuite) TestLoadSchemaInfoMissing(c *C) { 382 ctx := context.Background() 383 384 _, err := LoadSchemaInfo(ctx, []*mydump.MDDatabaseMeta{{Name: "asdjalsjdlas"}}, func(ctx context.Context, schema string) ([]*model.TableInfo, error) { 385 return nil, errors.Errorf("[schema:1049]Unknown database '%s'", schema) 386 }) 387 c.Assert(err, ErrorMatches, ".*Unknown database.*") 388 } 389 390 func (s *tidbSuite) TestGetGCLifetime(c *C) { 391 ctx := context.Background() 392 393 s.mockDB. 394 ExpectQuery("\\QSELECT VARIABLE_VALUE FROM mysql.tidb WHERE VARIABLE_NAME = 'tikv_gc_life_time'\\E"). 395 WillReturnRows(sqlmock.NewRows([]string{"VARIABLE_VALUE"}).AddRow("10m")) 396 s.mockDB. 397 ExpectClose() 398 399 res, err := ObtainGCLifeTime(ctx, s.timgr.db) 400 c.Assert(err, IsNil) 401 c.Assert(res, Equals, "10m") 402 } 403 404 func (s *tidbSuite) TestSetGCLifetime(c *C) { 405 ctx := context.Background() 406 407 s.mockDB. 408 ExpectExec("\\QUPDATE mysql.tidb SET VARIABLE_VALUE = ? WHERE VARIABLE_NAME = 'tikv_gc_life_time'\\E"). 409 WithArgs("12m"). 410 WillReturnResult(sqlmock.NewResult(1, 1)) 411 s.mockDB. 412 ExpectClose() 413 414 err := UpdateGCLifeTime(ctx, s.timgr.db, "12m") 415 c.Assert(err, IsNil) 416 } 417 418 func (s *tidbSuite) TestAlterAutoInc(c *C) { 419 ctx := context.Background() 420 421 s.mockDB. 422 ExpectExec("\\QALTER TABLE `db`.`table` AUTO_INCREMENT=12345\\E"). 423 WillReturnResult(sqlmock.NewResult(1, 1)) 424 s.mockDB. 425 ExpectClose() 426 427 err := AlterAutoIncrement(ctx, s.tiGlue.GetSQLExecutor(), "`db`.`table`", 12345) 428 c.Assert(err, IsNil) 429 } 430 431 func (s *tidbSuite) TestAlterAutoRandom(c *C) { 432 ctx := context.Background() 433 434 s.mockDB. 435 ExpectExec("\\QALTER TABLE `db`.`table` AUTO_RANDOM_BASE=12345\\E"). 436 WillReturnResult(sqlmock.NewResult(1, 1)) 437 s.mockDB. 438 ExpectClose() 439 440 err := AlterAutoRandom(ctx, s.tiGlue.GetSQLExecutor(), "`db`.`table`", 12345) 441 c.Assert(err, IsNil) 442 } 443 444 func (s *tidbSuite) TestObtainRowFormatVersionSucceed(c *C) { 445 ctx := context.Background() 446 447 s.mockDB. 448 ExpectBegin() 449 s.mockDB. 450 ExpectQuery(`SHOW VARIABLES WHERE Variable_name IN \(.*'tidb_row_format_version'.*\)`). 451 WillReturnRows(sqlmock.NewRows([]string{"Variable_name", "Value"}). 452 AddRow("tidb_row_format_version", "2"). 453 AddRow("max_allowed_packet", "1073741824"). 454 AddRow("div_precision_increment", "10"). 455 AddRow("time_zone", "-08:00"). 456 AddRow("lc_time_names", "ja_JP"). 457 AddRow("default_week_format", "1"). 458 AddRow("block_encryption_mode", "aes-256-cbc"). 459 AddRow("group_concat_max_len", "1073741824")) 460 s.mockDB. 461 ExpectCommit() 462 s.mockDB. 463 ExpectClose() 464 465 sysVars := ObtainImportantVariables(ctx, s.tiGlue.GetSQLExecutor()) 466 c.Assert(sysVars, DeepEquals, map[string]string{ 467 "tidb_row_format_version": "2", 468 "max_allowed_packet": "1073741824", 469 "div_precision_increment": "10", 470 "time_zone": "-08:00", 471 "lc_time_names": "ja_JP", 472 "default_week_format": "1", 473 "block_encryption_mode": "aes-256-cbc", 474 "group_concat_max_len": "1073741824", 475 }) 476 } 477 478 func (s *tidbSuite) TestObtainRowFormatVersionFailure(c *C) { 479 ctx := context.Background() 480 481 s.mockDB. 482 ExpectBegin() 483 s.mockDB. 484 ExpectQuery(`SHOW VARIABLES WHERE Variable_name IN \(.*'tidb_row_format_version'.*\)`). 485 WillReturnRows(sqlmock.NewRows([]string{"Variable_name", "Value"}).AddRow("time_zone", "+00:00")) 486 s.mockDB. 487 ExpectCommit() 488 s.mockDB. 489 ExpectClose() 490 491 sysVars := ObtainImportantVariables(ctx, s.tiGlue.GetSQLExecutor()) 492 c.Assert(sysVars, DeepEquals, map[string]string{ 493 "tidb_row_format_version": "1", 494 "max_allowed_packet": "67108864", 495 "div_precision_increment": "4", 496 "time_zone": "+00:00", 497 "lc_time_names": "en_US", 498 "default_week_format": "0", 499 "block_encryption_mode": "aes-128-ecb", 500 "group_concat_max_len": "1024", 501 }) 502 } 503 504 func (s *tidbSuite) TestObtainNewCollationEnabled(c *C) { 505 ctx := context.Background() 506 507 s.mockDB. 508 ExpectQuery("\\QSELECT variable_value FROM mysql.tidb WHERE variable_name = 'new_collation_enabled'\\E") 509 version := ObtainNewCollationEnabled(ctx, s.tiGlue.GetSQLExecutor()) 510 c.Assert(version, Equals, false) 511 512 kvMap := map[string]bool{ 513 "True": true, 514 "False": false, 515 } 516 for k, v := range kvMap { 517 s.mockDB. 518 ExpectQuery("\\QSELECT variable_value FROM mysql.tidb WHERE variable_name = 'new_collation_enabled'\\E"). 519 WillReturnRows(sqlmock.NewRows([]string{"variable_value"}).AddRow(k)) 520 521 version := ObtainNewCollationEnabled(ctx, s.tiGlue.GetSQLExecutor()) 522 c.Assert(version, Equals, v) 523 } 524 s.mockDB. 525 ExpectClose() 526 }