func (cc *clientConn) writeError(e error) error { var ( m *mysql.SQLError te *terror.Error ok bool ) originErr := errors.Cause(e) if te, ok = originErr.(*terror.Error); ok { m = te.ToSQLError() } else { m = mysql.NewErrf(mysql.ErrUnknown, e.Error()) } data := cc.alloc.AllocWithLen(4, 16+len(m.Message)) data = append(data, mysql.ErrHeader) data = append(data, byte(m.Code), byte(m.Code>>8)) if cc.capability&mysql.ClientProtocol41 > 0 { data = append(data, '#') data = append(data, m.State...) } data = append(data, m.Message...) err := cc.writePacket(data) if err != nil { return errors.Trace(err) } return errors.Trace(cc.flush()) }
// dispatch handles client request based on command which is the first byte of the data. // It also gets a token from server which is used to limit the concurrently handling clients. // The most frequently used command is ComQuery. func (cc *clientConn) dispatch(data []byte) error { cmd := data[0] data = data[1:] cc.lastCmd = hack.String(data) token := cc.server.getToken() startTS := time.Now() defer func() { cc.server.releaseToken(token) log.Debugf("[TIME_CMD] %v %d", time.Since(startTS), cmd) }() switch cmd { case mysql.ComSleep: // TODO: According to mysql document, this command is supposed to be used only internally. // So it's just a temp fix, not sure if it's done right. // Investigate this command and write test case later. return nil case mysql.ComQuit: return io.EOF case mysql.ComQuery: // Most frequently used command. // For issue 1989 // Input payload may end with byte '\0', we didn't find related mysql document about it, but mysql // implementation accept that case. So trim the last '\0' here as if the payload an EOF string. // See http://dev.mysql.com/doc/internals/en/com-query.html if data[len(data)-1] == 0 { data = data[:len(data)-1] } return cc.handleQuery(hack.String(data)) case mysql.ComPing: return cc.writeOK() case mysql.ComInitDB: log.Debug("init db", hack.String(data)) if err := cc.useDB(hack.String(data)); err != nil { return errors.Trace(err) } return cc.writeOK() case mysql.ComFieldList: return cc.handleFieldList(hack.String(data)) case mysql.ComStmtPrepare: return cc.handleStmtPrepare(hack.String(data)) case mysql.ComStmtExecute: return cc.handleStmtExecute(data) case mysql.ComStmtClose: return cc.handleStmtClose(data) case mysql.ComStmtSendLongData: return cc.handleStmtSendLongData(data) case mysql.ComStmtReset: return cc.handleStmtReset(data) case mysql.ComSetOption: return cc.handleSetOption(data) default: return mysql.NewErrf(mysql.ErrUnknown, "command %d not supported now", cmd) } }
// dispatch handles client request based on command which is the first byte of the data. // It also gets a token from server which is used to limit the concurrently handling clients. // The most frequently used command is ComQuery. func (cc *clientConn) dispatch(data []byte) error { cmd := data[0] data = data[1:] cc.lastCmd = hack.String(data) token := cc.server.getToken() startTS := time.Now() defer func() { cc.server.releaseToken(token) log.Debugf("[TIME_CMD] %v %d", time.Since(startTS), cmd) }() switch cmd { case mysql.ComSleep: // TODO: According to mysql document, this command is supposed to be used only internally. // So it's just a temp fix, not sure if it's done right. // Investigate this command and write test case later. return nil case mysql.ComQuit: return io.EOF case mysql.ComQuery: // Most frequently used command. return cc.handleQuery(hack.String(data)) case mysql.ComPing: return cc.writeOK() case mysql.ComInitDB: log.Debug("init db", hack.String(data)) if err := cc.useDB(hack.String(data)); err != nil { return errors.Trace(err) } return cc.writeOK() case mysql.ComFieldList: return cc.handleFieldList(hack.String(data)) case mysql.ComStmtPrepare: return cc.handleStmtPrepare(hack.String(data)) case mysql.ComStmtExecute: return cc.handleStmtExecute(data) case mysql.ComStmtClose: return cc.handleStmtClose(data) case mysql.ComStmtSendLongData: return cc.handleStmtSendLongData(data) case mysql.ComStmtReset: return cc.handleStmtReset(data) case mysql.ComSetOption: return cc.handleSetOption(data) default: return mysql.NewErrf(mysql.ErrUnknown, "command %d not supported now", cmd) } }
func (cc *clientConn) dispatch(data []byte) error { cmd := data[0] data = data[1:] cc.lastCmd = hack.String(data) token := cc.server.getToken() defer func() { cc.server.releaseToken(token) }() switch cmd { case mysql.ComQuit: return io.EOF case mysql.ComQuery: return cc.handleQuery(hack.String(data)) case mysql.ComPing: return cc.writeOK() case mysql.ComInitDB: log.Debug("init db", hack.String(data)) if err := cc.useDB(hack.String(data)); err != nil { return errors.Trace(err) } return cc.writeOK() case mysql.ComFieldList: return cc.handleFieldList(hack.String(data)) case mysql.ComStmtPrepare: return cc.handleStmtPrepare(hack.String(data)) case mysql.ComStmtExecute: return cc.handleStmtExecute(data) case mysql.ComStmtClose: return cc.handleStmtClose(data) case mysql.ComStmtSendLongData: return cc.handleStmtSendLongData(data) case mysql.ComStmtReset: return cc.handleStmtReset(data) default: return mysql.NewErrf(mysql.ErrUnknown, "command %d not supported now", cmd) } }
func (cc *clientConn) writeError(e error) error { var m *mysql.SQLError var ok bool originErr := errors.Cause(e) if m, ok = originErr.(*mysql.SQLError); !ok { m = mysql.NewErrf(mysql.ErrUnknown, e.Error()) } data := make([]byte, 4, 16+len(m.Message)) data = append(data, mysql.ErrHeader) data = append(data, byte(m.Code), byte(m.Code>>8)) if cc.capability&mysql.ClientProtocol41 > 0 { data = append(data, '#') data = append(data, m.State...) } data = append(data, m.Message...) err := cc.writePacket(data) if err != nil { return errors.Trace(err) } return errors.Trace(cc.flush()) }
func (cc *clientConn) handleStmtExecute(data []byte) (err error) { if len(data) < 9 { return mysql.ErrMalformPacket } pos := 0 stmtID := binary.LittleEndian.Uint32(data[0:4]) pos += 4 stmt := cc.ctx.GetStatement(int(stmtID)) if stmt == nil { return mysql.NewErr(mysql.ErrUnknownStmtHandler, strconv.FormatUint(uint64(stmtID), 10), "stmt_execute") } flag := data[pos] pos++ //now we only support CURSOR_TYPE_NO_CURSOR flag if flag != 0 { return mysql.NewErrf(mysql.ErrUnknown, "unsupported flag %d", flag) } //skip iteration-count, always 1 pos += 4 var ( nullBitmaps []byte paramTypes []byte paramValues []byte ) numParams := stmt.NumParams() args := make([]interface{}, numParams) if numParams > 0 { nullBitmapLen := (numParams + 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 + (numParams << 1)) { return mysql.ErrMalformPacket } paramTypes = data[pos : pos+(numParams<<1)] pos += (numParams << 1) paramValues = data[pos:] } err = parseStmtArgs(args, stmt.BoundParams(), nullBitmaps, paramTypes, paramValues) if err != nil { return errors.Trace(err) } } rs, err := stmt.Execute(args...) if err != nil { return errors.Trace(err) } if rs == nil { return errors.Trace(cc.writeOK()) } return errors.Trace(cc.writeResultset(rs, true)) }
// ToSQLError convert Error to mysql.SQLError. func (e *Error) ToSQLError() *mysql.SQLError { code := e.getMySQLErrorCode() return mysql.NewErrf(code, e.getMsg()) }