github.com/XiaoMi/Gaea@v1.2.5/proxy/plan/plan_test.go (about) 1 // Copyright 2019 The Gaea Authors. All Rights Reserved. 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package plan 16 17 import ( 18 "encoding/json" 19 "sync/atomic" 20 "testing" 21 22 "github.com/XiaoMi/Gaea/backend" 23 "github.com/XiaoMi/Gaea/models" 24 "github.com/XiaoMi/Gaea/parser" 25 "github.com/XiaoMi/Gaea/proxy/router" 26 "github.com/XiaoMi/Gaea/proxy/sequence" 27 ) 28 29 type SQLTestcase struct { 30 db string 31 sql string 32 sqls map[string]map[string][]string 33 hasErr bool 34 } 35 36 type OrderSequence struct { 37 v int64 38 db string 39 table string 40 pkName string 41 } 42 43 type PlanInfo struct { 44 phyDBs map[string]string 45 rt *router.Router 46 seqs *sequence.SequenceManager 47 } 48 49 func NewOrderSequence(db, table, pkName string) *OrderSequence { 50 return &OrderSequence{ 51 db: db, 52 table: table, 53 pkName: pkName, 54 } 55 } 56 57 func (s *OrderSequence) GetPKName() string { 58 return s.pkName 59 } 60 61 func (s *OrderSequence) NextSeq() (int64, error) { 62 newInt := atomic.AddInt64(&s.v, 1) 63 return newInt, nil 64 } 65 66 // 获取使用TiDB parser测试SQL改写结果的测试函数 67 func getTestFunc(info *PlanInfo, test SQLTestcase) func(t *testing.T) { 68 return func(t *testing.T) { 69 stmt, err := parser.ParseSQL(test.sql) 70 if err != nil { 71 if test.hasErr { 72 t.Logf("parse sql error: %v", err) 73 return 74 } 75 t.Fatalf("parse sql error: %v", err) 76 } 77 78 p, err := BuildPlan(stmt, info.phyDBs, test.db, test.sql, info.rt, info.seqs) 79 if err != nil { 80 if test.hasErr { 81 t.Logf("BuildPlan got expect error, sql: %s, err: %v", test.sql, err) 82 return 83 } 84 t.Fatalf("BuildPlan error, sql: %s, err: %v", test.sql, err) 85 } 86 87 var actualSQLs map[string]map[string][]string 88 switch plan := p.(type) { 89 case *SelectPlan: 90 actualSQLs = plan.GetSQLs() 91 case *InsertPlan: 92 actualSQLs = plan.sqls 93 case *UpdatePlan: 94 actualSQLs = plan.sqls 95 case *DeletePlan: 96 actualSQLs = plan.sqls 97 case *ExplainPlan: 98 actualSQLs = plan.sqls 99 case *UnshardPlan: 100 actualSQLs = make(map[string]map[string][]string) 101 dbSQLs := make(map[string][]string) 102 if db, ok := info.phyDBs[plan.db]; ok { 103 plan.db = db 104 } 105 dbSQLs[plan.db] = []string{plan.sql} 106 actualSQLs[backend.DefaultSlice] = dbSQLs 107 } 108 109 if actualSQLs == nil { 110 t.Fatalf("get sqls error: %v", err) 111 } 112 113 if !checkSQLs(test.sqls, actualSQLs) { 114 t.Errorf("not equal, expect: %v, actual: %v", test.sqls, actualSQLs) 115 } 116 } 117 } 118 119 func createNamespace(nsStr string) (*models.Namespace, error) { 120 ns := &models.Namespace{} 121 err := json.Unmarshal([]byte(nsStr), ns) 122 if err != nil { 123 return nil, err 124 } 125 if err := ns.Verify(); err != nil { 126 return nil, err 127 } 128 return ns, nil 129 } 130 131 func createRouter(nsCfg *models.Namespace) (*router.Router, error) { 132 return router.NewRouter(nsCfg) 133 } 134 135 func createSequenceManager(nsCfg *models.Namespace) (*sequence.SequenceManager, error) { 136 sequences := sequence.NewSequenceManager() 137 for _, v := range nsCfg.GlobalSequences { 138 seq := NewOrderSequence(v.DB, v.Table, v.PKName) 139 sequences.SetSequence(v.DB, v.Table, seq) 140 } 141 return sequences, nil 142 } 143 144 func checkSQLs(expect, actual map[string]map[string][]string) bool { 145 aContainsB := func(a, b map[string]map[string][]string) bool { 146 for sliceName, dbSQLs := range a { 147 if len(b[sliceName]) == 0 { 148 return false 149 } 150 for dbName, sqls := range dbSQLs { 151 if len(b[sliceName][dbName]) == 0 { 152 return false 153 } 154 for i, sql := range sqls { 155 if b[sliceName][dbName][i] != sql { 156 return false 157 } 158 } 159 } 160 } 161 return true 162 } 163 164 return aContainsB(expect, actual) && aContainsB(actual, expect) 165 } 166 167 func preparePlanInfo() (*PlanInfo, error) { 168 nsStr := ` 169 { 170 "name": "gaea_namespace_1", 171 "online": true, 172 "read_only": true, 173 "allowed_dbs": { 174 "db_ks": true, 175 "db_mycat": true 176 }, 177 "default_phy_dbs": { 178 "db_ks": "db_ks", 179 "db_mycat": "db_mycat_0" 180 }, 181 "slices": [ 182 { 183 "name": "slice-0", 184 "user_name": "root", 185 "password": "root", 186 "master": "127.0.0.1:3306", 187 "capacity": 64, 188 "max_capacity": 128, 189 "idle_timeout": 3600 190 }, 191 { 192 "name": "slice-1", 193 "user_name": "root", 194 "password": "root", 195 "master": "127.0.0.1:3307", 196 "capacity": 64, 197 "max_capacity": 128, 198 "idle_timeout": 3600 199 } 200 ], 201 "shard_rules": [ 202 { 203 "db": "db_ks", 204 "table": "tbl_ks", 205 "type": "mod", 206 "key": "id", 207 "locations": [ 208 2, 209 2 210 ], 211 "slices": [ 212 "slice-0", 213 "slice-1" 214 ] 215 }, 216 { 217 "db": "db_ks", 218 "table": "tbl_ks_child", 219 "type": "linked", 220 "key": "id", 221 "parent_table": "tbl_ks" 222 }, 223 { 224 "db": "db_ks", 225 "table": "tbl_ks_user_child", 226 "type": "linked", 227 "key": "user_id", 228 "parent_table": "tbl_ks" 229 }, 230 { 231 "db": "db_ks", 232 "table": "tbl_ks_global_one", 233 "type": "global", 234 "locations": [ 235 2, 236 2 237 ], 238 "slices": [ 239 "slice-0", 240 "slice-1" 241 ] 242 }, 243 { 244 "db": "db_ks", 245 "table": "tbl_ks_global_two", 246 "type": "global", 247 "locations": [ 248 2, 249 2 250 ], 251 "slices": [ 252 "slice-0", 253 "slice-1" 254 ] 255 }, 256 { 257 "db": "db_ks", 258 "table": "tbl_ks_range", 259 "type": "range", 260 "key": "id", 261 "locations": [ 262 2, 263 2 264 ], 265 "slices": [ 266 "slice-0", 267 "slice-1" 268 ], 269 "table_row_limit": 100 270 }, 271 { 272 "db": "db_ks", 273 "table": "tbl_ks_year", 274 "type": "date_year", 275 "key": "create_time", 276 "slices": [ 277 "slice-0", 278 "slice-1" 279 ], 280 "date_range": [ 281 "2014-2017", 282 "2018-2019" 283 ] 284 }, 285 { 286 "db": "db_ks", 287 "table": "tbl_ks_month", 288 "type": "date_month", 289 "key": "create_time", 290 "slices": [ 291 "slice-0", 292 "slice-1" 293 ], 294 "date_range": [ 295 "201405-201406", 296 "201408-201409" 297 ] 298 }, 299 { 300 "db": "db_ks", 301 "table": "tbl_ks_day", 302 "type": "date_day", 303 "key": "create_time", 304 "slices": [ 305 "slice-0", 306 "slice-1" 307 ], 308 "date_range": [ 309 "20140901-20140905", 310 "20140907-20140908" 311 ] 312 }, 313 { 314 "db": "db_ks", 315 "table": "TBL_KS_UPPERCASE", 316 "type": "mod", 317 "key": "id", 318 "locations": [ 319 2, 320 2 321 ], 322 "slices": [ 323 "slice-0", 324 "slice-1" 325 ] 326 }, 327 { 328 "db": "db_ks", 329 "table": "TBL_KS_UPPERCASE_CHILD", 330 "type": "linked", 331 "key": "ID", 332 "parent_table": "TBL_KS_UPPERCASE" 333 }, 334 { 335 "db": "db_mycat", 336 "table": "tbl_mycat", 337 "type": "mycat_mod", 338 "key": "id", 339 "locations": [ 340 2, 341 2 342 ], 343 "slices": [ 344 "slice-0", 345 "slice-1" 346 ], 347 "databases": [ 348 "db_mycat_[0-3]" 349 ] 350 }, 351 { 352 "db": "db_mycat", 353 "table": "tbl_mycat_child", 354 "type": "linked", 355 "parent_table": "tbl_mycat", 356 "key": "id" 357 }, 358 { 359 "db": "db_mycat", 360 "table": "tbl_mycat_user_child", 361 "type": "linked", 362 "parent_table": "tbl_mycat", 363 "key": "user_id" 364 }, 365 { 366 "db": "db_mycat", 367 "table": "tbl_mycat_murmur", 368 "type": "mycat_murmur", 369 "key": "id", 370 "locations": [ 371 2, 372 2 373 ], 374 "slices": [ 375 "slice-0", 376 "slice-1" 377 ], 378 "databases": [ 379 "db_mycat_0","db_mycat_1","db_mycat_2","db_mycat_3" 380 ], 381 "seed": "0", 382 "virtual_bucket_times": "160" 383 }, 384 { 385 "db": "db_mycat", 386 "table": "tbl_mycat_long", 387 "type": "mycat_long", 388 "key": "id", 389 "locations": [ 390 2, 391 2 392 ], 393 "slices": [ 394 "slice-0", 395 "slice-1" 396 ], 397 "databases": [ 398 "db_mycat_[0-3]" 399 ], 400 "partition_count": "4", 401 "partition_length": "256" 402 }, 403 { 404 "db": "db_mycat", 405 "table": "tbl_mycat_global_one", 406 "type": "global", 407 "locations": [ 408 2, 409 2 410 ], 411 "slices": [ 412 "slice-0", 413 "slice-1" 414 ], 415 "databases": [ 416 "db_mycat_[0-3]" 417 ] 418 }, 419 { 420 "db": "db_mycat", 421 "table": "tbl_mycat_global_two", 422 "type": "global", 423 "locations": [ 424 2, 425 2 426 ], 427 "slices": [ 428 "slice-0", 429 "slice-1" 430 ], 431 "databases": [ 432 "db_mycat_[0-3]" 433 ] 434 }, 435 { 436 "db": "db_mycat", 437 "table": "tbl_mycat_string", 438 "type": "mycat_string", 439 "key": "id", 440 "locations": [ 441 2, 442 2 443 ], 444 "slices": [ 445 "slice-0", 446 "slice-1" 447 ], 448 "databases": [ 449 "db_mycat_[0-3]" 450 ], 451 "partition_count": "4", 452 "partition_length": "256", 453 "hash_slice": "20" 454 } 455 ], 456 "global_sequences": [ 457 { 458 "db": "db_mycat", 459 "table": "tbl_mycat", 460 "type": "test", 461 "pk_name": "id" 462 }, 463 { 464 "db": "db_ks", 465 "table": "tbl_ks", 466 "type": "test", 467 "pk_name": "user_id" 468 } 469 ], 470 "users": [ 471 { 472 "user_name": "test_shard_hash", 473 "password": "test_shard_hash", 474 "namespace": "gaea_namespace_1", 475 "rw_flag": 2, 476 "rw_split": 1 477 } 478 ], 479 "default_slice": "slice-0" 480 }` 481 nsModel, err := createNamespace(nsStr) 482 if err != nil { 483 return nil, err 484 } 485 486 rt, err := createRouter(nsModel) 487 if err != nil { 488 return nil, err 489 } 490 491 seqs, err := createSequenceManager(nsModel) 492 if err != nil { 493 return nil, err 494 } 495 496 planInfo := &PlanInfo{ 497 phyDBs: nsModel.DefaultPhyDBS, 498 rt: rt, 499 seqs: seqs, 500 } 501 return planInfo, nil 502 }