Integrate public as bindata optionally (#293)
* Dropped unused codekit config * Integrated dynamic and static bindata for public * Ignore public bindata * Add a general generate make task * Integrated flexible public assets into web command * Updated vendoring, added all missiong govendor deps * Made the linter happy with the bindata and dynamic code * Moved public bindata definition to modules directory * Ignoring the new bindata path now * Updated to the new public modules import path * Updated public bindata command and drop the new prefix
This commit is contained in:
parent
4680c349dd
commit
b6a95a8cb3
691 changed files with 305318 additions and 1272 deletions
795
vendor/github.com/pingcap/tidb/optimizer/plan/planbuilder_join.go
generated
vendored
Normal file
795
vendor/github.com/pingcap/tidb/optimizer/plan/planbuilder_join.go
generated
vendored
Normal file
|
@ -0,0 +1,795 @@
|
|||
// Copyright 2015 PingCAP, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package plan
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/ngaut/log"
|
||||
"github.com/pingcap/tidb/ast"
|
||||
"github.com/pingcap/tidb/model"
|
||||
"github.com/pingcap/tidb/mysql"
|
||||
"github.com/pingcap/tidb/parser/opcode"
|
||||
)
|
||||
|
||||
// equalCond represents an equivalent join condition, like "t1.c1 = t2.c1".
|
||||
type equalCond struct {
|
||||
left *ast.ResultField
|
||||
leftIdx bool
|
||||
right *ast.ResultField
|
||||
rightIdx bool
|
||||
}
|
||||
|
||||
func newEqualCond(left, right *ast.ResultField) *equalCond {
|
||||
eq := &equalCond{left: left, right: right}
|
||||
eq.leftIdx = equivHasIndex(eq.left)
|
||||
eq.rightIdx = equivHasIndex(eq.right)
|
||||
return eq
|
||||
}
|
||||
|
||||
func equivHasIndex(rf *ast.ResultField) bool {
|
||||
if rf.Table.PKIsHandle && mysql.HasPriKeyFlag(rf.Column.Flag) {
|
||||
return true
|
||||
}
|
||||
for _, idx := range rf.Table.Indices {
|
||||
if len(idx.Columns) == 1 && idx.Columns[0].Name.L == rf.Column.Name.L {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// joinPath can be a single table path, inner join or outer join.
|
||||
type joinPath struct {
|
||||
// for table path
|
||||
table *ast.TableName
|
||||
totalFilterRate float64
|
||||
|
||||
// for subquery
|
||||
subquery ast.Node
|
||||
asName model.CIStr
|
||||
|
||||
neighborCount int // number of neighbor table.
|
||||
idxDepCount int // number of paths this table depends on.
|
||||
ordering *ast.ResultField
|
||||
orderingDesc bool
|
||||
|
||||
// for outer join path
|
||||
outer *joinPath
|
||||
inner *joinPath
|
||||
rightJoin bool
|
||||
|
||||
// for inner join path
|
||||
inners []*joinPath
|
||||
|
||||
// common
|
||||
parent *joinPath
|
||||
filterRate float64
|
||||
conditions []ast.ExprNode
|
||||
eqConds []*equalCond
|
||||
// The joinPaths that this path's index depends on.
|
||||
idxDeps map[*joinPath]bool
|
||||
neighbors map[*joinPath]bool
|
||||
}
|
||||
|
||||
// newTablePath creates a new table join path.
|
||||
func newTablePath(table *ast.TableName) *joinPath {
|
||||
return &joinPath{
|
||||
table: table,
|
||||
filterRate: rateFull,
|
||||
}
|
||||
}
|
||||
|
||||
// newSubqueryPath creates a new subquery join path.
|
||||
func newSubqueryPath(node ast.Node, asName model.CIStr) *joinPath {
|
||||
return &joinPath{
|
||||
subquery: node,
|
||||
asName: asName,
|
||||
filterRate: rateFull,
|
||||
}
|
||||
}
|
||||
|
||||
// newOuterJoinPath creates a new outer join path and pushes on condition to children paths.
|
||||
// The returned joinPath slice has one element.
|
||||
func newOuterJoinPath(isRightJoin bool, leftPath, rightPath *joinPath, on *ast.OnCondition) *joinPath {
|
||||
outerJoin := &joinPath{rightJoin: isRightJoin, outer: leftPath, inner: rightPath, filterRate: 1}
|
||||
leftPath.parent = outerJoin
|
||||
rightPath.parent = outerJoin
|
||||
if isRightJoin {
|
||||
outerJoin.outer, outerJoin.inner = outerJoin.inner, outerJoin.outer
|
||||
}
|
||||
if on != nil {
|
||||
conditions := splitWhere(on.Expr)
|
||||
availablePaths := []*joinPath{outerJoin.outer}
|
||||
for _, con := range conditions {
|
||||
if !outerJoin.inner.attachCondition(con, availablePaths) {
|
||||
log.Errorf("Inner failed to attach ON condition")
|
||||
}
|
||||
}
|
||||
}
|
||||
return outerJoin
|
||||
}
|
||||
|
||||
// newInnerJoinPath creates inner join path and pushes on condition to children paths.
|
||||
// If left path or right path is also inner join, it will be merged.
|
||||
func newInnerJoinPath(leftPath, rightPath *joinPath, on *ast.OnCondition) *joinPath {
|
||||
var innerJoin *joinPath
|
||||
if len(leftPath.inners) != 0 {
|
||||
innerJoin = leftPath
|
||||
} else {
|
||||
innerJoin = &joinPath{filterRate: leftPath.filterRate}
|
||||
innerJoin.inners = append(innerJoin.inners, leftPath)
|
||||
}
|
||||
if len(rightPath.inners) != 0 {
|
||||
innerJoin.inners = append(innerJoin.inners, rightPath.inners...)
|
||||
innerJoin.conditions = append(innerJoin.conditions, rightPath.conditions...)
|
||||
} else {
|
||||
innerJoin.inners = append(innerJoin.inners, rightPath)
|
||||
}
|
||||
innerJoin.filterRate *= rightPath.filterRate
|
||||
|
||||
for _, in := range innerJoin.inners {
|
||||
in.parent = innerJoin
|
||||
}
|
||||
|
||||
if on != nil {
|
||||
conditions := splitWhere(on.Expr)
|
||||
for _, con := range conditions {
|
||||
if !innerJoin.attachCondition(con, nil) {
|
||||
innerJoin.conditions = append(innerJoin.conditions, con)
|
||||
}
|
||||
}
|
||||
}
|
||||
return innerJoin
|
||||
}
|
||||
|
||||
func (p *joinPath) resultFields() []*ast.ResultField {
|
||||
if p.table != nil {
|
||||
return p.table.GetResultFields()
|
||||
}
|
||||
if p.outer != nil {
|
||||
if p.rightJoin {
|
||||
return append(p.inner.resultFields(), p.outer.resultFields()...)
|
||||
}
|
||||
return append(p.outer.resultFields(), p.inner.resultFields()...)
|
||||
}
|
||||
var rfs []*ast.ResultField
|
||||
for _, in := range p.inners {
|
||||
rfs = append(rfs, in.resultFields()...)
|
||||
}
|
||||
return rfs
|
||||
}
|
||||
|
||||
// attachCondition tries to attach a condition as deep as possible.
|
||||
// availablePaths are paths join before this path.
|
||||
func (p *joinPath) attachCondition(condition ast.ExprNode, availablePaths []*joinPath) (attached bool) {
|
||||
filterRate := guesstimateFilterRate(condition)
|
||||
// table
|
||||
if p.table != nil || p.subquery != nil {
|
||||
attacher := conditionAttachChecker{targetPath: p, availablePaths: availablePaths}
|
||||
condition.Accept(&attacher)
|
||||
if attacher.invalid {
|
||||
return false
|
||||
}
|
||||
p.conditions = append(p.conditions, condition)
|
||||
p.filterRate *= filterRate
|
||||
return true
|
||||
}
|
||||
// inner join
|
||||
if len(p.inners) > 0 {
|
||||
for _, in := range p.inners {
|
||||
if in.attachCondition(condition, availablePaths) {
|
||||
p.filterRate *= filterRate
|
||||
return true
|
||||
}
|
||||
}
|
||||
attacher := &conditionAttachChecker{targetPath: p, availablePaths: availablePaths}
|
||||
condition.Accept(attacher)
|
||||
if attacher.invalid {
|
||||
return false
|
||||
}
|
||||
p.conditions = append(p.conditions, condition)
|
||||
p.filterRate *= filterRate
|
||||
return true
|
||||
}
|
||||
|
||||
// outer join
|
||||
if p.outer.attachCondition(condition, availablePaths) {
|
||||
p.filterRate *= filterRate
|
||||
return true
|
||||
}
|
||||
if p.inner.attachCondition(condition, append(availablePaths, p.outer)) {
|
||||
p.filterRate *= filterRate
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *joinPath) containsTable(table *ast.TableName) bool {
|
||||
if p.table != nil {
|
||||
return p.table == table
|
||||
}
|
||||
if p.subquery != nil {
|
||||
return p.asName.L == table.Name.L
|
||||
}
|
||||
if len(p.inners) != 0 {
|
||||
for _, in := range p.inners {
|
||||
if in.containsTable(table) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return p.outer.containsTable(table) || p.inner.containsTable(table)
|
||||
}
|
||||
|
||||
// attachEqualCond tries to attach an equalCond deep into a table path if applicable.
|
||||
func (p *joinPath) attachEqualCond(eqCon *equalCond, availablePaths []*joinPath) (attached bool) {
|
||||
// table
|
||||
if p.table != nil {
|
||||
var prevTable *ast.TableName
|
||||
var needSwap bool
|
||||
if eqCon.left.TableName == p.table {
|
||||
prevTable = eqCon.right.TableName
|
||||
} else if eqCon.right.TableName == p.table {
|
||||
prevTable = eqCon.left.TableName
|
||||
needSwap = true
|
||||
}
|
||||
if prevTable != nil {
|
||||
for _, prev := range availablePaths {
|
||||
if prev.containsTable(prevTable) {
|
||||
if needSwap {
|
||||
eqCon.left, eqCon.right = eqCon.right, eqCon.left
|
||||
eqCon.leftIdx, eqCon.rightIdx = eqCon.rightIdx, eqCon.leftIdx
|
||||
}
|
||||
p.eqConds = append(p.eqConds, eqCon)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// inner join
|
||||
if len(p.inners) > 0 {
|
||||
for _, in := range p.inners {
|
||||
if in.attachEqualCond(eqCon, availablePaths) {
|
||||
p.filterRate *= rateEqual
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
// outer join
|
||||
if p.outer.attachEqualCond(eqCon, availablePaths) {
|
||||
p.filterRate *= rateEqual
|
||||
return true
|
||||
}
|
||||
if p.inner.attachEqualCond(eqCon, append(availablePaths, p.outer)) {
|
||||
p.filterRate *= rateEqual
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *joinPath) extractEqualConditon() {
|
||||
var equivs []*equalCond
|
||||
var cons []ast.ExprNode
|
||||
for _, con := range p.conditions {
|
||||
eq := equivFromExpr(con)
|
||||
if eq != nil {
|
||||
equivs = append(equivs, eq)
|
||||
if p.table != nil {
|
||||
if eq.right.TableName == p.table {
|
||||
eq.left, eq.right = eq.right, eq.left
|
||||
eq.leftIdx, eq.rightIdx = eq.rightIdx, eq.leftIdx
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cons = append(cons, con)
|
||||
}
|
||||
}
|
||||
p.eqConds = equivs
|
||||
p.conditions = cons
|
||||
for _, in := range p.inners {
|
||||
in.extractEqualConditon()
|
||||
}
|
||||
if p.outer != nil {
|
||||
p.outer.extractEqualConditon()
|
||||
p.inner.extractEqualConditon()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *joinPath) addIndexDependency() {
|
||||
if p.outer != nil {
|
||||
p.outer.addIndexDependency()
|
||||
p.inner.addIndexDependency()
|
||||
return
|
||||
}
|
||||
if p.table != nil {
|
||||
return
|
||||
}
|
||||
for _, eq := range p.eqConds {
|
||||
if !eq.leftIdx && !eq.rightIdx {
|
||||
continue
|
||||
}
|
||||
pathLeft := p.findInnerContains(eq.left.TableName)
|
||||
if pathLeft == nil {
|
||||
continue
|
||||
}
|
||||
pathRight := p.findInnerContains(eq.right.TableName)
|
||||
if pathRight == nil {
|
||||
continue
|
||||
}
|
||||
if eq.leftIdx && eq.rightIdx {
|
||||
pathLeft.addNeighbor(pathRight)
|
||||
pathRight.addNeighbor(pathLeft)
|
||||
} else if eq.leftIdx {
|
||||
if !pathLeft.hasOuterIdxEqualCond() {
|
||||
pathLeft.addIndexDep(pathRight)
|
||||
}
|
||||
} else if eq.rightIdx {
|
||||
if !pathRight.hasOuterIdxEqualCond() {
|
||||
pathRight.addIndexDep(pathLeft)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, in := range p.inners {
|
||||
in.removeIndexDepCycle(in)
|
||||
in.addIndexDependency()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *joinPath) hasOuterIdxEqualCond() bool {
|
||||
if p.table != nil {
|
||||
for _, eq := range p.eqConds {
|
||||
if eq.leftIdx {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if p.outer != nil {
|
||||
return p.outer.hasOuterIdxEqualCond()
|
||||
}
|
||||
for _, in := range p.inners {
|
||||
if in.hasOuterIdxEqualCond() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *joinPath) findInnerContains(table *ast.TableName) *joinPath {
|
||||
for _, in := range p.inners {
|
||||
if in.containsTable(table) {
|
||||
return in
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *joinPath) addNeighbor(neighbor *joinPath) {
|
||||
if p.neighbors == nil {
|
||||
p.neighbors = map[*joinPath]bool{}
|
||||
}
|
||||
p.neighbors[neighbor] = true
|
||||
p.neighborCount++
|
||||
}
|
||||
|
||||
func (p *joinPath) addIndexDep(dep *joinPath) {
|
||||
if p.idxDeps == nil {
|
||||
p.idxDeps = map[*joinPath]bool{}
|
||||
}
|
||||
p.idxDeps[dep] = true
|
||||
p.idxDepCount++
|
||||
}
|
||||
|
||||
func (p *joinPath) removeIndexDepCycle(origin *joinPath) {
|
||||
if p.idxDeps == nil {
|
||||
return
|
||||
}
|
||||
for dep := range p.idxDeps {
|
||||
if dep == origin {
|
||||
delete(p.idxDeps, origin)
|
||||
continue
|
||||
}
|
||||
dep.removeIndexDepCycle(origin)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *joinPath) score() float64 {
|
||||
return 1 / p.filterRate
|
||||
}
|
||||
|
||||
func (p *joinPath) String() string {
|
||||
if p.table != nil {
|
||||
return p.table.TableInfo.Name.L
|
||||
}
|
||||
if p.outer != nil {
|
||||
return "outer{" + p.outer.String() + "," + p.inner.String() + "}"
|
||||
}
|
||||
var innerStrs []string
|
||||
for _, in := range p.inners {
|
||||
innerStrs = append(innerStrs, in.String())
|
||||
}
|
||||
return "inner{" + strings.Join(innerStrs, ",") + "}"
|
||||
}
|
||||
|
||||
func (p *joinPath) optimizeJoinOrder(availablePaths []*joinPath) {
|
||||
if p.table != nil {
|
||||
return
|
||||
}
|
||||
if p.outer != nil {
|
||||
p.outer.optimizeJoinOrder(availablePaths)
|
||||
p.inner.optimizeJoinOrder(append(availablePaths, p.outer))
|
||||
return
|
||||
}
|
||||
var ordered []*joinPath
|
||||
pathMap := map[*joinPath]bool{}
|
||||
for _, in := range p.inners {
|
||||
pathMap[in] = true
|
||||
}
|
||||
for len(pathMap) > 0 {
|
||||
next := p.nextPath(pathMap, availablePaths)
|
||||
next.optimizeJoinOrder(availablePaths)
|
||||
ordered = append(ordered, next)
|
||||
delete(pathMap, next)
|
||||
availablePaths = append(availablePaths, next)
|
||||
for path := range pathMap {
|
||||
if path.idxDeps != nil {
|
||||
delete(path.idxDeps, next)
|
||||
}
|
||||
if path.neighbors != nil {
|
||||
delete(path.neighbors, next)
|
||||
}
|
||||
}
|
||||
p.reattach(pathMap, availablePaths)
|
||||
}
|
||||
p.inners = ordered
|
||||
}
|
||||
|
||||
// reattach is called by inner joinPath to retry attach conditions to inner paths
|
||||
// after an inner path has been added to available paths.
|
||||
func (p *joinPath) reattach(pathMap map[*joinPath]bool, availablePaths []*joinPath) {
|
||||
if len(p.conditions) != 0 {
|
||||
remainedConds := make([]ast.ExprNode, 0, len(p.conditions))
|
||||
for _, con := range p.conditions {
|
||||
var attached bool
|
||||
for path := range pathMap {
|
||||
if path.attachCondition(con, availablePaths) {
|
||||
attached = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !attached {
|
||||
remainedConds = append(remainedConds, con)
|
||||
}
|
||||
}
|
||||
p.conditions = remainedConds
|
||||
}
|
||||
if len(p.eqConds) != 0 {
|
||||
remainedEqConds := make([]*equalCond, 0, len(p.eqConds))
|
||||
for _, eq := range p.eqConds {
|
||||
var attached bool
|
||||
for path := range pathMap {
|
||||
if path.attachEqualCond(eq, availablePaths) {
|
||||
attached = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !attached {
|
||||
remainedEqConds = append(remainedEqConds, eq)
|
||||
}
|
||||
}
|
||||
p.eqConds = remainedEqConds
|
||||
}
|
||||
}
|
||||
|
||||
func (p *joinPath) nextPath(pathMap map[*joinPath]bool, availablePaths []*joinPath) *joinPath {
|
||||
cans := p.candidates(pathMap)
|
||||
if len(cans) == 0 {
|
||||
var v *joinPath
|
||||
for v = range pathMap {
|
||||
log.Errorf("index dep %v, prevs %v\n", v.idxDeps, len(availablePaths))
|
||||
}
|
||||
return v
|
||||
}
|
||||
indexPath := p.nextIndexPath(cans)
|
||||
if indexPath != nil {
|
||||
return indexPath
|
||||
}
|
||||
return p.pickPath(cans)
|
||||
}
|
||||
|
||||
func (p *joinPath) candidates(pathMap map[*joinPath]bool) []*joinPath {
|
||||
var cans []*joinPath
|
||||
for t := range pathMap {
|
||||
if len(t.idxDeps) > 0 {
|
||||
continue
|
||||
}
|
||||
cans = append(cans, t)
|
||||
}
|
||||
return cans
|
||||
}
|
||||
|
||||
func (p *joinPath) nextIndexPath(candidates []*joinPath) *joinPath {
|
||||
var best *joinPath
|
||||
for _, can := range candidates {
|
||||
// Since we may not have equal conditions attached on the path, we
|
||||
// need to check neighborCount and idxDepCount to see if this path
|
||||
// can be joined with index.
|
||||
neighborIsAvailable := len(can.neighbors) < can.neighborCount
|
||||
idxDepIsAvailable := can.idxDepCount > 0
|
||||
if can.hasOuterIdxEqualCond() || neighborIsAvailable || idxDepIsAvailable {
|
||||
if best == nil {
|
||||
best = can
|
||||
}
|
||||
if can.score() > best.score() {
|
||||
best = can
|
||||
}
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
func (p *joinPath) pickPath(candidates []*joinPath) *joinPath {
|
||||
var best *joinPath
|
||||
for _, path := range candidates {
|
||||
if best == nil {
|
||||
best = path
|
||||
}
|
||||
if path.score() > best.score() {
|
||||
best = path
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
// conditionAttachChecker checks if an expression is valid to
|
||||
// attach to a path. attach is valid only if all the referenced tables in the
|
||||
// expression are available.
|
||||
type conditionAttachChecker struct {
|
||||
targetPath *joinPath
|
||||
availablePaths []*joinPath
|
||||
invalid bool
|
||||
}
|
||||
|
||||
func (c *conditionAttachChecker) Enter(in ast.Node) (ast.Node, bool) {
|
||||
switch x := in.(type) {
|
||||
case *ast.ColumnNameExpr:
|
||||
table := x.Refer.TableName
|
||||
if c.targetPath.containsTable(table) {
|
||||
return in, false
|
||||
}
|
||||
c.invalid = true
|
||||
for _, path := range c.availablePaths {
|
||||
if path.containsTable(table) {
|
||||
c.invalid = false
|
||||
return in, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return in, false
|
||||
}
|
||||
|
||||
func (c *conditionAttachChecker) Leave(in ast.Node) (ast.Node, bool) {
|
||||
return in, !c.invalid
|
||||
}
|
||||
|
||||
func (b *planBuilder) buildJoin(sel *ast.SelectStmt) Plan {
|
||||
nrfinder := &nullRejectFinder{nullRejectTables: map[*ast.TableName]bool{}}
|
||||
if sel.Where != nil {
|
||||
sel.Where.Accept(nrfinder)
|
||||
}
|
||||
path := b.buildBasicJoinPath(sel.From.TableRefs, nrfinder.nullRejectTables)
|
||||
rfs := path.resultFields()
|
||||
|
||||
whereConditions := splitWhere(sel.Where)
|
||||
for _, whereCond := range whereConditions {
|
||||
if !path.attachCondition(whereCond, nil) {
|
||||
// TODO: Find a better way to handle this condition.
|
||||
path.conditions = append(path.conditions, whereCond)
|
||||
log.Errorf("Failed to attach where condtion.")
|
||||
}
|
||||
}
|
||||
path.extractEqualConditon()
|
||||
path.addIndexDependency()
|
||||
path.optimizeJoinOrder(nil)
|
||||
p := b.buildPlanFromJoinPath(path)
|
||||
p.SetFields(rfs)
|
||||
return p
|
||||
}
|
||||
|
||||
type nullRejectFinder struct {
|
||||
nullRejectTables map[*ast.TableName]bool
|
||||
}
|
||||
|
||||
func (n *nullRejectFinder) Enter(in ast.Node) (ast.Node, bool) {
|
||||
switch x := in.(type) {
|
||||
case *ast.BinaryOperationExpr:
|
||||
if x.Op == opcode.NullEQ || x.Op == opcode.OrOr {
|
||||
return in, true
|
||||
}
|
||||
case *ast.IsNullExpr:
|
||||
if !x.Not {
|
||||
return in, true
|
||||
}
|
||||
case *ast.IsTruthExpr:
|
||||
if x.Not {
|
||||
return in, true
|
||||
}
|
||||
}
|
||||
return in, false
|
||||
}
|
||||
|
||||
func (n *nullRejectFinder) Leave(in ast.Node) (ast.Node, bool) {
|
||||
switch x := in.(type) {
|
||||
case *ast.ColumnNameExpr:
|
||||
n.nullRejectTables[x.Refer.TableName] = true
|
||||
}
|
||||
return in, true
|
||||
}
|
||||
|
||||
func (b *planBuilder) buildBasicJoinPath(node ast.ResultSetNode, nullRejectTables map[*ast.TableName]bool) *joinPath {
|
||||
switch x := node.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case *ast.Join:
|
||||
leftPath := b.buildBasicJoinPath(x.Left, nullRejectTables)
|
||||
if x.Right == nil {
|
||||
return leftPath
|
||||
}
|
||||
righPath := b.buildBasicJoinPath(x.Right, nullRejectTables)
|
||||
isOuter := b.isOuterJoin(x.Tp, leftPath, righPath, nullRejectTables)
|
||||
if isOuter {
|
||||
return newOuterJoinPath(x.Tp == ast.RightJoin, leftPath, righPath, x.On)
|
||||
}
|
||||
return newInnerJoinPath(leftPath, righPath, x.On)
|
||||
case *ast.TableSource:
|
||||
switch v := x.Source.(type) {
|
||||
case *ast.TableName:
|
||||
return newTablePath(v)
|
||||
case *ast.SelectStmt, *ast.UnionStmt:
|
||||
return newSubqueryPath(v, x.AsName)
|
||||
default:
|
||||
b.err = ErrUnsupportedType.Gen("unsupported table source type %T", x)
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
b.err = ErrUnsupportedType.Gen("unsupported table source type %T", x)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *planBuilder) isOuterJoin(tp ast.JoinType, leftPaths, rightPaths *joinPath,
|
||||
nullRejectTables map[*ast.TableName]bool) bool {
|
||||
var innerPath *joinPath
|
||||
switch tp {
|
||||
case ast.LeftJoin:
|
||||
innerPath = rightPaths
|
||||
case ast.RightJoin:
|
||||
innerPath = leftPaths
|
||||
default:
|
||||
return false
|
||||
}
|
||||
for table := range nullRejectTables {
|
||||
if innerPath.containsTable(table) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func equivFromExpr(expr ast.ExprNode) *equalCond {
|
||||
binop, ok := expr.(*ast.BinaryOperationExpr)
|
||||
if !ok || binop.Op != opcode.EQ {
|
||||
return nil
|
||||
}
|
||||
ln, lOK := binop.L.(*ast.ColumnNameExpr)
|
||||
rn, rOK := binop.R.(*ast.ColumnNameExpr)
|
||||
if !lOK || !rOK {
|
||||
return nil
|
||||
}
|
||||
if ln.Name.Table.L == "" || rn.Name.Table.L == "" {
|
||||
return nil
|
||||
}
|
||||
if ln.Name.Schema.L == rn.Name.Schema.L && ln.Name.Table.L == rn.Name.Table.L {
|
||||
return nil
|
||||
}
|
||||
return newEqualCond(ln.Refer, rn.Refer)
|
||||
}
|
||||
|
||||
func (b *planBuilder) buildPlanFromJoinPath(path *joinPath) Plan {
|
||||
if path.table != nil {
|
||||
return b.buildTablePlanFromJoinPath(path)
|
||||
}
|
||||
if path.subquery != nil {
|
||||
return b.buildSubqueryJoinPath(path)
|
||||
}
|
||||
if path.outer != nil {
|
||||
join := &JoinOuter{
|
||||
Outer: b.buildPlanFromJoinPath(path.outer),
|
||||
Inner: b.buildPlanFromJoinPath(path.inner),
|
||||
}
|
||||
if path.rightJoin {
|
||||
join.SetFields(append(join.Inner.Fields(), join.Outer.Fields()...))
|
||||
} else {
|
||||
join.SetFields(append(join.Outer.Fields(), join.Inner.Fields()...))
|
||||
}
|
||||
return join
|
||||
}
|
||||
join := &JoinInner{}
|
||||
for _, in := range path.inners {
|
||||
join.Inners = append(join.Inners, b.buildPlanFromJoinPath(in))
|
||||
join.fields = append(join.fields, in.resultFields()...)
|
||||
}
|
||||
join.Conditions = path.conditions
|
||||
for _, equiv := range path.eqConds {
|
||||
cond := &ast.BinaryOperationExpr{L: equiv.left.Expr, R: equiv.right.Expr, Op: opcode.EQ}
|
||||
join.Conditions = append(join.Conditions, cond)
|
||||
}
|
||||
return join
|
||||
}
|
||||
|
||||
func (b *planBuilder) buildTablePlanFromJoinPath(path *joinPath) Plan {
|
||||
for _, equiv := range path.eqConds {
|
||||
columnNameExpr := &ast.ColumnNameExpr{}
|
||||
columnNameExpr.Name = &ast.ColumnName{}
|
||||
columnNameExpr.Name.Name = equiv.left.Column.Name
|
||||
columnNameExpr.Name.Table = equiv.left.Table.Name
|
||||
columnNameExpr.Refer = equiv.left
|
||||
condition := &ast.BinaryOperationExpr{L: columnNameExpr, R: equiv.right.Expr, Op: opcode.EQ}
|
||||
ast.SetFlag(condition)
|
||||
path.conditions = append(path.conditions, condition)
|
||||
}
|
||||
candidates := b.buildAllAccessMethodsPlan(path)
|
||||
var p Plan
|
||||
var lowestCost float64
|
||||
for _, can := range candidates {
|
||||
cost := EstimateCost(can)
|
||||
if p == nil {
|
||||
p = can
|
||||
lowestCost = cost
|
||||
}
|
||||
if cost < lowestCost {
|
||||
p = can
|
||||
lowestCost = cost
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Build subquery join path plan
|
||||
func (b *planBuilder) buildSubqueryJoinPath(path *joinPath) Plan {
|
||||
for _, equiv := range path.eqConds {
|
||||
columnNameExpr := &ast.ColumnNameExpr{}
|
||||
columnNameExpr.Name = &ast.ColumnName{}
|
||||
columnNameExpr.Name.Name = equiv.left.Column.Name
|
||||
columnNameExpr.Name.Table = equiv.left.Table.Name
|
||||
columnNameExpr.Refer = equiv.left
|
||||
condition := &ast.BinaryOperationExpr{L: columnNameExpr, R: equiv.right.Expr, Op: opcode.EQ}
|
||||
ast.SetFlag(condition)
|
||||
path.conditions = append(path.conditions, condition)
|
||||
}
|
||||
p := b.build(path.subquery)
|
||||
if len(path.conditions) == 0 {
|
||||
return p
|
||||
}
|
||||
filterPlan := &Filter{Conditions: path.conditions}
|
||||
filterPlan.SetSrc(p)
|
||||
filterPlan.SetFields(p.Fields())
|
||||
return filterPlan
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue