diff --git a/README.md b/README.md index 2990a3cdb..72717c808 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implement * Gracefully recover from a temporarily unreachable server ([details](https://github.com/globalsign/mgo/pull/69)) * Use JSON tags when no explicit BSON are tags set ([details](https://github.com/globalsign/mgo/pull/91)) * Support [$changeStream](https://docs.mongodb.com/manual/changeStreams/) tailing on 3.6+ ([details](https://github.com/globalsign/mgo/pull/97)) +* Fix deadlock in cluster synchronisation ([details](https://github.com/globalsign/mgo/issues/120)) --- @@ -55,11 +56,13 @@ A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implement * @carter2000 * @cezarsa * @drichelson +* @dvic * @eaglerayp * @feliixx * @fmpwizard * @idy * @jameinel +* @KJTsanaktsidis * @gazoon * @mapete94 * @peterdeka diff --git a/bson/bson_test.go b/bson/bson_test.go index ebb8b96bd..406ede6ae 100644 --- a/bson/bson_test.go +++ b/bson/bson_test.go @@ -607,6 +607,8 @@ func (s *S) TestMarshalOneWayItems(c *C) { // -------------------------------------------------------------------------- // One-way unmarshaling tests. +type intAlias int + var unmarshalItems = []testItemType{ // Field is private. Should not attempt to unmarshal it. {&struct{ priv byte }{}, @@ -661,6 +663,14 @@ var unmarshalItems = []testItemType{ // Decode a doc within a doc in to a slice within a doc; shouldn't error {&struct{ Foo []string }{}, "\x03\x66\x6f\x6f\x00\x05\x00\x00\x00\x00"}, + + // int key maps + {map[int]string{10: "s"}, + "\x0210\x00\x02\x00\x00\x00s\x00"}, + + //// event if type is alias to int + {map[intAlias]string{10: "s"}, + "\x0210\x00\x02\x00\x00\x00s\x00"}, } func (s *S) TestUnmarshalOneWayItems(c *C) { @@ -738,11 +748,6 @@ var unmarshalErrorItems = []unmarshalErrorType{ "\x10name\x00\x08\x00\x00\x00", "Duplicated key 'name' in struct bson_test.structWithDupKeys"}, - // Non-string map key. - {map[int]interface{}{}, - "\x10name\x00\x08\x00\x00\x00", - "BSON map must have string keys. Got: map\\[int\\]interface \\{\\}"}, - {nil, "\xEEname\x00", "Unknown element kind \\(0xEE\\)"}, @@ -758,6 +763,11 @@ var unmarshalErrorItems = []unmarshalErrorType{ {nil, "\x08\x62\x00\x02", "encoded boolean must be 1 or 0, found 2"}, + + // Non-string and not numeric map key. + {map[bool]interface{}{true: 1}, + "\x10true\x00\x01\x00\x00\x00", + "BSON map must have string or decimal keys. Got: map\\[bool\\]interface \\{\\}"}, } func (s *S) TestUnmarshalErrorItems(c *C) { @@ -1161,8 +1171,8 @@ type inlineBadKeyMap struct { M map[int]int `bson:",inline"` } type inlineUnexported struct { - M map[string]interface{} `bson:",inline"` - unexported `bson:",inline"` + M map[string]interface{} `bson:",inline"` + unexported `bson:",inline"` } type unexported struct { A int @@ -1219,11 +1229,11 @@ func (s ifaceSlice) GetBSON() (interface{}, error) { type ( MyString string - MyBytes []byte - MyBool bool - MyD []bson.DocElem - MyRawD []bson.RawDocElem - MyM map[string]interface{} + MyBytes []byte + MyBool bool + MyD []bson.DocElem + MyRawD []bson.RawDocElem + MyM map[string]interface{} ) var ( diff --git a/bson/decode.go b/bson/decode.go index e71eac23f..658856add 100644 --- a/bson/decode.go +++ b/bson/decode.go @@ -176,9 +176,6 @@ func (d *decoder) readDocTo(out reflect.Value) { switch outk { case reflect.Map: keyType = outt.Key() - if keyType.Kind() != reflect.String { - panic("BSON map must have string keys. Got: " + outt.String()) - } if keyType != typeString { convertKey = true } @@ -240,7 +237,42 @@ func (d *decoder) readDocTo(out reflect.Value) { if d.readElemTo(e, kind) { k := reflect.ValueOf(name) if convertKey { - k = k.Convert(keyType) + mapKeyType := out.Type().Key() + mapKeyKind := mapKeyType.Kind() + + switch mapKeyKind { + case reflect.Int: + fallthrough + case reflect.Int8: + fallthrough + case reflect.Int16: + fallthrough + case reflect.Int32: + fallthrough + case reflect.Int64: + fallthrough + case reflect.Uint: + fallthrough + case reflect.Uint8: + fallthrough + case reflect.Uint16: + fallthrough + case reflect.Uint32: + fallthrough + case reflect.Uint64: + fallthrough + case reflect.Float32: + fallthrough + case reflect.Float64: + parsed := d.parseMapKeyAsFloat(k, mapKeyKind) + k = reflect.ValueOf(parsed) + case reflect.String: + mapKeyType = keyType + default: + panic("BSON map must have string or decimal keys. Got: " + outt.String()) + } + + k = k.Convert(mapKeyType) } out.SetMapIndex(k, e) } @@ -276,6 +308,16 @@ func (d *decoder) readDocTo(out reflect.Value) { d.docType = docType } +func (decoder) parseMapKeyAsFloat(k reflect.Value, mapKeyKind reflect.Kind) float64 { + parsed, err := strconv.ParseFloat(k.String(), 64) + if err != nil { + panic("Map key is defined to be a decimal type (" + mapKeyKind.String() + ") but got error " + + err.Error()) + } + + return parsed +} + func (d *decoder) readArrayDocTo(out reflect.Value) { end := int(d.readInt32()) end += d.i - 4 diff --git a/bson/encode.go b/bson/encode.go index 5d5f452b0..7e0b84d77 100644 --- a/bson/encode.go +++ b/bson/encode.go @@ -203,7 +203,7 @@ func (e *encoder) addDoc(v reflect.Value) { func (e *encoder) addMap(v reflect.Value) { for _, k := range v.MapKeys() { - e.addElem(k.String(), v.MapIndex(k), false) + e.addElem(fmt.Sprint(k), v.MapIndex(k), false) } }