forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/go-sql-driver/mysql/packets.go

2272 lines
33 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// This Source Code Form is subject to the terms of the Mozilla Public
2024-02-18 10:42:21 +00:00
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
2024-02-18 10:42:21 +00:00
// You can obtain one at http://mozilla.org/MPL/2.0/.
package mysql
import (
"bytes"
"crypto/tls"
"database/sql/driver"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"math"
"time"
)
// Packets documentation:
2024-02-18 10:42:21 +00:00
// http://dev.mysql.com/doc/internals/en/client-server-protocol.html
// Read packet to buffer 'data'
2024-02-18 10:42:21 +00:00
func (mc *mysqlConn) readPacket() ([]byte, error) {
2024-02-18 10:42:21 +00:00
var prevData []byte
2024-02-18 10:42:21 +00:00
for {
2024-02-18 10:42:21 +00:00
// read packet header
2024-02-18 10:42:21 +00:00
data, err := mc.buf.readNext(4)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
if cerr := mc.canceled.Value(); cerr != nil {
2024-02-18 10:42:21 +00:00
return nil, cerr
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
errLog.Print(err)
2024-02-18 10:42:21 +00:00
mc.Close()
2024-02-18 10:42:21 +00:00
return nil, ErrInvalidConn
2024-02-18 10:42:21 +00:00
}
// packet length [24 bit]
2024-02-18 10:42:21 +00:00
pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16)
// check packet sync [8 bit]
2024-02-18 10:42:21 +00:00
if data[3] != mc.sequence {
2024-02-18 10:42:21 +00:00
if data[3] > mc.sequence {
2024-02-18 10:42:21 +00:00
return nil, ErrPktSyncMul
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return nil, ErrPktSync
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
mc.sequence++
// packets with length 0 terminate a previous packet which is a
2024-02-18 10:42:21 +00:00
// multiple of (2^24)-1 bytes long
2024-02-18 10:42:21 +00:00
if pktLen == 0 {
2024-02-18 10:42:21 +00:00
// there was no previous packet
2024-02-18 10:42:21 +00:00
if prevData == nil {
2024-02-18 10:42:21 +00:00
errLog.Print(ErrMalformPkt)
2024-02-18 10:42:21 +00:00
mc.Close()
2024-02-18 10:42:21 +00:00
return nil, ErrInvalidConn
2024-02-18 10:42:21 +00:00
}
return prevData, nil
2024-02-18 10:42:21 +00:00
}
// read packet body [pktLen bytes]
2024-02-18 10:42:21 +00:00
data, err = mc.buf.readNext(pktLen)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
if cerr := mc.canceled.Value(); cerr != nil {
2024-02-18 10:42:21 +00:00
return nil, cerr
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
errLog.Print(err)
2024-02-18 10:42:21 +00:00
mc.Close()
2024-02-18 10:42:21 +00:00
return nil, ErrInvalidConn
2024-02-18 10:42:21 +00:00
}
// return data if this was the last packet
2024-02-18 10:42:21 +00:00
if pktLen < maxPacketSize {
2024-02-18 10:42:21 +00:00
// zero allocations for non-split packets
2024-02-18 10:42:21 +00:00
if prevData == nil {
2024-02-18 10:42:21 +00:00
return data, nil
2024-02-18 10:42:21 +00:00
}
return append(prevData, data...), nil
2024-02-18 10:42:21 +00:00
}
prevData = append(prevData, data...)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// Write packet buffer 'data'
2024-02-18 10:42:21 +00:00
func (mc *mysqlConn) writePacket(data []byte) error {
2024-02-18 10:42:21 +00:00
pktLen := len(data) - 4
if pktLen > mc.maxAllowedPacket {
2024-02-18 10:42:21 +00:00
return ErrPktTooLarge
2024-02-18 10:42:21 +00:00
}
// Perform a stale connection check. We only perform this check for
2024-02-18 10:42:21 +00:00
// the first query on a connection that has been checked out of the
2024-02-18 10:42:21 +00:00
// connection pool: a fresh connection from the pool is more likely
2024-02-18 10:42:21 +00:00
// to be stale, and it has not performed any previous writes that
2024-02-18 10:42:21 +00:00
// could cause data corruption, so it's safe to return ErrBadConn
2024-02-18 10:42:21 +00:00
// if the check fails.
2024-02-18 10:42:21 +00:00
if mc.reset {
2024-02-18 10:42:21 +00:00
mc.reset = false
2024-02-18 10:42:21 +00:00
conn := mc.netConn
2024-02-18 10:42:21 +00:00
if mc.rawConn != nil {
2024-02-18 10:42:21 +00:00
conn = mc.rawConn
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
var err error
2024-02-18 10:42:21 +00:00
// If this connection has a ReadTimeout which we've been setting on
2024-02-18 10:42:21 +00:00
// reads, reset it to its default value before we attempt a non-blocking
2024-02-18 10:42:21 +00:00
// read, otherwise the scheduler will just time us out before we can read
2024-02-18 10:42:21 +00:00
if mc.cfg.ReadTimeout != 0 {
2024-02-18 10:42:21 +00:00
err = conn.SetReadDeadline(time.Time{})
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err == nil && mc.cfg.CheckConnLiveness {
2024-02-18 10:42:21 +00:00
err = connCheck(conn)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
errLog.Print("closing bad idle connection: ", err)
2024-02-18 10:42:21 +00:00
mc.Close()
2024-02-18 10:42:21 +00:00
return driver.ErrBadConn
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
for {
2024-02-18 10:42:21 +00:00
var size int
2024-02-18 10:42:21 +00:00
if pktLen >= maxPacketSize {
2024-02-18 10:42:21 +00:00
data[0] = 0xff
2024-02-18 10:42:21 +00:00
data[1] = 0xff
2024-02-18 10:42:21 +00:00
data[2] = 0xff
2024-02-18 10:42:21 +00:00
size = maxPacketSize
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
data[0] = byte(pktLen)
2024-02-18 10:42:21 +00:00
data[1] = byte(pktLen >> 8)
2024-02-18 10:42:21 +00:00
data[2] = byte(pktLen >> 16)
2024-02-18 10:42:21 +00:00
size = pktLen
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
data[3] = mc.sequence
// Write packet
2024-02-18 10:42:21 +00:00
if mc.writeTimeout > 0 {
2024-02-18 10:42:21 +00:00
if err := mc.netConn.SetWriteDeadline(time.Now().Add(mc.writeTimeout)); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
n, err := mc.netConn.Write(data[:4+size])
2024-02-18 10:42:21 +00:00
if err == nil && n == 4+size {
2024-02-18 10:42:21 +00:00
mc.sequence++
2024-02-18 10:42:21 +00:00
if size != maxPacketSize {
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pktLen -= size
2024-02-18 10:42:21 +00:00
data = data[size:]
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
// Handle error
2024-02-18 10:42:21 +00:00
if err == nil { // n != len(data)
2024-02-18 10:42:21 +00:00
mc.cleanup()
2024-02-18 10:42:21 +00:00
errLog.Print(ErrMalformPkt)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
if cerr := mc.canceled.Value(); cerr != nil {
2024-02-18 10:42:21 +00:00
return cerr
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if n == 0 && pktLen == len(data)-4 {
2024-02-18 10:42:21 +00:00
// only for the first loop iteration when nothing was written yet
2024-02-18 10:42:21 +00:00
return errBadConnNoWrite
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
mc.cleanup()
2024-02-18 10:42:21 +00:00
errLog.Print(err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return ErrInvalidConn
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
/******************************************************************************
2024-02-18 10:42:21 +00:00
* Initialization Process *
2024-02-18 10:42:21 +00:00
******************************************************************************/
// Handshake Initialization Packet
2024-02-18 10:42:21 +00:00
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake
2024-02-18 10:42:21 +00:00
func (mc *mysqlConn) readHandshakePacket() (data []byte, plugin string, err error) {
2024-02-18 10:42:21 +00:00
data, err = mc.readPacket()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
// for init we can rewrite this to ErrBadConn for sql.Driver to retry, since
2024-02-18 10:42:21 +00:00
// in connection initialization we don't risk retrying non-idempotent actions.
2024-02-18 10:42:21 +00:00
if err == ErrInvalidConn {
2024-02-18 10:42:21 +00:00
return nil, "", driver.ErrBadConn
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
if data[0] == iERR {
2024-02-18 10:42:21 +00:00
return nil, "", mc.handleErrorPacket(data)
2024-02-18 10:42:21 +00:00
}
// protocol version [1 byte]
2024-02-18 10:42:21 +00:00
if data[0] < minProtocolVersion {
2024-02-18 10:42:21 +00:00
return nil, "", fmt.Errorf(
2024-02-18 10:42:21 +00:00
"unsupported protocol version %d. Version %d or higher is required",
2024-02-18 10:42:21 +00:00
data[0],
2024-02-18 10:42:21 +00:00
minProtocolVersion,
)
2024-02-18 10:42:21 +00:00
}
// server version [null terminated string]
2024-02-18 10:42:21 +00:00
// connection id [4 bytes]
2024-02-18 10:42:21 +00:00
pos := 1 + bytes.IndexByte(data[1:], 0x00) + 1 + 4
// first part of the password cipher [8 bytes]
2024-02-18 10:42:21 +00:00
authData := data[pos : pos+8]
// (filler) always 0x00 [1 byte]
2024-02-18 10:42:21 +00:00
pos += 8 + 1
// capability flags (lower 2 bytes) [2 bytes]
2024-02-18 10:42:21 +00:00
mc.flags = clientFlag(binary.LittleEndian.Uint16(data[pos : pos+2]))
2024-02-18 10:42:21 +00:00
if mc.flags&clientProtocol41 == 0 {
2024-02-18 10:42:21 +00:00
return nil, "", ErrOldProtocol
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if mc.flags&clientSSL == 0 && mc.cfg.tls != nil {
2024-02-18 10:42:21 +00:00
if mc.cfg.TLSConfig == "preferred" {
2024-02-18 10:42:21 +00:00
mc.cfg.tls = nil
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
return nil, "", ErrNoTLS
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pos += 2
if len(data) > pos {
2024-02-18 10:42:21 +00:00
// character set [1 byte]
2024-02-18 10:42:21 +00:00
// status flags [2 bytes]
2024-02-18 10:42:21 +00:00
// capability flags (upper 2 bytes) [2 bytes]
2024-02-18 10:42:21 +00:00
// length of auth-plugin-data [1 byte]
2024-02-18 10:42:21 +00:00
// reserved (all [00]) [10 bytes]
2024-02-18 10:42:21 +00:00
pos += 1 + 2 + 2 + 1 + 10
// second part of the password cipher [mininum 13 bytes],
2024-02-18 10:42:21 +00:00
// where len=MAX(13, length of auth-plugin-data - 8)
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The web documentation is ambiguous about the length. However,
2024-02-18 10:42:21 +00:00
// according to mysql-5.7/sql/auth/sql_authentication.cc line 538,
2024-02-18 10:42:21 +00:00
// the 13th byte is "\0 byte, terminating the second part of
2024-02-18 10:42:21 +00:00
// a scramble". So the second part of the password cipher is
2024-02-18 10:42:21 +00:00
// a NULL terminated string that's at least 13 bytes with the
2024-02-18 10:42:21 +00:00
// last byte being NULL.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The official Python library uses the fixed length 12
2024-02-18 10:42:21 +00:00
// which seems to work but technically could have a hidden bug.
2024-02-18 10:42:21 +00:00
authData = append(authData, data[pos:pos+12]...)
2024-02-18 10:42:21 +00:00
pos += 13
// EOF if version (>= 5.5.7 and < 5.5.10) or (>= 5.6.0 and < 5.6.2)
2024-02-18 10:42:21 +00:00
// \NUL otherwise
2024-02-18 10:42:21 +00:00
if end := bytes.IndexByte(data[pos:], 0x00); end != -1 {
2024-02-18 10:42:21 +00:00
plugin = string(data[pos : pos+end])
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
plugin = string(data[pos:])
2024-02-18 10:42:21 +00:00
}
// make a memory safe copy of the cipher slice
2024-02-18 10:42:21 +00:00
var b [20]byte
2024-02-18 10:42:21 +00:00
copy(b[:], authData)
2024-02-18 10:42:21 +00:00
return b[:], plugin, nil
2024-02-18 10:42:21 +00:00
}
// make a memory safe copy of the cipher slice
2024-02-18 10:42:21 +00:00
var b [8]byte
2024-02-18 10:42:21 +00:00
copy(b[:], authData)
2024-02-18 10:42:21 +00:00
return b[:], plugin, nil
2024-02-18 10:42:21 +00:00
}
// Client Authentication Packet
2024-02-18 10:42:21 +00:00
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse
2024-02-18 10:42:21 +00:00
func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string) error {
2024-02-18 10:42:21 +00:00
// Adjust client flags based on server support
2024-02-18 10:42:21 +00:00
clientFlags := clientProtocol41 |
2024-02-18 10:42:21 +00:00
clientSecureConn |
2024-02-18 10:42:21 +00:00
clientLongPassword |
2024-02-18 10:42:21 +00:00
clientTransactions |
2024-02-18 10:42:21 +00:00
clientLocalFiles |
2024-02-18 10:42:21 +00:00
clientPluginAuth |
2024-02-18 10:42:21 +00:00
clientMultiResults |
2024-02-18 10:42:21 +00:00
mc.flags&clientLongFlag
if mc.cfg.ClientFoundRows {
2024-02-18 10:42:21 +00:00
clientFlags |= clientFoundRows
2024-02-18 10:42:21 +00:00
}
// To enable TLS / SSL
2024-02-18 10:42:21 +00:00
if mc.cfg.tls != nil {
2024-02-18 10:42:21 +00:00
clientFlags |= clientSSL
2024-02-18 10:42:21 +00:00
}
if mc.cfg.MultiStatements {
2024-02-18 10:42:21 +00:00
clientFlags |= clientMultiStatements
2024-02-18 10:42:21 +00:00
}
// encode length of the auth plugin data
2024-02-18 10:42:21 +00:00
var authRespLEIBuf [9]byte
2024-02-18 10:42:21 +00:00
authRespLen := len(authResp)
2024-02-18 10:42:21 +00:00
authRespLEI := appendLengthEncodedInteger(authRespLEIBuf[:0], uint64(authRespLen))
2024-02-18 10:42:21 +00:00
if len(authRespLEI) > 1 {
2024-02-18 10:42:21 +00:00
// if the length can not be written in 1 byte, it must be written as a
2024-02-18 10:42:21 +00:00
// length encoded integer
2024-02-18 10:42:21 +00:00
clientFlags |= clientPluginAuthLenEncClientData
2024-02-18 10:42:21 +00:00
}
pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + len(authRespLEI) + len(authResp) + 21 + 1
// To specify a db name
2024-02-18 10:42:21 +00:00
if n := len(mc.cfg.DBName); n > 0 {
2024-02-18 10:42:21 +00:00
clientFlags |= clientConnectWithDB
2024-02-18 10:42:21 +00:00
pktLen += n + 1
2024-02-18 10:42:21 +00:00
}
// Calculate packet length and get buffer with that size
2024-02-18 10:42:21 +00:00
data, err := mc.buf.takeSmallBuffer(pktLen + 4)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
// cannot take the buffer. Something must be wrong with the connection
2024-02-18 10:42:21 +00:00
errLog.Print(err)
2024-02-18 10:42:21 +00:00
return errBadConnNoWrite
2024-02-18 10:42:21 +00:00
}
// ClientFlags [32 bit]
2024-02-18 10:42:21 +00:00
data[4] = byte(clientFlags)
2024-02-18 10:42:21 +00:00
data[5] = byte(clientFlags >> 8)
2024-02-18 10:42:21 +00:00
data[6] = byte(clientFlags >> 16)
2024-02-18 10:42:21 +00:00
data[7] = byte(clientFlags >> 24)
// MaxPacketSize [32 bit] (none)
2024-02-18 10:42:21 +00:00
data[8] = 0x00
2024-02-18 10:42:21 +00:00
data[9] = 0x00
2024-02-18 10:42:21 +00:00
data[10] = 0x00
2024-02-18 10:42:21 +00:00
data[11] = 0x00
// Charset [1 byte]
2024-02-18 10:42:21 +00:00
var found bool
2024-02-18 10:42:21 +00:00
data[12], found = collations[mc.cfg.Collation]
2024-02-18 10:42:21 +00:00
if !found {
2024-02-18 10:42:21 +00:00
// Note possibility for false negatives:
2024-02-18 10:42:21 +00:00
// could be triggered although the collation is valid if the
2024-02-18 10:42:21 +00:00
// collations map does not contain entries the server supports.
2024-02-18 10:42:21 +00:00
return errors.New("unknown collation")
2024-02-18 10:42:21 +00:00
}
// Filler [23 bytes] (all 0x00)
2024-02-18 10:42:21 +00:00
pos := 13
2024-02-18 10:42:21 +00:00
for ; pos < 13+23; pos++ {
2024-02-18 10:42:21 +00:00
data[pos] = 0
2024-02-18 10:42:21 +00:00
}
// SSL Connection Request Packet
2024-02-18 10:42:21 +00:00
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest
2024-02-18 10:42:21 +00:00
if mc.cfg.tls != nil {
2024-02-18 10:42:21 +00:00
// Send TLS / SSL request packet
2024-02-18 10:42:21 +00:00
if err := mc.writePacket(data[:(4+4+1+23)+4]); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
// Switch to TLS
2024-02-18 10:42:21 +00:00
tlsConn := tls.Client(mc.netConn, mc.cfg.tls)
2024-02-18 10:42:21 +00:00
if err := tlsConn.Handshake(); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
mc.rawConn = mc.netConn
2024-02-18 10:42:21 +00:00
mc.netConn = tlsConn
2024-02-18 10:42:21 +00:00
mc.buf.nc = tlsConn
2024-02-18 10:42:21 +00:00
}
// User [null terminated string]
2024-02-18 10:42:21 +00:00
if len(mc.cfg.User) > 0 {
2024-02-18 10:42:21 +00:00
pos += copy(data[pos:], mc.cfg.User)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
data[pos] = 0x00
2024-02-18 10:42:21 +00:00
pos++
// Auth Data [length encoded integer]
2024-02-18 10:42:21 +00:00
pos += copy(data[pos:], authRespLEI)
2024-02-18 10:42:21 +00:00
pos += copy(data[pos:], authResp)
// Databasename [null terminated string]
2024-02-18 10:42:21 +00:00
if len(mc.cfg.DBName) > 0 {
2024-02-18 10:42:21 +00:00
pos += copy(data[pos:], mc.cfg.DBName)
2024-02-18 10:42:21 +00:00
data[pos] = 0x00
2024-02-18 10:42:21 +00:00
pos++
2024-02-18 10:42:21 +00:00
}
pos += copy(data[pos:], plugin)
2024-02-18 10:42:21 +00:00
data[pos] = 0x00
2024-02-18 10:42:21 +00:00
pos++
// Send Auth packet
2024-02-18 10:42:21 +00:00
return mc.writePacket(data[:pos])
2024-02-18 10:42:21 +00:00
}
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
2024-02-18 10:42:21 +00:00
func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte) error {
2024-02-18 10:42:21 +00:00
pktLen := 4 + len(authData)
2024-02-18 10:42:21 +00:00
data, err := mc.buf.takeSmallBuffer(pktLen)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
// cannot take the buffer. Something must be wrong with the connection
2024-02-18 10:42:21 +00:00
errLog.Print(err)
2024-02-18 10:42:21 +00:00
return errBadConnNoWrite
2024-02-18 10:42:21 +00:00
}
// Add the auth data [EOF]
2024-02-18 10:42:21 +00:00
copy(data[4:], authData)
2024-02-18 10:42:21 +00:00
return mc.writePacket(data)
2024-02-18 10:42:21 +00:00
}
/******************************************************************************
2024-02-18 10:42:21 +00:00
* Command Packets *
2024-02-18 10:42:21 +00:00
******************************************************************************/
func (mc *mysqlConn) writeCommandPacket(command byte) error {
2024-02-18 10:42:21 +00:00
// Reset Packet Sequence
2024-02-18 10:42:21 +00:00
mc.sequence = 0
data, err := mc.buf.takeSmallBuffer(4 + 1)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
// cannot take the buffer. Something must be wrong with the connection
2024-02-18 10:42:21 +00:00
errLog.Print(err)
2024-02-18 10:42:21 +00:00
return errBadConnNoWrite
2024-02-18 10:42:21 +00:00
}
// Add command byte
2024-02-18 10:42:21 +00:00
data[4] = command
// Send CMD packet
2024-02-18 10:42:21 +00:00
return mc.writePacket(data)
2024-02-18 10:42:21 +00:00
}
func (mc *mysqlConn) writeCommandPacketStr(command byte, arg string) error {
2024-02-18 10:42:21 +00:00
// Reset Packet Sequence
2024-02-18 10:42:21 +00:00
mc.sequence = 0
pktLen := 1 + len(arg)
2024-02-18 10:42:21 +00:00
data, err := mc.buf.takeBuffer(pktLen + 4)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
// cannot take the buffer. Something must be wrong with the connection
2024-02-18 10:42:21 +00:00
errLog.Print(err)
2024-02-18 10:42:21 +00:00
return errBadConnNoWrite
2024-02-18 10:42:21 +00:00
}
// Add command byte
2024-02-18 10:42:21 +00:00
data[4] = command
// Add arg
2024-02-18 10:42:21 +00:00
copy(data[5:], arg)
// Send CMD packet
2024-02-18 10:42:21 +00:00
return mc.writePacket(data)
2024-02-18 10:42:21 +00:00
}
func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error {
2024-02-18 10:42:21 +00:00
// Reset Packet Sequence
2024-02-18 10:42:21 +00:00
mc.sequence = 0
data, err := mc.buf.takeSmallBuffer(4 + 1 + 4)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
// cannot take the buffer. Something must be wrong with the connection
2024-02-18 10:42:21 +00:00
errLog.Print(err)
2024-02-18 10:42:21 +00:00
return errBadConnNoWrite
2024-02-18 10:42:21 +00:00
}
// Add command byte
2024-02-18 10:42:21 +00:00
data[4] = command
// Add arg [32 bit]
2024-02-18 10:42:21 +00:00
data[5] = byte(arg)
2024-02-18 10:42:21 +00:00
data[6] = byte(arg >> 8)
2024-02-18 10:42:21 +00:00
data[7] = byte(arg >> 16)
2024-02-18 10:42:21 +00:00
data[8] = byte(arg >> 24)
// Send CMD packet
2024-02-18 10:42:21 +00:00
return mc.writePacket(data)
2024-02-18 10:42:21 +00:00
}
/******************************************************************************
2024-02-18 10:42:21 +00:00
* Result Packets *
2024-02-18 10:42:21 +00:00
******************************************************************************/
func (mc *mysqlConn) readAuthResult() ([]byte, string, error) {
2024-02-18 10:42:21 +00:00
data, err := mc.readPacket()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, "", err
2024-02-18 10:42:21 +00:00
}
// packet indicator
2024-02-18 10:42:21 +00:00
switch data[0] {
case iOK:
2024-02-18 10:42:21 +00:00
return nil, "", mc.handleOkPacket(data)
case iAuthMoreData:
2024-02-18 10:42:21 +00:00
return data[1:], "", err
case iEOF:
2024-02-18 10:42:21 +00:00
if len(data) == 1 {
2024-02-18 10:42:21 +00:00
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
2024-02-18 10:42:21 +00:00
return nil, "mysql_old_password", nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pluginEndIndex := bytes.IndexByte(data, 0x00)
2024-02-18 10:42:21 +00:00
if pluginEndIndex < 0 {
2024-02-18 10:42:21 +00:00
return nil, "", ErrMalformPkt
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
plugin := string(data[1:pluginEndIndex])
2024-02-18 10:42:21 +00:00
authData := data[pluginEndIndex+1:]
2024-02-18 10:42:21 +00:00
return authData, plugin, nil
default: // Error otherwise
2024-02-18 10:42:21 +00:00
return nil, "", mc.handleErrorPacket(data)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// Returns error if Packet is not an 'Result OK'-Packet
2024-02-18 10:42:21 +00:00
func (mc *mysqlConn) readResultOK() error {
2024-02-18 10:42:21 +00:00
data, err := mc.readPacket()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
if data[0] == iOK {
2024-02-18 10:42:21 +00:00
return mc.handleOkPacket(data)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return mc.handleErrorPacket(data)
2024-02-18 10:42:21 +00:00
}
// Result Set Header Packet
2024-02-18 10:42:21 +00:00
// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-ProtocolText::Resultset
2024-02-18 10:42:21 +00:00
func (mc *mysqlConn) readResultSetHeaderPacket() (int, error) {
2024-02-18 10:42:21 +00:00
data, err := mc.readPacket()
2024-02-18 10:42:21 +00:00
if err == nil {
2024-02-18 10:42:21 +00:00
switch data[0] {
case iOK:
2024-02-18 10:42:21 +00:00
return 0, mc.handleOkPacket(data)
case iERR:
2024-02-18 10:42:21 +00:00
return 0, mc.handleErrorPacket(data)
case iLocalInFile:
2024-02-18 10:42:21 +00:00
return 0, mc.handleInFileRequest(string(data[1:]))
2024-02-18 10:42:21 +00:00
}
// column count
2024-02-18 10:42:21 +00:00
num, _, n := readLengthEncodedInteger(data)
2024-02-18 10:42:21 +00:00
if n-len(data) == 0 {
2024-02-18 10:42:21 +00:00
return int(num), nil
2024-02-18 10:42:21 +00:00
}
return 0, ErrMalformPkt
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return 0, err
2024-02-18 10:42:21 +00:00
}
// Error Packet
2024-02-18 10:42:21 +00:00
// http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-ERR_Packet
2024-02-18 10:42:21 +00:00
func (mc *mysqlConn) handleErrorPacket(data []byte) error {
2024-02-18 10:42:21 +00:00
if data[0] != iERR {
2024-02-18 10:42:21 +00:00
return ErrMalformPkt
2024-02-18 10:42:21 +00:00
}
// 0xff [1 byte]
// Error Number [16 bit uint]
2024-02-18 10:42:21 +00:00
errno := binary.LittleEndian.Uint16(data[1:3])
// 1792: ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION
2024-02-18 10:42:21 +00:00
// 1290: ER_OPTION_PREVENTS_STATEMENT (returned by Aurora during failover)
2024-02-18 10:42:21 +00:00
if (errno == 1792 || errno == 1290) && mc.cfg.RejectReadOnly {
2024-02-18 10:42:21 +00:00
// Oops; we are connected to a read-only connection, and won't be able
2024-02-18 10:42:21 +00:00
// to issue any write statements. Since RejectReadOnly is configured,
2024-02-18 10:42:21 +00:00
// we throw away this connection hoping this one would have write
2024-02-18 10:42:21 +00:00
// permission. This is specifically for a possible race condition
2024-02-18 10:42:21 +00:00
// during failover (e.g. on AWS Aurora). See README.md for more.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// We explicitly close the connection before returning
2024-02-18 10:42:21 +00:00
// driver.ErrBadConn to ensure that `database/sql` purges this
2024-02-18 10:42:21 +00:00
// connection and initiates a new one for next statement next time.
2024-02-18 10:42:21 +00:00
mc.Close()
2024-02-18 10:42:21 +00:00
return driver.ErrBadConn
2024-02-18 10:42:21 +00:00
}
pos := 3
// SQL State [optional: # + 5bytes string]
2024-02-18 10:42:21 +00:00
if data[3] == 0x23 {
2024-02-18 10:42:21 +00:00
//sqlstate := string(data[4 : 4+5])
2024-02-18 10:42:21 +00:00
pos = 9
2024-02-18 10:42:21 +00:00
}
// Error Message [string]
2024-02-18 10:42:21 +00:00
return &MySQLError{
Number: errno,
2024-02-18 10:42:21 +00:00
Message: string(data[pos:]),
}
2024-02-18 10:42:21 +00:00
}
func readStatus(b []byte) statusFlag {
2024-02-18 10:42:21 +00:00
return statusFlag(b[0]) | statusFlag(b[1])<<8
2024-02-18 10:42:21 +00:00
}
// Ok Packet
2024-02-18 10:42:21 +00:00
// http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-OK_Packet
2024-02-18 10:42:21 +00:00
func (mc *mysqlConn) handleOkPacket(data []byte) error {
2024-02-18 10:42:21 +00:00
var n, m int
// 0x00 [1 byte]
// Affected rows [Length Coded Binary]
2024-02-18 10:42:21 +00:00
mc.affectedRows, _, n = readLengthEncodedInteger(data[1:])
// Insert id [Length Coded Binary]
2024-02-18 10:42:21 +00:00
mc.insertId, _, m = readLengthEncodedInteger(data[1+n:])
// server_status [2 bytes]
2024-02-18 10:42:21 +00:00
mc.status = readStatus(data[1+n+m : 1+n+m+2])
2024-02-18 10:42:21 +00:00
if mc.status&statusMoreResultsExists != 0 {
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// warning count [2 bytes]
return nil
2024-02-18 10:42:21 +00:00
}
// Read Packets as Field Packets until EOF-Packet or an Error appears
2024-02-18 10:42:21 +00:00
// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnDefinition41
2024-02-18 10:42:21 +00:00
func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
2024-02-18 10:42:21 +00:00
columns := make([]mysqlField, count)
for i := 0; ; i++ {
2024-02-18 10:42:21 +00:00
data, err := mc.readPacket()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
// EOF Packet
2024-02-18 10:42:21 +00:00
if data[0] == iEOF && (len(data) == 5 || len(data) == 1) {
2024-02-18 10:42:21 +00:00
if i == count {
2024-02-18 10:42:21 +00:00
return columns, nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("column count mismatch n:%d len:%d", count, len(columns))
2024-02-18 10:42:21 +00:00
}
// Catalog
2024-02-18 10:42:21 +00:00
pos, err := skipLengthEncodedString(data)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
// Database [len coded string]
2024-02-18 10:42:21 +00:00
n, err := skipLengthEncodedString(data[pos:])
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pos += n
// Table [len coded string]
2024-02-18 10:42:21 +00:00
if mc.cfg.ColumnsWithAlias {
2024-02-18 10:42:21 +00:00
tableName, _, n, err := readLengthEncodedString(data[pos:])
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pos += n
2024-02-18 10:42:21 +00:00
columns[i].tableName = string(tableName)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
n, err = skipLengthEncodedString(data[pos:])
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pos += n
2024-02-18 10:42:21 +00:00
}
// Original table [len coded string]
2024-02-18 10:42:21 +00:00
n, err = skipLengthEncodedString(data[pos:])
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pos += n
// Name [len coded string]
2024-02-18 10:42:21 +00:00
name, _, n, err := readLengthEncodedString(data[pos:])
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
columns[i].name = string(name)
2024-02-18 10:42:21 +00:00
pos += n
// Original name [len coded string]
2024-02-18 10:42:21 +00:00
n, err = skipLengthEncodedString(data[pos:])
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pos += n
// Filler [uint8]
2024-02-18 10:42:21 +00:00
pos++
// Charset [charset, collation uint8]
2024-02-18 10:42:21 +00:00
columns[i].charSet = data[pos]
2024-02-18 10:42:21 +00:00
pos += 2
// Length [uint32]
2024-02-18 10:42:21 +00:00
columns[i].length = binary.LittleEndian.Uint32(data[pos : pos+4])
2024-02-18 10:42:21 +00:00
pos += 4
// Field type [uint8]
2024-02-18 10:42:21 +00:00
columns[i].fieldType = fieldType(data[pos])
2024-02-18 10:42:21 +00:00
pos++
// Flags [uint16]
2024-02-18 10:42:21 +00:00
columns[i].flags = fieldFlag(binary.LittleEndian.Uint16(data[pos : pos+2]))
2024-02-18 10:42:21 +00:00
pos += 2
// Decimals [uint8]
2024-02-18 10:42:21 +00:00
columns[i].decimals = data[pos]
2024-02-18 10:42:21 +00:00
//pos++
// Default value [len coded binary]
2024-02-18 10:42:21 +00:00
//if pos < len(data) {
2024-02-18 10:42:21 +00:00
// defaultVal, _, err = bytesToLengthCodedBinary(data[pos:])
2024-02-18 10:42:21 +00:00
//}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// Read Packets as Field Packets until EOF-Packet or an Error appears
2024-02-18 10:42:21 +00:00
// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-ProtocolText::ResultsetRow
2024-02-18 10:42:21 +00:00
func (rows *textRows) readRow(dest []driver.Value) error {
2024-02-18 10:42:21 +00:00
mc := rows.mc
if rows.rs.done {
2024-02-18 10:42:21 +00:00
return io.EOF
2024-02-18 10:42:21 +00:00
}
data, err := mc.readPacket()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
// EOF Packet
2024-02-18 10:42:21 +00:00
if data[0] == iEOF && len(data) == 5 {
2024-02-18 10:42:21 +00:00
// server_status [2 bytes]
2024-02-18 10:42:21 +00:00
rows.mc.status = readStatus(data[3:])
2024-02-18 10:42:21 +00:00
rows.rs.done = true
2024-02-18 10:42:21 +00:00
if !rows.HasNextResultSet() {
2024-02-18 10:42:21 +00:00
rows.mc = nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return io.EOF
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if data[0] == iERR {
2024-02-18 10:42:21 +00:00
rows.mc = nil
2024-02-18 10:42:21 +00:00
return mc.handleErrorPacket(data)
2024-02-18 10:42:21 +00:00
}
// RowSet Packet
2024-02-18 10:42:21 +00:00
var n int
2024-02-18 10:42:21 +00:00
var isNull bool
2024-02-18 10:42:21 +00:00
pos := 0
for i := range dest {
2024-02-18 10:42:21 +00:00
// Read bytes and convert to string
2024-02-18 10:42:21 +00:00
dest[i], isNull, n, err = readLengthEncodedString(data[pos:])
2024-02-18 10:42:21 +00:00
pos += n
2024-02-18 10:42:21 +00:00
if err == nil {
2024-02-18 10:42:21 +00:00
if !isNull {
2024-02-18 10:42:21 +00:00
if !mc.parseTime {
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
switch rows.rs.columns[i].fieldType {
2024-02-18 10:42:21 +00:00
case fieldTypeTimestamp, fieldTypeDateTime,
2024-02-18 10:42:21 +00:00
fieldTypeDate, fieldTypeNewDate:
2024-02-18 10:42:21 +00:00
dest[i], err = parseDateTime(
2024-02-18 10:42:21 +00:00
dest[i].([]byte),
2024-02-18 10:42:21 +00:00
mc.cfg.Loc,
)
2024-02-18 10:42:21 +00:00
if err == nil {
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
} else {
2024-02-18 10:42:21 +00:00
dest[i] = nil
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return err // err != nil
2024-02-18 10:42:21 +00:00
}
return nil
2024-02-18 10:42:21 +00:00
}
// Reads Packets until EOF-Packet or an Error appears. Returns count of Packets read
2024-02-18 10:42:21 +00:00
func (mc *mysqlConn) readUntilEOF() error {
2024-02-18 10:42:21 +00:00
for {
2024-02-18 10:42:21 +00:00
data, err := mc.readPacket()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
switch data[0] {
2024-02-18 10:42:21 +00:00
case iERR:
2024-02-18 10:42:21 +00:00
return mc.handleErrorPacket(data)
2024-02-18 10:42:21 +00:00
case iEOF:
2024-02-18 10:42:21 +00:00
if len(data) == 5 {
2024-02-18 10:42:21 +00:00
mc.status = readStatus(data[3:])
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
/******************************************************************************
2024-02-18 10:42:21 +00:00
* Prepared Statements *
2024-02-18 10:42:21 +00:00
******************************************************************************/
// Prepare Result Packets
2024-02-18 10:42:21 +00:00
// http://dev.mysql.com/doc/internals/en/com-stmt-prepare-response.html
2024-02-18 10:42:21 +00:00
func (stmt *mysqlStmt) readPrepareResultPacket() (uint16, error) {
2024-02-18 10:42:21 +00:00
data, err := stmt.mc.readPacket()
2024-02-18 10:42:21 +00:00
if err == nil {
2024-02-18 10:42:21 +00:00
// packet indicator [1 byte]
2024-02-18 10:42:21 +00:00
if data[0] != iOK {
2024-02-18 10:42:21 +00:00
return 0, stmt.mc.handleErrorPacket(data)
2024-02-18 10:42:21 +00:00
}
// statement id [4 bytes]
2024-02-18 10:42:21 +00:00
stmt.id = binary.LittleEndian.Uint32(data[1:5])
// Column count [16 bit uint]
2024-02-18 10:42:21 +00:00
columnCount := binary.LittleEndian.Uint16(data[5:7])
// Param count [16 bit uint]
2024-02-18 10:42:21 +00:00
stmt.paramCount = int(binary.LittleEndian.Uint16(data[7:9]))
// Reserved [8 bit]
// Warning count [16 bit uint]
return columnCount, nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return 0, err
2024-02-18 10:42:21 +00:00
}
// http://dev.mysql.com/doc/internals/en/com-stmt-send-long-data.html
2024-02-18 10:42:21 +00:00
func (stmt *mysqlStmt) writeCommandLongData(paramID int, arg []byte) error {
2024-02-18 10:42:21 +00:00
maxLen := stmt.mc.maxAllowedPacket - 1
2024-02-18 10:42:21 +00:00
pktLen := maxLen
// After the header (bytes 0-3) follows before the data:
2024-02-18 10:42:21 +00:00
// 1 byte command
2024-02-18 10:42:21 +00:00
// 4 bytes stmtID
2024-02-18 10:42:21 +00:00
// 2 bytes paramID
2024-02-18 10:42:21 +00:00
const dataOffset = 1 + 4 + 2
// Cannot use the write buffer since
2024-02-18 10:42:21 +00:00
// a) the buffer is too small
2024-02-18 10:42:21 +00:00
// b) it is in use
2024-02-18 10:42:21 +00:00
data := make([]byte, 4+1+4+2+len(arg))
copy(data[4+dataOffset:], arg)
for argLen := len(arg); argLen > 0; argLen -= pktLen - dataOffset {
2024-02-18 10:42:21 +00:00
if dataOffset+argLen < maxLen {
2024-02-18 10:42:21 +00:00
pktLen = dataOffset + argLen
2024-02-18 10:42:21 +00:00
}
stmt.mc.sequence = 0
2024-02-18 10:42:21 +00:00
// Add command byte [1 byte]
2024-02-18 10:42:21 +00:00
data[4] = comStmtSendLongData
// Add stmtID [32 bit]
2024-02-18 10:42:21 +00:00
data[5] = byte(stmt.id)
2024-02-18 10:42:21 +00:00
data[6] = byte(stmt.id >> 8)
2024-02-18 10:42:21 +00:00
data[7] = byte(stmt.id >> 16)
2024-02-18 10:42:21 +00:00
data[8] = byte(stmt.id >> 24)
// Add paramID [16 bit]
2024-02-18 10:42:21 +00:00
data[9] = byte(paramID)
2024-02-18 10:42:21 +00:00
data[10] = byte(paramID >> 8)
// Send CMD packet
2024-02-18 10:42:21 +00:00
err := stmt.mc.writePacket(data[:4+pktLen])
2024-02-18 10:42:21 +00:00
if err == nil {
2024-02-18 10:42:21 +00:00
data = data[pktLen-dataOffset:]
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return err
}
// Reset Packet Sequence
2024-02-18 10:42:21 +00:00
stmt.mc.sequence = 0
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// Execute Prepared Statement
2024-02-18 10:42:21 +00:00
// http://dev.mysql.com/doc/internals/en/com-stmt-execute.html
2024-02-18 10:42:21 +00:00
func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
2024-02-18 10:42:21 +00:00
if len(args) != stmt.paramCount {
2024-02-18 10:42:21 +00:00
return fmt.Errorf(
2024-02-18 10:42:21 +00:00
"argument count mismatch (got: %d; has: %d)",
2024-02-18 10:42:21 +00:00
len(args),
2024-02-18 10:42:21 +00:00
stmt.paramCount,
)
2024-02-18 10:42:21 +00:00
}
const minPktLen = 4 + 1 + 4 + 1 + 4
2024-02-18 10:42:21 +00:00
mc := stmt.mc
// Determine threshold dynamically to avoid packet size shortage.
2024-02-18 10:42:21 +00:00
longDataSize := mc.maxAllowedPacket / (stmt.paramCount + 1)
2024-02-18 10:42:21 +00:00
if longDataSize < 64 {
2024-02-18 10:42:21 +00:00
longDataSize = 64
2024-02-18 10:42:21 +00:00
}
// Reset packet-sequence
2024-02-18 10:42:21 +00:00
mc.sequence = 0
var data []byte
2024-02-18 10:42:21 +00:00
var err error
if len(args) == 0 {
2024-02-18 10:42:21 +00:00
data, err = mc.buf.takeBuffer(minPktLen)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
data, err = mc.buf.takeCompleteBuffer()
2024-02-18 10:42:21 +00:00
// In this case the len(data) == cap(data) which is used to optimise the flow below.
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
// cannot take the buffer. Something must be wrong with the connection
2024-02-18 10:42:21 +00:00
errLog.Print(err)
2024-02-18 10:42:21 +00:00
return errBadConnNoWrite
2024-02-18 10:42:21 +00:00
}
// command [1 byte]
2024-02-18 10:42:21 +00:00
data[4] = comStmtExecute
// statement_id [4 bytes]
2024-02-18 10:42:21 +00:00
data[5] = byte(stmt.id)
2024-02-18 10:42:21 +00:00
data[6] = byte(stmt.id >> 8)
2024-02-18 10:42:21 +00:00
data[7] = byte(stmt.id >> 16)
2024-02-18 10:42:21 +00:00
data[8] = byte(stmt.id >> 24)
// flags (0: CURSOR_TYPE_NO_CURSOR) [1 byte]
2024-02-18 10:42:21 +00:00
data[9] = 0x00
// iteration_count (uint32(1)) [4 bytes]
2024-02-18 10:42:21 +00:00
data[10] = 0x01
2024-02-18 10:42:21 +00:00
data[11] = 0x00
2024-02-18 10:42:21 +00:00
data[12] = 0x00
2024-02-18 10:42:21 +00:00
data[13] = 0x00
if len(args) > 0 {
2024-02-18 10:42:21 +00:00
pos := minPktLen
var nullMask []byte
2024-02-18 10:42:21 +00:00
if maskLen, typesLen := (len(args)+7)/8, 1+2*len(args); pos+maskLen+typesLen >= cap(data) {
2024-02-18 10:42:21 +00:00
// buffer has to be extended but we don't know by how much so
2024-02-18 10:42:21 +00:00
// we depend on append after all data with known sizes fit.
2024-02-18 10:42:21 +00:00
// We stop at that because we deal with a lot of columns here
2024-02-18 10:42:21 +00:00
// which makes the required allocation size hard to guess.
2024-02-18 10:42:21 +00:00
tmp := make([]byte, pos+maskLen+typesLen)
2024-02-18 10:42:21 +00:00
copy(tmp[:pos], data[:pos])
2024-02-18 10:42:21 +00:00
data = tmp
2024-02-18 10:42:21 +00:00
nullMask = data[pos : pos+maskLen]
2024-02-18 10:42:21 +00:00
// No need to clean nullMask as make ensures that.
2024-02-18 10:42:21 +00:00
pos += maskLen
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
nullMask = data[pos : pos+maskLen]
2024-02-18 10:42:21 +00:00
for i := range nullMask {
2024-02-18 10:42:21 +00:00
nullMask[i] = 0
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pos += maskLen
2024-02-18 10:42:21 +00:00
}
// newParameterBoundFlag 1 [1 byte]
2024-02-18 10:42:21 +00:00
data[pos] = 0x01
2024-02-18 10:42:21 +00:00
pos++
// type of each parameter [len(args)*2 bytes]
2024-02-18 10:42:21 +00:00
paramTypes := data[pos:]
2024-02-18 10:42:21 +00:00
pos += len(args) * 2
// value of each parameter [n bytes]
2024-02-18 10:42:21 +00:00
paramValues := data[pos:pos]
2024-02-18 10:42:21 +00:00
valuesCap := cap(paramValues)
for i, arg := range args {
2024-02-18 10:42:21 +00:00
// build NULL-bitmap
2024-02-18 10:42:21 +00:00
if arg == nil {
2024-02-18 10:42:21 +00:00
nullMask[i/8] |= 1 << (uint(i) & 7)
2024-02-18 10:42:21 +00:00
paramTypes[i+i] = byte(fieldTypeNULL)
2024-02-18 10:42:21 +00:00
paramTypes[i+i+1] = 0x00
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
if v, ok := arg.(json.RawMessage); ok {
2024-02-18 10:42:21 +00:00
arg = []byte(v)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
// cache types and values
2024-02-18 10:42:21 +00:00
switch v := arg.(type) {
2024-02-18 10:42:21 +00:00
case int64:
2024-02-18 10:42:21 +00:00
paramTypes[i+i] = byte(fieldTypeLongLong)
2024-02-18 10:42:21 +00:00
paramTypes[i+i+1] = 0x00
if cap(paramValues)-len(paramValues)-8 >= 0 {
2024-02-18 10:42:21 +00:00
paramValues = paramValues[:len(paramValues)+8]
2024-02-18 10:42:21 +00:00
binary.LittleEndian.PutUint64(
2024-02-18 10:42:21 +00:00
paramValues[len(paramValues)-8:],
2024-02-18 10:42:21 +00:00
uint64(v),
)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
paramValues = append(paramValues,
2024-02-18 10:42:21 +00:00
uint64ToBytes(uint64(v))...,
)
2024-02-18 10:42:21 +00:00
}
case uint64:
2024-02-18 10:42:21 +00:00
paramTypes[i+i] = byte(fieldTypeLongLong)
2024-02-18 10:42:21 +00:00
paramTypes[i+i+1] = 0x80 // type is unsigned
if cap(paramValues)-len(paramValues)-8 >= 0 {
2024-02-18 10:42:21 +00:00
paramValues = paramValues[:len(paramValues)+8]
2024-02-18 10:42:21 +00:00
binary.LittleEndian.PutUint64(
2024-02-18 10:42:21 +00:00
paramValues[len(paramValues)-8:],
2024-02-18 10:42:21 +00:00
uint64(v),
)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
paramValues = append(paramValues,
2024-02-18 10:42:21 +00:00
uint64ToBytes(uint64(v))...,
)
2024-02-18 10:42:21 +00:00
}
case float64:
2024-02-18 10:42:21 +00:00
paramTypes[i+i] = byte(fieldTypeDouble)
2024-02-18 10:42:21 +00:00
paramTypes[i+i+1] = 0x00
if cap(paramValues)-len(paramValues)-8 >= 0 {
2024-02-18 10:42:21 +00:00
paramValues = paramValues[:len(paramValues)+8]
2024-02-18 10:42:21 +00:00
binary.LittleEndian.PutUint64(
2024-02-18 10:42:21 +00:00
paramValues[len(paramValues)-8:],
2024-02-18 10:42:21 +00:00
math.Float64bits(v),
)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
paramValues = append(paramValues,
2024-02-18 10:42:21 +00:00
uint64ToBytes(math.Float64bits(v))...,
)
2024-02-18 10:42:21 +00:00
}
case bool:
2024-02-18 10:42:21 +00:00
paramTypes[i+i] = byte(fieldTypeTiny)
2024-02-18 10:42:21 +00:00
paramTypes[i+i+1] = 0x00
if v {
2024-02-18 10:42:21 +00:00
paramValues = append(paramValues, 0x01)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
paramValues = append(paramValues, 0x00)
2024-02-18 10:42:21 +00:00
}
case []byte:
2024-02-18 10:42:21 +00:00
// Common case (non-nil value) first
2024-02-18 10:42:21 +00:00
if v != nil {
2024-02-18 10:42:21 +00:00
paramTypes[i+i] = byte(fieldTypeString)
2024-02-18 10:42:21 +00:00
paramTypes[i+i+1] = 0x00
if len(v) < longDataSize {
2024-02-18 10:42:21 +00:00
paramValues = appendLengthEncodedInteger(paramValues,
2024-02-18 10:42:21 +00:00
uint64(len(v)),
)
2024-02-18 10:42:21 +00:00
paramValues = append(paramValues, v...)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
if err := stmt.writeCommandLongData(i, v); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
// Handle []byte(nil) as a NULL value
2024-02-18 10:42:21 +00:00
nullMask[i/8] |= 1 << (uint(i) & 7)
2024-02-18 10:42:21 +00:00
paramTypes[i+i] = byte(fieldTypeNULL)
2024-02-18 10:42:21 +00:00
paramTypes[i+i+1] = 0x00
case string:
2024-02-18 10:42:21 +00:00
paramTypes[i+i] = byte(fieldTypeString)
2024-02-18 10:42:21 +00:00
paramTypes[i+i+1] = 0x00
if len(v) < longDataSize {
2024-02-18 10:42:21 +00:00
paramValues = appendLengthEncodedInteger(paramValues,
2024-02-18 10:42:21 +00:00
uint64(len(v)),
)
2024-02-18 10:42:21 +00:00
paramValues = append(paramValues, v...)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
if err := stmt.writeCommandLongData(i, []byte(v)); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
case time.Time:
2024-02-18 10:42:21 +00:00
paramTypes[i+i] = byte(fieldTypeString)
2024-02-18 10:42:21 +00:00
paramTypes[i+i+1] = 0x00
var a [64]byte
2024-02-18 10:42:21 +00:00
var b = a[:0]
if v.IsZero() {
2024-02-18 10:42:21 +00:00
b = append(b, "0000-00-00"...)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
b, err = appendDateTime(b, v.In(mc.cfg.Loc))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
paramValues = appendLengthEncodedInteger(paramValues,
2024-02-18 10:42:21 +00:00
uint64(len(b)),
)
2024-02-18 10:42:21 +00:00
paramValues = append(paramValues, b...)
default:
2024-02-18 10:42:21 +00:00
return fmt.Errorf("cannot convert type: %T", arg)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// Check if param values exceeded the available buffer
2024-02-18 10:42:21 +00:00
// In that case we must build the data packet with the new values buffer
2024-02-18 10:42:21 +00:00
if valuesCap != cap(paramValues) {
2024-02-18 10:42:21 +00:00
data = append(data[:pos], paramValues...)
2024-02-18 10:42:21 +00:00
if err = mc.buf.store(data); err != nil {
2024-02-18 10:42:21 +00:00
errLog.Print(err)
2024-02-18 10:42:21 +00:00
return errBadConnNoWrite
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
pos += len(paramValues)
2024-02-18 10:42:21 +00:00
data = data[:pos]
2024-02-18 10:42:21 +00:00
}
return mc.writePacket(data)
2024-02-18 10:42:21 +00:00
}
func (mc *mysqlConn) discardResults() error {
2024-02-18 10:42:21 +00:00
for mc.status&statusMoreResultsExists != 0 {
2024-02-18 10:42:21 +00:00
resLen, err := mc.readResultSetHeaderPacket()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if resLen > 0 {
2024-02-18 10:42:21 +00:00
// columns
2024-02-18 10:42:21 +00:00
if err := mc.readUntilEOF(); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
// rows
2024-02-18 10:42:21 +00:00
if err := mc.readUntilEOF(); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// http://dev.mysql.com/doc/internals/en/binary-protocol-resultset-row.html
2024-02-18 10:42:21 +00:00
func (rows *binaryRows) readRow(dest []driver.Value) error {
2024-02-18 10:42:21 +00:00
data, err := rows.mc.readPacket()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
// packet indicator [1 byte]
2024-02-18 10:42:21 +00:00
if data[0] != iOK {
2024-02-18 10:42:21 +00:00
// EOF Packet
2024-02-18 10:42:21 +00:00
if data[0] == iEOF && len(data) == 5 {
2024-02-18 10:42:21 +00:00
rows.mc.status = readStatus(data[3:])
2024-02-18 10:42:21 +00:00
rows.rs.done = true
2024-02-18 10:42:21 +00:00
if !rows.HasNextResultSet() {
2024-02-18 10:42:21 +00:00
rows.mc = nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return io.EOF
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
mc := rows.mc
2024-02-18 10:42:21 +00:00
rows.mc = nil
// Error otherwise
2024-02-18 10:42:21 +00:00
return mc.handleErrorPacket(data)
2024-02-18 10:42:21 +00:00
}
// NULL-bitmap, [(column-count + 7 + 2) / 8 bytes]
2024-02-18 10:42:21 +00:00
pos := 1 + (len(dest)+7+2)>>3
2024-02-18 10:42:21 +00:00
nullMask := data[1:pos]
for i := range dest {
2024-02-18 10:42:21 +00:00
// Field is NULL
2024-02-18 10:42:21 +00:00
// (byte >> bit-pos) % 2 == 1
2024-02-18 10:42:21 +00:00
if ((nullMask[(i+2)>>3] >> uint((i+2)&7)) & 1) == 1 {
2024-02-18 10:42:21 +00:00
dest[i] = nil
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
// Convert to byte-coded string
2024-02-18 10:42:21 +00:00
switch rows.rs.columns[i].fieldType {
2024-02-18 10:42:21 +00:00
case fieldTypeNULL:
2024-02-18 10:42:21 +00:00
dest[i] = nil
2024-02-18 10:42:21 +00:00
continue
// Numeric Types
2024-02-18 10:42:21 +00:00
case fieldTypeTiny:
2024-02-18 10:42:21 +00:00
if rows.rs.columns[i].flags&flagUnsigned != 0 {
2024-02-18 10:42:21 +00:00
dest[i] = int64(data[pos])
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
dest[i] = int64(int8(data[pos]))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pos++
2024-02-18 10:42:21 +00:00
continue
case fieldTypeShort, fieldTypeYear:
2024-02-18 10:42:21 +00:00
if rows.rs.columns[i].flags&flagUnsigned != 0 {
2024-02-18 10:42:21 +00:00
dest[i] = int64(binary.LittleEndian.Uint16(data[pos : pos+2]))
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
dest[i] = int64(int16(binary.LittleEndian.Uint16(data[pos : pos+2])))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pos += 2
2024-02-18 10:42:21 +00:00
continue
case fieldTypeInt24, fieldTypeLong:
2024-02-18 10:42:21 +00:00
if rows.rs.columns[i].flags&flagUnsigned != 0 {
2024-02-18 10:42:21 +00:00
dest[i] = int64(binary.LittleEndian.Uint32(data[pos : pos+4]))
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
dest[i] = int64(int32(binary.LittleEndian.Uint32(data[pos : pos+4])))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pos += 4
2024-02-18 10:42:21 +00:00
continue
case fieldTypeLongLong:
2024-02-18 10:42:21 +00:00
if rows.rs.columns[i].flags&flagUnsigned != 0 {
2024-02-18 10:42:21 +00:00
val := binary.LittleEndian.Uint64(data[pos : pos+8])
2024-02-18 10:42:21 +00:00
if val > math.MaxInt64 {
2024-02-18 10:42:21 +00:00
dest[i] = uint64ToString(val)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
dest[i] = int64(val)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
dest[i] = int64(binary.LittleEndian.Uint64(data[pos : pos+8]))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pos += 8
2024-02-18 10:42:21 +00:00
continue
case fieldTypeFloat:
2024-02-18 10:42:21 +00:00
dest[i] = math.Float32frombits(binary.LittleEndian.Uint32(data[pos : pos+4]))
2024-02-18 10:42:21 +00:00
pos += 4
2024-02-18 10:42:21 +00:00
continue
case fieldTypeDouble:
2024-02-18 10:42:21 +00:00
dest[i] = math.Float64frombits(binary.LittleEndian.Uint64(data[pos : pos+8]))
2024-02-18 10:42:21 +00:00
pos += 8
2024-02-18 10:42:21 +00:00
continue
// Length coded Binary Strings
2024-02-18 10:42:21 +00:00
case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar,
2024-02-18 10:42:21 +00:00
fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB,
2024-02-18 10:42:21 +00:00
fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB,
2024-02-18 10:42:21 +00:00
fieldTypeVarString, fieldTypeString, fieldTypeGeometry, fieldTypeJSON:
2024-02-18 10:42:21 +00:00
var isNull bool
2024-02-18 10:42:21 +00:00
var n int
2024-02-18 10:42:21 +00:00
dest[i], isNull, n, err = readLengthEncodedString(data[pos:])
2024-02-18 10:42:21 +00:00
pos += n
2024-02-18 10:42:21 +00:00
if err == nil {
2024-02-18 10:42:21 +00:00
if !isNull {
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
dest[i] = nil
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return err
case
2024-02-18 10:42:21 +00:00
fieldTypeDate, fieldTypeNewDate, // Date YYYY-MM-DD
fieldTypeTime, // Time [-][H]HH:MM:SS[.fractal]
2024-02-18 10:42:21 +00:00
fieldTypeTimestamp, fieldTypeDateTime: // Timestamp YYYY-MM-DD HH:MM:SS[.fractal]
num, isNull, n := readLengthEncodedInteger(data[pos:])
2024-02-18 10:42:21 +00:00
pos += n
switch {
2024-02-18 10:42:21 +00:00
case isNull:
2024-02-18 10:42:21 +00:00
dest[i] = nil
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
case rows.rs.columns[i].fieldType == fieldTypeTime:
2024-02-18 10:42:21 +00:00
// database/sql does not support an equivalent to TIME, return a string
2024-02-18 10:42:21 +00:00
var dstlen uint8
2024-02-18 10:42:21 +00:00
switch decimals := rows.rs.columns[i].decimals; decimals {
2024-02-18 10:42:21 +00:00
case 0x00, 0x1f:
2024-02-18 10:42:21 +00:00
dstlen = 8
2024-02-18 10:42:21 +00:00
case 1, 2, 3, 4, 5, 6:
2024-02-18 10:42:21 +00:00
dstlen = 8 + 1 + decimals
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
return fmt.Errorf(
2024-02-18 10:42:21 +00:00
"protocol error, illegal decimals value %d",
2024-02-18 10:42:21 +00:00
rows.rs.columns[i].decimals,
)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
dest[i], err = formatBinaryTime(data[pos:pos+int(num)], dstlen)
2024-02-18 10:42:21 +00:00
case rows.mc.parseTime:
2024-02-18 10:42:21 +00:00
dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc)
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
var dstlen uint8
2024-02-18 10:42:21 +00:00
if rows.rs.columns[i].fieldType == fieldTypeDate {
2024-02-18 10:42:21 +00:00
dstlen = 10
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
switch decimals := rows.rs.columns[i].decimals; decimals {
2024-02-18 10:42:21 +00:00
case 0x00, 0x1f:
2024-02-18 10:42:21 +00:00
dstlen = 19
2024-02-18 10:42:21 +00:00
case 1, 2, 3, 4, 5, 6:
2024-02-18 10:42:21 +00:00
dstlen = 19 + 1 + decimals
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
return fmt.Errorf(
2024-02-18 10:42:21 +00:00
"protocol error, illegal decimals value %d",
2024-02-18 10:42:21 +00:00
rows.rs.columns[i].decimals,
)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen)
2024-02-18 10:42:21 +00:00
}
if err == nil {
2024-02-18 10:42:21 +00:00
pos += int(num)
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
// Please report if this happens!
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
return fmt.Errorf("unknown field type %d", rows.rs.columns[i].fieldType)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
return nil
2024-02-18 10:42:21 +00:00
}