github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/optimizer/plan/planbuilder_join.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 "strings" 18 19 "github.com/insionng/yougam/libraries/ngaut/log" 20 "github.com/insionng/yougam/libraries/pingcap/tidb/ast" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/model" 22 "github.com/insionng/yougam/libraries/pingcap/tidb/mysql" 23 "github.com/insionng/yougam/libraries/pingcap/tidb/parser/opcode" 24 ) 25 26 // equalCond represents an equivalent join condition, like "t1.c1 = t2.c1". 27 type equalCond struct { 28 left *ast.ResultField 29 leftIdx bool 30 right *ast.ResultField 31 rightIdx bool 32 } 33 34 func newEqualCond(left, right *ast.ResultField) *equalCond { 35 eq := &equalCond{left: left, right: right} 36 eq.leftIdx = equivHasIndex(eq.left) 37 eq.rightIdx = equivHasIndex(eq.right) 38 return eq 39 } 40 41 func equivHasIndex(rf *ast.ResultField) bool { 42 if rf.Table.PKIsHandle && mysql.HasPriKeyFlag(rf.Column.Flag) { 43 return true 44 } 45 for _, idx := range rf.Table.Indices { 46 if len(idx.Columns) == 1 && idx.Columns[0].Name.L == rf.Column.Name.L { 47 return true 48 } 49 } 50 return false 51 } 52 53 // joinPath can be a single table path, inner join or outer join. 54 type joinPath struct { 55 // for table path 56 table *ast.TableName 57 totalFilterRate float64 58 59 // for subquery 60 subquery ast.Node 61 asName model.CIStr 62 63 neighborCount int // number of neighbor table. 64 idxDepCount int // number of paths this table depends on. 65 ordering *ast.ResultField 66 orderingDesc bool 67 68 // for outer join path 69 outer *joinPath 70 inner *joinPath 71 rightJoin bool 72 73 // for inner join path 74 inners []*joinPath 75 76 // common 77 parent *joinPath 78 filterRate float64 79 conditions []ast.ExprNode 80 eqConds []*equalCond 81 // The joinPaths that this path's index depends on. 82 idxDeps map[*joinPath]bool 83 neighbors map[*joinPath]bool 84 } 85 86 // newTablePath creates a new table join path. 87 func newTablePath(table *ast.TableName) *joinPath { 88 return &joinPath{ 89 table: table, 90 filterRate: rateFull, 91 } 92 } 93 94 // newSubqueryPath creates a new subquery join path. 95 func newSubqueryPath(node ast.Node, asName model.CIStr) *joinPath { 96 return &joinPath{ 97 subquery: node, 98 asName: asName, 99 filterRate: rateFull, 100 } 101 } 102 103 // newOuterJoinPath creates a new outer join path and pushes on condition to children paths. 104 // The returned joinPath slice has one element. 105 func newOuterJoinPath(isRightJoin bool, leftPath, rightPath *joinPath, on *ast.OnCondition) *joinPath { 106 outerJoin := &joinPath{rightJoin: isRightJoin, outer: leftPath, inner: rightPath, filterRate: 1} 107 leftPath.parent = outerJoin 108 rightPath.parent = outerJoin 109 if isRightJoin { 110 outerJoin.outer, outerJoin.inner = outerJoin.inner, outerJoin.outer 111 } 112 if on != nil { 113 conditions := splitWhere(on.Expr) 114 availablePaths := []*joinPath{outerJoin.outer} 115 for _, con := range conditions { 116 if !outerJoin.inner.attachCondition(con, availablePaths, true) { 117 log.Errorf("Inner failed to attach ON condition") 118 outerJoin.conditions = append(outerJoin.conditions, con) 119 } 120 } 121 } 122 return outerJoin 123 } 124 125 // newInnerJoinPath creates inner join path and pushes on condition to children paths. 126 // If left path or right path is also inner join, it will be merged. 127 func newInnerJoinPath(leftPath, rightPath *joinPath, on *ast.OnCondition) *joinPath { 128 var innerJoin *joinPath 129 if len(leftPath.inners) != 0 { 130 innerJoin = leftPath 131 } else { 132 innerJoin = &joinPath{filterRate: leftPath.filterRate} 133 innerJoin.inners = append(innerJoin.inners, leftPath) 134 } 135 if len(rightPath.inners) != 0 { 136 innerJoin.inners = append(innerJoin.inners, rightPath.inners...) 137 innerJoin.conditions = append(innerJoin.conditions, rightPath.conditions...) 138 } else { 139 innerJoin.inners = append(innerJoin.inners, rightPath) 140 } 141 innerJoin.filterRate *= rightPath.filterRate 142 143 for _, in := range innerJoin.inners { 144 in.parent = innerJoin 145 } 146 147 if on != nil { 148 conditions := splitWhere(on.Expr) 149 for _, con := range conditions { 150 if !innerJoin.attachCondition(con, nil, true) { 151 innerJoin.conditions = append(innerJoin.conditions, con) 152 } 153 } 154 } 155 return innerJoin 156 } 157 158 func (p *joinPath) resultFields() []*ast.ResultField { 159 if p.table != nil { 160 return p.table.GetResultFields() 161 } 162 if p.outer != nil { 163 if p.rightJoin { 164 return append(p.inner.resultFields(), p.outer.resultFields()...) 165 } 166 return append(p.outer.resultFields(), p.inner.resultFields()...) 167 } 168 var rfs []*ast.ResultField 169 for _, in := range p.inners { 170 rfs = append(rfs, in.resultFields()...) 171 } 172 return rfs 173 } 174 175 // attachCondition tries to attach a condition as deep as possible. 176 // availablePaths are paths join before this path. 177 // onCond represents whether the conditions is from current join's on condition. The on condition from other joins is treated as where condition. 178 func (p *joinPath) attachCondition(condition ast.ExprNode, availablePaths []*joinPath, onCond bool) (attached bool) { 179 filterRate := guesstimateFilterRate(condition) 180 // table 181 if p.table != nil || p.subquery != nil { 182 attacher := conditionAttachChecker{targetPath: p, availablePaths: availablePaths} 183 condition.Accept(&attacher) 184 if attacher.invalid { 185 return false 186 } 187 p.conditions = append(p.conditions, condition) 188 p.filterRate *= filterRate 189 return true 190 } 191 // inner join 192 if len(p.inners) > 0 { 193 for _, in := range p.inners { 194 if in.attachCondition(condition, availablePaths, false) { 195 p.filterRate *= filterRate 196 return true 197 } 198 } 199 attacher := &conditionAttachChecker{targetPath: p, availablePaths: availablePaths} 200 condition.Accept(attacher) 201 if attacher.invalid { 202 return false 203 } 204 p.conditions = append(p.conditions, condition) 205 p.filterRate *= filterRate 206 return true 207 } 208 209 // outer join 210 if p.outer.attachCondition(condition, availablePaths, false) { 211 p.filterRate *= filterRate 212 return true 213 } 214 // can't attach any where condition 215 if onCond && p.inner.attachCondition(condition, availablePaths, false) { 216 p.filterRate *= filterRate 217 return true 218 } 219 return false 220 } 221 222 func (p *joinPath) containsTable(table *ast.TableName) bool { 223 if p.table != nil { 224 return p.table == table 225 } 226 if p.subquery != nil { 227 return p.asName.L == table.Name.L 228 } 229 if len(p.inners) != 0 { 230 for _, in := range p.inners { 231 if in.containsTable(table) { 232 return true 233 } 234 } 235 return false 236 } 237 238 return p.outer.containsTable(table) || p.inner.containsTable(table) 239 } 240 241 // attachEqualCond tries to attach an equalCond deep into a table path if applicable. 242 func (p *joinPath) attachEqualCond(eqCon *equalCond, availablePaths []*joinPath) (attached bool) { 243 // table 244 if p.table != nil { 245 var prevTable *ast.TableName 246 var needSwap bool 247 if eqCon.left.TableName == p.table { 248 prevTable = eqCon.right.TableName 249 } else if eqCon.right.TableName == p.table { 250 prevTable = eqCon.left.TableName 251 needSwap = true 252 } 253 if prevTable != nil { 254 for _, prev := range availablePaths { 255 if prev.containsTable(prevTable) { 256 if needSwap { 257 eqCon.left, eqCon.right = eqCon.right, eqCon.left 258 eqCon.leftIdx, eqCon.rightIdx = eqCon.rightIdx, eqCon.leftIdx 259 } 260 p.eqConds = append(p.eqConds, eqCon) 261 return true 262 } 263 } 264 } 265 return false 266 } 267 268 // inner join 269 if len(p.inners) > 0 { 270 for _, in := range p.inners { 271 if in.attachEqualCond(eqCon, availablePaths) { 272 p.filterRate *= rateEqual 273 return true 274 } 275 } 276 return false 277 } 278 // subquery join 279 if p.subquery != nil { 280 // TODO: find a way to attach condition to subquery. 281 return false 282 } 283 // outer join 284 if p.outer.attachEqualCond(eqCon, availablePaths) { 285 p.filterRate *= rateEqual 286 return true 287 } 288 if p.inner.attachEqualCond(eqCon, append(availablePaths, p.outer)) { 289 p.filterRate *= rateEqual 290 return true 291 } 292 return false 293 } 294 295 func (p *joinPath) extractEqualConditon() { 296 var equivs []*equalCond 297 var cons []ast.ExprNode 298 for _, con := range p.conditions { 299 eq := equivFromExpr(con) 300 if eq != nil { 301 equivs = append(equivs, eq) 302 if p.table != nil { 303 if eq.right.TableName == p.table { 304 eq.left, eq.right = eq.right, eq.left 305 eq.leftIdx, eq.rightIdx = eq.rightIdx, eq.leftIdx 306 } 307 } 308 } else { 309 cons = append(cons, con) 310 } 311 } 312 p.eqConds = equivs 313 p.conditions = cons 314 for _, in := range p.inners { 315 in.extractEqualConditon() 316 } 317 if p.outer != nil { 318 p.outer.extractEqualConditon() 319 p.inner.extractEqualConditon() 320 } 321 } 322 323 func (p *joinPath) addIndexDependency() { 324 if p.outer != nil { 325 p.outer.addIndexDependency() 326 p.inner.addIndexDependency() 327 return 328 } 329 if p.table != nil { 330 return 331 } 332 for _, eq := range p.eqConds { 333 if !eq.leftIdx && !eq.rightIdx { 334 continue 335 } 336 pathLeft := p.findInnerContains(eq.left.TableName) 337 if pathLeft == nil { 338 continue 339 } 340 pathRight := p.findInnerContains(eq.right.TableName) 341 if pathRight == nil { 342 continue 343 } 344 if eq.leftIdx && eq.rightIdx { 345 pathLeft.addNeighbor(pathRight) 346 pathRight.addNeighbor(pathLeft) 347 } else if eq.leftIdx { 348 if !pathLeft.hasOuterIdxEqualCond() { 349 pathLeft.addIndexDep(pathRight) 350 } 351 } else if eq.rightIdx { 352 if !pathRight.hasOuterIdxEqualCond() { 353 pathRight.addIndexDep(pathLeft) 354 } 355 } 356 } 357 for _, in := range p.inners { 358 in.removeIndexDepCycle(in) 359 in.addIndexDependency() 360 } 361 } 362 363 func (p *joinPath) hasOuterIdxEqualCond() bool { 364 if p.table != nil { 365 for _, eq := range p.eqConds { 366 if eq.leftIdx { 367 return true 368 } 369 } 370 return false 371 } 372 if p.outer != nil { 373 return p.outer.hasOuterIdxEqualCond() 374 } 375 for _, in := range p.inners { 376 if in.hasOuterIdxEqualCond() { 377 return true 378 } 379 } 380 return false 381 } 382 383 func (p *joinPath) findInnerContains(table *ast.TableName) *joinPath { 384 for _, in := range p.inners { 385 if in.containsTable(table) { 386 return in 387 } 388 } 389 return nil 390 } 391 392 func (p *joinPath) addNeighbor(neighbor *joinPath) { 393 if p.neighbors == nil { 394 p.neighbors = map[*joinPath]bool{} 395 } 396 p.neighbors[neighbor] = true 397 p.neighborCount++ 398 } 399 400 func (p *joinPath) addIndexDep(dep *joinPath) { 401 if p.idxDeps == nil { 402 p.idxDeps = map[*joinPath]bool{} 403 } 404 p.idxDeps[dep] = true 405 p.idxDepCount++ 406 } 407 408 func (p *joinPath) removeIndexDepCycle(origin *joinPath) { 409 if p.idxDeps == nil { 410 return 411 } 412 for dep := range p.idxDeps { 413 if dep == origin { 414 delete(p.idxDeps, origin) 415 continue 416 } 417 dep.removeIndexDepCycle(origin) 418 } 419 } 420 421 func (p *joinPath) score() float64 { 422 return 1 / p.filterRate 423 } 424 425 func (p *joinPath) String() string { 426 if p.table != nil { 427 return p.table.TableInfo.Name.L 428 } 429 if p.outer != nil { 430 return "outer{" + p.outer.String() + "," + p.inner.String() + "}" 431 } 432 var innerStrs []string 433 for _, in := range p.inners { 434 innerStrs = append(innerStrs, in.String()) 435 } 436 return "inner{" + strings.Join(innerStrs, ",") + "}" 437 } 438 439 func (p *joinPath) optimizeJoinOrder(availablePaths []*joinPath) { 440 if p.table != nil { 441 return 442 } 443 if p.outer != nil { 444 p.outer.optimizeJoinOrder(availablePaths) 445 p.inner.optimizeJoinOrder(append(availablePaths, p.outer)) 446 return 447 } 448 var ordered []*joinPath 449 pathMap := map[*joinPath]bool{} 450 for _, in := range p.inners { 451 pathMap[in] = true 452 } 453 for len(pathMap) > 0 { 454 next := p.nextPath(pathMap, availablePaths) 455 next.optimizeJoinOrder(availablePaths) 456 ordered = append(ordered, next) 457 delete(pathMap, next) 458 availablePaths = append(availablePaths, next) 459 for path := range pathMap { 460 if path.idxDeps != nil { 461 delete(path.idxDeps, next) 462 } 463 if path.neighbors != nil { 464 delete(path.neighbors, next) 465 } 466 } 467 p.reattach(pathMap, availablePaths) 468 } 469 p.inners = ordered 470 } 471 472 // reattach is called by inner joinPath to retry attach conditions to inner paths 473 // after an inner path has been added to available paths. 474 func (p *joinPath) reattach(pathMap map[*joinPath]bool, availablePaths []*joinPath) { 475 if len(p.conditions) != 0 { 476 remainedConds := make([]ast.ExprNode, 0, len(p.conditions)) 477 for _, con := range p.conditions { 478 var attached bool 479 for path := range pathMap { 480 if path.attachCondition(con, availablePaths, true) { 481 attached = true 482 break 483 } 484 } 485 if !attached { 486 remainedConds = append(remainedConds, con) 487 } 488 } 489 p.conditions = remainedConds 490 } 491 if len(p.eqConds) != 0 { 492 remainedEqConds := make([]*equalCond, 0, len(p.eqConds)) 493 for _, eq := range p.eqConds { 494 var attached bool 495 for path := range pathMap { 496 if path.attachEqualCond(eq, availablePaths) { 497 attached = true 498 break 499 } 500 } 501 if !attached { 502 remainedEqConds = append(remainedEqConds, eq) 503 } 504 } 505 p.eqConds = remainedEqConds 506 } 507 } 508 509 func (p *joinPath) nextPath(pathMap map[*joinPath]bool, availablePaths []*joinPath) *joinPath { 510 cans := p.candidates(pathMap) 511 if len(cans) == 0 { 512 var v *joinPath 513 for v = range pathMap { 514 log.Errorf("index dep %v, prevs %v\n", v.idxDeps, len(availablePaths)) 515 } 516 return v 517 } 518 indexPath := p.nextIndexPath(cans) 519 if indexPath != nil { 520 return indexPath 521 } 522 return p.pickPath(cans) 523 } 524 525 func (p *joinPath) candidates(pathMap map[*joinPath]bool) []*joinPath { 526 var cans []*joinPath 527 for t := range pathMap { 528 if len(t.idxDeps) > 0 { 529 continue 530 } 531 cans = append(cans, t) 532 } 533 return cans 534 } 535 536 func (p *joinPath) nextIndexPath(candidates []*joinPath) *joinPath { 537 var best *joinPath 538 for _, can := range candidates { 539 // Since we may not have equal conditions attached on the path, we 540 // need to check neighborCount and idxDepCount to see if this path 541 // can be joined with index. 542 neighborIsAvailable := len(can.neighbors) < can.neighborCount 543 idxDepIsAvailable := can.idxDepCount > 0 544 if can.hasOuterIdxEqualCond() || neighborIsAvailable || idxDepIsAvailable { 545 if best == nil { 546 best = can 547 } 548 if can.score() > best.score() { 549 best = can 550 } 551 } 552 } 553 return best 554 } 555 556 func (p *joinPath) pickPath(candidates []*joinPath) *joinPath { 557 var best *joinPath 558 for _, path := range candidates { 559 if best == nil { 560 best = path 561 } 562 if path.score() > best.score() { 563 best = path 564 } 565 } 566 return best 567 } 568 569 // conditionAttachChecker checks if an expression is valid to 570 // attach to a path. attach is valid only if all the referenced tables in the 571 // expression are available. 572 type conditionAttachChecker struct { 573 targetPath *joinPath 574 availablePaths []*joinPath 575 invalid bool 576 } 577 578 func (c *conditionAttachChecker) Enter(in ast.Node) (ast.Node, bool) { 579 switch x := in.(type) { 580 case *ast.ColumnNameExpr: 581 table := x.Refer.TableName 582 if c.targetPath.containsTable(table) { 583 return in, false 584 } 585 c.invalid = true 586 for _, path := range c.availablePaths { 587 if path.containsTable(table) { 588 c.invalid = false 589 return in, false 590 } 591 } 592 } 593 return in, false 594 } 595 596 func (c *conditionAttachChecker) Leave(in ast.Node) (ast.Node, bool) { 597 return in, !c.invalid 598 } 599 600 func (b *planBuilder) buildJoin(sel *ast.SelectStmt) Plan { 601 nrfinder := &nullRejectFinder{nullRejectTables: map[*ast.TableName]bool{}} 602 if sel.Where != nil { 603 sel.Where.Accept(nrfinder) 604 } 605 path := b.buildBasicJoinPath(sel.From.TableRefs, nrfinder.nullRejectTables) 606 rfs := path.resultFields() 607 608 var filterConditions []ast.ExprNode 609 whereConditions := splitWhere(sel.Where) 610 for _, whereCond := range whereConditions { 611 if !path.attachCondition(whereCond, nil, false) { 612 filterConditions = append(filterConditions, whereCond) 613 } 614 } 615 path.extractEqualConditon() 616 path.addIndexDependency() 617 path.optimizeJoinOrder(nil) 618 p := b.buildPlanFromJoinPath(path) 619 p.SetFields(rfs) 620 if filterConditions != nil { 621 filterPlan := &Filter{Conditions: filterConditions} 622 addChild(filterPlan, p) 623 filterPlan.SetFields(p.Fields()) 624 return filterPlan 625 } 626 return p 627 } 628 629 type nullRejectFinder struct { 630 nullRejectTables map[*ast.TableName]bool 631 } 632 633 func (n *nullRejectFinder) Enter(in ast.Node) (ast.Node, bool) { 634 switch x := in.(type) { 635 case *ast.BinaryOperationExpr: 636 if x.Op == opcode.NullEQ || x.Op == opcode.OrOr { 637 return in, true 638 } 639 case *ast.IsNullExpr: 640 if !x.Not { 641 return in, true 642 } 643 case *ast.IsTruthExpr: 644 if x.Not { 645 return in, true 646 } 647 } 648 return in, false 649 } 650 651 func (n *nullRejectFinder) Leave(in ast.Node) (ast.Node, bool) { 652 switch x := in.(type) { 653 case *ast.ColumnNameExpr: 654 n.nullRejectTables[x.Refer.TableName] = true 655 } 656 return in, true 657 } 658 659 func (b *planBuilder) buildBasicJoinPath(node ast.ResultSetNode, nullRejectTables map[*ast.TableName]bool) *joinPath { 660 switch x := node.(type) { 661 case nil: 662 return nil 663 case *ast.Join: 664 leftPath := b.buildBasicJoinPath(x.Left, nullRejectTables) 665 if x.Right == nil { 666 return leftPath 667 } 668 righPath := b.buildBasicJoinPath(x.Right, nullRejectTables) 669 isOuter := b.isOuterJoin(x.Tp, leftPath, righPath, nullRejectTables) 670 if isOuter { 671 return newOuterJoinPath(x.Tp == ast.RightJoin, leftPath, righPath, x.On) 672 } 673 return newInnerJoinPath(leftPath, righPath, x.On) 674 case *ast.TableSource: 675 switch v := x.Source.(type) { 676 case *ast.TableName: 677 return newTablePath(v) 678 case *ast.SelectStmt, *ast.UnionStmt: 679 return newSubqueryPath(v, x.AsName) 680 default: 681 b.err = ErrUnsupportedType.Gen("unsupported table source type %T", x) 682 return nil 683 } 684 default: 685 b.err = ErrUnsupportedType.Gen("unsupported table source type %T", x) 686 return nil 687 } 688 } 689 690 func (b *planBuilder) isOuterJoin(tp ast.JoinType, leftPaths, rightPaths *joinPath, 691 nullRejectTables map[*ast.TableName]bool) bool { 692 var innerPath *joinPath 693 switch tp { 694 case ast.LeftJoin: 695 innerPath = rightPaths 696 case ast.RightJoin: 697 innerPath = leftPaths 698 default: 699 return false 700 } 701 for table := range nullRejectTables { 702 if innerPath.containsTable(table) { 703 return false 704 } 705 } 706 return true 707 } 708 709 func equivFromExpr(expr ast.ExprNode) *equalCond { 710 binop, ok := expr.(*ast.BinaryOperationExpr) 711 if !ok || binop.Op != opcode.EQ { 712 return nil 713 } 714 ln, lOK := binop.L.(*ast.ColumnNameExpr) 715 rn, rOK := binop.R.(*ast.ColumnNameExpr) 716 if !lOK || !rOK { 717 return nil 718 } 719 if ln.Name.Table.L == "" || rn.Name.Table.L == "" { 720 return nil 721 } 722 if ln.Name.Schema.L == rn.Name.Schema.L && ln.Name.Table.L == rn.Name.Table.L { 723 return nil 724 } 725 return newEqualCond(ln.Refer, rn.Refer) 726 } 727 728 func (b *planBuilder) buildPlanFromJoinPath(path *joinPath) Plan { 729 if path.table != nil { 730 return b.buildTablePlanFromJoinPath(path) 731 } 732 if path.subquery != nil { 733 return b.buildSubqueryJoinPath(path) 734 } 735 if path.outer != nil { 736 join := &JoinOuter{ 737 Outer: b.buildPlanFromJoinPath(path.outer), 738 Inner: b.buildPlanFromJoinPath(path.inner), 739 } 740 addChild(join, join.Outer) 741 addChild(join, join.Inner) 742 if path.rightJoin { 743 join.SetFields(append(join.Inner.Fields(), join.Outer.Fields()...)) 744 } else { 745 join.SetFields(append(join.Outer.Fields(), join.Inner.Fields()...)) 746 } 747 return join 748 } 749 join := &JoinInner{} 750 for _, in := range path.inners { 751 inPlan := b.buildPlanFromJoinPath(in) 752 join.Inners = append(join.Inners, inPlan) 753 join.fields = append(join.fields, in.resultFields()...) 754 addChild(join, inPlan) 755 } 756 join.Conditions = path.conditions 757 for _, equiv := range path.eqConds { 758 columnNameExpr := &ast.ColumnNameExpr{} 759 columnNameExpr.Name = &ast.ColumnName{} 760 columnNameExpr.Name.Name = equiv.left.Column.Name 761 columnNameExpr.Name.Table = equiv.left.Table.Name 762 columnNameExpr.Refer = equiv.left 763 ast.SetFlag(columnNameExpr) 764 cond := &ast.BinaryOperationExpr{L: columnNameExpr, R: equiv.right.Expr, Op: opcode.EQ} 765 ast.MergeChildrenFlags(cond, columnNameExpr, equiv.right.Expr) 766 join.Conditions = append(join.Conditions, cond) 767 } 768 return join 769 } 770 771 func (b *planBuilder) buildTablePlanFromJoinPath(path *joinPath) Plan { 772 for _, equiv := range path.eqConds { 773 columnNameExpr := &ast.ColumnNameExpr{} 774 columnNameExpr.Name = &ast.ColumnName{} 775 columnNameExpr.Name.Name = equiv.left.Column.Name 776 columnNameExpr.Name.Table = equiv.left.Table.Name 777 columnNameExpr.Refer = equiv.left 778 columnNameExpr.Type = equiv.left.Expr.GetType() 779 ast.SetFlag(columnNameExpr) 780 condition := &ast.BinaryOperationExpr{L: columnNameExpr, R: equiv.right.Expr, Op: opcode.EQ} 781 ast.MergeChildrenFlags(condition, columnNameExpr, equiv.right.Expr) 782 path.conditions = append(path.conditions, condition) 783 } 784 candidates := b.buildAllAccessMethodsPlan(path) 785 var p Plan 786 var lowestCost float64 787 for _, can := range candidates { 788 cost := EstimateCost(can) 789 if p == nil { 790 p = can 791 lowestCost = cost 792 } 793 if cost < lowestCost { 794 p = can 795 lowestCost = cost 796 } 797 } 798 return p 799 } 800 801 // Build subquery join path plan 802 func (b *planBuilder) buildSubqueryJoinPath(path *joinPath) Plan { 803 for _, equiv := range path.eqConds { 804 columnNameExpr := &ast.ColumnNameExpr{} 805 columnNameExpr.Name = &ast.ColumnName{} 806 columnNameExpr.Name.Name = equiv.left.Column.Name 807 columnNameExpr.Name.Table = equiv.left.Table.Name 808 columnNameExpr.Refer = equiv.left 809 columnNameExpr.Type = equiv.left.Expr.GetType() 810 ast.SetFlag(columnNameExpr) 811 condition := &ast.BinaryOperationExpr{L: columnNameExpr, R: equiv.right.Expr, Op: opcode.EQ} 812 ast.MergeChildrenFlags(condition, columnNameExpr, equiv.right.Expr) 813 path.conditions = append(path.conditions, condition) 814 } 815 p := b.build(path.subquery) 816 if len(path.conditions) == 0 { 817 return p 818 } 819 filterPlan := &Filter{Conditions: path.conditions} 820 addChild(filterPlan, p) 821 filterPlan.SetFields(p.Fields()) 822 return filterPlan 823 }