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

Add support for Undefined BSON type #85

Merged
merged 2 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
10 changes: 8 additions & 2 deletions wirebson/bson.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
// Double float64
// String string
// Binary data Binary
// Undefined UndefinedType
// ObjectId ObjectID
// Boolean bool
// Date time.Time
Expand Down Expand Up @@ -64,7 +65,7 @@ type CompositeType interface {
//
// CString is not included as it is not a real BSON type.
type ScalarType interface {
float64 | string | Binary | ObjectID | bool | time.Time | NullType | Regex | int32 | Timestamp | int64 | Decimal128
float64 | string | Binary | UndefinedType | ObjectID | bool | time.Time | NullType | Regex | int32 | Timestamp | int64 | Decimal128
}

// AnyDocument represents a BSON document type (both [*Document] and [RawDocument]).
Expand Down Expand Up @@ -97,6 +98,7 @@ func validBSONType(v any) error {
case float64:
case string:
case Binary:
case UndefinedType:
case ObjectID:
case bool:
case time.Time:
Expand Down Expand Up @@ -160,6 +162,8 @@ func fromDriver(v any) (any, error) {
Subtype: BinarySubtype(v.Subtype),
B: v.Data,
}, nil
case bson.Undefined:
return Undefined, nil
case bson.ObjectID:
return ObjectID(v), nil
case bool:
Expand Down Expand Up @@ -234,14 +238,16 @@ func toDriver(v any) (any, error) {
Subtype: byte(v.Subtype),
Data: v.B,
}, nil
case UndefinedType:
return bson.Undefined{}, nil
case ObjectID:
return bson.ObjectID(v), nil
case bool:
return v, nil
case time.Time:
return bson.NewDateTimeFromTime(v), nil
case NullType:
return nil, nil
return bson.Null{}, nil
case Regex:
return bson.Regex{
Pattern: v.Pattern,
Expand Down
24 changes: 24 additions & 0 deletions wirebson/bson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ var normalTestCases = []normalTestCase{
Binary{Subtype: BinaryUser, B: []byte{0x42}},
Binary{Subtype: BinaryGeneric, B: []byte{}},
),
"undefined", MustArray(Undefined),
"objectID", MustArray(ObjectID{0x42}, ObjectID{}),
"bool", MustArray(true, false),
"datetime", MustArray(
Expand Down Expand Up @@ -327,6 +328,9 @@ var normalTestCases = []normalTestCase{
Binary(user:Qg==),
Binary(generic:),
],
"undefined": [
undefined,
],
"objectID": [
ObjectID(420000000000000000000000),
ObjectID(000000000000000000000000),
Expand Down Expand Up @@ -414,6 +418,11 @@ var normalTestCases = []normalTestCase{
}
}
],
"undefined": [
{
"$undefined": true
}
],
"objectID": [
{
"$oid": "420000000000000000000000"
Expand Down Expand Up @@ -593,6 +602,21 @@ var normalTestCases = []normalTestCase{
"f": Binary(user:dg==),
}`,
},
{
name: "undefinedDoc",
raw: RawDocument{
0x08, 0x00, 0x00, 0x00,
0x06, 0x66, 0x00,
0x00,
},
doc: MustDocument(
"f", Undefined,
),
mi: `
{
"f": undefined,
}`,
},
{
name: "objectIDDoc",
raw: RawDocument{
Expand Down
2 changes: 1 addition & 1 deletion wirebson/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func decodeScalarField(b []byte, t tag) (v any, size int, err error) {
size = sizeBinary(bin)

case tagUndefined:
err = lazyerrors.Errorf("unsupported tag %s: %w", t, ErrDecodeInvalidInput)
v = Undefined

case tagObjectID:
v, err = decodeObjectID(b)
Expand Down
4 changes: 4 additions & 0 deletions wirebson/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ func encodeScalarField(buf *bytes.Buffer, name string, v any) error {
buf.WriteByte(byte(tagString))
case Binary:
buf.WriteByte(byte(tagBinary))
case UndefinedType:
buf.WriteByte(byte(tagUndefined))
case ObjectID:
buf.WriteByte(byte(tagObjectID))
case bool:
Expand Down Expand Up @@ -172,6 +174,8 @@ func encodeScalarValue(b []byte, v any) {
encodeString(b, v)
case Binary:
encodeBinary(b, v)
case UndefinedType:
// nothing
case ObjectID:
encodeObjectID(b, v)
case bool:
Expand Down
6 changes: 6 additions & 0 deletions wirebson/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ func slogValue(v any, depth int) slog.Value {
case Binary:
return slog.StringValue(fmt.Sprintf("%#v", v))

case UndefinedType:
return slog.Value{}

case ObjectID:
return slog.StringValue("ObjectID(" + hex.EncodeToString(v[:]) + ")")

Expand Down Expand Up @@ -301,6 +304,9 @@ func logMessage(v any, indent, depth int, b *strings.Builder) {
b.WriteString(base64.StdEncoding.EncodeToString(v.B))
b.WriteByte(')')

case UndefinedType:
b.WriteString("undefined")

case ObjectID:
b.WriteString("ObjectID(")
b.WriteString(hex.EncodeToString(v[:]))
Expand Down
2 changes: 2 additions & 0 deletions wirebson/size.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ func sizeScalar(v any) int {
return sizeString(v)
case Binary:
return sizeBinary(v)
case UndefinedType:
return 0
case ObjectID:
return sizeObjectID
case bool:
Expand Down
43 changes: 22 additions & 21 deletions wirebson/testdata/all.hex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
00000000 13 02 00 00 04 64 6f 63 75 6d 65 6e 74 00 36 00 |.....document.6.|
00000000 26 02 00 00 04 64 6f 63 75 6d 65 6e 74 00 36 00 |&....document.6.|
00000010 00 00 03 30 00 26 00 00 00 02 00 04 00 00 00 66 |...0.&.........f|
00000020 6f 6f 00 02 62 61 72 00 04 00 00 00 62 61 7a 00 |oo..bar.....baz.|
00000030 02 00 04 00 00 00 71 75 78 00 00 03 31 00 05 00 |......qux...1...|
Expand All @@ -12,23 +12,24 @@
000000b0 04 73 74 72 69 6e 67 00 18 00 00 00 02 30 00 04 |.string......0..|
000000c0 00 00 00 66 6f 6f 00 02 31 00 01 00 00 00 00 00 |...foo..1.......|
000000d0 04 62 69 6e 61 72 79 00 16 00 00 00 05 30 00 01 |.binary......0..|
000000e0 00 00 00 80 42 05 31 00 00 00 00 00 00 00 04 6f |....B.1........o|
000000f0 62 6a 65 63 74 49 44 00 23 00 00 00 07 30 00 42 |bjectID.#....0.B|
00000100 00 00 00 00 00 00 00 00 00 00 00 07 31 00 00 00 |............1...|
00000110 00 00 00 00 00 00 00 00 00 00 00 04 62 6f 6f 6c |............bool|
00000120 00 0d 00 00 00 08 30 00 01 08 31 00 00 00 04 64 |......0...1....d|
00000130 61 74 65 74 69 6d 65 00 1b 00 00 00 09 30 00 2b |atetime......0.+|
00000140 e6 51 e7 7a 01 00 00 09 31 00 00 28 d3 ed 7c c7 |.Q.z....1..(..|.|
00000150 ff ff 00 04 6e 75 6c 6c 00 08 00 00 00 0a 30 00 |....null......0.|
00000160 00 04 72 65 67 65 78 00 11 00 00 00 0b 30 00 70 |..regex......0.p|
00000170 00 6f 00 0b 31 00 00 00 00 04 69 6e 74 33 32 00 |.o..1.....int32.|
00000180 13 00 00 00 10 30 00 2a 00 00 00 10 31 00 00 00 |.....0.*....1...|
00000190 00 00 00 04 74 69 6d 65 73 74 61 6d 70 00 1b 00 |....timestamp...|
000001a0 00 00 11 30 00 2a 00 00 00 00 00 00 00 11 31 00 |...0.*........1.|
000001b0 00 00 00 00 00 00 00 00 00 04 69 6e 74 36 34 00 |..........int64.|
000001c0 1b 00 00 00 12 30 00 2a 00 00 00 00 00 00 00 12 |.....0.*........|
000001d0 31 00 00 00 00 00 00 00 00 00 00 04 64 65 63 69 |1...........deci|
000001e0 6d 61 6c 31 32 38 00 2b 00 00 00 13 30 00 2a 00 |mal128.+....0.*.|
000001f0 00 00 00 00 00 00 0d 00 00 00 00 00 00 00 13 31 |...............1|
00000200 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000210 00 00 00 |...|
000000e0 00 00 00 80 42 05 31 00 00 00 00 00 00 00 04 75 |....B.1........u|
000000f0 6e 64 65 66 69 6e 65 64 00 08 00 00 00 06 30 00 |ndefined......0.|
00000100 00 04 6f 62 6a 65 63 74 49 44 00 23 00 00 00 07 |..objectID.#....|
00000110 30 00 42 00 00 00 00 00 00 00 00 00 00 00 07 31 |0.B............1|
00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 62 |...............b|
00000130 6f 6f 6c 00 0d 00 00 00 08 30 00 01 08 31 00 00 |ool......0...1..|
00000140 00 04 64 61 74 65 74 69 6d 65 00 1b 00 00 00 09 |..datetime......|
00000150 30 00 2b e6 51 e7 7a 01 00 00 09 31 00 00 28 d3 |0.+.Q.z....1..(.|
00000160 ed 7c c7 ff ff 00 04 6e 75 6c 6c 00 08 00 00 00 |.|.....null.....|
00000170 0a 30 00 00 04 72 65 67 65 78 00 11 00 00 00 0b |.0...regex......|
00000180 30 00 70 00 6f 00 0b 31 00 00 00 00 04 69 6e 74 |0.p.o..1.....int|
00000190 33 32 00 13 00 00 00 10 30 00 2a 00 00 00 10 31 |32......0.*....1|
000001a0 00 00 00 00 00 00 04 74 69 6d 65 73 74 61 6d 70 |.......timestamp|
000001b0 00 1b 00 00 00 11 30 00 2a 00 00 00 00 00 00 00 |......0.*.......|
000001c0 11 31 00 00 00 00 00 00 00 00 00 00 04 69 6e 74 |.1...........int|
000001d0 36 34 00 1b 00 00 00 12 30 00 2a 00 00 00 00 00 |64......0.*.....|
000001e0 00 00 12 31 00 00 00 00 00 00 00 00 00 00 04 64 |...1...........d|
000001f0 65 63 69 6d 61 6c 31 32 38 00 2b 00 00 00 13 30 |ecimal128.+....0|
00000200 00 2a 00 00 00 00 00 00 00 0d 00 00 00 00 00 00 |.*..............|
00000210 00 13 31 00 00 00 00 00 00 00 00 00 00 00 00 00 |..1.............|
00000220 00 00 00 00 00 00 |......|
24 changes: 24 additions & 0 deletions wirebson/undefined.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2021 FerretDB Inc.
//
// 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
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package wirebson

// UndefinedType represents BSON scalar type undefined.
type UndefinedType struct{}

// Undefined represents BSON scalar value undefined.
//
// Its usage is deprecated, but it is still used in a few places.
// See https://github.com/FerretDB/FerretDB/issues/2286 for an example.
var Undefined = UndefinedType{}
35 changes: 35 additions & 0 deletions wirebson/undefined_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2021 FerretDB Inc.
//
// 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
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package wirebson

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestUndefinedNull(t *testing.T) {
assert.Equal(t, Undefined, Undefined)
assert.Equal(t, Null, Null)

assert.NotEqual(t, Undefined, Null)
assert.EqualValues(t, Undefined, Null)

assert.Equal(t, UndefinedType{}, Undefined)
assert.Equal(t, NullType{}, Null)

assert.NotEqual(t, UndefinedType{}, Null)
assert.EqualValues(t, UndefinedType{}, Null)
}