Skip to content

Commit 57bf42b

Browse files
aksentyevdomodwyer
authored andcommitted
Support decimal number type keys in maps (globalsign#140)
* socket: only send client metadata once per socket (globalsign#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 globalsign#101 and fixes globalsign#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 (globalsign#111) * Brings in a patch on having flusher not suppress errors. (globalsign#81) go-mgo#360 * Fallback to JSON tags when BSON tag isn't present (globalsign#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 globalsign#101 and fixes globalsign#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 (globalsign#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 (globalsign#97) Add $changeStream support * readme: credit @peterdeka and @steve-gray (globalsign#110) * Hotfix globalsign#120 (globalsign#136) * cluster: fix deadlock in cluster synchronisation (globalsign#120) For a impressively thorough breakdown of the problem, see: globalsign#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
1 parent 104d5ee commit 57bf42b

File tree

4 files changed

+72
-17
lines changed

4 files changed

+72
-17
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implement
4545
* Gracefully recover from a temporarily unreachable server ([details](https://github.com/globalsign/mgo/pull/69))
4646
* Use JSON tags when no explicit BSON are tags set ([details](https://github.com/globalsign/mgo/pull/91))
4747
* Support [$changeStream](https://docs.mongodb.com/manual/changeStreams/) tailing on 3.6+ ([details](https://github.com/globalsign/mgo/pull/97))
48+
* Fix deadlock in cluster synchronisation ([details](https://github.com/globalsign/mgo/issues/120))
4849

4950
---
5051

@@ -55,11 +56,13 @@ A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implement
5556
* @carter2000
5657
* @cezarsa
5758
* @drichelson
59+
* @dvic
5860
* @eaglerayp
5961
* @feliixx
6062
* @fmpwizard
6163
* @idy
6264
* @jameinel
65+
* @KJTsanaktsidis
6366
* @gazoon
6467
* @mapete94
6568
* @peterdeka

bson/bson_test.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,8 @@ func (s *S) TestMarshalOneWayItems(c *C) {
607607
// --------------------------------------------------------------------------
608608
// One-way unmarshaling tests.
609609

610+
type intAlias int
611+
610612
var unmarshalItems = []testItemType{
611613
// Field is private. Should not attempt to unmarshal it.
612614
{&struct{ priv byte }{},
@@ -661,6 +663,14 @@ var unmarshalItems = []testItemType{
661663
// Decode a doc within a doc in to a slice within a doc; shouldn't error
662664
{&struct{ Foo []string }{},
663665
"\x03\x66\x6f\x6f\x00\x05\x00\x00\x00\x00"},
666+
667+
// int key maps
668+
{map[int]string{10: "s"},
669+
"\x0210\x00\x02\x00\x00\x00s\x00"},
670+
671+
//// event if type is alias to int
672+
{map[intAlias]string{10: "s"},
673+
"\x0210\x00\x02\x00\x00\x00s\x00"},
664674
}
665675

666676
func (s *S) TestUnmarshalOneWayItems(c *C) {
@@ -738,11 +748,6 @@ var unmarshalErrorItems = []unmarshalErrorType{
738748
"\x10name\x00\x08\x00\x00\x00",
739749
"Duplicated key 'name' in struct bson_test.structWithDupKeys"},
740750

741-
// Non-string map key.
742-
{map[int]interface{}{},
743-
"\x10name\x00\x08\x00\x00\x00",
744-
"BSON map must have string keys. Got: map\\[int\\]interface \\{\\}"},
745-
746751
{nil,
747752
"\xEEname\x00",
748753
"Unknown element kind \\(0xEE\\)"},
@@ -758,6 +763,11 @@ var unmarshalErrorItems = []unmarshalErrorType{
758763
{nil,
759764
"\x08\x62\x00\x02",
760765
"encoded boolean must be 1 or 0, found 2"},
766+
767+
// Non-string and not numeric map key.
768+
{map[bool]interface{}{true: 1},
769+
"\x10true\x00\x01\x00\x00\x00",
770+
"BSON map must have string or decimal keys. Got: map\\[bool\\]interface \\{\\}"},
761771
}
762772

763773
func (s *S) TestUnmarshalErrorItems(c *C) {
@@ -1161,8 +1171,8 @@ type inlineBadKeyMap struct {
11611171
M map[int]int `bson:",inline"`
11621172
}
11631173
type inlineUnexported struct {
1164-
M map[string]interface{} `bson:",inline"`
1165-
unexported `bson:",inline"`
1174+
M map[string]interface{} `bson:",inline"`
1175+
unexported `bson:",inline"`
11661176
}
11671177
type unexported struct {
11681178
A int
@@ -1219,11 +1229,11 @@ func (s ifaceSlice) GetBSON() (interface{}, error) {
12191229

12201230
type (
12211231
MyString string
1222-
MyBytes []byte
1223-
MyBool bool
1224-
MyD []bson.DocElem
1225-
MyRawD []bson.RawDocElem
1226-
MyM map[string]interface{}
1232+
MyBytes []byte
1233+
MyBool bool
1234+
MyD []bson.DocElem
1235+
MyRawD []bson.RawDocElem
1236+
MyM map[string]interface{}
12271237
)
12281238

12291239
var (

bson/decode.go

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,6 @@ func (d *decoder) readDocTo(out reflect.Value) {
176176
switch outk {
177177
case reflect.Map:
178178
keyType = outt.Key()
179-
if keyType.Kind() != reflect.String {
180-
panic("BSON map must have string keys. Got: " + outt.String())
181-
}
182179
if keyType != typeString {
183180
convertKey = true
184181
}
@@ -240,7 +237,42 @@ func (d *decoder) readDocTo(out reflect.Value) {
240237
if d.readElemTo(e, kind) {
241238
k := reflect.ValueOf(name)
242239
if convertKey {
243-
k = k.Convert(keyType)
240+
mapKeyType := out.Type().Key()
241+
mapKeyKind := mapKeyType.Kind()
242+
243+
switch mapKeyKind {
244+
case reflect.Int:
245+
fallthrough
246+
case reflect.Int8:
247+
fallthrough
248+
case reflect.Int16:
249+
fallthrough
250+
case reflect.Int32:
251+
fallthrough
252+
case reflect.Int64:
253+
fallthrough
254+
case reflect.Uint:
255+
fallthrough
256+
case reflect.Uint8:
257+
fallthrough
258+
case reflect.Uint16:
259+
fallthrough
260+
case reflect.Uint32:
261+
fallthrough
262+
case reflect.Uint64:
263+
fallthrough
264+
case reflect.Float32:
265+
fallthrough
266+
case reflect.Float64:
267+
parsed := d.parseMapKeyAsFloat(k, mapKeyKind)
268+
k = reflect.ValueOf(parsed)
269+
case reflect.String:
270+
mapKeyType = keyType
271+
default:
272+
panic("BSON map must have string or decimal keys. Got: " + outt.String())
273+
}
274+
275+
k = k.Convert(mapKeyType)
244276
}
245277
out.SetMapIndex(k, e)
246278
}
@@ -276,6 +308,16 @@ func (d *decoder) readDocTo(out reflect.Value) {
276308
d.docType = docType
277309
}
278310

311+
func (decoder) parseMapKeyAsFloat(k reflect.Value, mapKeyKind reflect.Kind) float64 {
312+
parsed, err := strconv.ParseFloat(k.String(), 64)
313+
if err != nil {
314+
panic("Map key is defined to be a decimal type (" + mapKeyKind.String() + ") but got error " +
315+
err.Error())
316+
}
317+
318+
return parsed
319+
}
320+
279321
func (d *decoder) readArrayDocTo(out reflect.Value) {
280322
end := int(d.readInt32())
281323
end += d.i - 4

bson/encode.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ func (e *encoder) addDoc(v reflect.Value) {
203203

204204
func (e *encoder) addMap(v reflect.Value) {
205205
for _, k := range v.MapKeys() {
206-
e.addElem(k.String(), v.MapIndex(k), false)
206+
e.addElem(fmt.Sprint(k), v.MapIndex(k), false)
207207
}
208208
}
209209

0 commit comments

Comments
 (0)