Skip to content

Commit c9d1001

Browse files
authored
Merge branch 'master' into godriver3475-fix-createcoll-doc
2 parents d3e6168 + 5afecc5 commit c9d1001

39 files changed

+830
-206
lines changed

bson/bson_binary_vector_spec_test.go

Lines changed: 24 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ package bson
99
import (
1010
"encoding/hex"
1111
"encoding/json"
12-
"fmt"
1312
"math"
1413
"os"
1514
"path"
@@ -64,53 +63,13 @@ func TestBsonBinaryVectorSpec(t *testing.T) {
6463
})
6564
}
6665

67-
t.Run("FLOAT32 with padding", func(t *testing.T) {
68-
t.Parallel()
69-
70-
t.Run("Unmarshaling", func(t *testing.T) {
71-
val := D{{"vector", Binary{Subtype: TypeBinaryVector, Data: []byte{Float32Vector, 3}}}}
72-
b, err := Marshal(val)
73-
require.NoError(t, err, "marshaling test BSON")
74-
var got struct {
75-
Vector Vector
76-
}
77-
err = Unmarshal(b, &got)
78-
require.ErrorContains(t, err, errNonZeroVectorPadding.Error())
79-
})
80-
})
81-
82-
t.Run("INT8 with padding", func(t *testing.T) {
83-
t.Parallel()
84-
85-
t.Run("Unmarshaling", func(t *testing.T) {
86-
val := D{{"vector", Binary{Subtype: TypeBinaryVector, Data: []byte{Int8Vector, 3}}}}
87-
b, err := Marshal(val)
88-
require.NoError(t, err, "marshaling test BSON")
89-
var got struct {
90-
Vector Vector
91-
}
92-
err = Unmarshal(b, &got)
93-
require.ErrorContains(t, err, errNonZeroVectorPadding.Error())
94-
})
95-
})
96-
9766
t.Run("Padding specified with no vector data PACKED_BIT", func(t *testing.T) {
9867
t.Parallel()
9968

10069
t.Run("Marshaling", func(t *testing.T) {
10170
_, err := NewPackedBitVector(nil, 1)
10271
require.EqualError(t, err, errNonZeroVectorPadding.Error())
10372
})
104-
t.Run("Unmarshaling", func(t *testing.T) {
105-
val := D{{"vector", Binary{Subtype: TypeBinaryVector, Data: []byte{PackedBitVector, 1}}}}
106-
b, err := Marshal(val)
107-
require.NoError(t, err, "marshaling test BSON")
108-
var got struct {
109-
Vector Vector
110-
}
111-
err = Unmarshal(b, &got)
112-
require.ErrorContains(t, err, errNonZeroVectorPadding.Error())
113-
})
11473
})
11574

11675
t.Run("Exceeding maximum padding PACKED_BIT", func(t *testing.T) {
@@ -120,47 +79,9 @@ func TestBsonBinaryVectorSpec(t *testing.T) {
12079
_, err := NewPackedBitVector(nil, 8)
12180
require.EqualError(t, err, errVectorPaddingTooLarge.Error())
12281
})
123-
t.Run("Unmarshaling", func(t *testing.T) {
124-
val := D{{"vector", Binary{Subtype: TypeBinaryVector, Data: []byte{PackedBitVector, 8}}}}
125-
b, err := Marshal(val)
126-
require.NoError(t, err, "marshaling test BSON")
127-
var got struct {
128-
Vector Vector
129-
}
130-
err = Unmarshal(b, &got)
131-
require.ErrorContains(t, err, errVectorPaddingTooLarge.Error())
132-
})
13382
})
13483
}
13584

136-
// TODO: This test may be added into the spec tests.
137-
func TestFloat32VectorWithInsufficientData(t *testing.T) {
138-
t.Parallel()
139-
140-
val := Binary{Subtype: TypeBinaryVector}
141-
142-
for _, tc := range [][]byte{
143-
{Float32Vector, 0, 42},
144-
{Float32Vector, 0, 42, 42},
145-
{Float32Vector, 0, 42, 42, 42},
146-
147-
{Float32Vector, 0, 42, 42, 42, 42, 42},
148-
{Float32Vector, 0, 42, 42, 42, 42, 42, 42},
149-
{Float32Vector, 0, 42, 42, 42, 42, 42, 42, 42},
150-
} {
151-
t.Run(fmt.Sprintf("marshaling %d bytes", len(tc)-2), func(t *testing.T) {
152-
val.Data = tc
153-
b, err := Marshal(D{{"vector", val}})
154-
require.NoError(t, err, "marshaling test BSON")
155-
var got struct {
156-
Vector Vector
157-
}
158-
err = Unmarshal(b, &got)
159-
require.ErrorContains(t, err, errInsufficientVectorData.Error())
160-
})
161-
}
162-
}
163-
16485
func convertSlice[T int8 | float32 | byte](s []interface{}) []T {
16586
v := make([]T, len(s))
16687
for i, e := range s {
@@ -208,33 +129,44 @@ func runBsonBinaryVectorTest(t *testing.T, testKey string, test bsonBinaryVector
208129

209130
t.Run("Unmarshaling", func(t *testing.T) {
210131
skipCases := map[string]string{
211-
"FLOAT32 with padding": "run in alternative case",
212-
"Overflow Vector INT8": "compile-time restriction",
213-
"Underflow Vector INT8": "compile-time restriction",
214-
"INT8 with padding": "run in alternative case",
215-
"INT8 with float inputs": "compile-time restriction",
216-
"Overflow Vector PACKED_BIT": "compile-time restriction",
217-
"Underflow Vector PACKED_BIT": "compile-time restriction",
218-
"Vector with float values PACKED_BIT": "compile-time restriction",
219-
"Padding specified with no vector data PACKED_BIT": "run in alternative case",
220-
"Exceeding maximum padding PACKED_BIT": "run in alternative case",
221-
"Negative padding PACKED_BIT": "compile-time restriction",
132+
"Overflow Vector INT8": "compile-time restriction",
133+
"Underflow Vector INT8": "compile-time restriction",
134+
"INT8 with float inputs": "compile-time restriction",
135+
"Overflow Vector PACKED_BIT": "compile-time restriction",
136+
"Underflow Vector PACKED_BIT": "compile-time restriction",
137+
"Vector with float values PACKED_BIT": "compile-time restriction",
138+
"Negative padding PACKED_BIT": "compile-time restriction",
222139
}
223140
if reason, ok := skipCases[test.Description]; ok {
224141
t.Skipf("skip test case %s: %s", test.Description, reason)
225142
}
226143

144+
errMap := map[string]string{
145+
"FLOAT32 with padding": "padding must be 0",
146+
"INT8 with padding": "padding must be 0",
147+
"Padding specified with no vector data PACKED_BIT": "padding must be 0",
148+
"Exceeding maximum padding PACKED_BIT": "padding cannot be larger than 7",
149+
}
150+
227151
t.Parallel()
228152

229153
var got map[string]Vector
230154
err := Unmarshal(testBSON, &got)
231-
require.NoError(t, err)
232-
require.Equal(t, testVector, got)
155+
if test.Valid {
156+
require.NoError(t, err)
157+
require.Equal(t, testVector, got)
158+
} else if errMsg, ok := errMap[test.Description]; ok {
159+
require.ErrorContains(t, err, errMsg)
160+
} else {
161+
require.Error(t, err)
162+
}
233163
})
234164

235165
t.Run("Marshaling", func(t *testing.T) {
236166
skipCases := map[string]string{
237167
"FLOAT32 with padding": "private padding field",
168+
"Insufficient vector data with 3 bytes FLOAT32": "invalid case",
169+
"Insufficient vector data with 5 bytes FLOAT32": "invalid case",
238170
"Overflow Vector INT8": "compile-time restriction",
239171
"Underflow Vector INT8": "compile-time restriction",
240172
"INT8 with padding": "private padding field",

bson/default_value_decoders.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1221,7 +1221,13 @@ func valueUnmarshalerDecodeValue(_ DecodeContext, vr ValueReader, val reflect.Va
12211221
return ValueDecoderError{Name: "ValueUnmarshalerDecodeValue", Types: []reflect.Type{tValueUnmarshaler}, Received: val}
12221222
}
12231223

1224-
if vr.Type() == TypeNull {
1224+
// If BSON value is null and the go value is a pointer, then don't call
1225+
// UnmarshalBSONValue. Even if the Go pointer is already initialized (i.e.,
1226+
// non-nil), encountering null in BSON will result in the pointer being
1227+
// directly set to nil here. Since the pointer is being replaced with nil,
1228+
// there is no opportunity (or reason) for the custom UnmarshalBSONValue logic
1229+
// to be called.
1230+
if vr.Type() == TypeNull && val.Kind() == reflect.Ptr {
12251231
val.Set(reflect.Zero(val.Type()))
12261232

12271233
return vr.ReadNull()

bson/unmarshal.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ type ValueUnmarshaler interface {
3636
}
3737

3838
// Unmarshal parses the BSON-encoded data and stores the result in the value
39-
// pointed to by val. If val is nil or not a pointer, Unmarshal returns an error.
39+
// pointed to by val. If val is nil or not a pointer, Unmarshal returns an
40+
// error.
41+
//
42+
// When unmarshaling BSON, if the BSON value is null and the Go value is a
43+
// pointer, the pointer is set to nil without calling UnmarshalBSONValue.
4044
func Unmarshal(data []byte, val interface{}) error {
4145
vr := newDocumentReader(bytes.NewReader(data))
4246
if l, err := vr.peekLength(); err != nil {

bson/unmarshal_value_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"testing"
1414

1515
"go.mongodb.org/mongo-driver/v2/internal/assert"
16+
"go.mongodb.org/mongo-driver/v2/internal/require"
1617
"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore"
1718
)
1819

@@ -33,6 +34,26 @@ func TestUnmarshalValue(t *testing.T) {
3334
}
3435
}
3536

37+
func TestInitializedPointerDataWithBSONNull(t *testing.T) {
38+
// Set up the test case with initialized pointers.
39+
tc := unmarshalBehaviorTestCase{
40+
BSONValuePtrTracker: &unmarshalBSONValueCallTracker{},
41+
BSONPtrTracker: &unmarshalBSONCallTracker{},
42+
}
43+
// Create BSON data where the '*_ptr_tracker' fields are explicitly set to
44+
// null.
45+
bytes := docToBytes(D{
46+
{Key: "bv_ptr_tracker", Value: nil},
47+
{Key: "b_ptr_tracker", Value: nil},
48+
})
49+
// Unmarshal the BSON data into the test case struct. This should set the
50+
// pointer fields to nil due to the BSON null value.
51+
err := Unmarshal(bytes, &tc)
52+
require.NoError(t, err)
53+
assert.Nil(t, tc.BSONValuePtrTracker)
54+
assert.Nil(t, tc.BSONPtrTracker)
55+
}
56+
3657
// tests covering GODRIVER-2779
3758
func BenchmarkSliceCodecUnmarshal(b *testing.B) {
3859
benchmarks := []struct {

bson/unmarshaling_cases_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,70 @@ func unmarshalingTestCases() []unmarshalingTestCase {
172172
want: &valNonPtrStruct,
173173
data: docToBytes(valNonPtrStruct),
174174
},
175+
{
176+
name: "nil pointer and non-pointer type with literal null BSON",
177+
sType: reflect.TypeOf(unmarshalBehaviorTestCase{}),
178+
want: &unmarshalBehaviorTestCase{
179+
BSONValueTracker: unmarshalBSONValueCallTracker{
180+
called: true,
181+
},
182+
BSONValuePtrTracker: nil,
183+
BSONTracker: unmarshalBSONCallTracker{
184+
called: true,
185+
},
186+
BSONPtrTracker: nil,
187+
},
188+
data: docToBytes(D{
189+
{Key: "bv_tracker", Value: nil},
190+
{Key: "bv_ptr_tracker", Value: nil},
191+
{Key: "b_tracker", Value: nil},
192+
{Key: "b_ptr_tracker", Value: nil},
193+
}),
194+
},
195+
{
196+
name: "nil pointer and non-pointer type with BSON minkey",
197+
sType: reflect.TypeOf(unmarshalBehaviorTestCase{}),
198+
want: &unmarshalBehaviorTestCase{
199+
BSONValueTracker: unmarshalBSONValueCallTracker{
200+
called: true,
201+
},
202+
BSONValuePtrTracker: &unmarshalBSONValueCallTracker{
203+
called: true,
204+
},
205+
BSONTracker: unmarshalBSONCallTracker{
206+
called: true,
207+
},
208+
BSONPtrTracker: nil,
209+
},
210+
data: docToBytes(D{
211+
{Key: "bv_tracker", Value: MinKey{}},
212+
{Key: "bv_ptr_tracker", Value: MinKey{}},
213+
{Key: "b_tracker", Value: MinKey{}},
214+
{Key: "b_ptr_tracker", Value: MinKey{}},
215+
}),
216+
},
217+
{
218+
name: "nil pointer and non-pointer type with BSON maxkey",
219+
sType: reflect.TypeOf(unmarshalBehaviorTestCase{}),
220+
want: &unmarshalBehaviorTestCase{
221+
BSONValueTracker: unmarshalBSONValueCallTracker{
222+
called: true,
223+
},
224+
BSONValuePtrTracker: &unmarshalBSONValueCallTracker{
225+
called: true,
226+
},
227+
BSONTracker: unmarshalBSONCallTracker{
228+
called: true,
229+
},
230+
BSONPtrTracker: nil,
231+
},
232+
data: docToBytes(D{
233+
{Key: "bv_tracker", Value: MaxKey{}},
234+
{Key: "bv_ptr_tracker", Value: MaxKey{}},
235+
{Key: "b_tracker", Value: MaxKey{}},
236+
{Key: "b_ptr_tracker", Value: MaxKey{}},
237+
}),
238+
},
175239
}
176240
}
177241

@@ -267,3 +331,39 @@ func (ms *myString) UnmarshalBSON(b []byte) error {
267331
*ms = myString(s)
268332
return nil
269333
}
334+
335+
// unmarshalBSONValueCallTracker is a test struct that tracks whether the
336+
// UnmarshalBSONValue method has been called.
337+
type unmarshalBSONValueCallTracker struct {
338+
called bool // called is set to true when UnmarshalBSONValue is invoked.
339+
}
340+
341+
var _ ValueUnmarshaler = &unmarshalBSONValueCallTracker{}
342+
343+
// unmarshalBSONCallTracker is a test struct that tracks whether the
344+
// UnmarshalBSON method has been called.
345+
type unmarshalBSONCallTracker struct {
346+
called bool // called is set to true when UnmarshalBSON is invoked.
347+
}
348+
349+
// Ensure unmarshalBSONCallTracker implements the Unmarshaler interface.
350+
var _ Unmarshaler = &unmarshalBSONCallTracker{}
351+
352+
// unmarshalBehaviorTestCase holds instances of call trackers for testing BSON
353+
// unmarshaling behavior.
354+
type unmarshalBehaviorTestCase struct {
355+
BSONValueTracker unmarshalBSONValueCallTracker `bson:"bv_tracker"` // BSON value unmarshaling by value.
356+
BSONValuePtrTracker *unmarshalBSONValueCallTracker `bson:"bv_ptr_tracker"` // BSON value unmarshaling by pointer.
357+
BSONTracker unmarshalBSONCallTracker `bson:"b_tracker"` // BSON unmarshaling by value.
358+
BSONPtrTracker *unmarshalBSONCallTracker `bson:"b_ptr_tracker"` // BSON unmarshaling by pointer.
359+
}
360+
361+
func (tracker *unmarshalBSONValueCallTracker) UnmarshalBSONValue(byte, []byte) error {
362+
tracker.called = true
363+
return nil
364+
}
365+
366+
func (tracker *unmarshalBSONCallTracker) UnmarshalBSON([]byte) error {
367+
tracker.called = true
368+
return nil
369+
}

etc/run-fuzz.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ do
3535
done
3636
fi
3737

38-
go test ${PARENTDIR} -run=${FUNC} -fuzz=${FUNC} -fuzztime=${FUZZTIME} || true
38+
GOMAXPROCS=2 go test ${PARENTDIR} -run=${FUNC} -fuzz=${FUNC} -fuzztime=${FUZZTIME} || true
3939

4040
# Check if any new corpus files were generated for the fuzzer. If there are new corpus files, move them
4141
# to $PROJECT_DIRECTORY/fuzz/$FUNC/* so they can be tarred up and uploaded to S3.

examples/_logger/logrus/go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ require (
1818
github.com/xdg-go/scram v1.1.2 // indirect
1919
github.com/xdg-go/stringprep v1.0.4 // indirect
2020
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
21-
golang.org/x/crypto v0.32.0 // indirect
22-
golang.org/x/sync v0.10.0 // indirect
23-
golang.org/x/sys v0.29.0 // indirect
24-
golang.org/x/text v0.21.0 // indirect
21+
golang.org/x/crypto v0.33.0 // indirect
22+
golang.org/x/sync v0.11.0 // indirect
23+
golang.org/x/sys v0.30.0 // indirect
24+
golang.org/x/text v0.22.0 // indirect
2525
)

0 commit comments

Comments
 (0)