func NewStmtInfo(db string, sql string, r *router.Router) *StmtInfo {
	return &StmtInfo{
		db:               db,
		sql:              sql,
		router:           r,
		tableRules:       make(map[string]router.Rule),
		globalTableRules: make(map[string]router.Rule),
		result:           NewRouteResult("", "", nil),  
	}
}
NewStmtInfo constructor of StmtInfo
package plan
import (
	"fmt"
	"strings"
	"github.com/nooncall/shazam/mysql"
	"github.com/nooncall/shazam/parser"
	"github.com/nooncall/shazam/parser/ast"
	"github.com/nooncall/shazam/parser/format"
	"github.com/nooncall/shazam/proxy/router"
	"github.com/nooncall/shazam/proxy/sequence"
	"github.com/nooncall/shazam/util"
	"github.com/nooncall/shazam/util/hack"
)
var _ Plan = &UnshardPlan{}
var _ Plan = &SelectPlan{}
var _ Plan = &DeletePlan{}
var _ Plan = &UpdatePlan{}
var _ Plan = &InsertPlan{}
var _ Plan = &SelectLastInsertIDPlan{}
type Plan interface {
	ExecuteIn(*util.RequestContext, Executor) (*mysql.Result, error)
	Size() int
}
type Executor interface {
	ExecuteSQL(ctx *util.RequestContext, slice, db, sql string) (*mysql.Result, error)
	ExecuteSQLs(*util.RequestContext, map[string]map[string][]string) ([]*mysql.Result, error)
	SetLastInsertID(uint64)
	GetLastInsertID() uint64
}
type Checker struct {
	db            string
	router        *router.Router
	hasShardTable bool  
	dbInvalid     bool  
	tableNames    []*ast.TableName
}
func NewChecker(db string, router *router.Router) *Checker {
	return &Checker{
		db:            db,
		router:        router,
		hasShardTable: false,
		dbInvalid:     false,
	}
}
func (s *Checker) GetUnshardTableNames() []*ast.TableName {
	return s.tableNames
}
func (s *Checker) IsDatabaseInvalid() bool {
	return s.dbInvalid
}
func (s *Checker) IsShard() bool {
	return s.hasShardTable
}
func (s *Checker) Enter(n ast.Node) (node ast.Node, skipChildren bool) {
	if s.hasShardTable {
		return n, true
	}
	switch nn := n.(type) {
	case *ast.TableName:
		if s.isTableNameDatabaseInvalid(nn) {
			s.dbInvalid = true
			return n, true
		}
		has := s.hasShardTableInTableName(nn)
		if has {
			s.hasShardTable = true
			return n, true
		}
		s.tableNames = append(s.tableNames, nn)
	}
	return n, false
}
func (s *Checker) Leave(n ast.Node) (node ast.Node, ok bool) {
	return n, !s.dbInvalid && !s.hasShardTable
}
func (s *Checker) isTableNameDatabaseInvalid(n *ast.TableName) bool {
	return s.db == "" && n.Schema.L == ""
}
func (s *Checker) hasShardTableInTableName(n *ast.TableName) bool {
	db := n.Schema.L
	if db == "" {
		db = s.db
	}
	table := n.Name.L
	_, ok := s.router.GetShardRule(db, table)
	return ok
}
func (s *Checker) hasShardTableInColumnName(n *ast.ColumnName) bool {
	db := n.Schema.L
	if db == "" {
		db = s.db
	}
	table := n.Table.L
	_, ok := s.router.GetShardRule(db, table)
	return ok
}
type basePlan struct{}
func (*basePlan) Size() int {
	return 1
}
type StmtInfo struct {
	db               string  
	sql              string  
	router           *router.Router
	tableRules       map[string]router.Rule  
	globalTableRules map[string]router.Rule  
	result           *RouteResult
}
type TableAliasStmtInfo struct {
	*StmtInfo
	tableAlias map[string]string  
	hintPhyDB  string             
}
func BuildPlan(stmt ast.StmtNode, phyDBs map[string]string, db, sql string, router *router.Router, seq *sequence.SequenceManager) (Plan, error) {
	if IsSelectLastInsertIDStmt(stmt) {
		return CreateSelectLastInsertIDPlan(), nil
	}
	if estmt, ok := stmt.(*ast.ExplainStmt); ok {
		return buildExplainPlan(estmt, phyDBs, db, sql, router, seq)
	}
	checker := NewChecker(db, router)
	stmt.Accept(checker)
	if checker.IsDatabaseInvalid() {
		return nil, fmt.Errorf("no database selected")  
	}
	if checker.IsShard() {
		return buildShardPlan(stmt, db, sql, router, seq)
	}
	return CreateUnshardPlan(stmt, phyDBs, db, checker.GetUnshardTableNames())
}
func buildShardPlan(stmt ast.StmtNode, db string, sql string, router *router.Router, seq *sequence.SequenceManager) (Plan, error) {
	switch s := stmt.(type) {
	case *ast.SelectStmt:
		plan := NewSelectPlan(db, sql, router)
		if err := HandleSelectStmt(plan, s); err != nil {
			return nil, err
		}
		return plan, nil
	case *ast.InsertStmt:
		plan := NewInsertPlan(db, sql, router, seq)
		if err := HandleInsertStmt(plan, s); err != nil {
			return nil, err
		}
		return plan, nil
	case *ast.UpdateStmt:
		plan := NewUpdatePlan(s, db, sql, router)
		if err := HandleUpdatePlan(plan); err != nil {
			return nil, err
		}
		return plan, nil
	case *ast.DeleteStmt:
		plan := NewDeletePlan(s, db, sql, router)
		if err := HandleDeletePlan(plan); err != nil {
			return nil, err
		}
		return plan, nil
	default:
		return nil, fmt.Errorf("stmt type does not support shard now")
	}
}