Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GODRIVER-2367 Un-export all result type BSON encoding logic #1442

Merged
merged 4 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 30 additions & 8 deletions mongo/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/internal/csfle"
"go.mongodb.org/mongo-driver/mongo/description"
"go.mongodb.org/mongo-driver/mongo/options"
Expand Down Expand Up @@ -352,9 +353,6 @@ func (db *Database) Drop(ctx context.Context) error {
// documentation).
//
// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listCollections/.
//
// BUG(benjirewis): ListCollectionSpecifications prevents listing more than 100 collections per database when running
// against MongoDB version 2.6.
func (db *Database) ListCollectionSpecifications(ctx context.Context, filter interface{},
opts ...*options.ListCollectionsOptions) ([]*CollectionSpecification, error) {

Expand All @@ -363,19 +361,43 @@ func (db *Database) ListCollectionSpecifications(ctx context.Context, filter int
return nil, err
}

var specs []*CollectionSpecification
err = cursor.All(ctx, &specs)
var resp []struct {
Name string `bson:"name"`
Type string `bson:"type"`
Info *struct {
ReadOnly bool `bson:"readOnly"`
UUID *primitive.Binary `bson:"uuid"`
} `bson:"info"`
Options bson.Raw `bson:"options"`
IDIndex indexListSpecificationResponse `bson:"idIndex"`
}

err = cursor.All(ctx, &resp)
if err != nil {
return nil, err
}

for _, spec := range specs {
specs := make([]*CollectionSpecification, len(resp))
for idx, spec := range resp {
specs[idx] = &CollectionSpecification{
Name: spec.Name,
Type: spec.Type,
Options: spec.Options,
IDIndex: newIndexSpecificationFromResponse(spec.IDIndex),
}

if spec.Info != nil {
specs[idx].ReadOnly = spec.Info.ReadOnly
specs[idx].UUID = spec.Info.UUID
}

// Pre-4.4 servers report a namespace in their responses, so we only set Namespace manually if it was not in
// the response.
if spec.IDIndex != nil && spec.IDIndex.Namespace == "" {
spec.IDIndex.Namespace = db.name + "." + spec.Name
if specs[idx].IDIndex != nil && specs[idx].IDIndex.Namespace == "" {
specs[idx].IDIndex.Namespace = db.name + "." + specs[idx].Name
}
}

return specs, nil
}

Expand Down
10 changes: 6 additions & 4 deletions mongo/gridfs/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,13 +483,15 @@ func (b *Bucket) openDownloadStream(filter interface{}, opts ...*options.FindOpt
// Unmarshal the data into a File instance, which can be passed to newDownloadStream. The _id value has to be
// parsed out separately because "_id" will not match the File.ID field and we want to avoid exposing BSON tags
// in the File type. After parsing it, use RawValue.Unmarshal to ensure File.ID is set to the appropriate value.
var foundFile File
if err = cursor.Decode(&foundFile); err != nil {
var resp findFileResponse
if err = cursor.Decode(&resp); err != nil {
return nil, fmt.Errorf("error decoding files collection document: %v", err)
}

foundFile := newFileFromResponse(resp)

if foundFile.Length == 0 {
return newDownloadStream(nil, foundFile.ChunkSize, &foundFile), nil
return newDownloadStream(nil, foundFile.ChunkSize, foundFile), nil
}

// For a file with non-zero length, chunkSize must exist so we know what size to expect when downloading chunks.
Expand All @@ -503,7 +505,7 @@ func (b *Bucket) openDownloadStream(filter interface{}, opts ...*options.FindOpt
}
// The chunk size can be overridden for individual files, so the expected chunk size should be the "chunkSize"
// field from the files collection document, not the bucket's chunk size.
return newDownloadStream(chunksCursor, foundFile.ChunkSize, &foundFile), nil
return newDownloadStream(chunksCursor, foundFile.ChunkSize, foundFile), nil
}

func deadlineContext(deadline time.Time) (context.Context, context.CancelFunc) {
Expand Down
32 changes: 12 additions & 20 deletions mongo/gridfs/download_stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,10 @@ type File struct {
Metadata bson.Raw
}

var _ bson.Unmarshaler = (*File)(nil)

// unmarshalFile 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.
type unmarshalFile struct {
// 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.
type findFileResponse struct {
ID interface{} `bson:"_id"`
Length int64 `bson:"length"`
ChunkSize int32 `bson:"chunkSize"`
Expand All @@ -83,22 +82,15 @@ type unmarshalFile struct {
Metadata bson.Raw `bson:"metadata"`
}

// UnmarshalBSON implements the bson.Unmarshaler interface.
//
// Deprecated: Unmarshaling a File from BSON will not be supported in Go Driver 2.0.
func (f *File) UnmarshalBSON(data []byte) error {
var temp unmarshalFile
if err := bson.Unmarshal(data, &temp); err != nil {
return err
func newFileFromResponse(resp findFileResponse) *File {
return &File{
ID: resp.ID,
Length: resp.Length,
ChunkSize: resp.ChunkSize,
UploadDate: resp.UploadDate,
Name: resp.Name,
Metadata: resp.Metadata,
}

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 newDownloadStream(cursor *mongo.Cursor, chunkSize int32, file *File) *DownloadStream {
Expand Down
19 changes: 10 additions & 9 deletions mongo/index_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,20 +146,21 @@ func (iv IndexView) ListSpecifications(ctx context.Context, opts ...*options.Lis
return nil, err
}

var results []*IndexSpecification
err = cursor.All(ctx, &results)
if err != nil {
var resp []indexListSpecificationResponse

if err := cursor.All(ctx, &resp); err != nil {
return nil, err
}

ns := iv.coll.db.Name() + "." + iv.coll.Name()
for _, res := range results {
// Pre-4.4 servers report a namespace in their responses, so we only set Namespace manually if it was not in
// the response.
res.Namespace = ns
namespace := iv.coll.db.Name() + "." + iv.coll.Name()

specs := make([]*IndexSpecification, len(resp))
for idx, spec := range resp {
specs[idx] = newIndexSpecificationFromResponse(spec)
specs[idx].Namespace = namespace
}

return results, nil
return specs, nil
}

// CreateOne executes a createIndexes command to create an index on the collection and returns the name of the new
Expand Down
134 changes: 12 additions & 122 deletions mongo/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
package mongo

import (
"fmt"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/x/mongo/driver/operation"
Expand Down Expand Up @@ -51,7 +49,7 @@ type InsertManyResult struct {

// DeleteResult is the result type returned by DeleteOne and DeleteMany operations.
type DeleteResult struct {
DeletedCount int64 `bson:"n"` // The number of documents deleted.
DeletedCount int64 // The number of documents deleted.
}

// RewrapManyDataKeyResult is the result of the bulk write operation used to update the key vault collection with
Expand Down Expand Up @@ -98,64 +96,6 @@ type UpdateResult struct {
UpsertedID interface{} // The _id field of the upserted document, or nil if no upsert was done.
}

// UnmarshalBSON implements the bson.Unmarshaler interface.
//
// Deprecated: Unmarshalling an UpdateResult directly from BSON is not supported and may produce
// different results compared to running Update* operations directly.
func (result *UpdateResult) UnmarshalBSON(b []byte) error {
// TODO(GODRIVER-2367): Remove the ability to unmarshal BSON directly to an UpdateResult.
elems, err := bson.Raw(b).Elements()
if err != nil {
return err
}

for _, elem := range elems {
switch elem.Key() {
case "n":
switch elem.Value().Type {
case bson.TypeInt32:
result.MatchedCount = int64(elem.Value().Int32())
case bson.TypeInt64:
result.MatchedCount = elem.Value().Int64()
default:
return fmt.Errorf("Received invalid type for n, should be Int32 or Int64, received %s", elem.Value().Type)
}
case "nModified":
switch elem.Value().Type {
case bson.TypeInt32:
result.ModifiedCount = int64(elem.Value().Int32())
case bson.TypeInt64:
result.ModifiedCount = elem.Value().Int64()
default:
return fmt.Errorf("Received invalid type for nModified, should be Int32 or Int64, received %s", elem.Value().Type)
}
case "upserted":
switch elem.Value().Type {
case bson.TypeArray:
e, err := elem.Value().Array().IndexErr(0)
if err != nil {
break
}
if e.Value().Type != bson.TypeEmbeddedDocument {
break
}
var d struct {
ID interface{} `bson:"_id"`
}
err = bson.Unmarshal(e.Value().Document(), &d)
if err != nil {
return err
}
result.UpsertedID = d.ID
default:
return fmt.Errorf("Received invalid type for upserted, should be Array, received %s", elem.Value().Type)
}
}
}

return nil
}

// IndexSpecification represents an index in a database. This type is returned by the IndexView.ListSpecifications
// function and is also used in the CollectionSpecification type.
type IndexSpecification struct {
Expand Down Expand Up @@ -187,9 +127,7 @@ type IndexSpecification struct {
Clustered *bool
}

var _ bson.Unmarshaler = (*IndexSpecification)(nil)

type unmarshalIndexSpecification struct {
type indexListSpecificationResponse struct {
Name string `bson:"name"`
Namespace string `bson:"ns"`
KeysDocument bson.Raw `bson:"key"`
Expand All @@ -200,24 +138,17 @@ type unmarshalIndexSpecification struct {
Clustered *bool `bson:"clustered"`
}

// UnmarshalBSON implements the bson.Unmarshaler interface.
//
// Deprecated: Unmarshaling an IndexSpecification from BSON will not be supported in Go Driver 2.0.
func (i *IndexSpecification) UnmarshalBSON(data []byte) error {
var temp unmarshalIndexSpecification
if err := bson.Unmarshal(data, &temp); err != nil {
return err
func newIndexSpecificationFromResponse(resp indexListSpecificationResponse) *IndexSpecification {
return &IndexSpecification{
Name: resp.Name,
Namespace: resp.Namespace,
KeysDocument: resp.KeysDocument,
Version: resp.Version,
ExpireAfterSeconds: resp.ExpireAfterSeconds,
Sparse: resp.Sparse,
Unique: resp.Unique,
Clustered: resp.Clustered,
}

i.Name = temp.Name
i.Namespace = temp.Namespace
i.KeysDocument = temp.KeysDocument
i.Version = temp.Version
i.ExpireAfterSeconds = temp.ExpireAfterSeconds
i.Sparse = temp.Sparse
i.Unique = temp.Unique
i.Clustered = temp.Clustered
return nil
}

// CollectionSpecification represents a collection in a database. This type is returned by the
Expand All @@ -243,44 +174,3 @@ type CollectionSpecification struct {
// option is used and for MongoDB versions < 3.4.
IDIndex *IndexSpecification
}

var _ bson.Unmarshaler = (*CollectionSpecification)(nil)

// unmarshalCollectionSpecification is used to unmarshal BSON bytes from a listCollections command into a
// CollectionSpecification.
type unmarshalCollectionSpecification struct {
Name string `bson:"name"`
Type string `bson:"type"`
Info *struct {
ReadOnly bool `bson:"readOnly"`
UUID *primitive.Binary `bson:"uuid"`
} `bson:"info"`
Options bson.Raw `bson:"options"`
IDIndex *IndexSpecification `bson:"idIndex"`
}

// UnmarshalBSON implements the bson.Unmarshaler interface.
//
// Deprecated: Unmarshaling a CollectionSpecification from BSON will not be supported in Go Driver
// 2.0.
func (cs *CollectionSpecification) UnmarshalBSON(data []byte) error {
var temp unmarshalCollectionSpecification
if err := bson.Unmarshal(data, &temp); err != nil {
return err
}

cs.Name = temp.Name
cs.Type = temp.Type
if cs.Type == "" {
// The "type" field is only present on 3.4+ because views were introduced in 3.4, so we implicitly set the
// value to "collection" if it's empty.
cs.Type = "collection"
}
if temp.Info != nil {
cs.ReadOnly = temp.Info.ReadOnly
cs.UUID = temp.Info.UUID
}
cs.Options = temp.Options
cs.IDIndex = temp.IDIndex
return nil
}
Loading
Loading