Skip to content

Commit ea02a8a

Browse files
Fix parsing of uint64 fields in a Frame (#806)
* add special case for uint64 values * add jsonitere wrapper. It will be used in Grafana too.
1 parent 48d3ad6 commit ea02a8a

File tree

3 files changed

+192
-18
lines changed

3 files changed

+192
-18
lines changed

data/frame_json.go

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import (
1616
"github.com/apache/arrow/go/v13/arrow/ipc"
1717
jsoniter "github.com/json-iterator/go"
1818
"github.com/mattetti/filebuffer"
19+
20+
"github.com/grafana/grafana-plugin-sdk-go/internal/jsonitere"
1921
)
2022

2123
const simpleTypeString = "string"
@@ -276,14 +278,12 @@ func readFrameData(iter *jsoniter.Iterator, frame *Frame) error {
276278
}
277279

278280
field := frame.Fields[0]
279-
first := make([]interface{}, 0)
280-
iter.ReadVal(&first)
281-
vec, err := jsonValuesToVector(field.Type(), first)
281+
vec, err := jsonValuesToVector(iter, field.Type())
282282
if err != nil {
283283
return err
284284
}
285285
field.vector = vec
286-
size := len(first)
286+
size := vec.Len()
287287

288288
addNanos := func() {
289289
if readNanos {
@@ -420,7 +420,37 @@ func int64FromJSON(v interface{}) (int64, error) {
420420
return 0, fmt.Errorf("unable to convert int64 in json [%T]", v)
421421
}
422422

423-
func jsonValuesToVector(ft FieldType, arr []interface{}) (vector, error) {
423+
func jsonValuesToVector(iter *jsoniter.Iterator, ft FieldType) (vector, error) {
424+
itere := jsonitere.NewIterator(iter)
425+
// we handle Uint64 differently because the regular method for unmarshalling to []any does not work for uint64 correctly
426+
// due to jsoniter parsing logic that automatically converts all numbers to float64.
427+
// We can't use readUint64VectorJSON here because the size of the array is not known and the function requires the length parameter
428+
switch ft {
429+
case FieldTypeUint64:
430+
parseUint64 := func(s string) (uint64, error) {
431+
return strconv.ParseUint(s, 0, 64)
432+
}
433+
u, err := readArrayOfNumbers[uint64](itere, parseUint64, itere.ReadUint64)
434+
if err != nil {
435+
return nil, err
436+
}
437+
return newUint64VectorWithValues(u), nil
438+
case FieldTypeNullableUint64:
439+
parseUint64 := func(s string) (*uint64, error) {
440+
u, err := strconv.ParseUint(s, 0, 64)
441+
if err != nil {
442+
return nil, err
443+
}
444+
return &u, nil
445+
}
446+
u, err := readArrayOfNumbers[*uint64](itere, parseUint64, itere.ReadUint64Pointer)
447+
if err != nil {
448+
return nil, err
449+
}
450+
return newNullableUint64VectorWithValues(u), nil
451+
}
452+
// if it's not uint64 field, handle the array the old way
453+
424454
convert := func(v interface{}) (interface{}, error) {
425455
return v, nil
426456
}
@@ -458,13 +488,6 @@ func jsonValuesToVector(ft FieldType, arr []interface{}) (vector, error) {
458488
iV, err := int64FromJSON(v)
459489
return uint32(iV), err
460490
}
461-
462-
case FieldTypeUint64:
463-
convert = func(v interface{}) (interface{}, error) {
464-
iV, err := int64FromJSON(v)
465-
return uint64(iV), err
466-
}
467-
468491
case FieldTypeInt8:
469492
convert = func(v interface{}) (interface{}, error) {
470493
iV, err := int64FromJSON(v)
@@ -524,6 +547,11 @@ func jsonValuesToVector(ft FieldType, arr []interface{}) (vector, error) {
524547
}
525548
}
526549

550+
arr := make([]interface{}, 0)
551+
err := itere.ReadVal(&arr)
552+
if err != nil {
553+
return nil, err
554+
}
527555
f := NewFieldFromFieldType(ft, len(arr))
528556
for i, v := range arr {
529557
if v != nil {
@@ -537,14 +565,53 @@ func jsonValuesToVector(ft FieldType, arr []interface{}) (vector, error) {
537565
return f.vector, nil
538566
}
539567

540-
// nolint:gocyclo
541-
func readVector(iter *jsoniter.Iterator, ft FieldType, size int) (vector, error) {
542-
if false {
543-
first := make([]interface{}, 0)
544-
iter.ReadVal(&first)
545-
return jsonValuesToVector(ft, first)
568+
func readArrayOfNumbers[T any](iter *jsonitere.Iterator, parse func(string) (T, error), reader func() (T, error)) ([]T, error) {
569+
var def T
570+
var result []T
571+
for {
572+
next, err := iter.ReadArray()
573+
if err != nil {
574+
return nil, err
575+
}
576+
if !next {
577+
break
578+
}
579+
nextType, err := iter.WhatIsNext()
580+
if err != nil {
581+
return nil, err
582+
}
583+
switch nextType {
584+
case jsoniter.StringValue:
585+
str, err := iter.ReadString()
586+
if err != nil {
587+
return nil, err
588+
}
589+
u, err := parse(str)
590+
if err != nil {
591+
return nil, iter.ReportError(fmt.Sprintf("readArrayOfNumbers[%T]", def), "cannot parse string")
592+
}
593+
result = append(result, u)
594+
case jsoniter.NilValue:
595+
_, err := iter.ReadNil()
596+
if err != nil {
597+
return nil, err
598+
}
599+
// add T's default value. For reference type it will be nil, for value types the default value such as 0, false, ""
600+
// This is the same logic as in `read<Type>VectorJSON`
601+
result = append(result, def)
602+
default: // read as a number, if it is not expected field type, there will be error.
603+
u, err := reader()
604+
if err != nil {
605+
return nil, err
606+
}
607+
result = append(result, u)
608+
}
546609
}
610+
return result, nil
611+
}
547612

613+
// nolint:gocyclo
614+
func readVector(iter *jsoniter.Iterator, ft FieldType, size int) (vector, error) {
548615
switch ft {
549616
// Manual
550617
case FieldTypeTime:

data/frame_json_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package data_test
33
import (
44
"encoding/json"
55
"fmt"
6+
"math"
67
"os"
78
"path/filepath"
89
"strings"
@@ -317,6 +318,25 @@ func TestFrame_UnmarshalJSON_DataOnly(t *testing.T) {
317318
require.Error(t, err)
318319
}
319320

321+
func TestFrame_UnmarshallUint64(t *testing.T) {
322+
expected := `
323+
{"schema":{"name":"test","fields":[{"name":"test-field","type":"number","typeInfo":{"frame":"uint64"}}]},"data":{"values":[[18446744073709551615,"18446744073709551615",0,1,2,3,4,5]]}}
324+
`
325+
326+
var actual data.Frame
327+
require.NoError(t, json.Unmarshal([]byte(expected), &actual))
328+
require.Len(t, actual.Fields, 1)
329+
require.Equal(t, data.FieldTypeUint64, actual.Fields[0].Type())
330+
var values []uint64
331+
for i := 0; i < actual.Fields[0].Len(); i++ {
332+
v, ok := actual.Fields[0].ConcreteAt(i)
333+
require.True(t, ok)
334+
require.IsType(t, v, uint64(1))
335+
values = append(values, v.(uint64))
336+
}
337+
require.EqualValues(t, []uint64{math.MaxUint64, math.MaxUint64, 0, 1, 2, 3, 4, 5}, values)
338+
}
339+
320340
// This function will write code to the console that should be copy/pasted into frame_json.gen.go
321341
// when changes are required. Typically this function will always be skipped.
322342
func TestGenerateGenericArrowCode(t *testing.T) {

internal/jsonitere/wrapper.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// package jsonitere wraps json-iterator/go's Iterator methods with error returns
2+
// so linting can catch unchecked errors.
3+
// The underlying iterator's Error property is returned and not reset.
4+
// See json-iterator/go for method documentation and additional methods that
5+
// can be added to this library.
6+
package jsonitere
7+
8+
import (
9+
j "github.com/json-iterator/go"
10+
)
11+
12+
type Iterator struct {
13+
// named property instead of embedded so there is no
14+
// confusion about which method or property is called
15+
i *j.Iterator
16+
}
17+
18+
func NewIterator(i *j.Iterator) *Iterator {
19+
return &Iterator{i}
20+
}
21+
22+
func (iter *Iterator) Read() (interface{}, error) {
23+
return iter.i.Read(), iter.i.Error
24+
}
25+
26+
func (iter *Iterator) ReadAny() (j.Any, error) {
27+
return iter.i.ReadAny(), iter.i.Error
28+
}
29+
30+
func (iter *Iterator) ReadArray() (bool, error) {
31+
return iter.i.ReadArray(), iter.i.Error
32+
}
33+
34+
func (iter *Iterator) ReadObject() (string, error) {
35+
return iter.i.ReadObject(), iter.i.Error
36+
}
37+
38+
func (iter *Iterator) ReadString() (string, error) {
39+
return iter.i.ReadString(), iter.i.Error
40+
}
41+
42+
func (iter *Iterator) WhatIsNext() (j.ValueType, error) {
43+
return iter.i.WhatIsNext(), iter.i.Error
44+
}
45+
46+
func (iter *Iterator) Skip() error {
47+
iter.i.Skip()
48+
return iter.i.Error
49+
}
50+
51+
func (iter *Iterator) ReadVal(obj interface{}) error {
52+
iter.i.ReadVal(obj)
53+
return iter.i.Error
54+
}
55+
56+
func (iter *Iterator) ReadFloat64() (float64, error) {
57+
return iter.i.ReadFloat64(), iter.i.Error
58+
}
59+
60+
func (iter *Iterator) ReadInt8() (int8, error) {
61+
return iter.i.ReadInt8(), iter.i.Error
62+
}
63+
64+
func (iter *Iterator) ReadUint64() (uint64, error) {
65+
return iter.i.ReadUint64(), iter.i.Error
66+
}
67+
68+
func (iter *Iterator) ReadUint64Pointer() (*uint64, error) {
69+
u := iter.i.ReadUint64()
70+
if iter.i.Error != nil {
71+
return nil, iter.i.Error
72+
}
73+
return &u, nil
74+
}
75+
76+
func (iter *Iterator) ReadNil() (bool, error) {
77+
return iter.i.ReadNil(), iter.i.Error
78+
}
79+
80+
func (iter *Iterator) ReadBool() (bool, error) {
81+
return iter.i.ReadBool(), iter.i.Error
82+
}
83+
84+
func (iter *Iterator) ReportError(op, msg string) error {
85+
iter.i.ReportError(op, msg)
86+
return iter.i.Error
87+
}

0 commit comments

Comments
 (0)