Every `ast.Statement` needs to set flag before use, set the flag in parse function, so we don't need to set it somewhere else and won't forget to do it.
105 lines
2.9 KiB
Go
105 lines
2.9 KiB
Go
// 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 executor
|
|
|
|
import (
|
|
"github.com/juju/errors"
|
|
"github.com/ngaut/log"
|
|
"github.com/pingcap/tidb/ast"
|
|
"github.com/pingcap/tidb/context"
|
|
"github.com/pingcap/tidb/infoschema"
|
|
"github.com/pingcap/tidb/plan"
|
|
"github.com/pingcap/tidb/sessionctx"
|
|
"github.com/pingcap/tidb/sessionctx/binloginfo"
|
|
"github.com/pingcap/tidb/sessionctx/variable"
|
|
)
|
|
|
|
// Compiler compiles an ast.StmtNode to a stmt.Statement.
|
|
type Compiler struct {
|
|
}
|
|
|
|
func statementLabel(node ast.StmtNode) string {
|
|
switch node.(type) {
|
|
case *ast.SelectStmt:
|
|
return "Select"
|
|
case *ast.DeleteStmt:
|
|
return "Delete"
|
|
case *ast.InsertStmt:
|
|
if node.(*ast.InsertStmt).IsReplace {
|
|
return "Replace"
|
|
}
|
|
return "Insert"
|
|
case *ast.UnionStmt:
|
|
return "Union"
|
|
case *ast.UpdateStmt:
|
|
return "Update"
|
|
case *ast.CreateIndexStmt:
|
|
return "CreateIndex"
|
|
case *ast.CreateTableStmt:
|
|
return "CreateTable"
|
|
case *ast.CreateDatabaseStmt:
|
|
return "CreateDatabase"
|
|
case *ast.DropDatabaseStmt:
|
|
return "DropDatabase"
|
|
case *ast.DropTableStmt:
|
|
return "DropTable"
|
|
case *ast.DropIndexStmt:
|
|
return "DropIndex"
|
|
}
|
|
return "unknown"
|
|
}
|
|
|
|
// Compile compiles an ast.StmtNode to a stmt.Statement.
|
|
// If it is supported to use new plan and executer, it optimizes the node to
|
|
// a plan, and we wrap the plan in an adapter as stmt.Statement.
|
|
// If it is not supported, the node will be converted to old statement.
|
|
func (c *Compiler) Compile(ctx context.Context, node ast.StmtNode) (ast.Statement, error) {
|
|
stmtNodeCounter.WithLabelValues(statementLabel(node)).Inc()
|
|
if _, ok := node.(*ast.UpdateStmt); ok {
|
|
sVars := variable.GetSessionVars(ctx)
|
|
sVars.InUpdateStmt = true
|
|
defer func() {
|
|
sVars.InUpdateStmt = false
|
|
}()
|
|
}
|
|
|
|
var is infoschema.InfoSchema
|
|
if snap := variable.GetSessionVars(ctx).SnapshotInfoschema; snap != nil {
|
|
is = snap.(infoschema.InfoSchema)
|
|
log.Infof("use snapshot schema %d", is.SchemaMetaVersion())
|
|
} else {
|
|
is = sessionctx.GetDomain(ctx).InfoSchema()
|
|
binloginfo.SetSchemaVersion(ctx, is.SchemaMetaVersion())
|
|
}
|
|
if err := plan.Preprocess(node, is, ctx); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
// Validate should be after NameResolve.
|
|
if err := plan.Validate(node, false); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
p, err := plan.Optimize(ctx, node, is)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
_, isDDL := node.(ast.DDLNode)
|
|
sa := &statement{
|
|
is: is,
|
|
plan: p,
|
|
text: node.Text(),
|
|
isDDL: isDDL,
|
|
}
|
|
return sa, nil
|
|
}
|