Skip to content

Commit 45151e7

Browse files
aksentyevdomodwyer
authored andcommitted
Respect nil slices, maps in bson encoder (#147)
* socket: only send client metadata once per socket (#105) Periodic cluster synchronisation calls isMaster() which currently resends the "client" metadata every call - the spec specifies: isMaster commands issued after the initial connection handshake MUST NOT contain handshake arguments https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.rst#connection-handshake This hotfix prevents subsequent isMaster calls from sending the client metadata again - fixes #101 and fixes #103. Thanks to @changwoo-nam @qhenkart @canthefason @jyoon17 for spotting the initial issue, opening tickets, and having the problem debugged with a PoC fix before I even woke up. * Merge Development (#111) * Brings in a patch on having flusher not suppress errors. (#81) go-mgo#360 * Fallback to JSON tags when BSON tag isn't present (#91) * Fallback to JSON tags when BSON tag isn't present Cleanup. * Add test to demonstrate tagging fallback. - Test coverage for tagging test. * socket: only send client metadata once per socket Periodic cluster synchronisation calls isMaster() which currently resends the "client" metadata every call - the spec specifies: isMaster commands issued after the initial connection handshake MUST NOT contain handshake arguments https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.rst#connection-handshake This hotfix prevents subsequent isMaster calls from sending the client metadata again - fixes #101 and fixes #103. Thanks to @changwoo-nam @qhenkart @canthefason @jyoon17 for spotting the initial issue, opening tickets, and having the problem debugged with a PoC fix before I even woke up. * Cluster abended test 254 (#100) * Add a test that mongo Server gets their abended reset as necessary. See https://github.com/go-mgo/mgo/issues/254 and https://github.com/go-mgo/mgo/pull/255/files * Include the patch from Issue 255. This brings in a test which fails without the patch, and passes with the patch. Still to be tested, manual tcpkill of a socket. * changeStream support (#97) Add $changeStream support * readme: credit @peterdeka and @steve-gray (#110) * Hotfix #120 (#136) * cluster: fix deadlock in cluster synchronisation (#120) For a impressively thorough breakdown of the problem, see: #120 (comment) Huge thanks to @dvic and @KJTsanaktsidis for the report and fix. * readme: credit @dvic and @KJTsanaktsidis * added support for marshalling/unmarshalling maps with non-string keys * refactor method receiver * added support for json-compatible support for slices and maps Marshal() func: nil slice or map converts to nil, not empty (initialized with len=0) * fix IsNil on slices and maps format * added godoc * fix sasl empty payload * fix scram-sha-1 auth * revert fix sasl empty payload
1 parent a46ca38 commit 45151e7

File tree

5 files changed

+78
-2
lines changed

5 files changed

+78
-2
lines changed

bson/bson_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1933,3 +1933,59 @@ func (s *S) BenchmarkNewObjectId(c *C) {
19331933
bson.NewObjectId()
19341934
}
19351935
}
1936+
1937+
func (s *S) TestMarshalRespectNil(c *C) {
1938+
type T struct {
1939+
Slice []int
1940+
SlicePtr *[]int
1941+
Ptr *int
1942+
Map map[string]interface{}
1943+
MapPtr *map[string]interface{}
1944+
}
1945+
1946+
bson.SetRespectNilValues(true)
1947+
defer bson.SetRespectNilValues(false)
1948+
1949+
testStruct1 := T{}
1950+
1951+
c.Assert(testStruct1.Slice, IsNil)
1952+
c.Assert(testStruct1.SlicePtr, IsNil)
1953+
c.Assert(testStruct1.Map, IsNil)
1954+
c.Assert(testStruct1.MapPtr, IsNil)
1955+
c.Assert(testStruct1.Ptr, IsNil)
1956+
1957+
b, _ := bson.Marshal(testStruct1)
1958+
1959+
testStruct2 := T{}
1960+
1961+
bson.Unmarshal(b, &testStruct2)
1962+
1963+
c.Assert(testStruct2.Slice, IsNil)
1964+
c.Assert(testStruct2.SlicePtr, IsNil)
1965+
c.Assert(testStruct2.Map, IsNil)
1966+
c.Assert(testStruct2.MapPtr, IsNil)
1967+
c.Assert(testStruct2.Ptr, IsNil)
1968+
1969+
testStruct1 = T{
1970+
Slice: []int{},
1971+
SlicePtr: &[]int{},
1972+
Map: map[string]interface{}{},
1973+
MapPtr: &map[string]interface{}{},
1974+
}
1975+
1976+
c.Assert(testStruct1.Slice, NotNil)
1977+
c.Assert(testStruct1.SlicePtr, NotNil)
1978+
c.Assert(testStruct1.Map, NotNil)
1979+
c.Assert(testStruct1.MapPtr, NotNil)
1980+
1981+
b, _ = bson.Marshal(testStruct1)
1982+
1983+
testStruct2 = T{}
1984+
1985+
bson.Unmarshal(b, &testStruct2)
1986+
1987+
c.Assert(testStruct2.Slice, NotNil)
1988+
c.Assert(testStruct2.SlicePtr, NotNil)
1989+
c.Assert(testStruct2.Map, NotNil)
1990+
c.Assert(testStruct2.MapPtr, NotNil)
1991+
}

bson/compatibility.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package bson
22

3-
// Current state of the JSON tag fallback option.
3+
// Current state of the JSON tag fallback option.
44
var useJSONTagFallback = false
5+
var useRespectNilValues = false
56

67
// SetJSONTagFallback enables or disables the JSON-tag fallback for structure tagging. When this is enabled, structures
78
// without BSON tags on a field will fall-back to using the JSON tag (if present).
@@ -14,3 +15,15 @@ func SetJSONTagFallback(state bool) {
1415
func JSONTagFallbackState() bool {
1516
return useJSONTagFallback
1617
}
18+
19+
// SetRespectNilValues enables or disables serializing nil slices or maps to `null` values.
20+
// In other words it enables `encoding/json` compatible behaviour.
21+
func SetRespectNilValues(state bool) {
22+
useRespectNilValues = state
23+
}
24+
25+
// RespectNilValuesState returns the current status of the JSON nil slices and maps fallback compatibility option.
26+
// See SetRespectNilValues for more information.
27+
func RespectNilValuesState() bool {
28+
return useRespectNilValues
29+
}

bson/encode.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,12 @@ func (e *encoder) addStruct(v reflect.Value) {
245245
if info.OmitEmpty && isZero(value) {
246246
continue
247247
}
248+
if useRespectNilValues &&
249+
(value.Kind() == reflect.Slice || value.Kind() == reflect.Map) &&
250+
value.IsNil() {
251+
e.addElem(info.Key, reflect.ValueOf(nil), info.MinSize)
252+
continue
253+
}
248254
e.addElem(info.Key, value, info.MinSize)
249255
}
250256
}

internal/sasl/sasl.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, er
127127
if rc == C.SASL_CONTINUE {
128128
return clientData, false, nil
129129
}
130+
130131
return nil, false, saslError(rc, ss.conn, "cannot establish SASL session")
131132
}
132133

internal/scram/scram.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func NewClient(newHash func() hash.Hash, user, pass string) *Client {
9191
// Out returns the data to be sent to the server in the current step.
9292
func (c *Client) Out() []byte {
9393
if c.out.Len() == 0 {
94-
return nil
94+
return []byte{}
9595
}
9696
return c.out.Bytes()
9797
}

0 commit comments

Comments
 (0)