@@ -5,9 +5,10 @@ import (
55"fmt"
66"math"
77
8- . "github.com/go-mysql-org/go-mysql/mysql"
98"github.com/pingcap/errors"
109"github.com/siddontang/go/hack"
10+
11+ . "github.com/go-mysql-org/go-mysql/mysql"
1112)
1213
1314const (
@@ -44,6 +45,60 @@ const (
4445jsonbValueEntrySizeLarge = 1 + jsonbLargeOffsetSize
4546)
4647
48+ var (
49+ ErrCorruptedJSONDiff = fmt .Errorf ("corrupted JSON diff" ) // ER_CORRUPTED_JSON_DIFF
50+ )
51+
52+ type (
53+ // JsonDiffOperation is an enum that describes what kind of operation a JsonDiff object represents.
54+ // https://github.com/mysql/mysql-server/blob/8.0/sql/json_diff.h
55+ JsonDiffOperation byte
56+ )
57+
58+ const (
59+ // The JSON value in the given path is replaced with a new value.
60+ //
61+ // It has the same effect as `JSON_REPLACE(col, path, value)`.
62+ JsonDiffOperationReplace = JsonDiffOperation (iota )
63+
64+ // Add a new element at the given path.
65+ //
66+ // If the path specifies an array element, it has the same effect as `JSON_ARRAY_INSERT(col, path, value)`.
67+ //
68+ // If the path specifies an object member, it has the same effect as `JSON_INSERT(col, path, value)`.
69+ JsonDiffOperationInsert
70+
71+ // The JSON value at the given path is removed from an array or object.
72+ //
73+ // It has the same effect as `JSON_REMOVE(col, path)`.
74+ JsonDiffOperationRemove
75+ )
76+
77+ type (
78+ JsonDiff struct {
79+ Op JsonDiffOperation
80+ Path string
81+ Value string
82+ }
83+ )
84+
85+ func (op JsonDiffOperation ) String () string {
86+ switch op {
87+ case JsonDiffOperationReplace :
88+ return "Replace"
89+ case JsonDiffOperationInsert :
90+ return "Insert"
91+ case JsonDiffOperationRemove :
92+ return "Remove"
93+ default :
94+ return fmt .Sprintf ("Unknown(%d)" , op )
95+ }
96+ }
97+
98+ func (jd * JsonDiff ) String () string {
99+ return fmt .Sprintf ("json_diff(op:%s path:%s value:%s)" , jd .Op , jd .Path , jd .Value )
100+ }
101+
47102func jsonbGetOffsetSize (isSmall bool ) int {
48103if isSmall {
49104return jsonbSmallOffsetSize
@@ -71,11 +126,6 @@ func jsonbGetValueEntrySize(isSmall bool) int {
71126// decodeJsonBinary decodes the JSON binary encoding data and returns
72127// the common JSON encoding data.
73128func (e * RowsEvent ) decodeJsonBinary (data []byte ) ([]byte , error ) {
74- // Sometimes, we can insert a NULL JSON even we set the JSON field as NOT NULL.
75- // If we meet this case, we can return an empty slice.
76- if len (data ) == 0 {
77- return []byte {}, nil
78- }
79129d := jsonBinaryDecoder {
80130useDecimal : e .useDecimal ,
81131ignoreDecodeErr : e .ignoreJSONDecodeErr ,
@@ -491,3 +541,43 @@ func (d *jsonBinaryDecoder) decodeVariableLength(data []byte) (int, int) {
491541
492542return 0 , 0
493543}
544+
545+ func (e * RowsEvent ) decodeJsonPartialBinary (data []byte ) (* JsonDiff , error ) {
546+ // see Json_diff_vector::read_binary() in mysql-server/sql/json_diff.cc
547+ operationNumber := JsonDiffOperation (data [0 ])
548+ switch operationNumber {
549+ case JsonDiffOperationReplace :
550+ case JsonDiffOperationInsert :
551+ case JsonDiffOperationRemove :
552+ default :
553+ return nil , ErrCorruptedJSONDiff
554+ }
555+ data = data [1 :]
556+
557+ pathLength , _ , n := LengthEncodedInt (data )
558+ data = data [n :]
559+
560+ path := data [:pathLength ]
561+ data = data [pathLength :]
562+
563+ diff := & JsonDiff {
564+ Op : operationNumber ,
565+ Path : string (path ),
566+ // Value will be filled below
567+ }
568+
569+ if operationNumber == JsonDiffOperationRemove {
570+ return diff , nil
571+ }
572+
573+ valueLength , _ , n := LengthEncodedInt (data )
574+ data = data [n :]
575+
576+ d , err := e .decodeJsonBinary (data [:valueLength ])
577+ if err != nil {
578+ return nil , fmt .Errorf ("cannot read json diff for field %q: %w" , path , err )
579+ }
580+ diff .Value = string (d )
581+
582+ return diff , nil
583+ }
0 commit comments