Skip to content

Commit fda5d25

Browse files
benjirewisBenjamin Rewis
authored andcommitted
GODRIVER-1846 Add bsoncore.Array type with new codec (#571)
1 parent 4b2134d commit fda5d25

13 files changed

+778
-34
lines changed

bson/bson_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/stretchr/testify/require"
1919
"go.mongodb.org/mongo-driver/bson/bsoncodec"
2020
"go.mongodb.org/mongo-driver/bson/bsonoptions"
21+
"go.mongodb.org/mongo-driver/bson/bsontype"
2122
"go.mongodb.org/mongo-driver/internal/testutil/assert"
2223
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
2324
)
@@ -202,3 +203,38 @@ func TestExtJSONEscapeKey(t *testing.T) {
202203
t.Errorf("Unmarshaled documents do not match. got %v; want %v", got, doc)
203204
}
204205
}
206+
207+
func TestBsoncoreArray(t *testing.T) {
208+
type BSONDocumentArray struct {
209+
Array []D `bson:"array"`
210+
}
211+
212+
type BSONArray struct {
213+
Array bsoncore.Array `bson:"array"`
214+
}
215+
216+
bda := BSONDocumentArray{
217+
Array: []D{
218+
{{"x", 1}},
219+
{{"x", 2}},
220+
{{"x", 3}},
221+
},
222+
}
223+
224+
expectedBSON, err := Marshal(bda)
225+
assert.Nil(t, err, "Marshal bsoncore.Document array error: %v", err)
226+
227+
var ba BSONArray
228+
err = Unmarshal(expectedBSON, &ba)
229+
assert.Nil(t, err, "Unmarshal error: %v", err)
230+
231+
actualBSON, err := Marshal(ba)
232+
assert.Nil(t, err, "Marshal bsoncore.Array error: %v", err)
233+
234+
assert.Equal(t, expectedBSON, actualBSON,
235+
"expected BSON to be %v after Marshalling again; got %v", expectedBSON, actualBSON)
236+
237+
doc := bsoncore.Document(actualBSON)
238+
v := doc.Lookup("array")
239+
assert.Equal(t, bsontype.Array, v.Type, "expected type array, got %v", v.Type)
240+
}

bson/bsoncodec/array_codec.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (C) MongoDB, Inc. 2017-present.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
// not use this file except in compliance with the License. You may obtain
5+
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
package bsoncodec
8+
9+
import (
10+
"reflect"
11+
12+
"go.mongodb.org/mongo-driver/bson/bsonrw"
13+
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
14+
)
15+
16+
// ArrayCodec is the Codec used for bsoncore.Array values.
17+
type ArrayCodec struct{}
18+
19+
var defaultArrayCodec = NewArrayCodec()
20+
21+
// NewArrayCodec returns an ArrayCodec.
22+
func NewArrayCodec() *ArrayCodec {
23+
return &ArrayCodec{}
24+
}
25+
26+
// EncodeValue is the ValueEncoder for bsoncore.Array values.
27+
func (ac *ArrayCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
28+
if !val.IsValid() || val.Type() != tCoreArray {
29+
return ValueEncoderError{Name: "CoreArrayEncodeValue", Types: []reflect.Type{tCoreArray}, Received: val}
30+
}
31+
32+
arr := val.Interface().(bsoncore.Array)
33+
return bsonrw.Copier{}.CopyArrayFromBytes(vw, arr)
34+
}
35+
36+
// DecodeValue is the ValueDecoder for bsoncore.Array values.
37+
func (ac *ArrayCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
38+
if !val.CanSet() || val.Type() != tCoreArray {
39+
return ValueDecoderError{Name: "CoreArrayDecodeValue", Types: []reflect.Type{tCoreArray}, Received: val}
40+
}
41+
42+
if val.IsNil() {
43+
val.Set(reflect.MakeSlice(val.Type(), 0, 0))
44+
}
45+
46+
val.SetLen(0)
47+
arr, err := bsonrw.Copier{}.AppendArrayBytes(val.Interface().(bsoncore.Array), vr)
48+
val.Set(reflect.ValueOf(arr))
49+
return err
50+
}

bson/bsoncodec/default_value_decoders.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ func (dvd DefaultValueDecoders) RegisterDefaultDecoders(rb *RegistryBuilder) {
8080
RegisterTypeDecoder(tByteSlice, defaultByteSliceCodec).
8181
RegisterTypeDecoder(tTime, defaultTimeCodec).
8282
RegisterTypeDecoder(tEmpty, defaultEmptyInterfaceCodec).
83+
RegisterTypeDecoder(tCoreArray, defaultArrayCodec).
8384
RegisterTypeDecoder(tOID, decodeAdapter{dvd.ObjectIDDecodeValue, dvd.objectIDDecodeType}).
8485
RegisterTypeDecoder(tDecimal, decodeAdapter{dvd.Decimal128DecodeValue, dvd.decimal128DecodeType}).
8586
RegisterTypeDecoder(tJSONNumber, decodeAdapter{dvd.JSONNumberDecodeValue, dvd.jsonNumberDecodeType}).

bson/bsoncodec/default_value_decoders_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2290,6 +2290,36 @@ func TestDefaultValueDecoders(t *testing.T) {
22902290
},
22912291
},
22922292
},
2293+
{
2294+
"CoreArrayDecodeValue",
2295+
defaultArrayCodec,
2296+
[]subtest{
2297+
{
2298+
"wrong type",
2299+
wrong,
2300+
nil,
2301+
&bsonrwtest.ValueReaderWriter{},
2302+
bsonrwtest.Nothing,
2303+
ValueDecoderError{
2304+
Name: "CoreArrayDecodeValue",
2305+
Types: []reflect.Type{tCoreArray},
2306+
Received: reflect.ValueOf(wrong),
2307+
},
2308+
},
2309+
{
2310+
"*bsoncore.Array is nil",
2311+
(*bsoncore.Array)(nil),
2312+
nil,
2313+
nil,
2314+
bsonrwtest.Nothing,
2315+
ValueDecoderError{
2316+
Name: "CoreArrayDecodeValue",
2317+
Types: []reflect.Type{tCoreArray},
2318+
Received: reflect.ValueOf((*bsoncore.Array)(nil)),
2319+
},
2320+
},
2321+
},
2322+
},
22932323
}
22942324

22952325
for _, tc := range testCases {

bson/bsoncodec/default_value_encoders.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ func (dve DefaultValueEncoders) RegisterDefaultEncoders(rb *RegistryBuilder) {
7070
RegisterTypeEncoder(tByteSlice, defaultByteSliceCodec).
7171
RegisterTypeEncoder(tTime, defaultTimeCodec).
7272
RegisterTypeEncoder(tEmpty, defaultEmptyInterfaceCodec).
73+
RegisterTypeEncoder(tCoreArray, defaultArrayCodec).
7374
RegisterTypeEncoder(tOID, ValueEncoderFunc(dve.ObjectIDEncodeValue)).
7475
RegisterTypeEncoder(tDecimal, ValueEncoderFunc(dve.Decimal128EncodeValue)).
7576
RegisterTypeEncoder(tJSONNumber, ValueEncoderFunc(dve.JSONNumberEncodeValue)).

bson/bsoncodec/default_value_encoders_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,53 @@ func TestDefaultValueEncoders(t *testing.T) {
11401140
},
11411141
},
11421142
},
1143+
{
1144+
"CoreArrayEncodeValue",
1145+
defaultArrayCodec,
1146+
[]subtest{
1147+
{
1148+
"wrong type",
1149+
wrong,
1150+
nil,
1151+
nil,
1152+
bsonrwtest.Nothing,
1153+
ValueEncoderError{
1154+
Name: "CoreArrayEncodeValue",
1155+
Types: []reflect.Type{tCoreArray},
1156+
Received: reflect.ValueOf(wrong),
1157+
},
1158+
},
1159+
1160+
{
1161+
"WriteArray Error",
1162+
bsoncore.Array{},
1163+
nil,
1164+
&bsonrwtest.ValueReaderWriter{Err: errors.New("wa error"), ErrAfter: bsonrwtest.WriteArray},
1165+
bsonrwtest.WriteArray,
1166+
errors.New("wa error"),
1167+
},
1168+
{
1169+
"WriteArrayElement Error",
1170+
bsoncore.Array(buildDocumentArray(func(doc []byte) []byte {
1171+
return bsoncore.AppendNullElement(nil, "foo")
1172+
})),
1173+
nil,
1174+
&bsonrwtest.ValueReaderWriter{Err: errors.New("wae error"), ErrAfter: bsonrwtest.WriteArrayElement},
1175+
bsonrwtest.WriteArrayElement,
1176+
errors.New("wae error"),
1177+
},
1178+
{
1179+
"encodeValue error",
1180+
bsoncore.Array(buildDocumentArray(func(doc []byte) []byte {
1181+
return bsoncore.AppendNullElement(nil, "foo")
1182+
})),
1183+
nil,
1184+
&bsonrwtest.ValueReaderWriter{Err: errors.New("ev error"), ErrAfter: bsonrwtest.WriteNull},
1185+
bsonrwtest.WriteNull,
1186+
errors.New("ev error"),
1187+
},
1188+
},
1189+
},
11431190
}
11441191

11451192
for _, tc := range testCases {

bson/bsoncodec/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,4 @@ var tA = reflect.TypeOf(primitive.A{})
7979
var tE = reflect.TypeOf(primitive.E{})
8080

8181
var tCoreDocument = reflect.TypeOf(bsoncore.Document{})
82+
var tCoreArray = reflect.TypeOf(bsoncore.Array{})

bson/bsonrw/copier.go

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,22 @@ func (c Copier) CopyDocument(dst ValueWriter, src ValueReader) error {
4545
return c.copyDocumentCore(dw, dr)
4646
}
4747

48+
// CopyArrayFromBytes copies the values from a BSON array represented as a
49+
// []byte to a ValueWriter.
50+
func (c Copier) CopyArrayFromBytes(dst ValueWriter, src []byte) error {
51+
aw, err := dst.WriteArray()
52+
if err != nil {
53+
return err
54+
}
55+
56+
err = c.CopyBytesToArrayWriter(aw, src)
57+
if err != nil {
58+
return err
59+
}
60+
61+
return aw.WriteArrayEnd()
62+
}
63+
4864
// CopyDocumentFromBytes copies the values from a BSON document represented as a
4965
// []byte to a ValueWriter.
5066
func (c Copier) CopyDocumentFromBytes(dst ValueWriter, src []byte) error {
@@ -61,9 +77,29 @@ func (c Copier) CopyDocumentFromBytes(dst ValueWriter, src []byte) error {
6177
return dw.WriteDocumentEnd()
6278
}
6379

80+
type writeElementFn func(key string) (ValueWriter, error)
81+
82+
// CopyBytesToArrayWriter copies the values from a BSON Array represented as a []byte to an
83+
// ArrayWriter.
84+
func (c Copier) CopyBytesToArrayWriter(dst ArrayWriter, src []byte) error {
85+
wef := func(_ string) (ValueWriter, error) {
86+
return dst.WriteArrayElement()
87+
}
88+
89+
return c.copyBytesToValueWriter(src, wef)
90+
}
91+
6492
// CopyBytesToDocumentWriter copies the values from a BSON document represented as a []byte to a
6593
// DocumentWriter.
6694
func (c Copier) CopyBytesToDocumentWriter(dst DocumentWriter, src []byte) error {
95+
wef := func(key string) (ValueWriter, error) {
96+
return dst.WriteDocumentElement(key)
97+
}
98+
99+
return c.copyBytesToValueWriter(src, wef)
100+
}
101+
102+
func (c Copier) copyBytesToValueWriter(src []byte, wef writeElementFn) error {
67103
// TODO(skriptble): Create errors types here. Anything thats a tag should be a property.
68104
length, rem, ok := bsoncore.ReadLength(src)
69105
if !ok {
@@ -93,15 +129,18 @@ func (c Copier) CopyBytesToDocumentWriter(dst DocumentWriter, src []byte) error
93129
if !ok {
94130
return fmt.Errorf("invalid key found. remaining bytes=%v", rem)
95131
}
96-
dvw, err := dst.WriteDocumentElement(key)
132+
133+
// write as either array element or document element using writeElementFn
134+
vw, err := wef(key)
97135
if err != nil {
98136
return err
99137
}
138+
100139
val, rem, ok = bsoncore.ReadValue(rem, t)
101140
if !ok {
102141
return fmt.Errorf("not enough bytes available to read type. bytes=%d type=%s", len(rem), t)
103142
}
104-
err = c.CopyValueFromBytes(dvw, t, val.Data)
143+
err = c.CopyValueFromBytes(vw, t, val.Data)
105144
if err != nil {
106145
return err
107146
}
@@ -133,6 +172,23 @@ func (c Copier) AppendDocumentBytes(dst []byte, src ValueReader) ([]byte, error)
133172
return dst, err
134173
}
135174

175+
// AppendArrayBytes copies an array from the ValueReader to dst.
176+
func (c Copier) AppendArrayBytes(dst []byte, src ValueReader) ([]byte, error) {
177+
if br, ok := src.(BytesReader); ok {
178+
_, dst, err := br.ReadValueBytes(dst)
179+
return dst, err
180+
}
181+
182+
vw := vwPool.Get().(*valueWriter)
183+
defer vwPool.Put(vw)
184+
185+
vw.reset(dst)
186+
187+
err := c.copyArray(vw, src)
188+
dst = vw.buf
189+
return dst, err
190+
}
191+
136192
// CopyValueFromBytes will write the value represtend by t and src to dst.
137193
func (c Copier) CopyValueFromBytes(dst ValueWriter, t bsontype.Type, src []byte) error {
138194
if wvb, ok := dst.(BytesWriter); ok {

bson/raw_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func TestRaw(t *testing.T) {
4747
}
4848
})
4949
t.Run("InvalidLength", func(t *testing.T) {
50-
want := bsoncore.DocumentValidationError("document length exceeds available bytes. length=200 remainingBytes=5")
50+
want := bsoncore.ValidationError("document length exceeds available bytes. length=200 remainingBytes=5")
5151
r := make(Raw, 5)
5252
binary.LittleEndian.PutUint32(r[0:4], 200)
5353
got := r.Validate()

0 commit comments

Comments
 (0)