Skip to content

Commit d9bf8aa

Browse files
authored
Merge pull request #222 from ydb-platform/json-unmarshaler
Json unmarshaler
2 parents 95fadff + 2ee4c2a commit d9bf8aa

File tree

5 files changed

+149
-4
lines changed

5 files changed

+149
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
* Reimplement sugar.DSN with net/url
1+
* Supported `json.Unmarshaler` type for scanning row to values
2+
* Reimplement `sugar.DSN` with `net/url`
23

34
## v3.21.0
45
* Fixed gtrace tool generation code style bug with leading spaces

internal/table/scanner/scanner.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package scanner
33
import (
44
"bytes"
55
"database/sql"
6+
"encoding/json"
67
"fmt"
78
"io"
89
"math"
@@ -717,6 +718,7 @@ func (s *scanner) trySetByteArray(v interface{}, optional bool, def bool) bool {
717718
return true
718719
}
719720

721+
// nolint: gocyclo
720722
func (s *scanner) scanRequired(value interface{}) {
721723
switch v := value.(type) {
722724
case *bool:
@@ -771,6 +773,19 @@ func (s *scanner) scanRequired(value interface{}) {
771773
if err != nil {
772774
_ = s.errorf(0, "sql.Scanner error: %w", err)
773775
}
776+
case json.Unmarshaler:
777+
var err error
778+
switch s.getType() {
779+
case types.TypeJSON:
780+
err = v.UnmarshalJSON(s.converter.JSON())
781+
case types.TypeJSONDocument:
782+
err = v.UnmarshalJSON(s.converter.JSONDocument())
783+
default:
784+
_ = s.errorf(0, "ydb required type %T not unsupported for applying to json.Unmarshaler", s.getType())
785+
}
786+
if err != nil {
787+
_ = s.errorf(0, "json.Unmarshaler error: %w", err)
788+
}
774789
default:
775790
ok := s.trySetByteArray(v, false, false)
776791
if !ok {
@@ -949,6 +964,20 @@ func (s *scanner) scanOptional(value interface{}, defaultValueForOptional bool)
949964
if err != nil {
950965
_ = s.errorf(0, "sql.Scanner error: %w", err)
951966
}
967+
case json.Unmarshaler:
968+
s.unwrap()
969+
var err error
970+
switch s.getType() {
971+
case types.TypeJSON:
972+
err = v.UnmarshalJSON(s.converter.JSON())
973+
case types.TypeJSONDocument:
974+
err = v.UnmarshalJSON(s.converter.JSONDocument())
975+
default:
976+
_ = s.errorf(0, "ydb optional type %T not unsupported for applying to json.Unmarshaler", s.getType())
977+
}
978+
if err != nil {
979+
_ = s.errorf(0, "json.Unmarshaler error: %w", err)
980+
}
952981
default:
953982
s.unwrap()
954983
ok := s.trySetByteArray(v, true, false)
@@ -1013,6 +1042,11 @@ func (s *scanner) setDefaultValue(dst interface{}) {
10131042
if err != nil {
10141043
_ = s.errorf(0, "ydb.Scanner error: %w", err)
10151044
}
1045+
case json.Unmarshaler:
1046+
err := v.UnmarshalJSON(nil)
1047+
if err != nil {
1048+
_ = s.errorf(0, "json.Unmarshaler error: %w", err)
1049+
}
10161050
default:
10171051
ok := s.trySetByteArray(v, false, true)
10181052
if !ok {

internal/table/scanner/scanner_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package scanner
22

33
import (
44
"encoding/binary"
5+
"encoding/json"
56
"math"
67
"strconv"
78
"testing"
@@ -595,3 +596,112 @@ func TestScanNamed(t *testing.T) {
595596
})
596597
}
597598
}
599+
600+
type jsonUnmarshaller struct {
601+
bytes []byte
602+
}
603+
604+
func (json *jsonUnmarshaller) UnmarshalJSON(bytes []byte) error {
605+
json.bytes = bytes
606+
return nil
607+
}
608+
609+
var _ json.Unmarshaler = &jsonUnmarshaller{}
610+
611+
func TestScanToJsonUnmarshaller(t *testing.T) {
612+
s := initScanner()
613+
for _, test := range []struct {
614+
name string
615+
count int
616+
columns []*column
617+
values []indexed.RequiredOrOptional
618+
}{
619+
{
620+
name: "<optional JSONDocument, required JSON> to json.Unmarshaller",
621+
count: 2,
622+
columns: []*column{{
623+
name: "jsondocument",
624+
typeID: Ydb.Type_JSON_DOCUMENT,
625+
optional: true,
626+
}, {
627+
name: "json",
628+
typeID: Ydb.Type_JSON,
629+
}},
630+
values: []indexed.RequiredOrOptional{
631+
new(jsonUnmarshaller),
632+
new(jsonUnmarshaller),
633+
},
634+
}, {
635+
name: "<required JSONDocument, optional JSON> to json.Unmarshaller",
636+
count: 2,
637+
columns: []*column{{
638+
name: "jsondocument",
639+
typeID: Ydb.Type_JSON_DOCUMENT,
640+
}, {
641+
name: "json",
642+
typeID: Ydb.Type_JSON,
643+
optional: true,
644+
}},
645+
values: []indexed.RequiredOrOptional{
646+
new(jsonUnmarshaller),
647+
new(jsonUnmarshaller),
648+
},
649+
}, {
650+
name: "<optional JSONDocument, optional JSON> to json.Unmarshaller",
651+
count: 2,
652+
columns: []*column{{
653+
name: "jsondocument",
654+
typeID: Ydb.Type_JSON_DOCUMENT,
655+
optional: true,
656+
}, {
657+
name: "json",
658+
typeID: Ydb.Type_JSON,
659+
optional: true,
660+
}},
661+
values: []indexed.RequiredOrOptional{
662+
new(jsonUnmarshaller),
663+
new(jsonUnmarshaller),
664+
},
665+
}, {
666+
name: "<required JSONDocument, required JSON> to json.Unmarshaller",
667+
count: 2,
668+
columns: []*column{{
669+
name: "jsondocument",
670+
typeID: Ydb.Type_JSON_DOCUMENT,
671+
}, {
672+
name: "json",
673+
typeID: Ydb.Type_JSON,
674+
}},
675+
values: []indexed.RequiredOrOptional{
676+
new(jsonUnmarshaller),
677+
new(jsonUnmarshaller),
678+
},
679+
},
680+
} {
681+
set, _ := getResultSet(test.count, test.columns)
682+
s.reset(set)
683+
for s.NextRow() {
684+
values := make([]indexed.RequiredOrOptional, 0, len(test.values))
685+
for i, col := range test.columns {
686+
if col.optional {
687+
values = append(
688+
values,
689+
indexed.Optional(
690+
test.values[i],
691+
),
692+
)
693+
} else {
694+
values = append(
695+
values,
696+
indexed.Required(
697+
test.values[i],
698+
),
699+
)
700+
}
701+
}
702+
if err := s.Scan(values...); err != nil {
703+
t.Fatalf("test: %s; error: %s", test.name, err)
704+
}
705+
}
706+
}
707+
}

table/result/indexed/indexed.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ package indexed
55
//
66
// This is a proxy type for preparing go1.18 type set constrains such as
77
// type Required interface {
8-
// *int8 | *int64 | *string | types.Scanner
8+
// *int8 | *int64 | *string | types.Scanner | json.Unmarshaler
99
// }
1010
type Required interface{}
1111

@@ -14,7 +14,7 @@ type Required interface{}
1414
//
1515
// This is a proxy type for preparing go1.18 type set constrains such as
1616
// type Optional interface {
17-
// **int8 | **int64 | **string | types.Scanner
17+
// **int8 | **int64 | **string | types.Scanner | json.Unmarshaler
1818
// }
1919
// or alias such as
2020
// type Optional *Required

table/result/result.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ type result interface {
9292
// time.Time
9393
// time.Duration
9494
// ydb.Value
95-
// For custom types implement sql.Scanner interface.
95+
// For custom types implement sql.Scanner or json.Unmarshaler interface.
9696
// For optional types use double pointer construction.
9797
// For unknown types use interface types.
9898
// Supported scanning byte arrays of various length.

0 commit comments

Comments
 (0)