func (s *Server) onConn(c net.Conn) { conn := s.newClientConn(c) //新建一个conn defer func() { err := recover() if err != nil { const size = 4096 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] //获得当前goroutine的stacktrace golog.Error("server", "onConn", "error", 0, "remoteAddr", c.RemoteAddr().String(), "stack", string(buf), ) } conn.Close() }() if allowConnect := conn.IsAllowConnect(); allowConnect == false { err := mysql.NewError(mysql.ER_ACCESS_DENIED_ERROR, "ip address access denied by kingshard.") conn.writeError(err) conn.Close() return } if err := conn.Handshake(); err != nil { golog.Error("server", "onConn", err.Error(), 0) c.Close() return } conn.Run() }
func (c *ClientConn) handlePrepareSelect(stmt *sqlparser.Select, sql string, args []interface{}) error { defaultRule := c.schema.rule.DefaultRule if len(defaultRule.Nodes) == 0 { return errors.ErrNoDefaultNode } defaultNode := c.proxy.GetNode(c.user) //execute in Master DB conn, err := c.getBackendConn(defaultNode, false) if err != nil { return err } if conn == nil { r := c.newEmptyResultset(stmt) return c.writeResultset(c.status, r) } var rs []*mysql.Result rs, err = c.executeInNode(conn, sql, args) c.closeConn(conn, false) if err != nil { golog.Error("ClientConn", "handlePrepareSelect", err.Error(), c.connectionId) return err } err = c.mergeSelectResult(rs, stmt) if err != nil { golog.Error("ClientConn", "handlePrepareSelect", err.Error(), c.connectionId) } return err }
func (n *Node) checkMaster() { db := n.Master if db == nil { golog.Error("Node", "checkMaster", "Master is no alive", 0) return } if atomic.LoadInt32(&(db.state)) == Down { return } if err := db.Ping(); err != nil { golog.Error("Node", "checkMaster", "Ping", 0, "db.Addr", db.Addr(), "error", err.Error()) } else { n.LastMasterPing = time.Now().Unix() atomic.StoreInt32(&(db.state), Up) return } if int64(n.DownAfterNoAlive) > 0 && time.Now().Unix()-n.LastMasterPing > int64(n.DownAfterNoAlive/time.Second) { golog.Info("Node", "checkMaster", "Master down", 0, "db.Addr", db.Addr(), "Master_down_time", int64(n.DownAfterNoAlive/time.Second)) n.DownMaster(db.addr) } }
func (r *Router) buildDeletePlan(statement sqlparser.Statement) (*Plan, error) { plan := &Plan{} var where *sqlparser.Where stmt := statement.(*sqlparser.Delete) plan.Rule = r.GetRule(sqlparser.String(stmt.Table)) where = stmt.Where if where != nil { plan.Criteria = where.Expr /*路由条件*/ } else { plan.Rule = r.DefaultRule } plan.TableIndexs = makeList(0, len(plan.Rule.TableToNode)) err := plan.calRouteIndexs() if err != nil { golog.Error("Route", "BuildDeletePlan", err.Error(), 0) return nil, err } if plan.Rule.Type != DefaultRuleType && len(plan.RouteTableIndexs) == 0 { golog.Error("Route", "BuildDeletePlan", errors.ErrNoCriteria.Error(), 0) return nil, errors.ErrNoCriteria } //generate sql,如果routeTableindexs为空则表示不分表,不分表则发default node err = r.generateDeleteSql(plan, stmt) if err != nil { return nil, err } return plan, nil }
func (c *ClientConn) Handshake() error { if err := c.writeInitialHandshake(); err != nil { golog.Error("server", "Handshake", err.Error(), c.connectionId, "msg", "send initial handshake error") return err } if err := c.readHandshakeResponse(); err != nil { golog.Error("server", "readHandshakeResponse", err.Error(), c.connectionId, "msg", "read Handshake Response error") c.writeError(err) return err } if err := c.writeOK(nil); err != nil { golog.Error("server", "readHandshakeResponse", "write ok fail", c.connectionId, "error", err.Error()) return err } c.pkg.Sequence = 0 return nil }
/*计算表下标和node下标 */ func (plan *Plan) calRouteIndexs() error { nodesCount := len(plan.Rule.Nodes) if plan.Rule.Type == DefaultRuleType { plan.RouteNodeIndexs = []int{0} return nil } if plan.Criteria == nil { //如果没有分表条件,则是全子表扫描 if plan.Rule.Type != DefaultRuleType { golog.Error("Plan", "calRouteIndexs", "plan have no criteria", 0, "type", plan.Rule.Type) return errors.ErrNoCriteria } } switch criteria := plan.Criteria.(type) { case sqlparser.Values: //代表insert中values tindex := plan.getInsertTableIndex(criteria) plan.RouteTableIndexs = []int{tindex} plan.RouteNodeIndexs = plan.TindexsToNindexs([]int{tindex}) return nil case sqlparser.BoolExpr: plan.RouteTableIndexs = plan.getTableIndexByBoolExpr(criteria) plan.RouteNodeIndexs = plan.TindexsToNindexs(plan.RouteTableIndexs) return nil default: plan.RouteTableIndexs = plan.TableIndexs plan.RouteNodeIndexs = makeList(0, nodesCount) return nil } }
func (r *Router) buildReplacePlan(statement sqlparser.Statement) (*Plan, error) { plan := &Plan{} stmt := statement.(*sqlparser.Replace) if _, ok := stmt.Rows.(sqlparser.SelectStatement); ok { panic(sqlparser.NewParserError("select in replace not allowed")) } plan.Rule = r.GetRule(sqlparser.String(stmt.Table)) plan.Criteria = plan.checkValuesType(stmt.Rows.(sqlparser.Values)) plan.TableIndexs = makeList(0, len(plan.Rule.TableToNode)) err := plan.calRouteIndexs() if err != nil { golog.Error("Route", "BuildReplacePlan", err.Error(), 0) return nil, err } err = r.generateReplaceSql(plan, stmt) if err != nil { return nil, err } return plan, nil }
func (c *ClientConn) handleExec(stmt sqlparser.Statement, args []interface{}) error { plan, err := c.schema.rule.BuildPlan(stmt) conns, err := c.getShardConns(false, plan) if err != nil { golog.Error("ClientConn", "handleExec", err.Error(), c.connectionId) return err } if conns == nil { return c.writeOK(nil) } var rs []*mysql.Result if 1 < len(conns) { return errors.ErrExecInMulti } if 1 < len(plan.RewrittenSqls) { nodeIndex := plan.RouteNodeIndexs[0] nodeName := plan.Rule.Nodes[nodeIndex] txSqls := []string{"begin;"} txSqls = append(txSqls, plan.RewrittenSqls[nodeName]...) txSqls = append(txSqls, "commit;") plan.RewrittenSqls[nodeName] = txSqls } rs, err = c.executeInMultiNodes(conns, plan.RewrittenSqls, args) c.closeShardConns(conns, err != nil) if err == nil { err = c.mergeExecResult(rs) } return err }
func (c *ClientConn) handleAdmin(admin *sqlparser.Admin) error { var err error var result *mysql.Resultset region := sqlparser.String(admin.Region) err = c.checkCmdOrder(region, admin.Columns) if err != nil { return err } switch strings.ToLower(region) { case NodeRegion: err = c.handleNodeCmd(admin.Rows) case ServerRegion: result, err = c.handleServerCmd(admin.Rows) default: return fmt.Errorf("admin %s not supported now", region) } if err != nil { golog.Error("ClientConn", "handleAdmin", err.Error(), c.connectionId, "sql", sqlparser.String(admin)) return err } if result != nil { return c.writeResultset(c.status, result) } return c.writeOK(nil) }
func (n *Node) checkSlave() { n.Lock() if n.Slave == nil { n.Unlock() return } slaves := make([]*DB, len(n.Slave)) copy(slaves, n.Slave) n.Unlock() for i := 0; i < len(slaves); i++ { if atomic.LoadInt32(&(slaves[i].state)) == Down { continue } if err := slaves[i].Ping(); err != nil { golog.Error("Node", "checkSlave", "Ping", 0, "db.Addr", slaves[i].Addr(), "error", err.Error()) } else { n.LastSlavePing = time.Now().Unix() atomic.StoreInt32(&(slaves[i].state), Up) continue } if int64(n.DownAfterNoAlive) > 0 && time.Now().Unix()-n.LastSlavePing > int64(n.DownAfterNoAlive/time.Second) { golog.Info("Node", "checkMaster", "Master down", 0, "db.Addr", slaves[i].Addr(), "slave_down_time", int64(n.DownAfterNoAlive/time.Second)) //If can't ping slave after DownAfterNoAlive, set slave Down n.DownSlave(slaves[i].addr) } } }
func (r *Router) buildInsertPlan(statement sqlparser.Statement) (*Plan, error) { plan := &Plan{} stmt := statement.(*sqlparser.Insert) if _, ok := stmt.Rows.(sqlparser.SelectStatement); ok { return nil, errors.ErrSelectInInsert } /*根据sql语句的表,获得对应的分片规则*/ plan.Rule = r.GetRule(sqlparser.String(stmt.Table)) if stmt.OnDup != nil { err := plan.Rule.checkUpdateExprs(sqlparser.UpdateExprs(stmt.OnDup)) if err != nil { return nil, err } } plan.Criteria = plan.checkValuesType(stmt.Rows.(sqlparser.Values)) plan.TableIndexs = makeList(0, len(plan.Rule.TableToNode)) err := plan.calRouteIndexs() if err != nil { golog.Error("Route", "BuildInsertPlan", err.Error(), 0) return nil, err } err = r.generateInsertSql(plan, stmt) if err != nil { return nil, err } return plan, nil }
func (n *Node) UpMaster(addr string) error { db, err := n.checkUpDB(addr) if err != nil { golog.Error("Node", "UpMaster", err.Error(), 0) } n.Master = db return err }
func (c *ClientConn) readHandshakeResponse() error { data, err := c.readPacket() if err != nil { return err } pos := 0 //capability c.capability = binary.LittleEndian.Uint32(data[:4]) pos += 4 //skip max packet size pos += 4 //charset, skip, if you want to use another charset, use set names //c.collation = CollationId(data[pos]) pos++ //skip reserved 23[00] pos += 23 //user name c.user = string(data[pos : pos+bytes.IndexByte(data[pos:], 0)]) pos += len(c.user) + 1 //auth length and auth authLen := int(data[pos]) pos++ auth := data[pos : pos+authLen] checkAuth := mysql.CalcPassword(c.salt, []byte(c.proxy.cfg.Password)) if !bytes.Equal(auth, checkAuth) { golog.Error("ClientConn", "readHandshakeResponse", "error", 0, "auth", auth, "checkAuth", checkAuth, "passworld", c.proxy.cfg.Password) return mysql.NewDefaultError(mysql.ER_ACCESS_DENIED_ERROR, c.c.RemoteAddr().String(), c.user, "Yes") } pos += authLen if c.capability&mysql.CLIENT_CONNECT_WITH_DB > 0 { if len(data[pos:]) == 0 { return nil } db := string(data[pos : pos+bytes.IndexByte(data[pos:], 0)]) pos += len(c.db) + 1 if err := c.useDB(db); err != nil { return err } } return nil }
//返回true表示已经处理,false表示未处理 func (c *ClientConn) preHandleShard(sql string) (bool, error) { var rs []*mysql.Result var err error var execNode *backend.Node var fromSlave bool = false if len(sql) == 0 { return false, errors.ErrCmdUnsupport } tokens := strings.Fields(sql) if len(tokens) == 0 { return false, errors.ErrCmdUnsupport } if c.isInTransaction() { execNode, err = c.GetTransExecNode(tokens, sql) } else { execNode, fromSlave, err = c.GetExecNode(tokens, sql) } if err != nil { return false, err } //need shard sql if execNode == nil { return false, nil } //execute in Master DB conn, err := c.getBackendConn(execNode, fromSlave) if err != nil { return false, err } rs, err = c.executeInNode(conn, sql, nil) if err != nil { return false, err } c.closeConn(conn, false) if len(rs) == 0 { msg := fmt.Sprintf("result is empty") golog.Error("ClientConn", "handleUnsupport", msg, c.connectionId) return false, mysql.NewError(mysql.ER_UNKNOWN_ERROR, msg) } if rs[0].Resultset != nil { err = c.writeResultset(c.status, rs[0].Resultset) } else { err = c.writeOK(rs[0]) } if err != nil { return false, err } return true, nil }
func (r *Router) buildSelectPlan(statement sqlparser.Statement) (*Plan, error) { plan := &Plan{} var where *sqlparser.Where var tableName string stmt := statement.(*sqlparser.Select) switch v := (stmt.From[0]).(type) { case *sqlparser.AliasedTableExpr: tableName = sqlparser.String(v.Expr) case *sqlparser.JoinTableExpr: if ate, ok := (v.LeftExpr).(*sqlparser.AliasedTableExpr); ok { tableName = sqlparser.String(ate.Expr) } else { tableName = sqlparser.String(v) } default: tableName = sqlparser.String(v) } plan.Rule = r.GetRule(tableName) //根据表名获得分表规则 where = stmt.Where if where != nil { plan.Criteria = where.Expr /*路由条件*/ } else { plan.Rule = r.DefaultRule } plan.TableIndexs = makeList(0, len(plan.Rule.TableToNode)) err := plan.calRouteIndexs() if err != nil { golog.Error("Route", "BuildSelectPlan", err.Error(), 0) return nil, err } if plan.Rule.Type != DefaultRuleType && len(plan.RouteTableIndexs) == 0 { golog.Error("Route", "BuildSelectPlan", errors.ErrNoCriteria.Error(), 0) return nil, errors.ErrNoCriteria } //generate sql,如果routeTableindexs为空则表示不分表,不分表则发default node err = r.generateSelectSql(plan, stmt) if err != nil { return nil, err } return plan, nil }
func (c *ClientConn) Run() { defer func() { r := recover() if err, ok := r.(error); ok { const size = 4096 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] golog.Error("ClientConn", "Run", err.Error(), 0, "stack", string(buf)) } c.Close() }() for { data, err := c.readPacket() if err != nil { return } if err := c.dispatch(data); err != nil { golog.Error("server", "Run", err.Error(), c.connectionId, ) if err != mysql.ErrBadConn { c.writeError(err) } } if c.closed { return } c.pkg.Sequence = 0 } }
/*处理select语句*/ func (c *ClientConn) handleSelect(stmt *sqlparser.Select, args []interface{}) error { var fromSlave bool = true plan, err := c.schema.rule.BuildPlan(stmt) if err != nil { return err } if 0 < len(stmt.Comments) { comment := string(stmt.Comments[0]) if 0 < len(comment) && strings.ToLower(comment) == MasterComment { fromSlave = false } } conns, err := c.getShardConns(fromSlave, plan) if err != nil { golog.Error("ClientConn", "handleSelect", err.Error(), c.connectionId) return err } if conns == nil { r := c.newEmptyResultset(stmt) return c.writeResultset(c.status, r) } var rs []*mysql.Result rs, err = c.executeInMultiNodes(conns, plan.RewrittenSqls, args) c.closeShardConns(conns, false) if err != nil { golog.Error("ClientConn", "handleSelect", err.Error(), c.connectionId) return err } err = c.mergeSelectResult(rs, stmt) if err != nil { golog.Error("ClientConn", "handleSelect", err.Error(), c.connectionId) } return err }
func (s *Server) Run() error { s.running = true for s.running { conn, err := s.listener.Accept() if err != nil { golog.Error("server", "Run", err.Error(), 0) continue } go s.onConn(conn) } return nil }
func (r *Router) generateUpdateSql(plan *Plan, stmt sqlparser.Statement) error { sqls := make(map[string][]string) node, ok := stmt.(*sqlparser.Update) if ok == false { return errors.ErrStmtConvert } if len(plan.RouteNodeIndexs) == 0 { return errors.ErrNoRouteNode } if len(plan.RouteTableIndexs) == 0 { buf := sqlparser.NewTrackedBuffer(nil) stmt.Format(buf) nodeName := r.Nodes[0] sqls[nodeName] = []string{buf.String()} } else { nodeCount := len(plan.RouteNodeIndexs) if 1 < nodeCount { golog.Error("Router", "generateUpdateSql", errors.ErrUpdateInMulti.Error(), 0, "RouteNodeIndexs", plan.RouteNodeIndexs) return errors.ErrUpdateInMulti } tableCount := len(plan.RouteTableIndexs) for i := 0; i < tableCount; i++ { buf := sqlparser.NewTrackedBuffer(nil) buf.Fprintf("update %v%v", node.Comments, node.Table, ) fmt.Fprintf(buf, "_%04d", plan.RouteTableIndexs[i]) buf.Fprintf(" set %v%v%v%v", node.Exprs, node.Where, node.OrderBy, node.Limit, ) tableIndex := plan.RouteTableIndexs[i] nodeIndex := plan.Rule.TableToNode[tableIndex] nodeName := r.Nodes[nodeIndex] if _, ok := sqls[nodeName]; ok == false { sqls[nodeName] = make([]string, 0, tableCount) } sqls[nodeName] = append(sqls[nodeName], buf.String()) } } plan.RewrittenSqls = sqls return nil }
func (c *ClientConn) getBackendConn(n *backend.Node, fromSlave bool) (co *backend.BackendConn, err error) { if !c.isInTransaction() { if fromSlave { co, err = n.GetSlaveConn() if err != nil { co, err = n.GetMasterConn() } } else { co, err = n.GetMasterConn() } if err != nil { golog.Error("server", "getBackendConn", err.Error(), 0) return } } else { var ok bool c.Lock() co, ok = c.txConns[n] c.Unlock() if !ok { if co, err = n.GetMasterConn(); err != nil { return } if err = co.Begin(); err != nil { return } c.Lock() c.txConns[n] = co c.Unlock() } } //todo, set conn charset, etc... if err = co.UseDB(c.db); err != nil { return } if err = co.SetCharset(c.charset); err != nil { return } return }
func (c *ClientConn) handleSet(stmt *sqlparser.Set, sql string) error { if len(stmt.Exprs) != 1 { return fmt.Errorf("must set one item once, not %s", nstring(stmt)) } k := string(stmt.Exprs[0].Name.Name) switch strings.ToUpper(k) { case `AUTOCOMMIT`: return c.handleSetAutoCommit(stmt.Exprs[0].Expr) case `NAMES`, `CHARACTER_SET_RESULTS`, `CHARACTER_SET_CLIENT`, `CHARACTER_SET_CONNECTION`: return c.handleSetNames(stmt.Exprs[0].Expr) default: golog.Error("ClientConn", "handleSet", "command not supported", c.connectionId, "sql", sql) return c.writeOK(nil) } }
func (n *Node) UpSlave(addr string) error { db, err := n.checkUpDB(addr) if err != nil { golog.Error("Node", "UpSlave", err.Error(), 0) } n.Lock() for k, slave := range n.Slave { if slave.addr == addr { n.Slave[k] = db n.Unlock() return nil } } n.Slave = append(n.Slave, db) n.Unlock() return err }
func (c *ClientConn) IsAllowConnect() bool { clientHost, _, err := net.SplitHostPort(c.c.RemoteAddr().String()) if err != nil { fmt.Println(err) } clientIP := net.ParseIP(clientHost) ipVec := c.proxy.allowips if ipVecLen := len(ipVec); ipVecLen == 0 { return true } for _, ip := range ipVec { if ip.Equal(clientIP) { return true } } golog.Error("server", "IsAllowConnect", "error", mysql.ER_ACCESS_DENIED_ERROR, "ip address", c.c.RemoteAddr().String(), " access denied by kindshard.") return false }
func (c *ClientConn) handleServerCmd(rows sqlparser.InsertRows) (*mysql.Resultset, error) { var err error var result *mysql.Resultset var opt, k, v string vals := rows.(sqlparser.Values) if len(vals) == 0 { return nil, errors.ErrCmdUnsupport } tuple := vals[0].(sqlparser.ValTuple) if len(tuple) != len(cmdServerOrder) { return nil, errors.ErrCmdUnsupport } opt = sqlparser.String(tuple[0]) opt = strings.Trim(opt, "'") k = sqlparser.String(tuple[1]) k = strings.Trim(k, "'") v = sqlparser.String(tuple[2]) v = strings.Trim(v, "'") switch strings.ToLower(opt) { case ADMIN_OPT_SHOW: result, err = c.handleAdminShow(k, v) default: err = errors.ErrCmdUnsupport golog.Error("ClientConn", "handleNodeCmd", err.Error(), c.connectionId, "opt", opt) } if err != nil { return nil, err } return result, nil }
/*处理query语句*/ func (c *ClientConn) handleQuery(sql string) (err error) { defer func() { if e := recover(); e != nil { golog.OutputSql("Error", "err:%v,sql:%s", e, sql) if err, ok := e.(error); ok { const size = 4096 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] golog.Error("ClientConn", "handleQuery", err.Error(), 0, "stack", string(buf), "sql", sql) } return } }() sql = strings.TrimRight(sql, ";") //删除sql语句最后的分号 hasHandled, err := c.preHandleShard(sql) if err != nil { golog.Error("server", "preHandleShard", err.Error(), 0, "hasHandled", hasHandled) return err } if hasHandled { return nil } var stmt sqlparser.Statement stmt, err = sqlparser.Parse(sql) //解析sql语句,得到的stmt是一个interface if err != nil { golog.Error("server", "parse", err.Error(), 0, "hasHandled", hasHandled, "sql", sql) return err } switch v := stmt.(type) { case *sqlparser.Select: return c.handleSelect(v, nil) case *sqlparser.Insert: return c.handleExec(stmt, nil) case *sqlparser.Update: return c.handleExec(stmt, nil) case *sqlparser.Delete: return c.handleExec(stmt, nil) case *sqlparser.Replace: return c.handleExec(stmt, nil) case *sqlparser.Set: return c.handleSet(v, sql) case *sqlparser.Begin: return c.handleBegin() case *sqlparser.Commit: return c.handleCommit() case *sqlparser.Rollback: return c.handleRollback() case *sqlparser.Admin: return c.handleAdmin(v) case *sqlparser.UseDB: return c.handleUseDB(v) default: return fmt.Errorf("statement %T not support now", stmt) } return nil }
func main() { fmt.Print(banner) runtime.GOMAXPROCS(runtime.NumCPU()) flag.Parse() if len(*configFile) == 0 { fmt.Println("must use a config file") return } cfg, err := config.ParseConfigFile(*configFile) if err != nil { fmt.Printf("parse config file error:%v\n", err.Error()) return } //when the log file size greater than 1GB, kingshard will generate a new file if len(cfg.LogPath) != 0 { sysFilePath := path.Join(cfg.LogPath, sysLogName) sysFile, err := golog.NewRotatingFileHandler(sysFilePath, MaxLogSize, 1) if err != nil { fmt.Printf("new log file error:%v\n", err.Error()) return } golog.GlobalSysLogger = golog.New(sysFile, golog.Lfile|golog.Ltime|golog.Llevel) sqlFilePath := path.Join(cfg.LogPath, sqlLogName) sqlFile, err := golog.NewRotatingFileHandler(sqlFilePath, MaxLogSize, 1) if err != nil { fmt.Printf("new log file error:%v\n", err.Error()) return } golog.GlobalSqlLogger = golog.New(sqlFile, golog.Lfile|golog.Ltime|golog.Llevel) } if *logLevel != "" { setLogLevel(*logLevel) } else { setLogLevel(cfg.LogLevel) } var svr *server.Server svr, err = server.NewServer(cfg) if err != nil { golog.Error("main", "main", err.Error(), 0) golog.GlobalSysLogger.Close() golog.GlobalSqlLogger.Close() return } sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) go func() { sig := <-sc golog.Info("main", "main", "Got signal", 0, "signal", sig) golog.GlobalSysLogger.Close() golog.GlobalSqlLogger.Close() svr.Close() }() svr.Run() }
func (c *ClientConn) handleNodeCmd(rows sqlparser.InsertRows) error { var err error var opt, nodeName, role, addr string vals := rows.(sqlparser.Values) if len(vals) == 0 { return errors.ErrCmdUnsupport } tuple := vals[0].(sqlparser.ValTuple) if len(tuple) != len(cmdNodeOrder) { return errors.ErrCmdUnsupport } opt = sqlparser.String(tuple[0]) opt = strings.Trim(opt, "'") nodeName = sqlparser.String(tuple[1]) nodeName = strings.Trim(nodeName, "'") if nodeName != c.user { nodeName = c.user } role = sqlparser.String(tuple[2]) role = strings.Trim(role, "'") addr = sqlparser.String(tuple[3]) addr = strings.Trim(addr, "'") switch strings.ToLower(opt) { case ADMIN_OPT_ADD: err = c.AddDatabase( nodeName, role, addr, ) case ADMIN_OPT_DEL: err = c.DeleteDatabase( nodeName, role, addr, ) case ADMIN_OPT_UP: err = c.UpDatabase( nodeName, role, addr, ) case ADMIN_OPT_DOWN: err = c.DownDatabase( nodeName, role, addr, ) default: err = errors.ErrCmdUnsupport golog.Error("ClientConn", "handleNodeCmd", err.Error(), c.connectionId, "opt", opt) } return err }
func (c *ClientConn) executeInMultiNodes(conns map[string]*backend.BackendConn, sqls map[string][]string, args []interface{}) ([]*mysql.Result, error) { if len(conns) != len(sqls) { golog.Error("ClientConn", "executeInMultiNodes", errors.ErrConnNotEqual.Error(), c.connectionId, "conns", conns, "sqls", sqls, ) return nil, errors.ErrConnNotEqual } var wg sync.WaitGroup if len(conns) == 0 { return nil, errors.ErrNoPlan } wg.Add(len(conns)) resultCount := 0 for _, sqlSlice := range sqls { resultCount += len(sqlSlice) } rs := make([]interface{}, resultCount) f := func(rs []interface{}, i int, execSqls []string, co *backend.BackendConn) { var state string for _, v := range execSqls { r, err := co.Execute(v, args...) if err != nil { state = "ERROR" rs[i] = err } else { state = "INFO" rs[i] = r } if c.proxy.cfg.LogSql != golog.LogSqlOff { golog.OutputSql(state, "%s->%s:%s", c.c.RemoteAddr(), co.GetAddr(), v, ) } i++ } wg.Done() } offsert := 0 for nodeName, co := range conns { s := sqls[nodeName] //[]string go f(rs, offsert, s, co) offsert += len(s) } wg.Wait() var err error r := make([]*mysql.Result, resultCount) for i, v := range rs { if e, ok := v.(error); ok { err = e break } r[i] = rs[i].(*mysql.Result) } return r, err }