264 lines
6.2 KiB
Go
264 lines
6.2 KiB
Go
package mysqlserver
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
ProtocolVersion = 10
|
|
ServerVersion = "8.0.0-mysql-tunnel"
|
|
AuthPluginNative = "mysql_native_password"
|
|
AuthPluginSHA2 = "caching_sha2_password"
|
|
|
|
MaxPacketSize = 16 << 20
|
|
)
|
|
|
|
const (
|
|
ComQuit byte = 0x01
|
|
ComInitDB byte = 0x02
|
|
ComQuery byte = 0x03
|
|
ComFieldList byte = 0x04
|
|
ComPing byte = 0x0e
|
|
ComStmtPrepare byte = 0x16
|
|
ComStmtExecute byte = 0x17
|
|
ComStmtClose byte = 0x19
|
|
ComResetConnection byte = 0x1f
|
|
)
|
|
|
|
const (
|
|
clientLongPassword uint32 = 1
|
|
clientFoundRows uint32 = 1 << 1
|
|
clientLongFlag uint32 = 1 << 2
|
|
clientConnectWithDB uint32 = 1 << 3
|
|
clientProtocol41 uint32 = 1 << 9
|
|
clientSSL uint32 = 1 << 11
|
|
clientTransactions uint32 = 1 << 13
|
|
clientSecureConnection uint32 = 1 << 15
|
|
clientMultiStatements uint32 = 1 << 16
|
|
clientMultiResults uint32 = 1 << 17
|
|
clientPSMultiResults uint32 = 1 << 18
|
|
clientPluginAuth uint32 = 1 << 19
|
|
clientConnectAttrs uint32 = 1 << 20
|
|
clientPluginAuthLenencClientData uint32 = 1 << 21
|
|
clientSessionTrack uint32 = 1 << 23
|
|
clientDeprecateEOF uint32 = 1 << 24
|
|
)
|
|
|
|
const (
|
|
serverStatusAutocommit uint16 = 0x0002
|
|
)
|
|
|
|
const (
|
|
charsetUTF8MB4 uint8 = 45
|
|
charsetBinary uint8 = 63
|
|
)
|
|
|
|
const (
|
|
typeDecimal byte = 0
|
|
typeTiny byte = 1
|
|
typeShort byte = 2
|
|
typeLong byte = 3
|
|
typeFloat byte = 4
|
|
typeDouble byte = 5
|
|
typeNull byte = 6
|
|
typeTimestamp byte = 7
|
|
typeLongLong byte = 8
|
|
typeInt24 byte = 9
|
|
typeDate byte = 10
|
|
typeTime byte = 11
|
|
typeDatetime byte = 12
|
|
typeYear byte = 13
|
|
typeJSON byte = 245
|
|
typeNewDecimal byte = 246
|
|
typeTinyBlob byte = 249
|
|
typeMediumBlob byte = 250
|
|
typeLongBlob byte = 251
|
|
typeBlob byte = 252
|
|
typeVarString byte = 253
|
|
typeString byte = 254
|
|
)
|
|
|
|
const (
|
|
flagNotNull uint16 = 1
|
|
flagPriKey uint16 = 2
|
|
flagUniqueKey uint16 = 4
|
|
flagMultipleKey uint16 = 8
|
|
flagBlob uint16 = 16
|
|
flagUnsigned uint16 = 32
|
|
flagZerofill uint16 = 64
|
|
flagBinary uint16 = 128
|
|
flagEnum uint16 = 256
|
|
flagAutoIncrement uint16 = 512
|
|
flagTimestamp uint16 = 1024
|
|
flagSet uint16 = 2048
|
|
)
|
|
|
|
const (
|
|
errAccessDenied uint16 = 1045
|
|
errUnknownCommand uint16 = 1047
|
|
errNoDB uint16 = 1046
|
|
errParseError uint16 = 1064
|
|
errUnknown uint16 = 1105
|
|
errNotSupported uint16 = 1235
|
|
)
|
|
|
|
const (
|
|
markerOK byte = 0x00
|
|
markerEOF byte = 0xfe
|
|
markerERR byte = 0xff
|
|
markerNull byte = 0xfb
|
|
)
|
|
|
|
var serverCapabilities = clientLongPassword | clientFoundRows | clientLongFlag |
|
|
clientConnectWithDB | clientProtocol41 | clientTransactions | clientSecureConnection |
|
|
clientMultiResults | clientPluginAuth | clientPluginAuthLenencClientData |
|
|
clientConnectAttrs | clientDeprecateEOF | clientSessionTrack
|
|
|
|
func appendU24(dst []byte, n uint32) []byte {
|
|
return append(dst, byte(n), byte(n>>8), byte(n>>16))
|
|
}
|
|
|
|
func appendU32(dst []byte, n uint32) []byte {
|
|
b := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(b, n)
|
|
return append(dst, b...)
|
|
}
|
|
|
|
func appendU16(dst []byte, n uint16) []byte {
|
|
return append(dst, byte(n), byte(n>>8))
|
|
}
|
|
|
|
func appendNUL(dst []byte, s string) []byte {
|
|
dst = append(dst, s...)
|
|
return append(dst, 0)
|
|
}
|
|
|
|
func appendLenencInt(dst []byte, n uint64) []byte {
|
|
switch {
|
|
case n < 251:
|
|
return append(dst, byte(n))
|
|
case n < 1<<16:
|
|
return append(dst, 0xfc, byte(n), byte(n>>8))
|
|
case n < 1<<24:
|
|
return append(dst, 0xfd, byte(n), byte(n>>8), byte(n>>16))
|
|
default:
|
|
b := make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(b, n)
|
|
return append(append(dst, 0xfe), b...)
|
|
}
|
|
}
|
|
|
|
func appendLenencString(dst []byte, s []byte) []byte {
|
|
dst = appendLenencInt(dst, uint64(len(s)))
|
|
return append(dst, s...)
|
|
}
|
|
|
|
func readU24(b []byte) uint32 {
|
|
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16
|
|
}
|
|
|
|
func readLenencInt(b []byte, off int) (uint64, int, error) {
|
|
if off >= len(b) {
|
|
return 0, off, io.ErrUnexpectedEOF
|
|
}
|
|
switch b[off] {
|
|
case 0xfb:
|
|
return 0, off + 1, fmt.Errorf("NULL lenenc")
|
|
case 0xfc:
|
|
if off+3 > len(b) {
|
|
return 0, off, io.ErrUnexpectedEOF
|
|
}
|
|
return uint64(b[off+1]) | uint64(b[off+2])<<8, off + 3, nil
|
|
case 0xfd:
|
|
if off+4 > len(b) {
|
|
return 0, off, io.ErrUnexpectedEOF
|
|
}
|
|
return uint64(b[off+1]) | uint64(b[off+2])<<8 | uint64(b[off+3])<<16, off + 4, nil
|
|
case 0xfe:
|
|
if off+9 > len(b) {
|
|
return 0, off, io.ErrUnexpectedEOF
|
|
}
|
|
return binary.LittleEndian.Uint64(b[off+1 : off+9]), off + 9, nil
|
|
default:
|
|
return uint64(b[off]), off + 1, nil
|
|
}
|
|
}
|
|
|
|
func readLenencString(b []byte, off int) ([]byte, int, error) {
|
|
n, off, err := readLenencInt(b, off)
|
|
if err != nil {
|
|
return nil, off, err
|
|
}
|
|
end := off + int(n)
|
|
if end > len(b) {
|
|
return nil, off, io.ErrUnexpectedEOF
|
|
}
|
|
return b[off:end], end, nil
|
|
}
|
|
|
|
func readNUL(b []byte, off int) (string, int, error) {
|
|
for i := off; i < len(b); i++ {
|
|
if b[i] == 0 {
|
|
return string(b[off:i]), i + 1, nil
|
|
}
|
|
}
|
|
return string(b[off:]), len(b), nil
|
|
}
|
|
|
|
func sqlState(errno uint16) string {
|
|
switch errno {
|
|
case errAccessDenied:
|
|
return "28000"
|
|
case errParseError:
|
|
return "42000"
|
|
case errNoDB:
|
|
return "3D000"
|
|
case errUnknownCommand, errNotSupported:
|
|
return "HY000"
|
|
default:
|
|
return "HY000"
|
|
}
|
|
}
|
|
|
|
func encodeOK(affected, lastInsertID uint64, status, warnings uint16) []byte {
|
|
b := []byte{markerOK}
|
|
b = appendLenencInt(b, affected)
|
|
b = appendLenencInt(b, lastInsertID)
|
|
b = appendU16(b, status)
|
|
b = appendU16(b, warnings)
|
|
return b
|
|
}
|
|
|
|
func encodeERR(errno uint16, msg string) []byte {
|
|
b := []byte{markerERR}
|
|
b = appendU16(b, errno)
|
|
b = append(b, '#')
|
|
state := sqlState(errno)
|
|
if len(state) != 5 {
|
|
state = "HY000"
|
|
}
|
|
b = append(b, state...)
|
|
b = append(b, msg...)
|
|
return b
|
|
}
|
|
|
|
func encodeEOF(warnings, status uint16) []byte {
|
|
b := []byte{markerEOF}
|
|
b = appendU16(b, warnings)
|
|
b = appendU16(b, status)
|
|
return b
|
|
}
|
|
|
|
func isNumericType(t byte) bool {
|
|
switch t {
|
|
case typeDecimal, typeTiny, typeShort, typeLong, typeFloat, typeDouble,
|
|
typeNull, typeLongLong, typeInt24, typeYear, typeNewDecimal, typeBit:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
const typeBit byte = 16
|