Skip to content

Commit fcea4fe

Browse files
committed
Merge pull request #27 from karmakaze/sign-extend-ints-when-widening
Issue #26 sign-extend int8/int16/int24/int32 when widening to int64
2 parents c2f5b70 + 879f46c commit fcea4fe

File tree

5 files changed

+129
-24
lines changed

5 files changed

+129
-24
lines changed

Makefile

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ build:
44
export GOBIN=./bin && \
55
godep go install ./...
66

7+
test: build
8+
export GOBIN=./bin && \
9+
godep go test ./...
10+
711
clean:
812
godep go clean -i ./...
9-
@rm -rf ./bin
13+
@rm -rf ./bin

mysql/mysql_test.go

+50
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,53 @@ func (t *mysqlTestSuite) TestMysqlGTIDContain(c *check.C) {
101101
c.Assert(g2.Contain(g1), check.Equals, true)
102102
c.Assert(g1.Contain(g2), check.Equals, false)
103103
}
104+
105+
func (t *mysqlTestSuite) TestMysqlParseBinaryInt8(c *check.C) {
106+
i64 := ParseBinaryInt8([]byte{128})
107+
c.Assert(i64, check.Equals, int64(-128))
108+
}
109+
110+
func (t *mysqlTestSuite) TestMysqlParseBinaryUint8(c *check.C) {
111+
u64 := ParseBinaryUint8([]byte{128})
112+
c.Assert(u64, check.Equals, uint64(128))
113+
}
114+
115+
func (t *mysqlTestSuite) TestMysqlParseBinaryInt16(c *check.C) {
116+
i64 := ParseBinaryInt16([]byte{1, 128})
117+
c.Assert(i64, check.Equals, int64(-128*256 + 1))
118+
}
119+
120+
func (t *mysqlTestSuite) TestMysqlParseBinaryUint16(c *check.C) {
121+
u64 := ParseBinaryUint16([]byte{1, 128})
122+
c.Assert(u64, check.Equals, uint64(128*256 + 1))
123+
}
124+
125+
func (t *mysqlTestSuite) TestMysqlParseBinaryInt24(c *check.C) {
126+
i64 := ParseBinaryInt24([]byte{1, 2, 128})
127+
c.Assert(i64, check.Equals, int64(-128*65536 + 2*256 + 1))
128+
}
129+
130+
func (t *mysqlTestSuite) TestMysqlParseBinaryUint24(c *check.C) {
131+
u64 := ParseBinaryUint24([]byte{1, 2, 128})
132+
c.Assert(u64, check.Equals, uint64(128*65536 + 2*256 + 1))
133+
}
134+
135+
func (t *mysqlTestSuite) TestMysqlParseBinaryInt32(c *check.C) {
136+
i64 := ParseBinaryInt32([]byte{1, 2, 3, 128})
137+
c.Assert(i64, check.Equals, int64(-128*16777216 + 3*65536 + 2*256 + 1))
138+
}
139+
140+
func (t *mysqlTestSuite) TestMysqlParseBinaryUint32(c *check.C) {
141+
u64 := ParseBinaryUint32([]byte{1, 2, 3, 128})
142+
c.Assert(u64, check.Equals, uint64(128*16777216 + 3*65536 + 2*256 + 1))
143+
}
144+
145+
func (t *mysqlTestSuite) TestMysqlParseBinaryInt64(c *check.C) {
146+
i64 := ParseBinaryInt64([]byte{1, 2, 3, 4, 5, 6, 7, 128})
147+
c.Assert(i64, check.Equals, -128*int64(72057594037927936) + 7*int64(281474976710656) + 6*int64(1099511627776) + 5*int64(4294967296) + 4*16777216 + 3*65536 + 2*256 + 1)
148+
}
149+
150+
func (t *mysqlTestSuite) TestMysqlParseBinaryUint64(c *check.C) {
151+
u64 := ParseBinaryUint64([]byte{1, 2, 3, 4, 5, 6, 7, 128})
152+
c.Assert(u64, check.Equals, 128*uint64(72057594037927936) + 7*uint64(281474976710656) + 6*uint64(1099511627776) + 5*uint64(4294967296) + 4*16777216 + 3*65536 + 2*256 + 1)
153+
}

mysql/parse_binary.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package mysql
2+
3+
import (
4+
"encoding/binary"
5+
)
6+
7+
func ParseBinaryInt8(data []byte) int64 {
8+
return int64(int8(data[0]))
9+
}
10+
func ParseBinaryUint8(data []byte) uint64 {
11+
return uint64((data[0]))
12+
}
13+
14+
func ParseBinaryInt16(data []byte) int64 {
15+
return int64(int16(binary.LittleEndian.Uint16(data)))
16+
}
17+
func ParseBinaryUint16(data []byte) uint64 {
18+
return uint64(binary.LittleEndian.Uint16(data))
19+
}
20+
21+
func ParseBinaryInt24(data []byte) int64 {
22+
u32 := uint32(ParseBinaryUint24(data))
23+
if u32&0x00800000 != 0 {
24+
u32 |= 0xFF000000
25+
}
26+
return int64(int32(u32))
27+
}
28+
func ParseBinaryUint24(data []byte) uint64 {
29+
return uint64(uint32(data[0]) + uint32(data[1])<<8 + uint32(data[2])<<16)
30+
}
31+
32+
func ParseBinaryInt32(data []byte) int64 {
33+
return int64(int32(binary.LittleEndian.Uint32(data)))
34+
}
35+
func ParseBinaryUint32(data []byte) uint64 {
36+
return uint64(binary.LittleEndian.Uint32(data))
37+
}
38+
39+
func ParseBinaryInt64(data []byte) int64 {
40+
return int64(binary.LittleEndian.Uint64(data))
41+
}
42+
func ParseBinaryUint64(data []byte) uint64 {
43+
return binary.LittleEndian.Uint64(data)
44+
}

mysql/resultset.go

+21-13
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func (p RowData) ParseText(f []*Field) ([]interface{}, error) {
2424

2525
var err error
2626
var v []byte
27-
var isNull, isUnsigned bool
27+
var isNull bool
2828
var pos int = 0
2929
var n int = 0
3030

@@ -39,7 +39,7 @@ func (p RowData) ParseText(f []*Field) ([]interface{}, error) {
3939
if isNull {
4040
data[i] = nil
4141
} else {
42-
isUnsigned = (f[i].Flag&UNSIGNED_FLAG > 0)
42+
isUnsigned := f[i].Flag&UNSIGNED_FLAG != 0
4343

4444
switch f[i].Type {
4545
case MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_INT24,
@@ -75,7 +75,6 @@ func (p RowData) ParseBinary(f []*Field) ([]interface{}, error) {
7575

7676
nullBitmap := p[1:pos]
7777

78-
var isUnsigned bool
7978
var isNull bool
8079
var n int
8180
var err error
@@ -86,7 +85,7 @@ func (p RowData) ParseBinary(f []*Field) ([]interface{}, error) {
8685
continue
8786
}
8887

89-
isUnsigned = f[i].Flag&UNSIGNED_FLAG > 0
88+
isUnsigned := f[i].Flag&UNSIGNED_FLAG != 0
9089

9190
switch f[i].Type {
9291
case MYSQL_TYPE_NULL:
@@ -95,36 +94,45 @@ func (p RowData) ParseBinary(f []*Field) ([]interface{}, error) {
9594

9695
case MYSQL_TYPE_TINY:
9796
if isUnsigned {
98-
data[i] = uint64(p[pos])
97+
data[i] = ParseBinaryUint8(p[pos : pos+1])
9998
} else {
100-
data[i] = int64(p[pos])
99+
data[i] = ParseBinaryInt8(p[pos : pos+1])
101100
}
102101
pos++
103102
continue
104103

105104
case MYSQL_TYPE_SHORT, MYSQL_TYPE_YEAR:
106105
if isUnsigned {
107-
data[i] = uint64(binary.LittleEndian.Uint16(p[pos : pos+2]))
106+
data[i] = ParseBinaryUint16(p[pos : pos+2])
108107
} else {
109-
data[i] = int64((binary.LittleEndian.Uint16(p[pos : pos+2])))
108+
data[i] = ParseBinaryInt16(p[pos : pos+2])
110109
}
111110
pos += 2
112111
continue
113112

114-
case MYSQL_TYPE_INT24, MYSQL_TYPE_LONG:
113+
case MYSQL_TYPE_INT24:
114+
if isUnsigned {
115+
data[i] = ParseBinaryUint24(p[pos : pos+3])
116+
} else {
117+
data[i] = ParseBinaryInt24(p[pos : pos+3])
118+
}
119+
pos += 4
120+
continue
121+
122+
case MYSQL_TYPE_LONG:
115123
if isUnsigned {
116-
data[i] = uint64(binary.LittleEndian.Uint32(p[pos : pos+4]))
124+
data[i] = ParseBinaryUint32(p[pos : pos+4])
117125
} else {
118-
data[i] = int64(binary.LittleEndian.Uint32(p[pos : pos+4]))
126+
data[i] = ParseBinaryInt32(p[pos : pos+4])
119127
}
120128
pos += 4
121129
continue
122130

123131
case MYSQL_TYPE_LONGLONG:
124132
if isUnsigned {
125-
data[i] = binary.LittleEndian.Uint64(p[pos : pos+8])
133+
data[i] = ParseBinaryUint64(p[pos : pos+8])
126134
} else {
127-
data[i] = int64(binary.LittleEndian.Uint64(p[pos : pos+8]))
135+
data[i] = ParseBinaryInt64(p[pos : pos+8])
128136
}
129137
pos += 8
130138
continue

replication/row_event.go

+9-10
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ func (e *RowsEvent) Decode(data []byte) error {
276276
}
277277

278278
func (e *RowsEvent) decodeRows(data []byte, table *TableMapEvent, bitmap []byte) (int, error) {
279-
rows := make([]interface{}, e.ColumnCount)
279+
row := make([]interface{}, e.ColumnCount)
280280

281281
pos := 0
282282

@@ -297,12 +297,12 @@ func (e *RowsEvent) decodeRows(data []byte, table *TableMapEvent, bitmap []byte)
297297
isNull := (uint32(nullBitmap[nullbitIndex/8]) >> uint32(nullbitIndex%8)) & 0x01
298298

299299
if isNull > 0 {
300-
rows[i] = nil
300+
row[i] = nil
301301
nullbitIndex++
302302
continue
303303
}
304304

305-
rows[i], n, err = e.decodeValue(data[pos:], table.ColumnType[i], table.ColumnMeta[i])
305+
row[i], n, err = e.decodeValue(data[pos:], table.ColumnType[i], table.ColumnMeta[i])
306306

307307
if err != nil {
308308
return 0, nil
@@ -312,7 +312,7 @@ func (e *RowsEvent) decodeRows(data []byte, table *TableMapEvent, bitmap []byte)
312312
nullbitIndex++
313313
}
314314

315-
e.Rows = append(e.Rows, rows)
315+
e.Rows = append(e.Rows, row)
316316
return pos, nil
317317
}
318318

@@ -342,20 +342,19 @@ func (e *RowsEvent) decodeValue(data []byte, tp byte, meta uint16) (v interface{
342342
return nil, 0, nil
343343
case MYSQL_TYPE_LONG:
344344
n = 4
345-
v = int64(binary.LittleEndian.Uint32(data))
345+
v = ParseBinaryInt32(data)
346346
case MYSQL_TYPE_TINY:
347347
n = 1
348-
v = int64(data[0])
348+
v = ParseBinaryInt8(data)
349349
case MYSQL_TYPE_SHORT:
350350
n = 2
351-
v = int64(binary.LittleEndian.Uint16(data))
351+
v = ParseBinaryInt16(data)
352352
case MYSQL_TYPE_INT24:
353353
n = 3
354-
v = int64(FixedLengthInt(data[0:3]))
354+
v = ParseBinaryInt24(data)
355355
case MYSQL_TYPE_LONGLONG:
356-
//em, maybe overflow for int64......
357356
n = 8
358-
v = int64(binary.LittleEndian.Uint64(data))
357+
v = ParseBinaryInt64(data)
359358
case MYSQL_TYPE_NEWDECIMAL:
360359
prec := uint8(meta >> 8)
361360
scale := uint8(meta & 0xFF)

0 commit comments

Comments
 (0)