Skip to content

Commit 7fc3b28

Browse files
amyangfeisiddontang
authored andcommitted
add compatibility to json decode for generated column (go-mysql-org#372)
1 parent 7263f56 commit 7fc3b28

File tree

4 files changed

+93
-5
lines changed

4 files changed

+93
-5
lines changed

replication/json_binary.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ func (e *RowsEvent) decodeJsonBinary(data []byte) ([]byte, error) {
7676
if len(data) == 0 {
7777
return []byte{}, nil
7878
}
79-
d := jsonBinaryDecoder{useDecimal: e.useDecimal}
79+
d := jsonBinaryDecoder{
80+
useDecimal: e.useDecimal,
81+
ignoreDecodeErr: e.ignoreJSONDecodeErr,
82+
}
8083

8184
if d.isDataShort(data, 1) {
8285
return nil, d.err
@@ -91,8 +94,9 @@ func (e *RowsEvent) decodeJsonBinary(data []byte) ([]byte, error) {
9194
}
9295

9396
type jsonBinaryDecoder struct {
94-
useDecimal bool
95-
err error
97+
useDecimal bool
98+
ignoreDecodeErr bool
99+
err error
96100
}
97101

98102
func (d *jsonBinaryDecoder) decodeValue(tp byte, data []byte) interface{} {
@@ -146,6 +150,13 @@ func (d *jsonBinaryDecoder) decodeObjectOrArray(data []byte, isSmall bool, isObj
146150
size := d.decodeCount(data[offsetSize:], isSmall)
147151

148152
if d.isDataShort(data, int(size)) {
153+
// Before MySQL 5.7.22, json type generated column may have invalid value,
154+
// bug ref: https://bugs.mysql.com/bug.php?id=88791
155+
// As generated column value is not used in replication, we can just ignore
156+
// this error and return a dummy value for this column.
157+
if d.ignoreDecodeErr {
158+
d.err = nil
159+
}
149160
return nil
150161
}
151162

replication/parser.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ type BinlogParser struct {
3232
// used to start/stop processing
3333
stopProcessing uint32
3434

35-
useDecimal bool
36-
verifyChecksum bool
35+
useDecimal bool
36+
ignoreJSONDecodeErr bool
37+
verifyChecksum bool
3738
}
3839

3940
func NewBinlogParser() *BinlogParser {
@@ -191,6 +192,10 @@ func (p *BinlogParser) SetUseDecimal(useDecimal bool) {
191192
p.useDecimal = useDecimal
192193
}
193194

195+
func (p *BinlogParser) SetIgnoreJSONDecodeError(ignoreJSONDecodeErr bool) {
196+
p.ignoreJSONDecodeErr = ignoreJSONDecodeErr
197+
}
198+
194199
func (p *BinlogParser) SetVerifyChecksum(verify bool) {
195200
p.verifyChecksum = verify
196201
}
@@ -355,6 +360,7 @@ func (p *BinlogParser) newRowsEvent(h *EventHeader) *RowsEvent {
355360
e.parseTime = p.parseTime
356361
e.timestampStringLocation = p.timestampStringLocation
357362
e.useDecimal = p.useDecimal
363+
e.ignoreJSONDecodeErr = p.ignoreJSONDecodeErr
358364

359365
switch h.EventType {
360366
case WRITE_ROWS_EVENTv0:

replication/row_event.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ type RowsEvent struct {
233233
parseTime bool
234234
timestampStringLocation *time.Location
235235
useDecimal bool
236+
ignoreJSONDecodeErr bool
236237
}
237238

238239
func (e *RowsEvent) Decode(data []byte) error {

replication/row_event_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,3 +660,73 @@ func (_ *testDecodeSuite) TestJsonNull(c *C) {
660660
c.Assert(err, IsNil)
661661
c.Assert(rows.Rows[0][3], HasLen, 0)
662662
}
663+
664+
func (_ *testDecodeSuite) TestJsonCompatibility(c *C) {
665+
// Table:
666+
// mysql> desc t11;
667+
// +----------+--------------+------+-----+---------+-------------------+
668+
// | Field | Type | Null | Key | Default | Extra |
669+
// +----------+--------------+------+-----+---------+-------------------+
670+
// | id | int(11) | YES | | NULL | |
671+
// | cfg | varchar(100) | YES | | NULL | |
672+
// | cfg_json | json | YES | | NULL | VIRTUAL GENERATED |
673+
// | age | int(11) | YES | | NULL | |
674+
// +----------+--------------+------+-----+---------+-------------------+
675+
// mysql> insert into t11(id, cfg) values (1, '{}');
676+
677+
// test json deserialization
678+
// mysql> update t11 set cfg = '{"a":1234}' where id = 1;
679+
// mysql> update test set cfg = '{}' where id = 1;
680+
681+
tableMapEventData := []byte("l\x00\x00\x00\x00\x00\x01\x00\x04test\x00\x03t11\x00\x04\x03\x0f\xf5\x03\x03d\x00\x04\x0f")
682+
683+
tableMapEvent := new(TableMapEvent)
684+
tableMapEvent.tableIDSize = 6
685+
err := tableMapEvent.Decode(tableMapEventData)
686+
c.Assert(err, IsNil)
687+
688+
rows := new(RowsEvent)
689+
rows.tableIDSize = 6
690+
rows.tables = make(map[uint64]*TableMapEvent)
691+
rows.tables[tableMapEvent.TableID] = tableMapEvent
692+
rows.Version = 2
693+
694+
data := []byte("l\x00\x00\x00\x00\x00\x01\x00\x02\x00\x04\xff\xf8\x01\x00\x00\x00\x02{}\x05\x00\x00\x00\x00\x00\x00\x04\x00")
695+
rows.Rows = nil
696+
err = rows.Decode(data)
697+
c.Assert(err, IsNil)
698+
c.Assert(rows.Rows[0][2], DeepEquals, []uint8("{}"))
699+
700+
// after MySQL 5.7.22
701+
data = []byte("l\x00\x00\x00\x00\x00\x01\x00\x02\x00\x04\xff\xff\xf8\x01\x00\x00\x00\x02{}\x05\x00\x00\x00\x00\x00\x00\x04\x00\xf8\x01\x00\x00\x00\n{\"a\":1234}\r\x00\x00\x00\x00\x01\x00\x0c\x00\x0b\x00\x01\x00\x05\xd2\x04a")
702+
rows.Rows = nil
703+
err = rows.Decode(data)
704+
c.Assert(err, IsNil)
705+
c.Assert(rows.Rows[1][2], DeepEquals, []uint8("{}"))
706+
c.Assert(rows.Rows[2][2], DeepEquals, []uint8("{\"a\":1234}"))
707+
708+
data = []byte("l\x00\x00\x00\x00\x00\x01\x00\x02\x00\x04\xff\xff\xf8\x01\x00\x00\x00\n{\"a\":1234}\r\x00\x00\x00\x00\x01\x00\x0c\x00\x0b\x00\x01\x00\x05\xd2\x04a\xf8\x01\x00\x00\x00\x02{}\x05\x00\x00\x00\x00\x00\x00\x04\x00")
709+
rows.Rows = nil
710+
err = rows.Decode(data)
711+
c.Assert(err, IsNil)
712+
c.Assert(rows.Rows[1][2], DeepEquals, []uint8("{\"a\":1234}"))
713+
c.Assert(rows.Rows[2][2], DeepEquals, []uint8("{}"))
714+
715+
// before MySQL 5.7.22
716+
rows.ignoreJSONDecodeErr = true
717+
data = []byte("l\x00\x00\x00\x00\x00\x01\x00\x02\x00\x04\xff\xff\xf8\x01\x00\x00\x00\x02{}\x05\x00\x00\x00\x00\x01\x00\x0c\x00\xf8\x01\x00\x00\x00\n{\"a\":1234}\r\x00\x00\x00\x00\x01\x00\x0c\x00\x0b\x00\x01\x00\x05\xd2\x04a")
718+
rows.Rows = nil
719+
err = rows.Decode(data)
720+
c.Assert(err, IsNil)
721+
c.Assert(rows.Rows[1][2], DeepEquals, []uint8("null"))
722+
c.Assert(rows.Rows[2][2], DeepEquals, []uint8("{\"a\":1234}"))
723+
724+
rows.ignoreJSONDecodeErr = false
725+
data = []byte("l\x00\x00\x00\x00\x00\x01\x00\x02\x00\x04\xff\xff\xf8\x01\x00\x00\x00\n{\"a\":1234}\r\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x01\x00\x05\xd2\x04a\xf8\x01\x00\x00\x00\x02{}\x05\x00\x00\x00\x00\x00\x00\x04\x00")
726+
rows.Rows = nil
727+
err = rows.Decode(data)
728+
c.Assert(err, IsNil)
729+
// this value is wrong in binlog, but can be parsed without error
730+
c.Assert(rows.Rows[1][2], DeepEquals, []uint8("{}"))
731+
c.Assert(rows.Rows[2][2], DeepEquals, []uint8("{}"))
732+
}

0 commit comments

Comments
 (0)