Copied mongo repo (to patch it)
This commit is contained in:
1712
mongo/bson/mgocompat/bson_test.go
Normal file
1712
mongo/bson/mgocompat/bson_test.go
Normal file
File diff suppressed because it is too large
Load Diff
57
mongo/bson/mgocompat/doc.go
Normal file
57
mongo/bson/mgocompat/doc.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (C) MongoDB, Inc. 2022-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
// Package mgocompat provides Registry, a BSON registry compatible with globalsign/mgo's BSON,
|
||||
// with some remaining differences. It also provides RegistryRespectNilValues for compatibility
|
||||
// with mgo's BSON with RespectNilValues set to true. A registry can be configured on a
|
||||
// mongo.Client with the SetRegistry option. See the bsoncodec docs for more details on registries.
|
||||
//
|
||||
// Registry supports Getter and Setter equivalents by registering hooks. Note that if a value
|
||||
// matches the hook for bsoncodec.Marshaler, bsoncodec.ValueMarshaler, or bsoncodec.Proxy, that
|
||||
// hook will take priority over the Getter hook. The same is true for the hooks for
|
||||
// bsoncodec.Unmarshaler and bsoncodec.ValueUnmarshaler and the Setter hook.
|
||||
//
|
||||
// The functional differences between Registry and globalsign/mgo's BSON library are:
|
||||
//
|
||||
// 1) Registry errors instead of silently skipping mismatched types when decoding.
|
||||
//
|
||||
// 2) Registry does not have special handling for marshaling array ops ("$in", "$nin", "$all").
|
||||
//
|
||||
// The driver uses different types than mgo's bson. The differences are:
|
||||
//
|
||||
// 1. The driver's bson.RawValue is equivalent to mgo's bson.Raw, but uses Value instead of Data and uses Type,
|
||||
// which is a bsontype.Type object that wraps a byte, instead of bson.Raw's Kind, a byte.
|
||||
//
|
||||
// 2. The driver uses primitive.ObjectID, which is a [12]byte instead of mgo's
|
||||
// bson.ObjectId, a string. Due to this, the zero value marshals and unmarshals differently
|
||||
// for Extended JSON, with the driver marshaling as {"ID":"000000000000000000000000"} and
|
||||
// mgo as {"Id":""}. The driver can unmarshal {"ID":""} to a primitive.ObjectID.
|
||||
//
|
||||
// 3. The driver's primitive.Symbol is equivalent to mgo's bson.Symbol.
|
||||
//
|
||||
// 4. The driver uses primitive.Timestamp instead of mgo's bson.MongoTimestamp. While
|
||||
// MongoTimestamp is an int64, primitive.Timestamp stores the time and counter as two separate
|
||||
// uint32 values, T and I respectively.
|
||||
//
|
||||
// 5. The driver uses primitive.MinKey and primitive.MaxKey, which are struct{}, instead
|
||||
// of mgo's bson.MinKey and bson.MaxKey, which are int64.
|
||||
//
|
||||
// 6. The driver's primitive.Undefined is equivalent to mgo's bson.Undefined.
|
||||
//
|
||||
// 7. The driver's primitive.Binary is equivalent to mgo's bson.Binary, with variables named Subtype
|
||||
// and Data instead of Kind and Data.
|
||||
//
|
||||
// 8. The driver's primitive.Regex is equivalent to mgo's bson.RegEx.
|
||||
//
|
||||
// 9. The driver's primitive.JavaScript is equivalent to mgo's bson.JavaScript with no
|
||||
// scope and primitive.CodeWithScope is equivalent to mgo's bson.JavaScript with scope.
|
||||
//
|
||||
// 10. The driver's primitive.DBPointer is equivalent to mgo's bson.DBPointer, with variables
|
||||
// named DB and Pointer instead of Namespace and Id.
|
||||
//
|
||||
// 11. When implementing the Setter interface, mgocompat.ErrSetZero is equivalent to mgo's
|
||||
// bson.ErrSetZero.
|
||||
package mgocompat
|
||||
114
mongo/bson/mgocompat/registry.go
Normal file
114
mongo/bson/mgocompat/registry.go
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package mgocompat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/bsoncodec"
|
||||
"go.mongodb.org/mongo-driver/bson/bsonoptions"
|
||||
"go.mongodb.org/mongo-driver/bson/bsontype"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrSetZero may be returned from a SetBSON method to have the value set to its respective zero value.
|
||||
ErrSetZero = errors.New("set to zero")
|
||||
|
||||
tInt = reflect.TypeOf(int(0))
|
||||
tTime = reflect.TypeOf(time.Time{})
|
||||
tM = reflect.TypeOf(bson.M{})
|
||||
tInterfaceSlice = reflect.TypeOf([]interface{}{})
|
||||
tByteSlice = reflect.TypeOf([]byte{})
|
||||
tEmpty = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
tGetter = reflect.TypeOf((*Getter)(nil)).Elem()
|
||||
tSetter = reflect.TypeOf((*Setter)(nil)).Elem()
|
||||
)
|
||||
|
||||
// Registry is the mgo compatible bsoncodec.Registry. It contains the default and
|
||||
// primitive codecs with mgo compatible options.
|
||||
var Registry = NewRegistryBuilder().Build()
|
||||
|
||||
// RegistryRespectNilValues is the bsoncodec.Registry compatible with mgo withSetRespectNilValues set to true.
|
||||
var RegistryRespectNilValues = NewRespectNilValuesRegistryBuilder().Build()
|
||||
|
||||
// NewRegistryBuilder creates a new bsoncodec.RegistryBuilder configured with the default encoders and
|
||||
// decoders from the bsoncodec.DefaultValueEncoders and bsoncodec.DefaultValueDecoders types and the
|
||||
// PrimitiveCodecs type in this package.
|
||||
func NewRegistryBuilder() *bsoncodec.RegistryBuilder {
|
||||
rb := bsoncodec.NewRegistryBuilder()
|
||||
bsoncodec.DefaultValueEncoders{}.RegisterDefaultEncoders(rb)
|
||||
bsoncodec.DefaultValueDecoders{}.RegisterDefaultDecoders(rb)
|
||||
bson.PrimitiveCodecs{}.RegisterPrimitiveCodecs(rb)
|
||||
|
||||
structcodec, _ := bsoncodec.NewStructCodec(bsoncodec.DefaultStructTagParser,
|
||||
bsonoptions.StructCodec().
|
||||
SetDecodeZeroStruct(true).
|
||||
SetEncodeOmitDefaultStruct(true).
|
||||
SetOverwriteDuplicatedInlinedFields(false).
|
||||
SetAllowUnexportedFields(true))
|
||||
emptyInterCodec := bsoncodec.NewEmptyInterfaceCodec(
|
||||
bsonoptions.EmptyInterfaceCodec().
|
||||
SetDecodeBinaryAsSlice(true))
|
||||
mapCodec := bsoncodec.NewMapCodec(
|
||||
bsonoptions.MapCodec().
|
||||
SetDecodeZerosMap(true).
|
||||
SetEncodeNilAsEmpty(true).
|
||||
SetEncodeKeysWithStringer(true))
|
||||
uintcodec := bsoncodec.NewUIntCodec(bsonoptions.UIntCodec().SetEncodeToMinSize(true))
|
||||
|
||||
rb.RegisterTypeDecoder(tEmpty, emptyInterCodec).
|
||||
RegisterDefaultDecoder(reflect.String, bsoncodec.NewStringCodec(bsonoptions.StringCodec().SetDecodeObjectIDAsHex(false))).
|
||||
RegisterDefaultDecoder(reflect.Struct, structcodec).
|
||||
RegisterDefaultDecoder(reflect.Map, mapCodec).
|
||||
RegisterTypeEncoder(tByteSlice, bsoncodec.NewByteSliceCodec(bsonoptions.ByteSliceCodec().SetEncodeNilAsEmpty(true))).
|
||||
RegisterDefaultEncoder(reflect.Struct, structcodec).
|
||||
RegisterDefaultEncoder(reflect.Slice, bsoncodec.NewSliceCodec(bsonoptions.SliceCodec().SetEncodeNilAsEmpty(true))).
|
||||
RegisterDefaultEncoder(reflect.Map, mapCodec).
|
||||
RegisterDefaultEncoder(reflect.Uint, uintcodec).
|
||||
RegisterDefaultEncoder(reflect.Uint8, uintcodec).
|
||||
RegisterDefaultEncoder(reflect.Uint16, uintcodec).
|
||||
RegisterDefaultEncoder(reflect.Uint32, uintcodec).
|
||||
RegisterDefaultEncoder(reflect.Uint64, uintcodec).
|
||||
RegisterTypeMapEntry(bsontype.Int32, tInt).
|
||||
RegisterTypeMapEntry(bsontype.DateTime, tTime).
|
||||
RegisterTypeMapEntry(bsontype.Array, tInterfaceSlice).
|
||||
RegisterTypeMapEntry(bsontype.Type(0), tM).
|
||||
RegisterTypeMapEntry(bsontype.EmbeddedDocument, tM).
|
||||
RegisterHookEncoder(tGetter, bsoncodec.ValueEncoderFunc(GetterEncodeValue)).
|
||||
RegisterHookDecoder(tSetter, bsoncodec.ValueDecoderFunc(SetterDecodeValue))
|
||||
|
||||
return rb
|
||||
}
|
||||
|
||||
// NewRespectNilValuesRegistryBuilder creates a new bsoncodec.RegistryBuilder configured to behave like mgo/bson
|
||||
// with RespectNilValues set to true.
|
||||
func NewRespectNilValuesRegistryBuilder() *bsoncodec.RegistryBuilder {
|
||||
rb := NewRegistryBuilder()
|
||||
|
||||
structcodec, _ := bsoncodec.NewStructCodec(bsoncodec.DefaultStructTagParser,
|
||||
bsonoptions.StructCodec().
|
||||
SetDecodeZeroStruct(true).
|
||||
SetEncodeOmitDefaultStruct(true).
|
||||
SetOverwriteDuplicatedInlinedFields(false).
|
||||
SetAllowUnexportedFields(true))
|
||||
mapCodec := bsoncodec.NewMapCodec(
|
||||
bsonoptions.MapCodec().
|
||||
SetDecodeZerosMap(true).
|
||||
SetEncodeNilAsEmpty(false))
|
||||
|
||||
rb.RegisterDefaultDecoder(reflect.Struct, structcodec).
|
||||
RegisterDefaultDecoder(reflect.Map, mapCodec).
|
||||
RegisterTypeEncoder(tByteSlice, bsoncodec.NewByteSliceCodec(bsonoptions.ByteSliceCodec().SetEncodeNilAsEmpty(false))).
|
||||
RegisterDefaultEncoder(reflect.Struct, structcodec).
|
||||
RegisterDefaultEncoder(reflect.Slice, bsoncodec.NewSliceCodec(bsonoptions.SliceCodec().SetEncodeNilAsEmpty(false))).
|
||||
RegisterDefaultEncoder(reflect.Map, mapCodec)
|
||||
|
||||
return rb
|
||||
}
|
||||
127
mongo/bson/mgocompat/setter_getter.go
Normal file
127
mongo/bson/mgocompat/setter_getter.go
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package mgocompat
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/bsoncodec"
|
||||
"go.mongodb.org/mongo-driver/bson/bsonrw"
|
||||
)
|
||||
|
||||
// Setter interface: a value implementing the bson.Setter interface will receive the BSON
|
||||
// value via the SetBSON method during unmarshaling, and the object
|
||||
// itself will not be changed as usual.
|
||||
//
|
||||
// If setting the value works, the method should return nil or alternatively
|
||||
// mgocompat.ErrSetZero to set the respective field to its zero value (nil for
|
||||
// pointer types). If SetBSON returns a non-nil error, the unmarshalling
|
||||
// procedure will stop and error out with the provided value.
|
||||
//
|
||||
// This interface is generally useful in pointer receivers, since the method
|
||||
// will want to change the receiver. A type field that implements the Setter
|
||||
// interface doesn't have to be a pointer, though.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// type MyString string
|
||||
//
|
||||
// func (s *MyString) SetBSON(raw bson.RawValue) error {
|
||||
// return raw.Unmarshal(s)
|
||||
// }
|
||||
type Setter interface {
|
||||
SetBSON(raw bson.RawValue) error
|
||||
}
|
||||
|
||||
// Getter interface: a value implementing the bson.Getter interface will have its GetBSON
|
||||
// method called when the given value has to be marshalled, and the result
|
||||
// of this method will be marshaled in place of the actual object.
|
||||
//
|
||||
// If GetBSON returns return a non-nil error, the marshalling procedure
|
||||
// will stop and error out with the provided value.
|
||||
type Getter interface {
|
||||
GetBSON() (interface{}, error)
|
||||
}
|
||||
|
||||
// SetterDecodeValue is the ValueDecoderFunc for Setter types.
|
||||
func SetterDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
|
||||
if !val.IsValid() || (!val.Type().Implements(tSetter) && !reflect.PtrTo(val.Type()).Implements(tSetter)) {
|
||||
return bsoncodec.ValueDecoderError{Name: "SetterDecodeValue", Types: []reflect.Type{tSetter}, Received: val}
|
||||
}
|
||||
|
||||
if val.Kind() == reflect.Ptr && val.IsNil() {
|
||||
if !val.CanSet() {
|
||||
return bsoncodec.ValueDecoderError{Name: "SetterDecodeValue", Types: []reflect.Type{tSetter}, Received: val}
|
||||
}
|
||||
val.Set(reflect.New(val.Type().Elem()))
|
||||
}
|
||||
|
||||
if !val.Type().Implements(tSetter) {
|
||||
if !val.CanAddr() {
|
||||
return bsoncodec.ValueDecoderError{Name: "ValueUnmarshalerDecodeValue", Types: []reflect.Type{tSetter}, Received: val}
|
||||
}
|
||||
val = val.Addr() // If the type doesn't implement the interface, a pointer to it must.
|
||||
}
|
||||
|
||||
t, src, err := bsonrw.Copier{}.CopyValueToBytes(vr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fn := val.Convert(tSetter).MethodByName("SetBSON")
|
||||
|
||||
errVal := fn.Call([]reflect.Value{reflect.ValueOf(bson.RawValue{Type: t, Value: src})})[0]
|
||||
if !errVal.IsNil() {
|
||||
err = errVal.Interface().(error)
|
||||
if err == ErrSetZero {
|
||||
val.Set(reflect.Zero(val.Type()))
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetterEncodeValue is the ValueEncoderFunc for Getter types.
|
||||
func GetterEncodeValue(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
|
||||
// Either val or a pointer to val must implement Getter
|
||||
switch {
|
||||
case !val.IsValid():
|
||||
return bsoncodec.ValueEncoderError{Name: "GetterEncodeValue", Types: []reflect.Type{tGetter}, Received: val}
|
||||
case val.Type().Implements(tGetter):
|
||||
// If Getter is implemented on a concrete type, make sure that val isn't a nil pointer
|
||||
if isImplementationNil(val, tGetter) {
|
||||
return vw.WriteNull()
|
||||
}
|
||||
case reflect.PtrTo(val.Type()).Implements(tGetter) && val.CanAddr():
|
||||
val = val.Addr()
|
||||
default:
|
||||
return bsoncodec.ValueEncoderError{Name: "GetterEncodeValue", Types: []reflect.Type{tGetter}, Received: val}
|
||||
}
|
||||
|
||||
fn := val.Convert(tGetter).MethodByName("GetBSON")
|
||||
returns := fn.Call(nil)
|
||||
if !returns[1].IsNil() {
|
||||
return returns[1].Interface().(error)
|
||||
}
|
||||
intermediate := returns[0]
|
||||
encoder, err := ec.Registry.LookupEncoder(intermediate.Type())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return encoder.EncodeValue(ec, vw, intermediate)
|
||||
}
|
||||
|
||||
// isImplementationNil returns if val is a nil pointer and inter is implemented on a concrete type
|
||||
func isImplementationNil(val reflect.Value, inter reflect.Type) bool {
|
||||
vt := val.Type()
|
||||
for vt.Kind() == reflect.Ptr {
|
||||
vt = vt.Elem()
|
||||
}
|
||||
return vt.Implements(inter) && val.Kind() == reflect.Ptr && val.IsNil()
|
||||
}
|
||||
Reference in New Issue
Block a user