Skip to content

Commit 7045023

Browse files
committed
Merge branch 'development' of ssh://github.com/globalsign/mgo into release/r2018.06.15
* 'development' of ssh://github.com/globalsign/mgo: readme: credit @tbruyelle (#190) Add support for ssl dial string (#184) revert: MGO-156 Avoid iter.Next deadlock on dead sockets (#182) (#188) readme: credit everyone (#187) Contributing:findAndModify support writeConcern (#185) Allow passing slice pointer as an interface pointer to Iter.All (#181) MGO-156 Avoid iter.Next deadlock on dead sockets (#182) add NewMongoTimestamp() and MongoTimestamp.Time(),Counter() (#171) Expand documentation for *Iter.Next (#163) Add Collation support for calling Count() on a Query (#166) add URI options: "w", "j", "wtimeoutMS" (#162) Separate read/write network timeouts (#161) Respect nil slices, maps in bson encoder (#147) fix(dbtest): Use os.Kill on windows instead of Interrupt 🐛 inline pointer_to_struce mode: update comments. return error on pointer not to struct allow ptr in inline structs
2 parents efe0945 + 20226c9 commit 7045023

File tree

18 files changed

+818
-218
lines changed

18 files changed

+818
-218
lines changed

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implement
3131
* Supports dropping all indexes on a collection ([details](https://github.com/globalsign/mgo/pull/25))
3232
* Annotates log entries/profiler output with optional appName on 3.4+ ([details](https://github.com/globalsign/mgo/pull/28))
3333
* Support for read-only [views](https://docs.mongodb.com/manual/core/views/) in 3.4+ ([details](https://github.com/globalsign/mgo/pull/33))
34-
* Support for [collations](https://docs.mongodb.com/manual/reference/collation/) in 3.4+ ([details](https://github.com/globalsign/mgo/pull/37))
34+
* Support for [collations](https://docs.mongodb.com/manual/reference/collation/) in 3.4+ ([details](https://github.com/globalsign/mgo/pull/37), [more](https://github.com/globalsign/mgo/pull/166))
3535
* Provide BSON constants for convenience/sanity ([details](https://github.com/globalsign/mgo/pull/41))
3636
* Consistently unmarshal time.Time values as UTC ([details](https://github.com/globalsign/mgo/pull/42))
3737
* Enforces best practise coding guidelines ([details](https://github.com/globalsign/mgo/pull/44))
@@ -49,6 +49,15 @@ A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implement
4949
* Add BSON stream encoders ([details](https://github.com/globalsign/mgo/pull/127))
5050
* Add integer map key support in the BSON encoder ([details](https://github.com/globalsign/mgo/pull/140))
5151
* Support aggregation [collations](https://docs.mongodb.com/manual/reference/collation/) ([details](https://github.com/globalsign/mgo/pull/144))
52+
* Support encoding of inline struct references ([details](https://github.com/globalsign/mgo/pull/146))
53+
* Improved windows test harness ([details](https://github.com/globalsign/mgo/pull/158))
54+
* Improved type and nil handling in the BSON codec ([details](https://github.com/globalsign/mgo/pull/147/files), [more](https://github.com/globalsign/mgo/pull/181))
55+
* Separated network read/write timeouts ([details](https://github.com/globalsign/mgo/pull/161))
56+
* Expanded dial string configuration options ([details](https://github.com/globalsign/mgo/pull/162))
57+
* Implement MongoTimestamp ([details](https://github.com/globalsign/mgo/pull/171))
58+
* Support setting `writeConcern` for `findAndModify` operations ([details](https://github.com/globalsign/mgo/pull/185))
59+
* Add `ssl` to the dial string options ([details](https://github.com/globalsign/mgo/pull/184))
60+
5261

5362
---
5463

@@ -59,23 +68,32 @@ A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implement
5968
* @BenLubar
6069
* @carldunham
6170
* @carter2000
71+
* @cedric-cordenier
6272
* @cezarsa
73+
* @DaytonG
74+
* @ddspog
6375
* @drichelson
6476
* @dvic
6577
* @eaglerayp
6678
* @feliixx
6779
* @fmpwizard
6880
* @gazoon
81+
* @gedge
6982
* @gnawux
7083
* @idy
7184
* @jameinel
85+
* @jefferickson
7286
* @johnlawsharrison
7387
* @KJTsanaktsidis
88+
* @larrycinnabar
7489
* @mapete94
7590
* @maxnoel
7691
* @mcspring
92+
* @Mei-Zhao
7793
* @peterdeka
7894
* @Reenjii
95+
* @roobre
7996
* @smoya
8097
* @steve-gray
98+
* @tbruyelle
8199
* @wgallagher

bson/bson.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"errors"
4343
"fmt"
4444
"io"
45+
"math"
4546
"os"
4647
"reflect"
4748
"runtime"
@@ -426,6 +427,36 @@ func Now() time.Time {
426427
// strange reason has its own datatype defined in BSON.
427428
type MongoTimestamp int64
428429

430+
// Time returns the time part of ts which is stored with second precision.
431+
func (ts MongoTimestamp) Time() time.Time {
432+
return time.Unix(int64(uint64(ts)>>32), 0)
433+
}
434+
435+
// Counter returns the counter part of ts.
436+
func (ts MongoTimestamp) Counter() uint32 {
437+
return uint32(ts)
438+
}
439+
440+
// NewMongoTimestamp creates a timestamp using the given
441+
// date `t` (with second precision) and counter `c` (unique for `t`).
442+
//
443+
// Returns an error if time `t` is not between 1970-01-01T00:00:00Z
444+
// and 2106-02-07T06:28:15Z (inclusive).
445+
//
446+
// Note that two MongoTimestamps should never have the same (time, counter) combination:
447+
// the caller must ensure the counter `c` is increased if creating multiple MongoTimestamp
448+
// values for the same time `t` (ignoring fractions of seconds).
449+
func NewMongoTimestamp(t time.Time, c uint32) (MongoTimestamp, error) {
450+
u := t.Unix()
451+
if u < 0 || u > math.MaxUint32 {
452+
return -1, errors.New("invalid value for time")
453+
}
454+
455+
i := int64(u<<32 | int64(c))
456+
457+
return MongoTimestamp(i), nil
458+
}
459+
429460
type orderKey int64
430461

431462
// MaxKey is a special value that compares higher than all other possible BSON
@@ -746,6 +777,14 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
746777
return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
747778
}
748779
inlineMap = info.Num
780+
case reflect.Ptr:
781+
// allow only pointer to struct
782+
if kind := field.Type.Elem().Kind(); kind != reflect.Struct {
783+
return nil, errors.New("Option ,inline allows a pointer only to a struct, was given pointer to " + kind.String())
784+
}
785+
786+
field.Type = field.Type.Elem()
787+
fallthrough
749788
case reflect.Struct:
750789
sinfo, err := getStructInfo(field.Type)
751790
if err != nil {
@@ -765,7 +804,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
765804
fieldsList = append(fieldsList, finfo)
766805
}
767806
default:
768-
panic("Option ,inline needs a struct value or map field")
807+
panic("Option ,inline needs a struct value or a pointer to a struct or map field")
769808
}
770809
continue
771810
}

bson/bson_test.go

Lines changed: 158 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import (
3232
"encoding/json"
3333
"encoding/xml"
3434
"errors"
35+
"fmt"
36+
"math/rand"
3537
"net/url"
3638
"reflect"
3739
"strings"
@@ -271,6 +273,42 @@ func (s *S) TestMarshalBuffer(c *C) {
271273
c.Assert(data, DeepEquals, buf[:len(data)])
272274
}
273275

276+
func (s *S) TestPtrInline(c *C) {
277+
cases := []struct {
278+
In interface{}
279+
Out bson.M
280+
}{
281+
{
282+
In: inlinePtrStruct{A: 1, MStruct: &MStruct{M: 3}},
283+
Out: bson.M{"a": 1, "m": 3},
284+
},
285+
{ // go deeper
286+
In: inlinePtrPtrStruct{B: 10, inlinePtrStruct: &inlinePtrStruct{A: 20, MStruct: &MStruct{M: 30}}},
287+
Out: bson.M{"b": 10, "a": 20, "m": 30},
288+
},
289+
{
290+
// nil embed struct
291+
In: &inlinePtrStruct{A: 3},
292+
Out: bson.M{"a": 3},
293+
},
294+
{
295+
// nil embed struct
296+
In: &inlinePtrPtrStruct{B: 5},
297+
Out: bson.M{"b": 5},
298+
},
299+
}
300+
301+
for _, cs := range cases {
302+
data, err := bson.Marshal(cs.In)
303+
c.Assert(err, IsNil)
304+
var dataBSON bson.M
305+
err = bson.Unmarshal(data, &dataBSON)
306+
c.Assert(err, IsNil)
307+
308+
c.Assert(dataBSON, DeepEquals, cs.Out)
309+
}
310+
}
311+
274312
// --------------------------------------------------------------------------
275313
// Some one way marshaling operations which would unmarshal differently.
276314

@@ -713,8 +751,6 @@ var marshalErrorItems = []testItemType{
713751
"Attempted to marshal empty Raw document"},
714752
{bson.M{"w": bson.Raw{Kind: 0x3, Data: []byte{}}},
715753
"Attempted to marshal empty Raw document"},
716-
{&inlineCantPtr{&struct{ A, B int }{1, 2}},
717-
"Option ,inline needs a struct value or map field"},
718754
{&inlineDupName{1, struct{ A, B int }{2, 3}},
719755
"Duplicated key 'a' in struct bson_test.inlineDupName"},
720756
{&inlineDupMap{},
@@ -1171,8 +1207,19 @@ type inlineBadKeyMap struct {
11711207
M map[int]int `bson:",inline"`
11721208
}
11731209
type inlineUnexported struct {
1174-
M map[string]interface{} `bson:",inline"`
1175-
unexported `bson:",inline"`
1210+
M map[string]interface{} `bson:",inline"`
1211+
unexported `bson:",inline"`
1212+
}
1213+
type MStruct struct {
1214+
M int `bson:"m,omitempty"`
1215+
}
1216+
type inlinePtrStruct struct {
1217+
A int
1218+
*MStruct `bson:",inline"`
1219+
}
1220+
type inlinePtrPtrStruct struct {
1221+
B int
1222+
*inlinePtrStruct `bson:",inline"`
11761223
}
11771224
type unexported struct {
11781225
A int
@@ -1229,11 +1276,11 @@ func (s ifaceSlice) GetBSON() (interface{}, error) {
12291276

12301277
type (
12311278
MyString string
1232-
MyBytes []byte
1233-
MyBool bool
1234-
MyD []bson.DocElem
1235-
MyRawD []bson.RawDocElem
1236-
MyM map[string]interface{}
1279+
MyBytes []byte
1280+
MyBool bool
1281+
MyD []bson.DocElem
1282+
MyRawD []bson.RawDocElem
1283+
MyM map[string]interface{}
12371284
)
12381285

12391286
var (
@@ -1888,3 +1935,105 @@ func (s *S) BenchmarkNewObjectId(c *C) {
18881935
bson.NewObjectId()
18891936
}
18901937
}
1938+
1939+
func (s *S) TestMarshalRespectNil(c *C) {
1940+
type T struct {
1941+
Slice []int
1942+
SlicePtr *[]int
1943+
Ptr *int
1944+
Map map[string]interface{}
1945+
MapPtr *map[string]interface{}
1946+
}
1947+
1948+
bson.SetRespectNilValues(true)
1949+
defer bson.SetRespectNilValues(false)
1950+
1951+
testStruct1 := T{}
1952+
1953+
c.Assert(testStruct1.Slice, IsNil)
1954+
c.Assert(testStruct1.SlicePtr, IsNil)
1955+
c.Assert(testStruct1.Map, IsNil)
1956+
c.Assert(testStruct1.MapPtr, IsNil)
1957+
c.Assert(testStruct1.Ptr, IsNil)
1958+
1959+
b, _ := bson.Marshal(testStruct1)
1960+
1961+
testStruct2 := T{}
1962+
1963+
bson.Unmarshal(b, &testStruct2)
1964+
1965+
c.Assert(testStruct2.Slice, IsNil)
1966+
c.Assert(testStruct2.SlicePtr, IsNil)
1967+
c.Assert(testStruct2.Map, IsNil)
1968+
c.Assert(testStruct2.MapPtr, IsNil)
1969+
c.Assert(testStruct2.Ptr, IsNil)
1970+
1971+
testStruct1 = T{
1972+
Slice: []int{},
1973+
SlicePtr: &[]int{},
1974+
Map: map[string]interface{}{},
1975+
MapPtr: &map[string]interface{}{},
1976+
}
1977+
1978+
c.Assert(testStruct1.Slice, NotNil)
1979+
c.Assert(testStruct1.SlicePtr, NotNil)
1980+
c.Assert(testStruct1.Map, NotNil)
1981+
c.Assert(testStruct1.MapPtr, NotNil)
1982+
1983+
b, _ = bson.Marshal(testStruct1)
1984+
1985+
testStruct2 = T{}
1986+
1987+
bson.Unmarshal(b, &testStruct2)
1988+
1989+
c.Assert(testStruct2.Slice, NotNil)
1990+
c.Assert(testStruct2.SlicePtr, NotNil)
1991+
c.Assert(testStruct2.Map, NotNil)
1992+
c.Assert(testStruct2.MapPtr, NotNil)
1993+
}
1994+
1995+
func (s *S) TestMongoTimestampTime(c *C) {
1996+
t := time.Now()
1997+
ts, err := bson.NewMongoTimestamp(t, 123)
1998+
c.Assert(err, IsNil)
1999+
c.Assert(ts.Time().Unix(), Equals, t.Unix())
2000+
}
2001+
2002+
func (s *S) TestMongoTimestampCounter(c *C) {
2003+
rnd := rand.Uint32()
2004+
ts, err := bson.NewMongoTimestamp(time.Now(), rnd)
2005+
c.Assert(err, IsNil)
2006+
c.Assert(ts.Counter(), Equals, rnd)
2007+
}
2008+
2009+
func (s *S) TestMongoTimestampError(c *C) {
2010+
t := time.Date(1969, time.December, 31, 23, 59, 59, 999, time.UTC)
2011+
ts, err := bson.NewMongoTimestamp(t, 321)
2012+
c.Assert(int64(ts), Equals, int64(-1))
2013+
c.Assert(err, ErrorMatches, "invalid value for time")
2014+
}
2015+
2016+
func ExampleNewMongoTimestamp() {
2017+
2018+
var counter uint32 = 1
2019+
var t time.Time
2020+
2021+
for i := 1; i <= 3; i++ {
2022+
2023+
if c := time.Now(); t.Unix() == c.Unix() {
2024+
counter++
2025+
} else {
2026+
t = c
2027+
counter = 1
2028+
}
2029+
2030+
ts, err := bson.NewMongoTimestamp(t, counter)
2031+
if err != nil {
2032+
fmt.Printf("NewMongoTimestamp error: %v", err)
2033+
} else {
2034+
fmt.Printf("NewMongoTimestamp encoded timestamp: %d\n", ts)
2035+
}
2036+
2037+
time.Sleep(500 * time.Millisecond)
2038+
}
2039+
}

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+
}

0 commit comments

Comments
 (0)