From 431cf52f9d66337eccf1f4088ef7e896401915b8 Mon Sep 17 00:00:00 2001 From: Preston Vasquez Date: Thu, 5 Jun 2025 12:08:55 -0600 Subject: [PATCH 1/4] GODRIVER-3524 Sync updates to reflect showExpandedEvents omissions (#2084) --- .../change-streams-disambiguatedPaths.json | 66 ------------------- .../change-streams-disambiguatedPaths.yml | 29 +------- testdata/change-streams/change-streams.json | 12 +++- testdata/change-streams/change-streams.yml | 5 +- 4 files changed, 15 insertions(+), 97 deletions(-) diff --git a/testdata/change-streams/change-streams-disambiguatedPaths.json b/testdata/change-streams/change-streams-disambiguatedPaths.json index dba4a4c34a..a8667b5436 100644 --- a/testdata/change-streams/change-streams-disambiguatedPaths.json +++ b/testdata/change-streams/change-streams-disambiguatedPaths.json @@ -28,7 +28,6 @@ "minServerVersion": "6.1.0", "topologies": [ "replicaset", - "sharded-replicaset", "load-balanced", "sharded" ], @@ -43,70 +42,6 @@ } ], "tests": [ - { - "description": "disambiguatedPaths is not present when showExpandedEvents is false/unset", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": { - "1": 1 - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "a.1": 2 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "updateDescription": { - "updatedFields": { - "$$exists": true - }, - "removedFields": { - "$$exists": true - }, - "truncatedArrays": { - "$$exists": true - }, - "disambiguatedPaths": { - "$$exists": false - } - } - } - } - ] - }, { "description": "disambiguatedPaths is present on updateDescription when an ambiguous path is present", "operations": [ @@ -250,4 +185,3 @@ } ] } - diff --git a/testdata/change-streams/change-streams-disambiguatedPaths.yml b/testdata/change-streams/change-streams-disambiguatedPaths.yml index 2469988cd6..7996c45f24 100644 --- a/testdata/change-streams/change-streams-disambiguatedPaths.yml +++ b/testdata/change-streams/change-streams-disambiguatedPaths.yml @@ -15,7 +15,7 @@ createEntities: runOnRequirements: - minServerVersion: "6.1.0" - topologies: [ replicaset, sharded-replicaset, load-balanced, sharded ] + topologies: [ replicaset, load-balanced, sharded ] serverless: forbid initialData: @@ -24,32 +24,6 @@ initialData: documents: [] tests: - - description: "disambiguatedPaths is not present when showExpandedEvents is false/unset" - operations: - - name: insertOne - object: *collection0 - arguments: - document: { _id: 1, 'a': { '1': 1 } } - - name: createChangeStream - object: *collection0 - arguments: { pipeline: [] } - saveResultAsEntity: &changeStream0 changeStream0 - - name: updateOne - object: *collection0 - arguments: - filter: { _id: 1 } - update: { $set: { 'a.1': 2 } } - - name: iterateUntilDocumentOrError - object: *changeStream0 - expectResult: - operationType: "update" - ns: { db: *database0, coll: *collection0 } - updateDescription: - updatedFields: { $$exists: true } - removedFields: { $$exists: true } - truncatedArrays: { $$exists: true } - disambiguatedPaths: { $$exists: false } - - description: "disambiguatedPaths is present on updateDescription when an ambiguous path is present" operations: - name: insertOne @@ -101,4 +75,3 @@ tests: removedFields: { $$exists: true } truncatedArrays: { $$exists: true } disambiguatedPaths: { 'a.0.1': ['a', { $$type: 'int' }, '1'] } - diff --git a/testdata/change-streams/change-streams.json b/testdata/change-streams/change-streams.json index d03fde97e5..b20868ee03 100644 --- a/testdata/change-streams/change-streams.json +++ b/testdata/change-streams/change-streams.json @@ -181,7 +181,12 @@ "field": "array", "newSize": 2 } - ] + ], + "disambiguatedPaths": { + "$$unsetOrMatches": { + "$$exists": true + } + } } } } @@ -1409,6 +1414,11 @@ "$$unsetOrMatches": { "$$exists": true } + }, + "disambiguatedPaths": { + "$$unsetOrMatches": { + "$$exists": true + } } } } diff --git a/testdata/change-streams/change-streams.yml b/testdata/change-streams/change-streams.yml index 113c80f50d..7f824623a6 100644 --- a/testdata/change-streams/change-streams.yml +++ b/testdata/change-streams/change-streams.yml @@ -115,7 +115,8 @@ tests: "field": "array", "newSize": 2 } - ] + ], + disambiguatedPaths: { $$unsetOrMatches: { $$exists: true } } } } @@ -140,7 +141,6 @@ tests: comment: *comment0 - description: "Test with document comment - pre 4.4" - skipReason: "TODO(GODRIVER-2386): aggregate only supports string comments" runOnRequirements: - maxServerVersion: "4.2.99" operations: @@ -723,6 +723,7 @@ tests: updatedFields: { x: 2 } removedFields: [] truncatedArrays: { $$unsetOrMatches: { $$exists: true } } + disambiguatedPaths: { $$unsetOrMatches: { $$exists: true } } - name: iterateUntilDocumentOrError object: *changeStream0 expectResult: From 4c4cafc2f35435794f614f78cbe2ea486832ff4b Mon Sep 17 00:00:00 2001 From: "mongodb-dbx-release-bot[bot]" <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 19:01:55 +0000 Subject: [PATCH 2/4] BUMP v1.17.4 Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com> --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index 8992fada15..cf9127b008 100644 --- a/version/version.go +++ b/version/version.go @@ -11,4 +11,4 @@ package version // Driver is the current version of the driver. -var Driver = "1.17.3" +var Driver = "1.17.4" From d6b8434a8877c0cb613100e3cf2be37f3cbcce7f Mon Sep 17 00:00:00 2001 From: Preston Vasquez Date: Wed, 4 Jun 2025 17:59:00 -0600 Subject: [PATCH 3/4] GODRIVER-3565 Add UnmarshalBSON to GridFSFile (#2077) --- mongo/gridfs_download_stream.go | 19 +++++++++ mongo/gridfs_test.go | 69 +++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/mongo/gridfs_download_stream.go b/mongo/gridfs_download_stream.go index 29df84cc0a..33174854cd 100644 --- a/mongo/gridfs_download_stream.go +++ b/mongo/gridfs_download_stream.go @@ -73,6 +73,8 @@ type GridFSFile struct { Metadata bson.Raw } +var _ bson.Unmarshaler = &GridFSFile{} + // findFileResponse is a temporary type used to unmarshal documents from the // files collection and can be transformed into a File instance. This type // exists to avoid adding BSON struct tags to the exported File type. @@ -96,6 +98,23 @@ func newFileFromResponse(resp findFileResponse) *GridFSFile { } } +// UnmarshalBSON implements the bson.Unmarshaler interface. +func (f *GridFSFile) UnmarshalBSON(data []byte) error { + var temp findFileResponse + if err := bson.Unmarshal(data, &temp); err != nil { + return err + } + + f.ID = temp.ID + f.Length = temp.Length + f.ChunkSize = temp.ChunkSize + f.UploadDate = temp.UploadDate + f.Name = temp.Name + f.Metadata = temp.Metadata + + return nil +} + func newGridFSDownloadStream( ctx context.Context, cancel context.CancelFunc, diff --git a/mongo/gridfs_test.go b/mongo/gridfs_test.go index 17e080fa72..84fa9bd871 100644 --- a/mongo/gridfs_test.go +++ b/mongo/gridfs_test.go @@ -10,6 +10,7 @@ import ( "context" "testing" + "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/integtest" @@ -109,3 +110,71 @@ func TestGridFS(t *testing.T) { } }) } + +func TestGridFSFile_UnmarshalBSON(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test in short mode") + } + + cs := integtest.ConnString(t) + + clientOpts := options.Client(). + ApplyURI(cs.Original). + SetReadPreference(readpref.Primary()). + SetWriteConcern(writeconcern.Majority()). + // Connect to a single host. For sharded clusters, this will pin to a single mongos, which avoids + // non-deterministic versioning errors in the server. This has no effect for replica sets because the driver + // will discover the other hosts during SDAM checks. + SetHosts(cs.Hosts[:1]) + + integtest.AddTestServerAPIVersion(clientOpts) + + client, err := Connect(clientOpts) + require.NoError(t, err) + + defer func() { + err := client.Disconnect(context.Background()) + require.NoError(t, err) + }() + + // Get the database and create a GridFS bucket + db := client.Database("gridfs_test_db") + + // Drop the collection + err = db.Collection("myfiles.files").Drop(context.Background()) + require.NoError(t, err) + + err = db.Collection("myfiles.chunks").Drop(context.Background()) + require.NoError(t, err) + + bucket := db.GridFSBucket(options.GridFSBucket().SetName("myfiles")) + + // Data to upload + fileName := "example-file.txt" + fileContent := []byte("Hello GridFS! This is a test file.") + + // Upload data into GridFS + uploadStream, err := bucket.OpenUploadStream(context.Background(), fileName) + require.NoError(t, err) + + _, err = uploadStream.Write(fileContent) + require.NoError(t, err) + + uploadStream.Close() + + // Verify the file metadata + fileCursor, err := bucket.Find(context.Background(), bson.D{}) + require.NoError(t, err) + + for fileCursor.Next(context.Background()) { + var file GridFSFile + err := fileCursor.Decode(&file) + require.NoError(t, err) + + assert.NotNil(t, file.ID) + assert.Equal(t, int64(34), file.Length) + assert.Equal(t, int32(261120), file.ChunkSize) + assert.NotNil(t, file.UploadDate) + assert.Equal(t, fileName, file.Name) + } +} From 0bb1953e1773154770ba956535ddf1dd8ee169cc Mon Sep 17 00:00:00 2001 From: Qingyang Hu <103950869+qingyang-hu@users.noreply.github.com> Date: Wed, 4 Jun 2025 17:45:43 -0400 Subject: [PATCH 4/4] GODRIVER-3574 Align BSON interface slice decoding with json package. (#2075) --- bson/bsoncodec.go | 3 + bson/decoder_test.go | 23 +++++ bson/default_value_decoders.go | 25 ++++- bson/default_value_decoders_test.go | 144 ++++++++++++++++++++++------ bson/primitive_codecs_test.go | 10 +- bson/unmarshal_test.go | 22 +++++ 6 files changed, 194 insertions(+), 33 deletions(-) diff --git a/bson/bsoncodec.go b/bson/bsoncodec.go index 80e13e7d81..91592a3e63 100644 --- a/bson/bsoncodec.go +++ b/bson/bsoncodec.go @@ -69,6 +69,9 @@ func (vde ValueDecoderError) Error() string { if vde.Received.IsValid() { received = vde.Received.Type().String() } + if !vde.Received.CanSet() { + received = "unsettable " + received + } return fmt.Sprintf("%s can only decode valid and settable %s, but got %s", vde.Name, strings.Join(typeKinds, ", "), received) } diff --git a/bson/decoder_test.go b/bson/decoder_test.go index e88aca6d6b..4179b5e4ba 100644 --- a/bson/decoder_test.go +++ b/bson/decoder_test.go @@ -172,6 +172,29 @@ func TestDecodingInterfaces(t *testing.T) { assert.Equal(t, testStruct{[3]interface{}{&str, &i, nil}}, receiver) } + return data, &receiver, check + }, + }, + { + name: "overwriting prepopulated slice", + stub: func() ([]byte, interface{}, func(*testing.T)) { + type testStruct struct { + Values []interface{} + } + + data := docToBytes(struct { + Values []interface{} + }{ + Values: []interface{}{1, 2, 3}, + }) + + receiver := testStruct{[]interface{}{7, 8}} + + check := func(t *testing.T) { + t.Helper() + assert.Equal(t, testStruct{[]interface{}{1, 2, int32(3)}}, receiver) + } + return data, &receiver, check }, }, diff --git a/bson/default_value_decoders.go b/bson/default_value_decoders.go index 42ea2e5864..b7d2033999 100644 --- a/bson/default_value_decoders.go +++ b/bson/default_value_decoders.go @@ -1332,8 +1332,12 @@ func decodeDefault(dc DecodeContext, vr ValueReader, val reflect.Value) ([]refle eType := val.Type().Elem() + isInterfaceSlice := eType.Kind() == reflect.Interface && val.Len() > 0 + + // If this is not an interface slice with pre-populated elements, we can look up + // the decoder for eType once. var vDecoder ValueDecoder - if !(eType.Kind() == reflect.Interface && val.Len() > 0) { + if !isInterfaceSlice { vDecoder, err = dc.LookupDecoder(eType) if err != nil { return nil, err @@ -1351,7 +1355,9 @@ func decodeDefault(dc DecodeContext, vr ValueReader, val reflect.Value) ([]refle } var elem reflect.Value - if vDecoder == nil { + if isInterfaceSlice && idx < val.Len() { + // Decode into an existing interface{} slot. + elem = val.Index(idx).Elem() switch { case elem.Kind() != reflect.Ptr || elem.IsNil(): @@ -1359,6 +1365,12 @@ func decodeDefault(dc DecodeContext, vr ValueReader, val reflect.Value) ([]refle if err != nil { return nil, err } + + // If an element is allocated and unsettable, it must be overwritten. + if !elem.CanSet() { + elem = reflect.New(elem.Type()).Elem() + } + err = valueDecoder.DecodeValue(dc, vr, elem) if err != nil { return nil, newDecodeError(strconv.Itoa(idx), err) @@ -1380,6 +1392,15 @@ func decodeDefault(dc DecodeContext, vr ValueReader, val reflect.Value) ([]refle } } } else { + // For non-interface slices, or if we've exhausted the pre-populated + // slots, we create a fresh value. + + if vDecoder == nil { + vDecoder, err = dc.LookupDecoder(eType) + if err != nil { + return nil, err + } + } elem, err = decodeTypeOrValueWithInfo(vDecoder, dc, vr, eType) if err != nil { return nil, newDecodeError(strconv.Itoa(idx), err) diff --git a/bson/default_value_decoders_test.go b/bson/default_value_decoders_test.go index 4dad538a26..b0e68b8f76 100644 --- a/bson/default_value_decoders_test.go +++ b/bson/default_value_decoders_test.go @@ -75,7 +75,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeBoolean}, nothing, - ValueDecoderError{Name: "BooleanDecodeValue", Kinds: []reflect.Kind{reflect.Bool}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "BooleanDecodeValue", + Kinds: []reflect.Kind{reflect.Bool}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not boolean", @@ -741,7 +745,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeDateTime, Return: int64(0)}, nothing, - ValueDecoderError{Name: "TimeDecodeValue", Types: []reflect.Type{tTime}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "TimeDecodeValue", + Types: []reflect.Type{tTime}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "ReadDateTime error", @@ -795,7 +803,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{}, nothing, - ValueDecoderError{Name: "MapDecodeValue", Kinds: []reflect.Kind{reflect.Map}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "MapDecodeValue", + Kinds: []reflect.Kind{reflect.Map}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "wrong kind (non-string key)", @@ -873,7 +885,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{}, nothing, - ValueDecoderError{Name: "ArrayDecodeValue", Kinds: []reflect.Kind{reflect.Array}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "ArrayDecodeValue", + Kinds: []reflect.Kind{reflect.Array}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "can set false", @@ -967,7 +983,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{}, nothing, - ValueDecoderError{Name: "SliceDecodeValue", Kinds: []reflect.Kind{reflect.Slice}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "SliceDecodeValue", + Kinds: []reflect.Kind{reflect.Slice}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "can set false", @@ -1061,7 +1081,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeObjectID}, nothing, - ValueDecoderError{Name: "ObjectIDDecodeValue", Types: []reflect.Type{tOID}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "ObjectIDDecodeValue", + Types: []reflect.Type{tOID}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not objectID", @@ -1148,7 +1172,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeDecimal128}, nothing, - ValueDecoderError{Name: "Decimal128DecodeValue", Types: []reflect.Type{tDecimal}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "Decimal128DecodeValue", + Types: []reflect.Type{tDecimal}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not decimal128", @@ -1210,7 +1238,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeObjectID}, nothing, - ValueDecoderError{Name: "JSONNumberDecodeValue", Types: []reflect.Type{tJSONNumber}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "JSONNumberDecodeValue", + Types: []reflect.Type{tJSONNumber}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not double/int32/int64", @@ -1312,7 +1344,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeString, Return: "http://example.com"}, nothing, - ValueDecoderError{Name: "URLDecodeValue", Types: []reflect.Type{tURL}, Received: reflect.ValueOf(int64(0))}, + ValueDecoderError{ + Name: "URLDecodeValue", + Types: []reflect.Type{tURL}, + Received: reflect.New(reflect.TypeOf(int64(0))).Elem(), + }, }, { "ReadString error", @@ -1386,7 +1422,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeBinary, Return: bsoncore.Value{Type: bsoncore.TypeBinary}}, nothing, - ValueDecoderError{Name: "ByteSliceDecodeValue", Types: []reflect.Type{tByteSlice}, Received: reflect.ValueOf(int64(0))}, + ValueDecoderError{ + Name: "ByteSliceDecodeValue", + Types: []reflect.Type{tByteSlice}, + Received: reflect.New(reflect.TypeOf(int64(0))).Elem(), + }, }, { "ReadBinary error", @@ -1479,7 +1519,7 @@ func TestDefaultValueDecoders(t *testing.T) { ValueDecoderError{ Name: "ValueUnmarshalerDecodeValue", Types: []reflect.Type{tValueUnmarshaler}, - Received: reflect.ValueOf(wrong), + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), }, }, { @@ -1510,7 +1550,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, nil, nothing, - ValueDecoderError{Name: "UnmarshalerDecodeValue", Types: []reflect.Type{tUnmarshaler}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "UnmarshalerDecodeValue", + Types: []reflect.Type{tUnmarshaler}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "copy error", @@ -1589,7 +1633,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{}, nothing, - ValueDecoderError{Name: "BinaryDecodeValue", Types: []reflect.Type{tBinary}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "BinaryDecodeValue", + Types: []reflect.Type{tBinary}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not binary", @@ -1649,7 +1697,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeUndefined}, nothing, - ValueDecoderError{Name: "UndefinedDecodeValue", Types: []reflect.Type{tUndefined}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "UndefinedDecodeValue", + Types: []reflect.Type{tUndefined}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not undefined", @@ -1695,7 +1747,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeDateTime}, nothing, - ValueDecoderError{Name: "DateTimeDecodeValue", Types: []reflect.Type{tDateTime}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "DateTimeDecodeValue", + Types: []reflect.Type{tDateTime}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not datetime", @@ -1749,7 +1805,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeNull}, nothing, - ValueDecoderError{Name: "NullDecodeValue", Types: []reflect.Type{tNull}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "NullDecodeValue", + Types: []reflect.Type{tNull}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not null", @@ -1787,7 +1847,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeRegex}, nothing, - ValueDecoderError{Name: "RegexDecodeValue", Types: []reflect.Type{tRegex}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "RegexDecodeValue", + Types: []reflect.Type{tRegex}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not regex", @@ -1847,7 +1911,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeDBPointer}, nothing, - ValueDecoderError{Name: "DBPointerDecodeValue", Types: []reflect.Type{tDBPointer}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "DBPointerDecodeValue", + Types: []reflect.Type{tDBPointer}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not dbpointer", @@ -1912,7 +1980,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeTimestamp}, nothing, - ValueDecoderError{Name: "TimestampDecodeValue", Types: []reflect.Type{tTimestamp}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "TimestampDecodeValue", + Types: []reflect.Type{tTimestamp}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not timestamp", @@ -1972,7 +2044,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeMinKey}, nothing, - ValueDecoderError{Name: "MinKeyDecodeValue", Types: []reflect.Type{tMinKey}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "MinKeyDecodeValue", + Types: []reflect.Type{tMinKey}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not null", @@ -2026,7 +2102,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeMaxKey}, nothing, - ValueDecoderError{Name: "MaxKeyDecodeValue", Types: []reflect.Type{tMaxKey}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "MaxKeyDecodeValue", + Types: []reflect.Type{tMaxKey}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not null", @@ -2080,7 +2160,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeJavaScript, Return: ""}, nothing, - ValueDecoderError{Name: "JavaScriptDecodeValue", Types: []reflect.Type{tJavaScript}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "JavaScriptDecodeValue", + Types: []reflect.Type{tJavaScript}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not Javascript", @@ -2134,7 +2218,11 @@ func TestDefaultValueDecoders(t *testing.T) { nil, &valueReaderWriter{BSONType: TypeSymbol, Return: ""}, nothing, - ValueDecoderError{Name: "SymbolDecodeValue", Types: []reflect.Type{tSymbol}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "SymbolDecodeValue", + Types: []reflect.Type{tSymbol}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "type not Symbol", @@ -2191,7 +2279,7 @@ func TestDefaultValueDecoders(t *testing.T) { ValueDecoderError{ Name: "CoreDocumentDecodeValue", Types: []reflect.Type{tCoreDocument}, - Received: reflect.ValueOf(wrong), + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), }, }, { @@ -2203,7 +2291,7 @@ func TestDefaultValueDecoders(t *testing.T) { ValueDecoderError{ Name: "CoreDocumentDecodeValue", Types: []reflect.Type{tCoreDocument}, - Received: reflect.ValueOf((*bsoncore.Document)(nil)), + Received: reflect.New(reflect.TypeOf((*bsoncore.Document)(nil))).Elem(), }, }, { @@ -2259,7 +2347,7 @@ func TestDefaultValueDecoders(t *testing.T) { ValueDecoderError{ Name: "CodeWithScopeDecodeValue", Types: []reflect.Type{tCodeWithScope}, - Received: reflect.ValueOf(wrong), + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), }, }, { @@ -2320,7 +2408,7 @@ func TestDefaultValueDecoders(t *testing.T) { ValueDecoderError{ Name: "CoreArrayDecodeValue", Types: []reflect.Type{tCoreArray}, - Received: reflect.ValueOf(wrong), + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), }, }, { @@ -2332,7 +2420,7 @@ func TestDefaultValueDecoders(t *testing.T) { ValueDecoderError{ Name: "CoreArrayDecodeValue", Types: []reflect.Type{tCoreArray}, - Received: reflect.ValueOf((*bsoncore.Array)(nil)), + Received: reflect.New(reflect.TypeOf((*bsoncore.Array)(nil))).Elem(), }, }, }, diff --git a/bson/primitive_codecs_test.go b/bson/primitive_codecs_test.go index d7480f9eaa..6071ea02f9 100644 --- a/bson/primitive_codecs_test.go +++ b/bson/primitive_codecs_test.go @@ -507,7 +507,7 @@ func TestPrimitiveValueDecoders(t *testing.T) { ValueDecoderError{ Name: "RawValueDecodeValue", Types: []reflect.Type{tRawValue}, - Received: reflect.ValueOf(wrong), + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), }, }, { @@ -548,7 +548,11 @@ func TestPrimitiveValueDecoders(t *testing.T) { nil, &valueReaderWriter{}, nothing, - ValueDecoderError{Name: "RawDecodeValue", Types: []reflect.Type{tRaw}, Received: reflect.ValueOf(wrong)}, + ValueDecoderError{ + Name: "RawDecodeValue", + Types: []reflect.Type{tRaw}, + Received: reflect.New(reflect.TypeOf(wrong)).Elem(), + }, }, { "*Raw is nil", @@ -559,7 +563,7 @@ func TestPrimitiveValueDecoders(t *testing.T) { ValueDecoderError{ Name: "RawDecodeValue", Types: []reflect.Type{tRaw}, - Received: reflect.ValueOf((*Raw)(nil)), + Received: reflect.New(reflect.TypeOf((*Raw)(nil))).Elem(), }, }, { diff --git a/bson/unmarshal_test.go b/bson/unmarshal_test.go index 35a66141be..06dfe4ff22 100644 --- a/bson/unmarshal_test.go +++ b/bson/unmarshal_test.go @@ -422,6 +422,28 @@ func TestUnmarshalInterface(t *testing.T) { assert.Equal(t, testStruct{[3]interface{}{&str, &i, nil}}, receiver) } + return data, &receiver, check + }, + }, + { + name: "overwriting prepopulated slice", + stub: func() ([]byte, interface{}, func(*testing.T)) { + type testStruct struct { + Values []interface{} + } + data := docToBytes(struct { + Values []interface{} + }{ + Values: []interface{}{1, 2, 3}, + }) + + receiver := testStruct{[]interface{}{7, 8}} + + check := func(t *testing.T) { + t.Helper() + assert.Equal(t, testStruct{[]interface{}{1, 2, int32(3)}}, receiver) + } + return data, &receiver, check }, },