Skip to content

Commit 6c34749

Browse files
prestonvasquezmatthewdale
authored andcommitted
GODRIVER-2896 Add IsZero to BSON RawValue (#1332)
Co-authored-by: Matt Dale <[email protected]>
1 parent e9e975d commit 6c34749

File tree

5 files changed

+154
-3
lines changed

5 files changed

+154
-3
lines changed

bson/bsontype/bsontype.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,14 @@ func (bt Type) String() string {
102102
return "invalid"
103103
}
104104
}
105+
106+
// IsValid will return true if the Type is valid.
107+
func (bt Type) IsValid() bool {
108+
switch bt {
109+
case Double, String, EmbeddedDocument, Array, Binary, Undefined, ObjectID, Boolean, DateTime, Null, Regex,
110+
DBPointer, JavaScript, Symbol, CodeWithScope, Int32, Timestamp, Int64, Decimal128, MinKey, MaxKey:
111+
return true
112+
default:
113+
return false
114+
}
115+
}

bson/primitive_codecs.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package bson
88

99
import (
1010
"errors"
11+
"fmt"
1112
"reflect"
1213

1314
"go.mongodb.org/mongo-driver/bson/bsoncodec"
@@ -45,15 +46,26 @@ func (pc PrimitiveCodecs) RegisterPrimitiveCodecs(rb *bsoncodec.RegistryBuilder)
4546

4647
// RawValueEncodeValue is the ValueEncoderFunc for RawValue.
4748
//
48-
// Deprecated: Use bson.NewRegistry to get a registry with all primitive encoders and decoders
49-
// registered.
49+
// If the RawValue's Type is "invalid" and the RawValue's Value is not empty or
50+
// nil, then this method will return an error.
51+
//
52+
// Deprecated: Use bson.NewRegistry to get a registry with all primitive
53+
// encoders and decoders registered.
5054
func (PrimitiveCodecs) RawValueEncodeValue(_ bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
5155
if !val.IsValid() || val.Type() != tRawValue {
52-
return bsoncodec.ValueEncoderError{Name: "RawValueEncodeValue", Types: []reflect.Type{tRawValue}, Received: val}
56+
return bsoncodec.ValueEncoderError{
57+
Name: "RawValueEncodeValue",
58+
Types: []reflect.Type{tRawValue},
59+
Received: val,
60+
}
5361
}
5462

5563
rawvalue := val.Interface().(RawValue)
5664

65+
if !rawvalue.Type.IsValid() {
66+
return fmt.Errorf("the RawValue Type specifies an invalid BSON type: %#x", byte(rawvalue.Type))
67+
}
68+
5769
return bsonrw.Copier{}.CopyValueFromBytes(vw, rawvalue.Type, rawvalue.Value)
5870
}
5971

bson/primitive_codecs_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ func compareErrors(err1, err2 error) bool {
6565
}
6666

6767
func TestDefaultValueEncoders(t *testing.T) {
68+
t.Parallel()
69+
6870
var pc PrimitiveCodecs
6971

7072
var wrong = func(string, string) string { return "wrong" }
@@ -107,6 +109,28 @@ func TestDefaultValueEncoders(t *testing.T) {
107109
bsonrwtest.WriteDouble,
108110
nil,
109111
},
112+
{
113+
"RawValue Type is zero with non-zero value",
114+
RawValue{
115+
Type: 0x00,
116+
Value: bsoncore.AppendDouble(nil, 3.14159),
117+
},
118+
nil,
119+
nil,
120+
bsonrwtest.Nothing,
121+
fmt.Errorf("the RawValue Type specifies an invalid BSON type: 0x0"),
122+
},
123+
{
124+
"RawValue Type is invalid",
125+
RawValue{
126+
Type: 0x8F,
127+
Value: bsoncore.AppendDouble(nil, 3.14159),
128+
},
129+
nil,
130+
nil,
131+
bsonrwtest.Nothing,
132+
fmt.Errorf("the RawValue Type specifies an invalid BSON type: 0x8f"),
133+
},
110134
},
111135
},
112136
{
@@ -166,9 +190,17 @@ func TestDefaultValueEncoders(t *testing.T) {
166190
}
167191

168192
for _, tc := range testCases {
193+
tc := tc // Capture the range variable
194+
169195
t.Run(tc.name, func(t *testing.T) {
196+
t.Parallel()
197+
170198
for _, subtest := range tc.subtests {
199+
subtest := subtest // Capture the range variable
200+
171201
t.Run(subtest.name, func(t *testing.T) {
202+
t.Parallel()
203+
172204
var ec bsoncodec.EncodeContext
173205
if subtest.ectx != nil {
174206
ec = *subtest.ectx
@@ -192,6 +224,8 @@ func TestDefaultValueEncoders(t *testing.T) {
192224
}
193225

194226
t.Run("success path", func(t *testing.T) {
227+
t.Parallel()
228+
195229
oid := primitive.NewObjectID()
196230
oids := []primitive.ObjectID{primitive.NewObjectID(), primitive.NewObjectID(), primitive.NewObjectID()}
197231
var str = new(string)
@@ -426,7 +460,11 @@ func TestDefaultValueEncoders(t *testing.T) {
426460
}
427461

428462
for _, tc := range testCases {
463+
tc := tc // Capture the range variable
464+
429465
t.Run(tc.name, func(t *testing.T) {
466+
t.Parallel()
467+
430468
b := make(bsonrw.SliceWriter, 0, 512)
431469
vw, err := bsonrw.NewBSONValueWriter(&b)
432470
noerr(t, err)

bson/raw_value.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ type RawValue struct {
3737
r *bsoncodec.Registry
3838
}
3939

40+
// IsZero reports whether the RawValue is zero, i.e. no data is present on
41+
// the RawValue. It returns true if Type is 0 and Value is empty or nil.
42+
func (rv RawValue) IsZero() bool {
43+
return rv.Type == 0x00 && len(rv.Value) == 0
44+
}
45+
4046
// Unmarshal deserializes BSON into the provided val. If RawValue cannot be unmarshaled into val, an
4147
// error is returned. This method will use the registry used to create the RawValue, if the RawValue
4248
// was created from partial BSON processing, or it will use the default registry. Users wishing to

bson/raw_value_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@ import (
1313

1414
"go.mongodb.org/mongo-driver/bson/bsoncodec"
1515
"go.mongodb.org/mongo-driver/bson/bsontype"
16+
"go.mongodb.org/mongo-driver/internal/assert"
1617
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
1718
)
1819

1920
func TestRawValue(t *testing.T) {
21+
t.Parallel()
22+
2023
t.Run("Unmarshal", func(t *testing.T) {
24+
t.Parallel()
25+
2126
t.Run("Uses registry attached to value", func(t *testing.T) {
27+
t.Parallel()
28+
2229
reg := bsoncodec.NewRegistryBuilder().Build()
2330
val := RawValue{Type: bsontype.String, Value: bsoncore.AppendString(nil, "foobar"), r: reg}
2431
var s string
@@ -29,6 +36,8 @@ func TestRawValue(t *testing.T) {
2936
}
3037
})
3138
t.Run("Uses default registry if no registry attached", func(t *testing.T) {
39+
t.Parallel()
40+
3241
want := "foobar"
3342
val := RawValue{Type: bsontype.String, Value: bsoncore.AppendString(nil, want)}
3443
var got string
@@ -40,7 +49,11 @@ func TestRawValue(t *testing.T) {
4049
})
4150
})
4251
t.Run("UnmarshalWithRegistry", func(t *testing.T) {
52+
t.Parallel()
53+
4354
t.Run("Returns error when registry is nil", func(t *testing.T) {
55+
t.Parallel()
56+
4457
want := ErrNilRegistry
4558
var val RawValue
4659
got := val.UnmarshalWithRegistry(nil, &D{})
@@ -49,6 +62,8 @@ func TestRawValue(t *testing.T) {
4962
}
5063
})
5164
t.Run("Returns lookup error", func(t *testing.T) {
65+
t.Parallel()
66+
5267
reg := bsoncodec.NewRegistryBuilder().Build()
5368
var val RawValue
5469
var s string
@@ -59,6 +74,8 @@ func TestRawValue(t *testing.T) {
5974
}
6075
})
6176
t.Run("Returns DecodeValue error", func(t *testing.T) {
77+
t.Parallel()
78+
6279
reg := NewRegistryBuilder().Build()
6380
val := RawValue{Type: bsontype.Double, Value: bsoncore.AppendDouble(nil, 3.14159)}
6481
var s string
@@ -69,6 +86,8 @@ func TestRawValue(t *testing.T) {
6986
}
7087
})
7188
t.Run("Success", func(t *testing.T) {
89+
t.Parallel()
90+
7291
reg := NewRegistryBuilder().Build()
7392
want := float64(3.14159)
7493
val := RawValue{Type: bsontype.Double, Value: bsoncore.AppendDouble(nil, want)}
@@ -81,7 +100,11 @@ func TestRawValue(t *testing.T) {
81100
})
82101
})
83102
t.Run("UnmarshalWithContext", func(t *testing.T) {
103+
t.Parallel()
104+
84105
t.Run("Returns error when DecodeContext is nil", func(t *testing.T) {
106+
t.Parallel()
107+
85108
want := ErrNilContext
86109
var val RawValue
87110
got := val.UnmarshalWithContext(nil, &D{})
@@ -90,6 +113,8 @@ func TestRawValue(t *testing.T) {
90113
}
91114
})
92115
t.Run("Returns lookup error", func(t *testing.T) {
116+
t.Parallel()
117+
93118
dc := bsoncodec.DecodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()}
94119
var val RawValue
95120
var s string
@@ -100,6 +125,8 @@ func TestRawValue(t *testing.T) {
100125
}
101126
})
102127
t.Run("Returns DecodeValue error", func(t *testing.T) {
128+
t.Parallel()
129+
103130
dc := bsoncodec.DecodeContext{Registry: NewRegistryBuilder().Build()}
104131
val := RawValue{Type: bsontype.Double, Value: bsoncore.AppendDouble(nil, 3.14159)}
105132
var s string
@@ -110,6 +137,8 @@ func TestRawValue(t *testing.T) {
110137
}
111138
})
112139
t.Run("Success", func(t *testing.T) {
140+
t.Parallel()
141+
113142
dc := bsoncodec.DecodeContext{Registry: NewRegistryBuilder().Build()}
114143
want := float64(3.14159)
115144
val := RawValue{Type: bsontype.Double, Value: bsoncore.AppendDouble(nil, want)}
@@ -121,4 +150,59 @@ func TestRawValue(t *testing.T) {
121150
}
122151
})
123152
})
153+
154+
t.Run("IsZero", func(t *testing.T) {
155+
t.Parallel()
156+
157+
tests := []struct {
158+
name string
159+
val RawValue
160+
want bool
161+
}{
162+
{
163+
name: "empty",
164+
val: RawValue{},
165+
want: true,
166+
},
167+
{
168+
name: "zero type but non-zero value",
169+
val: RawValue{
170+
Type: 0x00,
171+
Value: bsoncore.AppendInt32(nil, 0),
172+
},
173+
want: false,
174+
},
175+
{
176+
name: "zero type and zero value",
177+
val: RawValue{
178+
Type: 0x00,
179+
Value: bsoncore.AppendInt32(nil, 0),
180+
},
181+
},
182+
{
183+
name: "non-zero type and non-zero value",
184+
val: RawValue{
185+
Type: bsontype.String,
186+
Value: bsoncore.AppendString(nil, "foobar"),
187+
},
188+
want: false,
189+
},
190+
{
191+
name: "non-zero type and zero value",
192+
val: RawValue{
193+
Type: bsontype.String,
194+
Value: bsoncore.AppendString(nil, "foobar"),
195+
},
196+
},
197+
}
198+
199+
for _, tt := range tests {
200+
tt := tt // Capture the range variable
201+
t.Run(tt.name, func(t *testing.T) {
202+
t.Parallel()
203+
204+
assert.Equal(t, tt.want, tt.val.IsZero())
205+
})
206+
}
207+
})
124208
}

0 commit comments

Comments
 (0)