mgo

Merge lp:~rogpeppe/mgo/thread-safe-newobject into lp:mgo

Proposed by Roger Peppe
Status: Work in progress
Proposed branch: lp:~rogpeppe/mgo/thread-safe-newobject
Merge into: lp:mgo
Diff against target: 17597 lines (+17399/-0) (has conflicts)
39 files modified
.bzrignore (+2/-0)
LICENSE (+25/-0)
Makefile (+5/-0)
auth.go (+276/-0)
auth_test.go (+560/-0)
bson/LICENSE (+25/-0)
bson/bson.go (+639/-0)
bson/bson_test.go (+1369/-0)
bson/decode.go (+734/-0)
bson/encode.go (+430/-0)
cluster.go (+504/-0)
cluster_test.go (+1108/-0)
doc.go (+30/-0)
export_test.go (+10/-0)
gridfs.go (+716/-0)
gridfs_test.go (+607/-0)
log.go (+89/-0)
queue.go (+91/-0)
queue_test.go (+104/-0)
server.go (+332/-0)
session.go (+3075/-0)
session_test.go (+2978/-0)
socket.go (+550/-0)
stats.go (+137/-0)
suite_test.go (+170/-0)
testdb/dropall.js (+31/-0)
testdb/init.js (+86/-0)
testdb/setup.sh (+52/-0)
testdb/supervisord.conf (+49/-0)
testdb/wait.js (+51/-0)
txn/chaos.go (+68/-0)
txn/debug.go (+99/-0)
txn/flusher.go (+985/-0)
txn/mgo_test.go (+101/-0)
txn/sim_test.go (+389/-0)
txn/tarjan.go (+96/-0)
txn/tarjan_test.go (+44/-0)
txn/txn.go (+444/-0)
txn/txn_test.go (+338/-0)
Conflict adding file bson.  Moved existing file to bson.moved.
To merge this branch: bzr merge lp:~rogpeppe/mgo/thread-safe-newobject
Reviewer Review Type Date Requested Status
Gustavo Niemeyer Pending
Review via email: mp+156782@code.launchpad.net

Description of the change

bson: make NewObjectId thread safe

Also use an array rather than a slice to
remove a few unnecessary bounds checks.

To post a comment you must log in.

Unmerged revisions

196. By Roger Peppe

bson: make NewObjectId thread safe

195. By Gustavo Niemeyer

Other minor test fixes for 2.4.

194. By Gustavo Niemeyer

auth_test.go: fix expected auth error messages for 2.4

193. By Gustavo Niemeyer

supervisord.conf: drop startsecs for mongos

192. By Gustavo Niemeyer

Test direct connection to a replicaset server in unknown membership state.

191. By Gustavo Niemeyer

Allow direct connection to replica set member in unknown state.

190. By Gustavo Niemeyer

Fixed omitempty on float values, as reported by Otto Bretz.

189. By Gustavo Niemeyer

New bson.IsObjectIdHex function.

188. By Gustavo Niemeyer

In preparation for 2.4, which is coming with multiple new index types,
change the format of 2D indexes from "@foo" to "$2d:foo". Preserve
parsing of the old style for compatibility.

187. By Gustavo Niemeyer

Fix DialWithInfo so it dials to the standard MongoDB port if the address
doesn't inform one. Thanks to Mark Severson for reporting the issue.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.bzrignore'
2--- .bzrignore 1970-01-01 00:00:00 +0000
3+++ .bzrignore 2013-04-03 09:16:27 +0000
4@@ -0,0 +1,2 @@
5+_*
6+[856].out
7
8=== added file 'LICENSE'
9--- LICENSE 1970-01-01 00:00:00 +0000
10+++ LICENSE 2013-04-03 09:16:27 +0000
11@@ -0,0 +1,25 @@
12+mgo - MongoDB driver for Go
13+
14+Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
15+
16+All rights reserved.
17+
18+Redistribution and use in source and binary forms, with or without
19+modification, are permitted provided that the following conditions are met:
20+
21+1. Redistributions of source code must retain the above copyright notice, this
22+ list of conditions and the following disclaimer.
23+2. Redistributions in binary form must reproduce the above copyright notice,
24+ this list of conditions and the following disclaimer in the documentation
25+ and/or other materials provided with the distribution.
26+
27+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
28+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
29+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
31+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
34+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
38=== added file 'Makefile'
39--- Makefile 1970-01-01 00:00:00 +0000
40+++ Makefile 2013-04-03 09:16:27 +0000
41@@ -0,0 +1,5 @@
42+startdb:
43+ @testdb/setup.sh start
44+
45+stopdb:
46+ @testdb/setup.sh stop
47
48=== added file 'auth.go'
49--- auth.go 1970-01-01 00:00:00 +0000
50+++ auth.go 2013-04-03 09:16:27 +0000
51@@ -0,0 +1,276 @@
52+// mgo - MongoDB driver for Go
53+//
54+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
55+//
56+// All rights reserved.
57+//
58+// Redistribution and use in source and binary forms, with or without
59+// modification, are permitted provided that the following conditions are met:
60+//
61+// 1. Redistributions of source code must retain the above copyright notice, this
62+// list of conditions and the following disclaimer.
63+// 2. Redistributions in binary form must reproduce the above copyright notice,
64+// this list of conditions and the following disclaimer in the documentation
65+// and/or other materials provided with the distribution.
66+//
67+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
68+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
69+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
70+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
71+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
72+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
73+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
74+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
75+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
76+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
77+
78+package mgo
79+
80+import (
81+ "crypto/md5"
82+ "encoding/hex"
83+ "errors"
84+ "fmt"
85+ "labix.org/v2/mgo/bson"
86+ "sync"
87+)
88+
89+type authInfo struct {
90+ db, user, pass string
91+}
92+
93+type authCmd struct {
94+ Authenticate int
95+ Nonce, User, Key string
96+}
97+
98+type authResult struct {
99+ ErrMsg string
100+ Ok bool
101+}
102+
103+type getNonceCmd struct {
104+ GetNonce int
105+}
106+
107+type getNonceResult struct {
108+ Nonce string
109+ Err string "$err"
110+ Code int
111+}
112+
113+type logoutCmd struct {
114+ Logout int
115+}
116+
117+func (socket *mongoSocket) getNonce() (nonce string, err error) {
118+ socket.Lock()
119+ for socket.cachedNonce == "" && socket.dead == nil {
120+ debugf("Socket %p to %s: waiting for nonce", socket, socket.addr)
121+ socket.gotNonce.Wait()
122+ }
123+ if socket.cachedNonce == "mongos" {
124+ socket.Unlock()
125+ return "", errors.New("Can't authenticate with mongos; see http://j.mp/mongos-auth")
126+ }
127+ debugf("Socket %p to %s: got nonce", socket, socket.addr)
128+ nonce, err = socket.cachedNonce, socket.dead
129+ socket.cachedNonce = ""
130+ socket.Unlock()
131+ if err != nil {
132+ nonce = ""
133+ }
134+ return
135+}
136+
137+func (socket *mongoSocket) resetNonce() {
138+ debugf("Socket %p to %s: requesting a new nonce", socket, socket.addr)
139+ op := &queryOp{}
140+ op.query = &getNonceCmd{GetNonce: 1}
141+ op.collection = "admin.$cmd"
142+ op.limit = -1
143+ op.replyFunc = func(err error, reply *replyOp, docNum int, docData []byte) {
144+ if err != nil {
145+ socket.kill(errors.New("getNonce: " + err.Error()), true)
146+ return
147+ }
148+ result := &getNonceResult{}
149+ err = bson.Unmarshal(docData, &result)
150+ if err != nil {
151+ socket.kill(errors.New("Failed to unmarshal nonce: " + err.Error()), true)
152+ return
153+ }
154+ debugf("Socket %p to %s: nonce unmarshalled: %#v", socket, socket.addr, result)
155+ if result.Code == 13390 {
156+ // mongos doesn't yet support auth (see http://j.mp/mongos-auth)
157+ result.Nonce = "mongos"
158+ } else if result.Nonce == "" {
159+ var msg string
160+ if result.Err != "" {
161+ msg = fmt.Sprintf("Got an empty nonce: %s (%d)", result.Err, result.Code)
162+ } else {
163+ msg = "Got an empty nonce"
164+ }
165+ socket.kill(errors.New(msg), true)
166+ return
167+ }
168+ socket.Lock()
169+ if socket.cachedNonce != "" {
170+ socket.Unlock()
171+ panic("resetNonce: nonce already cached")
172+ }
173+ socket.cachedNonce = result.Nonce
174+ socket.gotNonce.Signal()
175+ socket.Unlock()
176+ }
177+ err := socket.Query(op)
178+ if err != nil {
179+ socket.kill(errors.New("resetNonce: " + err.Error()), true)
180+ }
181+}
182+
183+func (socket *mongoSocket) Login(db string, user string, pass string) error {
184+ socket.Lock()
185+ for _, a := range socket.auth {
186+ if a.db == db && a.user == user && a.pass == pass {
187+ debugf("Socket %p to %s: login: db=%q user=%q (already logged in)", socket, socket.addr, db, user)
188+ socket.Unlock()
189+ return nil
190+ }
191+ }
192+ if auth, found := socket.dropLogout(db, user, pass); found {
193+ debugf("Socket %p to %s: login: db=%q user=%q (cached)", socket, socket.addr, db, user)
194+ socket.auth = append(socket.auth, auth)
195+ socket.Unlock()
196+ return nil
197+ }
198+ socket.Unlock()
199+
200+ debugf("Socket %p to %s: login: db=%q user=%q", socket, socket.addr, db, user)
201+
202+ // Note that this only works properly because this function is
203+ // synchronous, which means the nonce won't get reset while we're
204+ // using it and any other login requests will block waiting for a
205+ // new nonce provided in the defer call below.
206+ nonce, err := socket.getNonce()
207+ if err != nil {
208+ return err
209+ }
210+ defer socket.resetNonce()
211+
212+ psum := md5.New()
213+ psum.Write([]byte(user + ":mongo:" + pass))
214+
215+ ksum := md5.New()
216+ ksum.Write([]byte(nonce + user))
217+ ksum.Write([]byte(hex.EncodeToString(psum.Sum(nil))))
218+
219+ key := hex.EncodeToString(ksum.Sum(nil))
220+
221+ cmd := authCmd{Authenticate: 1, User: user, Nonce: nonce, Key: key}
222+
223+ var mutex sync.Mutex
224+ var replyErr error
225+ mutex.Lock()
226+
227+ op := queryOp{}
228+ op.query = &cmd
229+ op.collection = db + ".$cmd"
230+ op.limit = -1
231+ op.replyFunc = func(err error, reply *replyOp, docNum int, docData []byte) {
232+ defer mutex.Unlock()
233+
234+ if err != nil {
235+ replyErr = err
236+ return
237+ }
238+
239+ // Must handle this within the read loop for the socket, so
240+ // that concurrent login requests are properly ordered.
241+ result := &authResult{}
242+ err = bson.Unmarshal(docData, result)
243+ if err != nil {
244+ replyErr = err
245+ return
246+ }
247+ if !result.Ok {
248+ replyErr = errors.New(result.ErrMsg)
249+ }
250+
251+ socket.Lock()
252+ socket.dropAuth(db)
253+ socket.auth = append(socket.auth, authInfo{db, user, pass})
254+ socket.Unlock()
255+ }
256+
257+ err = socket.Query(&op)
258+ if err != nil {
259+ return err
260+ }
261+ mutex.Lock() // Wait.
262+ if replyErr != nil {
263+ debugf("Socket %p to %s: login error: %s", socket, socket.addr, replyErr)
264+ } else {
265+ debugf("Socket %p to %s: login successful", socket, socket.addr)
266+ }
267+ return replyErr
268+}
269+
270+func (socket *mongoSocket) Logout(db string) {
271+ socket.Lock()
272+ auth, found := socket.dropAuth(db)
273+ if found {
274+ debugf("Socket %p to %s: logout: db=%q (flagged)", socket, socket.addr, db)
275+ socket.logout = append(socket.logout, auth)
276+ }
277+ socket.Unlock()
278+}
279+
280+func (socket *mongoSocket) LogoutAll() {
281+ socket.Lock()
282+ if l := len(socket.auth); l > 0 {
283+ debugf("Socket %p to %s: logout all (flagged %d)", socket, socket.addr, l)
284+ socket.logout = append(socket.logout, socket.auth...)
285+ socket.auth = socket.auth[0:0]
286+ }
287+ socket.Unlock()
288+}
289+
290+func (socket *mongoSocket) flushLogout() (ops []interface{}) {
291+ socket.Lock()
292+ if l := len(socket.logout); l > 0 {
293+ debugf("Socket %p to %s: logout all (flushing %d)", socket, socket.addr, l)
294+ for i := 0; i != l; i++ {
295+ op := queryOp{}
296+ op.query = &logoutCmd{1}
297+ op.collection = socket.logout[i].db + ".$cmd"
298+ op.limit = -1
299+ ops = append(ops, &op)
300+ }
301+ socket.logout = socket.logout[0:0]
302+ }
303+ socket.Unlock()
304+ return
305+}
306+
307+func (socket *mongoSocket) dropAuth(db string) (auth authInfo, found bool) {
308+ for i, a := range socket.auth {
309+ if a.db == db {
310+ copy(socket.auth[i:], socket.auth[i+1:])
311+ socket.auth = socket.auth[:len(socket.auth)-1]
312+ return a, true
313+ }
314+ }
315+ return auth, false
316+}
317+
318+func (socket *mongoSocket) dropLogout(db, user, pass string) (auth authInfo, found bool) {
319+ for i, a := range socket.logout {
320+ if a.db == db && a.user == user && a.pass == pass {
321+ copy(socket.logout[i:], socket.logout[i+1:])
322+ socket.logout = socket.logout[:len(socket.logout)-1]
323+ return a, true
324+ }
325+ }
326+ return auth, false
327+}
328
329=== added file 'auth_test.go'
330--- auth_test.go 1970-01-01 00:00:00 +0000
331+++ auth_test.go 2013-04-03 09:16:27 +0000
332@@ -0,0 +1,560 @@
333+// mgo - MongoDB driver for Go
334+//
335+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
336+//
337+// All rights reserved.
338+//
339+// Redistribution and use in source and binary forms, with or without
340+// modification, are permitted provided that the following conditions are met:
341+//
342+// 1. Redistributions of source code must retain the above copyright notice, this
343+// list of conditions and the following disclaimer.
344+// 2. Redistributions in binary form must reproduce the above copyright notice,
345+// this list of conditions and the following disclaimer in the documentation
346+// and/or other materials provided with the distribution.
347+//
348+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
349+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
350+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
351+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
352+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
353+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
354+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
355+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
356+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
357+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
358+
359+package mgo_test
360+
361+import (
362+ . "launchpad.net/gocheck"
363+ "labix.org/v2/mgo"
364+ "sync"
365+)
366+
367+func (s *S) TestAuthLogin(c *C) {
368+ session, err := mgo.Dial("localhost:40002")
369+ c.Assert(err, IsNil)
370+ defer session.Close()
371+
372+ coll := session.DB("mydb").C("mycoll")
373+ err = coll.Insert(M{"n": 1})
374+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
375+
376+ admindb := session.DB("admin")
377+
378+ err = admindb.Login("root", "wrong")
379+ c.Assert(err, ErrorMatches, "auth fails")
380+
381+ err = admindb.Login("root", "rapadura")
382+ c.Assert(err, IsNil)
383+
384+ err = coll.Insert(M{"n": 1})
385+ c.Assert(err, IsNil)
386+}
387+
388+func (s *S) TestAuthLoginLogout(c *C) {
389+ session, err := mgo.Dial("localhost:40002")
390+ c.Assert(err, IsNil)
391+ defer session.Close()
392+
393+ admindb := session.DB("admin")
394+ err = admindb.Login("root", "rapadura")
395+ c.Assert(err, IsNil)
396+
397+ admindb.Logout()
398+
399+ coll := session.DB("mydb").C("mycoll")
400+ err = coll.Insert(M{"n": 1})
401+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
402+
403+ // Must have dropped auth from the session too.
404+ session = session.Copy()
405+ defer session.Close()
406+
407+ coll = session.DB("mydb").C("mycoll")
408+ err = coll.Insert(M{"n": 1})
409+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
410+}
411+
412+func (s *S) TestAuthLoginLogoutAll(c *C) {
413+ session, err := mgo.Dial("localhost:40002")
414+ c.Assert(err, IsNil)
415+ defer session.Close()
416+
417+ admindb := session.DB("admin")
418+ err = admindb.Login("root", "rapadura")
419+ c.Assert(err, IsNil)
420+
421+ session.LogoutAll()
422+
423+ coll := session.DB("mydb").C("mycoll")
424+ err = coll.Insert(M{"n": 1})
425+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
426+
427+ // Must have dropped auth from the session too.
428+ session = session.Copy()
429+ defer session.Close()
430+
431+ coll = session.DB("mydb").C("mycoll")
432+ err = coll.Insert(M{"n": 1})
433+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
434+}
435+
436+func (s *S) TestAuthAddUser(c *C) {
437+ session, err := mgo.Dial("localhost:40002")
438+ c.Assert(err, IsNil)
439+ defer session.Close()
440+
441+ admindb := session.DB("admin")
442+ err = admindb.Login("root", "rapadura")
443+ c.Assert(err, IsNil)
444+
445+ mydb := session.DB("mydb")
446+ err = mydb.AddUser("myruser", "mypass", true)
447+ c.Assert(err, IsNil)
448+ err = mydb.AddUser("mywuser", "mypass", false)
449+ c.Assert(err, IsNil)
450+
451+ err = mydb.Login("myruser", "mypass")
452+ c.Assert(err, IsNil)
453+
454+ admindb.Logout()
455+
456+ coll := session.DB("mydb").C("mycoll")
457+ err = coll.Insert(M{"n": 1})
458+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
459+
460+ err = mydb.Login("mywuser", "mypass")
461+ c.Assert(err, IsNil)
462+
463+ err = coll.Insert(M{"n": 1})
464+ c.Assert(err, IsNil)
465+}
466+
467+func (s *S) TestAuthAddUserReplaces(c *C) {
468+ session, err := mgo.Dial("localhost:40002")
469+ c.Assert(err, IsNil)
470+ defer session.Close()
471+
472+ admindb := session.DB("admin")
473+ err = admindb.Login("root", "rapadura")
474+ c.Assert(err, IsNil)
475+
476+ mydb := session.DB("mydb")
477+ err = mydb.AddUser("myuser", "myoldpass", false)
478+ c.Assert(err, IsNil)
479+ err = mydb.AddUser("myuser", "mynewpass", true)
480+ c.Assert(err, IsNil)
481+
482+ admindb.Logout()
483+
484+ err = mydb.Login("myuser", "myoldpass")
485+ c.Assert(err, ErrorMatches, "auth fails")
486+ err = mydb.Login("myuser", "mynewpass")
487+ c.Assert(err, IsNil)
488+
489+ // ReadOnly flag was changed too.
490+ err = mydb.C("mycoll").Insert(M{"n": 1})
491+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
492+}
493+
494+func (s *S) TestAuthRemoveUser(c *C) {
495+ session, err := mgo.Dial("localhost:40002")
496+ c.Assert(err, IsNil)
497+ defer session.Close()
498+
499+ admindb := session.DB("admin")
500+ err = admindb.Login("root", "rapadura")
501+ c.Assert(err, IsNil)
502+
503+ mydb := session.DB("mydb")
504+ err = mydb.AddUser("myuser", "mypass", true)
505+ c.Assert(err, IsNil)
506+ err = mydb.RemoveUser("myuser")
507+ c.Assert(err, IsNil)
508+
509+ err = mydb.Login("myuser", "mypass")
510+ c.Assert(err, ErrorMatches, "auth fails")
511+}
512+
513+func (s *S) TestAuthLoginTwiceDoesNothing(c *C) {
514+ session, err := mgo.Dial("localhost:40002")
515+ c.Assert(err, IsNil)
516+ defer session.Close()
517+
518+ admindb := session.DB("admin")
519+ err = admindb.Login("root", "rapadura")
520+ c.Assert(err, IsNil)
521+
522+ oldStats := mgo.GetStats()
523+
524+ err = admindb.Login("root", "rapadura")
525+ c.Assert(err, IsNil)
526+
527+ newStats := mgo.GetStats()
528+ c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
529+}
530+
531+func (s *S) TestAuthLoginLogoutLoginDoesNothing(c *C) {
532+ session, err := mgo.Dial("localhost:40002")
533+ c.Assert(err, IsNil)
534+ defer session.Close()
535+
536+ admindb := session.DB("admin")
537+ err = admindb.Login("root", "rapadura")
538+ c.Assert(err, IsNil)
539+
540+ oldStats := mgo.GetStats()
541+
542+ admindb.Logout()
543+ err = admindb.Login("root", "rapadura")
544+ c.Assert(err, IsNil)
545+
546+ newStats := mgo.GetStats()
547+ c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
548+}
549+
550+func (s *S) TestAuthLoginSwitchUser(c *C) {
551+ session, err := mgo.Dial("localhost:40002")
552+ c.Assert(err, IsNil)
553+ defer session.Close()
554+
555+ admindb := session.DB("admin")
556+ err = admindb.Login("root", "rapadura")
557+ c.Assert(err, IsNil)
558+
559+ coll := session.DB("mydb").C("mycoll")
560+ err = coll.Insert(M{"n": 1})
561+ c.Assert(err, IsNil)
562+
563+ err = admindb.Login("reader", "rapadura")
564+ c.Assert(err, IsNil)
565+
566+ // Can't write.
567+ err = coll.Insert(M{"n": 1})
568+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
569+
570+ // But can read.
571+ result := struct{ N int }{}
572+ err = coll.Find(nil).One(&result)
573+ c.Assert(err, IsNil)
574+ c.Assert(result.N, Equals, 1)
575+}
576+
577+func (s *S) TestAuthLoginChangePassword(c *C) {
578+ session, err := mgo.Dial("localhost:40002")
579+ c.Assert(err, IsNil)
580+ defer session.Close()
581+
582+ admindb := session.DB("admin")
583+ err = admindb.Login("root", "rapadura")
584+ c.Assert(err, IsNil)
585+
586+ mydb := session.DB("mydb")
587+ err = mydb.AddUser("myuser", "myoldpass", false)
588+ c.Assert(err, IsNil)
589+
590+ err = mydb.Login("myuser", "myoldpass")
591+ c.Assert(err, IsNil)
592+
593+ err = mydb.AddUser("myuser", "mynewpass", true)
594+ c.Assert(err, IsNil)
595+
596+ err = mydb.Login("myuser", "mynewpass")
597+ c.Assert(err, IsNil)
598+
599+ admindb.Logout()
600+
601+ // The second login must be in effect, which means read-only.
602+ err = mydb.C("mycoll").Insert(M{"n": 1})
603+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
604+}
605+
606+func (s *S) TestAuthLoginCachingWithSessionRefresh(c *C) {
607+ session, err := mgo.Dial("localhost:40002")
608+ c.Assert(err, IsNil)
609+ defer session.Close()
610+
611+ admindb := session.DB("admin")
612+ err = admindb.Login("root", "rapadura")
613+ c.Assert(err, IsNil)
614+
615+ session.Refresh()
616+
617+ coll := session.DB("mydb").C("mycoll")
618+ err = coll.Insert(M{"n": 1})
619+ c.Assert(err, IsNil)
620+}
621+
622+func (s *S) TestAuthLoginCachingWithSessionCopy(c *C) {
623+ session, err := mgo.Dial("localhost:40002")
624+ c.Assert(err, IsNil)
625+ defer session.Close()
626+
627+ admindb := session.DB("admin")
628+ err = admindb.Login("root", "rapadura")
629+ c.Assert(err, IsNil)
630+
631+ session = session.Copy()
632+ defer session.Close()
633+
634+ coll := session.DB("mydb").C("mycoll")
635+ err = coll.Insert(M{"n": 1})
636+ c.Assert(err, IsNil)
637+}
638+
639+func (s *S) TestAuthLoginCachingWithSessionClone(c *C) {
640+ session, err := mgo.Dial("localhost:40002")
641+ c.Assert(err, IsNil)
642+ defer session.Close()
643+
644+ admindb := session.DB("admin")
645+ err = admindb.Login("root", "rapadura")
646+ c.Assert(err, IsNil)
647+
648+ session = session.Clone()
649+ defer session.Close()
650+
651+ coll := session.DB("mydb").C("mycoll")
652+ err = coll.Insert(M{"n": 1})
653+ c.Assert(err, IsNil)
654+}
655+
656+func (s *S) TestAuthLoginCachingWithNewSession(c *C) {
657+ session, err := mgo.Dial("localhost:40002")
658+ c.Assert(err, IsNil)
659+ defer session.Close()
660+
661+ admindb := session.DB("admin")
662+ err = admindb.Login("root", "rapadura")
663+ c.Assert(err, IsNil)
664+
665+ session = session.New()
666+ defer session.Close()
667+
668+ coll := session.DB("mydb").C("mycoll")
669+ err = coll.Insert(M{"n": 1})
670+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized for .*")
671+}
672+
673+func (s *S) TestAuthLoginCachingAcrossPool(c *C) {
674+ // Logins are cached even when the conenction goes back
675+ // into the pool.
676+
677+ session, err := mgo.Dial("localhost:40002")
678+ c.Assert(err, IsNil)
679+ defer session.Close()
680+
681+ admindb := session.DB("admin")
682+ err = admindb.Login("root", "rapadura")
683+ c.Assert(err, IsNil)
684+
685+ // Add another user to test the logout case at the same time.
686+ mydb := session.DB("mydb")
687+ err = mydb.AddUser("myuser", "mypass", false)
688+ c.Assert(err, IsNil)
689+
690+ err = mydb.Login("myuser", "mypass")
691+ c.Assert(err, IsNil)
692+
693+ // Logout root explicitly, to test both cases.
694+ admindb.Logout()
695+
696+ // Give socket back to pool.
697+ session.Refresh()
698+
699+ // Brand new session, should use socket from the pool.
700+ other := session.New()
701+ defer other.Close()
702+
703+ oldStats := mgo.GetStats()
704+
705+ err = other.DB("admin").Login("root", "rapadura")
706+ c.Assert(err, IsNil)
707+ err = other.DB("mydb").Login("myuser", "mypass")
708+ c.Assert(err, IsNil)
709+
710+ // Both logins were cached, so no ops.
711+ newStats := mgo.GetStats()
712+ c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
713+
714+ // And they actually worked.
715+ err = other.DB("mydb").C("mycoll").Insert(M{"n": 1})
716+ c.Assert(err, IsNil)
717+
718+ other.DB("admin").Logout()
719+
720+ err = other.DB("mydb").C("mycoll").Insert(M{"n": 1})
721+ c.Assert(err, IsNil)
722+}
723+
724+func (s *S) TestAuthLoginCachingAcrossPoolWithLogout(c *C) {
725+ // Now verify that logouts are properly flushed if they
726+ // are not revalidated after leaving the pool.
727+
728+ session, err := mgo.Dial("localhost:40002")
729+ c.Assert(err, IsNil)
730+ defer session.Close()
731+
732+ admindb := session.DB("admin")
733+ err = admindb.Login("root", "rapadura")
734+ c.Assert(err, IsNil)
735+
736+ // Add another user to test the logout case at the same time.
737+ mydb := session.DB("mydb")
738+ err = mydb.AddUser("myuser", "mypass", true)
739+ c.Assert(err, IsNil)
740+
741+ err = mydb.Login("myuser", "mypass")
742+ c.Assert(err, IsNil)
743+
744+ // Just some data to query later.
745+ err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
746+ c.Assert(err, IsNil)
747+
748+ // Give socket back to pool.
749+ session.Refresh()
750+
751+ // Brand new session, should use socket from the pool.
752+ other := session.New()
753+ defer other.Close()
754+
755+ oldStats := mgo.GetStats()
756+
757+ err = other.DB("mydb").Login("myuser", "mypass")
758+ c.Assert(err, IsNil)
759+
760+ // Login was cached, so no ops.
761+ newStats := mgo.GetStats()
762+ c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
763+
764+ // Can't write, since root has been implicitly logged out
765+ // when the collection went into the pool, and not revalidated.
766+ err = other.DB("mydb").C("mycoll").Insert(M{"n": 1})
767+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
768+
769+ // But can read due to the revalidated myuser login.
770+ result := struct{ N int }{}
771+ err = other.DB("mydb").C("mycoll").Find(nil).One(&result)
772+ c.Assert(err, IsNil)
773+ c.Assert(result.N, Equals, 1)
774+}
775+
776+func (s *S) TestAuthEventual(c *C) {
777+ // Eventual sessions don't keep sockets around, so they are
778+ // an interesting test case.
779+ session, err := mgo.Dial("localhost:40002")
780+ c.Assert(err, IsNil)
781+ defer session.Close()
782+
783+ admindb := session.DB("admin")
784+ err = admindb.Login("root", "rapadura")
785+ c.Assert(err, IsNil)
786+
787+ err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
788+ c.Assert(err, IsNil)
789+
790+ var wg sync.WaitGroup
791+ wg.Add(20)
792+
793+ for i := 0; i != 10; i++ {
794+ go func() {
795+ defer wg.Done()
796+ var result struct{ N int }
797+ err := session.DB("mydb").C("mycoll").Find(nil).One(&result)
798+ c.Assert(err, IsNil)
799+ c.Assert(result.N, Equals, 1)
800+ }()
801+ }
802+
803+ for i := 0; i != 10; i++ {
804+ go func() {
805+ defer wg.Done()
806+ err := session.DB("mydb").C("mycoll").Insert(M{"n": 1})
807+ c.Assert(err, IsNil)
808+ }()
809+ }
810+
811+ wg.Wait()
812+}
813+
814+func (s *S) TestAuthURL(c *C) {
815+ session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002/")
816+ c.Assert(err, IsNil)
817+ defer session.Close()
818+
819+ err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
820+ c.Assert(err, IsNil)
821+}
822+
823+func (s *S) TestAuthURLWrongCredentials(c *C) {
824+ session, err := mgo.Dial("mongodb://root:wrong@localhost:40002/")
825+ if session != nil {
826+ session.Close()
827+ }
828+ c.Assert(err, ErrorMatches, "auth fails")
829+ c.Assert(session, IsNil)
830+}
831+
832+func (s *S) TestAuthURLWithNewSession(c *C) {
833+ // When authentication is in the URL, the new session will
834+ // actually carry it on as well, even if logged out explicitly.
835+ session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002/")
836+ c.Assert(err, IsNil)
837+ defer session.Close()
838+
839+ session.DB("admin").Logout()
840+
841+ // Do it twice to ensure it passes the needed data on.
842+ session = session.New()
843+ defer session.Close()
844+ session = session.New()
845+ defer session.Close()
846+
847+ err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
848+ c.Assert(err, IsNil)
849+}
850+
851+func (s *S) TestAuthURLWithDatabase(c *C) {
852+ session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002")
853+ c.Assert(err, IsNil)
854+ defer session.Close()
855+
856+ mydb := session.DB("mydb")
857+ err = mydb.AddUser("myruser", "mypass", true)
858+ c.Assert(err, IsNil)
859+
860+ usession, err := mgo.Dial("mongodb://myruser:mypass@localhost:40002/mydb")
861+ c.Assert(err, IsNil)
862+ defer usession.Close()
863+
864+ ucoll := usession.DB("mydb").C("mycoll")
865+ err = ucoll.FindId(0).One(nil)
866+ c.Assert(err, Equals, mgo.ErrNotFound)
867+ err = ucoll.Insert(M{"n": 1})
868+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
869+}
870+
871+func (s *S) TestDefaultDatabase(c *C) {
872+ tests := []struct{ url, db string }{
873+ {"mongodb://root:rapadura@localhost:40002", "test"},
874+ {"mongodb://root:rapadura@localhost:40002/admin", "admin"},
875+ {"mongodb://localhost:40001", "test"},
876+ {"mongodb://localhost:40001/", "test"},
877+ {"mongodb://localhost:40001/mydb", "mydb"},
878+ }
879+
880+ for _, test := range tests {
881+ session, err := mgo.Dial(test.url)
882+ c.Assert(err, IsNil)
883+ defer session.Close()
884+
885+ c.Logf("test: %#v", test)
886+ c.Assert(session.DB("").Name, Equals, test.db)
887+
888+ scopy := session.Copy()
889+ c.Check(scopy.DB("").Name, Equals, test.db)
890+ scopy.Close()
891+ }
892+}
893
894=== added directory 'bson'
895=== renamed directory 'bson' => 'bson.moved'
896=== added file 'bson/LICENSE'
897--- bson/LICENSE 1970-01-01 00:00:00 +0000
898+++ bson/LICENSE 2013-04-03 09:16:27 +0000
899@@ -0,0 +1,25 @@
900+BSON library for Go
901+
902+Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
903+
904+All rights reserved.
905+
906+Redistribution and use in source and binary forms, with or without
907+modification, are permitted provided that the following conditions are met:
908+
909+1. Redistributions of source code must retain the above copyright notice, this
910+ list of conditions and the following disclaimer.
911+2. Redistributions in binary form must reproduce the above copyright notice,
912+ this list of conditions and the following disclaimer in the documentation
913+ and/or other materials provided with the distribution.
914+
915+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
916+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
917+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
918+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
919+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
920+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
921+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
922+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
923+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
924+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
925
926=== added file 'bson/bson.go'
927--- bson/bson.go 1970-01-01 00:00:00 +0000
928+++ bson/bson.go 2013-04-03 09:16:27 +0000
929@@ -0,0 +1,639 @@
930+// BSON library for Go
931+//
932+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
933+//
934+// All rights reserved.
935+//
936+// Redistribution and use in source and binary forms, with or without
937+// modification, are permitted provided that the following conditions are met:
938+//
939+// 1. Redistributions of source code must retain the above copyright notice, this
940+// list of conditions and the following disclaimer.
941+// 2. Redistributions in binary form must reproduce the above copyright notice,
942+// this list of conditions and the following disclaimer in the documentation
943+// and/or other materials provided with the distribution.
944+//
945+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
946+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
947+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
948+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
949+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
950+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
951+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
952+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
953+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
954+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
955+
956+package bson
957+
958+import (
959+ "crypto/md5"
960+ "crypto/rand"
961+ "encoding/binary"
962+ "encoding/hex"
963+ "errors"
964+ "fmt"
965+ "io"
966+ "os"
967+ "reflect"
968+ "runtime"
969+ "strings"
970+ "sync"
971+ "sync/atomic"
972+ "time"
973+)
974+
975+// --------------------------------------------------------------------------
976+// The public API.
977+
978+// A value implementing the bson.Getter interface will have its GetBSON
979+// method called when the given value has to be marshalled, and the result
980+// of this method will be marshaled in place of the actual object.
981+//
982+// If GetBSON returns return a non-nil error, the marshalling procedure
983+// will stop and error out with the provided value.
984+type Getter interface {
985+ GetBSON() (interface{}, error)
986+}
987+
988+// A value implementing the bson.Setter interface will receive the BSON
989+// value via the SetBSON method during unmarshaling, and the object
990+// itself will not be changed as usual.
991+//
992+// If setting the value works, the method should return nil or alternatively
993+// bson.SetZero to set the respective field to its zero value (nil for
994+// pointer types). If SetBSON returns a value of type bson.TypeError, the
995+// BSON value will be omitted from a map or slice being decoded and the
996+// unmarshalling will continue. If it returns any other non-nil error, the
997+// unmarshalling procedure will stop and error out with the provided value.
998+//
999+// This interface is generally useful in pointer receivers, since the method
1000+// will want to change the receiver. A type field that implements the Setter
1001+// interface doesn't have to be a pointer, though.
1002+//
1003+// Unlike the usual behavior, unmarshalling onto a value that implements a
1004+// Setter interface will NOT reset the value to its zero state. This allows
1005+// the value to decide by itself how to be unmarshalled.
1006+//
1007+// For example:
1008+//
1009+// type MyString string
1010+//
1011+// func (s *MyString) SetBSON(raw bson.Raw) error {
1012+// return raw.Unmarshal(s)
1013+// }
1014+//
1015+type Setter interface {
1016+ SetBSON(raw Raw) error
1017+}
1018+
1019+// SetZero may be returned from a SetBSON method to have the value set to
1020+// its respective zero value. When used in pointer values, this will set the
1021+// field to nil rather than to the pre-allocated value.
1022+var SetZero = errors.New("set to zero")
1023+
1024+// M is a convenient alias for a map[string]interface{} map, useful for
1025+// dealing with BSON in a native way. For instance:
1026+//
1027+// bson.M{"a": 1, "b": true}
1028+//
1029+// There's no special handling for this type in addition to what's done anyway
1030+// for an equivalent map type. Elements in the map will be dumped in an
1031+// undefined ordered. See also the bson.D type for an ordered alternative.
1032+type M map[string]interface{}
1033+
1034+// D is a type for dealing with documents containing ordered elements in a
1035+// native fashion. For instance:
1036+//
1037+// bson.D{{"a", 1}, {"b", true}}
1038+//
1039+// In some situations, such as when creating indexes for MongoDB, the order in
1040+// which the elements are defined is important. If the order is not important,
1041+// using a map is generally more comfortable. See the bson.M type and the
1042+// Map() method for D.
1043+type D []DocElem
1044+
1045+// See the bson.D type.
1046+type DocElem struct {
1047+ Name string
1048+ Value interface{}
1049+}
1050+
1051+// The Raw type represents raw unprocessed BSON documents and elements.
1052+// Kind is the kind of element as defined per the BSON specification, and
1053+// Data is the raw unprocessed data for the respective element.
1054+// Using this type it is possible to unmarshal or marshal values partially.
1055+//
1056+// Relevant documentation:
1057+//
1058+// http://bsonspec.org/#/specification
1059+//
1060+type Raw struct {
1061+ Kind byte
1062+ Data []byte
1063+}
1064+
1065+// Map returns a map out of the ordered element name/value pairs in d.
1066+func (d D) Map() (m M) {
1067+ m = make(M, len(d))
1068+ for _, item := range d {
1069+ m[item.Name] = item.Value
1070+ }
1071+ return m
1072+}
1073+
1074+// ObjectId is a unique ID identifying a BSON value. It must be exactly 12 bytes
1075+// long. MongoDB objects by default have such a property set in their "_id"
1076+// property.
1077+//
1078+// http://www.mongodb.org/display/DOCS/Object+IDs
1079+type ObjectId string
1080+
1081+// ObjectIdHex returns an ObjectId from the provided hex representation.
1082+// Calling this function with an invalid hex representation will
1083+// cause a runtime panic. See the IsObjectIdHex function.
1084+func ObjectIdHex(s string) ObjectId {
1085+ d, err := hex.DecodeString(s)
1086+ if err != nil || len(d) != 12 {
1087+ panic(fmt.Sprintf("Invalid input to ObjectIdHex: %q", s))
1088+ }
1089+ return ObjectId(d)
1090+}
1091+
1092+// IsObjectIdHex returns whether s is a valid hex representation of
1093+// an ObjectId. See the ObjectIdHex function.
1094+func IsObjectIdHex(s string) bool {
1095+ if len(s) != 24 {
1096+ return false
1097+ }
1098+ _, err := hex.DecodeString(s)
1099+ return err == nil
1100+}
1101+
1102+// objectIdCounter is atomically incremented when generating a new ObjectId
1103+// using NewObjectId() function. It's used as a counter part of an id.
1104+var objectIdCounter uint32 = 0
1105+
1106+// machineId stores machine id generated once and used in subsequent calls
1107+// to NewObjectId function.
1108+var machineId = readMachineId()
1109+
1110+// initMachineId generates machine id and puts it into the machineId global
1111+// variable. If this function fails to get the hostname, it will cause
1112+// a runtime error.
1113+func readMachineId() []byte {
1114+ var sum [3]byte
1115+ id := sum[:]
1116+ hostname, err1 := os.Hostname()
1117+ if err1 != nil {
1118+ _, err2 := io.ReadFull(rand.Reader, id)
1119+ if err2 != nil {
1120+ panic(fmt.Errorf("cannot get hostname: %v; %v", err1, err2))
1121+ }
1122+ return id
1123+ }
1124+ hw := md5.New()
1125+ hw.Write([]byte(hostname))
1126+ copy(id, hw.Sum(nil))
1127+ return id
1128+}
1129+
1130+// NewObjectId returns a new unique ObjectId.
1131+// This function causes a runtime error if it fails to get the hostname
1132+// of the current machine.
1133+func NewObjectId() ObjectId {
1134+ var b [12]byte
1135+ // Timestamp, 4 bytes, big endian
1136+ binary.BigEndian.PutUint32(b[:], uint32(time.Now().Unix()))
1137+ // Machine, first 3 bytes of md5(hostname)
1138+ b[4] = machineId[0]
1139+ b[5] = machineId[1]
1140+ b[6] = machineId[2]
1141+ // Pid, 2 bytes, specs don't specify endianness, but we use big endian.
1142+ pid := os.Getpid()
1143+ b[7] = byte(pid >> 8)
1144+ b[8] = byte(pid)
1145+ // Increment, 3 bytes, big endian
1146+ i := atomic.AddUint32(&objectIdCounter, 1)
1147+ b[9] = byte(i >> 16)
1148+ b[10] = byte(i >> 8)
1149+ b[11] = byte(i)
1150+ return ObjectId(b[:])
1151+}
1152+
1153+// NewObjectIdWithTime returns a dummy ObjectId with the timestamp part filled
1154+// with the provided number of seconds from epoch UTC, and all other parts
1155+// filled with zeroes. It's not safe to insert a document with an id generated
1156+// by this method, it is useful only for queries to find documents with ids
1157+// generated before or after the specified timestamp.
1158+func NewObjectIdWithTime(t time.Time) ObjectId {
1159+ var b [12]byte
1160+ binary.BigEndian.PutUint32(b[:4], uint32(t.Unix()))
1161+ return ObjectId(string(b[:]))
1162+}
1163+
1164+// String returns a hex string representation of the id.
1165+// Example: ObjectIdHex("4d88e15b60f486e428412dc9").
1166+func (id ObjectId) String() string {
1167+ return fmt.Sprintf(`ObjectIdHex("%x")`, string(id))
1168+}
1169+
1170+// Hex returns a hex representation of the ObjectId.
1171+func (id ObjectId) Hex() string {
1172+ return hex.EncodeToString([]byte(id))
1173+}
1174+
1175+// MarshalJSON turns a bson.ObjectId into a json.Marshaller.
1176+func (id ObjectId) MarshalJSON() ([]byte, error) {
1177+ return []byte(fmt.Sprintf(`"%x"`, string(id))), nil
1178+}
1179+
1180+// UnmarshalJSON turns *bson.ObjectId into a json.Unmarshaller.
1181+func (id *ObjectId) UnmarshalJSON(data []byte) error {
1182+ if len(data) != 26 || data[0] != '"' || data[25] != '"' {
1183+ return errors.New(fmt.Sprintf("Invalid ObjectId in JSON: %s", string(data)))
1184+ }
1185+ var buf [12]byte
1186+ _, err := hex.Decode(buf[:], data[1:25])
1187+ if err != nil {
1188+ return errors.New(fmt.Sprintf("Invalid ObjectId in JSON: %s (%s)", string(data), err))
1189+ }
1190+ *id = ObjectId(string(buf[:]))
1191+ return nil
1192+}
1193+
1194+// Valid returns true if id is valid. A valid id must contain exactly 12 bytes.
1195+func (id ObjectId) Valid() bool {
1196+ return len(id) == 12
1197+}
1198+
1199+// byteSlice returns byte slice of id from start to end.
1200+// Calling this function with an invalid id will cause a runtime panic.
1201+func (id ObjectId) byteSlice(start, end int) []byte {
1202+ if len(id) != 12 {
1203+ panic(fmt.Sprintf("Invalid ObjectId: %q", string(id)))
1204+ }
1205+ return []byte(string(id)[start:end])
1206+}
1207+
1208+// Time returns the timestamp part of the id.
1209+// It's a runtime error to call this method with an invalid id.
1210+func (id ObjectId) Time() time.Time {
1211+ // First 4 bytes of ObjectId is 32-bit big-endian seconds from epoch.
1212+ secs := int64(binary.BigEndian.Uint32(id.byteSlice(0, 4)))
1213+ return time.Unix(secs, 0)
1214+}
1215+
1216+// Machine returns the 3-byte machine id part of the id.
1217+// It's a runtime error to call this method with an invalid id.
1218+func (id ObjectId) Machine() []byte {
1219+ return id.byteSlice(4, 7)
1220+}
1221+
1222+// Pid returns the process id part of the id.
1223+// It's a runtime error to call this method with an invalid id.
1224+func (id ObjectId) Pid() uint16 {
1225+ return binary.BigEndian.Uint16(id.byteSlice(7, 9))
1226+}
1227+
1228+// Counter returns the incrementing value part of the id.
1229+// It's a runtime error to call this method with an invalid id.
1230+func (id ObjectId) Counter() int32 {
1231+ b := id.byteSlice(9, 12)
1232+ // Counter is stored as big-endian 3-byte value
1233+ return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2]))
1234+}
1235+
1236+// The Symbol type is similar to a string and is used in languages with a
1237+// distinct symbol type.
1238+type Symbol string
1239+
1240+// Now returns the current time with millisecond precision. MongoDB stores
1241+// timestamps with the same precision, so a Time returned from this method
1242+// will not change after a roundtrip to the database. That's the only reason
1243+// why this function exists. Using the time.Now function also works fine
1244+// otherwise.
1245+func Now() time.Time {
1246+ return time.Unix(0, time.Now().UnixNano()/1e6*1e6)
1247+}
1248+
1249+// MongoTimestamp is a special internal type used by MongoDB that for some
1250+// strange reason has its own datatype defined in BSON.
1251+type MongoTimestamp int64
1252+
1253+type orderKey int64
1254+
1255+// MaxKey is a special value that compares higher than all other possible BSON
1256+// values in a MongoDB database.
1257+var MaxKey = orderKey(1<<63 - 1)
1258+
1259+// MinKey is a special value that compares lower than all other possible BSON
1260+// values in a MongoDB database.
1261+var MinKey = orderKey(-1 << 63)
1262+
1263+type undefined struct{}
1264+
1265+// Undefined represents the undefined BSON value.
1266+var Undefined undefined
1267+
1268+// Binary is a representation for non-standard binary values. Any kind should
1269+// work, but the following are known as of this writing:
1270+//
1271+// 0x00 - Generic. This is decoded as []byte(data), not Binary{0x00, data}.
1272+// 0x01 - Function (!?)
1273+// 0x02 - Obsolete generic.
1274+// 0x03 - UUID
1275+// 0x05 - MD5
1276+// 0x80 - User defined.
1277+//
1278+type Binary struct {
1279+ Kind byte
1280+ Data []byte
1281+}
1282+
1283+// RegEx represents a regular expression. The Options field may contain
1284+// individual characters defining the way in which the pattern should be
1285+// applied, and must be sorted. Valid options as of this writing are 'i' for
1286+// case insensitive matching, 'm' for multi-line matching, 'x' for verbose
1287+// mode, 'l' to make \w, \W, and similar be locale-dependent, 's' for dot-all
1288+// mode (a '.' matches everything), and 'u' to make \w, \W, and similar match
1289+// unicode. The value of the Options parameter is not verified before being
1290+// marshaled into the BSON format.
1291+type RegEx struct {
1292+ Pattern string
1293+ Options string
1294+}
1295+
1296+// JavaScript is a type that holds JavaScript code. If Scope is non-nil, it
1297+// will be marshaled as a mapping from identifiers to values that may be
1298+// used when evaluating the provided Code.
1299+type JavaScript struct {
1300+ Code string
1301+ Scope interface{}
1302+}
1303+
1304+const initialBufferSize = 64
1305+
1306+func handleErr(err *error) {
1307+ if r := recover(); r != nil {
1308+ if _, ok := r.(runtime.Error); ok {
1309+ panic(r)
1310+ } else if _, ok := r.(externalPanic); ok {
1311+ panic(r)
1312+ } else if s, ok := r.(string); ok {
1313+ *err = errors.New(s)
1314+ } else if e, ok := r.(error); ok {
1315+ *err = e
1316+ } else {
1317+ panic(r)
1318+ }
1319+ }
1320+}
1321+
1322+// Marshal serializes the in value, which may be a map or a struct value.
1323+// In the case of struct values, only exported fields will be serialized.
1324+// The lowercased field name is used as the key for each exported field,
1325+// but this behavior may be changed using the respective field tag.
1326+// The tag may also contain flags to tweak the marshalling behavior for
1327+// the field. The tag formats accepted are:
1328+//
1329+// "[<key>][,<flag1>[,<flag2>]]"
1330+//
1331+// `(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)`
1332+//
1333+// The following flags are currently supported:
1334+//
1335+// omitempty Only include the field if it's not set to the zero
1336+// value for the type or to empty slices or maps.
1337+// Does not apply to zero valued structs.
1338+//
1339+// minsize Marshal an int64 value as an int32, if that's feasible
1340+// while preserving the numeric value.
1341+//
1342+// inline Inline the field, which must be a struct, causing all
1343+// of its fields to be processed as if they were part of
1344+// the outer struct.
1345+//
1346+// Some examples:
1347+//
1348+// type T struct {
1349+// A bool
1350+// B int "myb"
1351+// C string "myc,omitempty"
1352+// D string `bson:",omitempty" json:"jsonkey"`
1353+// E int64 ",minsize"
1354+// F int64 "myf,omitempty,minsize"
1355+// }
1356+//
1357+func Marshal(in interface{}) (out []byte, err error) {
1358+ defer handleErr(&err)
1359+ e := &encoder{make([]byte, 0, initialBufferSize)}
1360+ e.addDoc(reflect.ValueOf(in))
1361+ return e.out, nil
1362+}
1363+
1364+// Unmarshal deserializes data from in into the out value. The out value
1365+// must be a map, a pointer to a struct, or a pointer to a bson.D value.
1366+// The lowercased field name is used as the key for each exported field,
1367+// but this behavior may be changed using the respective field tag.
1368+// Pointer values are initialized when necessary.
1369+//
1370+// The target field or element types of out may not necessarily match
1371+// the BSON values of the provided data. The following conversions are
1372+// made automatically:
1373+//
1374+// - Numeric types are converted if at least the integer part of the
1375+// value would be preserved correctly
1376+// - Bools are converted to numeric types as 1 or 0
1377+// - Numeric types are converted to bools as true if not 0 or false otherwise
1378+// - Binary and string BSON data is converted to a string, array or byte slice
1379+//
1380+// If the value would not fit the type and cannot be converted, it's silently
1381+// skipped.
1382+func Unmarshal(in []byte, out interface{}) (err error) {
1383+ defer handleErr(&err)
1384+ v := reflect.ValueOf(out)
1385+ switch v.Kind() {
1386+ case reflect.Map, reflect.Ptr:
1387+ d := newDecoder(in)
1388+ d.readDocTo(v)
1389+ case reflect.Struct:
1390+ return errors.New("Unmarshal can't deal with struct values. Use a pointer.")
1391+ default:
1392+ return errors.New("Unmarshal needs a map or a pointer to a struct.")
1393+ }
1394+ return nil
1395+}
1396+
1397+// Unmarshal deserializes raw into the out value. If the out value type
1398+// is not compatible with raw, a *bson.TypeError is returned.
1399+//
1400+// See the Unmarshal function documentation for more details on the
1401+// unmarshalling process.
1402+func (raw Raw) Unmarshal(out interface{}) (err error) {
1403+ defer handleErr(&err)
1404+ v := reflect.ValueOf(out)
1405+ switch v.Kind() {
1406+ case reflect.Ptr:
1407+ v = v.Elem()
1408+ fallthrough
1409+ case reflect.Map:
1410+ d := newDecoder(raw.Data)
1411+ good := d.readElemTo(v, raw.Kind)
1412+ if !good {
1413+ return &TypeError{v.Type(), raw.Kind}
1414+ }
1415+ case reflect.Struct:
1416+ return errors.New("Raw Unmarshal can't deal with struct values. Use a pointer.")
1417+ default:
1418+ return errors.New("Raw Unmarshal needs a map or a valid pointer.")
1419+ }
1420+ return nil
1421+}
1422+
1423+type TypeError struct {
1424+ Type reflect.Type
1425+ Kind byte
1426+}
1427+
1428+func (e *TypeError) Error() string {
1429+ return fmt.Sprintf("BSON kind 0x%02x isn't compatible with type %s", e.Kind, e.Type.String())
1430+}
1431+
1432+// --------------------------------------------------------------------------
1433+// Maintain a mapping of keys to structure field indexes
1434+
1435+type structInfo struct {
1436+ FieldsMap map[string]fieldInfo
1437+ FieldsList []fieldInfo
1438+ Zero reflect.Value
1439+}
1440+
1441+type fieldInfo struct {
1442+ Key string
1443+ Num int
1444+ OmitEmpty bool
1445+ MinSize bool
1446+ Inline []int
1447+}
1448+
1449+var structMap = make(map[reflect.Type]*structInfo)
1450+var structMapMutex sync.RWMutex
1451+
1452+type externalPanic string
1453+
1454+func (e externalPanic) String() string {
1455+ return string(e)
1456+}
1457+
1458+func getStructInfo(st reflect.Type) (*structInfo, error) {
1459+ structMapMutex.RLock()
1460+ sinfo, found := structMap[st]
1461+ structMapMutex.RUnlock()
1462+ if found {
1463+ return sinfo, nil
1464+ }
1465+ n := st.NumField()
1466+ fieldsMap := make(map[string]fieldInfo)
1467+ fieldsList := make([]fieldInfo, 0, n)
1468+ for i := 0; i != n; i++ {
1469+ field := st.Field(i)
1470+ if field.PkgPath != "" {
1471+ continue // Private field
1472+ }
1473+
1474+ info := fieldInfo{Num: i}
1475+
1476+ tag := field.Tag.Get("bson")
1477+ if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
1478+ tag = string(field.Tag)
1479+ }
1480+ if tag == "-" {
1481+ continue
1482+ }
1483+
1484+ // XXX Drop this after a few releases.
1485+ if s := strings.Index(tag, "/"); s >= 0 {
1486+ recommend := tag[:s]
1487+ for _, c := range tag[s+1:] {
1488+ switch c {
1489+ case 'c':
1490+ recommend += ",omitempty"
1491+ case 's':
1492+ recommend += ",minsize"
1493+ default:
1494+ msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", string([]byte{uint8(c)}), tag, st)
1495+ panic(externalPanic(msg))
1496+ }
1497+ }
1498+ msg := fmt.Sprintf("Replace tag %q in field %s of type %s by %q", tag, field.Name, st, recommend)
1499+ panic(externalPanic(msg))
1500+ }
1501+
1502+ inline := false
1503+ fields := strings.Split(tag, ",")
1504+ if len(fields) > 1 {
1505+ for _, flag := range fields[1:] {
1506+ switch flag {
1507+ case "omitempty":
1508+ info.OmitEmpty = true
1509+ case "minsize":
1510+ info.MinSize = true
1511+ case "inline":
1512+ inline = true
1513+ default:
1514+ msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
1515+ panic(externalPanic(msg))
1516+ }
1517+ }
1518+ tag = fields[0]
1519+ }
1520+
1521+ if inline {
1522+ if field.Type.Kind() != reflect.Struct {
1523+ panic("Option ,inline needs a struct value field")
1524+ }
1525+ sinfo, err := getStructInfo(field.Type)
1526+ if err != nil {
1527+ return nil, err
1528+ }
1529+ for _, finfo := range sinfo.FieldsList {
1530+ if _, found := fieldsMap[finfo.Key]; found {
1531+ msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
1532+ return nil, errors.New(msg)
1533+ }
1534+ if finfo.Inline == nil {
1535+ finfo.Inline = []int{i, finfo.Num}
1536+ } else {
1537+ finfo.Inline = append([]int{i}, finfo.Inline...)
1538+ }
1539+ fieldsMap[finfo.Key] = finfo
1540+ fieldsList = append(fieldsList, finfo)
1541+ }
1542+ continue
1543+ }
1544+
1545+ if tag != "" {
1546+ info.Key = tag
1547+ } else {
1548+ info.Key = strings.ToLower(field.Name)
1549+ }
1550+
1551+ if _, found = fieldsMap[info.Key]; found {
1552+ msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
1553+ return nil, errors.New(msg)
1554+ }
1555+
1556+ fieldsList = append(fieldsList, info)
1557+ fieldsMap[info.Key] = info
1558+ }
1559+ sinfo = &structInfo{
1560+ fieldsMap,
1561+ fieldsList[:len(fieldsMap)],
1562+ reflect.New(st).Elem(),
1563+ }
1564+ structMapMutex.Lock()
1565+ structMap[st] = sinfo
1566+ structMapMutex.Unlock()
1567+ return sinfo, nil
1568+}
1569
1570=== added file 'bson/bson_test.go'
1571--- bson/bson_test.go 1970-01-01 00:00:00 +0000
1572+++ bson/bson_test.go 2013-04-03 09:16:27 +0000
1573@@ -0,0 +1,1369 @@
1574+// BSON library for Go
1575+//
1576+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
1577+//
1578+// All rights reserved.
1579+//
1580+// Redistribution and use in source and binary forms, with or without
1581+// modification, are permitted provided that the following conditions are met:
1582+//
1583+// 1. Redistributions of source code must retain the above copyright notice, this
1584+// list of conditions and the following disclaimer.
1585+// 2. Redistributions in binary form must reproduce the above copyright notice,
1586+// this list of conditions and the following disclaimer in the documentation
1587+// and/or other materials provided with the distribution.
1588+//
1589+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
1590+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1591+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1592+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
1593+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1594+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
1595+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1596+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1597+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
1598+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1599+// gobson - BSON library for Go.
1600+
1601+package bson_test
1602+
1603+import (
1604+ "encoding/binary"
1605+ "encoding/json"
1606+ "errors"
1607+ . "launchpad.net/gocheck"
1608+ "labix.org/v2/mgo/bson"
1609+ "net/url"
1610+ "reflect"
1611+ "testing"
1612+ "time"
1613+)
1614+
1615+func TestAll(t *testing.T) {
1616+ TestingT(t)
1617+}
1618+
1619+type S struct{}
1620+
1621+var _ = Suite(&S{})
1622+
1623+// Wrap up the document elements contained in data, prepending the int32
1624+// length of the data, and appending the '\x00' value closing the document.
1625+func wrapInDoc(data string) string {
1626+ result := make([]byte, len(data)+5)
1627+ binary.LittleEndian.PutUint32(result, uint32(len(result)))
1628+ copy(result[4:], []byte(data))
1629+ return string(result)
1630+}
1631+
1632+func makeZeroDoc(value interface{}) (zero interface{}) {
1633+ v := reflect.ValueOf(value)
1634+ t := v.Type()
1635+ switch t.Kind() {
1636+ case reflect.Map:
1637+ mv := reflect.MakeMap(t)
1638+ zero = mv.Interface()
1639+ case reflect.Ptr:
1640+ pv := reflect.New(v.Type().Elem())
1641+ zero = pv.Interface()
1642+ case reflect.Slice:
1643+ zero = reflect.New(t).Interface()
1644+ default:
1645+ panic("unsupported doc type")
1646+ }
1647+ return zero
1648+}
1649+
1650+func testUnmarshal(c *C, data string, obj interface{}) {
1651+ zero := makeZeroDoc(obj)
1652+ err := bson.Unmarshal([]byte(data), zero)
1653+ c.Assert(err, IsNil)
1654+ c.Assert(zero, DeepEquals, obj)
1655+}
1656+
1657+type testItemType struct {
1658+ obj interface{}
1659+ data string
1660+}
1661+
1662+// --------------------------------------------------------------------------
1663+// Samples from bsonspec.org:
1664+
1665+var sampleItems = []testItemType{
1666+ {bson.M{"hello": "world"},
1667+ "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00"},
1668+ {bson.M{"BSON": []interface{}{"awesome", float64(5.05), 1986}},
1669+ "1\x00\x00\x00\x04BSON\x00&\x00\x00\x00\x020\x00\x08\x00\x00\x00" +
1670+ "awesome\x00\x011\x00333333\x14@\x102\x00\xc2\x07\x00\x00\x00\x00"},
1671+}
1672+
1673+func (s *S) TestMarshalSampleItems(c *C) {
1674+ for i, item := range sampleItems {
1675+ data, err := bson.Marshal(item.obj)
1676+ c.Assert(err, IsNil)
1677+ c.Assert(string(data), Equals, item.data, Commentf("Failed on item %d", i))
1678+ }
1679+}
1680+
1681+func (s *S) TestUnmarshalSampleItems(c *C) {
1682+ for i, item := range sampleItems {
1683+ value := bson.M{}
1684+ err := bson.Unmarshal([]byte(item.data), value)
1685+ c.Assert(err, IsNil)
1686+ c.Assert(value, DeepEquals, item.obj, Commentf("Failed on item %d", i))
1687+ }
1688+}
1689+
1690+// --------------------------------------------------------------------------
1691+// Every type, ordered by the type flag. These are not wrapped with the
1692+// length and last \x00 from the document. wrapInDoc() computes them.
1693+// Note that all of them should be supported as two-way conversions.
1694+
1695+var allItems = []testItemType{
1696+ {bson.M{},
1697+ ""},
1698+ {bson.M{"_": float64(5.05)},
1699+ "\x01_\x00333333\x14@"},
1700+ {bson.M{"_": "yo"},
1701+ "\x02_\x00\x03\x00\x00\x00yo\x00"},
1702+ {bson.M{"_": bson.M{"a": true}},
1703+ "\x03_\x00\x09\x00\x00\x00\x08a\x00\x01\x00"},
1704+ {bson.M{"_": []interface{}{true, false}},
1705+ "\x04_\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"},
1706+ {bson.M{"_": []byte("yo")},
1707+ "\x05_\x00\x02\x00\x00\x00\x00yo"},
1708+ {bson.M{"_": bson.Binary{0x80, []byte("udef")}},
1709+ "\x05_\x00\x04\x00\x00\x00\x80udef"},
1710+ {bson.M{"_": bson.Undefined}, // Obsolete, but still seen in the wild.
1711+ "\x06_\x00"},
1712+ {bson.M{"_": bson.ObjectId("0123456789ab")},
1713+ "\x07_\x000123456789ab"},
1714+ {bson.M{"_": false},
1715+ "\x08_\x00\x00"},
1716+ {bson.M{"_": true},
1717+ "\x08_\x00\x01"},
1718+ {bson.M{"_": time.Unix(0, 258e6)}, // Note the NS <=> MS conversion.
1719+ "\x09_\x00\x02\x01\x00\x00\x00\x00\x00\x00"},
1720+ {bson.M{"_": nil},
1721+ "\x0A_\x00"},
1722+ {bson.M{"_": bson.RegEx{"ab", "cd"}},
1723+ "\x0B_\x00ab\x00cd\x00"},
1724+ {bson.M{"_": bson.JavaScript{"code", nil}},
1725+ "\x0D_\x00\x05\x00\x00\x00code\x00"},
1726+ {bson.M{"_": bson.Symbol("sym")},
1727+ "\x0E_\x00\x04\x00\x00\x00sym\x00"},
1728+ {bson.M{"_": bson.JavaScript{"code", bson.M{"": nil}}},
1729+ "\x0F_\x00\x14\x00\x00\x00\x05\x00\x00\x00code\x00" +
1730+ "\x07\x00\x00\x00\x0A\x00\x00"},
1731+ {bson.M{"_": 258},
1732+ "\x10_\x00\x02\x01\x00\x00"},
1733+ {bson.M{"_": bson.MongoTimestamp(258)},
1734+ "\x11_\x00\x02\x01\x00\x00\x00\x00\x00\x00"},
1735+ {bson.M{"_": int64(258)},
1736+ "\x12_\x00\x02\x01\x00\x00\x00\x00\x00\x00"},
1737+ {bson.M{"_": int64(258 << 32)},
1738+ "\x12_\x00\x00\x00\x00\x00\x02\x01\x00\x00"},
1739+ {bson.M{"_": bson.MaxKey},
1740+ "\x7F_\x00"},
1741+ {bson.M{"_": bson.MinKey},
1742+ "\xFF_\x00"},
1743+}
1744+
1745+func (s *S) TestMarshalAllItems(c *C) {
1746+ for i, item := range allItems {
1747+ data, err := bson.Marshal(item.obj)
1748+ c.Assert(err, IsNil)
1749+ c.Assert(string(data), Equals, wrapInDoc(item.data), Commentf("Failed on item %d: %#v", i, item))
1750+ }
1751+}
1752+
1753+func (s *S) TestUnmarshalAllItems(c *C) {
1754+ for i, item := range allItems {
1755+ value := bson.M{}
1756+ err := bson.Unmarshal([]byte(wrapInDoc(item.data)), value)
1757+ c.Assert(err, IsNil)
1758+ c.Assert(value, DeepEquals, item.obj, Commentf("Failed on item %d: %#v", i, item))
1759+ }
1760+}
1761+
1762+func (s *S) TestUnmarshalRawAllItems(c *C) {
1763+ for i, item := range allItems {
1764+ if len(item.data) == 0 {
1765+ continue
1766+ }
1767+ value := item.obj.(bson.M)["_"]
1768+ if value == nil {
1769+ continue
1770+ }
1771+ pv := reflect.New(reflect.ValueOf(value).Type())
1772+ raw := bson.Raw{item.data[0], []byte(item.data[3:])}
1773+ c.Logf("Unmarshal raw: %#v, %#v", raw, pv.Interface())
1774+ err := raw.Unmarshal(pv.Interface())
1775+ c.Assert(err, IsNil)
1776+ c.Assert(pv.Elem().Interface(), DeepEquals, value, Commentf("Failed on item %d: %#v", i, item))
1777+ }
1778+}
1779+
1780+func (s *S) TestUnmarshalRawIncompatible(c *C) {
1781+ raw := bson.Raw{0x08, []byte{0x01}} // true
1782+ err := raw.Unmarshal(&struct{}{})
1783+ c.Assert(err, ErrorMatches, "BSON kind 0x08 isn't compatible with type struct \\{\\}")
1784+}
1785+
1786+func (s *S) TestUnmarshalZeroesStruct(c *C) {
1787+ data, err := bson.Marshal(bson.M{"b": 2})
1788+ c.Assert(err, IsNil)
1789+ type T struct{ A, B int }
1790+ v := T{A: 1}
1791+ err = bson.Unmarshal(data, &v)
1792+ c.Assert(err, IsNil)
1793+ c.Assert(v.A, Equals, 0)
1794+ c.Assert(v.B, Equals, 2)
1795+}
1796+
1797+func (s *S) TestUnmarshalZeroesMap(c *C) {
1798+ data, err := bson.Marshal(bson.M{"b": 2})
1799+ c.Assert(err, IsNil)
1800+ m := bson.M{"a": 1}
1801+ err = bson.Unmarshal(data, &m)
1802+ c.Assert(err, IsNil)
1803+ c.Assert(m, DeepEquals, bson.M{"b": 2})
1804+}
1805+
1806+func (s *S) TestUnmarshalNonNilInterface(c *C) {
1807+ data, err := bson.Marshal(bson.M{"b": 2})
1808+ c.Assert(err, IsNil)
1809+ m := bson.M{"a": 1}
1810+ var i interface{}
1811+ i = m
1812+ err = bson.Unmarshal(data, &i)
1813+ c.Assert(err, IsNil)
1814+ c.Assert(i, DeepEquals, bson.M{"b": 2})
1815+ c.Assert(m, DeepEquals, bson.M{"a": 1})
1816+}
1817+
1818+// --------------------------------------------------------------------------
1819+// Some one way marshaling operations which would unmarshal differently.
1820+
1821+var oneWayMarshalItems = []testItemType{
1822+ // These are being passed as pointers, and will unmarshal as values.
1823+ {bson.M{"": &bson.Binary{0x02, []byte("old")}},
1824+ "\x05\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"},
1825+ {bson.M{"": &bson.Binary{0x80, []byte("udef")}},
1826+ "\x05\x00\x04\x00\x00\x00\x80udef"},
1827+ {bson.M{"": &bson.RegEx{"ab", "cd"}},
1828+ "\x0B\x00ab\x00cd\x00"},
1829+ {bson.M{"": &bson.JavaScript{"code", nil}},
1830+ "\x0D\x00\x05\x00\x00\x00code\x00"},
1831+ {bson.M{"": &bson.JavaScript{"code", bson.M{"": nil}}},
1832+ "\x0F\x00\x14\x00\x00\x00\x05\x00\x00\x00code\x00" +
1833+ "\x07\x00\x00\x00\x0A\x00\x00"},
1834+
1835+ // There's no float32 type in BSON. Will encode as a float64.
1836+ {bson.M{"": float32(5.05)},
1837+ "\x01\x00\x00\x00\x00@33\x14@"},
1838+
1839+ // The array will be unmarshaled as a slice instead.
1840+ {bson.M{"": [2]bool{true, false}},
1841+ "\x04\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"},
1842+
1843+ // The typed slice will be unmarshaled as []interface{}.
1844+ {bson.M{"": []bool{true, false}},
1845+ "\x04\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"},
1846+
1847+ // Will unmarshal as a []byte.
1848+ {bson.M{"": bson.Binary{0x00, []byte("yo")}},
1849+ "\x05\x00\x02\x00\x00\x00\x00yo"},
1850+ {bson.M{"": bson.Binary{0x02, []byte("old")}},
1851+ "\x05\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"},
1852+
1853+ // No way to preserve the type information here. We might encode as a zero
1854+ // value, but this would mean that pointer values in structs wouldn't be
1855+ // able to correctly distinguish between unset and set to the zero value.
1856+ {bson.M{"": (*byte)(nil)},
1857+ "\x0A\x00"},
1858+
1859+ // No int types smaller than int32 in BSON. Could encode this as a char,
1860+ // but it would still be ambiguous, take more, and be awkward in Go when
1861+ // loaded without typing information.
1862+ {bson.M{"": byte(8)},
1863+ "\x10\x00\x08\x00\x00\x00"},
1864+
1865+ // There are no unsigned types in BSON. Will unmarshal as int32 or int64.
1866+ {bson.M{"": uint32(258)},
1867+ "\x10\x00\x02\x01\x00\x00"},
1868+ {bson.M{"": uint64(258)},
1869+ "\x12\x00\x02\x01\x00\x00\x00\x00\x00\x00"},
1870+ {bson.M{"": uint64(258 << 32)},
1871+ "\x12\x00\x00\x00\x00\x00\x02\x01\x00\x00"},
1872+
1873+ // This will unmarshal as int.
1874+ {bson.M{"": int32(258)},
1875+ "\x10\x00\x02\x01\x00\x00"},
1876+
1877+ // That's a special case. The unsigned value is too large for an int32,
1878+ // so an int64 is used instead.
1879+ {bson.M{"": uint32(1<<32 - 1)},
1880+ "\x12\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"},
1881+ {bson.M{"": uint(1<<32 - 1)},
1882+ "\x12\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"},
1883+}
1884+
1885+func (s *S) TestOneWayMarshalItems(c *C) {
1886+ for i, item := range oneWayMarshalItems {
1887+ data, err := bson.Marshal(item.obj)
1888+ c.Assert(err, IsNil)
1889+ c.Assert(string(data), Equals, wrapInDoc(item.data),
1890+ Commentf("Failed on item %d", i))
1891+ }
1892+}
1893+
1894+// --------------------------------------------------------------------------
1895+// Two-way tests for user-defined structures using the samples
1896+// from bsonspec.org.
1897+
1898+type specSample1 struct {
1899+ Hello string
1900+}
1901+
1902+type specSample2 struct {
1903+ BSON []interface{} "BSON"
1904+}
1905+
1906+var structSampleItems = []testItemType{
1907+ {&specSample1{"world"},
1908+ "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00"},
1909+ {&specSample2{[]interface{}{"awesome", float64(5.05), 1986}},
1910+ "1\x00\x00\x00\x04BSON\x00&\x00\x00\x00\x020\x00\x08\x00\x00\x00" +
1911+ "awesome\x00\x011\x00333333\x14@\x102\x00\xc2\x07\x00\x00\x00\x00"},
1912+}
1913+
1914+func (s *S) TestMarshalStructSampleItems(c *C) {
1915+ for i, item := range structSampleItems {
1916+ data, err := bson.Marshal(item.obj)
1917+ c.Assert(err, IsNil)
1918+ c.Assert(string(data), Equals, item.data,
1919+ Commentf("Failed on item %d", i))
1920+ }
1921+}
1922+
1923+func (s *S) TestUnmarshalStructSampleItems(c *C) {
1924+ for _, item := range structSampleItems {
1925+ testUnmarshal(c, item.data, item.obj)
1926+ }
1927+}
1928+
1929+// --------------------------------------------------------------------------
1930+// Generic two-way struct marshaling tests.
1931+
1932+var bytevar = byte(8)
1933+var byteptr = &bytevar
1934+
1935+var structItems = []testItemType{
1936+ {&struct{ Ptr *byte }{nil},
1937+ "\x0Aptr\x00"},
1938+ {&struct{ Ptr *byte }{&bytevar},
1939+ "\x10ptr\x00\x08\x00\x00\x00"},
1940+ {&struct{ Ptr **byte }{&byteptr},
1941+ "\x10ptr\x00\x08\x00\x00\x00"},
1942+ {&struct{ Byte byte }{8},
1943+ "\x10byte\x00\x08\x00\x00\x00"},
1944+ {&struct{ Byte byte }{0},
1945+ "\x10byte\x00\x00\x00\x00\x00"},
1946+ {&struct {
1947+ V byte "Tag"
1948+ }{8},
1949+ "\x10Tag\x00\x08\x00\x00\x00"},
1950+ {&struct {
1951+ V *struct {
1952+ Byte byte
1953+ }
1954+ }{&struct{ Byte byte }{8}},
1955+ "\x03v\x00" + "\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00"},
1956+ {&struct{ priv byte }{}, ""},
1957+
1958+ // The order of the dumped fields should be the same in the struct.
1959+ {&struct{ A, C, B, D, F, E *byte }{},
1960+ "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x0Ae\x00"},
1961+
1962+ {&struct{ V bson.Raw }{bson.Raw{0x03, []byte("\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00")}},
1963+ "\x03v\x00" + "\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00"},
1964+ {&struct{ V bson.Raw }{bson.Raw{0x10, []byte("\x00\x00\x00\x00")}},
1965+ "\x10v\x00" + "\x00\x00\x00\x00"},
1966+
1967+ // Byte arrays.
1968+ {&struct{ V [2]byte }{[2]byte{'y', 'o'}},
1969+ "\x05v\x00\x02\x00\x00\x00\x00yo"},
1970+}
1971+
1972+func (s *S) TestMarshalStructItems(c *C) {
1973+ for i, item := range structItems {
1974+ data, err := bson.Marshal(item.obj)
1975+ c.Assert(err, IsNil)
1976+ c.Assert(string(data), Equals, wrapInDoc(item.data),
1977+ Commentf("Failed on item %d", i))
1978+ }
1979+}
1980+
1981+func (s *S) TestUnmarshalStructItems(c *C) {
1982+ for _, item := range structItems {
1983+ testUnmarshal(c, wrapInDoc(item.data), item.obj)
1984+ }
1985+}
1986+
1987+func (s *S) TestUnmarshalRawStructItems(c *C) {
1988+ for i, item := range structItems {
1989+ raw := bson.Raw{0x03, []byte(wrapInDoc(item.data))}
1990+ zero := makeZeroDoc(item.obj)
1991+ err := raw.Unmarshal(zero)
1992+ c.Assert(err, IsNil)
1993+ c.Assert(zero, DeepEquals, item.obj, Commentf("Failed on item %d: %#v", i, item))
1994+ }
1995+}
1996+
1997+func (s *S) TestUnmarshalRawNil(c *C) {
1998+ // Regression test: shouldn't try to nil out the pointer itself,
1999+ // as it's not settable.
2000+ raw := bson.Raw{0x0A, []byte{}}
2001+ err := raw.Unmarshal(&struct{}{})
2002+ c.Assert(err, IsNil)
2003+}
2004+
2005+// --------------------------------------------------------------------------
2006+// One-way marshaling tests.
2007+
2008+type dOnIface struct {
2009+ D interface{}
2010+}
2011+
2012+type ignoreField struct {
2013+ Before string
2014+ Ignore string `bson:"-"`
2015+ After string
2016+}
2017+
2018+var marshalItems = []testItemType{
2019+ // Ordered document dump. Will unmarshal as a dictionary by default.
2020+ {bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", nil}, {"f", nil}, {"e", true}},
2021+ "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x08e\x00\x01"},
2022+ {MyD{{"a", nil}, {"c", nil}, {"b", nil}, {"d", nil}, {"f", nil}, {"e", true}},
2023+ "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x08e\x00\x01"},
2024+ {&dOnIface{bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", true}}},
2025+ "\x03d\x00" + wrapInDoc("\x0Aa\x00\x0Ac\x00\x0Ab\x00\x08d\x00\x01")},
2026+ {&ignoreField{"before", "ignore", "after"},
2027+ "\x02before\x00\a\x00\x00\x00before\x00\x02after\x00\x06\x00\x00\x00after\x00"},
2028+
2029+ // Marshalling a Raw document does nothing.
2030+ {bson.Raw{0x03, []byte(wrapInDoc("anything"))},
2031+ "anything"},
2032+ {bson.Raw{Data: []byte(wrapInDoc("anything"))},
2033+ "anything"},
2034+}
2035+
2036+func (s *S) TestMarshalOneWayItems(c *C) {
2037+ for _, item := range marshalItems {
2038+ data, err := bson.Marshal(item.obj)
2039+ c.Assert(err, IsNil)
2040+ c.Assert(string(data), Equals, wrapInDoc(item.data))
2041+ }
2042+}
2043+
2044+// --------------------------------------------------------------------------
2045+// One-way unmarshaling tests.
2046+
2047+var unmarshalItems = []testItemType{
2048+ // Field is private. Should not attempt to unmarshal it.
2049+ {&struct{ priv byte }{},
2050+ "\x10priv\x00\x08\x00\x00\x00"},
2051+
2052+ // Wrong casing. Field names are lowercased.
2053+ {&struct{ Byte byte }{},
2054+ "\x10Byte\x00\x08\x00\x00\x00"},
2055+
2056+ // Ignore non-existing field.
2057+ {&struct{ Byte byte }{9},
2058+ "\x10boot\x00\x08\x00\x00\x00" + "\x10byte\x00\x09\x00\x00\x00"},
2059+
2060+ // Do not unmarshal on ignored field.
2061+ {&ignoreField{"before", "", "after"},
2062+ "\x02before\x00\a\x00\x00\x00before\x00" +
2063+ "\x02-\x00\a\x00\x00\x00ignore\x00" +
2064+ "\x02after\x00\x06\x00\x00\x00after\x00"},
2065+
2066+ // Ignore unsuitable types silently.
2067+ {map[string]string{"str": "s"},
2068+ "\x02str\x00\x02\x00\x00\x00s\x00" + "\x10int\x00\x01\x00\x00\x00"},
2069+ {map[string][]int{"array": []int{5, 9}},
2070+ "\x04array\x00" + wrapInDoc("\x100\x00\x05\x00\x00\x00"+"\x021\x00\x02\x00\x00\x00s\x00"+"\x102\x00\x09\x00\x00\x00")},
2071+
2072+ // Wrong type. Shouldn't init pointer.
2073+ {&struct{ Str *byte }{},
2074+ "\x02str\x00\x02\x00\x00\x00s\x00"},
2075+ {&struct{ Str *struct{ Str string } }{},
2076+ "\x02str\x00\x02\x00\x00\x00s\x00"},
2077+
2078+ // Ordered document.
2079+ {&struct{ bson.D }{bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", true}}},
2080+ "\x03d\x00" + wrapInDoc("\x0Aa\x00\x0Ac\x00\x0Ab\x00\x08d\x00\x01")},
2081+
2082+ // Raw document.
2083+ {&bson.Raw{0x03, []byte(wrapInDoc("\x10byte\x00\x08\x00\x00\x00"))},
2084+ "\x10byte\x00\x08\x00\x00\x00"},
2085+
2086+ // Decode old binary.
2087+ {bson.M{"_": []byte("old")},
2088+ "\x05_\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"},
2089+}
2090+
2091+func (s *S) TestUnmarshalOneWayItems(c *C) {
2092+ for _, item := range unmarshalItems {
2093+ testUnmarshal(c, wrapInDoc(item.data), item.obj)
2094+ }
2095+}
2096+
2097+func (s *S) TestUnmarshalNilInStruct(c *C) {
2098+ // Nil is the default value, so we need to ensure it's indeed being set.
2099+ b := byte(1)
2100+ v := &struct{ Ptr *byte }{&b}
2101+ err := bson.Unmarshal([]byte(wrapInDoc("\x0Aptr\x00")), v)
2102+ c.Assert(err, IsNil)
2103+ c.Assert(v, DeepEquals, &struct{ Ptr *byte }{nil})
2104+}
2105+
2106+// --------------------------------------------------------------------------
2107+// Marshalling error cases.
2108+
2109+type structWithDupKeys struct {
2110+ Name byte
2111+ Other byte "name" // Tag should precede.
2112+}
2113+
2114+var marshalErrorItems = []testItemType{
2115+ {bson.M{"": uint64(1 << 63)},
2116+ "BSON has no uint64 type, and value is too large to fit correctly in an int64"},
2117+ {bson.M{"": bson.ObjectId("tooshort")},
2118+ "ObjectIDs must be exactly 12 bytes long \\(got 8\\)"},
2119+ {int64(123),
2120+ "Can't marshal int64 as a BSON document"},
2121+ {bson.M{"": 1i},
2122+ "Can't marshal complex128 in a BSON document"},
2123+ {&structWithDupKeys{},
2124+ "Duplicated key 'name' in struct bson_test.structWithDupKeys"},
2125+ {bson.Raw{0x0A, []byte{}},
2126+ "Attempted to unmarshal Raw kind 10 as a document"},
2127+ {&inlineCantPtr{&struct{ A, B int }{1, 2}},
2128+ "Option ,inline needs a struct value field"},
2129+ {&inlineDupName{1, struct{ A, B int }{2, 3}},
2130+ "Duplicated key 'a' in struct bson_test.inlineDupName"},
2131+}
2132+
2133+func (s *S) TestMarshalErrorItems(c *C) {
2134+ for _, item := range marshalErrorItems {
2135+ data, err := bson.Marshal(item.obj)
2136+ c.Assert(err, ErrorMatches, item.data)
2137+ c.Assert(data, IsNil)
2138+ }
2139+}
2140+
2141+// --------------------------------------------------------------------------
2142+// Unmarshalling error cases.
2143+
2144+type unmarshalErrorType struct {
2145+ obj interface{}
2146+ data string
2147+ error string
2148+}
2149+
2150+var unmarshalErrorItems = []unmarshalErrorType{
2151+ // Tag name conflicts with existing parameter.
2152+ {&structWithDupKeys{},
2153+ "\x10name\x00\x08\x00\x00\x00",
2154+ "Duplicated key 'name' in struct bson_test.structWithDupKeys"},
2155+
2156+ // Non-string map key.
2157+ {map[int]interface{}{},
2158+ "\x10name\x00\x08\x00\x00\x00",
2159+ "BSON map must have string keys. Got: map\\[int\\]interface \\{\\}"},
2160+
2161+ {nil,
2162+ "\xEEname\x00",
2163+ "Unknown element kind \\(0xEE\\)"},
2164+
2165+ {struct{ Name bool }{},
2166+ "\x10name\x00\x08\x00\x00\x00",
2167+ "Unmarshal can't deal with struct values. Use a pointer."},
2168+
2169+ {123,
2170+ "\x10name\x00\x08\x00\x00\x00",
2171+ "Unmarshal needs a map or a pointer to a struct."},
2172+}
2173+
2174+func (s *S) TestUnmarshalErrorItems(c *C) {
2175+ for _, item := range unmarshalErrorItems {
2176+ data := []byte(wrapInDoc(item.data))
2177+ var value interface{}
2178+ switch reflect.ValueOf(item.obj).Kind() {
2179+ case reflect.Map, reflect.Ptr:
2180+ value = makeZeroDoc(item.obj)
2181+ case reflect.Invalid:
2182+ value = bson.M{}
2183+ default:
2184+ value = item.obj
2185+ }
2186+ err := bson.Unmarshal(data, value)
2187+ c.Assert(err, ErrorMatches, item.error)
2188+ }
2189+}
2190+
2191+type unmarshalRawErrorType struct {
2192+ obj interface{}
2193+ raw bson.Raw
2194+ error string
2195+}
2196+
2197+var unmarshalRawErrorItems = []unmarshalRawErrorType{
2198+ // Tag name conflicts with existing parameter.
2199+ {&structWithDupKeys{},
2200+ bson.Raw{0x03, []byte("\x10byte\x00\x08\x00\x00\x00")},
2201+ "Duplicated key 'name' in struct bson_test.structWithDupKeys"},
2202+
2203+ {&struct{}{},
2204+ bson.Raw{0xEE, []byte{}},
2205+ "Unknown element kind \\(0xEE\\)"},
2206+
2207+ {struct{ Name bool }{},
2208+ bson.Raw{0x10, []byte("\x08\x00\x00\x00")},
2209+ "Raw Unmarshal can't deal with struct values. Use a pointer."},
2210+
2211+ {123,
2212+ bson.Raw{0x10, []byte("\x08\x00\x00\x00")},
2213+ "Raw Unmarshal needs a map or a valid pointer."},
2214+}
2215+
2216+func (s *S) TestUnmarshalRawErrorItems(c *C) {
2217+ for i, item := range unmarshalRawErrorItems {
2218+ err := item.raw.Unmarshal(item.obj)
2219+ c.Assert(err, ErrorMatches, item.error, Commentf("Failed on item %d: %#v\n", i, item))
2220+ }
2221+}
2222+
2223+var corruptedData = []string{
2224+ "\x04\x00\x00\x00\x00", // Shorter than minimum
2225+ "\x06\x00\x00\x00\x00", // Not enough data
2226+ "\x05\x00\x00", // Broken length
2227+ "\x05\x00\x00\x00\xff", // Corrupted termination
2228+ "\x0A\x00\x00\x00\x0Aooop\x00", // Unfinished C string
2229+
2230+ // Array end past end of string (s[2]=0x07 is correct)
2231+ wrapInDoc("\x04\x00\x09\x00\x00\x00\x0A\x00\x00"),
2232+
2233+ // Array end within string, but past acceptable.
2234+ wrapInDoc("\x04\x00\x08\x00\x00\x00\x0A\x00\x00"),
2235+
2236+ // Document end within string, but past acceptable.
2237+ wrapInDoc("\x03\x00\x08\x00\x00\x00\x0A\x00\x00"),
2238+
2239+ // String with corrupted end.
2240+ wrapInDoc("\x02\x00\x03\x00\x00\x00yo\xFF"),
2241+}
2242+
2243+func (s *S) TestUnmarshalMapDocumentTooShort(c *C) {
2244+ for _, data := range corruptedData {
2245+ err := bson.Unmarshal([]byte(data), bson.M{})
2246+ c.Assert(err, ErrorMatches, "Document is corrupted")
2247+
2248+ err = bson.Unmarshal([]byte(data), &struct{}{})
2249+ c.Assert(err, ErrorMatches, "Document is corrupted")
2250+ }
2251+}
2252+
2253+// --------------------------------------------------------------------------
2254+// Setter test cases.
2255+
2256+var setterResult = map[string]error{}
2257+
2258+type setterType struct {
2259+ received interface{}
2260+}
2261+
2262+func (o *setterType) SetBSON(raw bson.Raw) error {
2263+ err := raw.Unmarshal(&o.received)
2264+ if err != nil {
2265+ panic("The panic:" + err.Error())
2266+ }
2267+ if s, ok := o.received.(string); ok {
2268+ if result, ok := setterResult[s]; ok {
2269+ return result
2270+ }
2271+ }
2272+ return nil
2273+}
2274+
2275+type ptrSetterDoc struct {
2276+ Field *setterType "_"
2277+}
2278+
2279+type valSetterDoc struct {
2280+ Field setterType "_"
2281+}
2282+
2283+func (s *S) TestUnmarshalAllItemsWithPtrSetter(c *C) {
2284+ for _, item := range allItems {
2285+ for i := 0; i != 2; i++ {
2286+ var field *setterType
2287+ if i == 0 {
2288+ obj := &ptrSetterDoc{}
2289+ err := bson.Unmarshal([]byte(wrapInDoc(item.data)), obj)
2290+ c.Assert(err, IsNil)
2291+ field = obj.Field
2292+ } else {
2293+ obj := &valSetterDoc{}
2294+ err := bson.Unmarshal([]byte(wrapInDoc(item.data)), obj)
2295+ c.Assert(err, IsNil)
2296+ field = &obj.Field
2297+ }
2298+ if item.data == "" {
2299+ // Nothing to unmarshal. Should be untouched.
2300+ if i == 0 {
2301+ c.Assert(field, IsNil)
2302+ } else {
2303+ c.Assert(field.received, IsNil)
2304+ }
2305+ } else {
2306+ expected := item.obj.(bson.M)["_"]
2307+ c.Assert(field, NotNil, Commentf("Pointer not initialized (%#v)", expected))
2308+ c.Assert(field.received, DeepEquals, expected)
2309+ }
2310+ }
2311+ }
2312+}
2313+
2314+func (s *S) TestUnmarshalWholeDocumentWithSetter(c *C) {
2315+ obj := &setterType{}
2316+ err := bson.Unmarshal([]byte(sampleItems[0].data), obj)
2317+ c.Assert(err, IsNil)
2318+ c.Assert(obj.received, DeepEquals, bson.M{"hello": "world"})
2319+}
2320+
2321+func (s *S) TestUnmarshalSetterOmits(c *C) {
2322+ setterResult["2"] = &bson.TypeError{}
2323+ setterResult["4"] = &bson.TypeError{}
2324+ defer func() {
2325+ delete(setterResult, "2")
2326+ delete(setterResult, "4")
2327+ }()
2328+
2329+ m := map[string]*setterType{}
2330+ data := wrapInDoc("\x02abc\x00\x02\x00\x00\x001\x00" +
2331+ "\x02def\x00\x02\x00\x00\x002\x00" +
2332+ "\x02ghi\x00\x02\x00\x00\x003\x00" +
2333+ "\x02jkl\x00\x02\x00\x00\x004\x00")
2334+ err := bson.Unmarshal([]byte(data), m)
2335+ c.Assert(err, IsNil)
2336+ c.Assert(m["abc"], NotNil)
2337+ c.Assert(m["def"], IsNil)
2338+ c.Assert(m["ghi"], NotNil)
2339+ c.Assert(m["jkl"], IsNil)
2340+
2341+ c.Assert(m["abc"].received, Equals, "1")
2342+ c.Assert(m["ghi"].received, Equals, "3")
2343+}
2344+
2345+func (s *S) TestUnmarshalSetterErrors(c *C) {
2346+ boom := errors.New("BOOM")
2347+ setterResult["2"] = boom
2348+ defer delete(setterResult, "2")
2349+
2350+ m := map[string]*setterType{}
2351+ data := wrapInDoc("\x02abc\x00\x02\x00\x00\x001\x00" +
2352+ "\x02def\x00\x02\x00\x00\x002\x00" +
2353+ "\x02ghi\x00\x02\x00\x00\x003\x00")
2354+ err := bson.Unmarshal([]byte(data), m)
2355+ c.Assert(err, Equals, boom)
2356+ c.Assert(m["abc"], NotNil)
2357+ c.Assert(m["def"], IsNil)
2358+ c.Assert(m["ghi"], IsNil)
2359+
2360+ c.Assert(m["abc"].received, Equals, "1")
2361+}
2362+
2363+func (s *S) TestDMap(c *C) {
2364+ d := bson.D{{"a", 1}, {"b", 2}}
2365+ c.Assert(d.Map(), DeepEquals, bson.M{"a": 1, "b": 2})
2366+}
2367+
2368+func (s *S) TestUnmarshalSetterSetZero(c *C) {
2369+ setterResult["foo"] = bson.SetZero
2370+ defer delete(setterResult, "field")
2371+
2372+ data, err := bson.Marshal(bson.M{"field": "foo"})
2373+ c.Assert(err, IsNil)
2374+
2375+ m := map[string]*setterType{}
2376+ err = bson.Unmarshal([]byte(data), m)
2377+ c.Assert(err, IsNil)
2378+
2379+ value, ok := m["field"]
2380+ c.Assert(ok, Equals, true)
2381+ c.Assert(value, IsNil)
2382+}
2383+
2384+
2385+// --------------------------------------------------------------------------
2386+// Getter test cases.
2387+
2388+type typeWithGetter struct {
2389+ result interface{}
2390+ err error
2391+}
2392+
2393+func (t *typeWithGetter) GetBSON() (interface{}, error) {
2394+ return t.result, t.err
2395+}
2396+
2397+type docWithGetterField struct {
2398+ Field *typeWithGetter "_"
2399+}
2400+
2401+func (s *S) TestMarshalAllItemsWithGetter(c *C) {
2402+ for i, item := range allItems {
2403+ if item.data == "" {
2404+ continue
2405+ }
2406+ obj := &docWithGetterField{}
2407+ obj.Field = &typeWithGetter{result: item.obj.(bson.M)["_"]}
2408+ data, err := bson.Marshal(obj)
2409+ c.Assert(err, IsNil)
2410+ c.Assert(string(data), Equals, wrapInDoc(item.data),
2411+ Commentf("Failed on item #%d", i))
2412+ }
2413+}
2414+
2415+func (s *S) TestMarshalWholeDocumentWithGetter(c *C) {
2416+ obj := &typeWithGetter{result: sampleItems[0].obj}
2417+ data, err := bson.Marshal(obj)
2418+ c.Assert(err, IsNil)
2419+ c.Assert(string(data), Equals, sampleItems[0].data)
2420+}
2421+
2422+func (s *S) TestGetterErrors(c *C) {
2423+ e := errors.New("oops")
2424+
2425+ obj1 := &docWithGetterField{}
2426+ obj1.Field = &typeWithGetter{sampleItems[0].obj, e}
2427+ data, err := bson.Marshal(obj1)
2428+ c.Assert(err, ErrorMatches, "oops")
2429+ c.Assert(data, IsNil)
2430+
2431+ obj2 := &typeWithGetter{sampleItems[0].obj, e}
2432+ data, err = bson.Marshal(obj2)
2433+ c.Assert(err, ErrorMatches, "oops")
2434+ c.Assert(data, IsNil)
2435+}
2436+
2437+type intGetter int64
2438+
2439+func (t intGetter) GetBSON() (interface{}, error) {
2440+ return int64(t), nil
2441+}
2442+
2443+type typeWithIntGetter struct {
2444+ V intGetter ",minsize"
2445+}
2446+
2447+func (s *S) TestMarshalShortWithGetter(c *C) {
2448+ obj := typeWithIntGetter{42}
2449+ data, err := bson.Marshal(obj)
2450+ c.Assert(err, IsNil)
2451+ m := bson.M{}
2452+ err = bson.Unmarshal(data, m)
2453+ c.Assert(m["v"], Equals, 42)
2454+}
2455+
2456+// --------------------------------------------------------------------------
2457+// Cross-type conversion tests.
2458+
2459+type crossTypeItem struct {
2460+ obj1 interface{}
2461+ obj2 interface{}
2462+}
2463+
2464+type condStr struct {
2465+ V string ",omitempty"
2466+}
2467+type condStrNS struct {
2468+ V string `a:"A" bson:",omitempty" b:"B"`
2469+}
2470+type condBool struct {
2471+ V bool ",omitempty"
2472+}
2473+type condInt struct {
2474+ V int ",omitempty"
2475+}
2476+type condUInt struct {
2477+ V uint ",omitempty"
2478+}
2479+type condFloat struct {
2480+ V float64 ",omitempty"
2481+}
2482+type condIface struct {
2483+ V interface{} ",omitempty"
2484+}
2485+type condPtr struct {
2486+ V *bool ",omitempty"
2487+}
2488+type condSlice struct {
2489+ V []string ",omitempty"
2490+}
2491+type condMap struct {
2492+ V map[string]int ",omitempty"
2493+}
2494+type namedCondStr struct {
2495+ V string "myv,omitempty"
2496+}
2497+type condTime struct {
2498+ V time.Time ",omitempty"
2499+}
2500+
2501+type shortInt struct {
2502+ V int64 ",minsize"
2503+}
2504+type shortUint struct {
2505+ V uint64 ",minsize"
2506+}
2507+type shortIface struct {
2508+ V interface{} ",minsize"
2509+}
2510+type shortPtr struct {
2511+ V *int64 ",minsize"
2512+}
2513+type shortNonEmptyInt struct {
2514+ V int64 ",minsize,omitempty"
2515+}
2516+
2517+type inlineInt struct {
2518+ V struct{ A, B int } ",inline"
2519+}
2520+type inlineCantPtr struct {
2521+ V *struct{ A, B int } ",inline"
2522+}
2523+type inlineDupName struct {
2524+ A int
2525+ V struct{ A, B int } ",inline"
2526+}
2527+
2528+type MyBytes []byte
2529+type MyBool bool
2530+type MyD []bson.DocElem
2531+type MyM map[string]interface{}
2532+
2533+var truevar = true
2534+var falsevar = false
2535+
2536+var int64var = int64(42)
2537+var int64ptr = &int64var
2538+var intvar = int(42)
2539+var intptr = &intvar
2540+
2541+func parseURL(s string) *url.URL {
2542+ u, err := url.Parse(s)
2543+ if err != nil {
2544+ panic(err)
2545+ }
2546+ return u
2547+}
2548+
2549+// That's a pretty fun test. It will dump the first item, generate a zero
2550+// value equivalent to the second one, load the dumped data onto it, and then
2551+// verify that the resulting value is deep-equal to the untouched second value.
2552+// Then, it will do the same in the *opposite* direction!
2553+var twoWayCrossItems = []crossTypeItem{
2554+ // int<=>int
2555+ {&struct{ I int }{42}, &struct{ I int8 }{42}},
2556+ {&struct{ I int }{42}, &struct{ I int32 }{42}},
2557+ {&struct{ I int }{42}, &struct{ I int64 }{42}},
2558+ {&struct{ I int8 }{42}, &struct{ I int32 }{42}},
2559+ {&struct{ I int8 }{42}, &struct{ I int64 }{42}},
2560+ {&struct{ I int32 }{42}, &struct{ I int64 }{42}},
2561+
2562+ // uint<=>uint
2563+ {&struct{ I uint }{42}, &struct{ I uint8 }{42}},
2564+ {&struct{ I uint }{42}, &struct{ I uint32 }{42}},
2565+ {&struct{ I uint }{42}, &struct{ I uint64 }{42}},
2566+ {&struct{ I uint8 }{42}, &struct{ I uint32 }{42}},
2567+ {&struct{ I uint8 }{42}, &struct{ I uint64 }{42}},
2568+ {&struct{ I uint32 }{42}, &struct{ I uint64 }{42}},
2569+
2570+ // float32<=>float64
2571+ {&struct{ I float32 }{42}, &struct{ I float64 }{42}},
2572+
2573+ // int<=>uint
2574+ {&struct{ I uint }{42}, &struct{ I int }{42}},
2575+ {&struct{ I uint }{42}, &struct{ I int8 }{42}},
2576+ {&struct{ I uint }{42}, &struct{ I int32 }{42}},
2577+ {&struct{ I uint }{42}, &struct{ I int64 }{42}},
2578+ {&struct{ I uint8 }{42}, &struct{ I int }{42}},
2579+ {&struct{ I uint8 }{42}, &struct{ I int8 }{42}},
2580+ {&struct{ I uint8 }{42}, &struct{ I int32 }{42}},
2581+ {&struct{ I uint8 }{42}, &struct{ I int64 }{42}},
2582+ {&struct{ I uint32 }{42}, &struct{ I int }{42}},
2583+ {&struct{ I uint32 }{42}, &struct{ I int8 }{42}},
2584+ {&struct{ I uint32 }{42}, &struct{ I int32 }{42}},
2585+ {&struct{ I uint32 }{42}, &struct{ I int64 }{42}},
2586+ {&struct{ I uint64 }{42}, &struct{ I int }{42}},
2587+ {&struct{ I uint64 }{42}, &struct{ I int8 }{42}},
2588+ {&struct{ I uint64 }{42}, &struct{ I int32 }{42}},
2589+ {&struct{ I uint64 }{42}, &struct{ I int64 }{42}},
2590+
2591+ // int <=> float
2592+ {&struct{ I int }{42}, &struct{ I float64 }{42}},
2593+
2594+ // int <=> bool
2595+ {&struct{ I int }{1}, &struct{ I bool }{true}},
2596+ {&struct{ I int }{0}, &struct{ I bool }{false}},
2597+
2598+ // uint <=> float64
2599+ {&struct{ I uint }{42}, &struct{ I float64 }{42}},
2600+
2601+ // uint <=> bool
2602+ {&struct{ I uint }{1}, &struct{ I bool }{true}},
2603+ {&struct{ I uint }{0}, &struct{ I bool }{false}},
2604+
2605+ // float64 <=> bool
2606+ {&struct{ I float64 }{1}, &struct{ I bool }{true}},
2607+ {&struct{ I float64 }{0}, &struct{ I bool }{false}},
2608+
2609+ // string <=> string and string <=> []byte
2610+ {&struct{ S []byte }{[]byte("abc")}, &struct{ S string }{"abc"}},
2611+ {&struct{ S []byte }{[]byte("def")}, &struct{ S bson.Symbol }{"def"}},
2612+ {&struct{ S string }{"ghi"}, &struct{ S bson.Symbol }{"ghi"}},
2613+
2614+ // map <=> struct
2615+ {&struct {
2616+ A struct {
2617+ B, C int
2618+ }
2619+ }{struct{ B, C int }{1, 2}},
2620+ map[string]map[string]int{"a": map[string]int{"b": 1, "c": 2}}},
2621+
2622+ {&struct{ A bson.Symbol }{"abc"}, map[string]string{"a": "abc"}},
2623+ {&struct{ A bson.Symbol }{"abc"}, map[string][]byte{"a": []byte("abc")}},
2624+ {&struct{ A []byte }{[]byte("abc")}, map[string]string{"a": "abc"}},
2625+ {&struct{ A uint }{42}, map[string]int{"a": 42}},
2626+ {&struct{ A uint }{42}, map[string]float64{"a": 42}},
2627+ {&struct{ A uint }{1}, map[string]bool{"a": true}},
2628+ {&struct{ A int }{42}, map[string]uint{"a": 42}},
2629+ {&struct{ A int }{42}, map[string]float64{"a": 42}},
2630+ {&struct{ A int }{1}, map[string]bool{"a": true}},
2631+ {&struct{ A float64 }{42}, map[string]float32{"a": 42}},
2632+ {&struct{ A float64 }{42}, map[string]int{"a": 42}},
2633+ {&struct{ A float64 }{42}, map[string]uint{"a": 42}},
2634+ {&struct{ A float64 }{1}, map[string]bool{"a": true}},
2635+ {&struct{ A bool }{true}, map[string]int{"a": 1}},
2636+ {&struct{ A bool }{true}, map[string]uint{"a": 1}},
2637+ {&struct{ A bool }{true}, map[string]float64{"a": 1}},
2638+ {&struct{ A **byte }{&byteptr}, map[string]byte{"a": 8}},
2639+
2640+ // url.URL <=> string
2641+ {&struct{ URL *url.URL }{parseURL("h://e.c/p")}, map[string]string{"url": "h://e.c/p"}},
2642+ {&struct{ URL url.URL }{*parseURL("h://e.c/p")}, map[string]string{"url": "h://e.c/p"}},
2643+
2644+ // Slices
2645+ {&struct{ S []int }{[]int{1, 2, 3}}, map[string][]int{"s": []int{1, 2, 3}}},
2646+ {&struct{ S *[]int }{&[]int{1, 2, 3}}, map[string][]int{"s": []int{1, 2, 3}}},
2647+
2648+ // Conditionals
2649+ {&condBool{true}, map[string]bool{"v": true}},
2650+ {&condBool{}, map[string]bool{}},
2651+ {&condInt{1}, map[string]int{"v": 1}},
2652+ {&condInt{}, map[string]int{}},
2653+ {&condUInt{1}, map[string]uint{"v": 1}},
2654+ {&condUInt{}, map[string]uint{}},
2655+ {&condFloat{}, map[string]int{}},
2656+ {&condStr{"yo"}, map[string]string{"v": "yo"}},
2657+ {&condStr{}, map[string]string{}},
2658+ {&condStrNS{"yo"}, map[string]string{"v": "yo"}},
2659+ {&condStrNS{}, map[string]string{}},
2660+ {&condSlice{[]string{"yo"}}, map[string][]string{"v": []string{"yo"}}},
2661+ {&condSlice{}, map[string][]string{}},
2662+ {&condMap{map[string]int{"k": 1}}, bson.M{"v": bson.M{"k": 1}}},
2663+ {&condMap{}, map[string][]string{}},
2664+ {&condIface{"yo"}, map[string]string{"v": "yo"}},
2665+ {&condIface{""}, map[string]string{"v": ""}},
2666+ {&condIface{}, map[string]string{}},
2667+ {&condPtr{&truevar}, map[string]bool{"v": true}},
2668+ {&condPtr{&falsevar}, map[string]bool{"v": false}},
2669+ {&condPtr{}, map[string]string{}},
2670+
2671+ {&condTime{time.Unix(123456789, 123e6)}, map[string]time.Time{"v": time.Unix(123456789, 123e6)}},
2672+ {&condTime{}, map[string]string{}},
2673+
2674+ {&namedCondStr{"yo"}, map[string]string{"myv": "yo"}},
2675+ {&namedCondStr{}, map[string]string{}},
2676+
2677+ {&shortInt{1}, map[string]interface{}{"v": 1}},
2678+ {&shortInt{1 << 30}, map[string]interface{}{"v": 1 << 30}},
2679+ {&shortInt{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}},
2680+ {&shortUint{1 << 30}, map[string]interface{}{"v": 1 << 30}},
2681+ {&shortUint{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}},
2682+ {&shortIface{int64(1) << 31}, map[string]interface{}{"v": int64(1 << 31)}},
2683+ {&shortPtr{int64ptr}, map[string]interface{}{"v": intvar}},
2684+
2685+ {&shortNonEmptyInt{1}, map[string]interface{}{"v": 1}},
2686+ {&shortNonEmptyInt{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}},
2687+ {&shortNonEmptyInt{}, map[string]interface{}{}},
2688+
2689+ {&inlineInt{struct{ A, B int }{1, 2}}, map[string]interface{}{"a": 1, "b": 2}},
2690+
2691+ // []byte <=> MyBytes
2692+ {&struct{ B MyBytes }{[]byte("abc")}, map[string]string{"b": "abc"}},
2693+ {&struct{ B MyBytes }{[]byte{}}, map[string]string{"b": ""}},
2694+ {&struct{ B MyBytes }{}, map[string]bool{}},
2695+ {&struct{ B []byte }{[]byte("abc")}, map[string]MyBytes{"b": []byte("abc")}},
2696+
2697+ // bool <=> MyBool
2698+ {&struct{ B MyBool }{true}, map[string]bool{"b": true}},
2699+ {&struct{ B MyBool }{}, map[string]bool{"b": false}},
2700+ {&struct{ B MyBool }{}, map[string]string{}},
2701+ {&struct{ B bool }{}, map[string]MyBool{"b": false}},
2702+
2703+ // arrays
2704+ {&struct{ V [2]int }{[...]int{1, 2}}, map[string][2]int{"v": [2]int{1, 2}}},
2705+
2706+ // zero time
2707+ {&struct{ V time.Time }{}, map[string]interface{}{"v": time.Time{}}},
2708+
2709+ // zero time + 1 second + 1 millisecond; overflows int64 as nanoseconds
2710+ {&struct{ V time.Time }{time.Unix(-62135596799, 1e6).Local()},
2711+ map[string]interface{}{"v": time.Unix(-62135596799, 1e6).Local()}},
2712+
2713+ // bson.D <=> []DocElem
2714+ {&bson.D{{"a", bson.D{{"b", 1}, {"c", 2}}}}, &bson.D{{"a", bson.D{{"b", 1}, {"c", 2}}}}},
2715+ {&bson.D{{"a", bson.D{{"b", 1}, {"c", 2}}}}, &MyD{{"a", MyD{{"b", 1}, {"c", 2}}}}},
2716+
2717+ // bson.M <=> map
2718+ {bson.M{"a": bson.M{"b": 1, "c": 2}}, MyM{"a": MyM{"b": 1, "c": 2}}},
2719+ {bson.M{"a": bson.M{"b": 1, "c": 2}}, map[string]interface{}{"a": map[string]interface{}{"b": 1, "c": 2}}},
2720+}
2721+
2722+// Same thing, but only one way (obj1 => obj2).
2723+var oneWayCrossItems = []crossTypeItem{
2724+ // map <=> struct
2725+ {map[string]interface{}{"a": 1, "b": "2", "c": 3},
2726+ map[string]int{"a": 1, "c": 3}},
2727+
2728+ // Can't decode int into struct.
2729+ {bson.M{"a": bson.M{"b": 2}}, &struct{ A bool }{}},
2730+
2731+ // Would get decoded into a int32 too in the opposite direction.
2732+ {&shortIface{int64(1) << 30}, map[string]interface{}{"v": 1 << 30}},
2733+}
2734+
2735+func testCrossPair(c *C, dump interface{}, load interface{}) {
2736+ c.Logf("Dump: %#v", dump)
2737+ c.Logf("Load: %#v", load)
2738+ zero := makeZeroDoc(load)
2739+ data, err := bson.Marshal(dump)
2740+ c.Assert(err, IsNil)
2741+ c.Logf("Dumped: %#v", string(data))
2742+ err = bson.Unmarshal(data, zero)
2743+ c.Assert(err, IsNil)
2744+ c.Logf("Loaded: %#v", zero)
2745+ c.Assert(zero, DeepEquals, load)
2746+}
2747+
2748+func (s *S) TestTwoWayCrossPairs(c *C) {
2749+ for _, item := range twoWayCrossItems {
2750+ testCrossPair(c, item.obj1, item.obj2)
2751+ testCrossPair(c, item.obj2, item.obj1)
2752+ }
2753+}
2754+
2755+func (s *S) TestOneWayCrossPairs(c *C) {
2756+ for _, item := range oneWayCrossItems {
2757+ testCrossPair(c, item.obj1, item.obj2)
2758+ }
2759+}
2760+
2761+// --------------------------------------------------------------------------
2762+// ObjectId hex representation test.
2763+
2764+func (s *S) TestObjectIdHex(c *C) {
2765+ id := bson.ObjectIdHex("4d88e15b60f486e428412dc9")
2766+ c.Assert(id.String(), Equals, `ObjectIdHex("4d88e15b60f486e428412dc9")`)
2767+ c.Assert(id.Hex(), Equals, "4d88e15b60f486e428412dc9")
2768+}
2769+
2770+func (s *S) TestIsObjectIdHex(c *C) {
2771+ test := []struct{ id string; valid bool }{
2772+ {"4d88e15b60f486e428412dc9", true},
2773+ {"4d88e15b60f486e428412dc", false},
2774+ {"4d88e15b60f486e428412dc9e", false},
2775+ {"4d88e15b60f486e428412dcx", false},
2776+ }
2777+ for _, t := range test {
2778+ c.Assert(bson.IsObjectIdHex(t.id), Equals, t.valid)
2779+ }
2780+}
2781+
2782+// --------------------------------------------------------------------------
2783+// ObjectId parts extraction tests.
2784+
2785+type objectIdParts struct {
2786+ id bson.ObjectId
2787+ timestamp int64
2788+ machine []byte
2789+ pid uint16
2790+ counter int32
2791+}
2792+
2793+var objectIds = []objectIdParts{
2794+ objectIdParts{
2795+ bson.ObjectIdHex("4d88e15b60f486e428412dc9"),
2796+ 1300816219,
2797+ []byte{0x60, 0xf4, 0x86},
2798+ 0xe428,
2799+ 4271561,
2800+ },
2801+ objectIdParts{
2802+ bson.ObjectIdHex("000000000000000000000000"),
2803+ 0,
2804+ []byte{0x00, 0x00, 0x00},
2805+ 0x0000,
2806+ 0,
2807+ },
2808+ objectIdParts{
2809+ bson.ObjectIdHex("00000000aabbccddee000001"),
2810+ 0,
2811+ []byte{0xaa, 0xbb, 0xcc},
2812+ 0xddee,
2813+ 1,
2814+ },
2815+}
2816+
2817+func (s *S) TestObjectIdPartsExtraction(c *C) {
2818+ for i, v := range objectIds {
2819+ t := time.Unix(v.timestamp, 0)
2820+ c.Assert(v.id.Time(), Equals, t, Commentf("#%d Wrong timestamp value", i))
2821+ c.Assert(v.id.Machine(), DeepEquals, v.machine, Commentf("#%d Wrong machine id value", i))
2822+ c.Assert(v.id.Pid(), Equals, v.pid, Commentf("#%d Wrong pid value", i))
2823+ c.Assert(v.id.Counter(), Equals, v.counter, Commentf("#%d Wrong counter value", i))
2824+ }
2825+}
2826+
2827+func (s *S) TestNow(c *C) {
2828+ before := time.Now()
2829+ time.Sleep(1e6)
2830+ now := bson.Now()
2831+ time.Sleep(1e6)
2832+ after := time.Now()
2833+ c.Assert(now.After(before) && now.Before(after), Equals, true, Commentf("now=%s, before=%s, after=%s", now, before, after))
2834+}
2835+
2836+// --------------------------------------------------------------------------
2837+// ObjectId generation tests.
2838+
2839+func (s *S) TestNewObjectId(c *C) {
2840+ // Generate 10 ids
2841+ ids := make([]bson.ObjectId, 10)
2842+ for i := 0; i < 10; i++ {
2843+ ids[i] = bson.NewObjectId()
2844+ }
2845+ for i := 1; i < 10; i++ {
2846+ prevId := ids[i-1]
2847+ id := ids[i]
2848+ // Test for uniqueness among all other 9 generated ids
2849+ for j, tid := range ids {
2850+ if j != i {
2851+ c.Assert(id, Not(Equals), tid, Commentf("Generated ObjectId is not unique"))
2852+ }
2853+ }
2854+ // Check that timestamp was incremented and is within 30 seconds of the previous one
2855+ secs := id.Time().Sub(prevId.Time()).Seconds()
2856+ c.Assert((secs >= 0 && secs <= 30), Equals, true, Commentf("Wrong timestamp in generated ObjectId"))
2857+ // Check that machine ids are the same
2858+ c.Assert(id.Machine(), DeepEquals, prevId.Machine())
2859+ // Check that pids are the same
2860+ c.Assert(id.Pid(), Equals, prevId.Pid())
2861+ // Test for proper increment
2862+ delta := int(id.Counter() - prevId.Counter())
2863+ c.Assert(delta, Equals, 1, Commentf("Wrong increment in generated ObjectId"))
2864+ }
2865+}
2866+
2867+func (s *S) TestNewObjectIdWithTime(c *C) {
2868+ t := time.Unix(12345678, 0)
2869+ id := bson.NewObjectIdWithTime(t)
2870+ c.Assert(id.Time(), Equals, t)
2871+ c.Assert(id.Machine(), DeepEquals, []byte{0x00, 0x00, 0x00})
2872+ c.Assert(int(id.Pid()), Equals, 0)
2873+ c.Assert(int(id.Counter()), Equals, 0)
2874+}
2875+
2876+// --------------------------------------------------------------------------
2877+// ObjectId JSON marshalling.
2878+
2879+type jsonType struct {
2880+ Id *bson.ObjectId
2881+}
2882+
2883+func (s *S) TestObjectIdJSONMarshaling(c *C) {
2884+ id := bson.ObjectIdHex("4d88e15b60f486e428412dc9")
2885+ v := jsonType{Id: &id}
2886+ data, err := json.Marshal(&v)
2887+ c.Assert(err, IsNil)
2888+ c.Assert(string(data), Equals, `{"Id":"4d88e15b60f486e428412dc9"}`)
2889+}
2890+
2891+func (s *S) TestObjectIdJSONUnmarshaling(c *C) {
2892+ data := []byte(`{"Id":"4d88e15b60f486e428412dc9"}`)
2893+ v := jsonType{}
2894+ err := json.Unmarshal(data, &v)
2895+ c.Assert(err, IsNil)
2896+ c.Assert(*v.Id, Equals, bson.ObjectIdHex("4d88e15b60f486e428412dc9"))
2897+}
2898+
2899+func (s *S) TestObjectIdJSONUnmarshalingError(c *C) {
2900+ v := jsonType{}
2901+ err := json.Unmarshal([]byte(`{"Id":"4d88e15b60f486e428412dc9A"}`), &v)
2902+ c.Assert(err, ErrorMatches, `Invalid ObjectId in JSON: "4d88e15b60f486e428412dc9A"`)
2903+ err = json.Unmarshal([]byte(`{"Id":"4d88e15b60f486e428412dcZ"}`), &v)
2904+ c.Assert(err, ErrorMatches, `Invalid ObjectId in JSON: "4d88e15b60f486e428412dcZ" .*`)
2905+}
2906+
2907+// --------------------------------------------------------------------------
2908+// Some simple benchmarks.
2909+
2910+type BenchT struct {
2911+ A, B, C, D, E, F string
2912+}
2913+
2914+func BenchmarkUnmarhsalStruct(b *testing.B) {
2915+ v := BenchT{A: "A", D: "D", E: "E"}
2916+ data, err := bson.Marshal(&v)
2917+ if err != nil {
2918+ panic(err)
2919+ }
2920+ b.ResetTimer()
2921+ for i := 0; i < b.N; i++ {
2922+ err = bson.Unmarshal(data, &v)
2923+ }
2924+ if err != nil {
2925+ panic(err)
2926+ }
2927+}
2928+
2929+func BenchmarkUnmarhsalMap(b *testing.B) {
2930+ m := bson.M{"a": "a", "d": "d", "e": "e"}
2931+ data, err := bson.Marshal(&m)
2932+ if err != nil {
2933+ panic(err)
2934+ }
2935+ b.ResetTimer()
2936+ for i := 0; i < b.N; i++ {
2937+ err = bson.Unmarshal(data, &m)
2938+ }
2939+ if err != nil {
2940+ panic(err)
2941+ }
2942+}
2943
2944=== added file 'bson/decode.go'
2945--- bson/decode.go 1970-01-01 00:00:00 +0000
2946+++ bson/decode.go 2013-04-03 09:16:27 +0000
2947@@ -0,0 +1,734 @@
2948+// BSON library for Go
2949+//
2950+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
2951+//
2952+// All rights reserved.
2953+//
2954+// Redistribution and use in source and binary forms, with or without
2955+// modification, are permitted provided that the following conditions are met:
2956+//
2957+// 1. Redistributions of source code must retain the above copyright notice, this
2958+// list of conditions and the following disclaimer.
2959+// 2. Redistributions in binary form must reproduce the above copyright notice,
2960+// this list of conditions and the following disclaimer in the documentation
2961+// and/or other materials provided with the distribution.
2962+//
2963+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
2964+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2965+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2966+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
2967+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2968+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2969+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2970+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2971+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2972+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2973+// gobson - BSON library for Go.
2974+
2975+package bson
2976+
2977+import (
2978+ "fmt"
2979+ "math"
2980+ "net/url"
2981+ "reflect"
2982+ "sync"
2983+ "time"
2984+)
2985+
2986+type decoder struct {
2987+ in []byte
2988+ i int
2989+ docType reflect.Type
2990+}
2991+
2992+var typeM = reflect.TypeOf(M{})
2993+
2994+func newDecoder(in []byte) *decoder {
2995+ return &decoder{in, 0, typeM}
2996+}
2997+
2998+// --------------------------------------------------------------------------
2999+// Some helper functions.
3000+
3001+func corrupted() {
3002+ panic("Document is corrupted")
3003+}
3004+
3005+func settableValueOf(i interface{}) reflect.Value {
3006+ v := reflect.ValueOf(i)
3007+ sv := reflect.New(v.Type()).Elem()
3008+ sv.Set(v)
3009+ return sv
3010+}
3011+
3012+// --------------------------------------------------------------------------
3013+// Unmarshaling of documents.
3014+
3015+const (
3016+ setterUnknown = iota
3017+ setterNone
3018+ setterType
3019+ setterAddr
3020+)
3021+
3022+var setterStyle map[reflect.Type]int
3023+var setterIface reflect.Type
3024+var setterMutex sync.RWMutex
3025+
3026+func init() {
3027+ var iface Setter
3028+ setterIface = reflect.TypeOf(&iface).Elem()
3029+ setterStyle = make(map[reflect.Type]int)
3030+}
3031+
3032+func getSetter(outt reflect.Type, out reflect.Value) Setter {
3033+ setterMutex.RLock()
3034+ style := setterStyle[outt]
3035+ setterMutex.RUnlock()
3036+ if style == setterNone {
3037+ return nil
3038+ }
3039+ if style == setterUnknown {
3040+ setterMutex.Lock()
3041+ defer setterMutex.Unlock()
3042+ if outt.Implements(setterIface) {
3043+ setterStyle[outt] = setterType
3044+ } else if reflect.PtrTo(outt).Implements(setterIface) {
3045+ setterStyle[outt] = setterAddr
3046+ } else {
3047+ setterStyle[outt] = setterNone
3048+ return nil
3049+ }
3050+ style = setterStyle[outt]
3051+ }
3052+ if style == setterAddr {
3053+ if !out.CanAddr() {
3054+ return nil
3055+ }
3056+ out = out.Addr()
3057+ } else if outt.Kind() == reflect.Ptr && out.IsNil() {
3058+ out.Set(reflect.New(outt.Elem()))
3059+ }
3060+ return out.Interface().(Setter)
3061+}
3062+
3063+func (d *decoder) readDocTo(out reflect.Value) {
3064+ var elemType reflect.Type
3065+ outt := out.Type()
3066+ outk := outt.Kind()
3067+
3068+ for {
3069+ if outk == reflect.Ptr && out.IsNil() {
3070+ out.Set(reflect.New(outt.Elem()))
3071+ }
3072+ if setter := getSetter(outt, out); setter != nil {
3073+ var raw Raw
3074+ d.readDocTo(reflect.ValueOf(&raw))
3075+ err := setter.SetBSON(raw)
3076+ if _, ok := err.(*TypeError); err != nil && !ok {
3077+ panic(err)
3078+ }
3079+ return
3080+ }
3081+ if outk == reflect.Ptr {
3082+ out = out.Elem()
3083+ outt = out.Type()
3084+ outk = out.Kind()
3085+ continue
3086+ }
3087+ break
3088+ }
3089+
3090+ var fieldsMap map[string]fieldInfo
3091+ start := d.i
3092+
3093+ origout := out
3094+ if outk == reflect.Interface {
3095+ if d.docType.Kind() == reflect.Map {
3096+ mv := reflect.MakeMap(d.docType)
3097+ out.Set(mv)
3098+ out = mv
3099+ } else {
3100+ dv := reflect.New(d.docType).Elem()
3101+ out.Set(dv)
3102+ out = dv
3103+ }
3104+ outt = out.Type()
3105+ outk = outt.Kind()
3106+ }
3107+
3108+ docType := d.docType
3109+ switch outk {
3110+ case reflect.Map:
3111+ if outt.Key().Kind() != reflect.String {
3112+ panic("BSON map must have string keys. Got: " + outt.String())
3113+ }
3114+ elemType = outt.Elem()
3115+ if elemType == typeIface {
3116+ d.docType = outt
3117+ }
3118+ if out.IsNil() {
3119+ out.Set(reflect.MakeMap(out.Type()))
3120+ } else if out.Len() > 0 {
3121+ var none reflect.Value
3122+ for _, k := range out.MapKeys() {
3123+ out.SetMapIndex(k, none)
3124+ }
3125+ }
3126+ case reflect.Struct:
3127+ if outt != typeRaw {
3128+ sinfo, err := getStructInfo(out.Type())
3129+ if err != nil {
3130+ panic(err)
3131+ }
3132+ fieldsMap = sinfo.FieldsMap
3133+ out.Set(sinfo.Zero)
3134+ }
3135+ case reflect.Slice:
3136+ if outt.Elem() == typeDocElem {
3137+ origout.Set(d.readDocElems(outt))
3138+ return
3139+ }
3140+ fallthrough
3141+ default:
3142+ panic("Unsupported document type for unmarshalling: " + out.Type().String())
3143+ }
3144+
3145+ end := d.i - 4 + int(d.readInt32())
3146+ if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
3147+ corrupted()
3148+ }
3149+ for d.in[d.i] != '\x00' {
3150+ kind := d.readByte()
3151+ name := d.readCStr()
3152+ if d.i >= end {
3153+ corrupted()
3154+ }
3155+
3156+ switch outk {
3157+ case reflect.Map:
3158+ e := reflect.New(elemType).Elem()
3159+ if d.readElemTo(e, kind) {
3160+ out.SetMapIndex(reflect.ValueOf(name), e)
3161+ }
3162+ case reflect.Struct:
3163+ if outt == typeRaw {
3164+ d.readElemTo(blackHole, kind)
3165+ } else {
3166+ if info, ok := fieldsMap[name]; ok {
3167+ if info.Inline == nil {
3168+ d.readElemTo(out.Field(info.Num), kind)
3169+ } else {
3170+ d.readElemTo(out.FieldByIndex(info.Inline), kind)
3171+ }
3172+ } else {
3173+ d.dropElem(kind)
3174+ }
3175+ }
3176+ case reflect.Slice:
3177+ }
3178+
3179+ if d.i >= end {
3180+ corrupted()
3181+ }
3182+ }
3183+ d.i++ // '\x00'
3184+ if d.i != end {
3185+ corrupted()
3186+ }
3187+ d.docType = docType
3188+
3189+ if outt == typeRaw {
3190+ out.Set(reflect.ValueOf(Raw{0x03, d.in[start:d.i]}))
3191+ }
3192+}
3193+
3194+func (d *decoder) readArrayDocTo(out reflect.Value) {
3195+ end := d.i - 4 + int(d.readInt32())
3196+ if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
3197+ corrupted()
3198+ }
3199+ i := 0
3200+ l := out.Len()
3201+ for d.in[d.i] != '\x00' {
3202+ if i >= l {
3203+ panic("Length mismatch on array field")
3204+ }
3205+ kind := d.readByte()
3206+ for d.i < end && d.in[d.i] != '\x00' {
3207+ d.i++
3208+ }
3209+ if d.i >= end {
3210+ corrupted()
3211+ }
3212+ d.i++
3213+ d.readElemTo(out.Index(i), kind)
3214+ if d.i >= end {
3215+ corrupted()
3216+ }
3217+ i++
3218+ }
3219+ if i != l {
3220+ panic("Length mismatch on array field")
3221+ }
3222+ d.i++ // '\x00'
3223+ if d.i != end {
3224+ corrupted()
3225+ }
3226+}
3227+
3228+func (d *decoder) readSliceDoc(t reflect.Type) interface{} {
3229+ tmp := make([]reflect.Value, 0, 8)
3230+ elemType := t.Elem()
3231+
3232+ end := d.i - 4 + int(d.readInt32())
3233+ if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
3234+ corrupted()
3235+ }
3236+ for d.in[d.i] != '\x00' {
3237+ kind := d.readByte()
3238+ for d.i < end && d.in[d.i] != '\x00' {
3239+ d.i++
3240+ }
3241+ if d.i >= end {
3242+ corrupted()
3243+ }
3244+ d.i++
3245+ e := reflect.New(elemType).Elem()
3246+ if d.readElemTo(e, kind) {
3247+ tmp = append(tmp, e)
3248+ }
3249+ if d.i >= end {
3250+ corrupted()
3251+ }
3252+ }
3253+ d.i++ // '\x00'
3254+ if d.i != end {
3255+ corrupted()
3256+ }
3257+
3258+ n := len(tmp)
3259+ slice := reflect.MakeSlice(t, n, n)
3260+ for i := 0; i != n; i++ {
3261+ slice.Index(i).Set(tmp[i])
3262+ }
3263+ return slice.Interface()
3264+}
3265+
3266+var typeSlice = reflect.TypeOf([]interface{}{})
3267+var typeIface = typeSlice.Elem()
3268+
3269+func (d *decoder) readDocElems(typ reflect.Type) reflect.Value {
3270+ docType := d.docType
3271+ d.docType = typ
3272+ slice := make([]DocElem, 0, 8)
3273+ d.readDocWith(func(kind byte, name string) {
3274+ e := DocElem{Name: name}
3275+ v := reflect.ValueOf(&e.Value)
3276+ if d.readElemTo(v.Elem(), kind) {
3277+ slice = append(slice, e)
3278+ }
3279+ })
3280+ slicev := reflect.New(typ).Elem()
3281+ slicev.Set(reflect.ValueOf(slice))
3282+ d.docType = docType
3283+ return slicev
3284+}
3285+
3286+func (d *decoder) readDocWith(f func(kind byte, name string)) {
3287+ end := d.i - 4 + int(d.readInt32())
3288+ if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
3289+ corrupted()
3290+ }
3291+ for d.in[d.i] != '\x00' {
3292+ kind := d.readByte()
3293+ name := d.readCStr()
3294+ if d.i >= end {
3295+ corrupted()
3296+ }
3297+ f(kind, name)
3298+ if d.i >= end {
3299+ corrupted()
3300+ }
3301+ }
3302+ d.i++ // '\x00'
3303+ if d.i != end {
3304+ corrupted()
3305+ }
3306+}
3307+
3308+// --------------------------------------------------------------------------
3309+// Unmarshaling of individual elements within a document.
3310+
3311+var blackHole = settableValueOf(struct{}{})
3312+
3313+func (d *decoder) dropElem(kind byte) {
3314+ d.readElemTo(blackHole, kind)
3315+}
3316+
3317+// Attempt to decode an element from the document and put it into out.
3318+// If the types are not compatible, the returned ok value will be
3319+// false and out will be unchanged.
3320+func (d *decoder) readElemTo(out reflect.Value, kind byte) (good bool) {
3321+
3322+ start := d.i
3323+
3324+ if kind == '\x03' {
3325+ // Special case for documents. Delegate to readDocTo().
3326+ switch out.Kind() {
3327+ case reflect.Interface, reflect.Ptr, reflect.Struct, reflect.Map:
3328+ d.readDocTo(out)
3329+ default:
3330+ if _, ok := out.Interface().(D); ok {
3331+ out.Set(d.readDocElems(out.Type()))
3332+ } else {
3333+ d.readDocTo(blackHole)
3334+ }
3335+ }
3336+ return true
3337+ }
3338+
3339+ var in interface{}
3340+
3341+ switch kind {
3342+ case 0x01: // Float64
3343+ in = d.readFloat64()
3344+ case 0x02: // UTF-8 string
3345+ in = d.readStr()
3346+ case 0x03: // Document
3347+ panic("Can't happen. Handled above.")
3348+ case 0x04: // Array
3349+ outt := out.Type()
3350+ for outt.Kind() == reflect.Ptr {
3351+ outt = outt.Elem()
3352+ }
3353+ switch outt.Kind() {
3354+ case reflect.Array:
3355+ d.readArrayDocTo(out)
3356+ return true
3357+ case reflect.Slice:
3358+ in = d.readSliceDoc(outt)
3359+ default:
3360+ in = d.readSliceDoc(typeSlice)
3361+ }
3362+ case 0x05: // Binary
3363+ b := d.readBinary()
3364+ if b.Kind == 0x00 || b.Kind == 0x02 {
3365+ in = b.Data
3366+ } else {
3367+ in = b
3368+ }
3369+ case 0x06: // Undefined (obsolete, but still seen in the wild)
3370+ in = Undefined
3371+ case 0x07: // ObjectId
3372+ in = ObjectId(d.readBytes(12))
3373+ case 0x08: // Bool
3374+ in = d.readBool()
3375+ case 0x09: // Timestamp
3376+ // MongoDB handles timestamps as milliseconds.
3377+ i := d.readInt64()
3378+ if i == -62135596800000 {
3379+ in = time.Time{} // In UTC for convenience.
3380+ } else {
3381+ in = time.Unix(i/1e3, i%1e3*1e6)
3382+ }
3383+ case 0x0A: // Nil
3384+ in = nil
3385+ case 0x0B: // RegEx
3386+ in = d.readRegEx()
3387+ case 0x0D: // JavaScript without scope
3388+ in = JavaScript{Code: d.readStr()}
3389+ case 0x0E: // Symbol
3390+ in = Symbol(d.readStr())
3391+ case 0x0F: // JavaScript with scope
3392+ d.i += 4 // Skip length
3393+ js := JavaScript{d.readStr(), make(M)}
3394+ d.readDocTo(reflect.ValueOf(js.Scope))
3395+ in = js
3396+ case 0x10: // Int32
3397+ in = int(d.readInt32())
3398+ case 0x11: // Mongo-specific timestamp
3399+ in = MongoTimestamp(d.readInt64())
3400+ case 0x12: // Int64
3401+ in = d.readInt64()
3402+ case 0x7F: // Max key
3403+ in = MaxKey
3404+ case 0xFF: // Min key
3405+ in = MinKey
3406+ default:
3407+ panic(fmt.Sprintf("Unknown element kind (0x%02X)", kind))
3408+ }
3409+
3410+ outt := out.Type()
3411+
3412+ if outt == typeRaw {
3413+ out.Set(reflect.ValueOf(Raw{kind, d.in[start:d.i]}))
3414+ return true
3415+ }
3416+
3417+ if setter := getSetter(outt, out); setter != nil {
3418+ err := setter.SetBSON(Raw{kind, d.in[start:d.i]})
3419+ if err == SetZero {
3420+ out.Set(reflect.Zero(outt))
3421+ return true
3422+ }
3423+ if err == nil {
3424+ return true
3425+ }
3426+ if _, ok := err.(*TypeError); !ok {
3427+ panic(err)
3428+ }
3429+ return false
3430+ }
3431+
3432+ if in == nil {
3433+ out.Set(reflect.Zero(outt))
3434+ return true
3435+ }
3436+
3437+ outk := outt.Kind()
3438+
3439+ // Dereference and initialize pointer if necessary.
3440+ first := true
3441+ for outk == reflect.Ptr {
3442+ if !out.IsNil() {
3443+ out = out.Elem()
3444+ } else {
3445+ elem := reflect.New(outt.Elem())
3446+ if first {
3447+ // Only set if value is compatible.
3448+ first = false
3449+ defer func(out, elem reflect.Value) {
3450+ if good {
3451+ out.Set(elem)
3452+ }
3453+ }(out, elem)
3454+ } else {
3455+ out.Set(elem)
3456+ }
3457+ out = elem
3458+ }
3459+ outt = out.Type()
3460+ outk = outt.Kind()
3461+ }
3462+
3463+ inv := reflect.ValueOf(in)
3464+ if outt == inv.Type() {
3465+ out.Set(inv)
3466+ return true
3467+ }
3468+
3469+ switch outk {
3470+ case reflect.Interface:
3471+ out.Set(inv)
3472+ return true
3473+ case reflect.String:
3474+ switch inv.Kind() {
3475+ case reflect.String:
3476+ out.SetString(inv.String())
3477+ return true
3478+ case reflect.Slice:
3479+ if b, ok := in.([]byte); ok {
3480+ out.SetString(string(b))
3481+ return true
3482+ }
3483+ }
3484+ case reflect.Slice, reflect.Array:
3485+ // Remember, array (0x04) slices are built with the correct
3486+ // element type. If we are here, must be a cross BSON kind
3487+ // conversion (e.g. 0x05 unmarshalling on string).
3488+ if outt.Elem().Kind() != reflect.Uint8 {
3489+ break
3490+ }
3491+ switch inv.Kind() {
3492+ case reflect.String:
3493+ slice := []byte(inv.String())
3494+ out.Set(reflect.ValueOf(slice))
3495+ return true
3496+ case reflect.Slice:
3497+ switch outt.Kind() {
3498+ case reflect.Array:
3499+ reflect.Copy(out, inv)
3500+ case reflect.Slice:
3501+ out.SetBytes(inv.Bytes())
3502+ }
3503+ return true
3504+ }
3505+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
3506+ switch inv.Kind() {
3507+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
3508+ out.SetInt(inv.Int())
3509+ return true
3510+ case reflect.Float32, reflect.Float64:
3511+ out.SetInt(int64(inv.Float()))
3512+ return true
3513+ case reflect.Bool:
3514+ if inv.Bool() {
3515+ out.SetInt(1)
3516+ } else {
3517+ out.SetInt(0)
3518+ }
3519+ return true
3520+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
3521+ panic("Can't happen. No uint types in BSON?")
3522+ }
3523+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
3524+ switch inv.Kind() {
3525+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
3526+ out.SetUint(uint64(inv.Int()))
3527+ return true
3528+ case reflect.Float32, reflect.Float64:
3529+ out.SetUint(uint64(inv.Float()))
3530+ return true
3531+ case reflect.Bool:
3532+ if inv.Bool() {
3533+ out.SetUint(1)
3534+ } else {
3535+ out.SetUint(0)
3536+ }
3537+ return true
3538+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
3539+ panic("Can't happen. No uint types in BSON.")
3540+ }
3541+ case reflect.Float32, reflect.Float64:
3542+ switch inv.Kind() {
3543+ case reflect.Float32, reflect.Float64:
3544+ out.SetFloat(inv.Float())
3545+ return true
3546+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
3547+ out.SetFloat(float64(inv.Int()))
3548+ return true
3549+ case reflect.Bool:
3550+ if inv.Bool() {
3551+ out.SetFloat(1)
3552+ } else {
3553+ out.SetFloat(0)
3554+ }
3555+ return true
3556+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
3557+ panic("Can't happen. No uint types in BSON?")
3558+ }
3559+ case reflect.Bool:
3560+ switch inv.Kind() {
3561+ case reflect.Bool:
3562+ out.SetBool(inv.Bool())
3563+ return true
3564+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
3565+ out.SetBool(inv.Int() != 0)
3566+ return true
3567+ case reflect.Float32, reflect.Float64:
3568+ out.SetBool(inv.Float() != 0)
3569+ return true
3570+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
3571+ panic("Can't happen. No uint types in BSON?")
3572+ }
3573+ case reflect.Struct:
3574+ if outt == typeURL && inv.Kind() == reflect.String {
3575+ u, err := url.Parse(inv.String())
3576+ if err != nil {
3577+ panic(err)
3578+ }
3579+ out.Set(reflect.ValueOf(u).Elem())
3580+ return true
3581+ }
3582+ }
3583+
3584+ return false
3585+}
3586+
3587+// --------------------------------------------------------------------------
3588+// Parsers of basic types.
3589+
3590+func (d *decoder) readRegEx() RegEx {
3591+ re := RegEx{}
3592+ re.Pattern = d.readCStr()
3593+ re.Options = d.readCStr()
3594+ return re
3595+}
3596+
3597+func (d *decoder) readBinary() Binary {
3598+ l := d.readInt32()
3599+ b := Binary{}
3600+ b.Kind = d.readByte()
3601+ b.Data = d.readBytes(l)
3602+ if b.Kind == 0x02 {
3603+ // Weird obsolete format with redundant length.
3604+ b.Data = b.Data[4:]
3605+ }
3606+ return b
3607+}
3608+
3609+func (d *decoder) readStr() string {
3610+ l := d.readInt32()
3611+ b := d.readBytes(l - 1)
3612+ if d.readByte() != '\x00' {
3613+ corrupted()
3614+ }
3615+ return string(b)
3616+}
3617+
3618+func (d *decoder) readCStr() string {
3619+ start := d.i
3620+ end := start
3621+ l := len(d.in)
3622+ for ; end != l; end++ {
3623+ if d.in[end] == '\x00' {
3624+ break
3625+ }
3626+ }
3627+ d.i = end + 1
3628+ if d.i > l {
3629+ corrupted()
3630+ }
3631+ return string(d.in[start:end])
3632+}
3633+
3634+func (d *decoder) readBool() bool {
3635+ if d.readByte() == 1 {
3636+ return true
3637+ }
3638+ return false
3639+}
3640+
3641+func (d *decoder) readFloat64() float64 {
3642+ return math.Float64frombits(uint64(d.readInt64()))
3643+}
3644+
3645+func (d *decoder) readInt32() int32 {
3646+ b := d.readBytes(4)
3647+ return int32((uint32(b[0]) << 0) |
3648+ (uint32(b[1]) << 8) |
3649+ (uint32(b[2]) << 16) |
3650+ (uint32(b[3]) << 24))
3651+}
3652+
3653+func (d *decoder) readInt64() int64 {
3654+ b := d.readBytes(8)
3655+ return int64((uint64(b[0]) << 0) |
3656+ (uint64(b[1]) << 8) |
3657+ (uint64(b[2]) << 16) |
3658+ (uint64(b[3]) << 24) |
3659+ (uint64(b[4]) << 32) |
3660+ (uint64(b[5]) << 40) |
3661+ (uint64(b[6]) << 48) |
3662+ (uint64(b[7]) << 56))
3663+}
3664+
3665+func (d *decoder) readByte() byte {
3666+ i := d.i
3667+ d.i++
3668+ if d.i > len(d.in) {
3669+ corrupted()
3670+ }
3671+ return d.in[i]
3672+}
3673+
3674+func (d *decoder) readBytes(length int32) []byte {
3675+ start := d.i
3676+ d.i += int(length)
3677+ if d.i > len(d.in) {
3678+ corrupted()
3679+ }
3680+ return d.in[start : start+int(length)]
3681+}
3682
3683=== added file 'bson/encode.go'
3684--- bson/encode.go 1970-01-01 00:00:00 +0000
3685+++ bson/encode.go 2013-04-03 09:16:27 +0000
3686@@ -0,0 +1,430 @@
3687+// BSON library for Go
3688+//
3689+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
3690+//
3691+// All rights reserved.
3692+//
3693+// Redistribution and use in source and binary forms, with or without
3694+// modification, are permitted provided that the following conditions are met:
3695+//
3696+// 1. Redistributions of source code must retain the above copyright notice, this
3697+// list of conditions and the following disclaimer.
3698+// 2. Redistributions in binary form must reproduce the above copyright notice,
3699+// this list of conditions and the following disclaimer in the documentation
3700+// and/or other materials provided with the distribution.
3701+//
3702+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
3703+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
3704+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
3705+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
3706+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
3707+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
3708+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
3709+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3710+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3711+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3712+// gobson - BSON library for Go.
3713+
3714+package bson
3715+
3716+import (
3717+ "math"
3718+ "net/url"
3719+ "reflect"
3720+ "strconv"
3721+ "time"
3722+)
3723+
3724+// --------------------------------------------------------------------------
3725+// Some internal infrastructure.
3726+
3727+var (
3728+ typeBinary = reflect.TypeOf(Binary{})
3729+ typeObjectId = reflect.TypeOf(ObjectId(""))
3730+ typeSymbol = reflect.TypeOf(Symbol(""))
3731+ typeMongoTimestamp = reflect.TypeOf(MongoTimestamp(0))
3732+ typeOrderKey = reflect.TypeOf(MinKey)
3733+ typeDocElem = reflect.TypeOf(DocElem{})
3734+ typeRaw = reflect.TypeOf(Raw{})
3735+ typeURL = reflect.TypeOf(url.URL{})
3736+ typeTime = reflect.TypeOf(time.Time{})
3737+)
3738+
3739+const itoaCacheSize = 32
3740+
3741+var itoaCache []string
3742+
3743+func init() {
3744+ itoaCache = make([]string, itoaCacheSize)
3745+ for i := 0; i != itoaCacheSize; i++ {
3746+ itoaCache[i] = strconv.Itoa(i)
3747+ }
3748+}
3749+
3750+func itoa(i int) string {
3751+ if i < itoaCacheSize {
3752+ return itoaCache[i]
3753+ }
3754+ return strconv.Itoa(i)
3755+}
3756+
3757+// --------------------------------------------------------------------------
3758+// Marshaling of the document value itself.
3759+
3760+type encoder struct {
3761+ out []byte
3762+}
3763+
3764+func (e *encoder) addDoc(v reflect.Value) {
3765+ for {
3766+ if vi, ok := v.Interface().(Getter); ok {
3767+ getv, err := vi.GetBSON()
3768+ if err != nil {
3769+ panic(err)
3770+ }
3771+ v = reflect.ValueOf(getv)
3772+ continue
3773+ }
3774+ if v.Kind() == reflect.Ptr {
3775+ v = v.Elem()
3776+ continue
3777+ }
3778+ break
3779+ }
3780+
3781+ if v.Type() == typeRaw {
3782+ raw := v.Interface().(Raw)
3783+ if raw.Kind != 0x03 && raw.Kind != 0x00 {
3784+ panic("Attempted to unmarshal Raw kind " + strconv.Itoa(int(raw.Kind)) + " as a document")
3785+ }
3786+ e.addBytes(raw.Data...)
3787+ return
3788+ }
3789+
3790+ start := e.reserveInt32()
3791+
3792+ switch v.Kind() {
3793+ case reflect.Map:
3794+ e.addMap(v)
3795+ case reflect.Struct:
3796+ e.addStruct(v)
3797+ case reflect.Array, reflect.Slice:
3798+ e.addSlice(v)
3799+ default:
3800+ panic("Can't marshal " + v.Type().String() + " as a BSON document")
3801+ }
3802+
3803+ e.addBytes(0)
3804+ e.setInt32(start, int32(len(e.out)-start))
3805+}
3806+
3807+func (e *encoder) addMap(v reflect.Value) {
3808+ for _, k := range v.MapKeys() {
3809+ e.addElem(k.String(), v.MapIndex(k), false)
3810+ }
3811+}
3812+
3813+func (e *encoder) addStruct(v reflect.Value) {
3814+ sinfo, err := getStructInfo(v.Type())
3815+ if err != nil {
3816+ panic(err)
3817+ }
3818+ var value reflect.Value
3819+ for _, info := range sinfo.FieldsList {
3820+ if info.Inline == nil {
3821+ value = v.Field(info.Num)
3822+ } else {
3823+ value = v.FieldByIndex(info.Inline)
3824+ }
3825+ if info.OmitEmpty && isZero(value) {
3826+ continue
3827+ }
3828+ e.addElem(info.Key, value, info.MinSize)
3829+ }
3830+}
3831+
3832+func isZero(v reflect.Value) bool {
3833+ switch v.Kind() {
3834+ case reflect.String:
3835+ return len(v.String()) == 0
3836+ case reflect.Ptr, reflect.Interface:
3837+ return v.IsNil()
3838+ case reflect.Slice:
3839+ return v.Len() == 0
3840+ case reflect.Map:
3841+ return v.Len() == 0
3842+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
3843+ return v.Int() == 0
3844+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
3845+ return v.Uint() == 0
3846+ case reflect.Float32, reflect.Float64:
3847+ return v.Float() == 0
3848+ case reflect.Bool:
3849+ return !v.Bool()
3850+ case reflect.Struct:
3851+ if v.Type() == typeTime {
3852+ return v.Interface().(time.Time).IsZero()
3853+ }
3854+ }
3855+ return false
3856+}
3857+
3858+func (e *encoder) addSlice(v reflect.Value) {
3859+ if d, ok := v.Interface().(D); ok {
3860+ for _, elem := range d {
3861+ e.addElem(elem.Name, reflect.ValueOf(elem.Value), false)
3862+ }
3863+ } else if v.Type().Elem() == typeDocElem {
3864+ l := v.Len()
3865+ for i := 0; i < l; i++ {
3866+ elem := v.Index(i).Interface().(DocElem)
3867+ e.addElem(elem.Name, reflect.ValueOf(elem.Value), false)
3868+ }
3869+ } else {
3870+ l := v.Len()
3871+ for i := 0; i < l; i++ {
3872+ e.addElem(itoa(i), v.Index(i), false)
3873+ }
3874+ }
3875+}
3876+
3877+// --------------------------------------------------------------------------
3878+// Marshaling of elements in a document.
3879+
3880+func (e *encoder) addElemName(kind byte, name string) {
3881+ e.addBytes(kind)
3882+ e.addBytes([]byte(name)...)
3883+ e.addBytes(0)
3884+}
3885+
3886+func (e *encoder) addElem(name string, v reflect.Value, minSize bool) {
3887+
3888+ if !v.IsValid() {
3889+ e.addElemName('\x0A', name)
3890+ return
3891+ }
3892+
3893+ if getter, ok := v.Interface().(Getter); ok {
3894+ getv, err := getter.GetBSON()
3895+ if err != nil {
3896+ panic(err)
3897+ }
3898+ e.addElem(name, reflect.ValueOf(getv), minSize)
3899+ return
3900+ }
3901+
3902+ switch v.Kind() {
3903+
3904+ case reflect.Interface:
3905+ e.addElem(name, v.Elem(), minSize)
3906+
3907+ case reflect.Ptr:
3908+ e.addElem(name, v.Elem(), minSize)
3909+
3910+ case reflect.String:
3911+ s := v.String()
3912+ switch v.Type() {
3913+ case typeObjectId:
3914+ if len(s) != 12 {
3915+ panic("ObjectIDs must be exactly 12 bytes long (got " +
3916+ strconv.Itoa(len(s)) + ")")
3917+ }
3918+ e.addElemName('\x07', name)
3919+ e.addBytes([]byte(s)...)
3920+ case typeSymbol:
3921+ e.addElemName('\x0E', name)
3922+ e.addStr(s)
3923+ default:
3924+ e.addElemName('\x02', name)
3925+ e.addStr(s)
3926+ }
3927+
3928+ case reflect.Float32, reflect.Float64:
3929+ e.addElemName('\x01', name)
3930+ e.addInt64(int64(math.Float64bits(v.Float())))
3931+
3932+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
3933+ u := v.Uint()
3934+ if int64(u) < 0 {
3935+ panic("BSON has no uint64 type, and value is too large to fit correctly in an int64")
3936+ } else if u <= math.MaxInt32 && (minSize || v.Kind() <= reflect.Uint32) {
3937+ e.addElemName('\x10', name)
3938+ e.addInt32(int32(u))
3939+ } else {
3940+ e.addElemName('\x12', name)
3941+ e.addInt64(int64(u))
3942+ }
3943+
3944+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
3945+ if v.Type().Kind() <= reflect.Int32 {
3946+ e.addElemName('\x10', name)
3947+ e.addInt32(int32(v.Int()))
3948+ } else {
3949+ switch v.Type() {
3950+ case typeMongoTimestamp:
3951+ e.addElemName('\x11', name)
3952+ e.addInt64(v.Int())
3953+
3954+ case typeOrderKey:
3955+ if v.Int() == int64(MaxKey) {
3956+ e.addElemName('\x7F', name)
3957+ } else {
3958+ e.addElemName('\xFF', name)
3959+ }
3960+
3961+ default:
3962+ i := v.Int()
3963+ if minSize && i >= math.MinInt32 && i <= math.MaxInt32 {
3964+ // It fits into an int32, encode as such.
3965+ e.addElemName('\x10', name)
3966+ e.addInt32(int32(i))
3967+ } else {
3968+ e.addElemName('\x12', name)
3969+ e.addInt64(i)
3970+ }
3971+ }
3972+ }
3973+
3974+ case reflect.Bool:
3975+ e.addElemName('\x08', name)
3976+ if v.Bool() {
3977+ e.addBytes(1)
3978+ } else {
3979+ e.addBytes(0)
3980+ }
3981+
3982+ case reflect.Map:
3983+ e.addElemName('\x03', name)
3984+ e.addDoc(v)
3985+
3986+ case reflect.Slice:
3987+ vt := v.Type()
3988+ et := vt.Elem()
3989+ if et.Kind() == reflect.Uint8 {
3990+ e.addElemName('\x05', name)
3991+ e.addBinary('\x00', v.Bytes())
3992+ } else if et == typeDocElem {
3993+ e.addElemName('\x03', name)
3994+ e.addDoc(v)
3995+ } else {
3996+ e.addElemName('\x04', name)
3997+ e.addDoc(v)
3998+ }
3999+
4000+ case reflect.Array:
4001+ et := v.Type().Elem()
4002+ if et.Kind() == reflect.Uint8 {
4003+ e.addElemName('\x05', name)
4004+ e.addBinary('\x00', v.Slice(0, v.Len()).Interface().([]byte))
4005+ } else {
4006+ e.addElemName('\x04', name)
4007+ e.addDoc(v)
4008+ }
4009+
4010+ case reflect.Struct:
4011+ switch s := v.Interface().(type) {
4012+
4013+ case Raw:
4014+ kind := s.Kind
4015+ if kind == 0x00 {
4016+ kind = 0x03
4017+ }
4018+ e.addElemName(kind, name)
4019+ e.addBytes(s.Data...)
4020+
4021+ case Binary:
4022+ e.addElemName('\x05', name)
4023+ e.addBinary(s.Kind, s.Data)
4024+
4025+ case RegEx:
4026+ e.addElemName('\x0B', name)
4027+ e.addCStr(s.Pattern)
4028+ e.addCStr(s.Options)
4029+
4030+ case JavaScript:
4031+ if s.Scope == nil {
4032+ e.addElemName('\x0D', name)
4033+ e.addStr(s.Code)
4034+ } else {
4035+ e.addElemName('\x0F', name)
4036+ start := e.reserveInt32()
4037+ e.addStr(s.Code)
4038+ e.addDoc(reflect.ValueOf(s.Scope))
4039+ e.setInt32(start, int32(len(e.out)-start))
4040+ }
4041+
4042+ case time.Time:
4043+ // MongoDB handles timestamps as milliseconds.
4044+ e.addElemName('\x09', name)
4045+ e.addInt64(s.Unix() * 1000 + int64(s.Nanosecond() / 1e6))
4046+
4047+ case url.URL:
4048+ e.addElemName('\x02', name)
4049+ e.addStr(s.String())
4050+
4051+ case undefined:
4052+ e.addElemName('\x06', name)
4053+
4054+ default:
4055+ e.addElemName('\x03', name)
4056+ e.addDoc(v)
4057+ }
4058+
4059+ default:
4060+ panic("Can't marshal " + v.Type().String() + " in a BSON document")
4061+ }
4062+}
4063+
4064+// --------------------------------------------------------------------------
4065+// Marshaling of base types.
4066+
4067+func (e *encoder) addBinary(subtype byte, v []byte) {
4068+ if subtype == 0x02 {
4069+ // Wonder how that brilliant idea came to life. Obsolete, luckily.
4070+ e.addInt32(int32(len(v) + 4))
4071+ e.addBytes(subtype)
4072+ e.addInt32(int32(len(v)))
4073+ } else {
4074+ e.addInt32(int32(len(v)))
4075+ e.addBytes(subtype)
4076+ }
4077+ e.addBytes(v...)
4078+}
4079+
4080+func (e *encoder) addStr(v string) {
4081+ e.addInt32(int32(len(v) + 1))
4082+ e.addCStr(v)
4083+}
4084+
4085+func (e *encoder) addCStr(v string) {
4086+ e.addBytes([]byte(v)...)
4087+ e.addBytes(0)
4088+}
4089+
4090+func (e *encoder) reserveInt32() (pos int) {
4091+ pos = len(e.out)
4092+ e.addBytes(0, 0, 0, 0)
4093+ return pos
4094+}
4095+
4096+func (e *encoder) setInt32(pos int, v int32) {
4097+ e.out[pos+0] = byte(v)
4098+ e.out[pos+1] = byte(v >> 8)
4099+ e.out[pos+2] = byte(v >> 16)
4100+ e.out[pos+3] = byte(v >> 24)
4101+}
4102+
4103+func (e *encoder) addInt32(v int32) {
4104+ u := uint32(v)
4105+ e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24))
4106+}
4107+
4108+func (e *encoder) addInt64(v int64) {
4109+ u := uint64(v)
4110+ e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24),
4111+ byte(u>>32), byte(u>>40), byte(u>>48), byte(u>>56))
4112+}
4113+
4114+func (e *encoder) addBytes(v ...byte) {
4115+ e.out = append(e.out, v...)
4116+}
4117
4118=== added file 'cluster.go'
4119--- cluster.go 1970-01-01 00:00:00 +0000
4120+++ cluster.go 2013-04-03 09:16:27 +0000
4121@@ -0,0 +1,504 @@
4122+// mgo - MongoDB driver for Go
4123+//
4124+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4125+//
4126+// All rights reserved.
4127+//
4128+// Redistribution and use in source and binary forms, with or without
4129+// modification, are permitted provided that the following conditions are met:
4130+//
4131+// 1. Redistributions of source code must retain the above copyright notice, this
4132+// list of conditions and the following disclaimer.
4133+// 2. Redistributions in binary form must reproduce the above copyright notice,
4134+// this list of conditions and the following disclaimer in the documentation
4135+// and/or other materials provided with the distribution.
4136+//
4137+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
4138+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
4139+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
4140+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
4141+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
4142+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
4143+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
4144+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4145+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
4146+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4147+
4148+package mgo
4149+
4150+import (
4151+ "errors"
4152+ "sync"
4153+ "time"
4154+)
4155+
4156+// ---------------------------------------------------------------------------
4157+// Mongo cluster encapsulation.
4158+//
4159+// A cluster enables the communication with one or more servers participating
4160+// in a mongo cluster. This works with individual servers, a replica set,
4161+// a replica pair, one or multiple mongos routers, etc.
4162+
4163+type mongoCluster struct {
4164+ sync.RWMutex
4165+ serverSynced sync.Cond
4166+ userSeeds []string
4167+ dynaSeeds []string
4168+ servers mongoServers
4169+ masters mongoServers
4170+ references int
4171+ syncing bool
4172+ direct bool
4173+ cachedIndex map[string]bool
4174+ sync chan bool
4175+ dial dialer
4176+}
4177+
4178+func newCluster(userSeeds []string, direct bool, dial dialer) *mongoCluster {
4179+ cluster := &mongoCluster{
4180+ userSeeds: userSeeds,
4181+ references: 1,
4182+ direct: direct,
4183+ dial: dial,
4184+ }
4185+ cluster.serverSynced.L = cluster.RWMutex.RLocker()
4186+ cluster.sync = make(chan bool, 1)
4187+ go cluster.syncServersLoop()
4188+ return cluster
4189+}
4190+
4191+// Acquire increases the reference count for the cluster.
4192+func (cluster *mongoCluster) Acquire() {
4193+ cluster.Lock()
4194+ cluster.references++
4195+ debugf("Cluster %p acquired (refs=%d)", cluster, cluster.references)
4196+ cluster.Unlock()
4197+}
4198+
4199+// Release decreases the reference count for the cluster. Once
4200+// it reaches zero, all servers will be closed.
4201+func (cluster *mongoCluster) Release() {
4202+ cluster.Lock()
4203+ if cluster.references == 0 {
4204+ panic("cluster.Release() with references == 0")
4205+ }
4206+ cluster.references--
4207+ debugf("Cluster %p released (refs=%d)", cluster, cluster.references)
4208+ if cluster.references == 0 {
4209+ for _, server := range cluster.servers.Slice() {
4210+ server.Close()
4211+ }
4212+ // Wake up the sync loop so it can die.
4213+ cluster.syncServers()
4214+ }
4215+ cluster.Unlock()
4216+}
4217+
4218+func (cluster *mongoCluster) LiveServers() (servers []string) {
4219+ cluster.RLock()
4220+ for _, serv := range cluster.servers.Slice() {
4221+ servers = append(servers, serv.Addr)
4222+ }
4223+ cluster.RUnlock()
4224+ return servers
4225+}
4226+
4227+func (cluster *mongoCluster) removeServer(server *mongoServer) {
4228+ cluster.Lock()
4229+ cluster.masters.Remove(server)
4230+ other := cluster.servers.Remove(server)
4231+ cluster.Unlock()
4232+ if other != nil {
4233+ other.Close()
4234+ log("Removed server ", server.Addr, " from cluster.")
4235+ }
4236+ server.Close()
4237+}
4238+
4239+type isMasterResult struct {
4240+ IsMaster bool
4241+ Secondary bool
4242+ Primary string
4243+ Hosts []string
4244+ Passives []string
4245+}
4246+
4247+func (cluster *mongoCluster) syncServer(server *mongoServer) (hosts []string, err error) {
4248+ addr := server.Addr
4249+ log("SYNC Processing ", addr, "...")
4250+
4251+ var result isMasterResult
4252+ var tryerr error
4253+ for retry := 0; ; retry++ {
4254+ if retry == 3 {
4255+ return nil, tryerr
4256+ }
4257+
4258+ socket, err := server.AcquireSocket(0)
4259+ if err != nil {
4260+ tryerr = err
4261+ logf("SYNC Failed to get socket to %s: %v", addr, err)
4262+ continue
4263+ }
4264+
4265+ // Monotonic will let us talk to a slave and still hold the socket.
4266+ session := newSession(Monotonic, cluster, 10 * time.Second)
4267+ session.setSocket(socket)
4268+
4269+ // session holds the socket now.
4270+ socket.Release()
4271+
4272+ err = session.Run("ismaster", &result)
4273+ session.Close()
4274+ if err != nil {
4275+ tryerr = err
4276+ logf("SYNC Command 'ismaster' to %s failed: %v", addr, err)
4277+ continue
4278+ }
4279+ debugf("SYNC Result of 'ismaster' from %s: %#v", addr, result)
4280+ break
4281+ }
4282+
4283+ if result.IsMaster {
4284+ debugf("SYNC %s is a master.", addr)
4285+ // Made an incorrect assumption above, so fix stats.
4286+ stats.conn(-1, false)
4287+ server.SetMaster(true)
4288+ stats.conn(+1, true)
4289+ } else if result.Secondary {
4290+ debugf("SYNC %s is a slave.", addr)
4291+ } else if cluster.direct {
4292+ logf("SYNC %s in unknown state. Pretending it's a slave due to direct connection.", addr)
4293+ } else {
4294+ logf("SYNC %s is neither a master nor a slave.", addr)
4295+ // Made an incorrect assumption above, so fix stats.
4296+ stats.conn(-1, false)
4297+ return nil, errors.New(addr + " is not a master nor slave")
4298+ }
4299+
4300+ hosts = make([]string, 0, 1+len(result.Hosts)+len(result.Passives))
4301+ if result.Primary != "" {
4302+ // First in the list to speed up master discovery.
4303+ hosts = append(hosts, result.Primary)
4304+ }
4305+ hosts = append(hosts, result.Hosts...)
4306+ hosts = append(hosts, result.Passives...)
4307+
4308+ debugf("SYNC %s knows about the following peers: %#v", addr, hosts)
4309+ return hosts, nil
4310+}
4311+
4312+func (cluster *mongoCluster) mergeServer(server *mongoServer) {
4313+ cluster.Lock()
4314+ previous := cluster.servers.Search(server)
4315+ isMaster := server.IsMaster()
4316+ if previous == nil {
4317+ cluster.servers.Add(server)
4318+ if isMaster {
4319+ cluster.masters.Add(server)
4320+ log("SYNC Adding ", server.Addr, " to cluster as a master.")
4321+ } else {
4322+ log("SYNC Adding ", server.Addr, " to cluster as a slave.")
4323+ }
4324+ } else {
4325+ if isMaster != previous.IsMaster() {
4326+ if isMaster {
4327+ log("SYNC Server ", server.Addr, " is now a master.")
4328+ cluster.masters.Add(previous)
4329+ } else {
4330+ log("SYNC Server ", server.Addr, " is now a slave.")
4331+ cluster.masters.Remove(previous)
4332+ }
4333+ }
4334+ previous.Merge(server)
4335+ }
4336+ debugf("SYNC Broadcasting availability of server %s", server.Addr)
4337+ cluster.serverSynced.Broadcast()
4338+ cluster.Unlock()
4339+}
4340+
4341+func (cluster *mongoCluster) getKnownAddrs() []string {
4342+ cluster.RLock()
4343+ max := len(cluster.userSeeds) + len(cluster.dynaSeeds) + cluster.servers.Len()
4344+ seen := make(map[string]bool, max)
4345+ known := make([]string, 0, max)
4346+
4347+ add := func(addr string) {
4348+ if _, found := seen[addr]; !found {
4349+ seen[addr] = true
4350+ known = append(known, addr)
4351+ }
4352+ }
4353+
4354+ for _, addr := range cluster.userSeeds {
4355+ add(addr)
4356+ }
4357+ for _, addr := range cluster.dynaSeeds {
4358+ add(addr)
4359+ }
4360+ for _, serv := range cluster.servers.Slice() {
4361+ add(serv.Addr)
4362+ }
4363+ cluster.RUnlock()
4364+
4365+ return known
4366+}
4367+
4368+// syncServers injects a value into the cluster.sync channel to force
4369+// an iteration of the syncServersLoop function.
4370+func (cluster *mongoCluster) syncServers() {
4371+ select {
4372+ case cluster.sync <- true:
4373+ default:
4374+ }
4375+}
4376+
4377+// How long to wait for a checkup of the cluster topology if nothing
4378+// else kicks a synchronization before that.
4379+const syncServersDelay = 3 * time.Minute
4380+
4381+// syncServersLoop loops while the cluster is alive to keep its idea of
4382+// the server topology up-to-date. It must be called just once from
4383+// newCluster. The loop iterates once syncServersDelay has passed, or
4384+// if somebody injects a value into the cluster.sync channel to force a
4385+// synchronization. A loop iteration will contact all servers in
4386+// parallel, ask them about known peers and their own role within the
4387+// cluster, and then attempt to do the same with all the peers
4388+// retrieved.
4389+func (cluster *mongoCluster) syncServersLoop() {
4390+ for {
4391+ debugf("SYNC Cluster %p is starting a sync loop iteration.", cluster)
4392+
4393+ cluster.Lock()
4394+ if cluster.references == 0 {
4395+ cluster.Unlock()
4396+ break
4397+ }
4398+ cluster.references++ // Keep alive while syncing.
4399+ direct := cluster.direct
4400+ cluster.Unlock()
4401+
4402+ cluster.syncServersIteration(direct)
4403+
4404+ // We just synchronized, so consume any outstanding requests.
4405+ select {
4406+ case <-cluster.sync:
4407+ default:
4408+ }
4409+
4410+ cluster.Release()
4411+
4412+ // Hold off before allowing another sync. No point in
4413+ // burning CPU looking for down servers.
4414+ time.Sleep(5e8)
4415+
4416+ cluster.Lock()
4417+ if cluster.references == 0 {
4418+ cluster.Unlock()
4419+ break
4420+ }
4421+ // Poke all waiters so they have a chance to timeout or
4422+ // restart syncing if they wish to.
4423+ cluster.serverSynced.Broadcast()
4424+ // Check if we have to restart immediately either way.
4425+ restart := !direct && cluster.masters.Empty() || cluster.servers.Empty()
4426+ cluster.Unlock()
4427+
4428+ if restart {
4429+ log("SYNC No masters found. Will synchronize again.")
4430+ continue
4431+ }
4432+
4433+ debugf("SYNC Cluster %p waiting for next requested or scheduled sync.", cluster)
4434+
4435+ // Hold off until somebody explicitly requests a synchronization
4436+ // or it's time to check for a cluster topology change again.
4437+ select {
4438+ case <-cluster.sync:
4439+ case <-time.After(syncServersDelay):
4440+ }
4441+ }
4442+ debugf("SYNC Cluster %p is stopping its sync loop.", cluster)
4443+}
4444+
4445+func (cluster *mongoCluster) syncServersIteration(direct bool) {
4446+ log("SYNC Starting full topology synchronization...")
4447+
4448+ var wg sync.WaitGroup
4449+ var m sync.Mutex
4450+ mergePending := make(map[string]*mongoServer)
4451+ mergeRequested := make(map[string]bool)
4452+ seen := make(map[string]bool)
4453+ goodSync := false
4454+
4455+ var spawnSync func(addr string, byMaster bool)
4456+ spawnSync = func(addr string, byMaster bool) {
4457+ wg.Add(1)
4458+ go func() {
4459+ defer wg.Done()
4460+
4461+ server, err := newServer(addr, cluster.sync, cluster.dial)
4462+ if err != nil {
4463+ log("SYNC Failed to start sync of ", addr, ": ", err.Error())
4464+ return
4465+ }
4466+
4467+ m.Lock()
4468+ if byMaster {
4469+ if s, found := mergePending[server.ResolvedAddr]; found {
4470+ delete(mergePending, server.ResolvedAddr)
4471+ m.Unlock()
4472+ cluster.mergeServer(s)
4473+ return
4474+ }
4475+ mergeRequested[server.ResolvedAddr] = true
4476+ }
4477+ if seen[server.ResolvedAddr] {
4478+ m.Unlock()
4479+ return
4480+ }
4481+ seen[server.ResolvedAddr] = true
4482+ m.Unlock()
4483+
4484+ hosts, err := cluster.syncServer(server)
4485+ if err == nil {
4486+ isMaster := server.IsMaster()
4487+ if !direct {
4488+ for _, addr := range hosts {
4489+ spawnSync(addr, isMaster)
4490+ }
4491+ }
4492+
4493+ m.Lock()
4494+ merge := direct || isMaster
4495+ if mergeRequested[server.ResolvedAddr] {
4496+ merge = true
4497+ } else if !merge {
4498+ mergePending[server.ResolvedAddr] = server
4499+ }
4500+ if merge {
4501+ goodSync = true
4502+ }
4503+ m.Unlock()
4504+ if merge {
4505+ cluster.mergeServer(server)
4506+ }
4507+ } else {
4508+ server.Close()
4509+ }
4510+ }()
4511+ }
4512+
4513+ for _, addr := range cluster.getKnownAddrs() {
4514+ spawnSync(addr, false)
4515+ }
4516+ wg.Wait()
4517+
4518+ for _, server := range mergePending {
4519+ if goodSync {
4520+ cluster.removeServer(server)
4521+ } else {
4522+ server.Close()
4523+ }
4524+ }
4525+
4526+ cluster.Lock()
4527+ ml := cluster.masters.Len()
4528+ logf("SYNC Synchronization completed: %d master(s) and %d slave(s) alive.", ml, cluster.servers.Len()-ml)
4529+
4530+ // Update dynamic seeds, but only if we have any good servers. Otherwise,
4531+ // leave them alone for better chances of a successful sync in the future.
4532+ if goodSync {
4533+ dynaSeeds := make([]string, cluster.servers.Len())
4534+ for i, server := range cluster.servers.Slice() {
4535+ dynaSeeds[i] = server.Addr
4536+ }
4537+ cluster.dynaSeeds = dynaSeeds
4538+ debugf("SYNC New dynamic seeds: %#v\n", dynaSeeds)
4539+ }
4540+ cluster.Unlock()
4541+}
4542+
4543+var socketsPerServer = 4096
4544+
4545+// AcquireSocket returns a socket to a server in the cluster. If slaveOk is
4546+// true, it will attempt to return a socket to a slave server. If it is
4547+// false, the socket will necessarily be to a master server.
4548+func (cluster *mongoCluster) AcquireSocket(slaveOk bool, syncTimeout time.Duration) (s *mongoSocket, err error) {
4549+ var started time.Time
4550+ warnedLimit := false
4551+ for {
4552+ cluster.RLock()
4553+ for {
4554+ ml := cluster.masters.Len()
4555+ sl := cluster.servers.Len()
4556+ debugf("Cluster has %d known masters and %d known slaves.", ml, sl-ml)
4557+ if ml > 0 || slaveOk && sl > 0 {
4558+ break
4559+ }
4560+ if started.IsZero() {
4561+ started = time.Now() // Initialize after fast path above.
4562+ } else if syncTimeout != 0 && started.Before(time.Now().Add(-syncTimeout)) {
4563+ cluster.RUnlock()
4564+ return nil, errors.New("no reachable servers")
4565+ }
4566+ log("Waiting for servers to synchronize...")
4567+ cluster.syncServers()
4568+
4569+ // Remember: this will release and reacquire the lock.
4570+ cluster.serverSynced.Wait()
4571+ }
4572+
4573+ var server *mongoServer
4574+ if slaveOk {
4575+ server = cluster.servers.MostAvailable()
4576+ } else {
4577+ server = cluster.masters.MostAvailable()
4578+ }
4579+ cluster.RUnlock()
4580+
4581+ s, err = server.AcquireSocket(socketsPerServer)
4582+ if err == errSocketLimit {
4583+ if !warnedLimit {
4584+ log("WARNING: Per-server connection limit reached.")
4585+ }
4586+ time.Sleep(1e8)
4587+ continue
4588+ }
4589+ if err != nil {
4590+ cluster.removeServer(server)
4591+ cluster.syncServers()
4592+ continue
4593+ }
4594+ return s, nil
4595+ }
4596+ panic("unreached")
4597+}
4598+
4599+func (cluster *mongoCluster) CacheIndex(cacheKey string, exists bool) {
4600+ cluster.Lock()
4601+ if cluster.cachedIndex == nil {
4602+ cluster.cachedIndex = make(map[string]bool)
4603+ }
4604+ if exists {
4605+ cluster.cachedIndex[cacheKey] = true
4606+ } else {
4607+ delete(cluster.cachedIndex, cacheKey)
4608+ }
4609+ cluster.Unlock()
4610+}
4611+
4612+func (cluster *mongoCluster) HasCachedIndex(cacheKey string) (result bool) {
4613+ cluster.RLock()
4614+ if cluster.cachedIndex != nil {
4615+ result = cluster.cachedIndex[cacheKey]
4616+ }
4617+ cluster.RUnlock()
4618+ return
4619+}
4620+
4621+func (cluster *mongoCluster) ResetIndexCache() {
4622+ cluster.Lock()
4623+ cluster.cachedIndex = make(map[string]bool)
4624+ cluster.Unlock()
4625+}
4626
4627=== added file 'cluster_test.go'
4628--- cluster_test.go 1970-01-01 00:00:00 +0000
4629+++ cluster_test.go 2013-04-03 09:16:27 +0000
4630@@ -0,0 +1,1108 @@
4631+// mgo - MongoDB driver for Go
4632+//
4633+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4634+//
4635+// All rights reserved.
4636+//
4637+// Redistribution and use in source and binary forms, with or without
4638+// modification, are permitted provided that the following conditions are met:
4639+//
4640+// 1. Redistributions of source code must retain the above copyright notice, this
4641+// list of conditions and the following disclaimer.
4642+// 2. Redistributions in binary form must reproduce the above copyright notice,
4643+// this list of conditions and the following disclaimer in the documentation
4644+// and/or other materials provided with the distribution.
4645+//
4646+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
4647+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
4648+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
4649+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
4650+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
4651+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
4652+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
4653+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4654+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
4655+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4656+
4657+package mgo_test
4658+
4659+import (
4660+ "fmt"
4661+ "io"
4662+ . "launchpad.net/gocheck"
4663+ "labix.org/v2/mgo"
4664+ "labix.org/v2/mgo/bson"
4665+ "net"
4666+ "strings"
4667+ "time"
4668+)
4669+
4670+func (s *S) TestNewSession(c *C) {
4671+ session, err := mgo.Dial("localhost:40001")
4672+ c.Assert(err, IsNil)
4673+ defer session.Close()
4674+
4675+ // Do a dummy operation to wait for connection.
4676+ coll := session.DB("mydb").C("mycoll")
4677+ err = coll.Insert(M{"_id": 1})
4678+ c.Assert(err, IsNil)
4679+
4680+ // Tweak safety and query settings to ensure other has copied those.
4681+ session.SetSafe(nil)
4682+ session.SetBatch(-1)
4683+ other := session.New()
4684+ defer other.Close()
4685+ session.SetSafe(&mgo.Safe{})
4686+
4687+ // Clone was copied while session was unsafe, so no errors.
4688+ otherColl := other.DB("mydb").C("mycoll")
4689+ err = otherColl.Insert(M{"_id": 1})
4690+ c.Assert(err, IsNil)
4691+
4692+ // Original session was made safe again.
4693+ err = coll.Insert(M{"_id": 1})
4694+ c.Assert(err, NotNil)
4695+
4696+ // With New(), each session has its own socket now.
4697+ stats := mgo.GetStats()
4698+ c.Assert(stats.MasterConns, Equals, 2)
4699+ c.Assert(stats.SocketsInUse, Equals, 2)
4700+
4701+ // Ensure query parameters were cloned.
4702+ err = otherColl.Insert(M{"_id": 2})
4703+ c.Assert(err, IsNil)
4704+
4705+ // Ping the database to ensure the nonce has been received already.
4706+ c.Assert(other.Ping(), IsNil)
4707+
4708+ mgo.ResetStats()
4709+
4710+ iter := otherColl.Find(M{}).Iter()
4711+ c.Assert(err, IsNil)
4712+
4713+ m := M{}
4714+ ok := iter.Next(m)
4715+ c.Assert(ok, Equals, true)
4716+ err = iter.Err()
4717+ c.Assert(err, IsNil)
4718+
4719+ // If Batch(-1) is in effect, a single document must have been received.
4720+ stats = mgo.GetStats()
4721+ c.Assert(stats.ReceivedDocs, Equals, 1)
4722+}
4723+
4724+func (s *S) TestCloneSession(c *C) {
4725+ session, err := mgo.Dial("localhost:40001")
4726+ c.Assert(err, IsNil)
4727+ defer session.Close()
4728+
4729+ // Do a dummy operation to wait for connection.
4730+ coll := session.DB("mydb").C("mycoll")
4731+ err = coll.Insert(M{"_id": 1})
4732+ c.Assert(err, IsNil)
4733+
4734+ // Tweak safety and query settings to ensure clone is copying those.
4735+ session.SetSafe(nil)
4736+ session.SetBatch(-1)
4737+ clone := session.Clone()
4738+ defer clone.Close()
4739+ session.SetSafe(&mgo.Safe{})
4740+
4741+ // Clone was copied while session was unsafe, so no errors.
4742+ cloneColl := clone.DB("mydb").C("mycoll")
4743+ err = cloneColl.Insert(M{"_id": 1})
4744+ c.Assert(err, IsNil)
4745+
4746+ // Original session was made safe again.
4747+ err = coll.Insert(M{"_id": 1})
4748+ c.Assert(err, NotNil)
4749+
4750+ // With Clone(), same socket is shared between sessions now.
4751+ stats := mgo.GetStats()
4752+ c.Assert(stats.SocketsInUse, Equals, 1)
4753+ c.Assert(stats.SocketRefs, Equals, 2)
4754+
4755+ // Refreshing one of them should let the original socket go,
4756+ // while preserving the safety settings.
4757+ clone.Refresh()
4758+ err = cloneColl.Insert(M{"_id": 1})
4759+ c.Assert(err, IsNil)
4760+
4761+ // Must have used another connection now.
4762+ stats = mgo.GetStats()
4763+ c.Assert(stats.SocketsInUse, Equals, 2)
4764+ c.Assert(stats.SocketRefs, Equals, 2)
4765+
4766+ // Ensure query parameters were cloned.
4767+ err = cloneColl.Insert(M{"_id": 2})
4768+ c.Assert(err, IsNil)
4769+
4770+ // Ping the database to ensure the nonce has been received already.
4771+ c.Assert(clone.Ping(), IsNil)
4772+
4773+ mgo.ResetStats()
4774+
4775+ iter := cloneColl.Find(M{}).Iter()
4776+ c.Assert(err, IsNil)
4777+
4778+ m := M{}
4779+ ok := iter.Next(m)
4780+ c.Assert(ok, Equals, true)
4781+ err = iter.Err()
4782+ c.Assert(err, IsNil)
4783+
4784+ // If Batch(-1) is in effect, a single document must have been received.
4785+ stats = mgo.GetStats()
4786+ c.Assert(stats.ReceivedDocs, Equals, 1)
4787+}
4788+
4789+func (s *S) TestSetModeStrong(c *C) {
4790+ session, err := mgo.Dial("localhost:40012")
4791+ c.Assert(err, IsNil)
4792+ defer session.Close()
4793+
4794+ session.SetMode(mgo.Monotonic, false)
4795+ session.SetMode(mgo.Strong, false)
4796+
4797+ c.Assert(session.Mode(), Equals, mgo.Strong)
4798+
4799+ result := M{}
4800+ cmd := session.DB("admin").C("$cmd")
4801+ err = cmd.Find(M{"ismaster": 1}).One(&result)
4802+ c.Assert(err, IsNil)
4803+ c.Assert(result["ismaster"], Equals, true)
4804+
4805+ coll := session.DB("mydb").C("mycoll")
4806+ err = coll.Insert(M{"a": 1})
4807+ c.Assert(err, IsNil)
4808+
4809+ // Wait since the sync also uses sockets.
4810+ for len(session.LiveServers()) != 3 {
4811+ c.Log("Waiting for cluster sync to finish...")
4812+ time.Sleep(5e8)
4813+ }
4814+
4815+ stats := mgo.GetStats()
4816+ c.Assert(stats.MasterConns, Equals, 1)
4817+ c.Assert(stats.SlaveConns, Equals, 2)
4818+ c.Assert(stats.SocketsInUse, Equals, 1)
4819+
4820+ session.SetMode(mgo.Strong, true)
4821+
4822+ stats = mgo.GetStats()
4823+ c.Assert(stats.SocketsInUse, Equals, 0)
4824+}
4825+
4826+func (s *S) TestSetModeMonotonic(c *C) {
4827+ // Must necessarily connect to a slave, otherwise the
4828+ // master connection will be available first.
4829+ session, err := mgo.Dial("localhost:40012")
4830+ c.Assert(err, IsNil)
4831+ defer session.Close()
4832+
4833+ session.SetMode(mgo.Monotonic, false)
4834+
4835+ c.Assert(session.Mode(), Equals, mgo.Monotonic)
4836+
4837+ result := M{}
4838+ cmd := session.DB("admin").C("$cmd")
4839+ err = cmd.Find(M{"ismaster": 1}).One(&result)
4840+ c.Assert(err, IsNil)
4841+ c.Assert(result["ismaster"], Equals, false)
4842+
4843+ coll := session.DB("mydb").C("mycoll")
4844+ err = coll.Insert(M{"a": 1})
4845+ c.Assert(err, IsNil)
4846+
4847+ result = M{}
4848+ err = cmd.Find(M{"ismaster": 1}).One(&result)
4849+ c.Assert(err, IsNil)
4850+ c.Assert(result["ismaster"], Equals, true)
4851+
4852+ // Wait since the sync also uses sockets.
4853+ for len(session.LiveServers()) != 3 {
4854+ c.Log("Waiting for cluster sync to finish...")
4855+ time.Sleep(5e8)
4856+ }
4857+
4858+ stats := mgo.GetStats()
4859+ c.Assert(stats.MasterConns, Equals, 1)
4860+ c.Assert(stats.SlaveConns, Equals, 2)
4861+ c.Assert(stats.SocketsInUse, Equals, 2)
4862+
4863+ session.SetMode(mgo.Monotonic, true)
4864+
4865+ stats = mgo.GetStats()
4866+ c.Assert(stats.SocketsInUse, Equals, 0)
4867+}
4868+
4869+func (s *S) TestSetModeMonotonicAfterStrong(c *C) {
4870+ // Test that a strong session shifting to a monotonic
4871+ // one preserves the socket untouched.
4872+
4873+ session, err := mgo.Dial("localhost:40012")
4874+ c.Assert(err, IsNil)
4875+ defer session.Close()
4876+
4877+ // Insert something to force a connection to the master.
4878+ coll := session.DB("mydb").C("mycoll")
4879+ err = coll.Insert(M{"a": 1})
4880+ c.Assert(err, IsNil)
4881+
4882+ session.SetMode(mgo.Monotonic, false)
4883+
4884+ // Wait since the sync also uses sockets.
4885+ for len(session.LiveServers()) != 3 {
4886+ c.Log("Waiting for cluster sync to finish...")
4887+ time.Sleep(5e8)
4888+ }
4889+
4890+ // Master socket should still be reserved.
4891+ stats := mgo.GetStats()
4892+ c.Assert(stats.SocketsInUse, Equals, 1)
4893+
4894+ // Confirm it's the master even though it's Monotonic by now.
4895+ result := M{}
4896+ cmd := session.DB("admin").C("$cmd")
4897+ err = cmd.Find(M{"ismaster": 1}).One(&result)
4898+ c.Assert(err, IsNil)
4899+ c.Assert(result["ismaster"], Equals, true)
4900+}
4901+
4902+func (s *S) TestSetModeStrongAfterMonotonic(c *C) {
4903+ // Test that shifting from Monotonic to Strong while
4904+ // using a slave socket will keep the socket reserved
4905+ // until the master socket is necessary, so that no
4906+ // switch over occurs unless it's actually necessary.
4907+
4908+ // Must necessarily connect to a slave, otherwise the
4909+ // master connection will be available first.
4910+ session, err := mgo.Dial("localhost:40012")
4911+ c.Assert(err, IsNil)
4912+ defer session.Close()
4913+
4914+ session.SetMode(mgo.Monotonic, false)
4915+
4916+ // Ensure we're talking to a slave, and reserve the socket.
4917+ result := M{}
4918+ err = session.Run("ismaster", &result)
4919+ c.Assert(err, IsNil)
4920+ c.Assert(result["ismaster"], Equals, false)
4921+
4922+ // Switch to a Strong session.
4923+ session.SetMode(mgo.Strong, false)
4924+
4925+ // Wait since the sync also uses sockets.
4926+ for len(session.LiveServers()) != 3 {
4927+ c.Log("Waiting for cluster sync to finish...")
4928+ time.Sleep(5e8)
4929+ }
4930+
4931+ // Slave socket should still be reserved.
4932+ stats := mgo.GetStats()
4933+ c.Assert(stats.SocketsInUse, Equals, 1)
4934+
4935+ // But any operation will switch it to the master.
4936+ result = M{}
4937+ err = session.Run("ismaster", &result)
4938+ c.Assert(err, IsNil)
4939+ c.Assert(result["ismaster"], Equals, true)
4940+}
4941+
4942+func (s *S) TestSetModeMonotonicWriteOnIteration(c *C) {
4943+ // Must necessarily connect to a slave, otherwise the
4944+ // master connection will be available first.
4945+ session, err := mgo.Dial("localhost:40012")
4946+ c.Assert(err, IsNil)
4947+ defer session.Close()
4948+
4949+ session.SetMode(mgo.Monotonic, false)
4950+
4951+ c.Assert(session.Mode(), Equals, mgo.Monotonic)
4952+
4953+ coll1 := session.DB("mydb").C("mycoll1")
4954+ coll2 := session.DB("mydb").C("mycoll2")
4955+
4956+ ns := []int{40, 41, 42, 43, 44, 45, 46}
4957+ for _, n := range ns {
4958+ err := coll1.Insert(M{"n": n})
4959+ c.Assert(err, IsNil)
4960+ }
4961+
4962+ // Release master so we can grab a slave again.
4963+ session.Refresh()
4964+
4965+ // Wait until synchronization is done.
4966+ for {
4967+ n, err := coll1.Count()
4968+ c.Assert(err, IsNil)
4969+ if n == len(ns) {
4970+ break
4971+ }
4972+ }
4973+
4974+ iter := coll1.Find(nil).Batch(2).Iter()
4975+ i := 0
4976+ m := M{}
4977+ for iter.Next(&m) {
4978+ i++
4979+ if i > 3 {
4980+ err := coll2.Insert(M{"n": 47 + i})
4981+ c.Assert(err, IsNil)
4982+ }
4983+ }
4984+ c.Assert(i, Equals, len(ns))
4985+}
4986+
4987+func (s *S) TestSetModeEventual(c *C) {
4988+ // Must necessarily connect to a slave, otherwise the
4989+ // master connection will be available first.
4990+ session, err := mgo.Dial("localhost:40012")
4991+ c.Assert(err, IsNil)
4992+ defer session.Close()
4993+
4994+ session.SetMode(mgo.Eventual, false)
4995+
4996+ c.Assert(session.Mode(), Equals, mgo.Eventual)
4997+
4998+ result := M{}
4999+ err = session.Run("ismaster", &result)
5000+ c.Assert(err, IsNil)
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches