mirror of
https://github.com/jeremyd/ergo.git
synced 2026-08-13 00:53:05 -07:00
upgrade mysql driver
This commit is contained in:
@@ -9,7 +9,7 @@ require (
|
||||
github.com/ergochat/confusables v0.0.0-20201108231250-4ab98ab61fb1
|
||||
github.com/ergochat/go-ident v0.0.0-20230911071154-8c30606d6881
|
||||
github.com/ergochat/irc-go v0.5.0
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/go-sql-driver/mysql v1.10.0
|
||||
github.com/gofrs/flock v0.8.1
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd
|
||||
@@ -32,7 +32,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.1 // indirect
|
||||
filippo.io/edwards25519 v1.2.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
|
||||
@@ -2,6 +2,8 @@ code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48 h1:/EMHruHCFXR9
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48/go.mod h1:wN/zk7mhREp/oviagqUXY3EwuHhWyOvAdsn5Y4CzOrc=
|
||||
filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=
|
||||
filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw=
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
@@ -29,6 +31,8 @@ github.com/ergochat/websocket v1.4.2-oragono1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw=
|
||||
github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
|
||||
+4
-2
@@ -7,8 +7,10 @@ import "filippo.io/edwards25519"
|
||||
This library implements the edwards25519 elliptic curve, exposing the necessary APIs to build a wide array of higher-level primitives.
|
||||
Read the docs at [pkg.go.dev/filippo.io/edwards25519](https://pkg.go.dev/filippo.io/edwards25519).
|
||||
|
||||
The code is originally derived from Adam Langley's internal implementation in the Go standard library, and includes George Tankersley's [performance improvements](https://golang.org/cl/71950). It was then further developed by Henry de Valence for use in ristretto255, and was finally [merged back into the Go standard library](https://golang.org/cl/276272) as of Go 1.17. It now tracks the upstream codebase and extends it with additional functionality.
|
||||
The package tracks the upstream standard library package `crypto/internal/fips140/edwards25519` and extends it with additional functionality.
|
||||
|
||||
Most users don't need this package, and should instead use `crypto/ed25519` for signatures, `golang.org/x/crypto/curve25519` for Diffie-Hellman, or `github.com/gtank/ristretto255` for prime order group logic. However, for anyone currently using a fork of `crypto/internal/edwards25519`/`crypto/ed25519/internal/edwards25519` or `github.com/agl/edwards25519`, this package should be a safer, faster, and more powerful alternative.
|
||||
The code is originally derived from Adam Langley's internal implementation in the Go standard library, and includes George Tankersley's [performance improvements](https://golang.org/cl/71950). It was then further developed by Henry de Valence for use in ristretto255, and was finally [merged back into the Go standard library](https://golang.org/cl/276272) as of Go 1.17.
|
||||
|
||||
Most users don't need this package, and should instead use `crypto/ed25519` for signatures, `crypto/ecdh` for Diffie-Hellman, or `github.com/gtank/ristretto255` for prime order group logic. However, for anyone currently using a fork of the internal `edwards25519` package or of `github.com/agl/edwards25519`, this package should be a safer, faster, and more powerful alternative.
|
||||
|
||||
Since this package is meant to curb proliferation of edwards25519 implementations in the Go ecosystem, it welcomes requests for new APIs or reviewable performance improvements.
|
||||
|
||||
+3
-3
@@ -10,11 +10,11 @@
|
||||
// the curve used by the Ed25519 signature scheme.
|
||||
//
|
||||
// Most users don't need this package, and should instead use crypto/ed25519 for
|
||||
// signatures, golang.org/x/crypto/curve25519 for Diffie-Hellman, or
|
||||
// github.com/gtank/ristretto255 for prime order group logic.
|
||||
// signatures, crypto/ecdh for Diffie-Hellman, or github.com/gtank/ristretto255
|
||||
// for prime order group logic.
|
||||
//
|
||||
// However, developers who do need to interact with low-level edwards25519
|
||||
// operations can use this package, which is an extended version of
|
||||
// crypto/internal/edwards25519 from the standard library repackaged as
|
||||
// crypto/internal/fips140/edwards25519 from the standard library repackaged as
|
||||
// an importable module.
|
||||
package edwards25519
|
||||
|
||||
+59
-8
@@ -9,6 +9,7 @@ package edwards25519
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"slices"
|
||||
|
||||
"filippo.io/edwards25519/field"
|
||||
)
|
||||
@@ -100,13 +101,15 @@ func (v *Point) bytesMontgomery(buf *[32]byte) []byte {
|
||||
//
|
||||
// u = (1 + y) / (1 - y)
|
||||
//
|
||||
// where y = Y / Z.
|
||||
// where y = Y / Z and therefore
|
||||
//
|
||||
// u = (Z + Y) / (Z - Y)
|
||||
|
||||
var y, recip, u field.Element
|
||||
var n, r, u field.Element
|
||||
|
||||
y.Multiply(&v.y, y.Invert(&v.z)) // y = Y / Z
|
||||
recip.Invert(recip.Subtract(feOne, &y)) // r = 1/(1 - y)
|
||||
u.Multiply(u.Add(feOne, &y), &recip) // u = (1 + y)*r
|
||||
n.Add(&v.z, &v.y) // n = Z + Y
|
||||
r.Invert(r.Subtract(&v.z, &v.y)) // r = 1 / (Z - Y)
|
||||
u.Multiply(&n, &r) // u = n * r
|
||||
|
||||
return copyFieldElement(buf, &u)
|
||||
}
|
||||
@@ -124,7 +127,7 @@ func (v *Point) MultByCofactor(p *Point) *Point {
|
||||
return v.fromP1xP1(&result)
|
||||
}
|
||||
|
||||
// Given k > 0, set s = s**(2*i).
|
||||
// Given k > 0, set s = s**(2*k).
|
||||
func (s *Scalar) pow2k(k int) {
|
||||
for i := 0; i < k; i++ {
|
||||
s.Multiply(s, s)
|
||||
@@ -250,12 +253,14 @@ func (v *Point) MultiScalarMult(scalars []*Scalar, points []*Point) *Point {
|
||||
// between each point in the multiscalar equation.
|
||||
|
||||
// Build lookup tables for each point
|
||||
tables := make([]projLookupTable, len(points))
|
||||
tables := make([]projLookupTable, 0, 2) // avoid allocation for small sizes
|
||||
tables = slices.Grow(tables, len(points))[:len(points)]
|
||||
for i := range tables {
|
||||
tables[i].FromP3(points[i])
|
||||
}
|
||||
// Compute signed radix-16 digits for each scalar
|
||||
digits := make([][64]int8, len(scalars))
|
||||
digits := make([][64]int8, 0, 2) // avoid allocation for small sizes
|
||||
digits = slices.Grow(digits, len(scalars))[:len(scalars)]
|
||||
for i := range digits {
|
||||
digits[i] = scalars[i].signedRadix16()
|
||||
}
|
||||
@@ -348,3 +353,49 @@ func (v *Point) VarTimeMultiScalarMult(scalars []*Scalar, points []*Point) *Poin
|
||||
v.fromP2(tmp2)
|
||||
return v
|
||||
}
|
||||
|
||||
// Select sets v to a if cond == 1 and to b if cond == 0.
|
||||
func (v *Point) Select(a, b *Point, cond int) *Point {
|
||||
checkInitialized(a, b)
|
||||
v.x.Select(&a.x, &b.x, cond)
|
||||
v.y.Select(&a.y, &b.y, cond)
|
||||
v.z.Select(&a.z, &b.z, cond)
|
||||
v.t.Select(&a.t, &b.t, cond)
|
||||
return v
|
||||
}
|
||||
|
||||
// Double sets v = p + p, and returns v.
|
||||
func (v *Point) Double(p *Point) *Point {
|
||||
checkInitialized(p)
|
||||
|
||||
pp := new(projP2).FromP3(p)
|
||||
p1 := new(projP1xP1).Double(pp)
|
||||
return v.fromP1xP1(p1)
|
||||
}
|
||||
|
||||
func (v *Point) addCached(p *Point, qCached *projCached) *Point {
|
||||
result := new(projP1xP1).Add(p, qCached)
|
||||
return v.fromP1xP1(result)
|
||||
}
|
||||
|
||||
// ScalarMultSlow sets v = x * q, and returns v. It doesn't precompute a large
|
||||
// table, so it is considerably slower, but requires less memory.
|
||||
//
|
||||
// The scalar multiplication is done in constant time.
|
||||
func (v *Point) ScalarMultSlow(x *Scalar, q *Point) *Point {
|
||||
checkInitialized(q)
|
||||
|
||||
s := x.Bytes()
|
||||
qCached := new(projCached).FromP3(q)
|
||||
v.Set(NewIdentityPoint())
|
||||
t := new(Point)
|
||||
|
||||
for i := 255; i >= 0; i-- {
|
||||
v.Double(v)
|
||||
t.addCached(v, qCached)
|
||||
cond := (s[i/8] >> (i % 8)) & 1
|
||||
v.Select(t, v, int(cond))
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
+17
-17
@@ -90,11 +90,7 @@ func (v *Element) Add(a, b *Element) *Element {
|
||||
v.l2 = a.l2 + b.l2
|
||||
v.l3 = a.l3 + b.l3
|
||||
v.l4 = a.l4 + b.l4
|
||||
// Using the generic implementation here is actually faster than the
|
||||
// assembly. Probably because the body of this function is so simple that
|
||||
// the compiler can figure out better optimizations by inlining the carry
|
||||
// propagation.
|
||||
return v.carryPropagateGeneric()
|
||||
return v.carryPropagate()
|
||||
}
|
||||
|
||||
// Subtract sets v = a - b, and returns v.
|
||||
@@ -232,18 +228,22 @@ func (v *Element) bytes(out *[32]byte) []byte {
|
||||
t := *v
|
||||
t.reduce()
|
||||
|
||||
var buf [8]byte
|
||||
for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} {
|
||||
bitsOffset := i * 51
|
||||
binary.LittleEndian.PutUint64(buf[:], l<<uint(bitsOffset%8))
|
||||
for i, bb := range buf {
|
||||
off := bitsOffset/8 + i
|
||||
if off >= len(out) {
|
||||
break
|
||||
}
|
||||
out[off] |= bb
|
||||
}
|
||||
}
|
||||
// Pack five 51-bit limbs into four 64-bit words:
|
||||
//
|
||||
// 255 204 153 102 51 0
|
||||
// ├──l4──┼──l3──┼──l2──┼──l1──┼──l0──┤
|
||||
// ├───u3───┼───u2───┼───u1───┼───u0───┤
|
||||
// 256 192 128 64 0
|
||||
|
||||
u0 := t.l1<<51 | t.l0
|
||||
u1 := t.l2<<(102-64) | t.l1>>(64-51)
|
||||
u2 := t.l3<<(153-128) | t.l2>>(128-102)
|
||||
u3 := t.l4<<(204-192) | t.l3>>(192-153)
|
||||
|
||||
binary.LittleEndian.PutUint64(out[0*8:], u0)
|
||||
binary.LittleEndian.PutUint64(out[1*8:], u1)
|
||||
binary.LittleEndian.PutUint64(out[2*8:], u2)
|
||||
binary.LittleEndian.PutUint64(out[3*8:], u3)
|
||||
|
||||
return out[:]
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
// +build amd64,gc,!purego
|
||||
//go:build !purego
|
||||
|
||||
package field
|
||||
|
||||
|
||||
+111
-92
@@ -1,7 +1,6 @@
|
||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
// +build amd64,gc,!purego
|
||||
//go:build !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
@@ -17,32 +16,36 @@ TEXT ·feMul(SB), NOSPLIT, $0-24
|
||||
MOVQ DX, SI
|
||||
|
||||
// r0 += 19×a1×b4
|
||||
MOVQ 8(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
MOVQ 8(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r0 += 19×a2×b3
|
||||
MOVQ 16(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
MOVQ 16(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r0 += 19×a3×b2
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
MOVQ 24(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r0 += 19×a4×b1
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
MOVQ 32(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r1 = a0×b1
|
||||
MOVQ (CX), AX
|
||||
@@ -57,25 +60,28 @@ TEXT ·feMul(SB), NOSPLIT, $0-24
|
||||
ADCQ DX, R8
|
||||
|
||||
// r1 += 19×a2×b4
|
||||
MOVQ 16(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
MOVQ 16(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r1 += 19×a3×b3
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
MOVQ 24(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r1 += 19×a4×b2
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
MOVQ 32(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r2 = a0×b2
|
||||
MOVQ (CX), AX
|
||||
@@ -96,18 +102,20 @@ TEXT ·feMul(SB), NOSPLIT, $0-24
|
||||
ADCQ DX, R10
|
||||
|
||||
// r2 += 19×a3×b4
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
MOVQ 24(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r2 += 19×a4×b3
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
MOVQ 32(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r3 = a0×b3
|
||||
MOVQ (CX), AX
|
||||
@@ -134,11 +142,12 @@ TEXT ·feMul(SB), NOSPLIT, $0-24
|
||||
ADCQ DX, R12
|
||||
|
||||
// r3 += 19×a4×b4
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
MOVQ 32(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r4 = a0×b4
|
||||
MOVQ (CX), AX
|
||||
@@ -232,18 +241,22 @@ TEXT ·feSquare(SB), NOSPLIT, $0-16
|
||||
MOVQ DX, BX
|
||||
|
||||
// r0 += 38×l1×l4
|
||||
MOVQ 8(CX), AX
|
||||
IMUL3Q $0x26, AX, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, SI
|
||||
ADCQ DX, BX
|
||||
MOVQ 8(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, SI
|
||||
ADCQ DX, BX
|
||||
|
||||
// r0 += 38×l2×l3
|
||||
MOVQ 16(CX), AX
|
||||
IMUL3Q $0x26, AX, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, SI
|
||||
ADCQ DX, BX
|
||||
MOVQ 16(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, SI
|
||||
ADCQ DX, BX
|
||||
|
||||
// r1 = 2×l0×l1
|
||||
MOVQ (CX), AX
|
||||
@@ -253,18 +266,21 @@ TEXT ·feSquare(SB), NOSPLIT, $0-16
|
||||
MOVQ DX, DI
|
||||
|
||||
// r1 += 38×l2×l4
|
||||
MOVQ 16(CX), AX
|
||||
IMUL3Q $0x26, AX, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R8
|
||||
ADCQ DX, DI
|
||||
MOVQ 16(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R8
|
||||
ADCQ DX, DI
|
||||
|
||||
// r1 += 19×l3×l3
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, R8
|
||||
ADCQ DX, DI
|
||||
MOVQ 24(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, R8
|
||||
ADCQ DX, DI
|
||||
|
||||
// r2 = 2×l0×l2
|
||||
MOVQ (CX), AX
|
||||
@@ -280,11 +296,13 @@ TEXT ·feSquare(SB), NOSPLIT, $0-16
|
||||
ADCQ DX, R9
|
||||
|
||||
// r2 += 38×l3×l4
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x26, AX, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R10
|
||||
ADCQ DX, R9
|
||||
MOVQ 24(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R10
|
||||
ADCQ DX, R9
|
||||
|
||||
// r3 = 2×l0×l3
|
||||
MOVQ (CX), AX
|
||||
@@ -294,18 +312,19 @@ TEXT ·feSquare(SB), NOSPLIT, $0-16
|
||||
MOVQ DX, R11
|
||||
|
||||
// r3 += 2×l1×l2
|
||||
MOVQ 8(CX), AX
|
||||
IMUL3Q $0x02, AX, AX
|
||||
MULQ 16(CX)
|
||||
ADDQ AX, R12
|
||||
ADCQ DX, R11
|
||||
MOVQ 8(CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 16(CX)
|
||||
ADDQ AX, R12
|
||||
ADCQ DX, R11
|
||||
|
||||
// r3 += 19×l4×l4
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R12
|
||||
ADCQ DX, R11
|
||||
MOVQ 32(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R12
|
||||
ADCQ DX, R11
|
||||
|
||||
// r4 = 2×l0×l4
|
||||
MOVQ (CX), AX
|
||||
@@ -315,11 +334,11 @@ TEXT ·feSquare(SB), NOSPLIT, $0-16
|
||||
MOVQ DX, R13
|
||||
|
||||
// r4 += 2×l1×l3
|
||||
MOVQ 8(CX), AX
|
||||
IMUL3Q $0x02, AX, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, R14
|
||||
ADCQ DX, R13
|
||||
MOVQ 8(CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, R14
|
||||
ADCQ DX, R13
|
||||
|
||||
// r4 += l2×l2
|
||||
MOVQ 16(CX), AX
|
||||
|
||||
+1
-2
@@ -2,8 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || !gc || purego
|
||||
// +build !amd64 !gc purego
|
||||
//go:build !amd64 || purego
|
||||
|
||||
package field
|
||||
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build arm64 && gc && !purego
|
||||
// +build arm64,gc,!purego
|
||||
|
||||
package field
|
||||
|
||||
//go:noescape
|
||||
func carryPropagate(v *Element)
|
||||
|
||||
func (v *Element) carryPropagate() *Element {
|
||||
carryPropagate(v)
|
||||
return v
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build arm64 && gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// carryPropagate works exactly like carryPropagateGeneric and uses the
|
||||
// same AND, ADD, and LSR+MADD instructions emitted by the compiler, but
|
||||
// avoids loading R0-R4 twice and uses LDP and STP.
|
||||
//
|
||||
// See https://golang.org/issues/43145 for the main compiler issue.
|
||||
//
|
||||
// func carryPropagate(v *Element)
|
||||
TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8
|
||||
MOVD v+0(FP), R20
|
||||
|
||||
LDP 0(R20), (R0, R1)
|
||||
LDP 16(R20), (R2, R3)
|
||||
MOVD 32(R20), R4
|
||||
|
||||
AND $0x7ffffffffffff, R0, R10
|
||||
AND $0x7ffffffffffff, R1, R11
|
||||
AND $0x7ffffffffffff, R2, R12
|
||||
AND $0x7ffffffffffff, R3, R13
|
||||
AND $0x7ffffffffffff, R4, R14
|
||||
|
||||
ADD R0>>51, R11, R11
|
||||
ADD R1>>51, R12, R12
|
||||
ADD R2>>51, R13, R13
|
||||
ADD R3>>51, R14, R14
|
||||
// R4>>51 * 19 + R10 -> R10
|
||||
LSR $51, R4, R21
|
||||
MOVD $19, R22
|
||||
MADD R22, R10, R21, R10
|
||||
|
||||
STP (R10, R11), 0(R20)
|
||||
STP (R12, R13), 16(R20)
|
||||
MOVD R14, 32(R20)
|
||||
|
||||
RET
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !arm64 || !gc || purego
|
||||
// +build !arm64 !gc purego
|
||||
|
||||
package field
|
||||
|
||||
func (v *Element) carryPropagate() *Element {
|
||||
return v.carryPropagateGeneric()
|
||||
}
|
||||
+88
-82
@@ -12,20 +12,42 @@ type uint128 struct {
|
||||
lo, hi uint64
|
||||
}
|
||||
|
||||
// mul64 returns a * b.
|
||||
func mul64(a, b uint64) uint128 {
|
||||
// mul returns a * b.
|
||||
func mul(a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(a, b)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
// addMul64 returns v + a * b.
|
||||
func addMul64(v uint128, a, b uint64) uint128 {
|
||||
// addMul returns v + a * b.
|
||||
func addMul(v uint128, a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(a, b)
|
||||
lo, c := bits.Add64(lo, v.lo, 0)
|
||||
hi, _ = bits.Add64(hi, v.hi, c)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
// mul19 returns v * 19.
|
||||
func mul19(v uint64) uint64 {
|
||||
// Using this approach seems to yield better optimizations than *19.
|
||||
return v + (v+v<<3)<<1
|
||||
}
|
||||
|
||||
// addMul19 returns v + 19 * a * b, where a and b are at most 52 bits.
|
||||
func addMul19(v uint128, a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(mul19(a), b)
|
||||
lo, c := bits.Add64(lo, v.lo, 0)
|
||||
hi, _ = bits.Add64(hi, v.hi, c)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
// addMul38 returns v + 38 * a * b, where a and b are at most 52 bits.
|
||||
func addMul38(v uint128, a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(mul19(a), b*2)
|
||||
lo, c := bits.Add64(lo, v.lo, 0)
|
||||
hi, _ = bits.Add64(hi, v.hi, c)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits.
|
||||
func shiftRightBy51(a uint128) uint64 {
|
||||
return (a.hi << (64 - 51)) | (a.lo >> 51)
|
||||
@@ -76,45 +98,40 @@ func feMulGeneric(v, a, b *Element) {
|
||||
//
|
||||
// Finally we add up the columns into wide, overlapping limbs.
|
||||
|
||||
a1_19 := a1 * 19
|
||||
a2_19 := a2 * 19
|
||||
a3_19 := a3 * 19
|
||||
a4_19 := a4 * 19
|
||||
|
||||
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||
r0 := mul64(a0, b0)
|
||||
r0 = addMul64(r0, a1_19, b4)
|
||||
r0 = addMul64(r0, a2_19, b3)
|
||||
r0 = addMul64(r0, a3_19, b2)
|
||||
r0 = addMul64(r0, a4_19, b1)
|
||||
r0 := mul(a0, b0)
|
||||
r0 = addMul19(r0, a1, b4)
|
||||
r0 = addMul19(r0, a2, b3)
|
||||
r0 = addMul19(r0, a3, b2)
|
||||
r0 = addMul19(r0, a4, b1)
|
||||
|
||||
// r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2)
|
||||
r1 := mul64(a0, b1)
|
||||
r1 = addMul64(r1, a1, b0)
|
||||
r1 = addMul64(r1, a2_19, b4)
|
||||
r1 = addMul64(r1, a3_19, b3)
|
||||
r1 = addMul64(r1, a4_19, b2)
|
||||
r1 := mul(a0, b1)
|
||||
r1 = addMul(r1, a1, b0)
|
||||
r1 = addMul19(r1, a2, b4)
|
||||
r1 = addMul19(r1, a3, b3)
|
||||
r1 = addMul19(r1, a4, b2)
|
||||
|
||||
// r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3)
|
||||
r2 := mul64(a0, b2)
|
||||
r2 = addMul64(r2, a1, b1)
|
||||
r2 = addMul64(r2, a2, b0)
|
||||
r2 = addMul64(r2, a3_19, b4)
|
||||
r2 = addMul64(r2, a4_19, b3)
|
||||
r2 := mul(a0, b2)
|
||||
r2 = addMul(r2, a1, b1)
|
||||
r2 = addMul(r2, a2, b0)
|
||||
r2 = addMul19(r2, a3, b4)
|
||||
r2 = addMul19(r2, a4, b3)
|
||||
|
||||
// r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4
|
||||
r3 := mul64(a0, b3)
|
||||
r3 = addMul64(r3, a1, b2)
|
||||
r3 = addMul64(r3, a2, b1)
|
||||
r3 = addMul64(r3, a3, b0)
|
||||
r3 = addMul64(r3, a4_19, b4)
|
||||
r3 := mul(a0, b3)
|
||||
r3 = addMul(r3, a1, b2)
|
||||
r3 = addMul(r3, a2, b1)
|
||||
r3 = addMul(r3, a3, b0)
|
||||
r3 = addMul19(r3, a4, b4)
|
||||
|
||||
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||
r4 := mul64(a0, b4)
|
||||
r4 = addMul64(r4, a1, b3)
|
||||
r4 = addMul64(r4, a2, b2)
|
||||
r4 = addMul64(r4, a3, b1)
|
||||
r4 = addMul64(r4, a4, b0)
|
||||
r4 := mul(a0, b4)
|
||||
r4 = addMul(r4, a1, b3)
|
||||
r4 = addMul(r4, a2, b2)
|
||||
r4 = addMul(r4, a3, b1)
|
||||
r4 = addMul(r4, a4, b0)
|
||||
|
||||
// After the multiplication, we need to reduce (carry) the five coefficients
|
||||
// to obtain a result with limbs that are at most slightly larger than 2⁵¹,
|
||||
@@ -149,7 +166,7 @@ func feMulGeneric(v, a, b *Element) {
|
||||
c3 := shiftRightBy51(r3)
|
||||
c4 := shiftRightBy51(r4)
|
||||
|
||||
rr0 := r0.lo&maskLow51Bits + c4*19
|
||||
rr0 := r0.lo&maskLow51Bits + mul19(c4)
|
||||
rr1 := r1.lo&maskLow51Bits + c0
|
||||
rr2 := r2.lo&maskLow51Bits + c1
|
||||
rr3 := r3.lo&maskLow51Bits + c2
|
||||
@@ -158,8 +175,12 @@ func feMulGeneric(v, a, b *Element) {
|
||||
// Now all coefficients fit into 64-bit registers but are still too large to
|
||||
// be passed around as an Element. We therefore do one last carry chain,
|
||||
// where the carries will be small enough to fit in the wiggle room above 2⁵¹.
|
||||
*v = Element{rr0, rr1, rr2, rr3, rr4}
|
||||
v.carryPropagate()
|
||||
|
||||
v.l0 = rr0&maskLow51Bits + mul19(rr4>>51)
|
||||
v.l1 = rr1&maskLow51Bits + rr0>>51
|
||||
v.l2 = rr2&maskLow51Bits + rr1>>51
|
||||
v.l3 = rr3&maskLow51Bits + rr2>>51
|
||||
v.l4 = rr4&maskLow51Bits + rr3>>51
|
||||
}
|
||||
|
||||
func feSquareGeneric(v, a *Element) {
|
||||
@@ -190,44 +211,31 @@ func feSquareGeneric(v, a *Element) {
|
||||
// l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 =
|
||||
// --------------------------------------
|
||||
// r4 r3 r2 r1 r0
|
||||
//
|
||||
// With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with
|
||||
// only three Mul64 and four Add64, instead of five and eight.
|
||||
|
||||
l0_2 := l0 * 2
|
||||
l1_2 := l1 * 2
|
||||
|
||||
l1_38 := l1 * 38
|
||||
l2_38 := l2 * 38
|
||||
l3_38 := l3 * 38
|
||||
|
||||
l3_19 := l3 * 19
|
||||
l4_19 := l4 * 19
|
||||
|
||||
// r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3)
|
||||
r0 := mul64(l0, l0)
|
||||
r0 = addMul64(r0, l1_38, l4)
|
||||
r0 = addMul64(r0, l2_38, l3)
|
||||
r0 := mul(l0, l0)
|
||||
r0 = addMul38(r0, l1, l4)
|
||||
r0 = addMul38(r0, l2, l3)
|
||||
|
||||
// r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3
|
||||
r1 := mul64(l0_2, l1)
|
||||
r1 = addMul64(r1, l2_38, l4)
|
||||
r1 = addMul64(r1, l3_19, l3)
|
||||
r1 := mul(l0*2, l1)
|
||||
r1 = addMul38(r1, l2, l4)
|
||||
r1 = addMul19(r1, l3, l3)
|
||||
|
||||
// r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4
|
||||
r2 := mul64(l0_2, l2)
|
||||
r2 = addMul64(r2, l1, l1)
|
||||
r2 = addMul64(r2, l3_38, l4)
|
||||
r2 := mul(l0*2, l2)
|
||||
r2 = addMul(r2, l1, l1)
|
||||
r2 = addMul38(r2, l3, l4)
|
||||
|
||||
// r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4
|
||||
r3 := mul64(l0_2, l3)
|
||||
r3 = addMul64(r3, l1_2, l2)
|
||||
r3 = addMul64(r3, l4_19, l4)
|
||||
r3 := mul(l0*2, l3)
|
||||
r3 = addMul(r3, l1*2, l2)
|
||||
r3 = addMul19(r3, l4, l4)
|
||||
|
||||
// r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2
|
||||
r4 := mul64(l0_2, l4)
|
||||
r4 = addMul64(r4, l1_2, l3)
|
||||
r4 = addMul64(r4, l2, l2)
|
||||
r4 := mul(l0*2, l4)
|
||||
r4 = addMul(r4, l1*2, l3)
|
||||
r4 = addMul(r4, l2, l2)
|
||||
|
||||
c0 := shiftRightBy51(r0)
|
||||
c1 := shiftRightBy51(r1)
|
||||
@@ -235,32 +243,30 @@ func feSquareGeneric(v, a *Element) {
|
||||
c3 := shiftRightBy51(r3)
|
||||
c4 := shiftRightBy51(r4)
|
||||
|
||||
rr0 := r0.lo&maskLow51Bits + c4*19
|
||||
rr0 := r0.lo&maskLow51Bits + mul19(c4)
|
||||
rr1 := r1.lo&maskLow51Bits + c0
|
||||
rr2 := r2.lo&maskLow51Bits + c1
|
||||
rr3 := r3.lo&maskLow51Bits + c2
|
||||
rr4 := r4.lo&maskLow51Bits + c3
|
||||
|
||||
*v = Element{rr0, rr1, rr2, rr3, rr4}
|
||||
v.carryPropagate()
|
||||
v.l0 = rr0&maskLow51Bits + mul19(rr4>>51)
|
||||
v.l1 = rr1&maskLow51Bits + rr0>>51
|
||||
v.l2 = rr2&maskLow51Bits + rr1>>51
|
||||
v.l3 = rr3&maskLow51Bits + rr2>>51
|
||||
v.l4 = rr4&maskLow51Bits + rr3>>51
|
||||
}
|
||||
|
||||
// carryPropagateGeneric brings the limbs below 52 bits by applying the reduction
|
||||
// carryPropagate brings the limbs below 52 bits by applying the reduction
|
||||
// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry.
|
||||
func (v *Element) carryPropagateGeneric() *Element {
|
||||
c0 := v.l0 >> 51
|
||||
c1 := v.l1 >> 51
|
||||
c2 := v.l2 >> 51
|
||||
c3 := v.l3 >> 51
|
||||
c4 := v.l4 >> 51
|
||||
|
||||
// c4 is at most 64 - 51 = 13 bits, so c4*19 is at most 18 bits, and
|
||||
func (v *Element) carryPropagate() *Element {
|
||||
// (l4>>51) is at most 64 - 51 = 13 bits, so (l4>>51)*19 is at most 18 bits, and
|
||||
// the final l0 will be at most 52 bits. Similarly for the rest.
|
||||
v.l0 = v.l0&maskLow51Bits + c4*19
|
||||
v.l1 = v.l1&maskLow51Bits + c0
|
||||
v.l2 = v.l2&maskLow51Bits + c1
|
||||
v.l3 = v.l3&maskLow51Bits + c2
|
||||
v.l4 = v.l4&maskLow51Bits + c3
|
||||
l0 := v.l0
|
||||
v.l0 = v.l0&maskLow51Bits + mul19(v.l4>>51)
|
||||
v.l4 = v.l4&maskLow51Bits + v.l3>>51
|
||||
v.l3 = v.l3&maskLow51Bits + v.l2>>51
|
||||
v.l2 = v.l2&maskLow51Bits + v.l1>>51
|
||||
v.l1 = v.l1&maskLow51Bits + l0>>51
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <tag>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TAG="$1"
|
||||
TMPDIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$TMPDIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
command -v git >/dev/null
|
||||
command -v git-filter-repo >/dev/null
|
||||
|
||||
if [ -d "$HOME/go/.git" ]; then
|
||||
REFERENCE=(--reference "$HOME/go" --dissociate)
|
||||
else
|
||||
REFERENCE=()
|
||||
fi
|
||||
|
||||
git -c advice.detachedHead=false clone --no-checkout "${REFERENCE[@]}" \
|
||||
-b "$TAG" https://go.googlesource.com/go.git "$TMPDIR"
|
||||
|
||||
# Simplify the history graph by removing the dev.boringcrypto branches, whose
|
||||
# merges end up empty after grafting anyway. This also fixes a weird quirk
|
||||
# (maybe a git-filter-repo bug?) where only one file from an old path,
|
||||
# src/crypto/ed25519/internal/edwards25519/const.go, would still exist in the
|
||||
# filtered repo.
|
||||
git -C "$TMPDIR" replace --graft f771edd7f9 99f1bf54eb
|
||||
git -C "$TMPDIR" replace --graft 109c13b64f c2f96e686f
|
||||
git -C "$TMPDIR" replace --graft aa4da4f189 912f075047
|
||||
|
||||
git -C "$TMPDIR" filter-repo --force \
|
||||
--paths-from-file /dev/stdin \
|
||||
--prune-empty always \
|
||||
--prune-degenerate always \
|
||||
--tag-callback 'tag.skip()' <<'EOF'
|
||||
src/crypto/internal/fips140/edwards25519
|
||||
src/crypto/internal/edwards25519
|
||||
src/crypto/ed25519/internal/edwards25519
|
||||
EOF
|
||||
|
||||
git fetch "$TMPDIR"
|
||||
git update-ref "refs/heads/upstream/$TAG" FETCH_HEAD
|
||||
|
||||
echo
|
||||
echo "Fetched upstream history up to $TAG. Merge with:"
|
||||
echo -e "\tgit merge --no-ff --no-commit --allow-unrelated-histories upstream/$TAG"
|
||||
+18
-9
@@ -7,6 +7,7 @@ package edwards25519
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// A Scalar is an integer modulo
|
||||
@@ -179,15 +180,23 @@ func isReduced(s []byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
switch {
|
||||
case s[i] > scalarMinusOneBytes[i]:
|
||||
return false
|
||||
case s[i] < scalarMinusOneBytes[i]:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
s0 := binary.LittleEndian.Uint64(s[:8])
|
||||
s1 := binary.LittleEndian.Uint64(s[8:16])
|
||||
s2 := binary.LittleEndian.Uint64(s[16:24])
|
||||
s3 := binary.LittleEndian.Uint64(s[24:])
|
||||
|
||||
l0 := binary.LittleEndian.Uint64(scalarMinusOneBytes[:8])
|
||||
l1 := binary.LittleEndian.Uint64(scalarMinusOneBytes[8:16])
|
||||
l2 := binary.LittleEndian.Uint64(scalarMinusOneBytes[16:24])
|
||||
l3 := binary.LittleEndian.Uint64(scalarMinusOneBytes[24:])
|
||||
|
||||
// Do a constant time subtraction chain scalarMinusOneBytes - s. If there is
|
||||
// a borrow at the end, then s > scalarMinusOneBytes.
|
||||
_, b := bits.Sub64(l0, s0, 0)
|
||||
_, b = bits.Sub64(l1, s1, b)
|
||||
_, b = bits.Sub64(l2, s2, b)
|
||||
_, b = bits.Sub64(l3, s3, b)
|
||||
return b == 0
|
||||
}
|
||||
|
||||
// SetBytesWithClamping applies the buffer pruning described in RFC 8032,
|
||||
|
||||
+1
-3
@@ -4,9 +4,7 @@
|
||||
|
||||
package edwards25519
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
)
|
||||
import "crypto/subtle"
|
||||
|
||||
// A dynamic lookup table for variable-base, constant-time scalar muls.
|
||||
type projLookupTable struct {
|
||||
|
||||
+7
-3
@@ -18,8 +18,8 @@ Alex Snast <alexsn at fb.com>
|
||||
Alexey Palazhchenko <alexey.palazhchenko at gmail.com>
|
||||
Andrew Reid <andrew.reid at tixtrack.com>
|
||||
Animesh Ray <mail.rayanimesh at gmail.com>
|
||||
Arne Hormann <arnehormann at gmail.com>
|
||||
Ariel Mashraki <ariel at mashraki.co.il>
|
||||
Arne Hormann <arnehormann at gmail.com>
|
||||
Artur Melanchyk <artur.melanchyk@gmail.com>
|
||||
Asta Xie <xiemengjun at gmail.com>
|
||||
B Lamarche <blam413 at gmail.com>
|
||||
@@ -38,6 +38,7 @@ Daniel Montoya <dsmontoyam at gmail.com>
|
||||
Daniel Nichter <nil at codenode.com>
|
||||
Daniël van Eeden <git at myname.nl>
|
||||
Dave Protasowski <dprotaso at gmail.com>
|
||||
Demouth <yuya at demouth.net>
|
||||
Diego Dupin <diego.dupin at gmail.com>
|
||||
Dirkjan Bussink <d.bussink at gmail.com>
|
||||
DisposaBoy <disposaboy at dby.me>
|
||||
@@ -66,6 +67,7 @@ Jeff Hodges <jeff at somethingsimilar.com>
|
||||
Jeffrey Charles <jeffreycharles at gmail.com>
|
||||
Jennifer Purevsuren <jennifer at dolthub.com>
|
||||
Jerome Meyer <jxmeyer at gmail.com>
|
||||
Jiabin Zhang <jiabin.z at qq.com>
|
||||
Jiajia Zhong <zhong2plus at gmail.com>
|
||||
Jian Zhen <zhenjl at gmail.com>
|
||||
Joe Mann <contact at joemann.co.uk>
|
||||
@@ -85,10 +87,12 @@ Linh Tran Tuan <linhduonggnu at gmail.com>
|
||||
Lion Yang <lion at aosc.xyz>
|
||||
Luca Looz <luca.looz92 at gmail.com>
|
||||
Lucas Liu <extrafliu at gmail.com>
|
||||
Lunny Xiao <xiaolunwen at gmail.com>
|
||||
Luke Scott <luke at webconnex.com>
|
||||
Lunny Xiao <xiaolunwen at gmail.com>
|
||||
Maciej Zimnoch <maciej.zimnoch at codilime.com>
|
||||
Michael Woolnough <michael.woolnough at gmail.com>
|
||||
Minh Quang <minhquang4334 at gmail.com>
|
||||
Morgan Tocker <tocker at gmail.com>
|
||||
Nao Yokotsuka <yokotukanao at gmail.com>
|
||||
Nathanial Murphy <nathanial.murphy at gmail.com>
|
||||
Nicola Peduzzi <thenikso at gmail.com>
|
||||
@@ -99,7 +103,6 @@ Paul Bonser <misterpib at gmail.com>
|
||||
Paulius Lozys <pauliuslozys at gmail.com>
|
||||
Peter Schultz <peter.schultz at classmarkets.com>
|
||||
Phil Porada <philporada at gmail.com>
|
||||
Minh Quang <minhquang4334 at gmail.com>
|
||||
Rebecca Chin <rchin at pivotal.io>
|
||||
Reed Allman <rdallman10 at gmail.com>
|
||||
Richard Wilkes <wilkes at me.com>
|
||||
@@ -134,6 +137,7 @@ Ziheng Lyu <zihenglv at gmail.com>
|
||||
# Organizations
|
||||
|
||||
Barracuda Networks, Inc.
|
||||
Block, Inc.
|
||||
Counting Ltd.
|
||||
Defined Networking Inc.
|
||||
DigitalOcean Inc.
|
||||
|
||||
+16
-3
@@ -1,13 +1,26 @@
|
||||
# Changelog
|
||||
|
||||
## v1.10.0 (2026-04-28)
|
||||
|
||||
* Fix `getSystemVar("max_allowed_packet")` potentially returned wrong value. (#1754)
|
||||
This affects only when `maxAllowedPacket=0` is set.
|
||||
|
||||
* Bump filippo.io/edwards25519 from 1.1.1 to 1.2.0. (#1756)
|
||||
While older versions have reported CVEs, they do not affect go-mysql.
|
||||
|
||||
* Update Go versions to 1.24-1.26. (#1763)
|
||||
|
||||
* Enhance interpolateParams to correctly handle placeholders. (#1732)
|
||||
The question mark (?) within strings and comments will no longer be treated as a placeholder.
|
||||
|
||||
|
||||
## v1.9.3 (2025-06-13)
|
||||
|
||||
* `tx.Commit()` and `tx.Rollback()` returned `ErrInvalidConn` always.
|
||||
Now they return cached real error if present. (#1690)
|
||||
|
||||
* Optimize reading small resultsets to fix performance regression
|
||||
introduced by compression protocol support. (#1707)
|
||||
|
||||
* Optimize reading small result sets to fix a performance regression
|
||||
introduced by compression protocol support. (`#1707`)
|
||||
* Fix `db.Ping()` on compressed connection. (#1723)
|
||||
|
||||
|
||||
|
||||
+5
-2
@@ -1,5 +1,8 @@
|
||||
# Go-MySQL-Driver
|
||||
|
||||
[](https://deepwiki.com/go-sql-driver/mysql)
|
||||
|
||||
|
||||
A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) package
|
||||
|
||||

|
||||
@@ -42,8 +45,8 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
|
||||
|
||||
## Requirements
|
||||
|
||||
* Go 1.21 or higher. We aim to support the 3 latest versions of Go.
|
||||
* MySQL (5.7+) and MariaDB (10.5+) are supported.
|
||||
* Go 1.24 or higher. We aim to support the 3 latest versions of Go.
|
||||
* MySQL (5.7+) and MariaDB (10.5+) are supported by maintainers.
|
||||
* [TiDB](https://github.com/pingcap/tidb) is supported by PingCAP.
|
||||
* Do not ask questions about TiDB in our issue tracker or forum.
|
||||
* [Document](https://docs.pingcap.com/tidb/v6.1/dev-guide-sample-application-golang)
|
||||
|
||||
+1
-1
@@ -305,7 +305,7 @@ func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
|
||||
if !mc.cfg.AllowNativePasswords {
|
||||
return nil, ErrNativePassword
|
||||
}
|
||||
// https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/8.4.5/page_protocol_connection_phase_authentication_methods_native_password_authentication.html
|
||||
// Native password authentication only need and will need 20-byte challenge.
|
||||
authResp := scramblePassword(authData[:20], mc.cfg.Passwd)
|
||||
return authResp, nil
|
||||
|
||||
-1
@@ -7,7 +7,6 @@
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
//go:build linux || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || illumos
|
||||
// +build linux darwin dragonfly freebsd netbsd openbsd solaris illumos
|
||||
|
||||
package mysql
|
||||
|
||||
|
||||
-1
@@ -7,7 +7,6 @@
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
//go:build !linux && !darwin && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !illumos
|
||||
// +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!illumos
|
||||
|
||||
package mysql
|
||||
|
||||
|
||||
+185
-90
@@ -33,7 +33,8 @@ type mysqlConn struct {
|
||||
connector *connector
|
||||
maxAllowedPacket int
|
||||
maxWriteSize int
|
||||
flags clientFlag
|
||||
capabilities capabilityFlag
|
||||
extCapabilities extendedCapabilityFlag
|
||||
status statusFlag
|
||||
sequence uint8
|
||||
compressSequence uint8
|
||||
@@ -171,7 +172,7 @@ func (mc *mysqlConn) close() {
|
||||
}
|
||||
|
||||
// Closes the network connection and unsets internal variables. Do not call this
|
||||
// function after successfully authentication, call Close instead. This function
|
||||
// function after successful authentication, call Close instead. This function
|
||||
// is called before auth or on auth failure because MySQL will have already
|
||||
// closed the network connection.
|
||||
func (mc *mysqlConn) cleanup() {
|
||||
@@ -223,13 +224,21 @@ func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
|
||||
columnCount, err := stmt.readPrepareResultPacket()
|
||||
if err == nil {
|
||||
if stmt.paramCount > 0 {
|
||||
if err = mc.readUntilEOF(); err != nil {
|
||||
if err = mc.skipColumns(stmt.paramCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if columnCount > 0 {
|
||||
err = mc.readUntilEOF()
|
||||
if mc.extCapabilities&clientCacheMetadata != 0 {
|
||||
if stmt.columns, err = mc.readColumns(int(columnCount), nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err = mc.skipColumns(int(columnCount)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,100 +246,184 @@ func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (string, error) {
|
||||
// Number of ? should be same to len(args)
|
||||
if strings.Count(query, "?") != len(args) {
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
noBackslashEscapes := (mc.status & statusNoBackslashEscapes) != 0
|
||||
const (
|
||||
stateNormal = iota
|
||||
stateString
|
||||
stateEscape
|
||||
stateEOLComment
|
||||
stateSlashStarComment
|
||||
stateBacktick
|
||||
)
|
||||
|
||||
const (
|
||||
QUOTE_BYTE = byte('\'')
|
||||
DBL_QUOTE_BYTE = byte('"')
|
||||
BACKSLASH_BYTE = byte('\\')
|
||||
QUESTION_MARK_BYTE = byte('?')
|
||||
SLASH_BYTE = byte('/')
|
||||
STAR_BYTE = byte('*')
|
||||
HASH_BYTE = byte('#')
|
||||
MINUS_BYTE = byte('-')
|
||||
LINE_FEED_BYTE = byte('\n')
|
||||
BACKTICK_BYTE = byte('`')
|
||||
)
|
||||
|
||||
buf, err := mc.buf.takeCompleteBuffer()
|
||||
if err != nil {
|
||||
// can not take the buffer. Something must be wrong with the connection
|
||||
mc.cleanup()
|
||||
// interpolateParams would be called before sending any query.
|
||||
// So its safe to retry.
|
||||
return "", driver.ErrBadConn
|
||||
}
|
||||
buf = buf[:0]
|
||||
state := stateNormal
|
||||
singleQuotes := false
|
||||
lastChar := byte(0)
|
||||
argPos := 0
|
||||
lenQuery := len(query)
|
||||
lastIdx := 0
|
||||
|
||||
for i := 0; i < len(query); i++ {
|
||||
q := strings.IndexByte(query[i:], '?')
|
||||
if q == -1 {
|
||||
buf = append(buf, query[i:]...)
|
||||
break
|
||||
}
|
||||
buf = append(buf, query[i:i+q]...)
|
||||
i += q
|
||||
|
||||
arg := args[argPos]
|
||||
argPos++
|
||||
|
||||
if arg == nil {
|
||||
buf = append(buf, "NULL"...)
|
||||
for i := range lenQuery {
|
||||
currentChar := query[i]
|
||||
if state == stateEscape && !((currentChar == QUOTE_BYTE && singleQuotes) || (currentChar == DBL_QUOTE_BYTE && !singleQuotes)) {
|
||||
state = stateString
|
||||
lastChar = currentChar
|
||||
continue
|
||||
}
|
||||
|
||||
switch v := arg.(type) {
|
||||
case int64:
|
||||
buf = strconv.AppendInt(buf, v, 10)
|
||||
case uint64:
|
||||
// Handle uint64 explicitly because our custom ConvertValue emits unsigned values
|
||||
buf = strconv.AppendUint(buf, v, 10)
|
||||
case float64:
|
||||
buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
|
||||
case bool:
|
||||
if v {
|
||||
buf = append(buf, '1')
|
||||
} else {
|
||||
buf = append(buf, '0')
|
||||
switch currentChar {
|
||||
case STAR_BYTE:
|
||||
if state == stateNormal && lastChar == SLASH_BYTE {
|
||||
state = stateSlashStarComment
|
||||
}
|
||||
case time.Time:
|
||||
if v.IsZero() {
|
||||
buf = append(buf, "'0000-00-00'"...)
|
||||
} else {
|
||||
buf = append(buf, '\'')
|
||||
buf, err = appendDateTime(buf, v.In(mc.cfg.Loc), mc.cfg.timeTruncate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
case SLASH_BYTE:
|
||||
if state == stateSlashStarComment && lastChar == STAR_BYTE {
|
||||
state = stateNormal
|
||||
// Clear lastChar so the '/' that closed the comment isn't
|
||||
// reused to start a new comment with a following '*'.
|
||||
lastChar = 0
|
||||
continue
|
||||
}
|
||||
case json.RawMessage:
|
||||
buf = append(buf, '\'')
|
||||
if mc.status&statusNoBackslashEscapes == 0 {
|
||||
buf = escapeBytesBackslash(buf, v)
|
||||
} else {
|
||||
buf = escapeBytesQuotes(buf, v)
|
||||
case HASH_BYTE:
|
||||
if state == stateNormal {
|
||||
state = stateEOLComment
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
case []byte:
|
||||
if v == nil {
|
||||
buf = append(buf, "NULL"...)
|
||||
} else {
|
||||
buf = append(buf, "_binary'"...)
|
||||
if mc.status&statusNoBackslashEscapes == 0 {
|
||||
buf = escapeBytesBackslash(buf, v)
|
||||
case MINUS_BYTE:
|
||||
if state == stateNormal && lastChar == MINUS_BYTE {
|
||||
// -- only starts a comment if followed by whitespace or control char
|
||||
if i+1 < lenQuery {
|
||||
nextChar := query[i+1]
|
||||
if nextChar == ' ' || nextChar == '\t' || nextChar == '\n' || nextChar == '\r' {
|
||||
state = stateEOLComment
|
||||
}
|
||||
} else {
|
||||
buf = escapeBytesQuotes(buf, v)
|
||||
state = stateEOLComment
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
}
|
||||
case string:
|
||||
buf = append(buf, '\'')
|
||||
if mc.status&statusNoBackslashEscapes == 0 {
|
||||
buf = escapeStringBackslash(buf, v)
|
||||
} else {
|
||||
buf = escapeStringQuotes(buf, v)
|
||||
case LINE_FEED_BYTE:
|
||||
if state == stateEOLComment {
|
||||
state = stateNormal
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
default:
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
case DBL_QUOTE_BYTE:
|
||||
if state == stateNormal {
|
||||
state = stateString
|
||||
singleQuotes = false
|
||||
} else if state == stateString && !singleQuotes {
|
||||
state = stateNormal
|
||||
} else if state == stateEscape {
|
||||
state = stateString
|
||||
}
|
||||
case QUOTE_BYTE:
|
||||
if state == stateNormal {
|
||||
state = stateString
|
||||
singleQuotes = true
|
||||
} else if state == stateString && singleQuotes {
|
||||
state = stateNormal
|
||||
} else if state == stateEscape {
|
||||
state = stateString
|
||||
}
|
||||
case BACKSLASH_BYTE:
|
||||
if state == stateString && !noBackslashEscapes {
|
||||
state = stateEscape
|
||||
}
|
||||
case QUESTION_MARK_BYTE:
|
||||
if state == stateNormal {
|
||||
if argPos >= len(args) {
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
buf = append(buf, query[lastIdx:i]...)
|
||||
arg := args[argPos]
|
||||
argPos++
|
||||
|
||||
if len(buf)+4 > mc.maxAllowedPacket {
|
||||
return "", driver.ErrSkip
|
||||
if arg == nil {
|
||||
buf = append(buf, "NULL"...)
|
||||
lastIdx = i + 1
|
||||
break
|
||||
}
|
||||
|
||||
switch v := arg.(type) {
|
||||
case int64:
|
||||
buf = strconv.AppendInt(buf, v, 10)
|
||||
case uint64:
|
||||
buf = strconv.AppendUint(buf, v, 10)
|
||||
case float64:
|
||||
buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
|
||||
case bool:
|
||||
if v {
|
||||
buf = append(buf, '1')
|
||||
} else {
|
||||
buf = append(buf, '0')
|
||||
}
|
||||
case time.Time:
|
||||
if v.IsZero() {
|
||||
buf = append(buf, "'0000-00-00'"...)
|
||||
} else {
|
||||
buf = append(buf, '\'')
|
||||
buf, err = appendDateTime(buf, v.In(mc.cfg.Loc), mc.cfg.timeTruncate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
}
|
||||
case json.RawMessage:
|
||||
if noBackslashEscapes {
|
||||
buf = escapeBytesQuotes(buf, v, false)
|
||||
} else {
|
||||
buf = escapeBytesBackslash(buf, v, false)
|
||||
}
|
||||
case []byte:
|
||||
if v == nil {
|
||||
buf = append(buf, "NULL"...)
|
||||
} else {
|
||||
if noBackslashEscapes {
|
||||
buf = escapeBytesQuotes(buf, v, true)
|
||||
} else {
|
||||
buf = escapeBytesBackslash(buf, v, true)
|
||||
}
|
||||
}
|
||||
case string:
|
||||
if noBackslashEscapes {
|
||||
buf = escapeStringQuotes(buf, v)
|
||||
} else {
|
||||
buf = escapeStringBackslash(buf, v)
|
||||
}
|
||||
default:
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
|
||||
if len(buf)+4 > mc.maxAllowedPacket {
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
lastIdx = i + 1
|
||||
}
|
||||
case BACKTICK_BYTE:
|
||||
if state == stateBacktick {
|
||||
state = stateNormal
|
||||
} else if state == stateNormal {
|
||||
state = stateBacktick
|
||||
}
|
||||
}
|
||||
lastChar = currentChar
|
||||
}
|
||||
buf = append(buf, query[lastIdx:]...)
|
||||
if argPos != len(args) {
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
@@ -370,19 +463,19 @@ func (mc *mysqlConn) exec(query string) error {
|
||||
}
|
||||
|
||||
// Read Result
|
||||
resLen, err := handleOk.readResultSetHeaderPacket()
|
||||
resLen, _, err := handleOk.readResultSetHeaderPacket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resLen > 0 {
|
||||
// columns
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
if err := mc.skipColumns(resLen); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// rows
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
if err := mc.skipRows(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -419,7 +512,7 @@ func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error)
|
||||
|
||||
// Read Result
|
||||
var resLen int
|
||||
resLen, err = handleOk.readResultSetHeaderPacket()
|
||||
resLen, _, err = handleOk.readResultSetHeaderPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -439,21 +532,20 @@ func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error)
|
||||
}
|
||||
|
||||
// Columns
|
||||
rows.rs.columns, err = mc.readColumns(resLen)
|
||||
rows.rs.columns, err = mc.readColumns(resLen, nil)
|
||||
return rows, err
|
||||
}
|
||||
|
||||
// Gets the value of the given MySQL System Variable
|
||||
// The returned byte slice is only valid until the next read
|
||||
func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
|
||||
func (mc *mysqlConn) getSystemVar(name string) (string, error) {
|
||||
// Send command
|
||||
handleOk := mc.clearResult()
|
||||
if err := mc.writeCommandPacketStr(comQuery, "SELECT @@"+name); err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Read Result
|
||||
resLen, err := handleOk.readResultSetHeaderPacket()
|
||||
resLen, _, err := handleOk.readResultSetHeaderPacket()
|
||||
if err == nil {
|
||||
rows := new(textRows)
|
||||
rows.mc = mc
|
||||
@@ -461,17 +553,20 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
|
||||
|
||||
if resLen > 0 {
|
||||
// Columns
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
return nil, err
|
||||
if err := mc.skipColumns(resLen); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
dest := make([]driver.Value, resLen)
|
||||
if err = rows.readRow(dest); err == nil {
|
||||
return dest[0].([]byte), mc.readUntilEOF()
|
||||
// Convert to string before skipRows, which may
|
||||
// overwrite the read buffer that dest[0] points into.
|
||||
val := string(dest[0].([]byte))
|
||||
return val, mc.skipRows()
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
// cancel is called when the query has canceled.
|
||||
|
||||
+7
-5
@@ -42,7 +42,7 @@ func encodeConnectionAttributes(cfg *Config) string {
|
||||
}
|
||||
|
||||
// user-defined connection attributes
|
||||
for _, connAttr := range strings.Split(cfg.ConnectionAttributes, ",") {
|
||||
for connAttr := range strings.SplitSeq(cfg.ConnectionAttributes, ",") {
|
||||
k, v, found := strings.Cut(connAttr, ":")
|
||||
if !found {
|
||||
continue
|
||||
@@ -131,7 +131,7 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
mc.buf = newBuffer()
|
||||
|
||||
// Reading Handshake Initialization Packet
|
||||
authData, plugin, err := mc.readHandshakePacket()
|
||||
authData, serverCapabilities, serverExtCapabilities, plugin, err := mc.readHandshakePacket()
|
||||
if err != nil {
|
||||
mc.cleanup()
|
||||
return nil, err
|
||||
@@ -153,6 +153,7 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
mc.initCapabilities(serverCapabilities, serverExtCapabilities, mc.cfg)
|
||||
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
|
||||
mc.cleanup()
|
||||
return nil, err
|
||||
@@ -161,13 +162,14 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
// Handle response to auth packet, switch methods if possible
|
||||
if err = mc.handleAuthResult(authData, plugin); err != nil {
|
||||
// Authentication failed and MySQL has already closed the connection
|
||||
// (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
|
||||
// (https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase.html#sect_protocol_connection_phase_fast_path_fails).
|
||||
// Do not send COM_QUIT, just cleanup and return the error.
|
||||
mc.cleanup()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if mc.cfg.compress && mc.flags&clientCompress == clientCompress {
|
||||
// compression is enabled after auth, not right after sending handshake response.
|
||||
if mc.capabilities&clientCompress > 0 {
|
||||
mc.compress = true
|
||||
mc.compIO = newCompIO(mc)
|
||||
}
|
||||
@@ -180,7 +182,7 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
mc.Close()
|
||||
return nil, err
|
||||
}
|
||||
n, err := strconv.Atoi(string(maxap))
|
||||
n, err := strconv.Atoi(maxap)
|
||||
if err != nil {
|
||||
mc.Close()
|
||||
return nil, fmt.Errorf("invalid max_allowed_packet value (%q): %w", maxap, err)
|
||||
|
||||
+17
-4
@@ -32,7 +32,7 @@ const (
|
||||
)
|
||||
|
||||
// MySQL constants documentation:
|
||||
// http://dev.mysql.com/doc/internals/en/client-server-protocol.html
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_PROTOCOL.html
|
||||
|
||||
const (
|
||||
iOK byte = 0x00
|
||||
@@ -42,11 +42,12 @@ const (
|
||||
iERR byte = 0xff
|
||||
)
|
||||
|
||||
// https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags
|
||||
type clientFlag uint32
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/group__group__cs__capabilities__flags.html
|
||||
// https://mariadb.com/kb/en/connection/#capabilities
|
||||
type capabilityFlag uint32
|
||||
|
||||
const (
|
||||
clientLongPassword clientFlag = 1 << iota
|
||||
clientMySQL capabilityFlag = 1 << iota
|
||||
clientFoundRows
|
||||
clientLongFlag
|
||||
clientConnectWithDB
|
||||
@@ -73,6 +74,18 @@ const (
|
||||
clientDeprecateEOF
|
||||
)
|
||||
|
||||
// https://mariadb.com/kb/en/connection/#capabilities
|
||||
type extendedCapabilityFlag uint32
|
||||
|
||||
const (
|
||||
progressIndicator extendedCapabilityFlag = 1 << iota
|
||||
clientComMulti
|
||||
clientStmtBulkOperations
|
||||
clientExtendedMetadata
|
||||
clientCacheMetadata
|
||||
clientUnitBulkResult
|
||||
)
|
||||
|
||||
const (
|
||||
comQuit byte = iota + 1
|
||||
comInitDB
|
||||
|
||||
+4
-5
@@ -15,6 +15,7 @@ import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/url"
|
||||
@@ -157,9 +158,7 @@ func (cfg *Config) Clone() *Config {
|
||||
}
|
||||
if len(cp.Params) > 0 {
|
||||
cp.Params = make(map[string]string, len(cfg.Params))
|
||||
for k, v := range cfg.Params {
|
||||
cp.Params[k] = v
|
||||
}
|
||||
maps.Copy(cp.Params, cfg.Params)
|
||||
}
|
||||
if cfg.pubKey != nil {
|
||||
cp.pubKey = &rsa.PublicKey{
|
||||
@@ -414,7 +413,7 @@ func ParseDSN(dsn string) (cfg *Config, err error) {
|
||||
if dsn[j] == '@' {
|
||||
// username[:password]
|
||||
// Find the first ':' in dsn[:j]
|
||||
for k = 0; k < j; k++ {
|
||||
for k = 0; k < j; k++ { // We cannot use k = range j here, because we use dsn[:k] below
|
||||
if dsn[k] == ':' {
|
||||
cfg.Passwd = dsn[k+1 : j]
|
||||
break
|
||||
@@ -477,7 +476,7 @@ func ParseDSN(dsn string) (cfg *Config, err error) {
|
||||
// parseDSNParams parses the DSN "query string"
|
||||
// Values must be url.QueryEscape'ed
|
||||
func parseDSNParams(cfg *Config, params string) (err error) {
|
||||
for _, v := range strings.Split(params, "&") {
|
||||
for v := range strings.SplitSeq(params, "&") {
|
||||
key, value, found := strings.Cut(v, "=")
|
||||
if !found {
|
||||
continue
|
||||
|
||||
+21
-17
@@ -120,23 +120,24 @@ func (mf *mysqlField) typeDatabaseName() string {
|
||||
}
|
||||
|
||||
var (
|
||||
scanTypeFloat32 = reflect.TypeOf(float32(0))
|
||||
scanTypeFloat64 = reflect.TypeOf(float64(0))
|
||||
scanTypeInt8 = reflect.TypeOf(int8(0))
|
||||
scanTypeInt16 = reflect.TypeOf(int16(0))
|
||||
scanTypeInt32 = reflect.TypeOf(int32(0))
|
||||
scanTypeInt64 = reflect.TypeOf(int64(0))
|
||||
scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{})
|
||||
scanTypeNullInt = reflect.TypeOf(sql.NullInt64{})
|
||||
scanTypeNullTime = reflect.TypeOf(sql.NullTime{})
|
||||
scanTypeUint8 = reflect.TypeOf(uint8(0))
|
||||
scanTypeUint16 = reflect.TypeOf(uint16(0))
|
||||
scanTypeUint32 = reflect.TypeOf(uint32(0))
|
||||
scanTypeUint64 = reflect.TypeOf(uint64(0))
|
||||
scanTypeString = reflect.TypeOf("")
|
||||
scanTypeNullString = reflect.TypeOf(sql.NullString{})
|
||||
scanTypeBytes = reflect.TypeOf([]byte{})
|
||||
scanTypeUnknown = reflect.TypeOf(new(any))
|
||||
scanTypeFloat32 = reflect.TypeFor[float32]()
|
||||
scanTypeFloat64 = reflect.TypeFor[float64]()
|
||||
scanTypeInt8 = reflect.TypeFor[int8]()
|
||||
scanTypeInt16 = reflect.TypeFor[int16]()
|
||||
scanTypeInt32 = reflect.TypeFor[int32]()
|
||||
scanTypeInt64 = reflect.TypeFor[int64]()
|
||||
scanTypeNullFloat = reflect.TypeFor[sql.NullFloat64]()
|
||||
scanTypeNullInt = reflect.TypeFor[sql.NullInt64]()
|
||||
scanTypeNullUint = reflect.TypeFor[sql.Null[uint64]]()
|
||||
scanTypeNullTime = reflect.TypeFor[sql.NullTime]()
|
||||
scanTypeUint8 = reflect.TypeFor[uint8]()
|
||||
scanTypeUint16 = reflect.TypeFor[uint16]()
|
||||
scanTypeUint32 = reflect.TypeFor[uint32]()
|
||||
scanTypeUint64 = reflect.TypeFor[uint64]()
|
||||
scanTypeString = reflect.TypeFor[string]()
|
||||
scanTypeNullString = reflect.TypeFor[sql.NullString]()
|
||||
scanTypeBytes = reflect.TypeFor[[]byte]()
|
||||
scanTypeUnknown = reflect.TypeFor[*any]()
|
||||
)
|
||||
|
||||
type mysqlField struct {
|
||||
@@ -185,6 +186,9 @@ func (mf *mysqlField) scanType() reflect.Type {
|
||||
}
|
||||
return scanTypeInt64
|
||||
}
|
||||
if mf.flags&flagUnsigned != 0 {
|
||||
return scanTypeNullUint
|
||||
}
|
||||
return scanTypeNullInt
|
||||
|
||||
case fieldTypeFloat:
|
||||
|
||||
+1
-4
@@ -95,10 +95,7 @@ const defaultPacketSize = 16 * 1024 // 16KB is small enough for disk readahead a
|
||||
|
||||
func (mc *okHandler) handleInFileRequest(name string) (err error) {
|
||||
var rdr io.Reader
|
||||
packetSize := defaultPacketSize
|
||||
if mc.maxWriteSize < packetSize {
|
||||
packetSize = mc.maxWriteSize
|
||||
}
|
||||
packetSize := min(mc.maxWriteSize, defaultPacketSize)
|
||||
|
||||
if idx := strings.Index(name, "Reader::"); idx == 0 || (idx > 0 && name[idx-1] == '/') { // io.Reader
|
||||
// The server might return an an absolute path. See issue #355.
|
||||
|
||||
+205
-138
@@ -179,20 +179,22 @@ func (mc *mysqlConn) writePacket(data []byte) error {
|
||||
******************************************************************************/
|
||||
|
||||
// Handshake Initialization Packet
|
||||
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake
|
||||
func (mc *mysqlConn) readHandshakePacket() (data []byte, plugin string, err error) {
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_handshake_v10.html
|
||||
// https://mariadb.com/kb/en/connection/#initial-handshake-packet
|
||||
func (mc *mysqlConn) readHandshakePacket() (data []byte, capabilities capabilityFlag, extendedCapabilities extendedCapabilityFlag, plugin string, err error) {
|
||||
data, err = mc.readPacket()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if data[0] == iERR {
|
||||
return nil, "", mc.handleErrorPacket(data)
|
||||
err = mc.handleErrorPacket(data)
|
||||
return
|
||||
}
|
||||
|
||||
// protocol version [1 byte]
|
||||
if data[0] < minProtocolVersion {
|
||||
return nil, "", fmt.Errorf(
|
||||
return nil, 0, 0, "", fmt.Errorf(
|
||||
"unsupported protocol version %d. Version %d or higher is required",
|
||||
data[0],
|
||||
minProtocolVersion,
|
||||
@@ -210,15 +212,15 @@ func (mc *mysqlConn) readHandshakePacket() (data []byte, plugin string, err erro
|
||||
pos += 8 + 1
|
||||
|
||||
// capability flags (lower 2 bytes) [2 bytes]
|
||||
mc.flags = clientFlag(binary.LittleEndian.Uint16(data[pos : pos+2]))
|
||||
if mc.flags&clientProtocol41 == 0 {
|
||||
return nil, "", ErrOldProtocol
|
||||
capabilities = capabilityFlag(binary.LittleEndian.Uint16(data[pos : pos+2]))
|
||||
if capabilities&clientProtocol41 == 0 {
|
||||
return nil, capabilities, 0, "", ErrOldProtocol
|
||||
}
|
||||
if mc.flags&clientSSL == 0 && mc.cfg.TLS != nil {
|
||||
if capabilities&clientSSL == 0 && mc.cfg.TLS != nil {
|
||||
if mc.cfg.AllowFallbackToPlaintext {
|
||||
mc.cfg.TLS = nil
|
||||
} else {
|
||||
return nil, "", ErrNoTLS
|
||||
return nil, capabilities, 0, "", ErrNoTLS
|
||||
}
|
||||
}
|
||||
pos += 2
|
||||
@@ -228,11 +230,16 @@ func (mc *mysqlConn) readHandshakePacket() (data []byte, plugin string, err erro
|
||||
// status flags [2 bytes]
|
||||
pos += 3
|
||||
// capability flags (upper 2 bytes) [2 bytes]
|
||||
mc.flags |= clientFlag(binary.LittleEndian.Uint16(data[pos:pos+2])) << 16
|
||||
capabilities |= capabilityFlag(binary.LittleEndian.Uint16(data[pos:pos+2])) << 16
|
||||
pos += 2
|
||||
// length of auth-plugin-data [1 byte]
|
||||
// reserved (all [00]) [10 bytes]
|
||||
pos += 11
|
||||
// reserved (all [00]) [6 bytes]
|
||||
pos += 7
|
||||
if capabilities&clientMySQL == 0 {
|
||||
// MariaDB server extended flag
|
||||
extendedCapabilities = extendedCapabilityFlag(binary.LittleEndian.Uint32(data[pos : pos+4]))
|
||||
}
|
||||
pos += 4
|
||||
|
||||
// second part of the password cipher [minimum 13 bytes],
|
||||
// where len=MAX(13, length of auth-plugin-data - 8)
|
||||
@@ -260,82 +267,72 @@ func (mc *mysqlConn) readHandshakePacket() (data []byte, plugin string, err erro
|
||||
// make a memory safe copy of the cipher slice
|
||||
var b [20]byte
|
||||
copy(b[:], authData)
|
||||
return b[:], plugin, nil
|
||||
return b[:], capabilities, extendedCapabilities, plugin, nil
|
||||
}
|
||||
|
||||
// make a memory safe copy of the cipher slice
|
||||
var b [8]byte
|
||||
copy(b[:], authData)
|
||||
return b[:], plugin, nil
|
||||
return b[:], capabilities, 0, plugin, nil
|
||||
}
|
||||
|
||||
// Client Authentication Packet
|
||||
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse
|
||||
func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string) error {
|
||||
// Adjust client flags based on server support
|
||||
clientFlags := clientProtocol41 |
|
||||
clientSecureConn |
|
||||
clientLongPassword |
|
||||
clientTransactions |
|
||||
clientLocalFiles |
|
||||
clientPluginAuth |
|
||||
clientMultiResults |
|
||||
mc.flags&clientConnectAttrs |
|
||||
mc.flags&clientLongFlag
|
||||
// initCapabilities initializes the capabilities based on server support and configuration
|
||||
func (mc *mysqlConn) initCapabilities(serverCapabilities capabilityFlag, serverExtCapabilities extendedCapabilityFlag, cfg *Config) {
|
||||
clientCapabilities :=
|
||||
clientMySQL |
|
||||
clientLongFlag |
|
||||
clientProtocol41 |
|
||||
clientSecureConn |
|
||||
clientTransactions |
|
||||
clientPluginAuthLenEncClientData |
|
||||
clientLocalFiles |
|
||||
clientPluginAuth |
|
||||
clientMultiResults |
|
||||
clientConnectAttrs |
|
||||
clientDeprecateEOF
|
||||
|
||||
sendConnectAttrs := mc.flags&clientConnectAttrs != 0
|
||||
|
||||
if mc.cfg.ClientFoundRows {
|
||||
clientFlags |= clientFoundRows
|
||||
if cfg.ClientFoundRows {
|
||||
clientCapabilities |= clientFoundRows
|
||||
}
|
||||
if mc.cfg.compress && mc.flags&clientCompress == clientCompress {
|
||||
clientFlags |= clientCompress
|
||||
if cfg.compress {
|
||||
clientCapabilities |= clientCompress
|
||||
}
|
||||
// To enable TLS / SSL
|
||||
if mc.cfg.TLS != nil {
|
||||
clientFlags |= clientSSL
|
||||
clientCapabilities |= clientSSL
|
||||
}
|
||||
|
||||
if mc.cfg.MultiStatements {
|
||||
clientFlags |= clientMultiStatements
|
||||
clientCapabilities |= clientMultiStatements
|
||||
}
|
||||
if n := len(cfg.DBName); n > 0 {
|
||||
clientCapabilities |= clientConnectWithDB
|
||||
}
|
||||
|
||||
// encode length of the auth plugin data
|
||||
var authRespLEIBuf [9]byte
|
||||
authRespLen := len(authResp)
|
||||
authRespLEI := appendLengthEncodedInteger(authRespLEIBuf[:0], uint64(authRespLen))
|
||||
if len(authRespLEI) > 1 {
|
||||
// if the length can not be written in 1 byte, it must be written as a
|
||||
// length encoded integer
|
||||
clientFlags |= clientPluginAuthLenEncClientData
|
||||
}
|
||||
// only keep client capabilities that server have
|
||||
mc.capabilities = clientCapabilities & serverCapabilities
|
||||
|
||||
pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + len(authRespLEI) + len(authResp) + 21 + 1
|
||||
// set MariaDB extended clientCacheMetadata capability if server support it
|
||||
mc.extCapabilities = clientCacheMetadata & serverExtCapabilities
|
||||
}
|
||||
|
||||
// To specify a db name
|
||||
if n := len(mc.cfg.DBName); n > 0 {
|
||||
clientFlags |= clientConnectWithDB
|
||||
pktLen += n + 1
|
||||
}
|
||||
|
||||
// encode length of the connection attributes
|
||||
var connAttrsLEI []byte
|
||||
if sendConnectAttrs {
|
||||
var connAttrsLEIBuf [9]byte
|
||||
connAttrsLen := len(mc.connector.encodedAttributes)
|
||||
connAttrsLEI = appendLengthEncodedInteger(connAttrsLEIBuf[:0], uint64(connAttrsLen))
|
||||
pktLen += len(connAttrsLEI) + len(mc.connector.encodedAttributes)
|
||||
}
|
||||
|
||||
// Calculate packet length and get buffer with that size
|
||||
data, err := mc.buf.takeBuffer(pktLen + 4)
|
||||
// Client Authentication Packet
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_handshake_response.html
|
||||
func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string) error {
|
||||
// packet header 4
|
||||
// capabilities 4
|
||||
// maxPacketSize 4
|
||||
// collation id 1
|
||||
// filler 23
|
||||
data, err := mc.buf.takeSmallBuffer(4*3 + 24)
|
||||
if err != nil {
|
||||
mc.cleanup()
|
||||
return err
|
||||
}
|
||||
_ = data[4*3+23] // boundery check
|
||||
|
||||
// ClientFlags [32 bit]
|
||||
binary.LittleEndian.PutUint32(data[4:], uint32(clientFlags))
|
||||
// clientCapabilities [32 bit]
|
||||
binary.LittleEndian.PutUint32(data[4:], uint32(mc.capabilities))
|
||||
|
||||
// MaxPacketSize [32 bit] (none)
|
||||
binary.LittleEndian.PutUint32(data[8:], 0)
|
||||
@@ -353,16 +350,26 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
|
||||
}
|
||||
|
||||
// Filler [23 bytes] (all 0x00)
|
||||
// or filler 19bytes + mariadb extCapabilities
|
||||
pos := 13
|
||||
for ; pos < 13+23; pos++ {
|
||||
data[pos] = 0
|
||||
if mc.capabilities&clientMySQL == 0 {
|
||||
for ; pos < 13+19; pos++ {
|
||||
data[pos] = 0
|
||||
}
|
||||
// MariaDB Extended Capabilities
|
||||
binary.LittleEndian.PutUint32(data[13+19:], uint32(mc.extCapabilities))
|
||||
} else {
|
||||
for ; pos < 13+23; pos++ {
|
||||
data[pos] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// SSL Connection Request Packet
|
||||
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_ssl_request.html
|
||||
// https://mariadb.com/kb/en/connection/#sslrequest-packet
|
||||
if mc.cfg.TLS != nil {
|
||||
// Send TLS / SSL request packet
|
||||
if err := mc.writePacket(data[:(4+4+1+23)+4]); err != nil {
|
||||
if err := mc.writePacket(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -379,37 +386,35 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
|
||||
|
||||
// User [null terminated string]
|
||||
if len(mc.cfg.User) > 0 {
|
||||
pos += copy(data[pos:], mc.cfg.User)
|
||||
data = append(data, mc.cfg.User...)
|
||||
}
|
||||
data[pos] = 0x00
|
||||
pos++
|
||||
data = append(data, 0)
|
||||
|
||||
// Auth Data [length encoded integer]
|
||||
pos += copy(data[pos:], authRespLEI)
|
||||
pos += copy(data[pos:], authResp)
|
||||
data = appendLengthEncodedInteger(data, uint64(len(authResp)))
|
||||
data = append(data, authResp...)
|
||||
|
||||
// Databasename [null terminated string]
|
||||
if len(mc.cfg.DBName) > 0 {
|
||||
pos += copy(data[pos:], mc.cfg.DBName)
|
||||
data[pos] = 0x00
|
||||
pos++
|
||||
// Database name [null terminated string]
|
||||
if mc.capabilities&clientConnectWithDB != 0 {
|
||||
data = append(data, mc.cfg.DBName...)
|
||||
data = append(data, 0)
|
||||
}
|
||||
|
||||
pos += copy(data[pos:], plugin)
|
||||
data[pos] = 0x00
|
||||
pos++
|
||||
data = append(data, plugin...)
|
||||
data = append(data, 0)
|
||||
|
||||
// Connection Attributes
|
||||
if sendConnectAttrs {
|
||||
pos += copy(data[pos:], connAttrsLEI)
|
||||
pos += copy(data[pos:], []byte(mc.connector.encodedAttributes))
|
||||
if mc.capabilities&clientConnectAttrs != 0 {
|
||||
connAttrsLen := len(mc.connector.encodedAttributes)
|
||||
data = appendLengthEncodedInteger(data, uint64(connAttrsLen))
|
||||
data = append(data, mc.connector.encodedAttributes...)
|
||||
}
|
||||
|
||||
// Send Auth packet
|
||||
return mc.writePacket(data[:pos])
|
||||
return mc.writePacket(data)
|
||||
}
|
||||
|
||||
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_auth_switch_response.html
|
||||
func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte) error {
|
||||
pktLen := 4 + len(authData)
|
||||
data, err := mc.buf.takeBuffer(pktLen)
|
||||
@@ -511,7 +516,7 @@ func (mc *mysqlConn) readAuthResult() ([]byte, string, error) {
|
||||
|
||||
case iEOF:
|
||||
if len(data) == 1 {
|
||||
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_old_auth_switch_request.html
|
||||
return nil, "mysql_old_password", nil
|
||||
}
|
||||
pluginEndIndex := bytes.IndexByte(data, 0x00)
|
||||
@@ -545,36 +550,41 @@ func (mc *okHandler) readResultOK() error {
|
||||
|
||||
// Result Set Header Packet
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_query_response.html
|
||||
func (mc *okHandler) readResultSetHeaderPacket() (int, error) {
|
||||
func (mc *okHandler) readResultSetHeaderPacket() (int, bool, error) {
|
||||
// handleOkPacket replaces both values; other cases leave the values unchanged.
|
||||
mc.result.affectedRows = append(mc.result.affectedRows, 0)
|
||||
mc.result.insertIds = append(mc.result.insertIds, 0)
|
||||
|
||||
data, err := mc.conn().readPacket()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
switch data[0] {
|
||||
case iOK:
|
||||
return 0, mc.handleOkPacket(data)
|
||||
return 0, false, mc.handleOkPacket(data)
|
||||
|
||||
case iERR:
|
||||
return 0, mc.conn().handleErrorPacket(data)
|
||||
return 0, false, mc.conn().handleErrorPacket(data)
|
||||
|
||||
case iLocalInFile:
|
||||
return 0, mc.handleInFileRequest(string(data[1:]))
|
||||
return 0, false, mc.handleInFileRequest(string(data[1:]))
|
||||
}
|
||||
|
||||
// column count
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_query_response_text_resultset.html
|
||||
num, _, _ := readLengthEncodedInteger(data)
|
||||
// https://mariadb.com/kb/en/result-set-packets/#column-count-packet
|
||||
num, _, len := readLengthEncodedInteger(data)
|
||||
|
||||
if mc.extCapabilities&clientCacheMetadata != 0 {
|
||||
return int(num), data[len] == 0x01, nil
|
||||
}
|
||||
// ignore remaining data in the packet. see #1478.
|
||||
return int(num), nil
|
||||
return int(num), true, nil
|
||||
}
|
||||
|
||||
// Error Packet
|
||||
// http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-ERR_Packet
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_err_packet.html
|
||||
func (mc *mysqlConn) handleErrorPacket(data []byte) error {
|
||||
if data[0] != iERR {
|
||||
return ErrMalformPkt
|
||||
@@ -656,7 +666,7 @@ func (mc *mysqlConn) clearResult() *okHandler {
|
||||
}
|
||||
|
||||
// Ok Packet
|
||||
// http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-OK_Packet
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_ok_packet.html
|
||||
func (mc *okHandler) handleOkPacket(data []byte) error {
|
||||
var n, m int
|
||||
var affectedRows, insertId uint64
|
||||
@@ -690,24 +700,19 @@ func (mc *okHandler) handleOkPacket(data []byte) error {
|
||||
}
|
||||
|
||||
// Read Packets as Field Packets until EOF-Packet or an Error appears
|
||||
// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnDefinition41
|
||||
func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_query_response_text_resultset_column_definition.html#sect_protocol_com_query_response_text_resultset_column_definition_41
|
||||
func (mc *mysqlConn) readColumns(count int, old []mysqlField) ([]mysqlField, error) {
|
||||
columns := make([]mysqlField, count)
|
||||
if len(old) != count {
|
||||
old = nil
|
||||
}
|
||||
|
||||
for i := 0; ; i++ {
|
||||
for i := range count {
|
||||
data, err := mc.readPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// EOF Packet
|
||||
if data[0] == iEOF && (len(data) == 5 || len(data) == 1) {
|
||||
if i == count {
|
||||
return columns, nil
|
||||
}
|
||||
return nil, fmt.Errorf("column count mismatch n:%d len:%d", count, len(columns))
|
||||
}
|
||||
|
||||
// Catalog
|
||||
pos, err := skipLengthEncodedString(data)
|
||||
if err != nil {
|
||||
@@ -728,7 +733,12 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
|
||||
return nil, err
|
||||
}
|
||||
pos += n
|
||||
columns[i].tableName = string(tableName)
|
||||
if old != nil && old[i].tableName == string(tableName) {
|
||||
// avoid allocating new string
|
||||
columns[i].tableName = old[i].tableName
|
||||
} else {
|
||||
columns[i].tableName = string(tableName)
|
||||
}
|
||||
} else {
|
||||
n, err = skipLengthEncodedString(data[pos:])
|
||||
if err != nil {
|
||||
@@ -749,7 +759,12 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columns[i].name = string(name)
|
||||
if old != nil && old[i].name == string(name) {
|
||||
// avoid allocating new string
|
||||
columns[i].name = old[i].name
|
||||
} else {
|
||||
columns[i].name = string(name)
|
||||
}
|
||||
pos += n
|
||||
|
||||
// Original name [len coded string]
|
||||
@@ -780,17 +795,17 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
|
||||
|
||||
// Decimals [uint8]
|
||||
columns[i].decimals = data[pos]
|
||||
//pos++
|
||||
|
||||
// Default value [len coded binary]
|
||||
//if pos < len(data) {
|
||||
// defaultVal, _, err = bytesToLengthCodedBinary(data[pos:])
|
||||
//}
|
||||
}
|
||||
|
||||
// skip EOF packet if client does not support deprecateEOF
|
||||
if err := mc.skipEof(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
// Read Packets as Field Packets until EOF-Packet or an Error appears
|
||||
// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-ProtocolText::ResultsetRow
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_query_response_text_resultset_row.html
|
||||
func (rows *textRows) readRow(dest []driver.Value) error {
|
||||
mc := rows.mc
|
||||
|
||||
@@ -804,9 +819,20 @@ func (rows *textRows) readRow(dest []driver.Value) error {
|
||||
}
|
||||
|
||||
// EOF Packet
|
||||
if data[0] == iEOF && len(data) == 5 {
|
||||
// server_status [2 bytes]
|
||||
rows.mc.status = readStatus(data[3:])
|
||||
// text row packets may starts with LengthEncodedString.
|
||||
// In such case, 0xFE can mean string larger than 0xffffff.
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_integers.html#sect_protocol_basic_dt_int_le
|
||||
if data[0] == iEOF && len(data) <= 0xffffff {
|
||||
if mc.capabilities&clientDeprecateEOF == 0 {
|
||||
// Deprecated EOF packet
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_eof_packet.html
|
||||
mc.status = readStatus(data[3:])
|
||||
} else {
|
||||
// Ok Packet with an 0xFE header
|
||||
_, _, n := readLengthEncodedInteger(data[1:]) // affected_rows
|
||||
_, _, m := readLengthEncodedInteger(data[1+n:]) // last_insert_id
|
||||
mc.status = readStatus(data[1+n+m:])
|
||||
}
|
||||
rows.rs.done = true
|
||||
if !rows.HasNextResultSet() {
|
||||
rows.mc = nil
|
||||
@@ -880,8 +906,34 @@ func (rows *textRows) readRow(dest []driver.Value) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reads Packets until EOF-Packet or an Error appears. Returns count of Packets read
|
||||
func (mc *mysqlConn) readUntilEOF() error {
|
||||
func (mc *mysqlConn) skipPackets(n int) error {
|
||||
for range n {
|
||||
if _, err := mc.readPacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// skips EOF packet after n * ColumnDefinition packets when clientDeprecateEOF is not set
|
||||
func (mc *mysqlConn) skipEof() error {
|
||||
if mc.capabilities&clientDeprecateEOF == 0 {
|
||||
if _, err := mc.readPacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) skipColumns(n int) error {
|
||||
if err := mc.skipPackets(n); err != nil {
|
||||
return err
|
||||
}
|
||||
return mc.skipEof()
|
||||
}
|
||||
|
||||
// Reads Packets until EOF-Packet or an Error appears.
|
||||
func (mc *mysqlConn) skipRows() error {
|
||||
for {
|
||||
data, err := mc.readPacket()
|
||||
if err != nil {
|
||||
@@ -892,10 +944,20 @@ func (mc *mysqlConn) readUntilEOF() error {
|
||||
case iERR:
|
||||
return mc.handleErrorPacket(data)
|
||||
case iEOF:
|
||||
if len(data) == 5 {
|
||||
mc.status = readStatus(data[3:])
|
||||
// text row packets may starts with LengthEncodedString.
|
||||
// In such case, 0xFE can mean string larger than 0xffffff.
|
||||
if len(data) <= 0xffffff {
|
||||
if mc.capabilities&clientDeprecateEOF == 0 {
|
||||
// EOF packet
|
||||
mc.status = readStatus(data[3:])
|
||||
} else {
|
||||
// OK packet with an 0xFE header
|
||||
_, _, n := readLengthEncodedInteger(data[1:]) // affected_rows
|
||||
_, _, m := readLengthEncodedInteger(data[1+n:]) // last_insert_id
|
||||
mc.status = readStatus(data[1+n+m:])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -905,7 +967,7 @@ func (mc *mysqlConn) readUntilEOF() error {
|
||||
******************************************************************************/
|
||||
|
||||
// Prepare Result Packets
|
||||
// http://dev.mysql.com/doc/internals/en/com-stmt-prepare-response.html
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_stmt_prepare.html#sect_protocol_com_stmt_prepare_response
|
||||
func (stmt *mysqlStmt) readPrepareResultPacket() (uint16, error) {
|
||||
data, err := stmt.mc.readPacket()
|
||||
if err == nil {
|
||||
@@ -932,7 +994,7 @@ func (stmt *mysqlStmt) readPrepareResultPacket() (uint16, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// http://dev.mysql.com/doc/internals/en/com-stmt-send-long-data.html
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_stmt_send_long_data.html
|
||||
func (stmt *mysqlStmt) writeCommandLongData(paramID int, arg []byte) error {
|
||||
maxLen := stmt.mc.maxAllowedPacket - 1
|
||||
pktLen := maxLen
|
||||
@@ -979,7 +1041,7 @@ func (stmt *mysqlStmt) writeCommandLongData(paramID int, arg []byte) error {
|
||||
}
|
||||
|
||||
// Execute Prepared Statement
|
||||
// http://dev.mysql.com/doc/internals/en/com-stmt-execute.html
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_stmt_execute.html
|
||||
func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
||||
if len(args) != stmt.paramCount {
|
||||
return fmt.Errorf(
|
||||
@@ -993,10 +1055,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
||||
mc := stmt.mc
|
||||
|
||||
// Determine threshold dynamically to avoid packet size shortage.
|
||||
longDataSize := mc.maxAllowedPacket / (stmt.paramCount + 1)
|
||||
if longDataSize < 64 {
|
||||
longDataSize = 64
|
||||
}
|
||||
longDataSize := max(mc.maxAllowedPacket/(stmt.paramCount+1), 64)
|
||||
|
||||
// Reset packet-sequence
|
||||
mc.resetSequence()
|
||||
@@ -1185,17 +1244,17 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
||||
// mc.affectedRows and mc.insertIds.
|
||||
func (mc *okHandler) discardResults() error {
|
||||
for mc.status&statusMoreResultsExists != 0 {
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
resLen, _, err := mc.readResultSetHeaderPacket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resLen > 0 {
|
||||
// columns
|
||||
if err := mc.conn().readUntilEOF(); err != nil {
|
||||
if err := mc.conn().skipColumns(resLen); err != nil {
|
||||
return err
|
||||
}
|
||||
// rows
|
||||
if err := mc.conn().readUntilEOF(); err != nil {
|
||||
if err := mc.conn().skipRows(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1203,7 +1262,7 @@ func (mc *okHandler) discardResults() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// http://dev.mysql.com/doc/internals/en/binary-protocol-resultset-row.html
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_binary_resultset.html#sect_protocol_binary_resultset_row
|
||||
func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||
data, err := rows.mc.readPacket()
|
||||
if err != nil {
|
||||
@@ -1212,9 +1271,17 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||
|
||||
// packet indicator [1 byte]
|
||||
if data[0] != iOK {
|
||||
// EOF Packet
|
||||
if data[0] == iEOF && len(data) == 5 {
|
||||
rows.mc.status = readStatus(data[3:])
|
||||
// EOF/OK Packet
|
||||
if data[0] == iEOF {
|
||||
if rows.mc.capabilities&clientDeprecateEOF == 0 {
|
||||
// EOF packet
|
||||
rows.mc.status = readStatus(data[3:])
|
||||
} else {
|
||||
// OK Packet with an 0xFE header
|
||||
_, _, n := readLengthEncodedInteger(data[1:])
|
||||
_, _, m := readLengthEncodedInteger(data[1+n:])
|
||||
rows.mc.status = readStatus(data[1+n+m:])
|
||||
}
|
||||
rows.rs.done = true
|
||||
if !rows.HasNextResultSet() {
|
||||
rows.mc = nil
|
||||
|
||||
+4
-2
@@ -8,6 +8,8 @@
|
||||
|
||||
package mysql
|
||||
|
||||
import "slices"
|
||||
|
||||
import "database/sql/driver"
|
||||
|
||||
// Result exposes data not available through *connection.Result.
|
||||
@@ -42,9 +44,9 @@ func (res *mysqlResult) RowsAffected() (int64, error) {
|
||||
}
|
||||
|
||||
func (res *mysqlResult) AllLastInsertIds() []int64 {
|
||||
return append([]int64{}, res.insertIds...) // defensive copy
|
||||
return slices.Clone(res.insertIds) // defensive copy
|
||||
}
|
||||
|
||||
func (res *mysqlResult) AllRowsAffected() []int64 {
|
||||
return append([]int64{}, res.affectedRows...) // defensive copy
|
||||
return slices.Clone(res.affectedRows) // defensive copy
|
||||
}
|
||||
|
||||
+5
-5
@@ -113,7 +113,7 @@ func (rows *mysqlRows) Close() (err error) {
|
||||
|
||||
// Remove unread packets from stream
|
||||
if !rows.rs.done {
|
||||
err = mc.readUntilEOF()
|
||||
err = mc.skipRows()
|
||||
}
|
||||
if err == nil {
|
||||
handleOk := mc.clearResult()
|
||||
@@ -143,7 +143,7 @@ func (rows *mysqlRows) nextResultSet() (int, error) {
|
||||
|
||||
// Remove unread packets from stream
|
||||
if !rows.rs.done {
|
||||
if err := rows.mc.readUntilEOF(); err != nil {
|
||||
if err := rows.mc.skipRows(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
rows.rs.done = true
|
||||
@@ -156,7 +156,7 @@ func (rows *mysqlRows) nextResultSet() (int, error) {
|
||||
rows.rs = resultSet{}
|
||||
// rows.mc.affectedRows and rows.mc.insertIds accumulate on each call to
|
||||
// nextResultSet.
|
||||
resLen, err := rows.mc.resultUnchanged().readResultSetHeaderPacket()
|
||||
resLen, _, err := rows.mc.resultUnchanged().readResultSetHeaderPacket()
|
||||
if err != nil {
|
||||
// Clean up about multi-results flag
|
||||
rows.rs.done = true
|
||||
@@ -186,7 +186,7 @@ func (rows *binaryRows) NextResultSet() error {
|
||||
return err
|
||||
}
|
||||
|
||||
rows.rs.columns, err = rows.mc.readColumns(resLen)
|
||||
rows.rs.columns, err = rows.mc.readColumns(resLen, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ func (rows *textRows) NextResultSet() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
rows.rs.columns, err = rows.mc.readColumns(resLen)
|
||||
rows.rs.columns, err = rows.mc.readColumns(resLen, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+26
-8
@@ -20,6 +20,7 @@ type mysqlStmt struct {
|
||||
mc *mysqlConn
|
||||
id uint32
|
||||
paramCount int
|
||||
columns []mysqlField
|
||||
}
|
||||
|
||||
func (stmt *mysqlStmt) Close() error {
|
||||
@@ -64,19 +65,26 @@ func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
handleOk := stmt.mc.clearResult()
|
||||
|
||||
// Read Result
|
||||
resLen, err := handleOk.readResultSetHeaderPacket()
|
||||
resLen, metadataFollows, err := handleOk.readResultSetHeaderPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resLen > 0 {
|
||||
// Columns
|
||||
if err = mc.readUntilEOF(); err != nil {
|
||||
return nil, err
|
||||
if metadataFollows && stmt.mc.extCapabilities&clientCacheMetadata != 0 {
|
||||
// we can not skip column metadata because next stmt.Query() may use it.
|
||||
if stmt.columns, err = mc.readColumns(resLen, stmt.columns); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err = mc.skipColumns(resLen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Rows
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
if err = mc.skipRows(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -107,7 +115,7 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
|
||||
|
||||
// Read Result
|
||||
handleOk := stmt.mc.clearResult()
|
||||
resLen, err := handleOk.readResultSetHeaderPacket()
|
||||
resLen, metadataFollows, err := handleOk.readResultSetHeaderPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -116,7 +124,17 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
|
||||
|
||||
if resLen > 0 {
|
||||
rows.mc = mc
|
||||
rows.rs.columns, err = mc.readColumns(resLen)
|
||||
if metadataFollows {
|
||||
if rows.rs.columns, err = mc.readColumns(resLen, stmt.columns); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt.columns = rows.rs.columns
|
||||
} else {
|
||||
if err = mc.skipEof(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows.rs.columns = stmt.columns
|
||||
}
|
||||
} else {
|
||||
rows.rs.done = true
|
||||
|
||||
@@ -131,7 +149,7 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
|
||||
return rows, err
|
||||
}
|
||||
|
||||
var jsonType = reflect.TypeOf(json.RawMessage{})
|
||||
var jsonType = reflect.TypeFor[json.RawMessage]()
|
||||
|
||||
type converter struct{}
|
||||
|
||||
@@ -193,7 +211,7 @@ func (c converter) ConvertValue(v any) (driver.Value, error) {
|
||||
return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
|
||||
}
|
||||
|
||||
var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
|
||||
var valuerReflectType = reflect.TypeFor[driver.Valuer]()
|
||||
|
||||
// callValuerValue returns vr.Value(), with one exception:
|
||||
// If vr.Value is an auto-generated method on a pointer type and the
|
||||
|
||||
+65
-90
@@ -182,7 +182,7 @@ func parseDateTime(b []byte, loc *time.Location) (time.Time, error) {
|
||||
|
||||
func parseByteYear(b []byte) (int, error) {
|
||||
year, n := 0, 1000
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := range 4 {
|
||||
v, err := bToi(b[i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -207,7 +207,7 @@ func parseByte2Digits(b1, b2 byte) (int, error) {
|
||||
|
||||
func parseByteNanoSec(b []byte) (int, error) {
|
||||
ns, digit := 0, 100000 // max is 6-digits
|
||||
for i := 0; i < len(b); i++ {
|
||||
for i := range b {
|
||||
v, err := bToi(b[i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -625,108 +625,80 @@ func reserveBuffer(buf []byte, appendSize int) []byte {
|
||||
return buf[:newSize]
|
||||
}
|
||||
|
||||
// escapeBytesBackslash escapes []byte with backslashes (\)
|
||||
// This escapes the contents of a string (provided as []byte) by adding backslashes before special
|
||||
// characters, and turning others into specific escape sequences, such as
|
||||
// turning newlines into \n and null bytes into \0.
|
||||
// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L823-L932
|
||||
func escapeBytesBackslash(buf, v []byte) []byte {
|
||||
pos := len(buf)
|
||||
buf = reserveBuffer(buf, len(v)*2)
|
||||
// Lookup table for backslash escapes (used for both string and bytes)
|
||||
var backslashEscapeTable [256]byte
|
||||
|
||||
for _, c := range v {
|
||||
switch c {
|
||||
case '\x00':
|
||||
buf[pos+1] = '0'
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
case '\n':
|
||||
buf[pos+1] = 'n'
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
case '\r':
|
||||
buf[pos+1] = 'r'
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
case '\x1a':
|
||||
buf[pos+1] = 'Z'
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
case '\'':
|
||||
buf[pos+1] = '\''
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
case '"':
|
||||
buf[pos+1] = '"'
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
case '\\':
|
||||
buf[pos+1] = '\\'
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
default:
|
||||
buf[pos] = c
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
return buf[:pos]
|
||||
func init() {
|
||||
backslashEscapeTable['\x00'] = '0'
|
||||
backslashEscapeTable['\n'] = 'n'
|
||||
backslashEscapeTable['\r'] = 'r'
|
||||
backslashEscapeTable['\x1a'] = 'Z'
|
||||
backslashEscapeTable['\''] = '\''
|
||||
backslashEscapeTable['"'] = '"'
|
||||
backslashEscapeTable['\\'] = '\\'
|
||||
}
|
||||
|
||||
// escapeStringBackslash is similar to escapeBytesBackslash but for string.
|
||||
func escapeStringBackslash(buf []byte, v string) []byte {
|
||||
pos := len(buf)
|
||||
buf = reserveBuffer(buf, len(v)*2)
|
||||
|
||||
buf = reserveBuffer(buf, len(v)*2+2)
|
||||
buf[pos] = '\''
|
||||
pos++
|
||||
for i := 0; i < len(v); i++ {
|
||||
c := v[i]
|
||||
switch c {
|
||||
case '\x00':
|
||||
buf[pos+1] = '0'
|
||||
if esc := backslashEscapeTable[c]; esc != 0 {
|
||||
buf[pos+1] = esc
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
case '\n':
|
||||
buf[pos+1] = 'n'
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
case '\r':
|
||||
buf[pos+1] = 'r'
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
case '\x1a':
|
||||
buf[pos+1] = 'Z'
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
case '\'':
|
||||
buf[pos+1] = '\''
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
case '"':
|
||||
buf[pos+1] = '"'
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
case '\\':
|
||||
buf[pos+1] = '\\'
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
default:
|
||||
} else {
|
||||
buf[pos] = c
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
buf[pos] = '\''
|
||||
pos++
|
||||
return buf[:pos]
|
||||
}
|
||||
|
||||
// escapeBytesQuotes escapes apostrophes in []byte by doubling them up.
|
||||
// This escapes the contents of a string by doubling up any apostrophes that
|
||||
// it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
|
||||
// effect on the server.
|
||||
// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L963-L1038
|
||||
func escapeBytesQuotes(buf, v []byte) []byte {
|
||||
// escapeBytesBackslash appends _binary'...' or '...' with backslash escaping for bytes.
|
||||
func escapeBytesBackslash(buf, v []byte, binary bool) []byte {
|
||||
pos := len(buf)
|
||||
buf = reserveBuffer(buf, len(v)*2)
|
||||
if binary {
|
||||
buf = reserveBuffer(buf, len(v)*2+9)
|
||||
copy(buf[pos:], []byte("_binary'"))
|
||||
pos += 8
|
||||
} else {
|
||||
buf = reserveBuffer(buf, len(v)*2+2)
|
||||
buf[pos] = '\''
|
||||
pos++
|
||||
}
|
||||
for _, c := range v {
|
||||
if esc := backslashEscapeTable[c]; esc != 0 {
|
||||
buf[pos+1] = esc
|
||||
buf[pos] = '\\'
|
||||
pos += 2
|
||||
} else {
|
||||
buf[pos] = c
|
||||
pos++
|
||||
}
|
||||
}
|
||||
buf[pos] = '\''
|
||||
pos++
|
||||
return buf[:pos]
|
||||
}
|
||||
|
||||
// escapeBytesQuotes appends _binary'...' or '...' with single-quote escaping for bytes.
|
||||
func escapeBytesQuotes(buf, v []byte, binary bool) []byte {
|
||||
pos := len(buf)
|
||||
if binary {
|
||||
buf = reserveBuffer(buf, len(v)*2+9)
|
||||
copy(buf[pos:], []byte("_binary'"))
|
||||
pos += 8
|
||||
} else {
|
||||
buf = reserveBuffer(buf, len(v)*2+2)
|
||||
buf[pos] = '\''
|
||||
pos++
|
||||
}
|
||||
for _, c := range v {
|
||||
if c == '\'' {
|
||||
buf[pos+1] = '\''
|
||||
@@ -737,16 +709,18 @@ func escapeBytesQuotes(buf, v []byte) []byte {
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
buf[pos] = '\''
|
||||
pos++
|
||||
return buf[:pos]
|
||||
}
|
||||
|
||||
// escapeStringQuotes is similar to escapeBytesQuotes but for string.
|
||||
func escapeStringQuotes(buf []byte, v string) []byte {
|
||||
pos := len(buf)
|
||||
buf = reserveBuffer(buf, len(v)*2)
|
||||
|
||||
for i := 0; i < len(v); i++ {
|
||||
buf = reserveBuffer(buf, len(v)*2+2)
|
||||
buf[pos] = '\''
|
||||
pos++
|
||||
for i := range len(v) {
|
||||
c := v[i]
|
||||
if c == '\'' {
|
||||
buf[pos+1] = '\''
|
||||
@@ -757,7 +731,8 @@ func escapeStringQuotes(buf []byte, v string) []byte {
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
buf[pos] = '\''
|
||||
pos++
|
||||
return buf[:pos]
|
||||
}
|
||||
|
||||
|
||||
Vendored
+4
-4
@@ -1,8 +1,8 @@
|
||||
# code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48
|
||||
## explicit
|
||||
code.cloudfoundry.org/bytefmt
|
||||
# filippo.io/edwards25519 v1.1.1
|
||||
## explicit; go 1.20
|
||||
# filippo.io/edwards25519 v1.2.0
|
||||
## explicit; go 1.24.0
|
||||
filippo.io/edwards25519
|
||||
filippo.io/edwards25519/field
|
||||
# github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
|
||||
@@ -35,8 +35,8 @@ github.com/ergochat/irc-go/ircutils
|
||||
# github.com/ergochat/webpush-go/v2 v2.0.0
|
||||
## explicit; go 1.20
|
||||
github.com/ergochat/webpush-go/v2
|
||||
# github.com/go-sql-driver/mysql v1.9.3
|
||||
## explicit; go 1.21.0
|
||||
# github.com/go-sql-driver/mysql v1.10.0
|
||||
## explicit; go 1.24.0
|
||||
github.com/go-sql-driver/mysql
|
||||
# github.com/gofrs/flock v0.8.1
|
||||
## explicit
|
||||
|
||||
Reference in New Issue
Block a user