github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/optimizer/plan/plan_test.go (about) 1 // Copyright 2015 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 plan 15 16 import ( 17 "fmt" 18 "testing" 19 20 . "github.com/insionng/yougam/libraries/pingcap/check" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/ast" 22 "github.com/insionng/yougam/libraries/pingcap/tidb/model" 23 "github.com/insionng/yougam/libraries/pingcap/tidb/mysql" 24 "github.com/insionng/yougam/libraries/pingcap/tidb/parser" 25 "github.com/insionng/yougam/libraries/pingcap/tidb/util/testleak" 26 ) 27 28 var _ = Suite(&testPlanSuite{}) 29 30 func TestT(t *testing.T) { 31 TestingT(t) 32 } 33 34 type testPlanSuite struct{} 35 36 func (s *testPlanSuite) TestRangeBuilder(c *C) { 37 defer testleak.AfterTest(c)() 38 rb := &rangeBuilder{} 39 40 cases := []struct { 41 exprStr string 42 resultStr string 43 }{ 44 { 45 exprStr: "a = 1", 46 resultStr: "[[1 1]]", 47 }, 48 { 49 exprStr: "1 = a", 50 resultStr: "[[1 1]]", 51 }, 52 { 53 exprStr: "a != 1", 54 resultStr: "[[-inf 1) (1 +inf]]", 55 }, 56 { 57 exprStr: "1 != a", 58 resultStr: "[[-inf 1) (1 +inf]]", 59 }, 60 { 61 exprStr: "a > 1", 62 resultStr: "[(1 +inf]]", 63 }, 64 { 65 exprStr: "1 < a", 66 resultStr: "[(1 +inf]]", 67 }, 68 { 69 exprStr: "a >= 1", 70 resultStr: "[[1 +inf]]", 71 }, 72 { 73 exprStr: "1 <= a", 74 resultStr: "[[1 +inf]]", 75 }, 76 { 77 exprStr: "a < 1", 78 resultStr: "[[-inf 1)]", 79 }, 80 { 81 exprStr: "1 > a", 82 resultStr: "[[-inf 1)]", 83 }, 84 { 85 exprStr: "a <= 1", 86 resultStr: "[[-inf 1]]", 87 }, 88 { 89 exprStr: "1 >= a", 90 resultStr: "[[-inf 1]]", 91 }, 92 { 93 exprStr: "(a)", 94 resultStr: "[[-inf 0) (0 +inf]]", 95 }, 96 { 97 exprStr: "a in (1, 3, NULL, 2)", 98 resultStr: "[[<nil> <nil>] [1 1] [2 2] [3 3]]", 99 }, 100 { 101 exprStr: "a between 1 and 2", 102 resultStr: "[[1 2]]", 103 }, 104 { 105 exprStr: "a not between 1 and 2", 106 resultStr: "[[-inf 1) (2 +inf]]", 107 }, 108 { 109 exprStr: "a not between null and 0", 110 resultStr: "[(0 +inf]]", 111 }, 112 { 113 exprStr: "a between 2 and 1", 114 resultStr: "[]", 115 }, 116 { 117 exprStr: "a not between 2 and 1", 118 resultStr: "[[-inf +inf]]", 119 }, 120 { 121 exprStr: "a IS NULL", 122 resultStr: "[[<nil> <nil>]]", 123 }, 124 { 125 exprStr: "a IS NOT NULL", 126 resultStr: "[[-inf +inf]]", 127 }, 128 { 129 exprStr: "a IS TRUE", 130 resultStr: "[[-inf 0) (0 +inf]]", 131 }, 132 { 133 exprStr: "a IS NOT TRUE", 134 resultStr: "[[<nil> <nil>] [0 0]]", 135 }, 136 { 137 exprStr: "a IS FALSE", 138 resultStr: "[[0 0]]", 139 }, 140 { 141 exprStr: "a IS NOT FALSE", 142 resultStr: "[[<nil> 0) (0 +inf]]", 143 }, 144 { 145 exprStr: "a LIKE 'abc%'", 146 resultStr: "[[abc abd)]", 147 }, 148 { 149 exprStr: "a LIKE 'abc_'", 150 resultStr: "[(abc abd)]", 151 }, 152 { 153 exprStr: "a LIKE '%'", 154 resultStr: "[[-inf +inf]]", 155 }, 156 { 157 exprStr: `a LIKE '\%a'`, 158 resultStr: `[[%a %b)]`, 159 }, 160 { 161 exprStr: `a LIKE "\\"`, 162 resultStr: `[[\ ])]`, 163 }, 164 { 165 exprStr: `a LIKE "\\\\a%"`, 166 resultStr: `[[\a \b)]`, 167 }, 168 { 169 exprStr: `a > 0 AND a < 1`, 170 resultStr: `[(0 1)]`, 171 }, 172 { 173 exprStr: `a > 1 AND a < 0`, 174 resultStr: `[]`, 175 }, 176 { 177 exprStr: `a > 1 OR a < 0`, 178 resultStr: `[[-inf 0) (1 +inf]]`, 179 }, 180 { 181 exprStr: `(a > 1 AND a < 2) OR (a > 3 AND a < 4)`, 182 resultStr: `[(1 2) (3 4)]`, 183 }, 184 { 185 exprStr: `(a < 0 OR a > 3) AND (a < 1 OR a > 4)`, 186 resultStr: `[[-inf 0) (4 +inf]]`, 187 }, 188 { 189 exprStr: `a > NULL`, 190 resultStr: `[]`, 191 }, 192 { 193 exprStr: `a IN (8,8,81,45)`, 194 resultStr: `[[8 8] [45 45] [81 81]]`, 195 }, 196 } 197 198 for _, ca := range cases { 199 sql := "select 1 from dual where " + ca.exprStr 200 stmts, err := parser.Parse(sql, "", "") 201 c.Assert(err, IsNil, Commentf("error %v, for expr %s", err, ca.exprStr)) 202 stmt := stmts[0].(*ast.SelectStmt) 203 result := rb.build(stmt.Where) 204 c.Assert(rb.err, IsNil) 205 got := fmt.Sprintf("%v", result) 206 c.Assert(got, Equals, ca.resultStr, Commentf("differen for expr %s", ca.exprStr)) 207 } 208 } 209 210 func (s *testPlanSuite) TestFilterRate(c *C) { 211 defer testleak.AfterTest(c)() 212 cases := []struct { 213 expr string 214 rate float64 215 }{ 216 {expr: "a = 1", rate: rateEqual}, 217 {expr: "a > 1", rate: rateGreaterOrLess}, 218 {expr: "a between 1 and 100", rate: rateBetween}, 219 {expr: "a is null", rate: rateIsNull}, 220 {expr: "a is not null", rate: rateFull - rateIsNull}, 221 {expr: "a is true", rate: rateFull - rateIsNull - rateIsFalse}, 222 {expr: "a is not true", rate: rateIsNull + rateIsFalse}, 223 {expr: "a is false", rate: rateIsFalse}, 224 {expr: "a is not false", rate: rateFull - rateIsFalse}, 225 {expr: "a like 'a'", rate: rateLike}, 226 {expr: "a not like 'a'", rate: rateFull - rateLike}, 227 {expr: "a in (1, 2, 3)", rate: rateEqual * 3}, 228 {expr: "a not in (1, 2, 3)", rate: rateFull - rateEqual*3}, 229 {expr: "a > 1 and a < 9", rate: float64(rateGreaterOrLess) * float64(rateGreaterOrLess)}, 230 {expr: "a = 1 or a = 2", rate: rateEqual + rateEqual - rateEqual*rateEqual}, 231 {expr: "a != 1", rate: rateNotEqual}, 232 } 233 for _, ca := range cases { 234 sql := "select 1 from dual where " + ca.expr 235 s, err := parser.ParseOneStmt(sql, "", "") 236 c.Assert(err, IsNil, Commentf("for expr %s", ca.expr)) 237 stmt := s.(*ast.SelectStmt) 238 rate := guesstimateFilterRate(stmt.Where) 239 c.Assert(rate, Equals, ca.rate, Commentf("for expr %s", ca.expr)) 240 } 241 } 242 243 func (s *testPlanSuite) TestBestPlan(c *C) { 244 defer testleak.AfterTest(c)() 245 cases := []struct { 246 sql string 247 best string 248 }{ 249 { 250 sql: "select * from t", 251 best: "Table(t)->Fields", 252 }, 253 { 254 sql: "select * from t order by a", 255 best: "Table(t)->Fields", 256 }, 257 { 258 sql: "select * from t where b = 1 order by a", 259 best: "Index(t.b)->Fields->Sort", 260 }, 261 { 262 sql: "select * from t where (a between 1 and 2) and (b = 3)", 263 best: "Index(t.b)->Fields", 264 }, 265 { 266 sql: "select * from t where a > 0 order by b limit 100", 267 best: "Index(t.b) + Limit(100)->Fields->Limit", 268 }, 269 { 270 sql: "select * from t where a > 0 order by b DESC limit 100", 271 best: "Index(t.b) + Limit(100)->Fields->Limit", 272 }, 273 { 274 sql: "select * from t where a > 0 order by b + a limit 100", 275 best: "Range(t)->Fields->Sort + Limit(100) + Offset(0)", 276 }, 277 { 278 sql: "select count(*) from t where a > 0 order by b limit 100", 279 best: "Range(t)->Aggregate->Fields->Sort + Limit(100) + Offset(0)", 280 }, 281 { 282 sql: "select count(*) from t where a > 0 limit 100", 283 best: "Range(t)->Aggregate->Fields->Limit", 284 }, 285 { 286 sql: "select distinct a from t where a > 0 limit 100", 287 best: "Range(t)->Fields->Distinct->Limit", 288 }, 289 { 290 sql: "select * from t where a > 0 order by a limit 100", 291 best: "Range(t) + Limit(100)->Fields->Limit", 292 }, 293 { 294 sql: "select * from t where d = 0", 295 best: "Table(t)->Fields", 296 }, 297 { 298 sql: "select * from t where c = 0 and d = 0", 299 best: "Index(t.c_d_e)->Fields", 300 }, 301 { 302 sql: "select * from t where c = 0 and d = 0 and e = 0", 303 best: "Index(t.c_d_e)->Fields", 304 }, 305 { 306 sql: "select * from t where (d = 0 and e = 0) and c = 0", 307 best: "Index(t.c_d_e)->Fields", 308 }, 309 { 310 sql: "select * from t where e = 0 and (d = 0 and c = 0)", 311 best: "Index(t.c_d_e)->Fields", 312 }, 313 { 314 sql: "select * from t where b like 'abc%'", 315 best: "Index(t.b)->Fields", 316 }, 317 { 318 sql: "select * from t where d", 319 best: "Table(t)->Fields", 320 }, 321 { 322 sql: "select * from t where a is null", 323 best: "Range(t)->Fields", 324 }, 325 { 326 sql: "select a from t where a = 1 limit 1 for update", 327 best: "Range(t) + Limit(1)->Lock->Fields->Limit", 328 }, 329 { 330 sql: "admin show ddl", 331 best: "ShowDDL", 332 }, 333 { 334 sql: "admin check table t", 335 best: "CheckTable", 336 }, 337 } 338 for _, ca := range cases { 339 comment := Commentf("for %s", ca.sql) 340 stmt, err := parser.ParseOneStmt(ca.sql, "", "") 341 c.Assert(err, IsNil, comment) 342 ast.SetFlag(stmt) 343 mockResolve(stmt) 344 345 p, err := BuildPlan(stmt, nil) 346 c.Assert(err, IsNil) 347 348 err = Refine(p) 349 c.Assert(err, IsNil) 350 c.Assert(ToString(p), Equals, ca.best, Commentf("for %s cost %v", ca.sql, EstimateCost(p))) 351 } 352 } 353 354 func (s *testPlanSuite) TestSplitWhere(c *C) { 355 defer testleak.AfterTest(c)() 356 cases := []struct { 357 expr string 358 count int 359 }{ 360 {"a = 1 and b = 2 and c = 3", 3}, 361 {"(a = 1 and b = 2) and c = 3", 3}, 362 {"a = 1 and (b = 2 and c = 3 or d = 4)", 2}, 363 {"a = 1 and (b = 2 or c = 3) and d = 4", 3}, 364 {"(a = 1 and b = 2) and (c = 3 and d = 4)", 4}, 365 } 366 for _, ca := range cases { 367 sql := "select 1 from dual where " + ca.expr 368 comment := Commentf("for expr %s", ca.expr) 369 s, err := parser.ParseOneStmt(sql, "", "") 370 c.Assert(err, IsNil, comment) 371 stmt := s.(*ast.SelectStmt) 372 conditions := splitWhere(stmt.Where) 373 c.Assert(conditions, HasLen, ca.count, comment) 374 } 375 } 376 377 func (s *testPlanSuite) TestNullRejectFinder(c *C) { 378 defer testleak.AfterTest(c)() 379 cases := []struct { 380 expr string 381 notNull bool 382 }{ 383 {"a = 1", true}, 384 {"a != 100 and a > 0", true}, 385 {"a is null", false}, 386 {"a is not null", true}, 387 {"a is true", true}, 388 {"a is not true", false}, 389 {"a is false", true}, 390 {"a is not false", false}, 391 {"a != 0 and a is not false", true}, 392 {"a > 0 or true", false}, 393 } 394 for _, ca := range cases { 395 sql := "select * from t where " + ca.expr 396 comment := Commentf("for expr %s", ca.expr) 397 s, err := parser.ParseOneStmt(sql, "", "") 398 c.Assert(err, IsNil, comment) 399 finder := &nullRejectFinder{nullRejectTables: map[*ast.TableName]bool{}} 400 stmt := s.(*ast.SelectStmt) 401 mockResolve(stmt) 402 stmt.Where.Accept(finder) 403 if ca.notNull { 404 c.Assert(finder.nullRejectTables, HasLen, 1, comment) 405 } else { 406 c.Assert(finder.nullRejectTables, HasLen, 0, comment) 407 } 408 } 409 } 410 411 func mockResolve(node ast.Node) { 412 indices := []*model.IndexInfo{ 413 { 414 Name: model.NewCIStr("b"), 415 Columns: []*model.IndexColumn{ 416 { 417 Name: model.NewCIStr("b"), 418 }, 419 }, 420 }, 421 { 422 Name: model.NewCIStr("c_d_e"), 423 Columns: []*model.IndexColumn{ 424 { 425 Name: model.NewCIStr("c"), 426 }, 427 { 428 Name: model.NewCIStr("d"), 429 }, 430 { 431 Name: model.NewCIStr("e"), 432 }, 433 }, 434 }, 435 } 436 pkColumn := &model.ColumnInfo{ 437 Name: model.NewCIStr("a"), 438 } 439 pkColumn.Flag = mysql.PriKeyFlag 440 table := &model.TableInfo{ 441 Columns: []*model.ColumnInfo{pkColumn}, 442 Indices: indices, 443 Name: model.NewCIStr("t"), 444 PKIsHandle: true, 445 } 446 tableName := &ast.TableName{Name: table.Name} 447 resolver := mockResolver{table: table, tableName: tableName} 448 node.Accept(&resolver) 449 } 450 451 type mockResolver struct { 452 table *model.TableInfo 453 tableName *ast.TableName 454 455 contextStack [][]*ast.ResultField 456 } 457 458 func (b *mockResolver) Enter(in ast.Node) (ast.Node, bool) { 459 switch in.(type) { 460 case *ast.SelectStmt: 461 b.contextStack = append(b.contextStack, make([]*ast.ResultField, 0)) 462 } 463 return in, false 464 } 465 466 func (b *mockResolver) Leave(in ast.Node) (ast.Node, bool) { 467 switch x := in.(type) { 468 case *ast.ColumnNameExpr: 469 x.Refer = &ast.ResultField{ 470 Column: &model.ColumnInfo{ 471 Name: x.Name.Name, 472 }, 473 Table: b.table, 474 TableName: b.tableName, 475 } 476 if x.Name.Name.L == "a" { 477 x.Refer.Column = b.table.Columns[0] 478 } 479 case *ast.TableName: 480 x.TableInfo = b.table 481 case *ast.FieldList: 482 for _, v := range x.Fields { 483 if v.WildCard == nil { 484 rf := &ast.ResultField{ColumnAsName: v.AsName} 485 switch k := v.Expr.(type) { 486 case *ast.ColumnNameExpr: 487 rf = &ast.ResultField{ 488 Column: &model.ColumnInfo{ 489 Name: k.Name.Name, 490 }, 491 Table: b.table, 492 TableName: b.tableName, 493 } 494 case *ast.AggregateFuncExpr: 495 rf.Column = &model.ColumnInfo{} // Empty column info. 496 rf.Table = &model.TableInfo{} // Empty table info. 497 rf.Expr = k 498 b.contextStack[len(b.contextStack)-1] = append(b.contextStack[len(b.contextStack)-1], rf) 499 } 500 } 501 } 502 case *ast.SelectStmt: 503 x.SetResultFields(b.contextStack[len(b.contextStack)-1]) 504 b.contextStack = b.contextStack[:len(b.contextStack)-1] 505 } 506 return in, true 507 } 508 509 // mockJoinResolve resolves multi talbe and column name for join statement. 510 func mockJoinResolve(c *C, node ast.Node) { 511 resolver := &mockJoinResolver{ 512 c: c, 513 tables: map[string]*ast.TableName{}, 514 refers: map[*model.ColumnInfo]*ast.ResultField{}, 515 } 516 node.Accept(resolver) 517 } 518 519 type mockJoinResolver struct { 520 tables map[string]*ast.TableName 521 refers map[*model.ColumnInfo]*ast.ResultField 522 c *C 523 } 524 525 func (b *mockJoinResolver) Enter(in ast.Node) (ast.Node, bool) { 526 return in, false 527 } 528 529 func (b *mockJoinResolver) Leave(in ast.Node) (ast.Node, bool) { 530 switch x := in.(type) { 531 case *ast.TableName: 532 if b.tables[x.Name.L] == nil { 533 b.tables[x.Name.L] = x 534 x.TableInfo = &model.TableInfo{ 535 Name: x.Name, 536 PKIsHandle: true, 537 } 538 } 539 case *ast.ColumnNameExpr: 540 tn := b.tables[x.Name.Table.L] 541 b.c.Assert(tn, NotNil) 542 for _, cn := range tn.TableInfo.Columns { 543 if cn.Name.L == x.Name.Name.L { 544 x.Refer = b.refers[cn] 545 return in, true 546 } 547 } 548 columnInfo := &model.ColumnInfo{Name: x.Name.Name} 549 tn.TableInfo.Columns = append(tn.TableInfo.Columns, columnInfo) 550 refer := &ast.ResultField{ 551 Column: columnInfo, 552 Table: tn.TableInfo, 553 TableName: tn, 554 Expr: ast.NewValueExpr(99), 555 } 556 b.refers[columnInfo] = refer 557 x.Refer = refer 558 if x.Name.Name.L == "id" { 559 columnInfo.Flag = mysql.PriKeyFlag 560 } else if x.Name.Name.L[0] == 'i' { 561 idxInfo := &model.IndexInfo{ 562 Name: x.Name.Name, 563 Columns: []*model.IndexColumn{ 564 { 565 Name: x.Name.Name, 566 }, 567 }, 568 } 569 tn.TableInfo.Indices = append(tn.TableInfo.Indices, idxInfo) 570 } 571 } 572 return in, true 573 } 574 575 func (s *testPlanSuite) TestJoinPath(c *C) { 576 defer testleak.AfterTest(c)() 577 cases := []struct { 578 sql string 579 explain string 580 }{ 581 { 582 "select * from t1, t2 where t1.c1 > 0", 583 "InnerJoin{Table(t1)->Table(t2)}->Fields", 584 }, 585 { 586 "select * from t1 left join t2 on 1 where t2.c1 != 0", 587 "InnerJoin{Table(t2)->Table(t1)}->Fields", 588 }, 589 { 590 "select * from t1 left join t2 on 1 where t2.c1 != 0 or t1.c1 != 0", 591 "OuterJoin{Table(t1)->Table(t2)}->Filter->Fields", 592 }, 593 { 594 "select * from t1 left join t2 on t1.i1 = t2.i1 where t1.i1 = 1", 595 "OuterJoin{Index(t1.i1)->Index(t2.i1)}->Fields", 596 }, 597 { 598 "select * from t1 join t2 on t2.c1 = t1.i1", 599 "InnerJoin{Table(t2)->Index(t1.i1)}->Fields", 600 }, 601 { 602 "select * from t1, t2 where t2.c1 = t1.i1", 603 "InnerJoin{Table(t2)->Index(t1.i1)}->Fields", 604 }, 605 { 606 `select * from 607 t1 left join 608 (t2 join 609 t3 610 on t2.i1 = t3.c1) 611 on t1.c1 = t2.i2`, 612 "OuterJoin{Table(t1)->InnerJoin{Index(t2.i2)->Table(t3)}}->Fields", 613 }, 614 { 615 `select * from 616 (t1, t2) left join 617 t3 618 on t2.c1 = t3.i1 619 where t2.i2 between 1 and 4 and t1.i1 = 3`, 620 "OuterJoin{InnerJoin{Index(t1.i1)->Index(t2.i2)}->Index(t3.i1)}->Fields", 621 }, 622 { 623 `select * from 624 t1 join ( 625 (t2 join t3 626 on t2.i3 = t3.i3 and t2.c2 > t3.c3 627 ) left join t4 628 on t3.c3 = t4.i4 629 ) 630 on t1.i1 = 1 and t1.c1 = t2.i2`, 631 "InnerJoin{Index(t1.i1)->OuterJoin{InnerJoin{Index(t2.i2)->Index(t3.i3)}->Index(t4.i4)}}->Fields", 632 }, 633 { 634 `select * from 635 t1 join ( 636 t2 left join ( 637 t3 join t4 638 on t3.i3 = t4.c4 639 ) 640 on t2.i2 = t3.c3 641 ) 642 on t1.i1 = t2.c2`, 643 "InnerJoin{OuterJoin{Table(t2)->InnerJoin{Table(t4)->Index(t3.i3)}}->Index(t1.i1)}->Fields", 644 }, 645 { 646 `select * from 647 t1 join ( 648 (t2 join t3 649 on t2.i2 = t3.i3 650 ) 651 ) on t1.i1 = t2.i2 652 where t1.i1 = 1`, 653 "InnerJoin{Index(t1.i1)->Index(t2.i2)->Index(t3.i3)}->Fields", 654 }, 655 } 656 for _, ca := range cases { 657 comment := Commentf("for %s", ca.sql) 658 s, err := parser.ParseOneStmt(ca.sql, "", "") 659 c.Assert(err, IsNil, comment) 660 stmt := s.(*ast.SelectStmt) 661 mockJoinResolve(c, stmt) 662 ast.SetFlag(stmt) 663 p, err := BuildPlan(stmt, nil) 664 c.Assert(err, IsNil) 665 c.Assert(ToString(p), Equals, ca.explain, comment) 666 } 667 } 668 669 func (s *testPlanSuite) TestMultiColumnIndex(c *C) { 670 defer testleak.AfterTest(c)() 671 cases := []struct { 672 sql string 673 accessEqualCount int 674 usedColumnCount int 675 }{ 676 {"select * from t where c = 0 and d = 0 and e = 0", 3, 3}, 677 {"select * from t where c = 0 and d = 0 and e > 0", 2, 3}, 678 {"select * from t where d > 0 and e = 0 and c = 0", 1, 2}, 679 } 680 for _, ca := range cases { 681 comment := Commentf("for %s", ca.sql) 682 s, err := parser.ParseOneStmt(ca.sql, "", "") 683 c.Assert(err, IsNil, comment) 684 stmt := s.(*ast.SelectStmt) 685 ast.SetFlag(stmt) 686 mockResolve(stmt) 687 b := &planBuilder{} 688 p := b.buildFrom(stmt) 689 err = Refine(p) 690 c.Assert(err, IsNil) 691 idxScan, ok := p.(*IndexScan) 692 c.Assert(ok, IsTrue) 693 c.Assert(idxScan.AccessEqualCount, Equals, ca.accessEqualCount) 694 c.Assert(idxScan.Ranges[0].LowVal, HasLen, ca.usedColumnCount) 695 } 696 } 697 698 func (s *testPlanSuite) TestIndexHint(c *C) { 699 defer testleak.AfterTest(c)() 700 cases := []struct { 701 sql string 702 explain string 703 }{ 704 { 705 "select * from t1 force index (i1) where t1.i1 > 0 and t1.i2 = 0", 706 "Index(t1.i1)->Fields", 707 }, 708 { 709 "select * from t1 use index (i1) where t1.i1 > 0 and t1.i2 = 0", 710 "Index(t1.i1)->Fields", 711 }, 712 { 713 "select * from t1 ignore index (i2) where t1.i1 > 0 and t1.i2 = 0", 714 "Index(t1.i1)->Fields", 715 }, 716 { 717 "select * from t1 use index (i1, i2) where t1.i1 > 0 and t1.i2 between 0 and 2 and t1.i3 = 0", 718 "Index(t1.i2)->Fields", 719 }, 720 { 721 "select * from t1 ignore index (i1, i2, i3) where t1.i1 = 0 and t1.i2 = 0 and t1.i3 = 0", 722 "Table(t1)->Fields", 723 }, 724 { 725 "select * from t1 use index () where t1.i1 = 0 and t1.i2 = 0 and t1.i3 = 0", 726 "Table(t1)->Fields", 727 }, 728 { 729 "select * from t1 use index (i1) ignore index (i1) where t1.i1 = 0", 730 "Table(t1)->Fields", 731 }, 732 } 733 for _, ca := range cases { 734 comment := Commentf("for %s", ca.sql) 735 s, err := parser.ParseOneStmt(ca.sql, "", "") 736 c.Assert(err, IsNil, comment) 737 stmt := s.(*ast.SelectStmt) 738 mockJoinResolve(c, stmt) 739 ast.SetFlag(stmt) 740 p, err := BuildPlan(stmt, nil) 741 c.Assert(err, IsNil) 742 c.Assert(ToString(p), Equals, ca.explain, comment) 743 } 744 }