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  }