-
Notifications
You must be signed in to change notification settings - Fork 898
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-3470 Correct BSON unmarshaling logic for null values #1924
base: v1
Are you sure you want to change the base?
Conversation
API Change ReportNo changes found! |
bson/unmarshaling_cases_test.go
Outdated
PtrTracker *unmarshalCallTracker `bson:"ptr_tracker"` | ||
} | ||
|
||
func (ms *unmarshalCallTracker) UnmarshalBSONValue(bsontype.Type, []byte) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also test this behavior for UnmarshalBSON
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, looks good! 👍
Open question about using the same set of conditions in ValueUnmarshalerDecodeValue
and UnmarshalerDecodeValue
so it's easier to understand in the future.
// directly set to nil here. Since the pointer is being replaced with nil, | ||
// there is no opportunity (or reason) for the custom UnmarshalBSONValue logic | ||
// to be called. | ||
if vr.Type() == bsontype.Null && val.Kind() == reflect.Ptr { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a similar block in UnmarshalerDecodeValue
after the value is read into a []byte
:
if val.Kind() == reflect.Ptr && len(src) == 0 {
val.Set(reflect.Zero(val.Type()))
return nil
}
Can we use the same condition in both methods? Or are they distinct scenarios?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The len(src)
check was implemented here: https://github.com/mongodb/mongo-go-driver/pull/833/files
Since the bytes represent BSON null are copied, the type is converted from null to invalid:
_, src, err := bsonrw.Copier{}.CopyValueToBytes(vr)
if err != nil {
return err
}
Which means that this check doesn’t work:
if val.Kind() == reflect.Ptr && vr.Type() == bsontype.Null {
val.Set(reflect.Zero(val.Type()))
return nil
}
Checking after copying is weaker since validity should be checked on the first block:
if !val.IsValid() || (!val.Type().Implements(tValueUnmarshaler) && !reflect.PtrTo(val.Type()).Implements(tValueUnmarshaler)) {
return ValueDecoderError{Name: "ValueUnmarshalerDecodeValue", Types: []reflect.Type{tValueUnmarshaler}, Received: val}
}
So I think BSON null is the only case where you would get an invalid type after copying. I suggest we mirror ValueUnmarshalerDecodeValue
.
32190fe
GODRIVER-3470
Summary
Ensure UnmarshalBSONValue is bypassed and the Go pointer is set to nil ONLY when the Go type is a pointer and the BSON value is null.
Background & Motivation
PR #1903 introduced logic where UnmarshalBSONValue() is not called for bson.TypeNull, which breaks applications that rely on this behavior for initializing fields.