func (c *ClientConn) handleStmtSendLongData(data []byte) error { if len(data) < 6 { return mysql.ErrMalformPacket } id := binary.LittleEndian.Uint32(data[0:4]) s, ok := c.stmts[id] if !ok { return mysql.NewDefaultError(mysql.ER_UNKNOWN_STMT_HANDLER, strconv.FormatUint(uint64(id), 10), "stmt_send_longdata") } paramId := binary.LittleEndian.Uint16(data[4:6]) if paramId >= uint16(s.params) { return mysql.NewDefaultError(mysql.ER_WRONG_ARGUMENTS, "stmt_send_longdata") } if s.args[paramId] == nil { s.args[paramId] = data[6:] } else { if b, ok := s.args[paramId].([]byte); ok { b = append(b, data[6:]...) s.args[paramId] = b } else { return fmt.Errorf("invalid param long data type %T", s.args[paramId]) } } return nil }
func (c *ClientConn) handleFieldList(data []byte) error { index := bytes.IndexByte(data, 0x00) table := string(data[0:index]) wildcard := string(data[index+1:]) if c.schema == nil { return mysql.NewDefaultError(mysql.ER_NO_DB_ERROR) } nodeName := c.schema.rule.GetRule(table).Nodes[0] n := c.proxy.GetNode(nodeName) co, err := n.GetMasterConn() defer c.closeConn(co, false) if err != nil { return err } if err = co.UseDB(c.db); err != nil { return err } if fs, err := co.FieldList(table, wildcard); err != nil { return err } else { return c.writeFieldList(c.status, fs) } }
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 c.user != c.proxy.cfg.User || !bytes.Equal(auth, checkAuth) { golog.Error("ClientConn", "readHandshakeResponse", "error", 0, "auth", auth, "checkAuth", checkAuth, "client_user", c.user, "config_set_user", c.proxy.cfg.User, "passworld", c.proxy.cfg.Password) return mysql.NewDefaultError(mysql.ER_ACCESS_DENIED_ERROR, c.user, c.c.RemoteAddr().String(), "Yes") } pos += authLen var db string 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 } c.db = db return nil }
func (c *ClientConn) handleUseDB(dbName string) error { var co *backend.BackendConn var err error if len(dbName) == 0 { return fmt.Errorf("must have database, the length of dbName is zero") } if c.schema == nil { return mysql.NewDefaultError(mysql.ER_NO_DB_ERROR) } nodeName := c.schema.rule.DefaultRule.Nodes[0] n := c.proxy.GetNode(nodeName) //get the connection from slave preferentially co, err = n.GetSlaveConn() if err != nil { co, err = n.GetMasterConn() } defer c.closeConn(co, false) if err != nil { return err } if err = co.UseDB(dbName); err != nil { return err } c.db = dbName return c.writeOK(nil) }
func (c *ClientConn) handleStmtPrepare(sql string) error { if c.schema == nil { return mysql.NewDefaultError(mysql.ER_NO_DB_ERROR) } s := new(Stmt) sql = strings.TrimRight(sql, ";") var err error s.s, err = sqlparser.Parse(sql) if err != nil { return fmt.Errorf(`parse sql "%s" error`, sql) } s.sql = sql defaultRule := c.schema.rule.DefaultRule n := c.proxy.GetNode(defaultRule.Nodes[0]) co, err := n.GetMasterConn() defer c.closeConn(co, false) if err != nil { return fmt.Errorf("prepare error %s", err) } err = co.UseDB(c.db) if err != nil { //reset the database to null c.db = "" return fmt.Errorf("prepare error %s", err) } t, err := co.Prepare(sql) if err != nil { return fmt.Errorf("prepare error %s", err) } s.params = t.ParamNum() s.columns = t.ColumnNum() s.id = c.stmtId c.stmtId++ if err = c.writePrepare(s); err != nil { return err } s.ResetParams() c.stmts[s.id] = s err = co.ClosePrepare(t.GetId()) if err != nil { return err } return nil }
func (c *ClientConn) handleShowTables(sql string, stmt *sqlparser.Show) (*mysql.Resultset, error) { s := c.schema if stmt.From != nil { db := nstring(stmt.From) s = c.proxy.GetSchema(db) } if s == nil { return nil, mysql.NewDefaultError(mysql.ER_NO_DB_ERROR) } var tables []string tmap := map[string]struct{}{} for _, n := range s.nodes { co, err := n.GetMasterConn() if err != nil { return nil, err } if err := co.UseDB(s.db); err != nil { co.Close() return nil, err } if r, err := co.Execute(sql); err != nil { co.Close() return nil, err } else { co.Close() for i := 0; i < r.RowNumber(); i++ { n, _ := r.GetString(i, 0) if _, ok := tmap[n]; !ok { tables = append(tables, n) } } } } sort.Strings(tables) values := make([]interface{}, len(tables)) for i := range tables { values[i] = tables[i] } return c.buildSimpleShowResultset(values, fmt.Sprintf("Tables_in_%s", s.db)) }
func (c *ClientConn) handleStmtReset(data []byte) error { if len(data) < 4 { return mysql.ErrMalformPacket } id := binary.LittleEndian.Uint32(data[0:4]) s, ok := c.stmts[id] if !ok { return mysql.NewDefaultError(mysql.ER_UNKNOWN_STMT_HANDLER, strconv.FormatUint(uint64(id), 10), "stmt_reset") } s.ResetParams() return c.writeOK(nil) }
func (c *ClientConn) useDB(db string) error { if c.schema == nil { return mysql.NewDefaultError(mysql.ER_NO_DB_ERROR) } nodeName := c.schema.rule.DefaultRule.Nodes[0] n := c.proxy.GetNode(nodeName) co, err := n.GetMasterConn() defer c.closeConn(co, false) if err != nil { return err } if err = co.UseDB(db); err != nil { return err } c.db = db return nil }
func (c *ClientConn) handleUseDB(stmt *sqlparser.UseDB) error { if len(stmt.DB) == 0 { return fmt.Errorf("must have database, not %s", sqlparser.String(stmt)) } if c.schema == nil { return mysql.NewDefaultError(mysql.ER_NO_DB_ERROR) } nodeName := c.schema.rule.DefaultRule.Nodes[0] n := c.proxy.GetNode(nodeName) co, err := n.GetMasterConn() defer c.closeConn(co, false) if err != nil { return err } if err = co.UseDB(string(stmt.DB)); err != nil { return err } c.db = string(stmt.DB) return c.writeOK(nil) }
func (c *ClientConn) handleStmtExecute(data []byte) error { if len(data) < 9 { return mysql.ErrMalformPacket } pos := 0 id := binary.LittleEndian.Uint32(data[0:4]) pos += 4 s, ok := c.stmts[id] if !ok { return mysql.NewDefaultError(mysql.ER_UNKNOWN_STMT_HANDLER, strconv.FormatUint(uint64(id), 10), "stmt_execute") } flag := data[pos] pos++ //now we only support CURSOR_TYPE_NO_CURSOR flag if flag != 0 { return mysql.NewError(mysql.ER_UNKNOWN_ERROR, fmt.Sprintf("unsupported flag %d", flag)) } //skip iteration-count, always 1 pos += 4 var nullBitmaps []byte var paramTypes []byte var paramValues []byte paramNum := s.params if paramNum > 0 { nullBitmapLen := (s.params + 7) >> 3 if len(data) < (pos + nullBitmapLen + 1) { return mysql.ErrMalformPacket } nullBitmaps = data[pos : pos+nullBitmapLen] pos += nullBitmapLen //new param bound flag if data[pos] == 1 { pos++ if len(data) < (pos + (paramNum << 1)) { return mysql.ErrMalformPacket } paramTypes = data[pos : pos+(paramNum<<1)] pos += (paramNum << 1) paramValues = data[pos:] } if err := c.bindStmtArgs(s, nullBitmaps, paramTypes, paramValues); err != nil { return err } } var err error switch stmt := s.s.(type) { case *sqlparser.Select: err = c.handlePrepareSelect(stmt, s.sql, s.args) case *sqlparser.Insert: err = c.handlePrepareExec(s.s, s.sql, s.args) case *sqlparser.Update: err = c.handlePrepareExec(s.s, s.sql, s.args) case *sqlparser.Delete: err = c.handlePrepareExec(s.s, s.sql, s.args) case *sqlparser.Replace: err = c.handlePrepareExec(s.s, s.sql, s.args) default: err = fmt.Errorf("command %T not supported now", stmt) } s.ResetParams() return err }