Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/ISSUE_TEMPLATE
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
We use the issue tracker to track bugs with mgo - if you have a usage question,
it's best to try Stack Overflow :)

Replace this text with your description, and please answer the questions below
before submitting your issue to help us out. Thanks!

-------------------------------------------------------------------------------

### What version of MongoDB are you using (`mongod --version`)?
```
<mongodb version here>
```

### What version of Go are you using (`go version`)?
```
<go version here>
```

### What operating system and processor architecture are you using (`go env`)?
```
<go env here>
```

### What did you do?

If possible, provide a recipe for reproducing the error.
A runnable program is great and really helps!

### Can you reproduce the issue on the latest `development` branch?
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ Detailed documentation of the API is available at

A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implements the [BSON](http://bsonspec.org) specification is also included, and may be used independently of the driver.

## Supported Versions

`mgo` is known to work well on (and has integration tests against) MongoDB v3.0, 3.2, 3.4 and 3.6.

MongoDB 4.0 is currently experimental - we would happily accept PRs to help improve support!

## Changes
* Fixes attempting to authenticate before every query ([details](https://github.com/go-mgo/mgo/issues/254))
* Removes bulk update / delete batch size limitations ([details](https://github.com/go-mgo/mgo/issues/288))
Expand All @@ -31,7 +37,7 @@ A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implement
* Supports dropping all indexes on a collection ([details](https://github.com/globalsign/mgo/pull/25))
* Annotates log entries/profiler output with optional appName on 3.4+ ([details](https://github.com/globalsign/mgo/pull/28))
* Support for read-only [views](https://docs.mongodb.com/manual/core/views/) in 3.4+ ([details](https://github.com/globalsign/mgo/pull/33))
* Support for [collations](https://docs.mongodb.com/manual/reference/collation/) in 3.4+ ([details](https://github.com/globalsign/mgo/pull/37))
* 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))
* Provide BSON constants for convenience/sanity ([details](https://github.com/globalsign/mgo/pull/41))
* Consistently unmarshal time.Time values as UTC ([details](https://github.com/globalsign/mgo/pull/42))
* Enforces best practise coding guidelines ([details](https://github.com/globalsign/mgo/pull/44))
Expand All @@ -49,6 +55,15 @@ A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implement
* Add BSON stream encoders ([details](https://github.com/globalsign/mgo/pull/127))
* Add integer map key support in the BSON encoder ([details](https://github.com/globalsign/mgo/pull/140))
* Support aggregation [collations](https://docs.mongodb.com/manual/reference/collation/) ([details](https://github.com/globalsign/mgo/pull/144))
* Support encoding of inline struct references ([details](https://github.com/globalsign/mgo/pull/146))
* Improved windows test harness ([details](https://github.com/globalsign/mgo/pull/158))
* 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))
* Separated network read/write timeouts ([details](https://github.com/globalsign/mgo/pull/161))
* Expanded dial string configuration options ([details](https://github.com/globalsign/mgo/pull/162))
* Implement MongoTimestamp ([details](https://github.com/globalsign/mgo/pull/171))
* Support setting `writeConcern` for `findAndModify` operations ([details](https://github.com/globalsign/mgo/pull/185))
* Add `ssl` to the dial string options ([details](https://github.com/globalsign/mgo/pull/184))


---

Expand All @@ -59,23 +74,32 @@ A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implement
* @BenLubar
* @carldunham
* @carter2000
* @cedric-cordenier
* @cezarsa
* @DaytonG
* @ddspog
* @drichelson
* @dvic
* @eaglerayp
* @feliixx
* @fmpwizard
* @gazoon
* @gedge
* @gnawux
* @idy
* @jameinel
* @jefferickson
* @johnlawsharrison
* @KJTsanaktsidis
* @larrycinnabar
* @mapete94
* @maxnoel
* @mcspring
* @Mei-Zhao
* @peterdeka
* @Reenjii
* @roobre
* @smoya
* @steve-gray
* @tbruyelle
* @wgallagher
41 changes: 40 additions & 1 deletion bson/bson.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"errors"
"fmt"
"io"
"math"
"os"
"reflect"
"runtime"
Expand Down Expand Up @@ -426,6 +427,36 @@ func Now() time.Time {
// strange reason has its own datatype defined in BSON.
type MongoTimestamp int64

// Time returns the time part of ts which is stored with second precision.
func (ts MongoTimestamp) Time() time.Time {
return time.Unix(int64(uint64(ts)>>32), 0)
}

// Counter returns the counter part of ts.
func (ts MongoTimestamp) Counter() uint32 {
return uint32(ts)
}

// NewMongoTimestamp creates a timestamp using the given
// date `t` (with second precision) and counter `c` (unique for `t`).
//
// Returns an error if time `t` is not between 1970-01-01T00:00:00Z
// and 2106-02-07T06:28:15Z (inclusive).
//
// Note that two MongoTimestamps should never have the same (time, counter) combination:
// the caller must ensure the counter `c` is increased if creating multiple MongoTimestamp
// values for the same time `t` (ignoring fractions of seconds).
func NewMongoTimestamp(t time.Time, c uint32) (MongoTimestamp, error) {
u := t.Unix()
if u < 0 || u > math.MaxUint32 {
return -1, errors.New("invalid value for time")
}

i := int64(u<<32 | int64(c))

return MongoTimestamp(i), nil
}

type orderKey int64

// MaxKey is a special value that compares higher than all other possible BSON
Expand Down Expand Up @@ -746,6 +777,14 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
}
inlineMap = info.Num
case reflect.Ptr:
// allow only pointer to struct
if kind := field.Type.Elem().Kind(); kind != reflect.Struct {
return nil, errors.New("Option ,inline allows a pointer only to a struct, was given pointer to " + kind.String())
}

field.Type = field.Type.Elem()
fallthrough
case reflect.Struct:
sinfo, err := getStructInfo(field.Type)
if err != nil {
Expand All @@ -765,7 +804,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
fieldsList = append(fieldsList, finfo)
}
default:
panic("Option ,inline needs a struct value or map field")
panic("Option ,inline needs a struct value or a pointer to a struct or map field")
}
continue
}
Expand Down
167 changes: 158 additions & 9 deletions bson/bson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import (
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"math/rand"
"net/url"
"reflect"
"strings"
Expand Down Expand Up @@ -271,6 +273,42 @@ func (s *S) TestMarshalBuffer(c *C) {
c.Assert(data, DeepEquals, buf[:len(data)])
}

func (s *S) TestPtrInline(c *C) {
cases := []struct {
In interface{}
Out bson.M
}{
{
In: inlinePtrStruct{A: 1, MStruct: &MStruct{M: 3}},
Out: bson.M{"a": 1, "m": 3},
},
{ // go deeper
In: inlinePtrPtrStruct{B: 10, inlinePtrStruct: &inlinePtrStruct{A: 20, MStruct: &MStruct{M: 30}}},
Out: bson.M{"b": 10, "a": 20, "m": 30},
},
{
// nil embed struct
In: &inlinePtrStruct{A: 3},
Out: bson.M{"a": 3},
},
{
// nil embed struct
In: &inlinePtrPtrStruct{B: 5},
Out: bson.M{"b": 5},
},
}

for _, cs := range cases {
data, err := bson.Marshal(cs.In)
c.Assert(err, IsNil)
var dataBSON bson.M
err = bson.Unmarshal(data, &dataBSON)
c.Assert(err, IsNil)

c.Assert(dataBSON, DeepEquals, cs.Out)
}
}

// --------------------------------------------------------------------------
// Some one way marshaling operations which would unmarshal differently.

Expand Down Expand Up @@ -713,8 +751,6 @@ var marshalErrorItems = []testItemType{
"Attempted to marshal empty Raw document"},
{bson.M{"w": bson.Raw{Kind: 0x3, Data: []byte{}}},
"Attempted to marshal empty Raw document"},
{&inlineCantPtr{&struct{ A, B int }{1, 2}},
"Option ,inline needs a struct value or map field"},
{&inlineDupName{1, struct{ A, B int }{2, 3}},
"Duplicated key 'a' in struct bson_test.inlineDupName"},
{&inlineDupMap{},
Expand Down Expand Up @@ -1171,8 +1207,19 @@ 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 MStruct struct {
M int `bson:"m,omitempty"`
}
type inlinePtrStruct struct {
A int
*MStruct `bson:",inline"`
}
type inlinePtrPtrStruct struct {
B int
*inlinePtrStruct `bson:",inline"`
}
type unexported struct {
A int
Expand Down Expand Up @@ -1229,11 +1276,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 (
Expand Down Expand Up @@ -1888,3 +1935,105 @@ func (s *S) BenchmarkNewObjectId(c *C) {
bson.NewObjectId()
}
}

func (s *S) TestMarshalRespectNil(c *C) {
type T struct {
Slice []int
SlicePtr *[]int
Ptr *int
Map map[string]interface{}
MapPtr *map[string]interface{}
}

bson.SetRespectNilValues(true)
defer bson.SetRespectNilValues(false)

testStruct1 := T{}

c.Assert(testStruct1.Slice, IsNil)
c.Assert(testStruct1.SlicePtr, IsNil)
c.Assert(testStruct1.Map, IsNil)
c.Assert(testStruct1.MapPtr, IsNil)
c.Assert(testStruct1.Ptr, IsNil)

b, _ := bson.Marshal(testStruct1)

testStruct2 := T{}

bson.Unmarshal(b, &testStruct2)

c.Assert(testStruct2.Slice, IsNil)
c.Assert(testStruct2.SlicePtr, IsNil)
c.Assert(testStruct2.Map, IsNil)
c.Assert(testStruct2.MapPtr, IsNil)
c.Assert(testStruct2.Ptr, IsNil)

testStruct1 = T{
Slice: []int{},
SlicePtr: &[]int{},
Map: map[string]interface{}{},
MapPtr: &map[string]interface{}{},
}

c.Assert(testStruct1.Slice, NotNil)
c.Assert(testStruct1.SlicePtr, NotNil)
c.Assert(testStruct1.Map, NotNil)
c.Assert(testStruct1.MapPtr, NotNil)

b, _ = bson.Marshal(testStruct1)

testStruct2 = T{}

bson.Unmarshal(b, &testStruct2)

c.Assert(testStruct2.Slice, NotNil)
c.Assert(testStruct2.SlicePtr, NotNil)
c.Assert(testStruct2.Map, NotNil)
c.Assert(testStruct2.MapPtr, NotNil)
}

func (s *S) TestMongoTimestampTime(c *C) {
t := time.Now()
ts, err := bson.NewMongoTimestamp(t, 123)
c.Assert(err, IsNil)
c.Assert(ts.Time().Unix(), Equals, t.Unix())
}

func (s *S) TestMongoTimestampCounter(c *C) {
rnd := rand.Uint32()
ts, err := bson.NewMongoTimestamp(time.Now(), rnd)
c.Assert(err, IsNil)
c.Assert(ts.Counter(), Equals, rnd)
}

func (s *S) TestMongoTimestampError(c *C) {
t := time.Date(1969, time.December, 31, 23, 59, 59, 999, time.UTC)
ts, err := bson.NewMongoTimestamp(t, 321)
c.Assert(int64(ts), Equals, int64(-1))
c.Assert(err, ErrorMatches, "invalid value for time")
}

func ExampleNewMongoTimestamp() {

var counter uint32 = 1
var t time.Time

for i := 1; i <= 3; i++ {

if c := time.Now(); t.Unix() == c.Unix() {
counter++
} else {
t = c
counter = 1
}

ts, err := bson.NewMongoTimestamp(t, counter)
if err != nil {
fmt.Printf("NewMongoTimestamp error: %v", err)
} else {
fmt.Printf("NewMongoTimestamp encoded timestamp: %d\n", ts)
}

time.Sleep(500 * time.Millisecond)
}
}
Loading