@@ -18,6 +18,8 @@ import (
18
18
"bytes"
19
19
"context"
20
20
goJSON "encoding/json"
21
+ "fmt"
22
+ "hash/crc32"
21
23
"strconv"
22
24
"strings"
23
25
@@ -35,6 +37,7 @@ import (
35
37
36
38
var (
37
39
_ functionClass = & jsonTypeFunctionClass {}
40
+ _ functionClass = & jsonSumCRC32FunctionClass {}
38
41
_ functionClass = & jsonExtractFunctionClass {}
39
42
_ functionClass = & jsonUnquoteFunctionClass {}
40
43
_ functionClass = & jsonQuoteFunctionClass {}
64
67
_ functionClass = & jsonLengthFunctionClass {}
65
68
66
69
_ builtinFunc = & builtinJSONTypeSig {}
70
+ _ builtinFunc = & builtinJSONSumCRC32Sig {}
67
71
_ builtinFunc = & builtinJSONQuoteSig {}
68
72
_ builtinFunc = & builtinJSONUnquoteSig {}
69
73
_ builtinFunc = & builtinJSONArraySig {}
@@ -180,6 +184,112 @@ func (b *builtinJSONTypeSig) evalString(ctx EvalContext, row chunk.Row) (val str
180
184
return j .Type (), false , nil
181
185
}
182
186
187
+ type jsonSumCRC32FunctionClass struct {
188
+ baseFunctionClass
189
+
190
+ tp * types.FieldType
191
+ }
192
+
193
+ func (c * jsonSumCRC32FunctionClass ) verifyArgs (ctx EvalContext , args []Expression ) error {
194
+ if err := c .baseFunctionClass .verifyArgs (args ); err != nil {
195
+ return err
196
+ }
197
+
198
+ if args [0 ].GetType (ctx ).EvalType () != types .ETJson {
199
+ return ErrInvalidTypeForJSON .GenWithStackByArgs (1 , "JSON_SUM_CRC32" )
200
+ }
201
+
202
+ return nil
203
+ }
204
+
205
+ func (c * jsonSumCRC32FunctionClass ) getFunction (ctx BuildContext , args []Expression ) (sig builtinFunc , err error ) {
206
+ if err := c .verifyArgs (ctx .GetEvalCtx (), args ); err != nil {
207
+ return nil , err
208
+ }
209
+ arrayType := c .tp .ArrayType ()
210
+ switch arrayType .GetType () {
211
+ case mysql .TypeYear , mysql .TypeJSON , mysql .TypeFloat , mysql .TypeNewDecimal :
212
+ return nil , ErrNotSupportedYet .GenWithStackByArgs (fmt .Sprintf ("calculating json_sum_crc32 on array of %s" , arrayType .String ()))
213
+ }
214
+ if arrayType .EvalType () == types .ETString && arrayType .GetCharset () != charset .CharsetUTF8MB4 && arrayType .GetCharset () != charset .CharsetBin {
215
+ return nil , ErrNotSupportedYet .GenWithStackByArgs ("unsupported charset" )
216
+ }
217
+ if arrayType .EvalType () == types .ETString && arrayType .GetFlen () == types .UnspecifiedLength {
218
+ return nil , ErrNotSupportedYet .GenWithStackByArgs ("calculating json_sum_crc32 on array of char/binary BLOBs with unspecified length" )
219
+ }
220
+
221
+ bf , err := newBaseBuiltinFunc (ctx , c .funcName , args , c .tp )
222
+ if err != nil {
223
+ return nil , err
224
+ }
225
+ sig = & builtinJSONSumCRC32Sig {bf }
226
+ return sig , nil
227
+ }
228
+
229
+ type builtinJSONSumCRC32Sig struct {
230
+ baseBuiltinFunc
231
+ }
232
+
233
+ func (b * builtinJSONSumCRC32Sig ) Clone () builtinFunc {
234
+ newSig := & builtinJSONSumCRC32Sig {}
235
+ newSig .cloneFrom (& b .baseBuiltinFunc )
236
+ return newSig
237
+ }
238
+
239
+ func (b * builtinJSONSumCRC32Sig ) evalInt (ctx EvalContext , row chunk.Row ) (res int64 , isNull bool , err error ) {
240
+ val , isNull , err := b .args [0 ].EvalJSON (ctx , row )
241
+ if isNull || err != nil {
242
+ return res , isNull , err
243
+ }
244
+
245
+ if val .TypeCode != types .JSONTypeCodeArray {
246
+ return 0 , false , ErrInvalidTypeForJSON .GenWithStackByArgs (1 , "JSON_SUM_CRC32" )
247
+ }
248
+
249
+ ft := b .tp .ArrayType ()
250
+ f := convertJSON2Tp (ft .EvalType ())
251
+ if f == nil {
252
+ return 0 , false , ErrNotSupportedYet .GenWithStackByArgs (fmt .Sprintf ("calculating sum of %s" , ft .String ()))
253
+ }
254
+
255
+ var sum int64
256
+ for i := range val .GetElemCount () {
257
+ item , err := f (fakeSctx , val .ArrayGetElem (i ), ft )
258
+ if err != nil {
259
+ if ErrInvalidJSONForFuncIndex .Equal (err ) {
260
+ err = errors .Errorf ("Invalid JSON value for CAST to type %s" , ft .CompactStr ())
261
+ }
262
+ return 0 , false , err
263
+ }
264
+ sum += int64 (crc32 .ChecksumIEEE (fmt .Appendf (nil , "%v" , item )))
265
+ }
266
+
267
+ return sum , false , err
268
+ }
269
+
270
+ // BuildJSONSumCrc32FunctionWithCheck builds a JSON_SUM_CRC32 ScalarFunction from the Expression and return error if any.
271
+ // The logic is almost the same as build CAST function, except that the return type is fixed to bigint.
272
+ func BuildJSONSumCrc32FunctionWithCheck (ctx BuildContext , expr Expression , tp * types.FieldType ) (res Expression , err error ) {
273
+ argType := expr .GetType (ctx .GetEvalCtx ())
274
+ // If source argument's nullable, then target type should be nullable
275
+ if ! mysql .HasNotNullFlag (argType .GetFlag ()) {
276
+ tp .DelFlag (mysql .NotNullFlag )
277
+ }
278
+ expr = TryPushCastIntoControlFunctionForHybridType (ctx , expr , tp )
279
+
280
+ if tp .EvalType () != types .ETJson || ! tp .IsArray () {
281
+ return nil , errors .Errorf ("json_sum_crc32 can only built on JSON array, got type %s" , tp .EvalType ())
282
+ }
283
+
284
+ fc := & jsonSumCRC32FunctionClass {baseFunctionClass {ast .JSONSumCrc32 , 1 , 1 }, tp }
285
+ f , err := fc .getFunction (ctx , []Expression {expr })
286
+ return & ScalarFunction {
287
+ FuncName : ast .NewCIStr (ast .JSONSumCrc32 ),
288
+ RetType : types .NewFieldType (mysql .TypeLonglong ),
289
+ Function : f ,
290
+ }, err
291
+ }
292
+
183
293
type jsonExtractFunctionClass struct {
184
294
baseFunctionClass
185
295
}
0 commit comments