// Compile compiles an ast.StmtNode to a physical plan.
func (c *Compiler) Compile(goCtx goctx.Context, stmtNode ast.StmtNode) (*ExecStmt, error) {
//validation, name binding
plan.Preprocess(c.Ctx, stmtNode, infoSchema, false)
infoSchema := GetInfoSchema(c.Ctx)
// build and optimize query plan
finalPlan = plan.Optimize(c.Ctx, stmtNode, infoSchema)
return &ExecStmt{
InfoSchema: infoSchema,
Plan: finalPlan,
Expensive: isExpensive,
Cacheable: plan.Cacheable(stmtNode),
Text: stmtNode.Text(),
StmtNode: stmtNode,
Ctx: c.Ctx,
}, nil
}
// Optimize does optimization and creates a Plan.
// The node must be prepared first.
func Optimize(ctx context.Context, node ast.Node, is infoschema.InfoSchema) (Plan, error) {
builder := &planBuilder{
ctx: ctx,
is: is,
colMapper: make(map[*ast.ColumnNameExpr]int),
}
p := builder.build(node)
//Assuming p is a LogicalPlan, which could be optimzied
return doOptimize(builder.optFlag, p)
}
func (b *planBuilder) build(node ast.Node) Plan {
b.optFlag = flagPrunColumns
switch x := node.(type) {
case *ast.InsertStmt:
return b.buildInsert(x)
case *ast.SelectStmt:
return b.buildSelect(x)
}
b.err = ErrUnsupportedType.Gen("Unsupported type %T", node)
return nil
}
/*
Hierarchy: ResultSet <- GroupBy <- Selection <- Aggregation <- Projection
ResultSetNode interface has a ResultFields property, represents a Node that returns result set. Implementations include SelectStmt, SubqueryExpr, TableSource, TableName and Join.
*/
func (b *planBuilder) buildSelect(sel *ast.SelectStmt) LogicalPlan {
var (
p LogicalPlan
aggFuncs []*ast.AggregateFuncExpr
totalMap map[*ast.AggregateFuncExpr]int
gbyCols []expression.Expression
)
//base plan node
p = b.buildResultSetNode(sel.From.TableRefs)
originalFields := sel.Fields.Fields
//add children
p, gbyCols = b.resolveGbyExprs(p, sel.GroupBy, sel.Fields.Fields)
//add children
p = b.buildSelection(p, sel.Where, nil)
aggFuncs, totalMap = b.extractAggFuncs(sel.Fields.Fields)
var aggIndexMap map[int]int
//add children
p, aggIndexMap = b.buildAggregation(p, aggFuncs, gbyCols)
for k, v := range totalMap {
totalMap[k] = aggIndexMap[v]
}
var oldLen int
//add children
p, oldLen = b.buildProjection(p, sel.Fields.Fields, totalMap)
p = b.buildDistinct(p, oldLen)
p = b.buildSort(p, sel.OrderBy.Items, orderMap)
p = b.buildLimit(p, sel.Limit)
sel.Fields.Fields = originalFields
if oldLen != p.Schema().Len() {
proj := LogicalProjection{Exprs: expression.Column2Exprs(p.Schema().Columns[:oldLen])}.init(b.ctx)
proj.SetChildren(p)
schema := expression.NewSchema(p.Schema().Clone().Columns[:oldLen]...)
for _, col := range schema.Columns {
col.FromID = proj.ID()
}
proj.SetSchema(schema)
return proj
}
return p
}
func (b *planBuilder) buildResultSetNode(node ast.ResultSetNode) LogicalPlan {
switch x := node.(type) {
case *ast.Join:
return b.buildJoin(x)
case *ast.TableSource:
var p LogicalPlan
switch v := x.Source.(type) {
case *ast.SelectStmt:
p = b.buildSelect(v)
case *ast.TableName:
p = b.buildDataSource(v)
if v, ok := p.(*DataSource); ok {
v.TableAsName = &x.AsName
}
for _, col := range p.Schema().Columns {
col.OrigTblName = col.TblName
if x.AsName.L != "" {
col.TblName = x.AsName
col.DBName = model.NewCIStr("")
}
}
// Duplicate column name in one table is not allowed.
// "select * from (select 1, 1) as a;" is duplicate
dupNames := make(map[string]struct{}, len(p.Schema().Columns))
for _, col := range p.Schema().Columns {
name := col.ColName.O
if _, ok := dupNames[name]; ok {
b.err = ErrDupFieldName.GenByArgs(name)
return nil
}
dupNames[name] = struct{}{}
}
return p
case *ast.SelectStmt:
return b.buildSelect(x)
}
}
// buildProjection returns a Projection plan and non-aux columns length.
func (b *planBuilder) buildProjection(p LogicalPlan, fields []*ast.SelectField, mapper map[*ast.AggregateFuncExpr]int) (LogicalPlan, int) {
b.optFlag |= flagEliminateProjection
b.curClause = fieldList
proj := LogicalProjection{Exprs: make([]expression.Expression, 0, len(fields))}.init(b.ctx)
schema := expression.NewSchema(make([]*expression.Column, 0, len(fields))...)
oldLen := 0
for _, field := range fields {
newExpr, np, err := b.rewrite(field.Expr, p, mapper, true)
p = np
proj.Exprs = append(proj.Exprs, newExpr)
col := b.buildProjectionField(proj.id, schema.Len()+1, field, newExpr)
schema.Append(col)
if !field.Auxiliary {
oldLen++
}
}
proj.SetSchema(schema)
proj.SetChildren(p)
return proj, oldLen
}
func (b *planBuilder) buildSelection(p LogicalPlan, where ast.ExprNode, AggMapper map[*ast.AggregateFuncExpr]int) LogicalPlan {
b.optFlag = b.optFlag | flagPredicatePushDown
if b.curClause != havingClause {
b.curClause = whereClause
}
conditions := splitWhere(where)
expressions := make([]expression.Expression, 0, len(conditions))
selection := LogicalSelection{}.init(b.ctx)
for _, cond := range conditions {
expr, np, err := b.rewrite(cond, p, AggMapper, false)
p = np
if expr == nil {
continue
}
cnfItems := expression.SplitCNFItems(expr)
for _, item := range cnfItems {
expressions = append(expressions, item)
}
}
selection.Conditions = expressions
selection.SetChildren(p)
return selection
}