Files
2026-09-21 16:46:55 +08:00

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