History

Move all server and controller packages into top-level

Harshavardhana committed 11 years ago

Commit d54488f1 parent d808c3680 files · +20431 −258Browse files

Unified · Split

80 files
  1. Makefile +3 −1
  2. buildscripts/checkdeps.sh +6 −6
  3. controller-main.go +2 −5
  4. controller-router.go +5 −6
  5. controller-rpc-auth.go +1 −1
  6. controller-rpc-donut.go +1 −1
  7. controller-rpc-server.go +1 −1
  8. controller-rpc-version.go +2 −3
  9. controller.go +3 −3
  10. controller_rpc_test.go +59 −64
  11. logger_test.go +5 −1
  12. minio_test.go +2 −11
  13. rpc-request.go +11 −11
  14. server-api-acl.go +1 −1
  15. server-api-bucket-handlers.go +14 −14
  16. server-api-contenttype.go +1 −1
  17. server-api-definitions.go +3 −3
  18. server-api-errors.go +10 −10
  19. server-api-generic-handlers.go +1 −5
  20. server-api-headers.go +3 −4
  21. server-api-httprange.go +1 −1
  22. server-api-logging-handlers.go +1 −1
  23. server-api-object-handlers.go +19 −19
  24. server-api-resources.go +1 −1
  25. server-api-response.go +1 −1
  26. server-api-signature.go +3 −1
  27. server-api-typed-errors.go +1 −1
  28. server-api-utils.go +1 −1
  29. server-api.go +10 −10
  30. server-main.go +4 −8
  31. server-router.go +11 −12
  32. server-signature-v4_test.go +3 −15
  33. server.go +5 −6
  34. server_donut_cache_test.go +9 −10
  35. server_donut_test.go +8 −9
  36. server_signature_v4_test.go +8 −9
  37. vendor.json +28 −0
  38. vendor/gopkg.in/mgo.v2/LICENSE +25 −0
  39. vendor/gopkg.in/mgo.v2/Makefile +5 −0
  40. vendor/gopkg.in/mgo.v2/README.md +4 −0
  41. vendor/gopkg.in/mgo.v2/auth.go +467 −0
  42. vendor/gopkg.in/mgo.v2/auth_test.go +1180 −0
  43. vendor/gopkg.in/mgo.v2/bson/LICENSE +25 −0
  44. vendor/gopkg.in/mgo.v2/bson/bson.go +705 −0
  45. vendor/gopkg.in/mgo.v2/bson/bson_test.go +1605 −0
  46. vendor/gopkg.in/mgo.v2/bson/decode.go +825 −0
  47. vendor/gopkg.in/mgo.v2/bson/encode.go +503 −0
  48. vendor/gopkg.in/mgo.v2/bulk.go +71 −0
  49. vendor/gopkg.in/mgo.v2/bulk_test.go +131 −0
  50. vendor/gopkg.in/mgo.v2/cluster.go +632 −0
  51. vendor/gopkg.in/mgo.v2/cluster_test.go +1657 −0
  52. vendor/gopkg.in/mgo.v2/doc.go +31 −0
  53. vendor/gopkg.in/mgo.v2/export_test.go +33 −0
  54. vendor/gopkg.in/mgo.v2/gridfs.go +755 −0
  55. vendor/gopkg.in/mgo.v2/gridfs_test.go +708 −0
  56. vendor/gopkg.in/mgo.v2/internal/sasl/sasl.c +77 −0
  57. vendor/gopkg.in/mgo.v2/internal/sasl/sasl.go +138 −0
  58. vendor/gopkg.in/mgo.v2/internal/sasl/sasl_windows.c +118 −0
  59. vendor/gopkg.in/mgo.v2/internal/sasl/sasl_windows.go +140 −0
  60. vendor/gopkg.in/mgo.v2/internal/sasl/sasl_windows.h +7 −0
  61. vendor/gopkg.in/mgo.v2/internal/sasl/sspi_windows.c +96 −0
  62. vendor/gopkg.in/mgo.v2/internal/sasl/sspi_windows.h +70 −0
  63. vendor/gopkg.in/mgo.v2/internal/scram/scram.go +266 −0
  64. vendor/gopkg.in/mgo.v2/internal/scram/scram_test.go +67 −0
  65. vendor/gopkg.in/mgo.v2/log.go +133 −0
  66. vendor/gopkg.in/mgo.v2/queue.go +91 −0
  67. vendor/gopkg.in/mgo.v2/queue_test.go +101 −0
  68. vendor/gopkg.in/mgo.v2/raceoff.go +5 −0
  69. vendor/gopkg.in/mgo.v2/raceon.go +5 −0
  70. vendor/gopkg.in/mgo.v2/saslimpl.go +11 −0
  71. vendor/gopkg.in/mgo.v2/saslstub.go +11 −0
  72. vendor/gopkg.in/mgo.v2/server.go +448 −0
  73. vendor/gopkg.in/mgo.v2/session.go +4224 −0
  74. vendor/gopkg.in/mgo.v2/session_test.go +3704 −0
  75. vendor/gopkg.in/mgo.v2/socket.go +677 −0
  76. vendor/gopkg.in/mgo.v2/stats.go +147 −0
  77. vendor/gopkg.in/mgo.v2/suite_test.go +254 −0
  78. vendor/gopkg.in/mgo.v2/syscall_test.go +15 −0
  79. vendor/gopkg.in/mgo.v2/syscall_windows_test.go +11 −0
  80. version_test.go +5 −1
Makefile+3−1
@@ -34,7 +34,8 @@ lint: · 33 unmodified lines
3434
3535 cyclo:
3636 @echo "Running $@:"
37− @GO15VENDOREXPERIMENT=1 gocyclo -over 25 .
37+ @GO15VENDOREXPERIMENT=1 gocyclo -over 25 *.go
38+ @GO15VENDOREXPERIMENT=1 gocyclo -over 25 pkg
3839
3940 build: getdeps verifiers
4041 @echo "Installing minio:"
@@ -42,6 +43,7 @@ build: getdeps verifiers · 1 unmodified line
4243
4344 test: build
4445 @echo "Running all testing:"
46+ @GO15VENDOREXPERIMENT=1 go test $(GOFLAGS) .
4547 @GO15VENDOREXPERIMENT=1 go test $(GOFLAGS) github.com/minio/minio/pkg...
4648
4749 gomake-all: build
buildscripts/checkdeps.sh+6−6
@@ -21,7 +21,7 @@ _init() { · 20 unmodified lines
2121
2222 ## Minimum required versions for build dependencies
2323 GCC_VERSION="4.0"
24− CLANG_VERSION="3.5"
24+ LLVM_VERSION="7.0.0"
2525 YASM_VERSION="1.2.0"
2626 GIT_VERSION="1.0"
2727 GO_VERSION="1.5.1"
@@ -173,7 +173,7 @@ is_supported_arch() { · 145 unmodified lines
173173 check_deps() {
174174 check_version "$(env go version 2>/dev/null | sed 's/^.* go\([0-9.]*\).*$/\1/')" "${GO_VERSION}"
175175 if [ $? -ge 2 ]; then
176− MISSING="${MISSING} golang(1.5)"
176+ MISSING="${MISSING} golang(${GO_VERSION})"
177177 fi
178178
179179 check_version "$(env git --version 2>/dev/null | sed -e 's/^.* \([0-9.\].*\).*$/\1/' -e 's/^\([0-9.\]*\).*/\1/g')" "${GIT_VERSION}"
@@ -185,13 +185,13 @@ check_deps() { · 5 unmodified lines
185185 "Linux")
186186 check_version "$(env gcc --version 2>/dev/null | sed 's/^.* \([0-9.]*\).*$/\1/' | head -1)" "${GCC_VERSION}"
187187 if [ $? -ge 2 ]; then
188− MISSING="${MISSING} build-essential"
188+ MISSING="${MISSING} build-essential(${GCC_VERSION})"
189189 fi
190190 ;;
191191 "Darwin")
192− check_version "$(env gcc --version 2>/dev/null | sed 's/^.* \([0-9.]*\).*$/\1/' | head -1)" "${CLANG_VERSION}"
192+ check_version "$(env gcc --version 2>/dev/null | awk '{print $4}' | head -1)" "${LLVM_VERSION}"
193193 if [ $? -ge 2 ]; then
194− MISSING="${MISSING} xcode-cli"
194+ MISSING="${MISSING} xcode-cli(${LLVM_VERSION})"
195195 fi
196196 ;;
197197 "*")
@@ -200,7 +200,7 @@ check_deps() { · 2 unmodified lines
200200
201201 check_version "$(env yasm --version 2>/dev/null | sed 's/^.* \([0-9.]*\).*$/\1/' | head -1)" "${YASM_VERSION}"
202202 if [ $? -ge 2 ]; then
203− MISSING="${MISSING} yasm(1.2.0)"
203+ MISSING="${MISSING} yasm(${YASM_VERSION})"
204204 fi
205205
206206 }
controller-main.go+2−5
@@ -16,10 +16,7 @@ · 15 unmodified lines
1616
1717 package main
1818
19−import (
20− "github.com/minio/cli"
21− "github.com/minio/minio/pkg/controller"
22−)
19+import "github.com/minio/cli"
2320
2421 var controllerCmd = cli.Command{
2522 Name: "controller",
@@ -43,6 +40,6 @@ func controllerMain(c *cli.Context) { · 17 unmodified lines
4340 cli.ShowCommandHelpAndExit(c, "controller", 1)
4441 }
4542
46− err := controller.Start()
43+ err := StartController()
4744 errorIf(err.Trace(), "Failed to start minio controller.", nil)
4845 }
controller-router.gorenamed from pkg/controller/router.go+5−6
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package controller
17+package main
1818
1919 import (
2020 "net/http"
@@ -22,17 +22,16 @@ import ( · 1 unmodified line
2222 router "github.com/gorilla/mux"
2323 jsonrpc "github.com/gorilla/rpc/v2"
2424 "github.com/gorilla/rpc/v2/json"
25− "github.com/minio/minio/pkg/controller/rpc"
2625 )
2726
2827 // getRPCHandler rpc handler
2928 func getRPCHandler() http.Handler {
3029 s := jsonrpc.NewServer()
3130 s.RegisterCodec(json.NewCodec(), "application/json")
32− s.RegisterService(new(rpc.VersionService), "Version")
33− s.RegisterService(new(rpc.DonutService), "Donut")
34− s.RegisterService(new(rpc.AuthService), "Auth")
35− s.RegisterService(new(rpc.ServerService), "Server")
31+ s.RegisterService(new(VersionService), "Version")
32+ s.RegisterService(new(DonutService), "Donut")
33+ s.RegisterService(new(AuthService), "Auth")
34+ s.RegisterService(new(ServerService), "Server")
3635 // Add new RPC services here
3736 return registerRPC(router.NewRouter(), s)
3837 }
controller-rpc-auth.gorenamed from pkg/controller/rpc/auth.go+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package rpc
17+package main
1818
1919 import (
2020 "errors"
controller-rpc-donut.gorenamed from pkg/controller/rpc/donut.go+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package rpc
17+package main
1818
1919 import (
2020 "net/http"
controller-rpc-server.gorenamed from pkg/controller/rpc/server.go+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package rpc
17+package main
1818
1919 import (
2020 "net/http"
controller-rpc-version.gorenamed from pkg/controller/rpc/version.go+2−3
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package rpc
17+package main
1818
1919 import (
2020 "net/http"
@@ -38,8 +38,7 @@ type VersionReply struct { · 17 unmodified lines
3838 // Get version
3939 func (v *VersionService) Get(r *http.Request, args *VersionArgs, reply *VersionReply) error {
4040 reply.Version = "0.0.1"
41− //TODO: Better approach needed here to pass global states like version. --ab.
42− // reply.BuildDate = version.Version
41+ reply.BuildDate = minioVersion
4342 reply.Architecture = runtime.GOARCH
4443 reply.OperatingSystem = runtime.GOOS
4544 return nil
controller.gorenamed from pkg/controller/server.go+3−3
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package controller
17+package main
1818
1919 import (
2020 "fmt"
@@ -54,8 +54,8 @@ func getRPCServer(rpcHandler http.Handler) (*http.Server, *probe.Error) { · 33 unmodified lines
5454 return httpServer, nil
5555 }
5656
57−// Start starts a controller
58−func Start() *probe.Error {
57+// StartController starts a minio controller
58+func StartController() *probe.Error {
5959 rpcServer, err := getRPCServer(getRPCHandler())
6060 if err != nil {
6161 return err.Trace()
controller_rpc_test.gorenamed from pkg/controller/rpc_test.go+59−64
@@ -14,31 +14,26 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package controller
17+package main
1818
1919 import (
2020 "io/ioutil"
2121 "net/http"
2222 "net/http/httptest"
2323 "os"
24− "testing"
2524
26− jsonrpc "github.com/gorilla/rpc/v2/json"
25+ "github.com/gorilla/rpc/v2/json"
2726 "github.com/minio/minio/pkg/auth"
28− "github.com/minio/minio/pkg/controller/rpc"
2927 . "gopkg.in/check.v1"
3028 )
3129
32−// Hook up gocheck into the "go test" runner.
33−func Test(t *testing.T) { TestingT(t) }
30+type ControllerRPCSuite struct{}
3431
35−type MySuite struct{}
36−
37−var _ = Suite(&MySuite{})
32+var _ = Suite(&ControllerRPCSuite{})
3833
3934 var testRPCServer *httptest.Server
4035
41−func (s *MySuite) SetUpSuite(c *C) {
36+func (s *ControllerRPCSuite) SetUpSuite(c *C) {
4237 root, err := ioutil.TempDir(os.TempDir(), "api-")
4338 c.Assert(err, IsNil)
4439 auth.SetAuthConfigPath(root)
@@ -46,136 +41,136 @@ func (s *MySuite) SetUpSuite(c *C) { · 1 unmodified line
4641 testRPCServer = httptest.NewServer(getRPCHandler())
4742 }
4843
49−func (s *MySuite) TearDownSuite(c *C) {
44+func (s *ControllerRPCSuite) TearDownSuite(c *C) {
5045 testRPCServer.Close()
5146 }
5247
53−func (s *MySuite) TestMemStats(c *C) {
54− op := rpc.Operation{
48+func (s *ControllerRPCSuite) TestMemStats(c *C) {
49+ op := rpcOperation{
5550 Method: "Server.MemStats",
56− Request: rpc.ServerArgs{},
51+ Request: ServerArgs{},
5752 }
58− req, err := rpc.NewRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
53+ req, err := newRPCRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
5954 c.Assert(err, IsNil)
6055 c.Assert(req.Get("Content-Type"), Equals, "application/json")
6156 resp, err := req.Do()
6257 c.Assert(err, IsNil)
6358 c.Assert(resp.StatusCode, Equals, http.StatusOK)
6459
65− var reply rpc.MemStatsReply
66− c.Assert(jsonrpc.DecodeClientResponse(resp.Body, &reply), IsNil)
60+ var reply MemStatsReply
61+ c.Assert(json.DecodeClientResponse(resp.Body, &reply), IsNil)
6762 resp.Body.Close()
68− c.Assert(reply, Not(DeepEquals), rpc.MemStatsReply{})
63+ c.Assert(reply, Not(DeepEquals), MemStatsReply{})
6964 }
7065
71−func (s *MySuite) TestSysInfo(c *C) {
72− op := rpc.Operation{
66+func (s *ControllerRPCSuite) TestSysInfo(c *C) {
67+ op := rpcOperation{
7368 Method: "Server.SysInfo",
74− Request: rpc.ServerArgs{},
69+ Request: ServerArgs{},
7570 }
76− req, err := rpc.NewRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
71+ req, err := newRPCRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
7772 c.Assert(err, IsNil)
7873 c.Assert(req.Get("Content-Type"), Equals, "application/json")
7974 resp, err := req.Do()
8075 c.Assert(err, IsNil)
8176 c.Assert(resp.StatusCode, Equals, http.StatusOK)
8277
83− var reply rpc.SysInfoReply
84− c.Assert(jsonrpc.DecodeClientResponse(resp.Body, &reply), IsNil)
78+ var reply SysInfoReply
79+ c.Assert(json.DecodeClientResponse(resp.Body, &reply), IsNil)
8580 resp.Body.Close()
86− c.Assert(reply, Not(DeepEquals), rpc.SysInfoReply{})
81+ c.Assert(reply, Not(DeepEquals), SysInfoReply{})
8782 }
8883
89−func (s *MySuite) TestServerList(c *C) {
90− op := rpc.Operation{
84+func (s *ControllerRPCSuite) TestServerList(c *C) {
85+ op := rpcOperation{
9186 Method: "Server.List",
92− Request: rpc.ServerArgs{},
87+ Request: ServerArgs{},
9388 }
94− req, err := rpc.NewRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
89+ req, err := newRPCRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
9590 c.Assert(err, IsNil)
9691 c.Assert(req.Get("Content-Type"), Equals, "application/json")
9792 resp, err := req.Do()
9893 c.Assert(err, IsNil)
9994 c.Assert(resp.StatusCode, Equals, http.StatusOK)
10095
101− var reply rpc.ServerListReply
102− c.Assert(jsonrpc.DecodeClientResponse(resp.Body, &reply), IsNil)
96+ var reply ServerListReply
97+ c.Assert(json.DecodeClientResponse(resp.Body, &reply), IsNil)
10398 resp.Body.Close()
104− c.Assert(reply, Not(DeepEquals), rpc.ServerListReply{})
99+ c.Assert(reply, Not(DeepEquals), ServerListReply{})
105100 }
106101
107−func (s *MySuite) TestServerAdd(c *C) {
108− op := rpc.Operation{
102+func (s *ControllerRPCSuite) TestServerAdd(c *C) {
103+ op := rpcOperation{
109104 Method: "Server.Add",
110− Request: rpc.ServerArgs{MinioServers: []rpc.MinioServer{}},
105+ Request: ServerArgs{MinioServers: []MinioServer{}},
111106 }
112− req, err := rpc.NewRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
107+ req, err := newRPCRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
113108 c.Assert(err, IsNil)
114109 c.Assert(req.Get("Content-Type"), Equals, "application/json")
115110 resp, err := req.Do()
116111 c.Assert(err, IsNil)
117112 c.Assert(resp.StatusCode, Equals, http.StatusOK)
118113
119− var reply rpc.ServerAddReply
120− c.Assert(jsonrpc.DecodeClientResponse(resp.Body, &reply), IsNil)
114+ var reply ServerAddReply
115+ c.Assert(json.DecodeClientResponse(resp.Body, &reply), IsNil)
121116 resp.Body.Close()
122− c.Assert(reply, Not(DeepEquals), rpc.ServerAddReply{ServersAdded: []rpc.MinioServer{}})
117+ c.Assert(reply, Not(DeepEquals), ServerAddReply{ServersAdded: []MinioServer{}})
123118 }
124119
125−func (s *MySuite) TestAuth(c *C) {
126− op := rpc.Operation{
120+func (s *ControllerRPCSuite) TestAuth(c *C) {
121+ op := rpcOperation{
127122 Method: "Auth.Generate",
128− Request: rpc.AuthArgs{User: "newuser"},
123+ Request: AuthArgs{User: "newuser"},
129124 }
130− req, err := rpc.NewRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
125+ req, err := newRPCRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
131126 c.Assert(err, IsNil)
132127 c.Assert(req.Get("Content-Type"), Equals, "application/json")
133128 resp, err := req.Do()
134129 c.Assert(err, IsNil)
135130 c.Assert(resp.StatusCode, Equals, http.StatusOK)
136131
137− var reply rpc.AuthReply
138− c.Assert(jsonrpc.DecodeClientResponse(resp.Body, &reply), IsNil)
132+ var reply AuthReply
133+ c.Assert(json.DecodeClientResponse(resp.Body, &reply), IsNil)
139134 resp.Body.Close()
140− c.Assert(reply, Not(DeepEquals), rpc.AuthReply{})
135+ c.Assert(reply, Not(DeepEquals), AuthReply{})
141136 c.Assert(len(reply.AccessKeyID), Equals, 20)
142137 c.Assert(len(reply.SecretAccessKey), Equals, 40)
143138 c.Assert(len(reply.Name), Not(Equals), 0)
144139
145− op = rpc.Operation{
140+ op = rpcOperation{
146141 Method: "Auth.Fetch",
147− Request: rpc.AuthArgs{User: "newuser"},
142+ Request: AuthArgs{User: "newuser"},
148143 }
149− req, err = rpc.NewRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
144+ req, err = newRPCRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
150145 c.Assert(err, IsNil)
151146 c.Assert(req.Get("Content-Type"), Equals, "application/json")
152147 resp, err = req.Do()
153148 c.Assert(err, IsNil)
154149 c.Assert(resp.StatusCode, Equals, http.StatusOK)
155150
156− var newReply rpc.AuthReply
157− c.Assert(jsonrpc.DecodeClientResponse(resp.Body, &newReply), IsNil)
151+ var newReply AuthReply
152+ c.Assert(json.DecodeClientResponse(resp.Body, &newReply), IsNil)
158153 resp.Body.Close()
159− c.Assert(newReply, Not(DeepEquals), rpc.AuthReply{})
154+ c.Assert(newReply, Not(DeepEquals), AuthReply{})
160155 c.Assert(reply.AccessKeyID, Equals, newReply.AccessKeyID)
161156 c.Assert(reply.SecretAccessKey, Equals, newReply.SecretAccessKey)
162157 c.Assert(len(reply.Name), Not(Equals), 0)
163158
164− op = rpc.Operation{
159+ op = rpcOperation{
165160 Method: "Auth.Reset",
166− Request: rpc.AuthArgs{User: "newuser"},
161+ Request: AuthArgs{User: "newuser"},
167162 }
168− req, err = rpc.NewRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
163+ req, err = newRPCRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
169164 c.Assert(err, IsNil)
170165 c.Assert(req.Get("Content-Type"), Equals, "application/json")
171166 resp, err = req.Do()
172167 c.Assert(err, IsNil)
173168 c.Assert(resp.StatusCode, Equals, http.StatusOK)
174169
175− var resetReply rpc.AuthReply
176− c.Assert(jsonrpc.DecodeClientResponse(resp.Body, &resetReply), IsNil)
170+ var resetReply AuthReply
171+ c.Assert(json.DecodeClientResponse(resp.Body, &resetReply), IsNil)
177172 resp.Body.Close()
178− c.Assert(newReply, Not(DeepEquals), rpc.AuthReply{})
173+ c.Assert(newReply, Not(DeepEquals), AuthReply{})
179174 c.Assert(reply.AccessKeyID, Not(Equals), resetReply.AccessKeyID)
180175 c.Assert(reply.SecretAccessKey, Not(Equals), resetReply.SecretAccessKey)
181176 c.Assert(len(reply.Name), Not(Equals), 0)
@@ -183,11 +178,11 @@ func (s *MySuite) TestAuth(c *C) { · 1 unmodified line
183178 // these operations should fail
184179
185180 /// generating access for existing user fails
186− op = rpc.Operation{
181+ op = rpcOperation{
187182 Method: "Auth.Generate",
188− Request: rpc.AuthArgs{User: "newuser"},
183+ Request: AuthArgs{User: "newuser"},
189184 }
190− req, err = rpc.NewRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
185+ req, err = newRPCRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
191186 c.Assert(err, IsNil)
192187 c.Assert(req.Get("Content-Type"), Equals, "application/json")
193188 resp, err = req.Do()
@@ -195,11 +190,11 @@ func (s *MySuite) TestAuth(c *C) { · 1 unmodified line
195190 c.Assert(resp.StatusCode, Equals, http.StatusBadRequest)
196191
197192 /// null user provided invalid
198− op = rpc.Operation{
193+ op = rpcOperation{
199194 Method: "Auth.Generate",
200− Request: rpc.AuthArgs{User: ""},
195+ Request: AuthArgs{User: ""},
201196 }
202− req, err = rpc.NewRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
197+ req, err = newRPCRequest(testRPCServer.URL+"/rpc", op, http.DefaultTransport)
203198 c.Assert(err, IsNil)
204199 c.Assert(req.Get("Content-Type"), Equals, "application/json")
205200 resp, err = req.Do()
logger_test.go+5−1
@@ -27,7 +27,11 @@ import ( · 26 unmodified lines
2727 . "gopkg.in/check.v1"
2828 )
2929
30−func (s *TestSuite) TestLogger(c *C) {
30+type LoggerSuite struct{}
31+
32+var _ = Suite(&LoggerSuite{})
33+
34+func (s *LoggerSuite) TestLogger(c *C) {
3135 var buffer bytes.Buffer
3236 var fields logrus.Fields
3337 log.Out = &buffer
minio_test.go+2−11
@@ -1,5 +1,5 @@
11 /*
2− * Minio Cloud Storage (C) 2015 Minio, Inc.
2+ * Minio Cloud Storage, (C) 2015 Minio, Inc.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
@@ -22,14 +22,5 @@ import ( · 16 unmodified lines
2222 . "gopkg.in/check.v1"
2323 )
2424
25+// Hook up gocheck into the "go test" runner.
2526 func Test(t *testing.T) { TestingT(t) }
26−
27−type TestSuite struct{}
28−
29−var _ = Suite(&TestSuite{})
30−
31−func (s *TestSuite) SetUpSuite(c *C) {
32−}
33−
34−func (s *TestSuite) TearDownSuite(c *C) {
35−}
rpc-request.gorenamed from pkg/controller/rpc/request.go+11−11
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package rpc
17+package main
1818
1919 import (
2020 "bytes"
@@ -24,20 +24,20 @@ import ( · 3 unmodified lines
2424 "github.com/minio/minio/pkg/probe"
2525 )
2626
27−// Operation RPC operation
28−type Operation struct {
27+// rpcOperation RPC operation
28+type rpcOperation struct {
2929 Method string
3030 Request interface{}
3131 }
3232
33−// Request rpc client request
34−type Request struct {
33+// rpcRequest rpc client request
34+type rpcRequest struct {
3535 req *http.Request
3636 transport http.RoundTripper
3737 }
3838
39−// NewRequest initiate a new client RPC request
40−func NewRequest(url string, op Operation, transport http.RoundTripper) (*Request, *probe.Error) {
39+// newRPCRequest initiate a new client RPC request
40+func newRPCRequest(url string, op rpcOperation, transport http.RoundTripper) (*rpcRequest, *probe.Error) {
4141 params, err := json.EncodeClientRequest(op.Method, op.Request)
4242 if err != nil {
4343 return nil, probe.NewError(err)
@@ -46,7 +46,7 @@ func NewRequest(url string, op Operation, transport http.RoundTripper) (*Request · 2 unmodified lines
4646 if err != nil {
4747 return nil, probe.NewError(err)
4848 }
49− rpcReq := &Request{}
49+ rpcReq := &rpcRequest{}
5050 rpcReq.req = req
5151 rpcReq.req.Header.Set("Content-Type", "application/json")
5252 if transport == nil {
@@ -57,7 +57,7 @@ func NewRequest(url string, op Operation, transport http.RoundTripper) (*Request · 4 unmodified lines
5757 }
5858
5959 // Do - make a http connection
60−func (r Request) Do() (*http.Response, *probe.Error) {
60+func (r rpcRequest) Do() (*http.Response, *probe.Error) {
6161 resp, err := r.transport.RoundTrip(r.req)
6262 if err != nil {
6363 if err, ok := probe.UnwrapError(err); ok {
@@ -69,11 +69,11 @@ func (r Request) Do() (*http.Response, *probe.Error) { · 5 unmodified lines
6969 }
7070
7171 // Get - get value of requested header
72−func (r Request) Get(key string) string {
72+func (r rpcRequest) Get(key string) string {
7373 return r.req.Header.Get(key)
7474 }
7575
7676 // Set - set value of a header key
77−func (r *Request) Set(key, value string) {
77+func (r *rpcRequest) Set(key, value string) {
7878 r.req.Header.Set(key, value)
7979 }
server-api-acl.gorenamed from pkg/server/api/acl.go+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import "net/http"
2020
server-api-bucket-handlers.gorenamed from pkg/server/api/bucket-handlers.go+14−14
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import (
2020 "net/http"
@@ -24,7 +24,7 @@ import ( · 3 unmodified lines
2424 "github.com/minio/minio/pkg/probe"
2525 )
2626
27−func (api Minio) isValidOp(w http.ResponseWriter, req *http.Request, acceptsContentType contentType) bool {
27+func (api MinioAPI) isValidOp(w http.ResponseWriter, req *http.Request, acceptsContentType contentType) bool {
2828 vars := mux.Vars(req)
2929 bucket := vars["bucket"]
3030
@@ -67,10 +67,10 @@ func (api Minio) isValidOp(w http.ResponseWriter, req *http.Request, acceptsCont · 36 unmodified lines
6767 // using the Initiate Multipart Upload request, but has not yet been completed or aborted.
6868 // This operation returns at most 1,000 multipart uploads in the response.
6969 //
70−func (api Minio) ListMultipartUploadsHandler(w http.ResponseWriter, req *http.Request) {
70+func (api MinioAPI) ListMultipartUploadsHandler(w http.ResponseWriter, req *http.Request) {
7171 // Ticket master block
7272 {
73− op := Operation{}
73+ op := APIOperation{}
7474 op.ProceedCh = make(chan struct{})
7575 api.OP <- op
7676 // block until ticket master gives us a go
@@ -133,10 +133,10 @@ func (api Minio) ListMultipartUploadsHandler(w http.ResponseWriter, req *http.Re · 56 unmodified lines
133133 // of the objects in a bucket. You can use the request parameters as selection
134134 // criteria to return a subset of the objects in a bucket.
135135 //
136−func (api Minio) ListObjectsHandler(w http.ResponseWriter, req *http.Request) {
136+func (api MinioAPI) ListObjectsHandler(w http.ResponseWriter, req *http.Request) {
137137 // Ticket master block
138138 {
139− op := Operation{}
139+ op := APIOperation{}
140140 op.ProceedCh = make(chan struct{})
141141 api.OP <- op
142142 // block until Ticket master gives us a go
@@ -208,10 +208,10 @@ func (api Minio) ListObjectsHandler(w http.ResponseWriter, req *http.Request) { · 65 unmodified lines
208208 // -----------
209209 // This implementation of the GET operation returns a list of all buckets
210210 // owned by the authenticated sender of the request.
211−func (api Minio) ListBucketsHandler(w http.ResponseWriter, req *http.Request) {
211+func (api MinioAPI) ListBucketsHandler(w http.ResponseWriter, req *http.Request) {
212212 // Ticket master block
213213 {
214− op := Operation{}
214+ op := APIOperation{}
215215 op.ProceedCh = make(chan struct{})
216216 api.OP <- op
217217 // block until Ticket master gives us a go
@@ -260,10 +260,10 @@ func (api Minio) ListBucketsHandler(w http.ResponseWriter, req *http.Request) { · 42 unmodified lines
260260 // PutBucketHandler - PUT Bucket
261261 // ----------
262262 // This implementation of the PUT operation creates a new bucket for authenticated request
263−func (api Minio) PutBucketHandler(w http.ResponseWriter, req *http.Request) {
263+func (api MinioAPI) PutBucketHandler(w http.ResponseWriter, req *http.Request) {
264264 // Ticket master block
265265 {
266− op := Operation{}
266+ op := APIOperation{}
267267 op.ProceedCh = make(chan struct{})
268268 api.OP <- op
269269 // block until Ticket master gives us a go
@@ -338,10 +338,10 @@ func (api Minio) PutBucketHandler(w http.ResponseWriter, req *http.Request) { · 68 unmodified lines
338338 // PutBucketACLHandler - PUT Bucket ACL
339339 // ----------
340340 // This implementation of the PUT operation modifies the bucketACL for authenticated request
341−func (api Minio) PutBucketACLHandler(w http.ResponseWriter, req *http.Request) {
341+func (api MinioAPI) PutBucketACLHandler(w http.ResponseWriter, req *http.Request) {
342342 // Ticket master block
343343 {
344− op := Operation{}
344+ op := APIOperation{}
345345 op.ProceedCh = make(chan struct{})
346346 api.OP <- op
347347 // block until Ticket master gives us a go
@@ -395,10 +395,10 @@ func (api Minio) PutBucketACLHandler(w http.ResponseWriter, req *http.Request) { · 47 unmodified lines
395395 // The operation returns a 200 OK if the bucket exists and you
396396 // have permission to access it. Otherwise, the operation might
397397 // return responses such as 404 Not Found and 403 Forbidden.
398−func (api Minio) HeadBucketHandler(w http.ResponseWriter, req *http.Request) {
398+func (api MinioAPI) HeadBucketHandler(w http.ResponseWriter, req *http.Request) {
399399 // Ticket master block
400400 {
401− op := Operation{}
401+ op := APIOperation{}
402402 op.ProceedCh = make(chan struct{})
403403 api.OP <- op
404404 // block until Ticket master gives us a go
server-api-contenttype.gorenamed from pkg/server/api/contenttype.go+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import "net/http"
2020
server-api-definitions.gorenamed from pkg/server/api/definitions.go+3−3
@@ -14,12 +14,12 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import "encoding/xml"
2020
21−// Config - http server config
22−type Config struct {
21+// APIConfig - http server config
22+type APIConfig struct {
2323 Address string
2424 TLS bool
2525 CertFile string
server-api-errors.gorenamed from pkg/server/api/errors.go+10−10
@@ -14,22 +14,22 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import (
2020 "encoding/xml"
2121 "net/http"
2222 )
2323
24−// Error structure
25−type Error struct {
24+// APIError structure
25+type APIError struct {
2626 Code string
2727 Description string
2828 HTTPStatusCode int
2929 }
3030
31−// ErrorResponse - error response format
32−type ErrorResponse struct {
31+// APIErrorResponse - error response format
32+type APIErrorResponse struct {
3333 XMLName xml.Name `xml:"Error" json:"-"`
3434 Code string
3535 Message string
@@ -77,8 +77,8 @@ const ( · 41 unmodified lines
7777 NotAcceptable = iota + 30
7878 )
7979
80−// Error code to Error structure map
81−var errorCodeResponse = map[int]Error{
80+// APIError code to Error structure map
81+var errorCodeResponse = map[int]APIError{
8282 InvalidMaxUploads: {
8383 Code: "InvalidArgument",
8484 Description: "Argument maxUploads must be an integer between 0 and 2147483647.",
@@ -232,14 +232,14 @@ var errorCodeResponse = map[int]Error{ · 147 unmodified lines
232232 }
233233
234234 // errorCodeError provides errorCode to Error. It returns empty if the code provided is unknown
235−func getErrorCode(code int) Error {
235+func getErrorCode(code int) APIError {
236236 return errorCodeResponse[code]
237237 }
238238
239239 // getErrorResponse gets in standard error and resource value and
240240 // provides a encodable populated response values
241−func getErrorResponse(err Error, resource string) ErrorResponse {
242− var data = ErrorResponse{}
241+func getErrorResponse(err APIError, resource string) APIErrorResponse {
242+ var data = APIErrorResponse{}
243243 data.Code = err.Code
244244 data.Message = err.Description
245245 if resource != "" {
server-api-generic-handlers.gorenamed from pkg/server/api/generic-handlers.go+1−5
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import (
2020 "errors"
@@ -44,10 +44,6 @@ type resourceHandler struct { · 23 unmodified lines
4444 handler http.Handler
4545 }
4646
47−const (
48− iso8601Format = "20060102T150405Z"
49−)
50−
5147 func parseDate(req *http.Request) (time.Time, error) {
5248 amzDate := req.Header.Get(http.CanonicalHeaderKey("x-amz-date"))
5349 switch {
server-api-headers.gorenamed from pkg/server/api/headers.go+3−4
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import (
2020 "bytes"
@@ -22,6 +22,7 @@ import ( · 1 unmodified line
2222 "encoding/json"
2323 "encoding/xml"
2424 "net/http"
25+ "runtime"
2526 "strconv"
2627
2728 "github.com/minio/minio/pkg/donut"
@@ -51,9 +52,7 @@ func generateRequestID() []byte { · 23 unmodified lines
5152 func setCommonHeaders(w http.ResponseWriter, acceptsType string, contentLength int) {
5253 // set unique request ID for each reply
5354 w.Header().Set("X-Amz-Request-Id", string(generateRequestID()))
54−
55− // TODO: Modularity comes in the way of passing global state like "version". A better approach needed here. -ab
56− // w.Header().Set("Server", ("Minio/" + version + " (" + runtime.GOOS + ";" + runtime.GOARCH + ")"))
55+ w.Header().Set("Server", ("Minio/" + minioReleaseTag + " (" + runtime.GOOS + ";" + runtime.GOARCH + ")"))
5756
5857 w.Header().Set("Accept-Ranges", "bytes")
5958 w.Header().Set("Content-Type", acceptsType)
server-api-httprange.gorenamed from pkg/server/api/range.go+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import (
2020 "errors"
server-api-logging-handlers.gorenamed from pkg/server/api/logging-handlers.go+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import (
2020 "bytes"
server-api-object-handlers.gorenamed from pkg/server/api/object-handlers.go+19−19
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import (
2020 "net/http"
@@ -33,10 +33,10 @@ const ( · 12 unmodified lines
3333 // ----------
3434 // This implementation of the GET operation retrieves object. To use GET,
3535 // you must have READ access to the object.
36−func (api Minio) GetObjectHandler(w http.ResponseWriter, req *http.Request) {
36+func (api MinioAPI) GetObjectHandler(w http.ResponseWriter, req *http.Request) {
3737 // ticket master block
3838 {
39− op := Operation{}
39+ op := APIOperation{}
4040 op.ProceedCh = make(chan struct{})
4141 api.OP <- op
4242 // block until Ticket master gives us a go
@@ -100,10 +100,10 @@ func (api Minio) GetObjectHandler(w http.ResponseWriter, req *http.Request) { · 57 unmodified lines
100100 // HeadObjectHandler - HEAD Object
101101 // -----------
102102 // The HEAD operation retrieves metadata from an object without returning the object itself.
103−func (api Minio) HeadObjectHandler(w http.ResponseWriter, req *http.Request) {
103+func (api MinioAPI) HeadObjectHandler(w http.ResponseWriter, req *http.Request) {
104104 // ticket master block
105105 {
106− op := Operation{}
106+ op := APIOperation{}
107107 op.ProceedCh = make(chan struct{})
108108 api.OP <- op
109109 // block until Ticket master gives us a go
@@ -157,10 +157,10 @@ func (api Minio) HeadObjectHandler(w http.ResponseWriter, req *http.Request) { · 47 unmodified lines
157157 // PutObjectHandler - PUT Object
158158 // ----------
159159 // This implementation of the PUT operation adds an object to a bucket.
160−func (api Minio) PutObjectHandler(w http.ResponseWriter, req *http.Request) {
160+func (api MinioAPI) PutObjectHandler(w http.ResponseWriter, req *http.Request) {
161161 // Ticket master block
162162 {
163− op := Operation{}
163+ op := APIOperation{}
164164 op.ProceedCh = make(chan struct{})
165165 api.OP <- op
166166 // block until Ticket master gives us a go
@@ -259,10 +259,10 @@ func (api Minio) PutObjectHandler(w http.ResponseWriter, req *http.Request) { · 92 unmodified lines
259259 /// Multipart API
260260
261261 // NewMultipartUploadHandler - New multipart upload
262−func (api Minio) NewMultipartUploadHandler(w http.ResponseWriter, req *http.Request) {
262+func (api MinioAPI) NewMultipartUploadHandler(w http.ResponseWriter, req *http.Request) {
263263 // Ticket master block
264264 {
265− op := Operation{}
265+ op := APIOperation{}
266266 op.ProceedCh = make(chan struct{})
267267 api.OP <- op
268268 // block until Ticket master gives us a go
@@ -317,10 +317,10 @@ func (api Minio) NewMultipartUploadHandler(w http.ResponseWriter, req *http.Requ · 48 unmodified lines
317317 }
318318
319319 // PutObjectPartHandler - Upload part
320−func (api Minio) PutObjectPartHandler(w http.ResponseWriter, req *http.Request) {
320+func (api MinioAPI) PutObjectPartHandler(w http.ResponseWriter, req *http.Request) {
321321 // Ticket master block
322322 {
323− op := Operation{}
323+ op := APIOperation{}
324324 op.ProceedCh = make(chan struct{})
325325 api.OP <- op
326326 // block until Ticket master gives us a go
@@ -417,10 +417,10 @@ func (api Minio) PutObjectPartHandler(w http.ResponseWriter, req *http.Request) · 90 unmodified lines
417417 }
418418
419419 // AbortMultipartUploadHandler - Abort multipart upload
420−func (api Minio) AbortMultipartUploadHandler(w http.ResponseWriter, req *http.Request) {
420+func (api MinioAPI) AbortMultipartUploadHandler(w http.ResponseWriter, req *http.Request) {
421421 // Ticket master block
422422 {
423− op := Operation{}
423+ op := APIOperation{}
424424 op.ProceedCh = make(chan struct{})
425425 api.OP <- op
426426 // block until Ticket master gives us a go
@@ -467,10 +467,10 @@ func (api Minio) AbortMultipartUploadHandler(w http.ResponseWriter, req *http.Re · 40 unmodified lines
467467 }
468468
469469 // ListObjectPartsHandler - List object parts
470−func (api Minio) ListObjectPartsHandler(w http.ResponseWriter, req *http.Request) {
470+func (api MinioAPI) ListObjectPartsHandler(w http.ResponseWriter, req *http.Request) {
471471 // Ticket master block
472472 {
473− op := Operation{}
473+ op := APIOperation{}
474474 op.ProceedCh = make(chan struct{})
475475 api.OP <- op
476476 // block until Ticket master gives us a go
@@ -532,10 +532,10 @@ func (api Minio) ListObjectPartsHandler(w http.ResponseWriter, req *http.Request · 55 unmodified lines
532532 }
533533
534534 // CompleteMultipartUploadHandler - Complete multipart upload
535−func (api Minio) CompleteMultipartUploadHandler(w http.ResponseWriter, req *http.Request) {
535+func (api MinioAPI) CompleteMultipartUploadHandler(w http.ResponseWriter, req *http.Request) {
536536 // Ticket master block
537537 {
538− op := Operation{}
538+ op := APIOperation{}
539539 op.ProceedCh = make(chan struct{})
540540 api.OP <- op
541541 // block until Ticket master gives us a go
@@ -597,13 +597,13 @@ func (api Minio) CompleteMultipartUploadHandler(w http.ResponseWriter, req *http · 55 unmodified lines
597597 /// Delete API
598598
599599 // DeleteBucketHandler - Delete bucket
600−func (api Minio) DeleteBucketHandler(w http.ResponseWriter, req *http.Request) {
600+func (api MinioAPI) DeleteBucketHandler(w http.ResponseWriter, req *http.Request) {
601601 error := getErrorCode(MethodNotAllowed)
602602 w.WriteHeader(error.HTTPStatusCode)
603603 }
604604
605605 // DeleteObjectHandler - Delete object
606−func (api Minio) DeleteObjectHandler(w http.ResponseWriter, req *http.Request) {
606+func (api MinioAPI) DeleteObjectHandler(w http.ResponseWriter, req *http.Request) {
607607 error := getErrorCode(MethodNotAllowed)
608608 w.WriteHeader(error.HTTPStatusCode)
609609 }
server-api-resources.gorenamed from pkg/server/api/resources.go+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import (
2020 "net/url"
server-api-response.gorenamed from pkg/server/api/response.go+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import (
2020 "net/http"
server-api-signature.gorenamed from pkg/server/api/signature.go+3−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import (
2020 "errors"
@@ -28,6 +28,8 @@ import ( · 7 unmodified lines
2828
2929 const (
3030 authHeaderPrefix = "AWS4-HMAC-SHA256"
31+ iso8601Format = "20060102T150405Z"
32+ yyyymmdd = "20060102"
3133 )
3234
3335 // getCredentialsFromAuth parse credentials tag from authorization value
server-api-typed-errors.gorenamed from pkg/server/api/typed-errors.go+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import "errors"
2020
server-api-utils.gorenamed from pkg/server/api/utils.go+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import (
2020 "encoding/base64"
server-api.gorenamed from pkg/server/api/api.go+10−10
@@ -14,30 +14,30 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package api
17+package main
1818
1919 import "github.com/minio/minio/pkg/donut"
2020
21−// Operation container for individual operations read by Ticket Master
22−type Operation struct {
21+// APIOperation container for individual operations read by Ticket Master
22+type APIOperation struct {
2323 ProceedCh chan struct{}
2424 }
2525
26−// Minio container for API and also carries OP (operation) channel
27−type Minio struct {
28− OP chan Operation
26+// MinioAPI container for API and also carries OP (operation) channel
27+type MinioAPI struct {
28+ OP chan APIOperation
2929 Donut donut.Interface
3030 }
3131
32−// New instantiate a new minio API
33−func New() Minio {
32+// NewAPI instantiate a new minio API
33+func NewAPI() MinioAPI {
3434 // ignore errors for now
3535 d, err := donut.New()
3636 if err != nil {
3737 panic(err)
3838 }
39− return Minio{
40− OP: make(chan Operation),
39+ return MinioAPI{
40+ OP: make(chan APIOperation),
4141 Donut: d,
4242 }
4343 }
server-main.go+4−8
@@ -16,11 +16,7 @@ · 15 unmodified lines
1616
1717 package main
1818
19−import (
20− "github.com/minio/cli"
21− "github.com/minio/minio/pkg/server"
22− "github.com/minio/minio/pkg/server/api"
23−)
19+import "github.com/minio/cli"
2420
2521 var serverCmd = cli.Command{
2622 Name: "server",
@@ -39,14 +35,14 @@ EXAMPLES: · 12 unmodified lines
3935 `,
4036 }
4137
42−func getServerConfig(c *cli.Context) api.Config {
38+func getServerConfig(c *cli.Context) APIConfig {
4339 certFile := c.GlobalString("cert")
4440 keyFile := c.GlobalString("key")
4541 if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
4642 Fatalln("Both certificate and key are required to enable https.")
4743 }
4844 tls := (certFile != "" && keyFile != "")
49− return api.Config{
45+ return APIConfig{
5046 Address: c.GlobalString("address"),
5147 TLS: tls,
5248 CertFile: certFile,
@@ -61,6 +57,6 @@ func serverMain(c *cli.Context) { · 8 unmodified lines
6157 }
6258
6359 apiServerConfig := getServerConfig(c)
64− err := server.Start(apiServerConfig)
60+ err := StartServer(apiServerConfig)
6561 errorIf(err.Trace(), "Failed to start the minio server.", nil)
6662 }
server-router.gorenamed from pkg/server/router.go+11−12
@@ -14,17 +14,16 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package server
17+package main
1818
1919 import (
2020 "net/http"
2121
2222 router "github.com/gorilla/mux"
23− "github.com/minio/minio/pkg/server/api"
2423 )
2524
2625 // registerAPI - register all the object API handlers to their respective paths
27−func registerAPI(mux *router.Router, a api.Minio) {
26+func registerAPI(mux *router.Router, a MinioAPI) {
2827 mux.HandleFunc("/", a.ListBucketsHandler).Methods("GET")
2928 mux.HandleFunc("/{bucket}", a.ListObjectsHandler).Methods("GET")
3029 mux.HandleFunc("/{bucket}", a.PutBucketHandler).Methods("PUT")
@@ -45,7 +44,7 @@ func registerAPI(mux *router.Router, a api.Minio) { · 14 unmodified lines
4544 mux.HandleFunc("/{bucket}/{object:.*}", a.DeleteObjectHandler).Methods("DELETE")
4645 }
4746
48−func registerCustomMiddleware(mux *router.Router, mwHandlers ...api.MiddlewareHandler) http.Handler {
47+func registerCustomMiddleware(mux *router.Router, mwHandlers ...MiddlewareHandler) http.Handler {
4948 var f http.Handler
5049 f = mux
5150 for _, mw := range mwHandlers {
@@ -55,18 +54,18 @@ func registerCustomMiddleware(mux *router.Router, mwHandlers ...api.MiddlewareHa · 3 unmodified lines
5554 }
5655
5756 // getAPIHandler api handler
58−func getAPIHandler(conf api.Config) (http.Handler, api.Minio) {
59− var mwHandlers = []api.MiddlewareHandler{
60− api.ValidContentTypeHandler,
61− api.TimeValidityHandler,
62− api.IgnoreResourcesHandler,
63− api.ValidateAuthHeaderHandler,
57+func getAPIHandler(conf APIConfig) (http.Handler, MinioAPI) {
58+ var mwHandlers = []MiddlewareHandler{
59+ ValidContentTypeHandler,
60+ TimeValidityHandler,
61+ IgnoreResourcesHandler,
62+ ValidateAuthHeaderHandler,
6463 // api.LoggingHandler, // Disabled logging until we bring in external logging support
65− api.CorsHandler,
64+ CorsHandler,
6665 }
6766
6867 mux := router.NewRouter()
69− minioAPI := api.New()
68+ minioAPI := NewAPI()
7069 registerAPI(mux, minioAPI)
7170 apiHandler := registerCustomMiddleware(mux, mwHandlers...)
7271 return apiHandler, minioAPI
server-signature-v4_test.gorenamed from pkg/server/signature-v4_test.go+3−15
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package server
17+package main
1818
1919 import (
2020 "bytes"
@@ -28,20 +28,8 @@ import ( · 7 unmodified lines
2828 "regexp"
2929 "sort"
3030 "strings"
31− "testing"
3231 "time"
3332 "unicode/utf8"
34−
35− . "gopkg.in/check.v1"
36−)
37−
38−// Hook up gocheck into the "go test" runner.
39−func Test(t *testing.T) { TestingT(t) }
40−
41−const (
42− authHeader = "AWS4-HMAC-SHA256"
43− iso8601Format = "20060102T150405Z"
44− yyyymmdd = "20060102"
4533 )
4634
4735 ///
@@ -254,7 +242,7 @@ func (s *MyAPISignatureV4Suite) newRequest(method, urlStr string, contentLength · 206 unmodified lines
254242 "aws4_request",
255243 }, "/")
256244
257− stringToSign := authHeader + "\n" + t.Format(iso8601Format) + "\n"
245+ stringToSign := authHeaderPrefix + "\n" + t.Format(iso8601Format) + "\n"
258246 stringToSign = stringToSign + scope + "\n"
259247 stringToSign = stringToSign + hex.EncodeToString(sum256([]byte(canonicalRequest)))
260248
@@ -267,7 +255,7 @@ func (s *MyAPISignatureV4Suite) newRequest(method, urlStr string, contentLength · 6 unmodified lines
267255
268256 // final Authorization header
269257 parts := []string{
270− authHeader + " Credential=" + s.accessKeyID + "/" + scope,
258+ authHeaderPrefix + " Credential=" + s.accessKeyID + "/" + scope,
271259 "SignedHeaders=" + signedHeaders,
272260 "Signature=" + signature,
273261 }
server.gorenamed from pkg/server/server.go+5−6
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package server
17+package main
1818
1919 import (
2020 "crypto/tls"
@@ -26,11 +26,10 @@ import ( · 5 unmodified lines
2626
2727 "github.com/minio/minio/pkg/minhttp"
2828 "github.com/minio/minio/pkg/probe"
29− "github.com/minio/minio/pkg/server/api"
3029 )
3130
3231 // getAPI server instance
33−func getAPIServer(conf api.Config, apiHandler http.Handler) (*http.Server, *probe.Error) {
32+func getAPIServer(conf APIConfig, apiHandler http.Handler) (*http.Server, *probe.Error) {
3433 // Minio server config
3534 httpServer := &http.Server{
3635 Addr: conf.Address,
@@ -84,7 +83,7 @@ func getAPIServer(conf api.Config, apiHandler http.Handler) (*http.Server, *prob · 47 unmodified lines
8483 }
8584
8685 // Start ticket master
87−func startTM(a api.Minio) {
86+func startTM(a MinioAPI) {
8887 for {
8988 for op := range a.OP {
9089 op.ProceedCh <- struct{}{}
@@ -92,8 +91,8 @@ func startTM(a api.Minio) { · 1 unmodified line
9291 }
9392 }
9493
95−// Start starts a s3 compatible cloud storage server
96−func Start(conf api.Config) *probe.Error {
94+// StartServer starts an s3 compatible cloud storage server
95+func StartServer(conf APIConfig) *probe.Error {
9796 apiHandler, minioAPI := getAPIHandler(conf)
9897 apiServer, err := getAPIServer(conf, apiHandler)
9998 if err != nil {
server_donut_cache_test.gorenamed from pkg/server/api_donut_cache_test.go+9−10
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package server
17+package main
1818
1919 import (
2020 "bytes"
@@ -28,7 +28,6 @@ import ( · 7 unmodified lines
2828 "net/http/httptest"
2929
3030 "github.com/minio/minio/pkg/donut"
31− "github.com/minio/minio/pkg/server/api"
3231 . "gopkg.in/check.v1"
3332 )
3433
@@ -52,7 +51,7 @@ func (s *MyAPIDonutCacheSuite) SetUpSuite(c *C) { · 17 unmodified lines
5251 perr := donut.SaveConfig(conf)
5352 c.Assert(perr, IsNil)
5453
55− httpHandler, minioAPI := getAPIHandler(api.Config{RateLimit: 16})
54+ httpHandler, minioAPI := getAPIHandler(APIConfig{RateLimit: 16})
5655 go startTM(minioAPI)
5756 testAPIDonutCacheServer = httptest.NewServer(httpHandler)
5857 }
@@ -319,7 +318,7 @@ func (s *MyAPIDonutCacheSuite) TestListBuckets(c *C) { · 260 unmodified lines
319318 c.Assert(err, IsNil)
320319 c.Assert(response.StatusCode, Equals, http.StatusOK)
321320
322− var results api.ListBucketsResponse
321+ var results ListBucketsResponse
323322 decoder := xml.NewDecoder(response.Body)
324323 err = decoder.Decode(&results)
325324 c.Assert(err, IsNil)
@@ -676,7 +675,7 @@ func (s *MyAPIDonutCacheSuite) TestObjectMultipartAbort(c *C) { · 350 unmodified lines
676675 c.Assert(response.StatusCode, Equals, http.StatusOK)
677676
678677 decoder := xml.NewDecoder(response.Body)
679− newResponse := &api.InitiateMultipartUploadResponse{}
678+ newResponse := &InitiateMultipartUploadResponse{}
680679
681680 err = decoder.Decode(newResponse)
682681 c.Assert(err, IsNil)
@@ -721,7 +720,7 @@ func (s *MyAPIDonutCacheSuite) TestBucketMultipartList(c *C) { · 38 unmodified lines
721720 c.Assert(response.StatusCode, Equals, http.StatusOK)
722721
723722 decoder := xml.NewDecoder(response.Body)
724− newResponse := &api.InitiateMultipartUploadResponse{}
723+ newResponse := &InitiateMultipartUploadResponse{}
725724
726725 err = decoder.Decode(newResponse)
727726 c.Assert(err, IsNil)
@@ -750,7 +749,7 @@ func (s *MyAPIDonutCacheSuite) TestBucketMultipartList(c *C) { · 22 unmodified lines
750749 c.Assert(response3.StatusCode, Equals, http.StatusOK)
751750
752751 decoder = xml.NewDecoder(response3.Body)
753− newResponse3 := &api.ListMultipartUploadsResponse{}
752+ newResponse3 := &ListMultipartUploadsResponse{}
754753 err = decoder.Decode(newResponse3)
755754 c.Assert(err, IsNil)
756755 c.Assert(newResponse3.Bucket, Equals, "bucketmultipartlist")
@@ -772,7 +771,7 @@ func (s *MyAPIDonutCacheSuite) TestObjectMultipartList(c *C) { · 15 unmodified lines
772771 c.Assert(response.StatusCode, Equals, http.StatusOK)
773772
774773 decoder := xml.NewDecoder(response.Body)
775− newResponse := &api.InitiateMultipartUploadResponse{}
774+ newResponse := &InitiateMultipartUploadResponse{}
776775
777776 err = decoder.Decode(newResponse)
778777 c.Assert(err, IsNil)
@@ -826,7 +825,7 @@ func (s *MyAPIDonutCacheSuite) TestObjectMultipart(c *C) { · 47 unmodified lines
826825 c.Assert(response.StatusCode, Equals, http.StatusOK)
827826
828827 decoder := xml.NewDecoder(response.Body)
829− newResponse := &api.InitiateMultipartUploadResponse{}
828+ newResponse := &InitiateMultipartUploadResponse{}
830829
831830 err = decoder.Decode(newResponse)
832831 c.Assert(err, IsNil)
@@ -888,7 +887,7 @@ func (s *MyAPIDonutCacheSuite) TestObjectMultipart(c *C) { · 55 unmodified lines
888887 func verifyError(c *C, response *http.Response, code, description string, statusCode int) {
889888 data, err := ioutil.ReadAll(response.Body)
890889 c.Assert(err, IsNil)
891− errorResponse := api.ErrorResponse{}
890+ errorResponse := APIErrorResponse{}
892891 err = xml.Unmarshal(data, &errorResponse)
893892 c.Assert(err, IsNil)
894893 c.Assert(errorResponse.Code, Equals, code)
server_donut_test.gorenamed from pkg/server/api_donut_test.go+8−9
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package server
17+package main
1818
1919 import (
2020 "bytes"
@@ -29,7 +29,6 @@ import ( · 8 unmodified lines
2929 "net/http/httptest"
3030
3131 "github.com/minio/minio/pkg/donut"
32− "github.com/minio/minio/pkg/server/api"
3332 . "gopkg.in/check.v1"
3433 )
3534
@@ -71,7 +70,7 @@ func (s *MyAPIDonutSuite) SetUpSuite(c *C) { · 35 unmodified lines
7170 perr := donut.SaveConfig(conf)
7271 c.Assert(perr, IsNil)
7372
74− httpHandler, minioAPI := getAPIHandler(api.Config{RateLimit: 16})
73+ httpHandler, minioAPI := getAPIHandler(APIConfig{RateLimit: 16})
7574 go startTM(minioAPI)
7675 testAPIDonutServer = httptest.NewServer(httpHandler)
7776 }
@@ -338,7 +337,7 @@ func (s *MyAPIDonutSuite) TestListBuckets(c *C) { · 260 unmodified lines
338337 c.Assert(err, IsNil)
339338 c.Assert(response.StatusCode, Equals, http.StatusOK)
340339
341− var results api.ListBucketsResponse
340+ var results ListBucketsResponse
342341 decoder := xml.NewDecoder(response.Body)
343342 err = decoder.Decode(&results)
344343 c.Assert(err, IsNil)
@@ -696,7 +695,7 @@ func (s *MyAPIDonutSuite) TestObjectMultipartAbort(c *C) { · 351 unmodified lines
696695 c.Assert(response.StatusCode, Equals, http.StatusOK)
697696
698697 decoder := xml.NewDecoder(response.Body)
699− newResponse := &api.InitiateMultipartUploadResponse{}
698+ newResponse := &InitiateMultipartUploadResponse{}
700699
701700 err = decoder.Decode(newResponse)
702701 c.Assert(err, IsNil)
@@ -741,7 +740,7 @@ func (s *MyAPIDonutSuite) TestBucketMultipartList(c *C) { · 38 unmodified lines
741740 c.Assert(response.StatusCode, Equals, http.StatusOK)
742741
743742 decoder := xml.NewDecoder(response.Body)
744− newResponse := &api.InitiateMultipartUploadResponse{}
743+ newResponse := &InitiateMultipartUploadResponse{}
745744
746745 err = decoder.Decode(newResponse)
747746 c.Assert(err, IsNil)
@@ -770,7 +769,7 @@ func (s *MyAPIDonutSuite) TestBucketMultipartList(c *C) { · 22 unmodified lines
770769 c.Assert(response3.StatusCode, Equals, http.StatusOK)
771770
772771 decoder = xml.NewDecoder(response3.Body)
773− newResponse3 := &api.ListMultipartUploadsResponse{}
772+ newResponse3 := &ListMultipartUploadsResponse{}
774773 err = decoder.Decode(newResponse3)
775774 c.Assert(err, IsNil)
776775 c.Assert(newResponse3.Bucket, Equals, "bucketmultipartlist")
@@ -792,7 +791,7 @@ func (s *MyAPIDonutSuite) TestObjectMultipartList(c *C) { · 15 unmodified lines
792791 c.Assert(response.StatusCode, Equals, http.StatusOK)
793792
794793 decoder := xml.NewDecoder(response.Body)
795− newResponse := &api.InitiateMultipartUploadResponse{}
794+ newResponse := &InitiateMultipartUploadResponse{}
796795
797796 err = decoder.Decode(newResponse)
798797 c.Assert(err, IsNil)
@@ -846,7 +845,7 @@ func (s *MyAPIDonutSuite) TestObjectMultipart(c *C) { · 47 unmodified lines
846845 c.Assert(response.StatusCode, Equals, http.StatusOK)
847846
848847 decoder := xml.NewDecoder(response.Body)
849− newResponse := &api.InitiateMultipartUploadResponse{}
848+ newResponse := &InitiateMultipartUploadResponse{}
850849
851850 err = decoder.Decode(newResponse)
852851 c.Assert(err, IsNil)
server_signature_v4_test.gorenamed from pkg/server/api_signature_v4_test.go+8−9
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 * limitations under the License.
1515 */
1616
17−package server
17+package main
1818
1919 import (
2020 "bytes"
@@ -30,7 +30,6 @@ import ( · 9 unmodified lines
3030
3131 "github.com/minio/minio/pkg/auth"
3232 "github.com/minio/minio/pkg/donut"
33− "github.com/minio/minio/pkg/server/api"
3433 . "gopkg.in/check.v1"
3534 )
3635
@@ -79,7 +78,7 @@ func (s *MyAPISignatureV4Suite) SetUpSuite(c *C) { · 42 unmodified lines
7978 perr = auth.SaveConfig(authConf)
8079 c.Assert(perr, IsNil)
8180
82− httpHandler, minioAPI := getAPIHandler(api.Config{RateLimit: 16})
81+ httpHandler, minioAPI := getAPIHandler(APIConfig{RateLimit: 16})
8382 go startTM(minioAPI)
8483 testSignatureV4Server = httptest.NewServer(httpHandler)
8584 }
@@ -347,7 +346,7 @@ func (s *MyAPISignatureV4Suite) TestListBuckets(c *C) { · 261 unmodified lines
347346 c.Assert(err, IsNil)
348347 c.Assert(response.StatusCode, Equals, http.StatusOK)
349348
350− var results api.ListBucketsResponse
349+ var results ListBucketsResponse
351350 decoder := xml.NewDecoder(response.Body)
352351 err = decoder.Decode(&results)
353352 c.Assert(err, IsNil)
@@ -689,7 +688,7 @@ func (s *MyAPISignatureV4Suite) TestObjectMultipartAbort(c *C) { · 335 unmodified lines
689688 c.Assert(response.StatusCode, Equals, http.StatusOK)
690689
691690 decoder := xml.NewDecoder(response.Body)
692− newResponse := &api.InitiateMultipartUploadResponse{}
691+ newResponse := &InitiateMultipartUploadResponse{}
693692
694693 err = decoder.Decode(newResponse)
695694 c.Assert(err, IsNil)
@@ -736,7 +735,7 @@ func (s *MyAPISignatureV4Suite) TestBucketMultipartList(c *C) { · 40 unmodified lines
736735 c.Assert(response.StatusCode, Equals, http.StatusOK)
737736
738737 decoder := xml.NewDecoder(response.Body)
739− newResponse := &api.InitiateMultipartUploadResponse{}
738+ newResponse := &InitiateMultipartUploadResponse{}
740739
741740 err = decoder.Decode(newResponse)
742741 c.Assert(err, IsNil)
@@ -767,7 +766,7 @@ func (s *MyAPISignatureV4Suite) TestBucketMultipartList(c *C) { · 24 unmodified lines
767766 c.Assert(response3.StatusCode, Equals, http.StatusOK)
768767
769768 decoder = xml.NewDecoder(response3.Body)
770− newResponse3 := &api.ListMultipartUploadsResponse{}
769+ newResponse3 := &ListMultipartUploadsResponse{}
771770 err = decoder.Decode(newResponse3)
772771 c.Assert(err, IsNil)
773772 c.Assert(newResponse3.Bucket, Equals, "bucketmultipartlist")
@@ -789,7 +788,7 @@ func (s *MyAPISignatureV4Suite) TestObjectMultipartList(c *C) { · 15 unmodified lines
789788 c.Assert(response.StatusCode, Equals, http.StatusOK)
790789
791790 decoder := xml.NewDecoder(response.Body)
792− newResponse := &api.InitiateMultipartUploadResponse{}
791+ newResponse := &InitiateMultipartUploadResponse{}
793792
794793 err = decoder.Decode(newResponse)
795794 c.Assert(err, IsNil)
@@ -845,7 +844,7 @@ func (s *MyAPISignatureV4Suite) TestObjectMultipart(c *C) { · 49 unmodified lines
845844 c.Assert(response.StatusCode, Equals, http.StatusOK)
846845
847846 decoder := xml.NewDecoder(response.Body)
848− newResponse := &api.InitiateMultipartUploadResponse{}
847+ newResponse := &InitiateMultipartUploadResponse{}
849848
850849 err = decoder.Decode(newResponse)
851850 c.Assert(err, IsNil)
vendor.json+28−0
@@ -99,6 +99,34 @@ · 98 unmodified lines
9999 "local": "vendor/gopkg.in/check.v1",
100100 "revision": "11d3bc7aa68e238947792f30573146a3231fc0f1",
101101 "revisionTime": "2015-07-29T10:04:31+02:00"
102+ },
103+ {
104+ "canonical": "gopkg.in/mgo.v2",
105+ "comment": "",
106+ "local": "vendor/gopkg.in/mgo.v2",
107+ "revision": "f4923a569136442e900b8cf5c1a706c0a8b0883c",
108+ "revisionTime": "2015-08-21T12:30:02-03:00"
109+ },
110+ {
111+ "canonical": "gopkg.in/mgo.v2/bson",
112+ "comment": "",
113+ "local": "vendor/gopkg.in/mgo.v2/bson",
114+ "revision": "f4923a569136442e900b8cf5c1a706c0a8b0883c",
115+ "revisionTime": "2015-08-21T12:30:02-03:00"
116+ },
117+ {
118+ "canonical": "gopkg.in/mgo.v2/internal/sasl",
119+ "comment": "",
120+ "local": "vendor/gopkg.in/mgo.v2/internal/sasl",
121+ "revision": "f4923a569136442e900b8cf5c1a706c0a8b0883c",
122+ "revisionTime": "2015-08-21T12:30:02-03:00"
123+ },
124+ {
125+ "canonical": "gopkg.in/mgo.v2/internal/scram",
126+ "comment": "",
127+ "local": "vendor/gopkg.in/mgo.v2/internal/scram",
128+ "revision": "f4923a569136442e900b8cf5c1a706c0a8b0883c",
129+ "revisionTime": "2015-08-21T12:30:02-03:00"
102130 }
103131 ]
104132 }
vendor/gopkg.in/mgo.v2/LICENSE+25−0
@@ -0,0 +1,25 @@
1+mgo - MongoDB driver for Go
2+
3+Copyright (c) 2010-2013 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+
5+All rights reserved.
6+
7+Redistribution and use in source and binary forms, with or without
8+modification, are permitted provided that the following conditions are met:
9+
10+1. Redistributions of source code must retain the above copyright notice, this
11+ list of conditions and the following disclaimer.
12+2. Redistributions in binary form must reproduce the above copyright notice,
13+ this list of conditions and the following disclaimer in the documentation
14+ and/or other materials provided with the distribution.
15+
16+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
vendor/gopkg.in/mgo.v2/Makefile+5−0
@@ -0,0 +1,5 @@
1+startdb:
2+ @testdb/setup.sh start
3+
4+stopdb:
5+ @testdb/setup.sh stop
vendor/gopkg.in/mgo.v2/README.md+4−0
@@ -0,0 +1,4 @@
1+The MongoDB driver for Go
2+-------------------------
3+
4+Please go to [http://labix.org/mgo](http://labix.org/mgo) for all project details.
vendor/gopkg.in/mgo.v2/auth.go+467−0
@@ -0,0 +1,467 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo
28+
29+import (
30+ "crypto/md5"
31+ "crypto/sha1"
32+ "encoding/hex"
33+ "errors"
34+ "fmt"
35+ "sync"
36+
37+ "gopkg.in/mgo.v2/bson"
38+ "gopkg.in/mgo.v2/internal/scram"
39+)
40+
41+type authCmd struct {
42+ Authenticate int
43+
44+ Nonce string
45+ User string
46+ Key string
47+}
48+
49+type startSaslCmd struct {
50+ StartSASL int `bson:"startSasl"`
51+}
52+
53+type authResult struct {
54+ ErrMsg string
55+ Ok bool
56+}
57+
58+type getNonceCmd struct {
59+ GetNonce int
60+}
61+
62+type getNonceResult struct {
63+ Nonce string
64+ Err string "$err"
65+ Code int
66+}
67+
68+type logoutCmd struct {
69+ Logout int
70+}
71+
72+type saslCmd struct {
73+ Start int `bson:"saslStart,omitempty"`
74+ Continue int `bson:"saslContinue,omitempty"`
75+ ConversationId int `bson:"conversationId,omitempty"`
76+ Mechanism string `bson:"mechanism,omitempty"`
77+ Payload []byte
78+}
79+
80+type saslResult struct {
81+ Ok bool `bson:"ok"`
82+ NotOk bool `bson:"code"` // Server <= 2.3.2 returns ok=1 & code>0 on errors (WTF?)
83+ Done bool
84+
85+ ConversationId int `bson:"conversationId"`
86+ Payload []byte
87+ ErrMsg string
88+}
89+
90+type saslStepper interface {
91+ Step(serverData []byte) (clientData []byte, done bool, err error)
92+ Close()
93+}
94+
95+func (socket *mongoSocket) getNonce() (nonce string, err error) {
96+ socket.Lock()
97+ for socket.cachedNonce == "" && socket.dead == nil {
98+ debugf("Socket %p to %s: waiting for nonce", socket, socket.addr)
99+ socket.gotNonce.Wait()
100+ }
101+ if socket.cachedNonce == "mongos" {
102+ socket.Unlock()
103+ return "", errors.New("Can't authenticate with mongos; see http://j.mp/mongos-auth")
104+ }
105+ debugf("Socket %p to %s: got nonce", socket, socket.addr)
106+ nonce, err = socket.cachedNonce, socket.dead
107+ socket.cachedNonce = ""
108+ socket.Unlock()
109+ if err != nil {
110+ nonce = ""
111+ }
112+ return
113+}
114+
115+func (socket *mongoSocket) resetNonce() {
116+ debugf("Socket %p to %s: requesting a new nonce", socket, socket.addr)
117+ op := &queryOp{}
118+ op.query = &getNonceCmd{GetNonce: 1}
119+ op.collection = "admin.$cmd"
120+ op.limit = -1
121+ op.replyFunc = func(err error, reply *replyOp, docNum int, docData []byte) {
122+ if err != nil {
123+ socket.kill(errors.New("getNonce: "+err.Error()), true)
124+ return
125+ }
126+ result := &getNonceResult{}
127+ err = bson.Unmarshal(docData, &result)
128+ if err != nil {
129+ socket.kill(errors.New("Failed to unmarshal nonce: "+err.Error()), true)
130+ return
131+ }
132+ debugf("Socket %p to %s: nonce unmarshalled: %#v", socket, socket.addr, result)
133+ if result.Code == 13390 {
134+ // mongos doesn't yet support auth (see http://j.mp/mongos-auth)
135+ result.Nonce = "mongos"
136+ } else if result.Nonce == "" {
137+ var msg string
138+ if result.Err != "" {
139+ msg = fmt.Sprintf("Got an empty nonce: %s (%d)", result.Err, result.Code)
140+ } else {
141+ msg = "Got an empty nonce"
142+ }
143+ socket.kill(errors.New(msg), true)
144+ return
145+ }
146+ socket.Lock()
147+ if socket.cachedNonce != "" {
148+ socket.Unlock()
149+ panic("resetNonce: nonce already cached")
150+ }
151+ socket.cachedNonce = result.Nonce
152+ socket.gotNonce.Signal()
153+ socket.Unlock()
154+ }
155+ err := socket.Query(op)
156+ if err != nil {
157+ socket.kill(errors.New("resetNonce: "+err.Error()), true)
158+ }
159+}
160+
161+func (socket *mongoSocket) Login(cred Credential) error {
162+ socket.Lock()
163+ if cred.Mechanism == "" && socket.serverInfo.MaxWireVersion >= 3 {
164+ cred.Mechanism = "SCRAM-SHA-1"
165+ }
166+ for _, sockCred := range socket.creds {
167+ if sockCred == cred {
168+ debugf("Socket %p to %s: login: db=%q user=%q (already logged in)", socket, socket.addr, cred.Source, cred.Username)
169+ socket.Unlock()
170+ return nil
171+ }
172+ }
173+ if socket.dropLogout(cred) {
174+ debugf("Socket %p to %s: login: db=%q user=%q (cached)", socket, socket.addr, cred.Source, cred.Username)
175+ socket.creds = append(socket.creds, cred)
176+ socket.Unlock()
177+ return nil
178+ }
179+ socket.Unlock()
180+
181+ debugf("Socket %p to %s: login: db=%q user=%q", socket, socket.addr, cred.Source, cred.Username)
182+
183+ var err error
184+ switch cred.Mechanism {
185+ case "", "MONGODB-CR", "MONGO-CR": // Name changed to MONGODB-CR in SERVER-8501.
186+ err = socket.loginClassic(cred)
187+ case "PLAIN":
188+ err = socket.loginPlain(cred)
189+ case "MONGODB-X509":
190+ err = socket.loginX509(cred)
191+ default:
192+ // Try SASL for everything else, if it is available.
193+ err = socket.loginSASL(cred)
194+ }
195+
196+ if err != nil {
197+ debugf("Socket %p to %s: login error: %s", socket, socket.addr, err)
198+ } else {
199+ debugf("Socket %p to %s: login successful", socket, socket.addr)
200+ }
201+ return err
202+}
203+
204+func (socket *mongoSocket) loginClassic(cred Credential) error {
205+ // Note that this only works properly because this function is
206+ // synchronous, which means the nonce won't get reset while we're
207+ // using it and any other login requests will block waiting for a
208+ // new nonce provided in the defer call below.
209+ nonce, err := socket.getNonce()
210+ if err != nil {
211+ return err
212+ }
213+ defer socket.resetNonce()
214+
215+ psum := md5.New()
216+ psum.Write([]byte(cred.Username + ":mongo:" + cred.Password))
217+
218+ ksum := md5.New()
219+ ksum.Write([]byte(nonce + cred.Username))
220+ ksum.Write([]byte(hex.EncodeToString(psum.Sum(nil))))
221+
222+ key := hex.EncodeToString(ksum.Sum(nil))
223+
224+ cmd := authCmd{Authenticate: 1, User: cred.Username, Nonce: nonce, Key: key}
225+ res := authResult{}
226+ return socket.loginRun(cred.Source, &cmd, &res, func() error {
227+ if !res.Ok {
228+ return errors.New(res.ErrMsg)
229+ }
230+ socket.Lock()
231+ socket.dropAuth(cred.Source)
232+ socket.creds = append(socket.creds, cred)
233+ socket.Unlock()
234+ return nil
235+ })
236+}
237+
238+type authX509Cmd struct {
239+ Authenticate int
240+ User string
241+ Mechanism string
242+}
243+
244+func (socket *mongoSocket) loginX509(cred Credential) error {
245+ cmd := authX509Cmd{Authenticate: 1, User: cred.Username, Mechanism: "MONGODB-X509"}
246+ res := authResult{}
247+ return socket.loginRun(cred.Source, &cmd, &res, func() error {
248+ if !res.Ok {
249+ return errors.New(res.ErrMsg)
250+ }
251+ socket.Lock()
252+ socket.dropAuth(cred.Source)
253+ socket.creds = append(socket.creds, cred)
254+ socket.Unlock()
255+ return nil
256+ })
257+}
258+
259+func (socket *mongoSocket) loginPlain(cred Credential) error {
260+ cmd := saslCmd{Start: 1, Mechanism: "PLAIN", Payload: []byte("\x00" + cred.Username + "\x00" + cred.Password)}
261+ res := authResult{}
262+ return socket.loginRun(cred.Source, &cmd, &res, func() error {
263+ if !res.Ok {
264+ return errors.New(res.ErrMsg)
265+ }
266+ socket.Lock()
267+ socket.dropAuth(cred.Source)
268+ socket.creds = append(socket.creds, cred)
269+ socket.Unlock()
270+ return nil
271+ })
272+}
273+
274+func (socket *mongoSocket) loginSASL(cred Credential) error {
275+ var sasl saslStepper
276+ var err error
277+ if cred.Mechanism == "SCRAM-SHA-1" {
278+ // SCRAM is handled without external libraries.
279+ sasl = saslNewScram(cred)
280+ } else if len(cred.ServiceHost) > 0 {
281+ sasl, err = saslNew(cred, cred.ServiceHost)
282+ } else {
283+ sasl, err = saslNew(cred, socket.Server().Addr)
284+ }
285+ if err != nil {
286+ return err
287+ }
288+ defer sasl.Close()
289+
290+ // The goal of this logic is to carry a locked socket until the
291+ // local SASL step confirms the auth is valid; the socket needs to be
292+ // locked so that concurrent action doesn't leave the socket in an
293+ // auth state that doesn't reflect the operations that took place.
294+ // As a simple case, imagine inverting login=>logout to logout=>login.
295+ //
296+ // The logic below works because the lock func isn't called concurrently.
297+ locked := false
298+ lock := func(b bool) {
299+ if locked != b {
300+ locked = b
301+ if b {
302+ socket.Lock()
303+ } else {
304+ socket.Unlock()
305+ }
306+ }
307+ }
308+
309+ lock(true)
310+ defer lock(false)
311+
312+ start := 1
313+ cmd := saslCmd{}
314+ res := saslResult{}
315+ for {
316+ payload, done, err := sasl.Step(res.Payload)
317+ if err != nil {
318+ return err
319+ }
320+ if done && res.Done {
321+ socket.dropAuth(cred.Source)
322+ socket.creds = append(socket.creds, cred)
323+ break
324+ }
325+ lock(false)
326+
327+ cmd = saslCmd{
328+ Start: start,
329+ Continue: 1 - start,
330+ ConversationId: res.ConversationId,
331+ Mechanism: cred.Mechanism,
332+ Payload: payload,
333+ }
334+ start = 0
335+ err = socket.loginRun(cred.Source, &cmd, &res, func() error {
336+ // See the comment on lock for why this is necessary.
337+ lock(true)
338+ if !res.Ok || res.NotOk {
339+ return fmt.Errorf("server returned error on SASL authentication step: %s", res.ErrMsg)
340+ }
341+ return nil
342+ })
343+ if err != nil {
344+ return err
345+ }
346+ if done && res.Done {
347+ socket.dropAuth(cred.Source)
348+ socket.creds = append(socket.creds, cred)
349+ break
350+ }
351+ }
352+
353+ return nil
354+}
355+
356+func saslNewScram(cred Credential) *saslScram {
357+ credsum := md5.New()
358+ credsum.Write([]byte(cred.Username + ":mongo:" + cred.Password))
359+ client := scram.NewClient(sha1.New, cred.Username, hex.EncodeToString(credsum.Sum(nil)))
360+ return &saslScram{cred: cred, client: client}
361+}
362+
363+type saslScram struct {
364+ cred Credential
365+ client *scram.Client
366+}
367+
368+func (s *saslScram) Close() {}
369+
370+func (s *saslScram) Step(serverData []byte) (clientData []byte, done bool, err error) {
371+ more := s.client.Step(serverData)
372+ return s.client.Out(), !more, s.client.Err()
373+}
374+
375+func (socket *mongoSocket) loginRun(db string, query, result interface{}, f func() error) error {
376+ var mutex sync.Mutex
377+ var replyErr error
378+ mutex.Lock()
379+
380+ op := queryOp{}
381+ op.query = query
382+ op.collection = db + ".$cmd"
383+ op.limit = -1
384+ op.replyFunc = func(err error, reply *replyOp, docNum int, docData []byte) {
385+ defer mutex.Unlock()
386+
387+ if err != nil {
388+ replyErr = err
389+ return
390+ }
391+
392+ err = bson.Unmarshal(docData, result)
393+ if err != nil {
394+ replyErr = err
395+ } else {
396+ // Must handle this within the read loop for the socket, so
397+ // that concurrent login requests are properly ordered.
398+ replyErr = f()
399+ }
400+ }
401+
402+ err := socket.Query(&op)
403+ if err != nil {
404+ return err
405+ }
406+ mutex.Lock() // Wait.
407+ return replyErr
408+}
409+
410+func (socket *mongoSocket) Logout(db string) {
411+ socket.Lock()
412+ cred, found := socket.dropAuth(db)
413+ if found {
414+ debugf("Socket %p to %s: logout: db=%q (flagged)", socket, socket.addr, db)
415+ socket.logout = append(socket.logout, cred)
416+ }
417+ socket.Unlock()
418+}
419+
420+func (socket *mongoSocket) LogoutAll() {
421+ socket.Lock()
422+ if l := len(socket.creds); l > 0 {
423+ debugf("Socket %p to %s: logout all (flagged %d)", socket, socket.addr, l)
424+ socket.logout = append(socket.logout, socket.creds...)
425+ socket.creds = socket.creds[0:0]
426+ }
427+ socket.Unlock()
428+}
429+
430+func (socket *mongoSocket) flushLogout() (ops []interface{}) {
431+ socket.Lock()
432+ if l := len(socket.logout); l > 0 {
433+ debugf("Socket %p to %s: logout all (flushing %d)", socket, socket.addr, l)
434+ for i := 0; i != l; i++ {
435+ op := queryOp{}
436+ op.query = &logoutCmd{1}
437+ op.collection = socket.logout[i].Source + ".$cmd"
438+ op.limit = -1
439+ ops = append(ops, &op)
440+ }
441+ socket.logout = socket.logout[0:0]
442+ }
443+ socket.Unlock()
444+ return
445+}
446+
447+func (socket *mongoSocket) dropAuth(db string) (cred Credential, found bool) {
448+ for i, sockCred := range socket.creds {
449+ if sockCred.Source == db {
450+ copy(socket.creds[i:], socket.creds[i+1:])
451+ socket.creds = socket.creds[:len(socket.creds)-1]
452+ return sockCred, true
453+ }
454+ }
455+ return cred, false
456+}
457+
458+func (socket *mongoSocket) dropLogout(cred Credential) (found bool) {
459+ for i, sockCred := range socket.logout {
460+ if sockCred == cred {
461+ copy(socket.logout[i:], socket.logout[i+1:])
462+ socket.logout = socket.logout[:len(socket.logout)-1]
463+ return true
464+ }
465+ }
466+ return false
467+}
vendor/gopkg.in/mgo.v2/auth_test.go+1180−0
@@ -0,0 +1,1180 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo_test
28+
29+import (
30+ "crypto/tls"
31+ "flag"
32+ "fmt"
33+ "io/ioutil"
34+ "net"
35+ "net/url"
36+ "os"
37+ "runtime"
38+ "sync"
39+ "time"
40+
41+ . "gopkg.in/check.v1"
42+ "gopkg.in/mgo.v2"
43+)
44+
45+func (s *S) TestAuthLoginDatabase(c *C) {
46+ // Test both with a normal database and with an authenticated shard.
47+ for _, addr := range []string{"localhost:40002", "localhost:40203"} {
48+ session, err := mgo.Dial(addr)
49+ c.Assert(err, IsNil)
50+ defer session.Close()
51+
52+ coll := session.DB("mydb").C("mycoll")
53+ err = coll.Insert(M{"n": 1})
54+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
55+
56+ admindb := session.DB("admin")
57+
58+ err = admindb.Login("root", "wrong")
59+ c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.")
60+
61+ err = admindb.Login("root", "rapadura")
62+ c.Assert(err, IsNil)
63+
64+ err = coll.Insert(M{"n": 1})
65+ c.Assert(err, IsNil)
66+ }
67+}
68+
69+func (s *S) TestAuthLoginSession(c *C) {
70+ // Test both with a normal database and with an authenticated shard.
71+ for _, addr := range []string{"localhost:40002", "localhost:40203"} {
72+ session, err := mgo.Dial(addr)
73+ c.Assert(err, IsNil)
74+ defer session.Close()
75+
76+ coll := session.DB("mydb").C("mycoll")
77+ err = coll.Insert(M{"n": 1})
78+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
79+
80+ cred := mgo.Credential{
81+ Username: "root",
82+ Password: "wrong",
83+ }
84+ err = session.Login(&cred)
85+ c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.")
86+
87+ cred.Password = "rapadura"
88+
89+ err = session.Login(&cred)
90+ c.Assert(err, IsNil)
91+
92+ err = coll.Insert(M{"n": 1})
93+ c.Assert(err, IsNil)
94+ }
95+}
96+
97+func (s *S) TestAuthLoginLogout(c *C) {
98+ // Test both with a normal database and with an authenticated shard.
99+ for _, addr := range []string{"localhost:40002", "localhost:40203"} {
100+ session, err := mgo.Dial(addr)
101+ c.Assert(err, IsNil)
102+ defer session.Close()
103+
104+ admindb := session.DB("admin")
105+ err = admindb.Login("root", "rapadura")
106+ c.Assert(err, IsNil)
107+
108+ admindb.Logout()
109+
110+ coll := session.DB("mydb").C("mycoll")
111+ err = coll.Insert(M{"n": 1})
112+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
113+
114+ // Must have dropped auth from the session too.
115+ session = session.Copy()
116+ defer session.Close()
117+
118+ coll = session.DB("mydb").C("mycoll")
119+ err = coll.Insert(M{"n": 1})
120+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
121+ }
122+}
123+
124+func (s *S) TestAuthLoginLogoutAll(c *C) {
125+ session, err := mgo.Dial("localhost:40002")
126+ c.Assert(err, IsNil)
127+ defer session.Close()
128+
129+ admindb := session.DB("admin")
130+ err = admindb.Login("root", "rapadura")
131+ c.Assert(err, IsNil)
132+
133+ session.LogoutAll()
134+
135+ coll := session.DB("mydb").C("mycoll")
136+ err = coll.Insert(M{"n": 1})
137+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
138+
139+ // Must have dropped auth from the session too.
140+ session = session.Copy()
141+ defer session.Close()
142+
143+ coll = session.DB("mydb").C("mycoll")
144+ err = coll.Insert(M{"n": 1})
145+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
146+}
147+
148+func (s *S) TestAuthUpsertUserErrors(c *C) {
149+ session, err := mgo.Dial("localhost:40002")
150+ c.Assert(err, IsNil)
151+ defer session.Close()
152+
153+ admindb := session.DB("admin")
154+ err = admindb.Login("root", "rapadura")
155+ c.Assert(err, IsNil)
156+
157+ mydb := session.DB("mydb")
158+
159+ err = mydb.UpsertUser(&mgo.User{})
160+ c.Assert(err, ErrorMatches, "user has no Username")
161+
162+ err = mydb.UpsertUser(&mgo.User{Username: "user", Password: "pass", UserSource: "source"})
163+ c.Assert(err, ErrorMatches, "user has both Password/PasswordHash and UserSource set")
164+
165+ err = mydb.UpsertUser(&mgo.User{Username: "user", Password: "pass", OtherDBRoles: map[string][]mgo.Role{"db": nil}})
166+ c.Assert(err, ErrorMatches, "user with OtherDBRoles is only supported in the admin or \\$external databases")
167+}
168+
169+func (s *S) TestAuthUpsertUser(c *C) {
170+ if !s.versionAtLeast(2, 4) {
171+ c.Skip("UpsertUser only works on 2.4+")
172+ }
173+ session, err := mgo.Dial("localhost:40002")
174+ c.Assert(err, IsNil)
175+ defer session.Close()
176+
177+ admindb := session.DB("admin")
178+ err = admindb.Login("root", "rapadura")
179+ c.Assert(err, IsNil)
180+
181+ mydb := session.DB("mydb")
182+
183+ ruser := &mgo.User{
184+ Username: "myruser",
185+ Password: "mypass",
186+ Roles: []mgo.Role{mgo.RoleRead},
187+ }
188+ rwuser := &mgo.User{
189+ Username: "myrwuser",
190+ Password: "mypass",
191+ Roles: []mgo.Role{mgo.RoleReadWrite},
192+ }
193+
194+ err = mydb.UpsertUser(ruser)
195+ c.Assert(err, IsNil)
196+ err = mydb.UpsertUser(rwuser)
197+ c.Assert(err, IsNil)
198+
199+ err = mydb.Login("myruser", "mypass")
200+ c.Assert(err, IsNil)
201+
202+ admindb.Logout()
203+
204+ coll := session.DB("mydb").C("mycoll")
205+ err = coll.Insert(M{"n": 1})
206+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
207+
208+ err = mydb.Login("myrwuser", "mypass")
209+ c.Assert(err, IsNil)
210+
211+ err = coll.Insert(M{"n": 1})
212+ c.Assert(err, IsNil)
213+
214+ myotherdb := session.DB("myotherdb")
215+
216+ err = admindb.Login("root", "rapadura")
217+ c.Assert(err, IsNil)
218+
219+ // Test UserSource.
220+ rwuserother := &mgo.User{
221+ Username: "myrwuser",
222+ UserSource: "mydb",
223+ Roles: []mgo.Role{mgo.RoleRead},
224+ }
225+
226+ err = myotherdb.UpsertUser(rwuserother)
227+ if s.versionAtLeast(2, 6) {
228+ c.Assert(err, ErrorMatches, `MongoDB 2.6\+ does not support the UserSource setting`)
229+ return
230+ }
231+ c.Assert(err, IsNil)
232+
233+ admindb.Logout()
234+
235+ // Test indirection via UserSource: we can't write to it, because
236+ // the roles for myrwuser are different there.
237+ othercoll := myotherdb.C("myothercoll")
238+ err = othercoll.Insert(M{"n": 1})
239+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
240+
241+ // Reading works, though.
242+ err = othercoll.Find(nil).One(nil)
243+ c.Assert(err, Equals, mgo.ErrNotFound)
244+
245+ // Can't login directly into the database using UserSource, though.
246+ err = myotherdb.Login("myrwuser", "mypass")
247+ c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.")
248+}
249+
250+func (s *S) TestAuthUpsertUserOtherDBRoles(c *C) {
251+ if !s.versionAtLeast(2, 4) {
252+ c.Skip("UpsertUser only works on 2.4+")
253+ }
254+ session, err := mgo.Dial("localhost:40002")
255+ c.Assert(err, IsNil)
256+ defer session.Close()
257+
258+ admindb := session.DB("admin")
259+ err = admindb.Login("root", "rapadura")
260+ c.Assert(err, IsNil)
261+
262+ ruser := &mgo.User{
263+ Username: "myruser",
264+ Password: "mypass",
265+ OtherDBRoles: map[string][]mgo.Role{"mydb": []mgo.Role{mgo.RoleRead}},
266+ }
267+
268+ err = admindb.UpsertUser(ruser)
269+ c.Assert(err, IsNil)
270+ defer admindb.RemoveUser("myruser")
271+
272+ admindb.Logout()
273+ err = admindb.Login("myruser", "mypass")
274+
275+ coll := session.DB("mydb").C("mycoll")
276+ err = coll.Insert(M{"n": 1})
277+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
278+
279+ err = coll.Find(nil).One(nil)
280+ c.Assert(err, Equals, mgo.ErrNotFound)
281+}
282+
283+func (s *S) TestAuthUpsertUserUpdates(c *C) {
284+ if !s.versionAtLeast(2, 4) {
285+ c.Skip("UpsertUser only works on 2.4+")
286+ }
287+ session, err := mgo.Dial("localhost:40002")
288+ c.Assert(err, IsNil)
289+ defer session.Close()
290+
291+ admindb := session.DB("admin")
292+ err = admindb.Login("root", "rapadura")
293+ c.Assert(err, IsNil)
294+
295+ mydb := session.DB("mydb")
296+
297+ // Insert a user that can read.
298+ user := &mgo.User{
299+ Username: "myruser",
300+ Password: "mypass",
301+ Roles: []mgo.Role{mgo.RoleRead},
302+ }
303+ err = mydb.UpsertUser(user)
304+ c.Assert(err, IsNil)
305+
306+ // Now update the user password.
307+ user = &mgo.User{
308+ Username: "myruser",
309+ Password: "mynewpass",
310+ }
311+ err = mydb.UpsertUser(user)
312+ c.Assert(err, IsNil)
313+
314+ // Login with the new user.
315+ usession, err := mgo.Dial("myruser:mynewpass@localhost:40002/mydb")
316+ c.Assert(err, IsNil)
317+ defer usession.Close()
318+
319+ // Can read, but not write.
320+ err = usession.DB("mydb").C("mycoll").Find(nil).One(nil)
321+ c.Assert(err, Equals, mgo.ErrNotFound)
322+ err = usession.DB("mydb").C("mycoll").Insert(M{"ok": 1})
323+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
324+
325+ // Update the user role.
326+ user = &mgo.User{
327+ Username: "myruser",
328+ Roles: []mgo.Role{mgo.RoleReadWrite},
329+ }
330+ err = mydb.UpsertUser(user)
331+ c.Assert(err, IsNil)
332+
333+ // Dial again to ensure the password hasn't changed.
334+ usession, err = mgo.Dial("myruser:mynewpass@localhost:40002/mydb")
335+ c.Assert(err, IsNil)
336+ defer usession.Close()
337+
338+ // Now it can write.
339+ err = usession.DB("mydb").C("mycoll").Insert(M{"ok": 1})
340+ c.Assert(err, IsNil)
341+}
342+
343+func (s *S) TestAuthAddUser(c *C) {
344+ session, err := mgo.Dial("localhost:40002")
345+ c.Assert(err, IsNil)
346+ defer session.Close()
347+
348+ admindb := session.DB("admin")
349+ err = admindb.Login("root", "rapadura")
350+ c.Assert(err, IsNil)
351+
352+ mydb := session.DB("mydb")
353+ err = mydb.AddUser("myruser", "mypass", true)
354+ c.Assert(err, IsNil)
355+ err = mydb.AddUser("mywuser", "mypass", false)
356+ c.Assert(err, IsNil)
357+
358+ err = mydb.Login("myruser", "mypass")
359+ c.Assert(err, IsNil)
360+
361+ admindb.Logout()
362+
363+ coll := session.DB("mydb").C("mycoll")
364+ err = coll.Insert(M{"n": 1})
365+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
366+
367+ err = mydb.Login("mywuser", "mypass")
368+ c.Assert(err, IsNil)
369+
370+ err = coll.Insert(M{"n": 1})
371+ c.Assert(err, IsNil)
372+}
373+
374+func (s *S) TestAuthAddUserReplaces(c *C) {
375+ session, err := mgo.Dial("localhost:40002")
376+ c.Assert(err, IsNil)
377+ defer session.Close()
378+
379+ admindb := session.DB("admin")
380+ err = admindb.Login("root", "rapadura")
381+ c.Assert(err, IsNil)
382+
383+ mydb := session.DB("mydb")
384+ err = mydb.AddUser("myuser", "myoldpass", false)
385+ c.Assert(err, IsNil)
386+ err = mydb.AddUser("myuser", "mynewpass", true)
387+ c.Assert(err, IsNil)
388+
389+ admindb.Logout()
390+
391+ err = mydb.Login("myuser", "myoldpass")
392+ c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.")
393+ err = mydb.Login("myuser", "mynewpass")
394+ c.Assert(err, IsNil)
395+
396+ // ReadOnly flag was changed too.
397+ err = mydb.C("mycoll").Insert(M{"n": 1})
398+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
399+}
400+
401+func (s *S) TestAuthRemoveUser(c *C) {
402+ session, err := mgo.Dial("localhost:40002")
403+ c.Assert(err, IsNil)
404+ defer session.Close()
405+
406+ admindb := session.DB("admin")
407+ err = admindb.Login("root", "rapadura")
408+ c.Assert(err, IsNil)
409+
410+ mydb := session.DB("mydb")
411+ err = mydb.AddUser("myuser", "mypass", true)
412+ c.Assert(err, IsNil)
413+ err = mydb.RemoveUser("myuser")
414+ c.Assert(err, IsNil)
415+ err = mydb.RemoveUser("myuser")
416+ c.Assert(err, Equals, mgo.ErrNotFound)
417+
418+ err = mydb.Login("myuser", "mypass")
419+ c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.")
420+}
421+
422+func (s *S) TestAuthLoginTwiceDoesNothing(c *C) {
423+ session, err := mgo.Dial("localhost:40002")
424+ c.Assert(err, IsNil)
425+ defer session.Close()
426+
427+ admindb := session.DB("admin")
428+ err = admindb.Login("root", "rapadura")
429+ c.Assert(err, IsNil)
430+
431+ oldStats := mgo.GetStats()
432+
433+ err = admindb.Login("root", "rapadura")
434+ c.Assert(err, IsNil)
435+
436+ newStats := mgo.GetStats()
437+ c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
438+}
439+
440+func (s *S) TestAuthLoginLogoutLoginDoesNothing(c *C) {
441+ session, err := mgo.Dial("localhost:40002")
442+ c.Assert(err, IsNil)
443+ defer session.Close()
444+
445+ admindb := session.DB("admin")
446+ err = admindb.Login("root", "rapadura")
447+ c.Assert(err, IsNil)
448+
449+ oldStats := mgo.GetStats()
450+
451+ admindb.Logout()
452+ err = admindb.Login("root", "rapadura")
453+ c.Assert(err, IsNil)
454+
455+ newStats := mgo.GetStats()
456+ c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
457+}
458+
459+func (s *S) TestAuthLoginSwitchUser(c *C) {
460+ session, err := mgo.Dial("localhost:40002")
461+ c.Assert(err, IsNil)
462+ defer session.Close()
463+
464+ admindb := session.DB("admin")
465+ err = admindb.Login("root", "rapadura")
466+ c.Assert(err, IsNil)
467+
468+ coll := session.DB("mydb").C("mycoll")
469+ err = coll.Insert(M{"n": 1})
470+ c.Assert(err, IsNil)
471+
472+ err = admindb.Login("reader", "rapadura")
473+ c.Assert(err, IsNil)
474+
475+ // Can't write.
476+ err = coll.Insert(M{"n": 1})
477+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
478+
479+ // But can read.
480+ result := struct{ N int }{}
481+ err = coll.Find(nil).One(&result)
482+ c.Assert(err, IsNil)
483+ c.Assert(result.N, Equals, 1)
484+}
485+
486+func (s *S) TestAuthLoginChangePassword(c *C) {
487+ session, err := mgo.Dial("localhost:40002")
488+ c.Assert(err, IsNil)
489+ defer session.Close()
490+
491+ admindb := session.DB("admin")
492+ err = admindb.Login("root", "rapadura")
493+ c.Assert(err, IsNil)
494+
495+ mydb := session.DB("mydb")
496+ err = mydb.AddUser("myuser", "myoldpass", false)
497+ c.Assert(err, IsNil)
498+
499+ err = mydb.Login("myuser", "myoldpass")
500+ c.Assert(err, IsNil)
501+
502+ err = mydb.AddUser("myuser", "mynewpass", true)
503+ c.Assert(err, IsNil)
504+
505+ err = mydb.Login("myuser", "mynewpass")
506+ c.Assert(err, IsNil)
507+
508+ admindb.Logout()
509+
510+ // The second login must be in effect, which means read-only.
511+ err = mydb.C("mycoll").Insert(M{"n": 1})
512+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
513+}
514+
515+func (s *S) TestAuthLoginCachingWithSessionRefresh(c *C) {
516+ session, err := mgo.Dial("localhost:40002")
517+ c.Assert(err, IsNil)
518+ defer session.Close()
519+
520+ admindb := session.DB("admin")
521+ err = admindb.Login("root", "rapadura")
522+ c.Assert(err, IsNil)
523+
524+ session.Refresh()
525+
526+ coll := session.DB("mydb").C("mycoll")
527+ err = coll.Insert(M{"n": 1})
528+ c.Assert(err, IsNil)
529+}
530+
531+func (s *S) TestAuthLoginCachingWithSessionCopy(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+ session = session.Copy()
541+ defer session.Close()
542+
543+ coll := session.DB("mydb").C("mycoll")
544+ err = coll.Insert(M{"n": 1})
545+ c.Assert(err, IsNil)
546+}
547+
548+func (s *S) TestAuthLoginCachingWithSessionClone(c *C) {
549+ session, err := mgo.Dial("localhost:40002")
550+ c.Assert(err, IsNil)
551+ defer session.Close()
552+
553+ admindb := session.DB("admin")
554+ err = admindb.Login("root", "rapadura")
555+ c.Assert(err, IsNil)
556+
557+ session = session.Clone()
558+ defer session.Close()
559+
560+ coll := session.DB("mydb").C("mycoll")
561+ err = coll.Insert(M{"n": 1})
562+ c.Assert(err, IsNil)
563+}
564+
565+func (s *S) TestAuthLoginCachingWithNewSession(c *C) {
566+ session, err := mgo.Dial("localhost:40002")
567+ c.Assert(err, IsNil)
568+ defer session.Close()
569+
570+ admindb := session.DB("admin")
571+ err = admindb.Login("root", "rapadura")
572+ c.Assert(err, IsNil)
573+
574+ session = session.New()
575+ defer session.Close()
576+
577+ coll := session.DB("mydb").C("mycoll")
578+ err = coll.Insert(M{"n": 1})
579+ c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
580+}
581+
582+func (s *S) TestAuthLoginCachingAcrossPool(c *C) {
583+ // Logins are cached even when the conenction goes back
584+ // into the pool.
585+
586+ session, err := mgo.Dial("localhost:40002")
587+ c.Assert(err, IsNil)
588+ defer session.Close()
589+
590+ admindb := session.DB("admin")
591+ err = admindb.Login("root", "rapadura")
592+ c.Assert(err, IsNil)
593+
594+ // Add another user to test the logout case at the same time.
595+ mydb := session.DB("mydb")
596+ err = mydb.AddUser("myuser", "mypass", false)
597+ c.Assert(err, IsNil)
598+
599+ err = mydb.Login("myuser", "mypass")
600+ c.Assert(err, IsNil)
601+
602+ // Logout root explicitly, to test both cases.
603+ admindb.Logout()
604+
605+ // Give socket back to pool.
606+ session.Refresh()
607+
608+ // Brand new session, should use socket from the pool.
609+ other := session.New()
610+ defer other.Close()
611+
612+ oldStats := mgo.GetStats()
613+
614+ err = other.DB("admin").Login("root", "rapadura")
615+ c.Assert(err, IsNil)
616+ err = other.DB("mydb").Login("myuser", "mypass")
617+ c.Assert(err, IsNil)
618+
619+ // Both logins were cached, so no ops.
620+ newStats := mgo.GetStats()
621+ c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
622+
623+ // And they actually worked.
624+ err = other.DB("mydb").C("mycoll").Insert(M{"n": 1})
625+ c.Assert(err, IsNil)
626+
627+ other.DB("admin").Logout()
628+
629+ err = other.DB("mydb").C("mycoll").Insert(M{"n": 1})
630+ c.Assert(err, IsNil)
631+}
632+
633+func (s *S) TestAuthLoginCachingAcrossPoolWithLogout(c *C) {
634+ // Now verify that logouts are properly flushed if they
635+ // are not revalidated after leaving the pool.
636+
637+ session, err := mgo.Dial("localhost:40002")
638+ c.Assert(err, IsNil)
639+ defer session.Close()
640+
641+ admindb := session.DB("admin")
642+ err = admindb.Login("root", "rapadura")
643+ c.Assert(err, IsNil)
644+
645+ // Add another user to test the logout case at the same time.
646+ mydb := session.DB("mydb")
647+ err = mydb.AddUser("myuser", "mypass", true)
648+ c.Assert(err, IsNil)
649+
650+ err = mydb.Login("myuser", "mypass")
651+ c.Assert(err, IsNil)
652+
653+ // Just some data to query later.
654+ err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
655+ c.Assert(err, IsNil)
656+
657+ // Give socket back to pool.
658+ session.Refresh()
659+
660+ // Brand new session, should use socket from the pool.
661+ other := session.New()
662+ defer other.Close()
663+
664+ oldStats := mgo.GetStats()
665+
666+ err = other.DB("mydb").Login("myuser", "mypass")
667+ c.Assert(err, IsNil)
668+
669+ // Login was cached, so no ops.
670+ newStats := mgo.GetStats()
671+ c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
672+
673+ // Can't write, since root has been implicitly logged out
674+ // when the collection went into the pool, and not revalidated.
675+ err = other.DB("mydb").C("mycoll").Insert(M{"n": 1})
676+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
677+
678+ // But can read due to the revalidated myuser login.
679+ result := struct{ N int }{}
680+ err = other.DB("mydb").C("mycoll").Find(nil).One(&result)
681+ c.Assert(err, IsNil)
682+ c.Assert(result.N, Equals, 1)
683+}
684+
685+func (s *S) TestAuthEventual(c *C) {
686+ // Eventual sessions don't keep sockets around, so they are
687+ // an interesting test case.
688+ session, err := mgo.Dial("localhost:40002")
689+ c.Assert(err, IsNil)
690+ defer session.Close()
691+
692+ admindb := session.DB("admin")
693+ err = admindb.Login("root", "rapadura")
694+ c.Assert(err, IsNil)
695+
696+ err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
697+ c.Assert(err, IsNil)
698+
699+ var wg sync.WaitGroup
700+ wg.Add(20)
701+
702+ for i := 0; i != 10; i++ {
703+ go func() {
704+ defer wg.Done()
705+ var result struct{ N int }
706+ err := session.DB("mydb").C("mycoll").Find(nil).One(&result)
707+ c.Assert(err, IsNil)
708+ c.Assert(result.N, Equals, 1)
709+ }()
710+ }
711+
712+ for i := 0; i != 10; i++ {
713+ go func() {
714+ defer wg.Done()
715+ err := session.DB("mydb").C("mycoll").Insert(M{"n": 1})
716+ c.Assert(err, IsNil)
717+ }()
718+ }
719+
720+ wg.Wait()
721+}
722+
723+func (s *S) TestAuthURL(c *C) {
724+ session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002/")
725+ c.Assert(err, IsNil)
726+ defer session.Close()
727+
728+ err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
729+ c.Assert(err, IsNil)
730+}
731+
732+func (s *S) TestAuthURLWrongCredentials(c *C) {
733+ session, err := mgo.Dial("mongodb://root:wrong@localhost:40002/")
734+ if session != nil {
735+ session.Close()
736+ }
737+ c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.")
738+ c.Assert(session, IsNil)
739+}
740+
741+func (s *S) TestAuthURLWithNewSession(c *C) {
742+ // When authentication is in the URL, the new session will
743+ // actually carry it on as well, even if logged out explicitly.
744+ session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002/")
745+ c.Assert(err, IsNil)
746+ defer session.Close()
747+
748+ session.DB("admin").Logout()
749+
750+ // Do it twice to ensure it passes the needed data on.
751+ session = session.New()
752+ defer session.Close()
753+ session = session.New()
754+ defer session.Close()
755+
756+ err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
757+ c.Assert(err, IsNil)
758+}
759+
760+func (s *S) TestAuthURLWithDatabase(c *C) {
761+ session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002")
762+ c.Assert(err, IsNil)
763+ defer session.Close()
764+
765+ mydb := session.DB("mydb")
766+ err = mydb.AddUser("myruser", "mypass", true)
767+ c.Assert(err, IsNil)
768+
769+ // Test once with database, and once with source.
770+ for i := 0; i < 2; i++ {
771+ var url string
772+ if i == 0 {
773+ url = "mongodb://myruser:mypass@localhost:40002/mydb"
774+ } else {
775+ url = "mongodb://myruser:mypass@localhost:40002/admin?authSource=mydb"
776+ }
777+ usession, err := mgo.Dial(url)
778+ c.Assert(err, IsNil)
779+ defer usession.Close()
780+
781+ ucoll := usession.DB("mydb").C("mycoll")
782+ err = ucoll.FindId(0).One(nil)
783+ c.Assert(err, Equals, mgo.ErrNotFound)
784+ err = ucoll.Insert(M{"n": 1})
785+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
786+ }
787+}
788+
789+func (s *S) TestDefaultDatabase(c *C) {
790+ tests := []struct{ url, db string }{
791+ {"mongodb://root:rapadura@localhost:40002", "test"},
792+ {"mongodb://root:rapadura@localhost:40002/admin", "admin"},
793+ {"mongodb://localhost:40001", "test"},
794+ {"mongodb://localhost:40001/", "test"},
795+ {"mongodb://localhost:40001/mydb", "mydb"},
796+ }
797+
798+ for _, test := range tests {
799+ session, err := mgo.Dial(test.url)
800+ c.Assert(err, IsNil)
801+ defer session.Close()
802+
803+ c.Logf("test: %#v", test)
804+ c.Assert(session.DB("").Name, Equals, test.db)
805+
806+ scopy := session.Copy()
807+ c.Check(scopy.DB("").Name, Equals, test.db)
808+ scopy.Close()
809+ }
810+}
811+
812+func (s *S) TestAuthDirect(c *C) {
813+ // Direct connections must work to the master and slaves.
814+ for _, port := range []string{"40031", "40032", "40033"} {
815+ url := fmt.Sprintf("mongodb://root:rapadura@localhost:%s/?connect=direct", port)
816+ session, err := mgo.Dial(url)
817+ c.Assert(err, IsNil)
818+ defer session.Close()
819+
820+ session.SetMode(mgo.Monotonic, true)
821+
822+ var result struct{}
823+ err = session.DB("mydb").C("mycoll").Find(nil).One(&result)
824+ c.Assert(err, Equals, mgo.ErrNotFound)
825+ }
826+}
827+
828+func (s *S) TestAuthDirectWithLogin(c *C) {
829+ // Direct connections must work to the master and slaves.
830+ for _, port := range []string{"40031", "40032", "40033"} {
831+ url := fmt.Sprintf("mongodb://localhost:%s/?connect=direct", port)
832+ session, err := mgo.Dial(url)
833+ c.Assert(err, IsNil)
834+ defer session.Close()
835+
836+ session.SetMode(mgo.Monotonic, true)
837+ session.SetSyncTimeout(3 * time.Second)
838+
839+ err = session.DB("admin").Login("root", "rapadura")
840+ c.Assert(err, IsNil)
841+
842+ var result struct{}
843+ err = session.DB("mydb").C("mycoll").Find(nil).One(&result)
844+ c.Assert(err, Equals, mgo.ErrNotFound)
845+ }
846+}
847+
848+func (s *S) TestAuthScramSha1Cred(c *C) {
849+ if !s.versionAtLeast(2, 7, 7) {
850+ c.Skip("SCRAM-SHA-1 tests depend on 2.7.7")
851+ }
852+ cred := &mgo.Credential{
853+ Username: "root",
854+ Password: "rapadura",
855+ Mechanism: "SCRAM-SHA-1",
856+ Source: "admin",
857+ }
858+ host := "localhost:40002"
859+ c.Logf("Connecting to %s...", host)
860+ session, err := mgo.Dial(host)
861+ c.Assert(err, IsNil)
862+ defer session.Close()
863+
864+ mycoll := session.DB("admin").C("mycoll")
865+
866+ c.Logf("Connected! Testing the need for authentication...")
867+ err = mycoll.Find(nil).One(nil)
868+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
869+
870+ c.Logf("Authenticating...")
871+ err = session.Login(cred)
872+ c.Assert(err, IsNil)
873+ c.Logf("Authenticated!")
874+
875+ c.Logf("Connected! Testing the need for authentication...")
876+ err = mycoll.Find(nil).One(nil)
877+ c.Assert(err, Equals, mgo.ErrNotFound)
878+}
879+
880+func (s *S) TestAuthScramSha1URL(c *C) {
881+ if !s.versionAtLeast(2, 7, 7) {
882+ c.Skip("SCRAM-SHA-1 tests depend on 2.7.7")
883+ }
884+ host := "localhost:40002"
885+ c.Logf("Connecting to %s...", host)
886+ session, err := mgo.Dial(fmt.Sprintf("root:rapadura@%s?authMechanism=SCRAM-SHA-1", host))
887+ c.Assert(err, IsNil)
888+ defer session.Close()
889+
890+ mycoll := session.DB("admin").C("mycoll")
891+
892+ c.Logf("Connected! Testing the need for authentication...")
893+ err = mycoll.Find(nil).One(nil)
894+ c.Assert(err, Equals, mgo.ErrNotFound)
895+}
896+
897+func (s *S) TestAuthX509Cred(c *C) {
898+ session, err := mgo.Dial("localhost:40001")
899+ c.Assert(err, IsNil)
900+ defer session.Close()
901+ binfo, err := session.BuildInfo()
902+ c.Assert(err, IsNil)
903+ if binfo.OpenSSLVersion == "" {
904+ c.Skip("server does not support SSL")
905+ }
906+
907+ clientCertPEM, err := ioutil.ReadFile("testdb/client.pem")
908+ c.Assert(err, IsNil)
909+
910+ clientCert, err := tls.X509KeyPair(clientCertPEM, clientCertPEM)
911+ c.Assert(err, IsNil)
912+
913+ tlsConfig := &tls.Config{
914+ // Isolating tests to client certs, don't care about server validation.
915+ InsecureSkipVerify: true,
916+ Certificates: []tls.Certificate{clientCert},
917+ }
918+
919+ var host = "localhost:40003"
920+ c.Logf("Connecting to %s...", host)
921+ session, err = mgo.DialWithInfo(&mgo.DialInfo{
922+ Addrs: []string{host},
923+ DialServer: func(addr *mgo.ServerAddr) (net.Conn, error) {
924+ return tls.Dial("tcp", addr.String(), tlsConfig)
925+ },
926+ })
927+ c.Assert(err, IsNil)
928+ defer session.Close()
929+
930+ err = session.Login(&mgo.Credential{Username: "root", Password: "rapadura"})
931+ c.Assert(err, IsNil)
932+
933+ // This needs to be kept in sync with client.pem
934+ x509Subject := "CN=localhost,OU=Client,O=MGO,L=MGO,ST=MGO,C=GO"
935+
936+ externalDB := session.DB("$external")
937+ var x509User mgo.User = mgo.User{
938+ Username: x509Subject,
939+ OtherDBRoles: map[string][]mgo.Role{"admin": []mgo.Role{mgo.RoleRoot}},
940+ }
941+ err = externalDB.UpsertUser(&x509User)
942+ c.Assert(err, IsNil)
943+
944+ session.LogoutAll()
945+
946+ c.Logf("Connected! Ensuring authentication is required...")
947+ names, err := session.DatabaseNames()
948+ c.Assert(err, ErrorMatches, "not authorized .*")
949+
950+ cred := &mgo.Credential{
951+ Username: x509Subject,
952+ Mechanism: "MONGODB-X509",
953+ Source: "$external",
954+ }
955+
956+ c.Logf("Authenticating...")
957+ err = session.Login(cred)
958+ c.Assert(err, IsNil)
959+ c.Logf("Authenticated!")
960+
961+ names, err = session.DatabaseNames()
962+ c.Assert(err, IsNil)
963+ c.Assert(len(names) > 0, Equals, true)
964+}
965+
966+var (
967+ plainFlag = flag.String("plain", "", "Host to test PLAIN authentication against (depends on custom environment)")
968+ plainUser = "einstein"
969+ plainPass = "password"
970+)
971+
972+func (s *S) TestAuthPlainCred(c *C) {
973+ if *plainFlag == "" {
974+ c.Skip("no -plain")
975+ }
976+ cred := &mgo.Credential{
977+ Username: plainUser,
978+ Password: plainPass,
979+ Source: "$external",
980+ Mechanism: "PLAIN",
981+ }
982+ c.Logf("Connecting to %s...", *plainFlag)
983+ session, err := mgo.Dial(*plainFlag)
984+ c.Assert(err, IsNil)
985+ defer session.Close()
986+
987+ records := session.DB("records").C("records")
988+
989+ c.Logf("Connected! Testing the need for authentication...")
990+ err = records.Find(nil).One(nil)
991+ c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
992+
993+ c.Logf("Authenticating...")
994+ err = session.Login(cred)
995+ c.Assert(err, IsNil)
996+ c.Logf("Authenticated!")
997+
998+ c.Logf("Connected! Testing the need for authentication...")
999+ err = records.Find(nil).One(nil)
1000+ c.Assert(err, Equals, mgo.ErrNotFound)
1001+}
1002+
1003+func (s *S) TestAuthPlainURL(c *C) {
1004+ if *plainFlag == "" {
1005+ c.Skip("no -plain")
1006+ }
1007+ c.Logf("Connecting to %s...", *plainFlag)
1008+ session, err := mgo.Dial(fmt.Sprintf("%s:%s@%s?authMechanism=PLAIN", url.QueryEscape(plainUser), url.QueryEscape(plainPass), *plainFlag))
1009+ c.Assert(err, IsNil)
1010+ defer session.Close()
1011+
1012+ c.Logf("Connected! Testing the need for authentication...")
1013+ err = session.DB("records").C("records").Find(nil).One(nil)
1014+ c.Assert(err, Equals, mgo.ErrNotFound)
1015+}
1016+
1017+var (
1018+ kerberosFlag = flag.Bool("kerberos", false, "Test Kerberos authentication (depends on custom environment)")
1019+ kerberosHost = "ldaptest.10gen.cc"
1020+ kerberosUser = "drivers@LDAPTEST.10GEN.CC"
1021+
1022+ winKerberosPasswordEnv = "MGO_KERBEROS_PASSWORD"
1023+)
1024+
1025+// Kerberos has its own suite because it talks to a remote server
1026+// that is prepared to authenticate against a kerberos deployment.
1027+type KerberosSuite struct{}
1028+
1029+var _ = Suite(&KerberosSuite{})
1030+
1031+func (kerberosSuite *KerberosSuite) SetUpSuite(c *C) {
1032+ mgo.SetDebug(true)
1033+ mgo.SetStats(true)
1034+}
1035+
1036+func (kerberosSuite *KerberosSuite) TearDownSuite(c *C) {
1037+ mgo.SetDebug(false)
1038+ mgo.SetStats(false)
1039+}
1040+
1041+func (kerberosSuite *KerberosSuite) SetUpTest(c *C) {
1042+ mgo.SetLogger((*cLogger)(c))
1043+ mgo.ResetStats()
1044+}
1045+
1046+func (kerberosSuite *KerberosSuite) TearDownTest(c *C) {
1047+ mgo.SetLogger(nil)
1048+}
1049+
1050+func (kerberosSuite *KerberosSuite) TestAuthKerberosCred(c *C) {
1051+ if !*kerberosFlag {
1052+ c.Skip("no -kerberos")
1053+ }
1054+ cred := &mgo.Credential{
1055+ Username: kerberosUser,
1056+ Mechanism: "GSSAPI",
1057+ }
1058+ windowsAppendPasswordToCredential(cred)
1059+ c.Logf("Connecting to %s...", kerberosHost)
1060+ session, err := mgo.Dial(kerberosHost)
1061+ c.Assert(err, IsNil)
1062+ defer session.Close()
1063+
1064+ c.Logf("Connected! Testing the need for authentication...")
1065+ n, err := session.DB("kerberos").C("test").Find(M{}).Count()
1066+ c.Assert(err, ErrorMatches, ".*authorized.*")
1067+
1068+ c.Logf("Authenticating...")
1069+ err = session.Login(cred)
1070+ c.Assert(err, IsNil)
1071+ c.Logf("Authenticated!")
1072+
1073+ n, err = session.DB("kerberos").C("test").Find(M{}).Count()
1074+ c.Assert(err, IsNil)
1075+ c.Assert(n, Equals, 1)
1076+}
1077+
1078+func (kerberosSuite *KerberosSuite) TestAuthKerberosURL(c *C) {
1079+ if !*kerberosFlag {
1080+ c.Skip("no -kerberos")
1081+ }
1082+ c.Logf("Connecting to %s...", kerberosHost)
1083+ connectUri := url.QueryEscape(kerberosUser) + "@" + kerberosHost + "?authMechanism=GSSAPI"
1084+ if runtime.GOOS == "windows" {
1085+ connectUri = url.QueryEscape(kerberosUser) + ":" + url.QueryEscape(getWindowsKerberosPassword()) + "@" + kerberosHost + "?authMechanism=GSSAPI"
1086+ }
1087+ session, err := mgo.Dial(connectUri)
1088+ c.Assert(err, IsNil)
1089+ defer session.Close()
1090+ n, err := session.DB("kerberos").C("test").Find(M{}).Count()
1091+ c.Assert(err, IsNil)
1092+ c.Assert(n, Equals, 1)
1093+}
1094+
1095+func (kerberosSuite *KerberosSuite) TestAuthKerberosServiceName(c *C) {
1096+ if !*kerberosFlag {
1097+ c.Skip("no -kerberos")
1098+ }
1099+
1100+ wrongServiceName := "wrong"
1101+ rightServiceName := "mongodb"
1102+
1103+ cred := &mgo.Credential{
1104+ Username: kerberosUser,
1105+ Mechanism: "GSSAPI",
1106+ Service: wrongServiceName,
1107+ }
1108+ windowsAppendPasswordToCredential(cred)
1109+
1110+ c.Logf("Connecting to %s...", kerberosHost)
1111+ session, err := mgo.Dial(kerberosHost)
1112+ c.Assert(err, IsNil)
1113+ defer session.Close()
1114+
1115+ c.Logf("Authenticating with incorrect service name...")
1116+ err = session.Login(cred)
1117+ c.Assert(err, ErrorMatches, ".*@LDAPTEST.10GEN.CC not found.*")
1118+
1119+ cred.Service = rightServiceName
1120+ c.Logf("Authenticating with correct service name...")
1121+ err = session.Login(cred)
1122+ c.Assert(err, IsNil)
1123+ c.Logf("Authenticated!")
1124+
1125+ n, err := session.DB("kerberos").C("test").Find(M{}).Count()
1126+ c.Assert(err, IsNil)
1127+ c.Assert(n, Equals, 1)
1128+}
1129+
1130+func (kerberosSuite *KerberosSuite) TestAuthKerberosServiceHost(c *C) {
1131+ if !*kerberosFlag {
1132+ c.Skip("no -kerberos")
1133+ }
1134+
1135+ wrongServiceHost := "eggs.bacon.tk"
1136+ rightServiceHost := kerberosHost
1137+
1138+ cred := &mgo.Credential{
1139+ Username: kerberosUser,
1140+ Mechanism: "GSSAPI",
1141+ ServiceHost: wrongServiceHost,
1142+ }
1143+ windowsAppendPasswordToCredential(cred)
1144+
1145+ c.Logf("Connecting to %s...", kerberosHost)
1146+ session, err := mgo.Dial(kerberosHost)
1147+ c.Assert(err, IsNil)
1148+ defer session.Close()
1149+
1150+ c.Logf("Authenticating with incorrect service host...")
1151+ err = session.Login(cred)
1152+ c.Assert(err, ErrorMatches, ".*@LDAPTEST.10GEN.CC not found.*")
1153+
1154+ cred.ServiceHost = rightServiceHost
1155+ c.Logf("Authenticating with correct service host...")
1156+ err = session.Login(cred)
1157+ c.Assert(err, IsNil)
1158+ c.Logf("Authenticated!")
1159+
1160+ n, err := session.DB("kerberos").C("test").Find(M{}).Count()
1161+ c.Assert(err, IsNil)
1162+ c.Assert(n, Equals, 1)
1163+}
1164+
1165+// No kinit on SSPI-style Kerberos, so we need to provide a password. In order
1166+// to avoid inlining password, require it to be set as an environment variable,
1167+// for instance: `SET MGO_KERBEROS_PASSWORD=this_isnt_the_password`
1168+func getWindowsKerberosPassword() string {
1169+ pw := os.Getenv(winKerberosPasswordEnv)
1170+ if pw == "" {
1171+ panic(fmt.Sprintf("Need to set %v environment variable to run Kerberos tests on Windows", winKerberosPasswordEnv))
1172+ }
1173+ return pw
1174+}
1175+
1176+func windowsAppendPasswordToCredential(cred *mgo.Credential) {
1177+ if runtime.GOOS == "windows" {
1178+ cred.Password = getWindowsKerberosPassword()
1179+ }
1180+}
vendor/gopkg.in/mgo.v2/bson/LICENSE+25−0
@@ -0,0 +1,25 @@
1+BSON library for Go
2+
3+Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+
5+All rights reserved.
6+
7+Redistribution and use in source and binary forms, with or without
8+modification, are permitted provided that the following conditions are met:
9+
10+1. Redistributions of source code must retain the above copyright notice, this
11+ list of conditions and the following disclaimer.
12+2. Redistributions in binary form must reproduce the above copyright notice,
13+ this list of conditions and the following disclaimer in the documentation
14+ and/or other materials provided with the distribution.
15+
16+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
vendor/gopkg.in/mgo.v2/bson/bson.go+705−0
@@ -0,0 +1,705 @@
1+// BSON library for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+// Package bson is an implementation of the BSON specification for Go:
28+//
29+// http://bsonspec.org
30+//
31+// It was created as part of the mgo MongoDB driver for Go, but is standalone
32+// and may be used on its own without the driver.
33+package bson
34+
35+import (
36+ "bytes"
37+ "crypto/md5"
38+ "crypto/rand"
39+ "encoding/binary"
40+ "encoding/hex"
41+ "errors"
42+ "fmt"
43+ "io"
44+ "os"
45+ "reflect"
46+ "runtime"
47+ "strings"
48+ "sync"
49+ "sync/atomic"
50+ "time"
51+)
52+
53+// --------------------------------------------------------------------------
54+// The public API.
55+
56+// A value implementing the bson.Getter interface will have its GetBSON
57+// method called when the given value has to be marshalled, and the result
58+// of this method will be marshaled in place of the actual object.
59+//
60+// If GetBSON returns return a non-nil error, the marshalling procedure
61+// will stop and error out with the provided value.
62+type Getter interface {
63+ GetBSON() (interface{}, error)
64+}
65+
66+// A value implementing the bson.Setter interface will receive the BSON
67+// value via the SetBSON method during unmarshaling, and the object
68+// itself will not be changed as usual.
69+//
70+// If setting the value works, the method should return nil or alternatively
71+// bson.SetZero to set the respective field to its zero value (nil for
72+// pointer types). If SetBSON returns a value of type bson.TypeError, the
73+// BSON value will be omitted from a map or slice being decoded and the
74+// unmarshalling will continue. If it returns any other non-nil error, the
75+// unmarshalling procedure will stop and error out with the provided value.
76+//
77+// This interface is generally useful in pointer receivers, since the method
78+// will want to change the receiver. A type field that implements the Setter
79+// interface doesn't have to be a pointer, though.
80+//
81+// Unlike the usual behavior, unmarshalling onto a value that implements a
82+// Setter interface will NOT reset the value to its zero state. This allows
83+// the value to decide by itself how to be unmarshalled.
84+//
85+// For example:
86+//
87+// type MyString string
88+//
89+// func (s *MyString) SetBSON(raw bson.Raw) error {
90+// return raw.Unmarshal(s)
91+// }
92+//
93+type Setter interface {
94+ SetBSON(raw Raw) error
95+}
96+
97+// SetZero may be returned from a SetBSON method to have the value set to
98+// its respective zero value. When used in pointer values, this will set the
99+// field to nil rather than to the pre-allocated value.
100+var SetZero = errors.New("set to zero")
101+
102+// M is a convenient alias for a map[string]interface{} map, useful for
103+// dealing with BSON in a native way. For instance:
104+//
105+// bson.M{"a": 1, "b": true}
106+//
107+// There's no special handling for this type in addition to what's done anyway
108+// for an equivalent map type. Elements in the map will be dumped in an
109+// undefined ordered. See also the bson.D type for an ordered alternative.
110+type M map[string]interface{}
111+
112+// D represents a BSON document containing ordered elements. For example:
113+//
114+// bson.D{{"a", 1}, {"b", true}}
115+//
116+// In some situations, such as when creating indexes for MongoDB, the order in
117+// which the elements are defined is important. If the order is not important,
118+// using a map is generally more comfortable. See bson.M and bson.RawD.
119+type D []DocElem
120+
121+// DocElem is an element of the bson.D document representation.
122+type DocElem struct {
123+ Name string
124+ Value interface{}
125+}
126+
127+// Map returns a map out of the ordered element name/value pairs in d.
128+func (d D) Map() (m M) {
129+ m = make(M, len(d))
130+ for _, item := range d {
131+ m[item.Name] = item.Value
132+ }
133+ return m
134+}
135+
136+// The Raw type represents raw unprocessed BSON documents and elements.
137+// Kind is the kind of element as defined per the BSON specification, and
138+// Data is the raw unprocessed data for the respective element.
139+// Using this type it is possible to unmarshal or marshal values partially.
140+//
141+// Relevant documentation:
142+//
143+// http://bsonspec.org/#/specification
144+//
145+type Raw struct {
146+ Kind byte
147+ Data []byte
148+}
149+
150+// RawD represents a BSON document containing raw unprocessed elements.
151+// This low-level representation may be useful when lazily processing
152+// documents of uncertain content, or when manipulating the raw content
153+// documents in general.
154+type RawD []RawDocElem
155+
156+// See the RawD type.
157+type RawDocElem struct {
158+ Name string
159+ Value Raw
160+}
161+
162+// ObjectId is a unique ID identifying a BSON value. It must be exactly 12 bytes
163+// long. MongoDB objects by default have such a property set in their "_id"
164+// property.
165+//
166+// http://www.mongodb.org/display/DOCS/Object+IDs
167+type ObjectId string
168+
169+// ObjectIdHex returns an ObjectId from the provided hex representation.
170+// Calling this function with an invalid hex representation will
171+// cause a runtime panic. See the IsObjectIdHex function.
172+func ObjectIdHex(s string) ObjectId {
173+ d, err := hex.DecodeString(s)
174+ if err != nil || len(d) != 12 {
175+ panic(fmt.Sprintf("Invalid input to ObjectIdHex: %q", s))
176+ }
177+ return ObjectId(d)
178+}
179+
180+// IsObjectIdHex returns whether s is a valid hex representation of
181+// an ObjectId. See the ObjectIdHex function.
182+func IsObjectIdHex(s string) bool {
183+ if len(s) != 24 {
184+ return false
185+ }
186+ _, err := hex.DecodeString(s)
187+ return err == nil
188+}
189+
190+// objectIdCounter is atomically incremented when generating a new ObjectId
191+// using NewObjectId() function. It's used as a counter part of an id.
192+var objectIdCounter uint32 = 0
193+
194+// machineId stores machine id generated once and used in subsequent calls
195+// to NewObjectId function.
196+var machineId = readMachineId()
197+
198+// readMachineId generates machine id and puts it into the machineId global
199+// variable. If this function fails to get the hostname, it will cause
200+// a runtime error.
201+func readMachineId() []byte {
202+ var sum [3]byte
203+ id := sum[:]
204+ hostname, err1 := os.Hostname()
205+ if err1 != nil {
206+ _, err2 := io.ReadFull(rand.Reader, id)
207+ if err2 != nil {
208+ panic(fmt.Errorf("cannot get hostname: %v; %v", err1, err2))
209+ }
210+ return id
211+ }
212+ hw := md5.New()
213+ hw.Write([]byte(hostname))
214+ copy(id, hw.Sum(nil))
215+ return id
216+}
217+
218+// NewObjectId returns a new unique ObjectId.
219+func NewObjectId() ObjectId {
220+ var b [12]byte
221+ // Timestamp, 4 bytes, big endian
222+ binary.BigEndian.PutUint32(b[:], uint32(time.Now().Unix()))
223+ // Machine, first 3 bytes of md5(hostname)
224+ b[4] = machineId[0]
225+ b[5] = machineId[1]
226+ b[6] = machineId[2]
227+ // Pid, 2 bytes, specs don't specify endianness, but we use big endian.
228+ pid := os.Getpid()
229+ b[7] = byte(pid >> 8)
230+ b[8] = byte(pid)
231+ // Increment, 3 bytes, big endian
232+ i := atomic.AddUint32(&objectIdCounter, 1)
233+ b[9] = byte(i >> 16)
234+ b[10] = byte(i >> 8)
235+ b[11] = byte(i)
236+ return ObjectId(b[:])
237+}
238+
239+// NewObjectIdWithTime returns a dummy ObjectId with the timestamp part filled
240+// with the provided number of seconds from epoch UTC, and all other parts
241+// filled with zeroes. It's not safe to insert a document with an id generated
242+// by this method, it is useful only for queries to find documents with ids
243+// generated before or after the specified timestamp.
244+func NewObjectIdWithTime(t time.Time) ObjectId {
245+ var b [12]byte
246+ binary.BigEndian.PutUint32(b[:4], uint32(t.Unix()))
247+ return ObjectId(string(b[:]))
248+}
249+
250+// String returns a hex string representation of the id.
251+// Example: ObjectIdHex("4d88e15b60f486e428412dc9").
252+func (id ObjectId) String() string {
253+ return fmt.Sprintf(`ObjectIdHex("%x")`, string(id))
254+}
255+
256+// Hex returns a hex representation of the ObjectId.
257+func (id ObjectId) Hex() string {
258+ return hex.EncodeToString([]byte(id))
259+}
260+
261+// MarshalJSON turns a bson.ObjectId into a json.Marshaller.
262+func (id ObjectId) MarshalJSON() ([]byte, error) {
263+ return []byte(fmt.Sprintf(`"%x"`, string(id))), nil
264+}
265+
266+var nullBytes = []byte("null")
267+
268+// UnmarshalJSON turns *bson.ObjectId into a json.Unmarshaller.
269+func (id *ObjectId) UnmarshalJSON(data []byte) error {
270+ if len(data) == 2 && data[0] == '"' && data[1] == '"' || bytes.Equal(data, nullBytes) {
271+ *id = ""
272+ return nil
273+ }
274+ if len(data) != 26 || data[0] != '"' || data[25] != '"' {
275+ return errors.New(fmt.Sprintf("Invalid ObjectId in JSON: %s", string(data)))
276+ }
277+ var buf [12]byte
278+ _, err := hex.Decode(buf[:], data[1:25])
279+ if err != nil {
280+ return errors.New(fmt.Sprintf("Invalid ObjectId in JSON: %s (%s)", string(data), err))
281+ }
282+ *id = ObjectId(string(buf[:]))
283+ return nil
284+}
285+
286+// Valid returns true if id is valid. A valid id must contain exactly 12 bytes.
287+func (id ObjectId) Valid() bool {
288+ return len(id) == 12
289+}
290+
291+// byteSlice returns byte slice of id from start to end.
292+// Calling this function with an invalid id will cause a runtime panic.
293+func (id ObjectId) byteSlice(start, end int) []byte {
294+ if len(id) != 12 {
295+ panic(fmt.Sprintf("Invalid ObjectId: %q", string(id)))
296+ }
297+ return []byte(string(id)[start:end])
298+}
299+
300+// Time returns the timestamp part of the id.
301+// It's a runtime error to call this method with an invalid id.
302+func (id ObjectId) Time() time.Time {
303+ // First 4 bytes of ObjectId is 32-bit big-endian seconds from epoch.
304+ secs := int64(binary.BigEndian.Uint32(id.byteSlice(0, 4)))
305+ return time.Unix(secs, 0)
306+}
307+
308+// Machine returns the 3-byte machine id part of the id.
309+// It's a runtime error to call this method with an invalid id.
310+func (id ObjectId) Machine() []byte {
311+ return id.byteSlice(4, 7)
312+}
313+
314+// Pid returns the process id part of the id.
315+// It's a runtime error to call this method with an invalid id.
316+func (id ObjectId) Pid() uint16 {
317+ return binary.BigEndian.Uint16(id.byteSlice(7, 9))
318+}
319+
320+// Counter returns the incrementing value part of the id.
321+// It's a runtime error to call this method with an invalid id.
322+func (id ObjectId) Counter() int32 {
323+ b := id.byteSlice(9, 12)
324+ // Counter is stored as big-endian 3-byte value
325+ return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2]))
326+}
327+
328+// The Symbol type is similar to a string and is used in languages with a
329+// distinct symbol type.
330+type Symbol string
331+
332+// Now returns the current time with millisecond precision. MongoDB stores
333+// timestamps with the same precision, so a Time returned from this method
334+// will not change after a roundtrip to the database. That's the only reason
335+// why this function exists. Using the time.Now function also works fine
336+// otherwise.
337+func Now() time.Time {
338+ return time.Unix(0, time.Now().UnixNano()/1e6*1e6)
339+}
340+
341+// MongoTimestamp is a special internal type used by MongoDB that for some
342+// strange reason has its own datatype defined in BSON.
343+type MongoTimestamp int64
344+
345+type orderKey int64
346+
347+// MaxKey is a special value that compares higher than all other possible BSON
348+// values in a MongoDB database.
349+var MaxKey = orderKey(1<<63 - 1)
350+
351+// MinKey is a special value that compares lower than all other possible BSON
352+// values in a MongoDB database.
353+var MinKey = orderKey(-1 << 63)
354+
355+type undefined struct{}
356+
357+// Undefined represents the undefined BSON value.
358+var Undefined undefined
359+
360+// Binary is a representation for non-standard binary values. Any kind should
361+// work, but the following are known as of this writing:
362+//
363+// 0x00 - Generic. This is decoded as []byte(data), not Binary{0x00, data}.
364+// 0x01 - Function (!?)
365+// 0x02 - Obsolete generic.
366+// 0x03 - UUID
367+// 0x05 - MD5
368+// 0x80 - User defined.
369+//
370+type Binary struct {
371+ Kind byte
372+ Data []byte
373+}
374+
375+// RegEx represents a regular expression. The Options field may contain
376+// individual characters defining the way in which the pattern should be
377+// applied, and must be sorted. Valid options as of this writing are 'i' for
378+// case insensitive matching, 'm' for multi-line matching, 'x' for verbose
379+// mode, 'l' to make \w, \W, and similar be locale-dependent, 's' for dot-all
380+// mode (a '.' matches everything), and 'u' to make \w, \W, and similar match
381+// unicode. The value of the Options parameter is not verified before being
382+// marshaled into the BSON format.
383+type RegEx struct {
384+ Pattern string
385+ Options string
386+}
387+
388+// JavaScript is a type that holds JavaScript code. If Scope is non-nil, it
389+// will be marshaled as a mapping from identifiers to values that may be
390+// used when evaluating the provided Code.
391+type JavaScript struct {
392+ Code string
393+ Scope interface{}
394+}
395+
396+// DBPointer refers to a document id in a namespace.
397+//
398+// This type is deprecated in the BSON specification and should not be used
399+// except for backwards compatibility with ancient applications.
400+type DBPointer struct {
401+ Namespace string
402+ Id ObjectId
403+}
404+
405+const initialBufferSize = 64
406+
407+func handleErr(err *error) {
408+ if r := recover(); r != nil {
409+ if _, ok := r.(runtime.Error); ok {
410+ panic(r)
411+ } else if _, ok := r.(externalPanic); ok {
412+ panic(r)
413+ } else if s, ok := r.(string); ok {
414+ *err = errors.New(s)
415+ } else if e, ok := r.(error); ok {
416+ *err = e
417+ } else {
418+ panic(r)
419+ }
420+ }
421+}
422+
423+// Marshal serializes the in value, which may be a map or a struct value.
424+// In the case of struct values, only exported fields will be serialized.
425+// The lowercased field name is used as the key for each exported field,
426+// but this behavior may be changed using the respective field tag.
427+// The tag may also contain flags to tweak the marshalling behavior for
428+// the field. The tag formats accepted are:
429+//
430+// "[<key>][,<flag1>[,<flag2>]]"
431+//
432+// `(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)`
433+//
434+// The following flags are currently supported:
435+//
436+// omitempty Only include the field if it's not set to the zero
437+// value for the type or to empty slices or maps.
438+//
439+// minsize Marshal an int64 value as an int32, if that's feasible
440+// while preserving the numeric value.
441+//
442+// inline Inline the field, which must be a struct or a map,
443+// causing all of its fields or keys to be processed as if
444+// they were part of the outer struct. For maps, keys must
445+// not conflict with the bson keys of other struct fields.
446+//
447+// Some examples:
448+//
449+// type T struct {
450+// A bool
451+// B int "myb"
452+// C string "myc,omitempty"
453+// D string `bson:",omitempty" json:"jsonkey"`
454+// E int64 ",minsize"
455+// F int64 "myf,omitempty,minsize"
456+// }
457+//
458+func Marshal(in interface{}) (out []byte, err error) {
459+ defer handleErr(&err)
460+ e := &encoder{make([]byte, 0, initialBufferSize)}
461+ e.addDoc(reflect.ValueOf(in))
462+ return e.out, nil
463+}
464+
465+// Unmarshal deserializes data from in into the out value. The out value
466+// must be a map, a pointer to a struct, or a pointer to a bson.D value.
467+// The lowercased field name is used as the key for each exported field,
468+// but this behavior may be changed using the respective field tag.
469+// The tag may also contain flags to tweak the marshalling behavior for
470+// the field. The tag formats accepted are:
471+//
472+// "[<key>][,<flag1>[,<flag2>]]"
473+//
474+// `(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)`
475+//
476+// The following flags are currently supported during unmarshal (see the
477+// Marshal method for other flags):
478+//
479+// inline Inline the field, which must be a struct or a map.
480+// Inlined structs are handled as if its fields were part
481+// of the outer struct. An inlined map causes keys that do
482+// not match any other struct field to be inserted in the
483+// map rather than being discarded as usual.
484+//
485+// The target field or element types of out may not necessarily match
486+// the BSON values of the provided data. The following conversions are
487+// made automatically:
488+//
489+// - Numeric types are converted if at least the integer part of the
490+// value would be preserved correctly
491+// - Bools are converted to numeric types as 1 or 0
492+// - Numeric types are converted to bools as true if not 0 or false otherwise
493+// - Binary and string BSON data is converted to a string, array or byte slice
494+//
495+// If the value would not fit the type and cannot be converted, it's
496+// silently skipped.
497+//
498+// Pointer values are initialized when necessary.
499+func Unmarshal(in []byte, out interface{}) (err error) {
500+ if raw, ok := out.(*Raw); ok {
501+ raw.Kind = 3
502+ raw.Data = in
503+ return nil
504+ }
505+ defer handleErr(&err)
506+ v := reflect.ValueOf(out)
507+ switch v.Kind() {
508+ case reflect.Ptr:
509+ fallthrough
510+ case reflect.Map:
511+ d := newDecoder(in)
512+ d.readDocTo(v)
513+ case reflect.Struct:
514+ return errors.New("Unmarshal can't deal with struct values. Use a pointer.")
515+ default:
516+ return errors.New("Unmarshal needs a map or a pointer to a struct.")
517+ }
518+ return nil
519+}
520+
521+// Unmarshal deserializes raw into the out value. If the out value type
522+// is not compatible with raw, a *bson.TypeError is returned.
523+//
524+// See the Unmarshal function documentation for more details on the
525+// unmarshalling process.
526+func (raw Raw) Unmarshal(out interface{}) (err error) {
527+ defer handleErr(&err)
528+ v := reflect.ValueOf(out)
529+ switch v.Kind() {
530+ case reflect.Ptr:
531+ v = v.Elem()
532+ fallthrough
533+ case reflect.Map:
534+ d := newDecoder(raw.Data)
535+ good := d.readElemTo(v, raw.Kind)
536+ if !good {
537+ return &TypeError{v.Type(), raw.Kind}
538+ }
539+ case reflect.Struct:
540+ return errors.New("Raw Unmarshal can't deal with struct values. Use a pointer.")
541+ default:
542+ return errors.New("Raw Unmarshal needs a map or a valid pointer.")
543+ }
544+ return nil
545+}
546+
547+type TypeError struct {
548+ Type reflect.Type
549+ Kind byte
550+}
551+
552+func (e *TypeError) Error() string {
553+ return fmt.Sprintf("BSON kind 0x%02x isn't compatible with type %s", e.Kind, e.Type.String())
554+}
555+
556+// --------------------------------------------------------------------------
557+// Maintain a mapping of keys to structure field indexes
558+
559+type structInfo struct {
560+ FieldsMap map[string]fieldInfo
561+ FieldsList []fieldInfo
562+ InlineMap int
563+ Zero reflect.Value
564+}
565+
566+type fieldInfo struct {
567+ Key string
568+ Num int
569+ OmitEmpty bool
570+ MinSize bool
571+ Inline []int
572+}
573+
574+var structMap = make(map[reflect.Type]*structInfo)
575+var structMapMutex sync.RWMutex
576+
577+type externalPanic string
578+
579+func (e externalPanic) String() string {
580+ return string(e)
581+}
582+
583+func getStructInfo(st reflect.Type) (*structInfo, error) {
584+ structMapMutex.RLock()
585+ sinfo, found := structMap[st]
586+ structMapMutex.RUnlock()
587+ if found {
588+ return sinfo, nil
589+ }
590+ n := st.NumField()
591+ fieldsMap := make(map[string]fieldInfo)
592+ fieldsList := make([]fieldInfo, 0, n)
593+ inlineMap := -1
594+ for i := 0; i != n; i++ {
595+ field := st.Field(i)
596+ if field.PkgPath != "" {
597+ continue // Private field
598+ }
599+
600+ info := fieldInfo{Num: i}
601+
602+ tag := field.Tag.Get("bson")
603+ if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
604+ tag = string(field.Tag)
605+ }
606+ if tag == "-" {
607+ continue
608+ }
609+
610+ // XXX Drop this after a few releases.
611+ if s := strings.Index(tag, "/"); s >= 0 {
612+ recommend := tag[:s]
613+ for _, c := range tag[s+1:] {
614+ switch c {
615+ case 'c':
616+ recommend += ",omitempty"
617+ case 's':
618+ recommend += ",minsize"
619+ default:
620+ msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", string([]byte{uint8(c)}), tag, st)
621+ panic(externalPanic(msg))
622+ }
623+ }
624+ msg := fmt.Sprintf("Replace tag %q in field %s of type %s by %q", tag, field.Name, st, recommend)
625+ panic(externalPanic(msg))
626+ }
627+
628+ inline := false
629+ fields := strings.Split(tag, ",")
630+ if len(fields) > 1 {
631+ for _, flag := range fields[1:] {
632+ switch flag {
633+ case "omitempty":
634+ info.OmitEmpty = true
635+ case "minsize":
636+ info.MinSize = true
637+ case "inline":
638+ inline = true
639+ default:
640+ msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
641+ panic(externalPanic(msg))
642+ }
643+ }
644+ tag = fields[0]
645+ }
646+
647+ if inline {
648+ switch field.Type.Kind() {
649+ case reflect.Map:
650+ if inlineMap >= 0 {
651+ return nil, errors.New("Multiple ,inline maps in struct " + st.String())
652+ }
653+ if field.Type.Key() != reflect.TypeOf("") {
654+ return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
655+ }
656+ inlineMap = info.Num
657+ case reflect.Struct:
658+ sinfo, err := getStructInfo(field.Type)
659+ if err != nil {
660+ return nil, err
661+ }
662+ for _, finfo := range sinfo.FieldsList {
663+ if _, found := fieldsMap[finfo.Key]; found {
664+ msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
665+ return nil, errors.New(msg)
666+ }
667+ if finfo.Inline == nil {
668+ finfo.Inline = []int{i, finfo.Num}
669+ } else {
670+ finfo.Inline = append([]int{i}, finfo.Inline...)
671+ }
672+ fieldsMap[finfo.Key] = finfo
673+ fieldsList = append(fieldsList, finfo)
674+ }
675+ default:
676+ panic("Option ,inline needs a struct value or map field")
677+ }
678+ continue
679+ }
680+
681+ if tag != "" {
682+ info.Key = tag
683+ } else {
684+ info.Key = strings.ToLower(field.Name)
685+ }
686+
687+ if _, found = fieldsMap[info.Key]; found {
688+ msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
689+ return nil, errors.New(msg)
690+ }
691+
692+ fieldsList = append(fieldsList, info)
693+ fieldsMap[info.Key] = info
694+ }
695+ sinfo = &structInfo{
696+ fieldsMap,
697+ fieldsList,
698+ inlineMap,
699+ reflect.New(st).Elem(),
700+ }
701+ structMapMutex.Lock()
702+ structMap[st] = sinfo
703+ structMapMutex.Unlock()
704+ return sinfo, nil
705+}
vendor/gopkg.in/mgo.v2/bson/bson_test.go+1605−0
@@ -0,0 +1,1605 @@
1+// BSON library for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+// gobson - BSON library for Go.
27+
28+package bson_test
29+
30+import (
31+ "encoding/binary"
32+ "encoding/json"
33+ "errors"
34+ "net/url"
35+ "reflect"
36+ "testing"
37+ "time"
38+
39+ . "gopkg.in/check.v1"
40+ "gopkg.in/mgo.v2/bson"
41+)
42+
43+func TestAll(t *testing.T) {
44+ TestingT(t)
45+}
46+
47+type S struct{}
48+
49+var _ = Suite(&S{})
50+
51+// Wrap up the document elements contained in data, prepending the int32
52+// length of the data, and appending the '\x00' value closing the document.
53+func wrapInDoc(data string) string {
54+ result := make([]byte, len(data)+5)
55+ binary.LittleEndian.PutUint32(result, uint32(len(result)))
56+ copy(result[4:], []byte(data))
57+ return string(result)
58+}
59+
60+func makeZeroDoc(value interface{}) (zero interface{}) {
61+ v := reflect.ValueOf(value)
62+ t := v.Type()
63+ switch t.Kind() {
64+ case reflect.Map:
65+ mv := reflect.MakeMap(t)
66+ zero = mv.Interface()
67+ case reflect.Ptr:
68+ pv := reflect.New(v.Type().Elem())
69+ zero = pv.Interface()
70+ case reflect.Slice, reflect.Int:
71+ zero = reflect.New(t).Interface()
72+ default:
73+ panic("unsupported doc type")
74+ }
75+ return zero
76+}
77+
78+func testUnmarshal(c *C, data string, obj interface{}) {
79+ zero := makeZeroDoc(obj)
80+ err := bson.Unmarshal([]byte(data), zero)
81+ c.Assert(err, IsNil)
82+ c.Assert(zero, DeepEquals, obj)
83+}
84+
85+type testItemType struct {
86+ obj interface{}
87+ data string
88+}
89+
90+// --------------------------------------------------------------------------
91+// Samples from bsonspec.org:
92+
93+var sampleItems = []testItemType{
94+ {bson.M{"hello": "world"},
95+ "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00"},
96+ {bson.M{"BSON": []interface{}{"awesome", float64(5.05), 1986}},
97+ "1\x00\x00\x00\x04BSON\x00&\x00\x00\x00\x020\x00\x08\x00\x00\x00" +
98+ "awesome\x00\x011\x00333333\x14@\x102\x00\xc2\x07\x00\x00\x00\x00"},
99+}
100+
101+func (s *S) TestMarshalSampleItems(c *C) {
102+ for i, item := range sampleItems {
103+ data, err := bson.Marshal(item.obj)
104+ c.Assert(err, IsNil)
105+ c.Assert(string(data), Equals, item.data, Commentf("Failed on item %d", i))
106+ }
107+}
108+
109+func (s *S) TestUnmarshalSampleItems(c *C) {
110+ for i, item := range sampleItems {
111+ value := bson.M{}
112+ err := bson.Unmarshal([]byte(item.data), value)
113+ c.Assert(err, IsNil)
114+ c.Assert(value, DeepEquals, item.obj, Commentf("Failed on item %d", i))
115+ }
116+}
117+
118+// --------------------------------------------------------------------------
119+// Every type, ordered by the type flag. These are not wrapped with the
120+// length and last \x00 from the document. wrapInDoc() computes them.
121+// Note that all of them should be supported as two-way conversions.
122+
123+var allItems = []testItemType{
124+ {bson.M{},
125+ ""},
126+ {bson.M{"_": float64(5.05)},
127+ "\x01_\x00333333\x14@"},
128+ {bson.M{"_": "yo"},
129+ "\x02_\x00\x03\x00\x00\x00yo\x00"},
130+ {bson.M{"_": bson.M{"a": true}},
131+ "\x03_\x00\x09\x00\x00\x00\x08a\x00\x01\x00"},
132+ {bson.M{"_": []interface{}{true, false}},
133+ "\x04_\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"},
134+ {bson.M{"_": []byte("yo")},
135+ "\x05_\x00\x02\x00\x00\x00\x00yo"},
136+ {bson.M{"_": bson.Binary{0x80, []byte("udef")}},
137+ "\x05_\x00\x04\x00\x00\x00\x80udef"},
138+ {bson.M{"_": bson.Undefined}, // Obsolete, but still seen in the wild.
139+ "\x06_\x00"},
140+ {bson.M{"_": bson.ObjectId("0123456789ab")},
141+ "\x07_\x000123456789ab"},
142+ {bson.M{"_": bson.DBPointer{"testnamespace", bson.ObjectId("0123456789ab")}},
143+ "\x0C_\x00\x0e\x00\x00\x00testnamespace\x000123456789ab"},
144+ {bson.M{"_": false},
145+ "\x08_\x00\x00"},
146+ {bson.M{"_": true},
147+ "\x08_\x00\x01"},
148+ {bson.M{"_": time.Unix(0, 258e6)}, // Note the NS <=> MS conversion.
149+ "\x09_\x00\x02\x01\x00\x00\x00\x00\x00\x00"},
150+ {bson.M{"_": nil},
151+ "\x0A_\x00"},
152+ {bson.M{"_": bson.RegEx{"ab", "cd"}},
153+ "\x0B_\x00ab\x00cd\x00"},
154+ {bson.M{"_": bson.JavaScript{"code", nil}},
155+ "\x0D_\x00\x05\x00\x00\x00code\x00"},
156+ {bson.M{"_": bson.Symbol("sym")},
157+ "\x0E_\x00\x04\x00\x00\x00sym\x00"},
158+ {bson.M{"_": bson.JavaScript{"code", bson.M{"": nil}}},
159+ "\x0F_\x00\x14\x00\x00\x00\x05\x00\x00\x00code\x00" +
160+ "\x07\x00\x00\x00\x0A\x00\x00"},
161+ {bson.M{"_": 258},
162+ "\x10_\x00\x02\x01\x00\x00"},
163+ {bson.M{"_": bson.MongoTimestamp(258)},
164+ "\x11_\x00\x02\x01\x00\x00\x00\x00\x00\x00"},
165+ {bson.M{"_": int64(258)},
166+ "\x12_\x00\x02\x01\x00\x00\x00\x00\x00\x00"},
167+ {bson.M{"_": int64(258 << 32)},
168+ "\x12_\x00\x00\x00\x00\x00\x02\x01\x00\x00"},
169+ {bson.M{"_": bson.MaxKey},
170+ "\x7F_\x00"},
171+ {bson.M{"_": bson.MinKey},
172+ "\xFF_\x00"},
173+}
174+
175+func (s *S) TestMarshalAllItems(c *C) {
176+ for i, item := range allItems {
177+ data, err := bson.Marshal(item.obj)
178+ c.Assert(err, IsNil)
179+ c.Assert(string(data), Equals, wrapInDoc(item.data), Commentf("Failed on item %d: %#v", i, item))
180+ }
181+}
182+
183+func (s *S) TestUnmarshalAllItems(c *C) {
184+ for i, item := range allItems {
185+ value := bson.M{}
186+ err := bson.Unmarshal([]byte(wrapInDoc(item.data)), value)
187+ c.Assert(err, IsNil)
188+ c.Assert(value, DeepEquals, item.obj, Commentf("Failed on item %d: %#v", i, item))
189+ }
190+}
191+
192+func (s *S) TestUnmarshalRawAllItems(c *C) {
193+ for i, item := range allItems {
194+ if len(item.data) == 0 {
195+ continue
196+ }
197+ value := item.obj.(bson.M)["_"]
198+ if value == nil {
199+ continue
200+ }
201+ pv := reflect.New(reflect.ValueOf(value).Type())
202+ raw := bson.Raw{item.data[0], []byte(item.data[3:])}
203+ c.Logf("Unmarshal raw: %#v, %#v", raw, pv.Interface())
204+ err := raw.Unmarshal(pv.Interface())
205+ c.Assert(err, IsNil)
206+ c.Assert(pv.Elem().Interface(), DeepEquals, value, Commentf("Failed on item %d: %#v", i, item))
207+ }
208+}
209+
210+func (s *S) TestUnmarshalRawIncompatible(c *C) {
211+ raw := bson.Raw{0x08, []byte{0x01}} // true
212+ err := raw.Unmarshal(&struct{}{})
213+ c.Assert(err, ErrorMatches, "BSON kind 0x08 isn't compatible with type struct \\{\\}")
214+}
215+
216+func (s *S) TestUnmarshalZeroesStruct(c *C) {
217+ data, err := bson.Marshal(bson.M{"b": 2})
218+ c.Assert(err, IsNil)
219+ type T struct{ A, B int }
220+ v := T{A: 1}
221+ err = bson.Unmarshal(data, &v)
222+ c.Assert(err, IsNil)
223+ c.Assert(v.A, Equals, 0)
224+ c.Assert(v.B, Equals, 2)
225+}
226+
227+func (s *S) TestUnmarshalZeroesMap(c *C) {
228+ data, err := bson.Marshal(bson.M{"b": 2})
229+ c.Assert(err, IsNil)
230+ m := bson.M{"a": 1}
231+ err = bson.Unmarshal(data, &m)
232+ c.Assert(err, IsNil)
233+ c.Assert(m, DeepEquals, bson.M{"b": 2})
234+}
235+
236+func (s *S) TestUnmarshalNonNilInterface(c *C) {
237+ data, err := bson.Marshal(bson.M{"b": 2})
238+ c.Assert(err, IsNil)
239+ m := bson.M{"a": 1}
240+ var i interface{}
241+ i = m
242+ err = bson.Unmarshal(data, &i)
243+ c.Assert(err, IsNil)
244+ c.Assert(i, DeepEquals, bson.M{"b": 2})
245+ c.Assert(m, DeepEquals, bson.M{"a": 1})
246+}
247+
248+// --------------------------------------------------------------------------
249+// Some one way marshaling operations which would unmarshal differently.
250+
251+var oneWayMarshalItems = []testItemType{
252+ // These are being passed as pointers, and will unmarshal as values.
253+ {bson.M{"": &bson.Binary{0x02, []byte("old")}},
254+ "\x05\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"},
255+ {bson.M{"": &bson.Binary{0x80, []byte("udef")}},
256+ "\x05\x00\x04\x00\x00\x00\x80udef"},
257+ {bson.M{"": &bson.RegEx{"ab", "cd"}},
258+ "\x0B\x00ab\x00cd\x00"},
259+ {bson.M{"": &bson.JavaScript{"code", nil}},
260+ "\x0D\x00\x05\x00\x00\x00code\x00"},
261+ {bson.M{"": &bson.JavaScript{"code", bson.M{"": nil}}},
262+ "\x0F\x00\x14\x00\x00\x00\x05\x00\x00\x00code\x00" +
263+ "\x07\x00\x00\x00\x0A\x00\x00"},
264+
265+ // There's no float32 type in BSON. Will encode as a float64.
266+ {bson.M{"": float32(5.05)},
267+ "\x01\x00\x00\x00\x00@33\x14@"},
268+
269+ // The array will be unmarshaled as a slice instead.
270+ {bson.M{"": [2]bool{true, false}},
271+ "\x04\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"},
272+
273+ // The typed slice will be unmarshaled as []interface{}.
274+ {bson.M{"": []bool{true, false}},
275+ "\x04\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"},
276+
277+ // Will unmarshal as a []byte.
278+ {bson.M{"": bson.Binary{0x00, []byte("yo")}},
279+ "\x05\x00\x02\x00\x00\x00\x00yo"},
280+ {bson.M{"": bson.Binary{0x02, []byte("old")}},
281+ "\x05\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"},
282+
283+ // No way to preserve the type information here. We might encode as a zero
284+ // value, but this would mean that pointer values in structs wouldn't be
285+ // able to correctly distinguish between unset and set to the zero value.
286+ {bson.M{"": (*byte)(nil)},
287+ "\x0A\x00"},
288+
289+ // No int types smaller than int32 in BSON. Could encode this as a char,
290+ // but it would still be ambiguous, take more, and be awkward in Go when
291+ // loaded without typing information.
292+ {bson.M{"": byte(8)},
293+ "\x10\x00\x08\x00\x00\x00"},
294+
295+ // There are no unsigned types in BSON. Will unmarshal as int32 or int64.
296+ {bson.M{"": uint32(258)},
297+ "\x10\x00\x02\x01\x00\x00"},
298+ {bson.M{"": uint64(258)},
299+ "\x12\x00\x02\x01\x00\x00\x00\x00\x00\x00"},
300+ {bson.M{"": uint64(258 << 32)},
301+ "\x12\x00\x00\x00\x00\x00\x02\x01\x00\x00"},
302+
303+ // This will unmarshal as int.
304+ {bson.M{"": int32(258)},
305+ "\x10\x00\x02\x01\x00\x00"},
306+
307+ // That's a special case. The unsigned value is too large for an int32,
308+ // so an int64 is used instead.
309+ {bson.M{"": uint32(1<<32 - 1)},
310+ "\x12\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"},
311+ {bson.M{"": uint(1<<32 - 1)},
312+ "\x12\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"},
313+}
314+
315+func (s *S) TestOneWayMarshalItems(c *C) {
316+ for i, item := range oneWayMarshalItems {
317+ data, err := bson.Marshal(item.obj)
318+ c.Assert(err, IsNil)
319+ c.Assert(string(data), Equals, wrapInDoc(item.data),
320+ Commentf("Failed on item %d", i))
321+ }
322+}
323+
324+// --------------------------------------------------------------------------
325+// Two-way tests for user-defined structures using the samples
326+// from bsonspec.org.
327+
328+type specSample1 struct {
329+ Hello string
330+}
331+
332+type specSample2 struct {
333+ BSON []interface{} "BSON"
334+}
335+
336+var structSampleItems = []testItemType{
337+ {&specSample1{"world"},
338+ "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00"},
339+ {&specSample2{[]interface{}{"awesome", float64(5.05), 1986}},
340+ "1\x00\x00\x00\x04BSON\x00&\x00\x00\x00\x020\x00\x08\x00\x00\x00" +
341+ "awesome\x00\x011\x00333333\x14@\x102\x00\xc2\x07\x00\x00\x00\x00"},
342+}
343+
344+func (s *S) TestMarshalStructSampleItems(c *C) {
345+ for i, item := range structSampleItems {
346+ data, err := bson.Marshal(item.obj)
347+ c.Assert(err, IsNil)
348+ c.Assert(string(data), Equals, item.data,
349+ Commentf("Failed on item %d", i))
350+ }
351+}
352+
353+func (s *S) TestUnmarshalStructSampleItems(c *C) {
354+ for _, item := range structSampleItems {
355+ testUnmarshal(c, item.data, item.obj)
356+ }
357+}
358+
359+func (s *S) Test64bitInt(c *C) {
360+ var i int64 = (1 << 31)
361+ if int(i) > 0 {
362+ data, err := bson.Marshal(bson.M{"i": int(i)})
363+ c.Assert(err, IsNil)
364+ c.Assert(string(data), Equals, wrapInDoc("\x12i\x00\x00\x00\x00\x80\x00\x00\x00\x00"))
365+
366+ var result struct{ I int }
367+ err = bson.Unmarshal(data, &result)
368+ c.Assert(err, IsNil)
369+ c.Assert(int64(result.I), Equals, i)
370+ }
371+}
372+
373+// --------------------------------------------------------------------------
374+// Generic two-way struct marshaling tests.
375+
376+var bytevar = byte(8)
377+var byteptr = &bytevar
378+
379+var structItems = []testItemType{
380+ {&struct{ Ptr *byte }{nil},
381+ "\x0Aptr\x00"},
382+ {&struct{ Ptr *byte }{&bytevar},
383+ "\x10ptr\x00\x08\x00\x00\x00"},
384+ {&struct{ Ptr **byte }{&byteptr},
385+ "\x10ptr\x00\x08\x00\x00\x00"},
386+ {&struct{ Byte byte }{8},
387+ "\x10byte\x00\x08\x00\x00\x00"},
388+ {&struct{ Byte byte }{0},
389+ "\x10byte\x00\x00\x00\x00\x00"},
390+ {&struct {
391+ V byte "Tag"
392+ }{8},
393+ "\x10Tag\x00\x08\x00\x00\x00"},
394+ {&struct {
395+ V *struct {
396+ Byte byte
397+ }
398+ }{&struct{ Byte byte }{8}},
399+ "\x03v\x00" + "\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00"},
400+ {&struct{ priv byte }{}, ""},
401+
402+ // The order of the dumped fields should be the same in the struct.
403+ {&struct{ A, C, B, D, F, E *byte }{},
404+ "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x0Ae\x00"},
405+
406+ {&struct{ V bson.Raw }{bson.Raw{0x03, []byte("\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00")}},
407+ "\x03v\x00" + "\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00"},
408+ {&struct{ V bson.Raw }{bson.Raw{0x10, []byte("\x00\x00\x00\x00")}},
409+ "\x10v\x00" + "\x00\x00\x00\x00"},
410+
411+ // Byte arrays.
412+ {&struct{ V [2]byte }{[2]byte{'y', 'o'}},
413+ "\x05v\x00\x02\x00\x00\x00\x00yo"},
414+}
415+
416+func (s *S) TestMarshalStructItems(c *C) {
417+ for i, item := range structItems {
418+ data, err := bson.Marshal(item.obj)
419+ c.Assert(err, IsNil)
420+ c.Assert(string(data), Equals, wrapInDoc(item.data),
421+ Commentf("Failed on item %d", i))
422+ }
423+}
424+
425+func (s *S) TestUnmarshalStructItems(c *C) {
426+ for _, item := range structItems {
427+ testUnmarshal(c, wrapInDoc(item.data), item.obj)
428+ }
429+}
430+
431+func (s *S) TestUnmarshalRawStructItems(c *C) {
432+ for i, item := range structItems {
433+ raw := bson.Raw{0x03, []byte(wrapInDoc(item.data))}
434+ zero := makeZeroDoc(item.obj)
435+ err := raw.Unmarshal(zero)
436+ c.Assert(err, IsNil)
437+ c.Assert(zero, DeepEquals, item.obj, Commentf("Failed on item %d: %#v", i, item))
438+ }
439+}
440+
441+func (s *S) TestUnmarshalRawNil(c *C) {
442+ // Regression test: shouldn't try to nil out the pointer itself,
443+ // as it's not settable.
444+ raw := bson.Raw{0x0A, []byte{}}
445+ err := raw.Unmarshal(&struct{}{})
446+ c.Assert(err, IsNil)
447+}
448+
449+// --------------------------------------------------------------------------
450+// One-way marshaling tests.
451+
452+type dOnIface struct {
453+ D interface{}
454+}
455+
456+type ignoreField struct {
457+ Before string
458+ Ignore string `bson:"-"`
459+ After string
460+}
461+
462+var marshalItems = []testItemType{
463+ // Ordered document dump. Will unmarshal as a dictionary by default.
464+ {bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", nil}, {"f", nil}, {"e", true}},
465+ "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x08e\x00\x01"},
466+ {MyD{{"a", nil}, {"c", nil}, {"b", nil}, {"d", nil}, {"f", nil}, {"e", true}},
467+ "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x08e\x00\x01"},
468+ {&dOnIface{bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", true}}},
469+ "\x03d\x00" + wrapInDoc("\x0Aa\x00\x0Ac\x00\x0Ab\x00\x08d\x00\x01")},
470+
471+ {bson.RawD{{"a", bson.Raw{0x0A, nil}}, {"c", bson.Raw{0x0A, nil}}, {"b", bson.Raw{0x08, []byte{0x01}}}},
472+ "\x0Aa\x00" + "\x0Ac\x00" + "\x08b\x00\x01"},
473+ {MyRawD{{"a", bson.Raw{0x0A, nil}}, {"c", bson.Raw{0x0A, nil}}, {"b", bson.Raw{0x08, []byte{0x01}}}},
474+ "\x0Aa\x00" + "\x0Ac\x00" + "\x08b\x00\x01"},
475+ {&dOnIface{bson.RawD{{"a", bson.Raw{0x0A, nil}}, {"c", bson.Raw{0x0A, nil}}, {"b", bson.Raw{0x08, []byte{0x01}}}}},
476+ "\x03d\x00" + wrapInDoc("\x0Aa\x00"+"\x0Ac\x00"+"\x08b\x00\x01")},
477+
478+ {&ignoreField{"before", "ignore", "after"},
479+ "\x02before\x00\a\x00\x00\x00before\x00\x02after\x00\x06\x00\x00\x00after\x00"},
480+
481+ // Marshalling a Raw document does nothing.
482+ {bson.Raw{0x03, []byte(wrapInDoc("anything"))},
483+ "anything"},
484+ {bson.Raw{Data: []byte(wrapInDoc("anything"))},
485+ "anything"},
486+}
487+
488+func (s *S) TestMarshalOneWayItems(c *C) {
489+ for _, item := range marshalItems {
490+ data, err := bson.Marshal(item.obj)
491+ c.Assert(err, IsNil)
492+ c.Assert(string(data), Equals, wrapInDoc(item.data))
493+ }
494+}
495+
496+// --------------------------------------------------------------------------
497+// One-way unmarshaling tests.
498+
499+var unmarshalItems = []testItemType{
500+ // Field is private. Should not attempt to unmarshal it.
501+ {&struct{ priv byte }{},
502+ "\x10priv\x00\x08\x00\x00\x00"},
503+
504+ // Wrong casing. Field names are lowercased.
505+ {&struct{ Byte byte }{},
506+ "\x10Byte\x00\x08\x00\x00\x00"},
507+
508+ // Ignore non-existing field.
509+ {&struct{ Byte byte }{9},
510+ "\x10boot\x00\x08\x00\x00\x00" + "\x10byte\x00\x09\x00\x00\x00"},
511+
512+ // Do not unmarshal on ignored field.
513+ {&ignoreField{"before", "", "after"},
514+ "\x02before\x00\a\x00\x00\x00before\x00" +
515+ "\x02-\x00\a\x00\x00\x00ignore\x00" +
516+ "\x02after\x00\x06\x00\x00\x00after\x00"},
517+
518+ // Ignore unsuitable types silently.
519+ {map[string]string{"str": "s"},
520+ "\x02str\x00\x02\x00\x00\x00s\x00" + "\x10int\x00\x01\x00\x00\x00"},
521+ {map[string][]int{"array": []int{5, 9}},
522+ "\x04array\x00" + wrapInDoc("\x100\x00\x05\x00\x00\x00"+"\x021\x00\x02\x00\x00\x00s\x00"+"\x102\x00\x09\x00\x00\x00")},
523+
524+ // Wrong type. Shouldn't init pointer.
525+ {&struct{ Str *byte }{},
526+ "\x02str\x00\x02\x00\x00\x00s\x00"},
527+ {&struct{ Str *struct{ Str string } }{},
528+ "\x02str\x00\x02\x00\x00\x00s\x00"},
529+
530+ // Ordered document.
531+ {&struct{ bson.D }{bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", true}}},
532+ "\x03d\x00" + wrapInDoc("\x0Aa\x00\x0Ac\x00\x0Ab\x00\x08d\x00\x01")},
533+
534+ // Raw document.
535+ {&bson.Raw{0x03, []byte(wrapInDoc("\x10byte\x00\x08\x00\x00\x00"))},
536+ "\x10byte\x00\x08\x00\x00\x00"},
537+
538+ // RawD document.
539+ {&struct{ bson.RawD }{bson.RawD{{"a", bson.Raw{0x0A, []byte{}}}, {"c", bson.Raw{0x0A, []byte{}}}, {"b", bson.Raw{0x08, []byte{0x01}}}}},
540+ "\x03rawd\x00" + wrapInDoc("\x0Aa\x00\x0Ac\x00\x08b\x00\x01")},
541+
542+ // Decode old binary.
543+ {bson.M{"_": []byte("old")},
544+ "\x05_\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"},
545+
546+ // Decode old binary without length. According to the spec, this shouldn't happen.
547+ {bson.M{"_": []byte("old")},
548+ "\x05_\x00\x03\x00\x00\x00\x02old"},
549+}
550+
551+func (s *S) TestUnmarshalOneWayItems(c *C) {
552+ for _, item := range unmarshalItems {
553+ testUnmarshal(c, wrapInDoc(item.data), item.obj)
554+ }
555+}
556+
557+func (s *S) TestUnmarshalNilInStruct(c *C) {
558+ // Nil is the default value, so we need to ensure it's indeed being set.
559+ b := byte(1)
560+ v := &struct{ Ptr *byte }{&b}
561+ err := bson.Unmarshal([]byte(wrapInDoc("\x0Aptr\x00")), v)
562+ c.Assert(err, IsNil)
563+ c.Assert(v, DeepEquals, &struct{ Ptr *byte }{nil})
564+}
565+
566+// --------------------------------------------------------------------------
567+// Marshalling error cases.
568+
569+type structWithDupKeys struct {
570+ Name byte
571+ Other byte "name" // Tag should precede.
572+}
573+
574+var marshalErrorItems = []testItemType{
575+ {bson.M{"": uint64(1 << 63)},
576+ "BSON has no uint64 type, and value is too large to fit correctly in an int64"},
577+ {bson.M{"": bson.ObjectId("tooshort")},
578+ "ObjectIDs must be exactly 12 bytes long \\(got 8\\)"},
579+ {int64(123),
580+ "Can't marshal int64 as a BSON document"},
581+ {bson.M{"": 1i},
582+ "Can't marshal complex128 in a BSON document"},
583+ {&structWithDupKeys{},
584+ "Duplicated key 'name' in struct bson_test.structWithDupKeys"},
585+ {bson.Raw{0x0A, []byte{}},
586+ "Attempted to unmarshal Raw kind 10 as a document"},
587+ {&inlineCantPtr{&struct{ A, B int }{1, 2}},
588+ "Option ,inline needs a struct value or map field"},
589+ {&inlineDupName{1, struct{ A, B int }{2, 3}},
590+ "Duplicated key 'a' in struct bson_test.inlineDupName"},
591+ {&inlineDupMap{},
592+ "Multiple ,inline maps in struct bson_test.inlineDupMap"},
593+ {&inlineBadKeyMap{},
594+ "Option ,inline needs a map with string keys in struct bson_test.inlineBadKeyMap"},
595+ {&inlineMap{A: 1, M: map[string]interface{}{"a": 1}},
596+ `Can't have key "a" in inlined map; conflicts with struct field`},
597+}
598+
599+func (s *S) TestMarshalErrorItems(c *C) {
600+ for _, item := range marshalErrorItems {
601+ data, err := bson.Marshal(item.obj)
602+ c.Assert(err, ErrorMatches, item.data)
603+ c.Assert(data, IsNil)
604+ }
605+}
606+
607+// --------------------------------------------------------------------------
608+// Unmarshalling error cases.
609+
610+type unmarshalErrorType struct {
611+ obj interface{}
612+ data string
613+ error string
614+}
615+
616+var unmarshalErrorItems = []unmarshalErrorType{
617+ // Tag name conflicts with existing parameter.
618+ {&structWithDupKeys{},
619+ "\x10name\x00\x08\x00\x00\x00",
620+ "Duplicated key 'name' in struct bson_test.structWithDupKeys"},
621+
622+ // Non-string map key.
623+ {map[int]interface{}{},
624+ "\x10name\x00\x08\x00\x00\x00",
625+ "BSON map must have string keys. Got: map\\[int\\]interface \\{\\}"},
626+
627+ {nil,
628+ "\xEEname\x00",
629+ "Unknown element kind \\(0xEE\\)"},
630+
631+ {struct{ Name bool }{},
632+ "\x10name\x00\x08\x00\x00\x00",
633+ "Unmarshal can't deal with struct values. Use a pointer."},
634+
635+ {123,
636+ "\x10name\x00\x08\x00\x00\x00",
637+ "Unmarshal needs a map or a pointer to a struct."},
638+}
639+
640+func (s *S) TestUnmarshalErrorItems(c *C) {
641+ for _, item := range unmarshalErrorItems {
642+ data := []byte(wrapInDoc(item.data))
643+ var value interface{}
644+ switch reflect.ValueOf(item.obj).Kind() {
645+ case reflect.Map, reflect.Ptr:
646+ value = makeZeroDoc(item.obj)
647+ case reflect.Invalid:
648+ value = bson.M{}
649+ default:
650+ value = item.obj
651+ }
652+ err := bson.Unmarshal(data, value)
653+ c.Assert(err, ErrorMatches, item.error)
654+ }
655+}
656+
657+type unmarshalRawErrorType struct {
658+ obj interface{}
659+ raw bson.Raw
660+ error string
661+}
662+
663+var unmarshalRawErrorItems = []unmarshalRawErrorType{
664+ // Tag name conflicts with existing parameter.
665+ {&structWithDupKeys{},
666+ bson.Raw{0x03, []byte("\x10byte\x00\x08\x00\x00\x00")},
667+ "Duplicated key 'name' in struct bson_test.structWithDupKeys"},
668+
669+ {&struct{}{},
670+ bson.Raw{0xEE, []byte{}},
671+ "Unknown element kind \\(0xEE\\)"},
672+
673+ {struct{ Name bool }{},
674+ bson.Raw{0x10, []byte("\x08\x00\x00\x00")},
675+ "Raw Unmarshal can't deal with struct values. Use a pointer."},
676+
677+ {123,
678+ bson.Raw{0x10, []byte("\x08\x00\x00\x00")},
679+ "Raw Unmarshal needs a map or a valid pointer."},
680+}
681+
682+func (s *S) TestUnmarshalRawErrorItems(c *C) {
683+ for i, item := range unmarshalRawErrorItems {
684+ err := item.raw.Unmarshal(item.obj)
685+ c.Assert(err, ErrorMatches, item.error, Commentf("Failed on item %d: %#v\n", i, item))
686+ }
687+}
688+
689+var corruptedData = []string{
690+ "\x04\x00\x00\x00\x00", // Shorter than minimum
691+ "\x06\x00\x00\x00\x00", // Not enough data
692+ "\x05\x00\x00", // Broken length
693+ "\x05\x00\x00\x00\xff", // Corrupted termination
694+ "\x0A\x00\x00\x00\x0Aooop\x00", // Unfinished C string
695+
696+ // Array end past end of string (s[2]=0x07 is correct)
697+ wrapInDoc("\x04\x00\x09\x00\x00\x00\x0A\x00\x00"),
698+
699+ // Array end within string, but past acceptable.
700+ wrapInDoc("\x04\x00\x08\x00\x00\x00\x0A\x00\x00"),
701+
702+ // Document end within string, but past acceptable.
703+ wrapInDoc("\x03\x00\x08\x00\x00\x00\x0A\x00\x00"),
704+
705+ // String with corrupted end.
706+ wrapInDoc("\x02\x00\x03\x00\x00\x00yo\xFF"),
707+}
708+
709+func (s *S) TestUnmarshalMapDocumentTooShort(c *C) {
710+ for _, data := range corruptedData {
711+ err := bson.Unmarshal([]byte(data), bson.M{})
712+ c.Assert(err, ErrorMatches, "Document is corrupted")
713+
714+ err = bson.Unmarshal([]byte(data), &struct{}{})
715+ c.Assert(err, ErrorMatches, "Document is corrupted")
716+ }
717+}
718+
719+// --------------------------------------------------------------------------
720+// Setter test cases.
721+
722+var setterResult = map[string]error{}
723+
724+type setterType struct {
725+ received interface{}
726+}
727+
728+func (o *setterType) SetBSON(raw bson.Raw) error {
729+ err := raw.Unmarshal(&o.received)
730+ if err != nil {
731+ panic("The panic:" + err.Error())
732+ }
733+ if s, ok := o.received.(string); ok {
734+ if result, ok := setterResult[s]; ok {
735+ return result
736+ }
737+ }
738+ return nil
739+}
740+
741+type ptrSetterDoc struct {
742+ Field *setterType "_"
743+}
744+
745+type valSetterDoc struct {
746+ Field setterType "_"
747+}
748+
749+func (s *S) TestUnmarshalAllItemsWithPtrSetter(c *C) {
750+ for _, item := range allItems {
751+ for i := 0; i != 2; i++ {
752+ var field *setterType
753+ if i == 0 {
754+ obj := &ptrSetterDoc{}
755+ err := bson.Unmarshal([]byte(wrapInDoc(item.data)), obj)
756+ c.Assert(err, IsNil)
757+ field = obj.Field
758+ } else {
759+ obj := &valSetterDoc{}
760+ err := bson.Unmarshal([]byte(wrapInDoc(item.data)), obj)
761+ c.Assert(err, IsNil)
762+ field = &obj.Field
763+ }
764+ if item.data == "" {
765+ // Nothing to unmarshal. Should be untouched.
766+ if i == 0 {
767+ c.Assert(field, IsNil)
768+ } else {
769+ c.Assert(field.received, IsNil)
770+ }
771+ } else {
772+ expected := item.obj.(bson.M)["_"]
773+ c.Assert(field, NotNil, Commentf("Pointer not initialized (%#v)", expected))
774+ c.Assert(field.received, DeepEquals, expected)
775+ }
776+ }
777+ }
778+}
779+
780+func (s *S) TestUnmarshalWholeDocumentWithSetter(c *C) {
781+ obj := &setterType{}
782+ err := bson.Unmarshal([]byte(sampleItems[0].data), obj)
783+ c.Assert(err, IsNil)
784+ c.Assert(obj.received, DeepEquals, bson.M{"hello": "world"})
785+}
786+
787+func (s *S) TestUnmarshalSetterOmits(c *C) {
788+ setterResult["2"] = &bson.TypeError{}
789+ setterResult["4"] = &bson.TypeError{}
790+ defer func() {
791+ delete(setterResult, "2")
792+ delete(setterResult, "4")
793+ }()
794+
795+ m := map[string]*setterType{}
796+ data := wrapInDoc("\x02abc\x00\x02\x00\x00\x001\x00" +
797+ "\x02def\x00\x02\x00\x00\x002\x00" +
798+ "\x02ghi\x00\x02\x00\x00\x003\x00" +
799+ "\x02jkl\x00\x02\x00\x00\x004\x00")
800+ err := bson.Unmarshal([]byte(data), m)
801+ c.Assert(err, IsNil)
802+ c.Assert(m["abc"], NotNil)
803+ c.Assert(m["def"], IsNil)
804+ c.Assert(m["ghi"], NotNil)
805+ c.Assert(m["jkl"], IsNil)
806+
807+ c.Assert(m["abc"].received, Equals, "1")
808+ c.Assert(m["ghi"].received, Equals, "3")
809+}
810+
811+func (s *S) TestUnmarshalSetterErrors(c *C) {
812+ boom := errors.New("BOOM")
813+ setterResult["2"] = boom
814+ defer delete(setterResult, "2")
815+
816+ m := map[string]*setterType{}
817+ data := wrapInDoc("\x02abc\x00\x02\x00\x00\x001\x00" +
818+ "\x02def\x00\x02\x00\x00\x002\x00" +
819+ "\x02ghi\x00\x02\x00\x00\x003\x00")
820+ err := bson.Unmarshal([]byte(data), m)
821+ c.Assert(err, Equals, boom)
822+ c.Assert(m["abc"], NotNil)
823+ c.Assert(m["def"], IsNil)
824+ c.Assert(m["ghi"], IsNil)
825+
826+ c.Assert(m["abc"].received, Equals, "1")
827+}
828+
829+func (s *S) TestDMap(c *C) {
830+ d := bson.D{{"a", 1}, {"b", 2}}
831+ c.Assert(d.Map(), DeepEquals, bson.M{"a": 1, "b": 2})
832+}
833+
834+func (s *S) TestUnmarshalSetterSetZero(c *C) {
835+ setterResult["foo"] = bson.SetZero
836+ defer delete(setterResult, "field")
837+
838+ data, err := bson.Marshal(bson.M{"field": "foo"})
839+ c.Assert(err, IsNil)
840+
841+ m := map[string]*setterType{}
842+ err = bson.Unmarshal([]byte(data), m)
843+ c.Assert(err, IsNil)
844+
845+ value, ok := m["field"]
846+ c.Assert(ok, Equals, true)
847+ c.Assert(value, IsNil)
848+}
849+
850+// --------------------------------------------------------------------------
851+// Getter test cases.
852+
853+type typeWithGetter struct {
854+ result interface{}
855+ err error
856+}
857+
858+func (t *typeWithGetter) GetBSON() (interface{}, error) {
859+ if t == nil {
860+ return "<value is nil>", nil
861+ }
862+ return t.result, t.err
863+}
864+
865+type docWithGetterField struct {
866+ Field *typeWithGetter "_"
867+}
868+
869+func (s *S) TestMarshalAllItemsWithGetter(c *C) {
870+ for i, item := range allItems {
871+ if item.data == "" {
872+ continue
873+ }
874+ obj := &docWithGetterField{}
875+ obj.Field = &typeWithGetter{result: item.obj.(bson.M)["_"]}
876+ data, err := bson.Marshal(obj)
877+ c.Assert(err, IsNil)
878+ c.Assert(string(data), Equals, wrapInDoc(item.data),
879+ Commentf("Failed on item #%d", i))
880+ }
881+}
882+
883+func (s *S) TestMarshalWholeDocumentWithGetter(c *C) {
884+ obj := &typeWithGetter{result: sampleItems[0].obj}
885+ data, err := bson.Marshal(obj)
886+ c.Assert(err, IsNil)
887+ c.Assert(string(data), Equals, sampleItems[0].data)
888+}
889+
890+func (s *S) TestGetterErrors(c *C) {
891+ e := errors.New("oops")
892+
893+ obj1 := &docWithGetterField{}
894+ obj1.Field = &typeWithGetter{sampleItems[0].obj, e}
895+ data, err := bson.Marshal(obj1)
896+ c.Assert(err, ErrorMatches, "oops")
897+ c.Assert(data, IsNil)
898+
899+ obj2 := &typeWithGetter{sampleItems[0].obj, e}
900+ data, err = bson.Marshal(obj2)
901+ c.Assert(err, ErrorMatches, "oops")
902+ c.Assert(data, IsNil)
903+}
904+
905+type intGetter int64
906+
907+func (t intGetter) GetBSON() (interface{}, error) {
908+ return int64(t), nil
909+}
910+
911+type typeWithIntGetter struct {
912+ V intGetter ",minsize"
913+}
914+
915+func (s *S) TestMarshalShortWithGetter(c *C) {
916+ obj := typeWithIntGetter{42}
917+ data, err := bson.Marshal(obj)
918+ c.Assert(err, IsNil)
919+ m := bson.M{}
920+ err = bson.Unmarshal(data, m)
921+ c.Assert(err, IsNil)
922+ c.Assert(m["v"], Equals, 42)
923+}
924+
925+func (s *S) TestMarshalWithGetterNil(c *C) {
926+ obj := docWithGetterField{}
927+ data, err := bson.Marshal(obj)
928+ c.Assert(err, IsNil)
929+ m := bson.M{}
930+ err = bson.Unmarshal(data, m)
931+ c.Assert(err, IsNil)
932+ c.Assert(m, DeepEquals, bson.M{"_": "<value is nil>"})
933+}
934+
935+// --------------------------------------------------------------------------
936+// Cross-type conversion tests.
937+
938+type crossTypeItem struct {
939+ obj1 interface{}
940+ obj2 interface{}
941+}
942+
943+type condStr struct {
944+ V string ",omitempty"
945+}
946+type condStrNS struct {
947+ V string `a:"A" bson:",omitempty" b:"B"`
948+}
949+type condBool struct {
950+ V bool ",omitempty"
951+}
952+type condInt struct {
953+ V int ",omitempty"
954+}
955+type condUInt struct {
956+ V uint ",omitempty"
957+}
958+type condFloat struct {
959+ V float64 ",omitempty"
960+}
961+type condIface struct {
962+ V interface{} ",omitempty"
963+}
964+type condPtr struct {
965+ V *bool ",omitempty"
966+}
967+type condSlice struct {
968+ V []string ",omitempty"
969+}
970+type condMap struct {
971+ V map[string]int ",omitempty"
972+}
973+type namedCondStr struct {
974+ V string "myv,omitempty"
975+}
976+type condTime struct {
977+ V time.Time ",omitempty"
978+}
979+type condStruct struct {
980+ V struct{ A []int } ",omitempty"
981+}
982+
983+type shortInt struct {
984+ V int64 ",minsize"
985+}
986+type shortUint struct {
987+ V uint64 ",minsize"
988+}
989+type shortIface struct {
990+ V interface{} ",minsize"
991+}
992+type shortPtr struct {
993+ V *int64 ",minsize"
994+}
995+type shortNonEmptyInt struct {
996+ V int64 ",minsize,omitempty"
997+}
998+
999+type inlineInt struct {
1000+ V struct{ A, B int } ",inline"
1001+}
1002+type inlineCantPtr struct {
1003+ V *struct{ A, B int } ",inline"
1004+}
1005+type inlineDupName struct {
1006+ A int
1007+ V struct{ A, B int } ",inline"
1008+}
1009+type inlineMap struct {
1010+ A int
1011+ M map[string]interface{} ",inline"
1012+}
1013+type inlineMapInt struct {
1014+ A int
1015+ M map[string]int ",inline"
1016+}
1017+type inlineMapMyM struct {
1018+ A int
1019+ M MyM ",inline"
1020+}
1021+type inlineDupMap struct {
1022+ M1 map[string]interface{} ",inline"
1023+ M2 map[string]interface{} ",inline"
1024+}
1025+type inlineBadKeyMap struct {
1026+ M map[int]int ",inline"
1027+}
1028+
1029+type getterSetterD bson.D
1030+
1031+func (s getterSetterD) GetBSON() (interface{}, error) {
1032+ if len(s) == 0 {
1033+ return bson.D{}, nil
1034+ }
1035+ return bson.D(s[:len(s)-1]), nil
1036+}
1037+
1038+func (s *getterSetterD) SetBSON(raw bson.Raw) error {
1039+ var doc bson.D
1040+ err := raw.Unmarshal(&doc)
1041+ doc = append(doc, bson.DocElem{"suffix", true})
1042+ *s = getterSetterD(doc)
1043+ return err
1044+}
1045+
1046+type getterSetterInt int
1047+
1048+func (i getterSetterInt) GetBSON() (interface{}, error) {
1049+ return bson.D{{"a", int(i)}}, nil
1050+}
1051+
1052+func (i *getterSetterInt) SetBSON(raw bson.Raw) error {
1053+ var doc struct{ A int }
1054+ err := raw.Unmarshal(&doc)
1055+ *i = getterSetterInt(doc.A)
1056+ return err
1057+}
1058+
1059+type ifaceType interface {
1060+ Hello()
1061+}
1062+
1063+type ifaceSlice []ifaceType
1064+
1065+func (s *ifaceSlice) SetBSON(raw bson.Raw) error {
1066+ var ns []int
1067+ if err := raw.Unmarshal(&ns); err != nil {
1068+ return err
1069+ }
1070+ *s = make(ifaceSlice, ns[0])
1071+ return nil
1072+}
1073+
1074+func (s ifaceSlice) GetBSON() (interface{}, error) {
1075+ return []int{len(s)}, nil
1076+}
1077+
1078+type (
1079+ MyString string
1080+ MyBytes []byte
1081+ MyBool bool
1082+ MyD []bson.DocElem
1083+ MyRawD []bson.RawDocElem
1084+ MyM map[string]interface{}
1085+)
1086+
1087+var (
1088+ truevar = true
1089+ falsevar = false
1090+
1091+ int64var = int64(42)
1092+ int64ptr = &int64var
1093+ intvar = int(42)
1094+ intptr = &intvar
1095+
1096+ gsintvar = getterSetterInt(42)
1097+)
1098+
1099+func parseURL(s string) *url.URL {
1100+ u, err := url.Parse(s)
1101+ if err != nil {
1102+ panic(err)
1103+ }
1104+ return u
1105+}
1106+
1107+// That's a pretty fun test. It will dump the first item, generate a zero
1108+// value equivalent to the second one, load the dumped data onto it, and then
1109+// verify that the resulting value is deep-equal to the untouched second value.
1110+// Then, it will do the same in the *opposite* direction!
1111+var twoWayCrossItems = []crossTypeItem{
1112+ // int<=>int
1113+ {&struct{ I int }{42}, &struct{ I int8 }{42}},
1114+ {&struct{ I int }{42}, &struct{ I int32 }{42}},
1115+ {&struct{ I int }{42}, &struct{ I int64 }{42}},
1116+ {&struct{ I int8 }{42}, &struct{ I int32 }{42}},
1117+ {&struct{ I int8 }{42}, &struct{ I int64 }{42}},
1118+ {&struct{ I int32 }{42}, &struct{ I int64 }{42}},
1119+
1120+ // uint<=>uint
1121+ {&struct{ I uint }{42}, &struct{ I uint8 }{42}},
1122+ {&struct{ I uint }{42}, &struct{ I uint32 }{42}},
1123+ {&struct{ I uint }{42}, &struct{ I uint64 }{42}},
1124+ {&struct{ I uint8 }{42}, &struct{ I uint32 }{42}},
1125+ {&struct{ I uint8 }{42}, &struct{ I uint64 }{42}},
1126+ {&struct{ I uint32 }{42}, &struct{ I uint64 }{42}},
1127+
1128+ // float32<=>float64
1129+ {&struct{ I float32 }{42}, &struct{ I float64 }{42}},
1130+
1131+ // int<=>uint
1132+ {&struct{ I uint }{42}, &struct{ I int }{42}},
1133+ {&struct{ I uint }{42}, &struct{ I int8 }{42}},
1134+ {&struct{ I uint }{42}, &struct{ I int32 }{42}},
1135+ {&struct{ I uint }{42}, &struct{ I int64 }{42}},
1136+ {&struct{ I uint8 }{42}, &struct{ I int }{42}},
1137+ {&struct{ I uint8 }{42}, &struct{ I int8 }{42}},
1138+ {&struct{ I uint8 }{42}, &struct{ I int32 }{42}},
1139+ {&struct{ I uint8 }{42}, &struct{ I int64 }{42}},
1140+ {&struct{ I uint32 }{42}, &struct{ I int }{42}},
1141+ {&struct{ I uint32 }{42}, &struct{ I int8 }{42}},
1142+ {&struct{ I uint32 }{42}, &struct{ I int32 }{42}},
1143+ {&struct{ I uint32 }{42}, &struct{ I int64 }{42}},
1144+ {&struct{ I uint64 }{42}, &struct{ I int }{42}},
1145+ {&struct{ I uint64 }{42}, &struct{ I int8 }{42}},
1146+ {&struct{ I uint64 }{42}, &struct{ I int32 }{42}},
1147+ {&struct{ I uint64 }{42}, &struct{ I int64 }{42}},
1148+
1149+ // int <=> float
1150+ {&struct{ I int }{42}, &struct{ I float64 }{42}},
1151+
1152+ // int <=> bool
1153+ {&struct{ I int }{1}, &struct{ I bool }{true}},
1154+ {&struct{ I int }{0}, &struct{ I bool }{false}},
1155+
1156+ // uint <=> float64
1157+ {&struct{ I uint }{42}, &struct{ I float64 }{42}},
1158+
1159+ // uint <=> bool
1160+ {&struct{ I uint }{1}, &struct{ I bool }{true}},
1161+ {&struct{ I uint }{0}, &struct{ I bool }{false}},
1162+
1163+ // float64 <=> bool
1164+ {&struct{ I float64 }{1}, &struct{ I bool }{true}},
1165+ {&struct{ I float64 }{0}, &struct{ I bool }{false}},
1166+
1167+ // string <=> string and string <=> []byte
1168+ {&struct{ S []byte }{[]byte("abc")}, &struct{ S string }{"abc"}},
1169+ {&struct{ S []byte }{[]byte("def")}, &struct{ S bson.Symbol }{"def"}},
1170+ {&struct{ S string }{"ghi"}, &struct{ S bson.Symbol }{"ghi"}},
1171+
1172+ // map <=> struct
1173+ {&struct {
1174+ A struct {
1175+ B, C int
1176+ }
1177+ }{struct{ B, C int }{1, 2}},
1178+ map[string]map[string]int{"a": map[string]int{"b": 1, "c": 2}}},
1179+
1180+ {&struct{ A bson.Symbol }{"abc"}, map[string]string{"a": "abc"}},
1181+ {&struct{ A bson.Symbol }{"abc"}, map[string][]byte{"a": []byte("abc")}},
1182+ {&struct{ A []byte }{[]byte("abc")}, map[string]string{"a": "abc"}},
1183+ {&struct{ A uint }{42}, map[string]int{"a": 42}},
1184+ {&struct{ A uint }{42}, map[string]float64{"a": 42}},
1185+ {&struct{ A uint }{1}, map[string]bool{"a": true}},
1186+ {&struct{ A int }{42}, map[string]uint{"a": 42}},
1187+ {&struct{ A int }{42}, map[string]float64{"a": 42}},
1188+ {&struct{ A int }{1}, map[string]bool{"a": true}},
1189+ {&struct{ A float64 }{42}, map[string]float32{"a": 42}},
1190+ {&struct{ A float64 }{42}, map[string]int{"a": 42}},
1191+ {&struct{ A float64 }{42}, map[string]uint{"a": 42}},
1192+ {&struct{ A float64 }{1}, map[string]bool{"a": true}},
1193+ {&struct{ A bool }{true}, map[string]int{"a": 1}},
1194+ {&struct{ A bool }{true}, map[string]uint{"a": 1}},
1195+ {&struct{ A bool }{true}, map[string]float64{"a": 1}},
1196+ {&struct{ A **byte }{&byteptr}, map[string]byte{"a": 8}},
1197+
1198+ // url.URL <=> string
1199+ {&struct{ URL *url.URL }{parseURL("h://e.c/p")}, map[string]string{"url": "h://e.c/p"}},
1200+ {&struct{ URL url.URL }{*parseURL("h://e.c/p")}, map[string]string{"url": "h://e.c/p"}},
1201+
1202+ // Slices
1203+ {&struct{ S []int }{[]int{1, 2, 3}}, map[string][]int{"s": []int{1, 2, 3}}},
1204+ {&struct{ S *[]int }{&[]int{1, 2, 3}}, map[string][]int{"s": []int{1, 2, 3}}},
1205+
1206+ // Conditionals
1207+ {&condBool{true}, map[string]bool{"v": true}},
1208+ {&condBool{}, map[string]bool{}},
1209+ {&condInt{1}, map[string]int{"v": 1}},
1210+ {&condInt{}, map[string]int{}},
1211+ {&condUInt{1}, map[string]uint{"v": 1}},
1212+ {&condUInt{}, map[string]uint{}},
1213+ {&condFloat{}, map[string]int{}},
1214+ {&condStr{"yo"}, map[string]string{"v": "yo"}},
1215+ {&condStr{}, map[string]string{}},
1216+ {&condStrNS{"yo"}, map[string]string{"v": "yo"}},
1217+ {&condStrNS{}, map[string]string{}},
1218+ {&condSlice{[]string{"yo"}}, map[string][]string{"v": []string{"yo"}}},
1219+ {&condSlice{}, map[string][]string{}},
1220+ {&condMap{map[string]int{"k": 1}}, bson.M{"v": bson.M{"k": 1}}},
1221+ {&condMap{}, map[string][]string{}},
1222+ {&condIface{"yo"}, map[string]string{"v": "yo"}},
1223+ {&condIface{""}, map[string]string{"v": ""}},
1224+ {&condIface{}, map[string]string{}},
1225+ {&condPtr{&truevar}, map[string]bool{"v": true}},
1226+ {&condPtr{&falsevar}, map[string]bool{"v": false}},
1227+ {&condPtr{}, map[string]string{}},
1228+
1229+ {&condTime{time.Unix(123456789, 123e6)}, map[string]time.Time{"v": time.Unix(123456789, 123e6)}},
1230+ {&condTime{}, map[string]string{}},
1231+
1232+ {&condStruct{struct{ A []int }{[]int{1}}}, bson.M{"v": bson.M{"a": []interface{}{1}}}},
1233+ {&condStruct{struct{ A []int }{}}, bson.M{}},
1234+
1235+ {&namedCondStr{"yo"}, map[string]string{"myv": "yo"}},
1236+ {&namedCondStr{}, map[string]string{}},
1237+
1238+ {&shortInt{1}, map[string]interface{}{"v": 1}},
1239+ {&shortInt{1 << 30}, map[string]interface{}{"v": 1 << 30}},
1240+ {&shortInt{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}},
1241+ {&shortUint{1 << 30}, map[string]interface{}{"v": 1 << 30}},
1242+ {&shortUint{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}},
1243+ {&shortIface{int64(1) << 31}, map[string]interface{}{"v": int64(1 << 31)}},
1244+ {&shortPtr{int64ptr}, map[string]interface{}{"v": intvar}},
1245+
1246+ {&shortNonEmptyInt{1}, map[string]interface{}{"v": 1}},
1247+ {&shortNonEmptyInt{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}},
1248+ {&shortNonEmptyInt{}, map[string]interface{}{}},
1249+
1250+ {&inlineInt{struct{ A, B int }{1, 2}}, map[string]interface{}{"a": 1, "b": 2}},
1251+ {&inlineMap{A: 1, M: map[string]interface{}{"b": 2}}, map[string]interface{}{"a": 1, "b": 2}},
1252+ {&inlineMap{A: 1, M: nil}, map[string]interface{}{"a": 1}},
1253+ {&inlineMapInt{A: 1, M: map[string]int{"b": 2}}, map[string]int{"a": 1, "b": 2}},
1254+ {&inlineMapInt{A: 1, M: nil}, map[string]int{"a": 1}},
1255+ {&inlineMapMyM{A: 1, M: MyM{"b": MyM{"c": 3}}}, map[string]interface{}{"a": 1, "b": map[string]interface{}{"c": 3}}},
1256+
1257+ // []byte <=> MyBytes
1258+ {&struct{ B MyBytes }{[]byte("abc")}, map[string]string{"b": "abc"}},
1259+ {&struct{ B MyBytes }{[]byte{}}, map[string]string{"b": ""}},
1260+ {&struct{ B MyBytes }{}, map[string]bool{}},
1261+ {&struct{ B []byte }{[]byte("abc")}, map[string]MyBytes{"b": []byte("abc")}},
1262+
1263+ // bool <=> MyBool
1264+ {&struct{ B MyBool }{true}, map[string]bool{"b": true}},
1265+ {&struct{ B MyBool }{}, map[string]bool{"b": false}},
1266+ {&struct{ B MyBool }{}, map[string]string{}},
1267+ {&struct{ B bool }{}, map[string]MyBool{"b": false}},
1268+
1269+ // arrays
1270+ {&struct{ V [2]int }{[...]int{1, 2}}, map[string][2]int{"v": [2]int{1, 2}}},
1271+ {&struct{ V [2]byte }{[...]byte{1, 2}}, map[string][2]byte{"v": [2]byte{1, 2}}},
1272+
1273+ // zero time
1274+ {&struct{ V time.Time }{}, map[string]interface{}{"v": time.Time{}}},
1275+
1276+ // zero time + 1 second + 1 millisecond; overflows int64 as nanoseconds
1277+ {&struct{ V time.Time }{time.Unix(-62135596799, 1e6).Local()},
1278+ map[string]interface{}{"v": time.Unix(-62135596799, 1e6).Local()}},
1279+
1280+ // bson.D <=> []DocElem
1281+ {&bson.D{{"a", bson.D{{"b", 1}, {"c", 2}}}}, &bson.D{{"a", bson.D{{"b", 1}, {"c", 2}}}}},
1282+ {&bson.D{{"a", bson.D{{"b", 1}, {"c", 2}}}}, &MyD{{"a", MyD{{"b", 1}, {"c", 2}}}}},
1283+ {&struct{ V MyD }{MyD{{"a", 1}}}, &bson.D{{"v", bson.D{{"a", 1}}}}},
1284+
1285+ // bson.RawD <=> []RawDocElem
1286+ {&bson.RawD{{"a", bson.Raw{0x08, []byte{0x01}}}}, &bson.RawD{{"a", bson.Raw{0x08, []byte{0x01}}}}},
1287+ {&bson.RawD{{"a", bson.Raw{0x08, []byte{0x01}}}}, &MyRawD{{"a", bson.Raw{0x08, []byte{0x01}}}}},
1288+
1289+ // bson.M <=> map
1290+ {bson.M{"a": bson.M{"b": 1, "c": 2}}, MyM{"a": MyM{"b": 1, "c": 2}}},
1291+ {bson.M{"a": bson.M{"b": 1, "c": 2}}, map[string]interface{}{"a": map[string]interface{}{"b": 1, "c": 2}}},
1292+
1293+ // bson.M <=> map[MyString]
1294+ {bson.M{"a": bson.M{"b": 1, "c": 2}}, map[MyString]interface{}{"a": map[MyString]interface{}{"b": 1, "c": 2}}},
1295+
1296+ // json.Number <=> int64, float64
1297+ {&struct{ N json.Number }{"5"}, map[string]interface{}{"n": int64(5)}},
1298+ {&struct{ N json.Number }{"5.05"}, map[string]interface{}{"n": 5.05}},
1299+ {&struct{ N json.Number }{"9223372036854776000"}, map[string]interface{}{"n": float64(1 << 63)}},
1300+
1301+ // bson.D <=> non-struct getter/setter
1302+ {&bson.D{{"a", 1}}, &getterSetterD{{"a", 1}, {"suffix", true}}},
1303+ {&bson.D{{"a", 42}}, &gsintvar},
1304+
1305+ // Interface slice setter.
1306+ {&struct{ V ifaceSlice }{ifaceSlice{nil, nil, nil}}, bson.M{"v": []interface{}{3}}},
1307+}
1308+
1309+// Same thing, but only one way (obj1 => obj2).
1310+var oneWayCrossItems = []crossTypeItem{
1311+ // map <=> struct
1312+ {map[string]interface{}{"a": 1, "b": "2", "c": 3}, map[string]int{"a": 1, "c": 3}},
1313+
1314+ // inline map elides badly typed values
1315+ {map[string]interface{}{"a": 1, "b": "2", "c": 3}, &inlineMapInt{A: 1, M: map[string]int{"c": 3}}},
1316+
1317+ // Can't decode int into struct.
1318+ {bson.M{"a": bson.M{"b": 2}}, &struct{ A bool }{}},
1319+
1320+ // Would get decoded into a int32 too in the opposite direction.
1321+ {&shortIface{int64(1) << 30}, map[string]interface{}{"v": 1 << 30}},
1322+
1323+ // Ensure omitempty on struct with private fields works properly.
1324+ {&struct {
1325+ V struct{ v time.Time } ",omitempty"
1326+ }{}, map[string]interface{}{}},
1327+}
1328+
1329+func testCrossPair(c *C, dump interface{}, load interface{}) {
1330+ c.Logf("Dump: %#v", dump)
1331+ c.Logf("Load: %#v", load)
1332+ zero := makeZeroDoc(load)
1333+ data, err := bson.Marshal(dump)
1334+ c.Assert(err, IsNil)
1335+ c.Logf("Dumped: %#v", string(data))
1336+ err = bson.Unmarshal(data, zero)
1337+ c.Assert(err, IsNil)
1338+ c.Logf("Loaded: %#v", zero)
1339+ c.Assert(zero, DeepEquals, load)
1340+}
1341+
1342+func (s *S) TestTwoWayCrossPairs(c *C) {
1343+ for _, item := range twoWayCrossItems {
1344+ testCrossPair(c, item.obj1, item.obj2)
1345+ testCrossPair(c, item.obj2, item.obj1)
1346+ }
1347+}
1348+
1349+func (s *S) TestOneWayCrossPairs(c *C) {
1350+ for _, item := range oneWayCrossItems {
1351+ testCrossPair(c, item.obj1, item.obj2)
1352+ }
1353+}
1354+
1355+// --------------------------------------------------------------------------
1356+// ObjectId hex representation test.
1357+
1358+func (s *S) TestObjectIdHex(c *C) {
1359+ id := bson.ObjectIdHex("4d88e15b60f486e428412dc9")
1360+ c.Assert(id.String(), Equals, `ObjectIdHex("4d88e15b60f486e428412dc9")`)
1361+ c.Assert(id.Hex(), Equals, "4d88e15b60f486e428412dc9")
1362+}
1363+
1364+func (s *S) TestIsObjectIdHex(c *C) {
1365+ test := []struct {
1366+ id string
1367+ valid bool
1368+ }{
1369+ {"4d88e15b60f486e428412dc9", true},
1370+ {"4d88e15b60f486e428412dc", false},
1371+ {"4d88e15b60f486e428412dc9e", false},
1372+ {"4d88e15b60f486e428412dcx", false},
1373+ }
1374+ for _, t := range test {
1375+ c.Assert(bson.IsObjectIdHex(t.id), Equals, t.valid)
1376+ }
1377+}
1378+
1379+// --------------------------------------------------------------------------
1380+// ObjectId parts extraction tests.
1381+
1382+type objectIdParts struct {
1383+ id bson.ObjectId
1384+ timestamp int64
1385+ machine []byte
1386+ pid uint16
1387+ counter int32
1388+}
1389+
1390+var objectIds = []objectIdParts{
1391+ objectIdParts{
1392+ bson.ObjectIdHex("4d88e15b60f486e428412dc9"),
1393+ 1300816219,
1394+ []byte{0x60, 0xf4, 0x86},
1395+ 0xe428,
1396+ 4271561,
1397+ },
1398+ objectIdParts{
1399+ bson.ObjectIdHex("000000000000000000000000"),
1400+ 0,
1401+ []byte{0x00, 0x00, 0x00},
1402+ 0x0000,
1403+ 0,
1404+ },
1405+ objectIdParts{
1406+ bson.ObjectIdHex("00000000aabbccddee000001"),
1407+ 0,
1408+ []byte{0xaa, 0xbb, 0xcc},
1409+ 0xddee,
1410+ 1,
1411+ },
1412+}
1413+
1414+func (s *S) TestObjectIdPartsExtraction(c *C) {
1415+ for i, v := range objectIds {
1416+ t := time.Unix(v.timestamp, 0)
1417+ c.Assert(v.id.Time(), Equals, t, Commentf("#%d Wrong timestamp value", i))
1418+ c.Assert(v.id.Machine(), DeepEquals, v.machine, Commentf("#%d Wrong machine id value", i))
1419+ c.Assert(v.id.Pid(), Equals, v.pid, Commentf("#%d Wrong pid value", i))
1420+ c.Assert(v.id.Counter(), Equals, v.counter, Commentf("#%d Wrong counter value", i))
1421+ }
1422+}
1423+
1424+func (s *S) TestNow(c *C) {
1425+ before := time.Now()
1426+ time.Sleep(1e6)
1427+ now := bson.Now()
1428+ time.Sleep(1e6)
1429+ after := time.Now()
1430+ c.Assert(now.After(before) && now.Before(after), Equals, true, Commentf("now=%s, before=%s, after=%s", now, before, after))
1431+}
1432+
1433+// --------------------------------------------------------------------------
1434+// ObjectId generation tests.
1435+
1436+func (s *S) TestNewObjectId(c *C) {
1437+ // Generate 10 ids
1438+ ids := make([]bson.ObjectId, 10)
1439+ for i := 0; i < 10; i++ {
1440+ ids[i] = bson.NewObjectId()
1441+ }
1442+ for i := 1; i < 10; i++ {
1443+ prevId := ids[i-1]
1444+ id := ids[i]
1445+ // Test for uniqueness among all other 9 generated ids
1446+ for j, tid := range ids {
1447+ if j != i {
1448+ c.Assert(id, Not(Equals), tid, Commentf("Generated ObjectId is not unique"))
1449+ }
1450+ }
1451+ // Check that timestamp was incremented and is within 30 seconds of the previous one
1452+ secs := id.Time().Sub(prevId.Time()).Seconds()
1453+ c.Assert((secs >= 0 && secs <= 30), Equals, true, Commentf("Wrong timestamp in generated ObjectId"))
1454+ // Check that machine ids are the same
1455+ c.Assert(id.Machine(), DeepEquals, prevId.Machine())
1456+ // Check that pids are the same
1457+ c.Assert(id.Pid(), Equals, prevId.Pid())
1458+ // Test for proper increment
1459+ delta := int(id.Counter() - prevId.Counter())
1460+ c.Assert(delta, Equals, 1, Commentf("Wrong increment in generated ObjectId"))
1461+ }
1462+}
1463+
1464+func (s *S) TestNewObjectIdWithTime(c *C) {
1465+ t := time.Unix(12345678, 0)
1466+ id := bson.NewObjectIdWithTime(t)
1467+ c.Assert(id.Time(), Equals, t)
1468+ c.Assert(id.Machine(), DeepEquals, []byte{0x00, 0x00, 0x00})
1469+ c.Assert(int(id.Pid()), Equals, 0)
1470+ c.Assert(int(id.Counter()), Equals, 0)
1471+}
1472+
1473+// --------------------------------------------------------------------------
1474+// ObjectId JSON marshalling.
1475+
1476+type jsonType struct {
1477+ Id bson.ObjectId
1478+}
1479+
1480+var jsonIdTests = []struct {
1481+ value jsonType
1482+ json string
1483+ marshal bool
1484+ unmarshal bool
1485+ error string
1486+}{{
1487+ value: jsonType{Id: bson.ObjectIdHex("4d88e15b60f486e428412dc9")},
1488+ json: `{"Id":"4d88e15b60f486e428412dc9"}`,
1489+ marshal: true,
1490+ unmarshal: true,
1491+}, {
1492+ value: jsonType{},
1493+ json: `{"Id":""}`,
1494+ marshal: true,
1495+ unmarshal: true,
1496+}, {
1497+ value: jsonType{},
1498+ json: `{"Id":null}`,
1499+ marshal: false,
1500+ unmarshal: true,
1501+}, {
1502+ json: `{"Id":"4d88e15b60f486e428412dc9A"}`,
1503+ error: `Invalid ObjectId in JSON: "4d88e15b60f486e428412dc9A"`,
1504+ marshal: false,
1505+ unmarshal: true,
1506+}, {
1507+ json: `{"Id":"4d88e15b60f486e428412dcZ"}`,
1508+ error: `Invalid ObjectId in JSON: "4d88e15b60f486e428412dcZ" .*`,
1509+ marshal: false,
1510+ unmarshal: true,
1511+}}
1512+
1513+func (s *S) TestObjectIdJSONMarshaling(c *C) {
1514+ for _, test := range jsonIdTests {
1515+ if test.marshal {
1516+ data, err := json.Marshal(&test.value)
1517+ if test.error == "" {
1518+ c.Assert(err, IsNil)
1519+ c.Assert(string(data), Equals, test.json)
1520+ } else {
1521+ c.Assert(err, ErrorMatches, test.error)
1522+ }
1523+ }
1524+
1525+ if test.unmarshal {
1526+ var value jsonType
1527+ err := json.Unmarshal([]byte(test.json), &value)
1528+ if test.error == "" {
1529+ c.Assert(err, IsNil)
1530+ c.Assert(value, DeepEquals, test.value)
1531+ } else {
1532+ c.Assert(err, ErrorMatches, test.error)
1533+ }
1534+ }
1535+ }
1536+}
1537+
1538+// --------------------------------------------------------------------------
1539+// Some simple benchmarks.
1540+
1541+type BenchT struct {
1542+ A, B, C, D, E, F string
1543+}
1544+
1545+type BenchRawT struct {
1546+ A string
1547+ B int
1548+ C bson.M
1549+ D []float64
1550+}
1551+
1552+func (s *S) BenchmarkUnmarhsalStruct(c *C) {
1553+ v := BenchT{A: "A", D: "D", E: "E"}
1554+ data, err := bson.Marshal(&v)
1555+ if err != nil {
1556+ panic(err)
1557+ }
1558+ c.ResetTimer()
1559+ for i := 0; i < c.N; i++ {
1560+ err = bson.Unmarshal(data, &v)
1561+ }
1562+ if err != nil {
1563+ panic(err)
1564+ }
1565+}
1566+
1567+func (s *S) BenchmarkUnmarhsalMap(c *C) {
1568+ m := bson.M{"a": "a", "d": "d", "e": "e"}
1569+ data, err := bson.Marshal(&m)
1570+ if err != nil {
1571+ panic(err)
1572+ }
1573+ c.ResetTimer()
1574+ for i := 0; i < c.N; i++ {
1575+ err = bson.Unmarshal(data, &m)
1576+ }
1577+ if err != nil {
1578+ panic(err)
1579+ }
1580+}
1581+
1582+func (s *S) BenchmarkUnmarshalRaw(c *C) {
1583+ var err error
1584+ m := BenchRawT{
1585+ A: "test_string",
1586+ B: 123,
1587+ C: bson.M{
1588+ "subdoc_int": 12312,
1589+ "subdoc_doc": bson.M{"1": 1},
1590+ },
1591+ D: []float64{0.0, 1.3333, -99.9997, 3.1415},
1592+ }
1593+ data, err := bson.Marshal(&m)
1594+ if err != nil {
1595+ panic(err)
1596+ }
1597+ raw := bson.Raw{}
1598+ c.ResetTimer()
1599+ for i := 0; i < c.N; i++ {
1600+ err = bson.Unmarshal(data, &raw)
1601+ }
1602+ if err != nil {
1603+ panic(err)
1604+ }
1605+}
vendor/gopkg.in/mgo.v2/bson/decode.go+825−0
@@ -0,0 +1,825 @@
1+// BSON library for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+// gobson - BSON library for Go.
27+
28+package bson
29+
30+import (
31+ "fmt"
32+ "math"
33+ "net/url"
34+ "reflect"
35+ "strconv"
36+ "sync"
37+ "time"
38+)
39+
40+type decoder struct {
41+ in []byte
42+ i int
43+ docType reflect.Type
44+}
45+
46+var typeM = reflect.TypeOf(M{})
47+
48+func newDecoder(in []byte) *decoder {
49+ return &decoder{in, 0, typeM}
50+}
51+
52+// --------------------------------------------------------------------------
53+// Some helper functions.
54+
55+func corrupted() {
56+ panic("Document is corrupted")
57+}
58+
59+func settableValueOf(i interface{}) reflect.Value {
60+ v := reflect.ValueOf(i)
61+ sv := reflect.New(v.Type()).Elem()
62+ sv.Set(v)
63+ return sv
64+}
65+
66+// --------------------------------------------------------------------------
67+// Unmarshaling of documents.
68+
69+const (
70+ setterUnknown = iota
71+ setterNone
72+ setterType
73+ setterAddr
74+)
75+
76+var setterStyles map[reflect.Type]int
77+var setterIface reflect.Type
78+var setterMutex sync.RWMutex
79+
80+func init() {
81+ var iface Setter
82+ setterIface = reflect.TypeOf(&iface).Elem()
83+ setterStyles = make(map[reflect.Type]int)
84+}
85+
86+func setterStyle(outt reflect.Type) int {
87+ setterMutex.RLock()
88+ style := setterStyles[outt]
89+ setterMutex.RUnlock()
90+ if style == setterUnknown {
91+ setterMutex.Lock()
92+ defer setterMutex.Unlock()
93+ if outt.Implements(setterIface) {
94+ setterStyles[outt] = setterType
95+ } else if reflect.PtrTo(outt).Implements(setterIface) {
96+ setterStyles[outt] = setterAddr
97+ } else {
98+ setterStyles[outt] = setterNone
99+ }
100+ style = setterStyles[outt]
101+ }
102+ return style
103+}
104+
105+func getSetter(outt reflect.Type, out reflect.Value) Setter {
106+ style := setterStyle(outt)
107+ if style == setterNone {
108+ return nil
109+ }
110+ if style == setterAddr {
111+ if !out.CanAddr() {
112+ return nil
113+ }
114+ out = out.Addr()
115+ } else if outt.Kind() == reflect.Ptr && out.IsNil() {
116+ out.Set(reflect.New(outt.Elem()))
117+ }
118+ return out.Interface().(Setter)
119+}
120+
121+func clearMap(m reflect.Value) {
122+ var none reflect.Value
123+ for _, k := range m.MapKeys() {
124+ m.SetMapIndex(k, none)
125+ }
126+}
127+
128+func (d *decoder) readDocTo(out reflect.Value) {
129+ var elemType reflect.Type
130+ outt := out.Type()
131+ outk := outt.Kind()
132+
133+ for {
134+ if outk == reflect.Ptr && out.IsNil() {
135+ out.Set(reflect.New(outt.Elem()))
136+ }
137+ if setter := getSetter(outt, out); setter != nil {
138+ var raw Raw
139+ d.readDocTo(reflect.ValueOf(&raw))
140+ err := setter.SetBSON(raw)
141+ if _, ok := err.(*TypeError); err != nil && !ok {
142+ panic(err)
143+ }
144+ return
145+ }
146+ if outk == reflect.Ptr {
147+ out = out.Elem()
148+ outt = out.Type()
149+ outk = out.Kind()
150+ continue
151+ }
152+ break
153+ }
154+
155+ var fieldsMap map[string]fieldInfo
156+ var inlineMap reflect.Value
157+ start := d.i
158+
159+ origout := out
160+ if outk == reflect.Interface {
161+ if d.docType.Kind() == reflect.Map {
162+ mv := reflect.MakeMap(d.docType)
163+ out.Set(mv)
164+ out = mv
165+ } else {
166+ dv := reflect.New(d.docType).Elem()
167+ out.Set(dv)
168+ out = dv
169+ }
170+ outt = out.Type()
171+ outk = outt.Kind()
172+ }
173+
174+ docType := d.docType
175+ keyType := typeString
176+ convertKey := false
177+ switch outk {
178+ case reflect.Map:
179+ keyType = outt.Key()
180+ if keyType.Kind() != reflect.String {
181+ panic("BSON map must have string keys. Got: " + outt.String())
182+ }
183+ if keyType != typeString {
184+ convertKey = true
185+ }
186+ elemType = outt.Elem()
187+ if elemType == typeIface {
188+ d.docType = outt
189+ }
190+ if out.IsNil() {
191+ out.Set(reflect.MakeMap(out.Type()))
192+ } else if out.Len() > 0 {
193+ clearMap(out)
194+ }
195+ case reflect.Struct:
196+ if outt != typeRaw {
197+ sinfo, err := getStructInfo(out.Type())
198+ if err != nil {
199+ panic(err)
200+ }
201+ fieldsMap = sinfo.FieldsMap
202+ out.Set(sinfo.Zero)
203+ if sinfo.InlineMap != -1 {
204+ inlineMap = out.Field(sinfo.InlineMap)
205+ if !inlineMap.IsNil() && inlineMap.Len() > 0 {
206+ clearMap(inlineMap)
207+ }
208+ elemType = inlineMap.Type().Elem()
209+ if elemType == typeIface {
210+ d.docType = inlineMap.Type()
211+ }
212+ }
213+ }
214+ case reflect.Slice:
215+ switch outt.Elem() {
216+ case typeDocElem:
217+ origout.Set(d.readDocElems(outt))
218+ return
219+ case typeRawDocElem:
220+ origout.Set(d.readRawDocElems(outt))
221+ return
222+ }
223+ fallthrough
224+ default:
225+ panic("Unsupported document type for unmarshalling: " + out.Type().String())
226+ }
227+
228+ end := int(d.readInt32())
229+ end += d.i - 4
230+ if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
231+ corrupted()
232+ }
233+ for d.in[d.i] != '\x00' {
234+ kind := d.readByte()
235+ name := d.readCStr()
236+ if d.i >= end {
237+ corrupted()
238+ }
239+
240+ switch outk {
241+ case reflect.Map:
242+ e := reflect.New(elemType).Elem()
243+ if d.readElemTo(e, kind) {
244+ k := reflect.ValueOf(name)
245+ if convertKey {
246+ k = k.Convert(keyType)
247+ }
248+ out.SetMapIndex(k, e)
249+ }
250+ case reflect.Struct:
251+ if outt == typeRaw {
252+ d.dropElem(kind)
253+ } else {
254+ if info, ok := fieldsMap[name]; ok {
255+ if info.Inline == nil {
256+ d.readElemTo(out.Field(info.Num), kind)
257+ } else {
258+ d.readElemTo(out.FieldByIndex(info.Inline), kind)
259+ }
260+ } else if inlineMap.IsValid() {
261+ if inlineMap.IsNil() {
262+ inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
263+ }
264+ e := reflect.New(elemType).Elem()
265+ if d.readElemTo(e, kind) {
266+ inlineMap.SetMapIndex(reflect.ValueOf(name), e)
267+ }
268+ } else {
269+ d.dropElem(kind)
270+ }
271+ }
272+ case reflect.Slice:
273+ }
274+
275+ if d.i >= end {
276+ corrupted()
277+ }
278+ }
279+ d.i++ // '\x00'
280+ if d.i != end {
281+ corrupted()
282+ }
283+ d.docType = docType
284+
285+ if outt == typeRaw {
286+ out.Set(reflect.ValueOf(Raw{0x03, d.in[start:d.i]}))
287+ }
288+}
289+
290+func (d *decoder) readArrayDocTo(out reflect.Value) {
291+ end := int(d.readInt32())
292+ end += d.i - 4
293+ if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
294+ corrupted()
295+ }
296+ i := 0
297+ l := out.Len()
298+ for d.in[d.i] != '\x00' {
299+ if i >= l {
300+ panic("Length mismatch on array field")
301+ }
302+ kind := d.readByte()
303+ for d.i < end && d.in[d.i] != '\x00' {
304+ d.i++
305+ }
306+ if d.i >= end {
307+ corrupted()
308+ }
309+ d.i++
310+ d.readElemTo(out.Index(i), kind)
311+ if d.i >= end {
312+ corrupted()
313+ }
314+ i++
315+ }
316+ if i != l {
317+ panic("Length mismatch on array field")
318+ }
319+ d.i++ // '\x00'
320+ if d.i != end {
321+ corrupted()
322+ }
323+}
324+
325+func (d *decoder) readSliceDoc(t reflect.Type) interface{} {
326+ tmp := make([]reflect.Value, 0, 8)
327+ elemType := t.Elem()
328+
329+ end := int(d.readInt32())
330+ end += d.i - 4
331+ if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
332+ corrupted()
333+ }
334+ for d.in[d.i] != '\x00' {
335+ kind := d.readByte()
336+ for d.i < end && d.in[d.i] != '\x00' {
337+ d.i++
338+ }
339+ if d.i >= end {
340+ corrupted()
341+ }
342+ d.i++
343+ e := reflect.New(elemType).Elem()
344+ if d.readElemTo(e, kind) {
345+ tmp = append(tmp, e)
346+ }
347+ if d.i >= end {
348+ corrupted()
349+ }
350+ }
351+ d.i++ // '\x00'
352+ if d.i != end {
353+ corrupted()
354+ }
355+
356+ n := len(tmp)
357+ slice := reflect.MakeSlice(t, n, n)
358+ for i := 0; i != n; i++ {
359+ slice.Index(i).Set(tmp[i])
360+ }
361+ return slice.Interface()
362+}
363+
364+var typeSlice = reflect.TypeOf([]interface{}{})
365+var typeIface = typeSlice.Elem()
366+
367+func (d *decoder) readDocElems(typ reflect.Type) reflect.Value {
368+ docType := d.docType
369+ d.docType = typ
370+ slice := make([]DocElem, 0, 8)
371+ d.readDocWith(func(kind byte, name string) {
372+ e := DocElem{Name: name}
373+ v := reflect.ValueOf(&e.Value)
374+ if d.readElemTo(v.Elem(), kind) {
375+ slice = append(slice, e)
376+ }
377+ })
378+ slicev := reflect.New(typ).Elem()
379+ slicev.Set(reflect.ValueOf(slice))
380+ d.docType = docType
381+ return slicev
382+}
383+
384+func (d *decoder) readRawDocElems(typ reflect.Type) reflect.Value {
385+ docType := d.docType
386+ d.docType = typ
387+ slice := make([]RawDocElem, 0, 8)
388+ d.readDocWith(func(kind byte, name string) {
389+ e := RawDocElem{Name: name}
390+ v := reflect.ValueOf(&e.Value)
391+ if d.readElemTo(v.Elem(), kind) {
392+ slice = append(slice, e)
393+ }
394+ })
395+ slicev := reflect.New(typ).Elem()
396+ slicev.Set(reflect.ValueOf(slice))
397+ d.docType = docType
398+ return slicev
399+}
400+
401+func (d *decoder) readDocWith(f func(kind byte, name string)) {
402+ end := int(d.readInt32())
403+ end += d.i - 4
404+ if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
405+ corrupted()
406+ }
407+ for d.in[d.i] != '\x00' {
408+ kind := d.readByte()
409+ name := d.readCStr()
410+ if d.i >= end {
411+ corrupted()
412+ }
413+ f(kind, name)
414+ if d.i >= end {
415+ corrupted()
416+ }
417+ }
418+ d.i++ // '\x00'
419+ if d.i != end {
420+ corrupted()
421+ }
422+}
423+
424+// --------------------------------------------------------------------------
425+// Unmarshaling of individual elements within a document.
426+
427+var blackHole = settableValueOf(struct{}{})
428+
429+func (d *decoder) dropElem(kind byte) {
430+ d.readElemTo(blackHole, kind)
431+}
432+
433+// Attempt to decode an element from the document and put it into out.
434+// If the types are not compatible, the returned ok value will be
435+// false and out will be unchanged.
436+func (d *decoder) readElemTo(out reflect.Value, kind byte) (good bool) {
437+
438+ start := d.i
439+
440+ if kind == '\x03' {
441+ // Delegate unmarshaling of documents.
442+ outt := out.Type()
443+ outk := out.Kind()
444+ switch outk {
445+ case reflect.Interface, reflect.Ptr, reflect.Struct, reflect.Map:
446+ d.readDocTo(out)
447+ return true
448+ }
449+ if setterStyle(outt) != setterNone {
450+ d.readDocTo(out)
451+ return true
452+ }
453+ if outk == reflect.Slice {
454+ switch outt.Elem() {
455+ case typeDocElem:
456+ out.Set(d.readDocElems(outt))
457+ case typeRawDocElem:
458+ out.Set(d.readRawDocElems(outt))
459+ }
460+ return true
461+ }
462+ d.readDocTo(blackHole)
463+ return true
464+ }
465+
466+ var in interface{}
467+
468+ switch kind {
469+ case 0x01: // Float64
470+ in = d.readFloat64()
471+ case 0x02: // UTF-8 string
472+ in = d.readStr()
473+ case 0x03: // Document
474+ panic("Can't happen. Handled above.")
475+ case 0x04: // Array
476+ outt := out.Type()
477+ if setterStyle(outt) != setterNone {
478+ // Skip the value so its data is handed to the setter below.
479+ d.dropElem(kind)
480+ break
481+ }
482+ for outt.Kind() == reflect.Ptr {
483+ outt = outt.Elem()
484+ }
485+ switch outt.Kind() {
486+ case reflect.Array:
487+ d.readArrayDocTo(out)
488+ return true
489+ case reflect.Slice:
490+ in = d.readSliceDoc(outt)
491+ default:
492+ in = d.readSliceDoc(typeSlice)
493+ }
494+ case 0x05: // Binary
495+ b := d.readBinary()
496+ if b.Kind == 0x00 || b.Kind == 0x02 {
497+ in = b.Data
498+ } else {
499+ in = b
500+ }
501+ case 0x06: // Undefined (obsolete, but still seen in the wild)
502+ in = Undefined
503+ case 0x07: // ObjectId
504+ in = ObjectId(d.readBytes(12))
505+ case 0x08: // Bool
506+ in = d.readBool()
507+ case 0x09: // Timestamp
508+ // MongoDB handles timestamps as milliseconds.
509+ i := d.readInt64()
510+ if i == -62135596800000 {
511+ in = time.Time{} // In UTC for convenience.
512+ } else {
513+ in = time.Unix(i/1e3, i%1e3*1e6)
514+ }
515+ case 0x0A: // Nil
516+ in = nil
517+ case 0x0B: // RegEx
518+ in = d.readRegEx()
519+ case 0x0C:
520+ in = DBPointer{Namespace: d.readStr(), Id: ObjectId(d.readBytes(12))}
521+ case 0x0D: // JavaScript without scope
522+ in = JavaScript{Code: d.readStr()}
523+ case 0x0E: // Symbol
524+ in = Symbol(d.readStr())
525+ case 0x0F: // JavaScript with scope
526+ d.i += 4 // Skip length
527+ js := JavaScript{d.readStr(), make(M)}
528+ d.readDocTo(reflect.ValueOf(js.Scope))
529+ in = js
530+ case 0x10: // Int32
531+ in = int(d.readInt32())
532+ case 0x11: // Mongo-specific timestamp
533+ in = MongoTimestamp(d.readInt64())
534+ case 0x12: // Int64
535+ in = d.readInt64()
536+ case 0x7F: // Max key
537+ in = MaxKey
538+ case 0xFF: // Min key
539+ in = MinKey
540+ default:
541+ panic(fmt.Sprintf("Unknown element kind (0x%02X)", kind))
542+ }
543+
544+ outt := out.Type()
545+
546+ if outt == typeRaw {
547+ out.Set(reflect.ValueOf(Raw{kind, d.in[start:d.i]}))
548+ return true
549+ }
550+
551+ if setter := getSetter(outt, out); setter != nil {
552+ err := setter.SetBSON(Raw{kind, d.in[start:d.i]})
553+ if err == SetZero {
554+ out.Set(reflect.Zero(outt))
555+ return true
556+ }
557+ if err == nil {
558+ return true
559+ }
560+ if _, ok := err.(*TypeError); !ok {
561+ panic(err)
562+ }
563+ return false
564+ }
565+
566+ if in == nil {
567+ out.Set(reflect.Zero(outt))
568+ return true
569+ }
570+
571+ outk := outt.Kind()
572+
573+ // Dereference and initialize pointer if necessary.
574+ first := true
575+ for outk == reflect.Ptr {
576+ if !out.IsNil() {
577+ out = out.Elem()
578+ } else {
579+ elem := reflect.New(outt.Elem())
580+ if first {
581+ // Only set if value is compatible.
582+ first = false
583+ defer func(out, elem reflect.Value) {
584+ if good {
585+ out.Set(elem)
586+ }
587+ }(out, elem)
588+ } else {
589+ out.Set(elem)
590+ }
591+ out = elem
592+ }
593+ outt = out.Type()
594+ outk = outt.Kind()
595+ }
596+
597+ inv := reflect.ValueOf(in)
598+ if outt == inv.Type() {
599+ out.Set(inv)
600+ return true
601+ }
602+
603+ switch outk {
604+ case reflect.Interface:
605+ out.Set(inv)
606+ return true
607+ case reflect.String:
608+ switch inv.Kind() {
609+ case reflect.String:
610+ out.SetString(inv.String())
611+ return true
612+ case reflect.Slice:
613+ if b, ok := in.([]byte); ok {
614+ out.SetString(string(b))
615+ return true
616+ }
617+ case reflect.Int, reflect.Int64:
618+ if outt == typeJSONNumber {
619+ out.SetString(strconv.FormatInt(inv.Int(), 10))
620+ return true
621+ }
622+ case reflect.Float64:
623+ if outt == typeJSONNumber {
624+ out.SetString(strconv.FormatFloat(inv.Float(), 'f', -1, 64))
625+ return true
626+ }
627+ }
628+ case reflect.Slice, reflect.Array:
629+ // Remember, array (0x04) slices are built with the correct
630+ // element type. If we are here, must be a cross BSON kind
631+ // conversion (e.g. 0x05 unmarshalling on string).
632+ if outt.Elem().Kind() != reflect.Uint8 {
633+ break
634+ }
635+ switch inv.Kind() {
636+ case reflect.String:
637+ slice := []byte(inv.String())
638+ out.Set(reflect.ValueOf(slice))
639+ return true
640+ case reflect.Slice:
641+ switch outt.Kind() {
642+ case reflect.Array:
643+ reflect.Copy(out, inv)
644+ case reflect.Slice:
645+ out.SetBytes(inv.Bytes())
646+ }
647+ return true
648+ }
649+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
650+ switch inv.Kind() {
651+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
652+ out.SetInt(inv.Int())
653+ return true
654+ case reflect.Float32, reflect.Float64:
655+ out.SetInt(int64(inv.Float()))
656+ return true
657+ case reflect.Bool:
658+ if inv.Bool() {
659+ out.SetInt(1)
660+ } else {
661+ out.SetInt(0)
662+ }
663+ return true
664+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
665+ panic("can't happen: no uint types in BSON (!?)")
666+ }
667+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
668+ switch inv.Kind() {
669+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
670+ out.SetUint(uint64(inv.Int()))
671+ return true
672+ case reflect.Float32, reflect.Float64:
673+ out.SetUint(uint64(inv.Float()))
674+ return true
675+ case reflect.Bool:
676+ if inv.Bool() {
677+ out.SetUint(1)
678+ } else {
679+ out.SetUint(0)
680+ }
681+ return true
682+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
683+ panic("Can't happen. No uint types in BSON.")
684+ }
685+ case reflect.Float32, reflect.Float64:
686+ switch inv.Kind() {
687+ case reflect.Float32, reflect.Float64:
688+ out.SetFloat(inv.Float())
689+ return true
690+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
691+ out.SetFloat(float64(inv.Int()))
692+ return true
693+ case reflect.Bool:
694+ if inv.Bool() {
695+ out.SetFloat(1)
696+ } else {
697+ out.SetFloat(0)
698+ }
699+ return true
700+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
701+ panic("Can't happen. No uint types in BSON?")
702+ }
703+ case reflect.Bool:
704+ switch inv.Kind() {
705+ case reflect.Bool:
706+ out.SetBool(inv.Bool())
707+ return true
708+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
709+ out.SetBool(inv.Int() != 0)
710+ return true
711+ case reflect.Float32, reflect.Float64:
712+ out.SetBool(inv.Float() != 0)
713+ return true
714+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
715+ panic("Can't happen. No uint types in BSON?")
716+ }
717+ case reflect.Struct:
718+ if outt == typeURL && inv.Kind() == reflect.String {
719+ u, err := url.Parse(inv.String())
720+ if err != nil {
721+ panic(err)
722+ }
723+ out.Set(reflect.ValueOf(u).Elem())
724+ return true
725+ }
726+ }
727+
728+ return false
729+}
730+
731+// --------------------------------------------------------------------------
732+// Parsers of basic types.
733+
734+func (d *decoder) readRegEx() RegEx {
735+ re := RegEx{}
736+ re.Pattern = d.readCStr()
737+ re.Options = d.readCStr()
738+ return re
739+}
740+
741+func (d *decoder) readBinary() Binary {
742+ l := d.readInt32()
743+ b := Binary{}
744+ b.Kind = d.readByte()
745+ b.Data = d.readBytes(l)
746+ if b.Kind == 0x02 && len(b.Data) >= 4 {
747+ // Weird obsolete format with redundant length.
748+ b.Data = b.Data[4:]
749+ }
750+ return b
751+}
752+
753+func (d *decoder) readStr() string {
754+ l := d.readInt32()
755+ b := d.readBytes(l - 1)
756+ if d.readByte() != '\x00' {
757+ corrupted()
758+ }
759+ return string(b)
760+}
761+
762+func (d *decoder) readCStr() string {
763+ start := d.i
764+ end := start
765+ l := len(d.in)
766+ for ; end != l; end++ {
767+ if d.in[end] == '\x00' {
768+ break
769+ }
770+ }
771+ d.i = end + 1
772+ if d.i > l {
773+ corrupted()
774+ }
775+ return string(d.in[start:end])
776+}
777+
778+func (d *decoder) readBool() bool {
779+ if d.readByte() == 1 {
780+ return true
781+ }
782+ return false
783+}
784+
785+func (d *decoder) readFloat64() float64 {
786+ return math.Float64frombits(uint64(d.readInt64()))
787+}
788+
789+func (d *decoder) readInt32() int32 {
790+ b := d.readBytes(4)
791+ return int32((uint32(b[0]) << 0) |
792+ (uint32(b[1]) << 8) |
793+ (uint32(b[2]) << 16) |
794+ (uint32(b[3]) << 24))
795+}
796+
797+func (d *decoder) readInt64() int64 {
798+ b := d.readBytes(8)
799+ return int64((uint64(b[0]) << 0) |
800+ (uint64(b[1]) << 8) |
801+ (uint64(b[2]) << 16) |
802+ (uint64(b[3]) << 24) |
803+ (uint64(b[4]) << 32) |
804+ (uint64(b[5]) << 40) |
805+ (uint64(b[6]) << 48) |
806+ (uint64(b[7]) << 56))
807+}
808+
809+func (d *decoder) readByte() byte {
810+ i := d.i
811+ d.i++
812+ if d.i > len(d.in) {
813+ corrupted()
814+ }
815+ return d.in[i]
816+}
817+
818+func (d *decoder) readBytes(length int32) []byte {
819+ start := d.i
820+ d.i += int(length)
821+ if d.i > len(d.in) {
822+ corrupted()
823+ }
824+ return d.in[start : start+int(length)]
825+}
vendor/gopkg.in/mgo.v2/bson/encode.go+503−0
@@ -0,0 +1,503 @@
1+// BSON library for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+// gobson - BSON library for Go.
27+
28+package bson
29+
30+import (
31+ "encoding/json"
32+ "fmt"
33+ "math"
34+ "net/url"
35+ "reflect"
36+ "strconv"
37+ "time"
38+)
39+
40+// --------------------------------------------------------------------------
41+// Some internal infrastructure.
42+
43+var (
44+ typeBinary = reflect.TypeOf(Binary{})
45+ typeObjectId = reflect.TypeOf(ObjectId(""))
46+ typeDBPointer = reflect.TypeOf(DBPointer{"", ObjectId("")})
47+ typeSymbol = reflect.TypeOf(Symbol(""))
48+ typeMongoTimestamp = reflect.TypeOf(MongoTimestamp(0))
49+ typeOrderKey = reflect.TypeOf(MinKey)
50+ typeDocElem = reflect.TypeOf(DocElem{})
51+ typeRawDocElem = reflect.TypeOf(RawDocElem{})
52+ typeRaw = reflect.TypeOf(Raw{})
53+ typeURL = reflect.TypeOf(url.URL{})
54+ typeTime = reflect.TypeOf(time.Time{})
55+ typeString = reflect.TypeOf("")
56+ typeJSONNumber = reflect.TypeOf(json.Number(""))
57+)
58+
59+const itoaCacheSize = 32
60+
61+var itoaCache []string
62+
63+func init() {
64+ itoaCache = make([]string, itoaCacheSize)
65+ for i := 0; i != itoaCacheSize; i++ {
66+ itoaCache[i] = strconv.Itoa(i)
67+ }
68+}
69+
70+func itoa(i int) string {
71+ if i < itoaCacheSize {
72+ return itoaCache[i]
73+ }
74+ return strconv.Itoa(i)
75+}
76+
77+// --------------------------------------------------------------------------
78+// Marshaling of the document value itself.
79+
80+type encoder struct {
81+ out []byte
82+}
83+
84+func (e *encoder) addDoc(v reflect.Value) {
85+ for {
86+ if vi, ok := v.Interface().(Getter); ok {
87+ getv, err := vi.GetBSON()
88+ if err != nil {
89+ panic(err)
90+ }
91+ v = reflect.ValueOf(getv)
92+ continue
93+ }
94+ if v.Kind() == reflect.Ptr {
95+ v = v.Elem()
96+ continue
97+ }
98+ break
99+ }
100+
101+ if v.Type() == typeRaw {
102+ raw := v.Interface().(Raw)
103+ if raw.Kind != 0x03 && raw.Kind != 0x00 {
104+ panic("Attempted to unmarshal Raw kind " + strconv.Itoa(int(raw.Kind)) + " as a document")
105+ }
106+ e.addBytes(raw.Data...)
107+ return
108+ }
109+
110+ start := e.reserveInt32()
111+
112+ switch v.Kind() {
113+ case reflect.Map:
114+ e.addMap(v)
115+ case reflect.Struct:
116+ e.addStruct(v)
117+ case reflect.Array, reflect.Slice:
118+ e.addSlice(v)
119+ default:
120+ panic("Can't marshal " + v.Type().String() + " as a BSON document")
121+ }
122+
123+ e.addBytes(0)
124+ e.setInt32(start, int32(len(e.out)-start))
125+}
126+
127+func (e *encoder) addMap(v reflect.Value) {
128+ for _, k := range v.MapKeys() {
129+ e.addElem(k.String(), v.MapIndex(k), false)
130+ }
131+}
132+
133+func (e *encoder) addStruct(v reflect.Value) {
134+ sinfo, err := getStructInfo(v.Type())
135+ if err != nil {
136+ panic(err)
137+ }
138+ var value reflect.Value
139+ if sinfo.InlineMap >= 0 {
140+ m := v.Field(sinfo.InlineMap)
141+ if m.Len() > 0 {
142+ for _, k := range m.MapKeys() {
143+ ks := k.String()
144+ if _, found := sinfo.FieldsMap[ks]; found {
145+ panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", ks))
146+ }
147+ e.addElem(ks, m.MapIndex(k), false)
148+ }
149+ }
150+ }
151+ for _, info := range sinfo.FieldsList {
152+ if info.Inline == nil {
153+ value = v.Field(info.Num)
154+ } else {
155+ value = v.FieldByIndex(info.Inline)
156+ }
157+ if info.OmitEmpty && isZero(value) {
158+ continue
159+ }
160+ e.addElem(info.Key, value, info.MinSize)
161+ }
162+}
163+
164+func isZero(v reflect.Value) bool {
165+ switch v.Kind() {
166+ case reflect.String:
167+ return len(v.String()) == 0
168+ case reflect.Ptr, reflect.Interface:
169+ return v.IsNil()
170+ case reflect.Slice:
171+ return v.Len() == 0
172+ case reflect.Map:
173+ return v.Len() == 0
174+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
175+ return v.Int() == 0
176+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
177+ return v.Uint() == 0
178+ case reflect.Float32, reflect.Float64:
179+ return v.Float() == 0
180+ case reflect.Bool:
181+ return !v.Bool()
182+ case reflect.Struct:
183+ vt := v.Type()
184+ if vt == typeTime {
185+ return v.Interface().(time.Time).IsZero()
186+ }
187+ for i := 0; i < v.NumField(); i++ {
188+ if vt.Field(i).PkgPath != "" {
189+ continue // Private field
190+ }
191+ if !isZero(v.Field(i)) {
192+ return false
193+ }
194+ }
195+ return true
196+ }
197+ return false
198+}
199+
200+func (e *encoder) addSlice(v reflect.Value) {
201+ vi := v.Interface()
202+ if d, ok := vi.(D); ok {
203+ for _, elem := range d {
204+ e.addElem(elem.Name, reflect.ValueOf(elem.Value), false)
205+ }
206+ return
207+ }
208+ if d, ok := vi.(RawD); ok {
209+ for _, elem := range d {
210+ e.addElem(elem.Name, reflect.ValueOf(elem.Value), false)
211+ }
212+ return
213+ }
214+ l := v.Len()
215+ et := v.Type().Elem()
216+ if et == typeDocElem {
217+ for i := 0; i < l; i++ {
218+ elem := v.Index(i).Interface().(DocElem)
219+ e.addElem(elem.Name, reflect.ValueOf(elem.Value), false)
220+ }
221+ return
222+ }
223+ if et == typeRawDocElem {
224+ for i := 0; i < l; i++ {
225+ elem := v.Index(i).Interface().(RawDocElem)
226+ e.addElem(elem.Name, reflect.ValueOf(elem.Value), false)
227+ }
228+ return
229+ }
230+ for i := 0; i < l; i++ {
231+ e.addElem(itoa(i), v.Index(i), false)
232+ }
233+}
234+
235+// --------------------------------------------------------------------------
236+// Marshaling of elements in a document.
237+
238+func (e *encoder) addElemName(kind byte, name string) {
239+ e.addBytes(kind)
240+ e.addBytes([]byte(name)...)
241+ e.addBytes(0)
242+}
243+
244+func (e *encoder) addElem(name string, v reflect.Value, minSize bool) {
245+
246+ if !v.IsValid() {
247+ e.addElemName('\x0A', name)
248+ return
249+ }
250+
251+ if getter, ok := v.Interface().(Getter); ok {
252+ getv, err := getter.GetBSON()
253+ if err != nil {
254+ panic(err)
255+ }
256+ e.addElem(name, reflect.ValueOf(getv), minSize)
257+ return
258+ }
259+
260+ switch v.Kind() {
261+
262+ case reflect.Interface:
263+ e.addElem(name, v.Elem(), minSize)
264+
265+ case reflect.Ptr:
266+ e.addElem(name, v.Elem(), minSize)
267+
268+ case reflect.String:
269+ s := v.String()
270+ switch v.Type() {
271+ case typeObjectId:
272+ if len(s) != 12 {
273+ panic("ObjectIDs must be exactly 12 bytes long (got " +
274+ strconv.Itoa(len(s)) + ")")
275+ }
276+ e.addElemName('\x07', name)
277+ e.addBytes([]byte(s)...)
278+ case typeSymbol:
279+ e.addElemName('\x0E', name)
280+ e.addStr(s)
281+ case typeJSONNumber:
282+ n := v.Interface().(json.Number)
283+ if i, err := n.Int64(); err == nil {
284+ e.addElemName('\x12', name)
285+ e.addInt64(i)
286+ } else if f, err := n.Float64(); err == nil {
287+ e.addElemName('\x01', name)
288+ e.addFloat64(f)
289+ } else {
290+ panic("failed to convert json.Number to a number: " + s)
291+ }
292+ default:
293+ e.addElemName('\x02', name)
294+ e.addStr(s)
295+ }
296+
297+ case reflect.Float32, reflect.Float64:
298+ e.addElemName('\x01', name)
299+ e.addFloat64(v.Float())
300+
301+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
302+ u := v.Uint()
303+ if int64(u) < 0 {
304+ panic("BSON has no uint64 type, and value is too large to fit correctly in an int64")
305+ } else if u <= math.MaxInt32 && (minSize || v.Kind() <= reflect.Uint32) {
306+ e.addElemName('\x10', name)
307+ e.addInt32(int32(u))
308+ } else {
309+ e.addElemName('\x12', name)
310+ e.addInt64(int64(u))
311+ }
312+
313+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
314+ switch v.Type() {
315+ case typeMongoTimestamp:
316+ e.addElemName('\x11', name)
317+ e.addInt64(v.Int())
318+
319+ case typeOrderKey:
320+ if v.Int() == int64(MaxKey) {
321+ e.addElemName('\x7F', name)
322+ } else {
323+ e.addElemName('\xFF', name)
324+ }
325+
326+ default:
327+ i := v.Int()
328+ if (minSize || v.Type().Kind() != reflect.Int64) && i >= math.MinInt32 && i <= math.MaxInt32 {
329+ // It fits into an int32, encode as such.
330+ e.addElemName('\x10', name)
331+ e.addInt32(int32(i))
332+ } else {
333+ e.addElemName('\x12', name)
334+ e.addInt64(i)
335+ }
336+ }
337+
338+ case reflect.Bool:
339+ e.addElemName('\x08', name)
340+ if v.Bool() {
341+ e.addBytes(1)
342+ } else {
343+ e.addBytes(0)
344+ }
345+
346+ case reflect.Map:
347+ e.addElemName('\x03', name)
348+ e.addDoc(v)
349+
350+ case reflect.Slice:
351+ vt := v.Type()
352+ et := vt.Elem()
353+ if et.Kind() == reflect.Uint8 {
354+ e.addElemName('\x05', name)
355+ e.addBinary('\x00', v.Bytes())
356+ } else if et == typeDocElem || et == typeRawDocElem {
357+ e.addElemName('\x03', name)
358+ e.addDoc(v)
359+ } else {
360+ e.addElemName('\x04', name)
361+ e.addDoc(v)
362+ }
363+
364+ case reflect.Array:
365+ et := v.Type().Elem()
366+ if et.Kind() == reflect.Uint8 {
367+ e.addElemName('\x05', name)
368+ if v.CanAddr() {
369+ e.addBinary('\x00', v.Slice(0, v.Len()).Interface().([]byte))
370+ } else {
371+ n := v.Len()
372+ e.addInt32(int32(n))
373+ e.addBytes('\x00')
374+ for i := 0; i < n; i++ {
375+ el := v.Index(i)
376+ e.addBytes(byte(el.Uint()))
377+ }
378+ }
379+ } else {
380+ e.addElemName('\x04', name)
381+ e.addDoc(v)
382+ }
383+
384+ case reflect.Struct:
385+ switch s := v.Interface().(type) {
386+
387+ case Raw:
388+ kind := s.Kind
389+ if kind == 0x00 {
390+ kind = 0x03
391+ }
392+ e.addElemName(kind, name)
393+ e.addBytes(s.Data...)
394+
395+ case Binary:
396+ e.addElemName('\x05', name)
397+ e.addBinary(s.Kind, s.Data)
398+
399+ case DBPointer:
400+ e.addElemName('\x0C', name)
401+ e.addStr(s.Namespace)
402+ if len(s.Id) != 12 {
403+ panic("ObjectIDs must be exactly 12 bytes long (got " +
404+ strconv.Itoa(len(s.Id)) + ")")
405+ }
406+ e.addBytes([]byte(s.Id)...)
407+
408+ case RegEx:
409+ e.addElemName('\x0B', name)
410+ e.addCStr(s.Pattern)
411+ e.addCStr(s.Options)
412+
413+ case JavaScript:
414+ if s.Scope == nil {
415+ e.addElemName('\x0D', name)
416+ e.addStr(s.Code)
417+ } else {
418+ e.addElemName('\x0F', name)
419+ start := e.reserveInt32()
420+ e.addStr(s.Code)
421+ e.addDoc(reflect.ValueOf(s.Scope))
422+ e.setInt32(start, int32(len(e.out)-start))
423+ }
424+
425+ case time.Time:
426+ // MongoDB handles timestamps as milliseconds.
427+ e.addElemName('\x09', name)
428+ e.addInt64(s.Unix()*1000 + int64(s.Nanosecond()/1e6))
429+
430+ case url.URL:
431+ e.addElemName('\x02', name)
432+ e.addStr(s.String())
433+
434+ case undefined:
435+ e.addElemName('\x06', name)
436+
437+ default:
438+ e.addElemName('\x03', name)
439+ e.addDoc(v)
440+ }
441+
442+ default:
443+ panic("Can't marshal " + v.Type().String() + " in a BSON document")
444+ }
445+}
446+
447+// --------------------------------------------------------------------------
448+// Marshaling of base types.
449+
450+func (e *encoder) addBinary(subtype byte, v []byte) {
451+ if subtype == 0x02 {
452+ // Wonder how that brilliant idea came to life. Obsolete, luckily.
453+ e.addInt32(int32(len(v) + 4))
454+ e.addBytes(subtype)
455+ e.addInt32(int32(len(v)))
456+ } else {
457+ e.addInt32(int32(len(v)))
458+ e.addBytes(subtype)
459+ }
460+ e.addBytes(v...)
461+}
462+
463+func (e *encoder) addStr(v string) {
464+ e.addInt32(int32(len(v) + 1))
465+ e.addCStr(v)
466+}
467+
468+func (e *encoder) addCStr(v string) {
469+ e.addBytes([]byte(v)...)
470+ e.addBytes(0)
471+}
472+
473+func (e *encoder) reserveInt32() (pos int) {
474+ pos = len(e.out)
475+ e.addBytes(0, 0, 0, 0)
476+ return pos
477+}
478+
479+func (e *encoder) setInt32(pos int, v int32) {
480+ e.out[pos+0] = byte(v)
481+ e.out[pos+1] = byte(v >> 8)
482+ e.out[pos+2] = byte(v >> 16)
483+ e.out[pos+3] = byte(v >> 24)
484+}
485+
486+func (e *encoder) addInt32(v int32) {
487+ u := uint32(v)
488+ e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24))
489+}
490+
491+func (e *encoder) addInt64(v int64) {
492+ u := uint64(v)
493+ e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24),
494+ byte(u>>32), byte(u>>40), byte(u>>48), byte(u>>56))
495+}
496+
497+func (e *encoder) addFloat64(v float64) {
498+ e.addInt64(int64(math.Float64bits(v)))
499+}
500+
501+func (e *encoder) addBytes(v ...byte) {
502+ e.out = append(e.out, v...)
503+}
vendor/gopkg.in/mgo.v2/bulk.go+71−0
@@ -0,0 +1,71 @@
1+package mgo
2+
3+// Bulk represents an operation that can be prepared with several
4+// orthogonal changes before being delivered to the server.
5+//
6+// WARNING: This API is still experimental.
7+//
8+// Relevant documentation:
9+//
10+// http://blog.mongodb.org/post/84922794768/mongodbs-new-bulk-api
11+//
12+type Bulk struct {
13+ c *Collection
14+ ordered bool
15+ inserts []interface{}
16+}
17+
18+// BulkError holds an error returned from running a Bulk operation.
19+//
20+// TODO: This is private for the moment, until we understand exactly how
21+// to report these multi-errors in a useful and convenient way.
22+type bulkError struct {
23+ err error
24+}
25+
26+// BulkResult holds the results for a bulk operation.
27+type BulkResult struct {
28+ // Be conservative while we understand exactly how to report these
29+ // results in a useful and convenient way, and also how to emulate
30+ // them with prior servers.
31+ private bool
32+}
33+
34+func (e *bulkError) Error() string {
35+ return e.err.Error()
36+}
37+
38+// Bulk returns a value to prepare the execution of a bulk operation.
39+//
40+// WARNING: This API is still experimental.
41+//
42+func (c *Collection) Bulk() *Bulk {
43+ return &Bulk{c: c, ordered: true}
44+}
45+
46+// Unordered puts the bulk operation in unordered mode.
47+//
48+// In unordered mode the indvidual operations may be sent
49+// out of order, which means latter operations may proceed
50+// even if prior ones have failed.
51+func (b *Bulk) Unordered() {
52+ b.ordered = false
53+}
54+
55+// Insert queues up the provided documents for insertion.
56+func (b *Bulk) Insert(docs ...interface{}) {
57+ b.inserts = append(b.inserts, docs...)
58+}
59+
60+// Run runs all the operations queued up.
61+func (b *Bulk) Run() (*BulkResult, error) {
62+ op := &insertOp{b.c.FullName, b.inserts, 0}
63+ if !b.ordered {
64+ op.flags = 1 // ContinueOnError
65+ }
66+ _, err := b.c.writeQuery(op)
67+ if err != nil {
68+ return nil, &bulkError{err}
69+ }
70+ return &BulkResult{}, nil
71+}
vendor/gopkg.in/mgo.v2/bulk_test.go+131−0
@@ -0,0 +1,131 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2014 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo_test
28+
29+import (
30+ . "gopkg.in/check.v1"
31+ "gopkg.in/mgo.v2"
32+)
33+
34+func (s *S) TestBulkInsert(c *C) {
35+ session, err := mgo.Dial("localhost:40001")
36+ c.Assert(err, IsNil)
37+ defer session.Close()
38+
39+ coll := session.DB("mydb").C("mycoll")
40+ bulk := coll.Bulk()
41+ bulk.Insert(M{"n": 1})
42+ bulk.Insert(M{"n": 2}, M{"n": 3})
43+ r, err := bulk.Run()
44+ c.Assert(err, IsNil)
45+ c.Assert(r, FitsTypeOf, &mgo.BulkResult{})
46+
47+ type doc struct{ N int }
48+ var res []doc
49+ err = coll.Find(nil).Sort("n").All(&res)
50+ c.Assert(err, IsNil)
51+ c.Assert(res, DeepEquals, []doc{{1}, {2}, {3}})
52+}
53+
54+func (s *S) TestBulkInsertError(c *C) {
55+ session, err := mgo.Dial("localhost:40001")
56+ c.Assert(err, IsNil)
57+ defer session.Close()
58+
59+ coll := session.DB("mydb").C("mycoll")
60+ bulk := coll.Bulk()
61+ bulk.Insert(M{"_id": 1}, M{"_id": 2}, M{"_id": 2}, M{"_id": 3})
62+ _, err = bulk.Run()
63+ c.Assert(err, ErrorMatches, ".*duplicate key.*")
64+
65+ type doc struct {
66+ N int `_id`
67+ }
68+ var res []doc
69+ err = coll.Find(nil).Sort("_id").All(&res)
70+ c.Assert(err, IsNil)
71+ c.Assert(res, DeepEquals, []doc{{1}, {2}})
72+}
73+
74+func (s *S) TestBulkInsertErrorUnordered(c *C) {
75+ session, err := mgo.Dial("localhost:40001")
76+ c.Assert(err, IsNil)
77+ defer session.Close()
78+
79+ coll := session.DB("mydb").C("mycoll")
80+ bulk := coll.Bulk()
81+ bulk.Unordered()
82+ bulk.Insert(M{"_id": 1}, M{"_id": 2}, M{"_id": 2}, M{"_id": 3})
83+ _, err = bulk.Run()
84+ c.Assert(err, ErrorMatches, ".*duplicate key.*")
85+
86+ type doc struct {
87+ N int `_id`
88+ }
89+ var res []doc
90+ err = coll.Find(nil).Sort("_id").All(&res)
91+ c.Assert(err, IsNil)
92+ c.Assert(res, DeepEquals, []doc{{1}, {2}, {3}})
93+}
94+
95+func (s *S) TestBulkInsertErrorUnorderedSplitBatch(c *C) {
96+ // The server has a batch limit of 1000 documents when using write commands.
97+ // This artificial limit did not exist with the old wire protocol, so to
98+ // avoid compatibility issues the implementation internally split batches
99+ // into the proper size and delivers them one by one. This test ensures that
100+ // the behavior of unordered (that is, continue on error) remains correct
101+ // when errors happen and there are batches left.
102+ session, err := mgo.Dial("localhost:40001")
103+ c.Assert(err, IsNil)
104+ defer session.Close()
105+
106+ coll := session.DB("mydb").C("mycoll")
107+ bulk := coll.Bulk()
108+ bulk.Unordered()
109+
110+ const total = 4096
111+ type doc struct {
112+ Id int `_id`
113+ }
114+ docs := make([]interface{}, total)
115+ for i := 0; i < total; i++ {
116+ docs[i] = doc{i}
117+ }
118+ docs[1] = doc{0}
119+ bulk.Insert(docs...)
120+ _, err = bulk.Run()
121+ c.Assert(err, ErrorMatches, ".*duplicate key.*")
122+
123+ n, err := coll.Count()
124+ c.Assert(err, IsNil)
125+ c.Assert(n, Equals, total-1)
126+
127+ var res doc
128+ err = coll.FindId(1500).One(&res)
129+ c.Assert(err, IsNil)
130+ c.Assert(res.Id, Equals, 1500)
131+}
vendor/gopkg.in/mgo.v2/cluster.go+632−0
@@ -0,0 +1,632 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo
28+
29+import (
30+ "errors"
31+ "fmt"
32+ "net"
33+ "sync"
34+ "time"
35+
36+ "gopkg.in/mgo.v2/bson"
37+)
38+
39+// ---------------------------------------------------------------------------
40+// Mongo cluster encapsulation.
41+//
42+// A cluster enables the communication with one or more servers participating
43+// in a mongo cluster. This works with individual servers, a replica set,
44+// a replica pair, one or multiple mongos routers, etc.
45+
46+type mongoCluster struct {
47+ sync.RWMutex
48+ serverSynced sync.Cond
49+ userSeeds []string
50+ dynaSeeds []string
51+ servers mongoServers
52+ masters mongoServers
53+ references int
54+ syncing bool
55+ direct bool
56+ failFast bool
57+ syncCount uint
58+ setName string
59+ cachedIndex map[string]bool
60+ sync chan bool
61+ dial dialer
62+}
63+
64+func newCluster(userSeeds []string, direct, failFast bool, dial dialer, setName string) *mongoCluster {
65+ cluster := &mongoCluster{
66+ userSeeds: userSeeds,
67+ references: 1,
68+ direct: direct,
69+ failFast: failFast,
70+ dial: dial,
71+ setName: setName,
72+ }
73+ cluster.serverSynced.L = cluster.RWMutex.RLocker()
74+ cluster.sync = make(chan bool, 1)
75+ stats.cluster(+1)
76+ go cluster.syncServersLoop()
77+ return cluster
78+}
79+
80+// Acquire increases the reference count for the cluster.
81+func (cluster *mongoCluster) Acquire() {
82+ cluster.Lock()
83+ cluster.references++
84+ debugf("Cluster %p acquired (refs=%d)", cluster, cluster.references)
85+ cluster.Unlock()
86+}
87+
88+// Release decreases the reference count for the cluster. Once
89+// it reaches zero, all servers will be closed.
90+func (cluster *mongoCluster) Release() {
91+ cluster.Lock()
92+ if cluster.references == 0 {
93+ panic("cluster.Release() with references == 0")
94+ }
95+ cluster.references--
96+ debugf("Cluster %p released (refs=%d)", cluster, cluster.references)
97+ if cluster.references == 0 {
98+ for _, server := range cluster.servers.Slice() {
99+ server.Close()
100+ }
101+ // Wake up the sync loop so it can die.
102+ cluster.syncServers()
103+ stats.cluster(-1)
104+ }
105+ cluster.Unlock()
106+}
107+
108+func (cluster *mongoCluster) LiveServers() (servers []string) {
109+ cluster.RLock()
110+ for _, serv := range cluster.servers.Slice() {
111+ servers = append(servers, serv.Addr)
112+ }
113+ cluster.RUnlock()
114+ return servers
115+}
116+
117+func (cluster *mongoCluster) removeServer(server *mongoServer) {
118+ cluster.Lock()
119+ cluster.masters.Remove(server)
120+ other := cluster.servers.Remove(server)
121+ cluster.Unlock()
122+ if other != nil {
123+ other.Close()
124+ log("Removed server ", server.Addr, " from cluster.")
125+ }
126+ server.Close()
127+}
128+
129+type isMasterResult struct {
130+ IsMaster bool
131+ Secondary bool
132+ Primary string
133+ Hosts []string
134+ Passives []string
135+ Tags bson.D
136+ Msg string
137+ SetName string `bson:"setName"`
138+ MaxWireVersion int `bson:"maxWireVersion"`
139+}
140+
141+func (cluster *mongoCluster) isMaster(socket *mongoSocket, result *isMasterResult) error {
142+ // Monotonic let's it talk to a slave and still hold the socket.
143+ session := newSession(Monotonic, cluster, 10*time.Second)
144+ session.setSocket(socket)
145+ err := session.Run("ismaster", result)
146+ session.Close()
147+ return err
148+}
149+
150+type possibleTimeout interface {
151+ Timeout() bool
152+}
153+
154+var syncSocketTimeout = 5 * time.Second
155+
156+func (cluster *mongoCluster) syncServer(server *mongoServer) (info *mongoServerInfo, hosts []string, err error) {
157+ var syncTimeout time.Duration
158+ if raceDetector {
159+ // This variable is only ever touched by tests.
160+ globalMutex.Lock()
161+ syncTimeout = syncSocketTimeout
162+ globalMutex.Unlock()
163+ } else {
164+ syncTimeout = syncSocketTimeout
165+ }
166+
167+ addr := server.Addr
168+ log("SYNC Processing ", addr, "...")
169+
170+ // Retry a few times to avoid knocking a server down for a hiccup.
171+ var result isMasterResult
172+ var tryerr error
173+ for retry := 0; ; retry++ {
174+ if retry == 3 || retry == 1 && cluster.failFast {
175+ return nil, nil, tryerr
176+ }
177+ if retry > 0 {
178+ // Don't abuse the server needlessly if there's something actually wrong.
179+ if err, ok := tryerr.(possibleTimeout); ok && err.Timeout() {
180+ // Give a chance for waiters to timeout as well.
181+ cluster.serverSynced.Broadcast()
182+ }
183+ time.Sleep(syncShortDelay)
184+ }
185+
186+ // It's not clear what would be a good timeout here. Is it
187+ // better to wait longer or to retry?
188+ socket, _, err := server.AcquireSocket(0, syncTimeout)
189+ if err != nil {
190+ tryerr = err
191+ logf("SYNC Failed to get socket to %s: %v", addr, err)
192+ continue
193+ }
194+ err = cluster.isMaster(socket, &result)
195+ socket.Release()
196+ if err != nil {
197+ tryerr = err
198+ logf("SYNC Command 'ismaster' to %s failed: %v", addr, err)
199+ continue
200+ }
201+ debugf("SYNC Result of 'ismaster' from %s: %#v", addr, result)
202+ break
203+ }
204+
205+ if cluster.setName != "" && result.SetName != cluster.setName {
206+ logf("SYNC Server %s is not a member of replica set %q", addr, cluster.setName)
207+ return nil, nil, fmt.Errorf("server %s is not a member of replica set %q", addr, cluster.setName)
208+ }
209+
210+ if result.IsMaster {
211+ debugf("SYNC %s is a master.", addr)
212+ if !server.info.Master {
213+ // Made an incorrect assumption above, so fix stats.
214+ stats.conn(-1, false)
215+ stats.conn(+1, true)
216+ }
217+ } else if result.Secondary {
218+ debugf("SYNC %s is a slave.", addr)
219+ } else if cluster.direct {
220+ logf("SYNC %s in unknown state. Pretending it's a slave due to direct connection.", addr)
221+ } else {
222+ logf("SYNC %s is neither a master nor a slave.", addr)
223+ // Let stats track it as whatever was known before.
224+ return nil, nil, errors.New(addr + " is not a master nor slave")
225+ }
226+
227+ info = &mongoServerInfo{
228+ Master: result.IsMaster,
229+ Mongos: result.Msg == "isdbgrid",
230+ Tags: result.Tags,
231+ SetName: result.SetName,
232+ MaxWireVersion: result.MaxWireVersion,
233+ }
234+
235+ hosts = make([]string, 0, 1+len(result.Hosts)+len(result.Passives))
236+ if result.Primary != "" {
237+ // First in the list to speed up master discovery.
238+ hosts = append(hosts, result.Primary)
239+ }
240+ hosts = append(hosts, result.Hosts...)
241+ hosts = append(hosts, result.Passives...)
242+
243+ debugf("SYNC %s knows about the following peers: %#v", addr, hosts)
244+ return info, hosts, nil
245+}
246+
247+type syncKind bool
248+
249+const (
250+ completeSync syncKind = true
251+ partialSync syncKind = false
252+)
253+
254+func (cluster *mongoCluster) addServer(server *mongoServer, info *mongoServerInfo, syncKind syncKind) {
255+ cluster.Lock()
256+ current := cluster.servers.Search(server.ResolvedAddr)
257+ if current == nil {
258+ if syncKind == partialSync {
259+ cluster.Unlock()
260+ server.Close()
261+ log("SYNC Discarding unknown server ", server.Addr, " due to partial sync.")
262+ return
263+ }
264+ cluster.servers.Add(server)
265+ if info.Master {
266+ cluster.masters.Add(server)
267+ log("SYNC Adding ", server.Addr, " to cluster as a master.")
268+ } else {
269+ log("SYNC Adding ", server.Addr, " to cluster as a slave.")
270+ }
271+ } else {
272+ if server != current {
273+ panic("addServer attempting to add duplicated server")
274+ }
275+ if server.Info().Master != info.Master {
276+ if info.Master {
277+ log("SYNC Server ", server.Addr, " is now a master.")
278+ cluster.masters.Add(server)
279+ } else {
280+ log("SYNC Server ", server.Addr, " is now a slave.")
281+ cluster.masters.Remove(server)
282+ }
283+ }
284+ }
285+ server.SetInfo(info)
286+ debugf("SYNC Broadcasting availability of server %s", server.Addr)
287+ cluster.serverSynced.Broadcast()
288+ cluster.Unlock()
289+}
290+
291+func (cluster *mongoCluster) getKnownAddrs() []string {
292+ cluster.RLock()
293+ max := len(cluster.userSeeds) + len(cluster.dynaSeeds) + cluster.servers.Len()
294+ seen := make(map[string]bool, max)
295+ known := make([]string, 0, max)
296+
297+ add := func(addr string) {
298+ if _, found := seen[addr]; !found {
299+ seen[addr] = true
300+ known = append(known, addr)
301+ }
302+ }
303+
304+ for _, addr := range cluster.userSeeds {
305+ add(addr)
306+ }
307+ for _, addr := range cluster.dynaSeeds {
308+ add(addr)
309+ }
310+ for _, serv := range cluster.servers.Slice() {
311+ add(serv.Addr)
312+ }
313+ cluster.RUnlock()
314+
315+ return known
316+}
317+
318+// syncServers injects a value into the cluster.sync channel to force
319+// an iteration of the syncServersLoop function.
320+func (cluster *mongoCluster) syncServers() {
321+ select {
322+ case cluster.sync <- true:
323+ default:
324+ }
325+}
326+
327+// How long to wait for a checkup of the cluster topology if nothing
328+// else kicks a synchronization before that.
329+const syncServersDelay = 30 * time.Second
330+const syncShortDelay = 500 * time.Millisecond
331+
332+// syncServersLoop loops while the cluster is alive to keep its idea of
333+// the server topology up-to-date. It must be called just once from
334+// newCluster. The loop iterates once syncServersDelay has passed, or
335+// if somebody injects a value into the cluster.sync channel to force a
336+// synchronization. A loop iteration will contact all servers in
337+// parallel, ask them about known peers and their own role within the
338+// cluster, and then attempt to do the same with all the peers
339+// retrieved.
340+func (cluster *mongoCluster) syncServersLoop() {
341+ for {
342+ debugf("SYNC Cluster %p is starting a sync loop iteration.", cluster)
343+
344+ cluster.Lock()
345+ if cluster.references == 0 {
346+ cluster.Unlock()
347+ break
348+ }
349+ cluster.references++ // Keep alive while syncing.
350+ direct := cluster.direct
351+ cluster.Unlock()
352+
353+ cluster.syncServersIteration(direct)
354+
355+ // We just synchronized, so consume any outstanding requests.
356+ select {
357+ case <-cluster.sync:
358+ default:
359+ }
360+
361+ cluster.Release()
362+
363+ // Hold off before allowing another sync. No point in
364+ // burning CPU looking for down servers.
365+ if !cluster.failFast {
366+ time.Sleep(syncShortDelay)
367+ }
368+
369+ cluster.Lock()
370+ if cluster.references == 0 {
371+ cluster.Unlock()
372+ break
373+ }
374+ cluster.syncCount++
375+ // Poke all waiters so they have a chance to timeout or
376+ // restart syncing if they wish to.
377+ cluster.serverSynced.Broadcast()
378+ // Check if we have to restart immediately either way.
379+ restart := !direct && cluster.masters.Empty() || cluster.servers.Empty()
380+ cluster.Unlock()
381+
382+ if restart {
383+ log("SYNC No masters found. Will synchronize again.")
384+ time.Sleep(syncShortDelay)
385+ continue
386+ }
387+
388+ debugf("SYNC Cluster %p waiting for next requested or scheduled sync.", cluster)
389+
390+ // Hold off until somebody explicitly requests a synchronization
391+ // or it's time to check for a cluster topology change again.
392+ select {
393+ case <-cluster.sync:
394+ case <-time.After(syncServersDelay):
395+ }
396+ }
397+ debugf("SYNC Cluster %p is stopping its sync loop.", cluster)
398+}
399+
400+func (cluster *mongoCluster) server(addr string, tcpaddr *net.TCPAddr) *mongoServer {
401+ cluster.RLock()
402+ server := cluster.servers.Search(tcpaddr.String())
403+ cluster.RUnlock()
404+ if server != nil {
405+ return server
406+ }
407+ return newServer(addr, tcpaddr, cluster.sync, cluster.dial)
408+}
409+
410+func resolveAddr(addr string) (*net.TCPAddr, error) {
411+ // This hack allows having a timeout on resolution.
412+ conn, err := net.DialTimeout("udp4", addr, 10*time.Second)
413+ if err != nil {
414+ log("SYNC Failed to resolve server address: ", addr)
415+ return nil, errors.New("failed to resolve server address: " + addr)
416+ }
417+ tcpaddr := (*net.TCPAddr)(conn.RemoteAddr().(*net.UDPAddr))
418+ conn.Close()
419+ if tcpaddr.String() != addr {
420+ debug("SYNC Address ", addr, " resolved as ", tcpaddr.String())
421+ }
422+ return tcpaddr, nil
423+}
424+
425+type pendingAdd struct {
426+ server *mongoServer
427+ info *mongoServerInfo
428+}
429+
430+func (cluster *mongoCluster) syncServersIteration(direct bool) {
431+ log("SYNC Starting full topology synchronization...")
432+
433+ var wg sync.WaitGroup
434+ var m sync.Mutex
435+ notYetAdded := make(map[string]pendingAdd)
436+ addIfFound := make(map[string]bool)
437+ seen := make(map[string]bool)
438+ syncKind := partialSync
439+
440+ var spawnSync func(addr string, byMaster bool)
441+ spawnSync = func(addr string, byMaster bool) {
442+ wg.Add(1)
443+ go func() {
444+ defer wg.Done()
445+
446+ tcpaddr, err := resolveAddr(addr)
447+ if err != nil {
448+ log("SYNC Failed to start sync of ", addr, ": ", err.Error())
449+ return
450+ }
451+ resolvedAddr := tcpaddr.String()
452+
453+ m.Lock()
454+ if byMaster {
455+ if pending, ok := notYetAdded[resolvedAddr]; ok {
456+ delete(notYetAdded, resolvedAddr)
457+ m.Unlock()
458+ cluster.addServer(pending.server, pending.info, completeSync)
459+ return
460+ }
461+ addIfFound[resolvedAddr] = true
462+ }
463+ if seen[resolvedAddr] {
464+ m.Unlock()
465+ return
466+ }
467+ seen[resolvedAddr] = true
468+ m.Unlock()
469+
470+ server := cluster.server(addr, tcpaddr)
471+ info, hosts, err := cluster.syncServer(server)
472+ if err != nil {
473+ cluster.removeServer(server)
474+ return
475+ }
476+
477+ m.Lock()
478+ add := direct || info.Master || addIfFound[resolvedAddr]
479+ if add {
480+ syncKind = completeSync
481+ } else {
482+ notYetAdded[resolvedAddr] = pendingAdd{server, info}
483+ }
484+ m.Unlock()
485+ if add {
486+ cluster.addServer(server, info, completeSync)
487+ }
488+ if !direct {
489+ for _, addr := range hosts {
490+ spawnSync(addr, info.Master)
491+ }
492+ }
493+ }()
494+ }
495+
496+ knownAddrs := cluster.getKnownAddrs()
497+ for _, addr := range knownAddrs {
498+ spawnSync(addr, false)
499+ }
500+ wg.Wait()
501+
502+ if syncKind == completeSync {
503+ logf("SYNC Synchronization was complete (got data from primary).")
504+ for _, pending := range notYetAdded {
505+ cluster.removeServer(pending.server)
506+ }
507+ } else {
508+ logf("SYNC Synchronization was partial (cannot talk to primary).")
509+ for _, pending := range notYetAdded {
510+ cluster.addServer(pending.server, pending.info, partialSync)
511+ }
512+ }
513+
514+ cluster.Lock()
515+ ml := cluster.masters.Len()
516+ logf("SYNC Synchronization completed: %d master(s) and %d slave(s) alive.", ml, cluster.servers.Len()-ml)
517+
518+ // Update dynamic seeds, but only if we have any good servers. Otherwise,
519+ // leave them alone for better chances of a successful sync in the future.
520+ if syncKind == completeSync {
521+ dynaSeeds := make([]string, cluster.servers.Len())
522+ for i, server := range cluster.servers.Slice() {
523+ dynaSeeds[i] = server.Addr
524+ }
525+ cluster.dynaSeeds = dynaSeeds
526+ debugf("SYNC New dynamic seeds: %#v\n", dynaSeeds)
527+ }
528+ cluster.Unlock()
529+}
530+
531+// AcquireSocket returns a socket to a server in the cluster. If slaveOk is
532+// true, it will attempt to return a socket to a slave server. If it is
533+// false, the socket will necessarily be to a master server.
534+func (cluster *mongoCluster) AcquireSocket(slaveOk bool, syncTimeout time.Duration, socketTimeout time.Duration, serverTags []bson.D, poolLimit int) (s *mongoSocket, err error) {
535+ var started time.Time
536+ var syncCount uint
537+ warnedLimit := false
538+ for {
539+ cluster.RLock()
540+ for {
541+ ml := cluster.masters.Len()
542+ sl := cluster.servers.Len()
543+ debugf("Cluster has %d known masters and %d known slaves.", ml, sl-ml)
544+ if ml > 0 || slaveOk && sl > 0 {
545+ break
546+ }
547+ if started.IsZero() {
548+ // Initialize after fast path above.
549+ started = time.Now()
550+ syncCount = cluster.syncCount
551+ } else if syncTimeout != 0 && started.Before(time.Now().Add(-syncTimeout)) || cluster.failFast && cluster.syncCount != syncCount {
552+ cluster.RUnlock()
553+ return nil, errors.New("no reachable servers")
554+ }
555+ log("Waiting for servers to synchronize...")
556+ cluster.syncServers()
557+
558+ // Remember: this will release and reacquire the lock.
559+ cluster.serverSynced.Wait()
560+ }
561+
562+ var server *mongoServer
563+ if slaveOk {
564+ server = cluster.servers.BestFit(serverTags)
565+ } else {
566+ server = cluster.masters.BestFit(nil)
567+ }
568+ cluster.RUnlock()
569+
570+ if server == nil {
571+ // Must have failed the requested tags. Sleep to avoid spinning.
572+ time.Sleep(1e8)
573+ continue
574+ }
575+
576+ s, abended, err := server.AcquireSocket(poolLimit, socketTimeout)
577+ if err == errPoolLimit {
578+ if !warnedLimit {
579+ warnedLimit = true
580+ log("WARNING: Per-server connection limit reached.")
581+ }
582+ time.Sleep(100 * time.Millisecond)
583+ continue
584+ }
585+ if err != nil {
586+ cluster.removeServer(server)
587+ cluster.syncServers()
588+ continue
589+ }
590+ if abended && !slaveOk {
591+ var result isMasterResult
592+ err := cluster.isMaster(s, &result)
593+ if err != nil || !result.IsMaster {
594+ logf("Cannot confirm server %s as master (%v)", server.Addr, err)
595+ s.Release()
596+ cluster.syncServers()
597+ time.Sleep(100 * time.Millisecond)
598+ continue
599+ }
600+ }
601+ return s, nil
602+ }
603+ panic("unreached")
604+}
605+
606+func (cluster *mongoCluster) CacheIndex(cacheKey string, exists bool) {
607+ cluster.Lock()
608+ if cluster.cachedIndex == nil {
609+ cluster.cachedIndex = make(map[string]bool)
610+ }
611+ if exists {
612+ cluster.cachedIndex[cacheKey] = true
613+ } else {
614+ delete(cluster.cachedIndex, cacheKey)
615+ }
616+ cluster.Unlock()
617+}
618+
619+func (cluster *mongoCluster) HasCachedIndex(cacheKey string) (result bool) {
620+ cluster.RLock()
621+ if cluster.cachedIndex != nil {
622+ result = cluster.cachedIndex[cacheKey]
623+ }
624+ cluster.RUnlock()
625+ return
626+}
627+
628+func (cluster *mongoCluster) ResetIndexCache() {
629+ cluster.Lock()
630+ cluster.cachedIndex = make(map[string]bool)
631+ cluster.Unlock()
632+}
vendor/gopkg.in/mgo.v2/cluster_test.go+1657−0
@@ -0,0 +1,1657 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo_test
28+
29+import (
30+ "fmt"
31+ "io"
32+ "net"
33+ "strings"
34+ "sync"
35+ "time"
36+
37+ . "gopkg.in/check.v1"
38+ "gopkg.in/mgo.v2"
39+ "gopkg.in/mgo.v2/bson"
40+)
41+
42+func (s *S) TestNewSession(c *C) {
43+ session, err := mgo.Dial("localhost:40001")
44+ c.Assert(err, IsNil)
45+ defer session.Close()
46+
47+ // Do a dummy operation to wait for connection.
48+ coll := session.DB("mydb").C("mycoll")
49+ err = coll.Insert(M{"_id": 1})
50+ c.Assert(err, IsNil)
51+
52+ // Tweak safety and query settings to ensure other has copied those.
53+ session.SetSafe(nil)
54+ session.SetBatch(-1)
55+ other := session.New()
56+ defer other.Close()
57+ session.SetSafe(&mgo.Safe{})
58+
59+ // Clone was copied while session was unsafe, so no errors.
60+ otherColl := other.DB("mydb").C("mycoll")
61+ err = otherColl.Insert(M{"_id": 1})
62+ c.Assert(err, IsNil)
63+
64+ // Original session was made safe again.
65+ err = coll.Insert(M{"_id": 1})
66+ c.Assert(err, NotNil)
67+
68+ // With New(), each session has its own socket now.
69+ stats := mgo.GetStats()
70+ c.Assert(stats.MasterConns, Equals, 2)
71+ c.Assert(stats.SocketsInUse, Equals, 2)
72+
73+ // Ensure query parameters were cloned.
74+ err = otherColl.Insert(M{"_id": 2})
75+ c.Assert(err, IsNil)
76+
77+ // Ping the database to ensure the nonce has been received already.
78+ c.Assert(other.Ping(), IsNil)
79+
80+ mgo.ResetStats()
81+
82+ iter := otherColl.Find(M{}).Iter()
83+ c.Assert(err, IsNil)
84+
85+ m := M{}
86+ ok := iter.Next(m)
87+ c.Assert(ok, Equals, true)
88+ err = iter.Close()
89+ c.Assert(err, IsNil)
90+
91+ // If Batch(-1) is in effect, a single document must have been received.
92+ stats = mgo.GetStats()
93+ c.Assert(stats.ReceivedDocs, Equals, 1)
94+}
95+
96+func (s *S) TestCloneSession(c *C) {
97+ session, err := mgo.Dial("localhost:40001")
98+ c.Assert(err, IsNil)
99+ defer session.Close()
100+
101+ // Do a dummy operation to wait for connection.
102+ coll := session.DB("mydb").C("mycoll")
103+ err = coll.Insert(M{"_id": 1})
104+ c.Assert(err, IsNil)
105+
106+ // Tweak safety and query settings to ensure clone is copying those.
107+ session.SetSafe(nil)
108+ session.SetBatch(-1)
109+ clone := session.Clone()
110+ defer clone.Close()
111+ session.SetSafe(&mgo.Safe{})
112+
113+ // Clone was copied while session was unsafe, so no errors.
114+ cloneColl := clone.DB("mydb").C("mycoll")
115+ err = cloneColl.Insert(M{"_id": 1})
116+ c.Assert(err, IsNil)
117+
118+ // Original session was made safe again.
119+ err = coll.Insert(M{"_id": 1})
120+ c.Assert(err, NotNil)
121+
122+ // With Clone(), same socket is shared between sessions now.
123+ stats := mgo.GetStats()
124+ c.Assert(stats.SocketsInUse, Equals, 1)
125+ c.Assert(stats.SocketRefs, Equals, 2)
126+
127+ // Refreshing one of them should let the original socket go,
128+ // while preserving the safety settings.
129+ clone.Refresh()
130+ err = cloneColl.Insert(M{"_id": 1})
131+ c.Assert(err, IsNil)
132+
133+ // Must have used another connection now.
134+ stats = mgo.GetStats()
135+ c.Assert(stats.SocketsInUse, Equals, 2)
136+ c.Assert(stats.SocketRefs, Equals, 2)
137+
138+ // Ensure query parameters were cloned.
139+ err = cloneColl.Insert(M{"_id": 2})
140+ c.Assert(err, IsNil)
141+
142+ // Ping the database to ensure the nonce has been received already.
143+ c.Assert(clone.Ping(), IsNil)
144+
145+ mgo.ResetStats()
146+
147+ iter := cloneColl.Find(M{}).Iter()
148+ c.Assert(err, IsNil)
149+
150+ m := M{}
151+ ok := iter.Next(m)
152+ c.Assert(ok, Equals, true)
153+ err = iter.Close()
154+ c.Assert(err, IsNil)
155+
156+ // If Batch(-1) is in effect, a single document must have been received.
157+ stats = mgo.GetStats()
158+ c.Assert(stats.ReceivedDocs, Equals, 1)
159+}
160+
161+func (s *S) TestSetModeStrong(c *C) {
162+ session, err := mgo.Dial("localhost:40012")
163+ c.Assert(err, IsNil)
164+ defer session.Close()
165+
166+ session.SetMode(mgo.Monotonic, false)
167+ session.SetMode(mgo.Strong, false)
168+
169+ c.Assert(session.Mode(), Equals, mgo.Strong)
170+
171+ result := M{}
172+ cmd := session.DB("admin").C("$cmd")
173+ err = cmd.Find(M{"ismaster": 1}).One(&result)
174+ c.Assert(err, IsNil)
175+ c.Assert(result["ismaster"], Equals, true)
176+
177+ coll := session.DB("mydb").C("mycoll")
178+ err = coll.Insert(M{"a": 1})
179+ c.Assert(err, IsNil)
180+
181+ // Wait since the sync also uses sockets.
182+ for len(session.LiveServers()) != 3 {
183+ c.Log("Waiting for cluster sync to finish...")
184+ time.Sleep(5e8)
185+ }
186+
187+ stats := mgo.GetStats()
188+ c.Assert(stats.MasterConns, Equals, 1)
189+ c.Assert(stats.SlaveConns, Equals, 2)
190+ c.Assert(stats.SocketsInUse, Equals, 1)
191+
192+ session.SetMode(mgo.Strong, true)
193+
194+ stats = mgo.GetStats()
195+ c.Assert(stats.SocketsInUse, Equals, 0)
196+}
197+
198+func (s *S) TestSetModeMonotonic(c *C) {
199+ // Must necessarily connect to a slave, otherwise the
200+ // master connection will be available first.
201+ session, err := mgo.Dial("localhost:40012")
202+ c.Assert(err, IsNil)
203+ defer session.Close()
204+
205+ session.SetMode(mgo.Monotonic, false)
206+
207+ c.Assert(session.Mode(), Equals, mgo.Monotonic)
208+
209+ result := M{}
210+ cmd := session.DB("admin").C("$cmd")
211+ err = cmd.Find(M{"ismaster": 1}).One(&result)
212+ c.Assert(err, IsNil)
213+ c.Assert(result["ismaster"], Equals, false)
214+
215+ coll := session.DB("mydb").C("mycoll")
216+ err = coll.Insert(M{"a": 1})
217+ c.Assert(err, IsNil)
218+
219+ result = M{}
220+ err = cmd.Find(M{"ismaster": 1}).One(&result)
221+ c.Assert(err, IsNil)
222+ c.Assert(result["ismaster"], Equals, true)
223+
224+ // Wait since the sync also uses sockets.
225+ for len(session.LiveServers()) != 3 {
226+ c.Log("Waiting for cluster sync to finish...")
227+ time.Sleep(5e8)
228+ }
229+
230+ stats := mgo.GetStats()
231+ c.Assert(stats.MasterConns, Equals, 1)
232+ c.Assert(stats.SlaveConns, Equals, 2)
233+ c.Assert(stats.SocketsInUse, Equals, 2)
234+
235+ session.SetMode(mgo.Monotonic, true)
236+
237+ stats = mgo.GetStats()
238+ c.Assert(stats.SocketsInUse, Equals, 0)
239+}
240+
241+func (s *S) TestSetModeMonotonicAfterStrong(c *C) {
242+ // Test that a strong session shifting to a monotonic
243+ // one preserves the socket untouched.
244+
245+ session, err := mgo.Dial("localhost:40012")
246+ c.Assert(err, IsNil)
247+ defer session.Close()
248+
249+ // Insert something to force a connection to the master.
250+ coll := session.DB("mydb").C("mycoll")
251+ err = coll.Insert(M{"a": 1})
252+ c.Assert(err, IsNil)
253+
254+ session.SetMode(mgo.Monotonic, false)
255+
256+ // Wait since the sync also uses sockets.
257+ for len(session.LiveServers()) != 3 {
258+ c.Log("Waiting for cluster sync to finish...")
259+ time.Sleep(5e8)
260+ }
261+
262+ // Master socket should still be reserved.
263+ stats := mgo.GetStats()
264+ c.Assert(stats.SocketsInUse, Equals, 1)
265+
266+ // Confirm it's the master even though it's Monotonic by now.
267+ result := M{}
268+ cmd := session.DB("admin").C("$cmd")
269+ err = cmd.Find(M{"ismaster": 1}).One(&result)
270+ c.Assert(err, IsNil)
271+ c.Assert(result["ismaster"], Equals, true)
272+}
273+
274+func (s *S) TestSetModeStrongAfterMonotonic(c *C) {
275+ // Test that shifting from Monotonic to Strong while
276+ // using a slave socket will keep the socket reserved
277+ // until the master socket is necessary, so that no
278+ // switch over occurs unless it's actually necessary.
279+
280+ // Must necessarily connect to a slave, otherwise the
281+ // master connection will be available first.
282+ session, err := mgo.Dial("localhost:40012")
283+ c.Assert(err, IsNil)
284+ defer session.Close()
285+
286+ session.SetMode(mgo.Monotonic, false)
287+
288+ // Ensure we're talking to a slave, and reserve the socket.
289+ result := M{}
290+ err = session.Run("ismaster", &result)
291+ c.Assert(err, IsNil)
292+ c.Assert(result["ismaster"], Equals, false)
293+
294+ // Switch to a Strong session.
295+ session.SetMode(mgo.Strong, false)
296+
297+ // Wait since the sync also uses sockets.
298+ for len(session.LiveServers()) != 3 {
299+ c.Log("Waiting for cluster sync to finish...")
300+ time.Sleep(5e8)
301+ }
302+
303+ // Slave socket should still be reserved.
304+ stats := mgo.GetStats()
305+ c.Assert(stats.SocketsInUse, Equals, 1)
306+
307+ // But any operation will switch it to the master.
308+ result = M{}
309+ err = session.Run("ismaster", &result)
310+ c.Assert(err, IsNil)
311+ c.Assert(result["ismaster"], Equals, true)
312+}
313+
314+func (s *S) TestSetModeMonotonicWriteOnIteration(c *C) {
315+ // Must necessarily connect to a slave, otherwise the
316+ // master connection will be available first.
317+ session, err := mgo.Dial("localhost:40012")
318+ c.Assert(err, IsNil)
319+ defer session.Close()
320+
321+ session.SetMode(mgo.Monotonic, false)
322+
323+ c.Assert(session.Mode(), Equals, mgo.Monotonic)
324+
325+ coll1 := session.DB("mydb").C("mycoll1")
326+ coll2 := session.DB("mydb").C("mycoll2")
327+
328+ ns := []int{40, 41, 42, 43, 44, 45, 46}
329+ for _, n := range ns {
330+ err := coll1.Insert(M{"n": n})
331+ c.Assert(err, IsNil)
332+ }
333+
334+ // Release master so we can grab a slave again.
335+ session.Refresh()
336+
337+ // Wait until synchronization is done.
338+ for {
339+ n, err := coll1.Count()
340+ c.Assert(err, IsNil)
341+ if n == len(ns) {
342+ break
343+ }
344+ }
345+
346+ iter := coll1.Find(nil).Batch(2).Iter()
347+ i := 0
348+ m := M{}
349+ for iter.Next(&m) {
350+ i++
351+ if i > 3 {
352+ err := coll2.Insert(M{"n": 47 + i})
353+ c.Assert(err, IsNil)
354+ }
355+ }
356+ c.Assert(i, Equals, len(ns))
357+}
358+
359+func (s *S) TestSetModeEventual(c *C) {
360+ // Must necessarily connect to a slave, otherwise the
361+ // master connection will be available first.
362+ session, err := mgo.Dial("localhost:40012")
363+ c.Assert(err, IsNil)
364+ defer session.Close()
365+
366+ session.SetMode(mgo.Eventual, false)
367+
368+ c.Assert(session.Mode(), Equals, mgo.Eventual)
369+
370+ result := M{}
371+ err = session.Run("ismaster", &result)
372+ c.Assert(err, IsNil)
373+ c.Assert(result["ismaster"], Equals, false)
374+
375+ coll := session.DB("mydb").C("mycoll")
376+ err = coll.Insert(M{"a": 1})
377+ c.Assert(err, IsNil)
378+
379+ result = M{}
380+ err = session.Run("ismaster", &result)
381+ c.Assert(err, IsNil)
382+ c.Assert(result["ismaster"], Equals, false)
383+
384+ // Wait since the sync also uses sockets.
385+ for len(session.LiveServers()) != 3 {
386+ c.Log("Waiting for cluster sync to finish...")
387+ time.Sleep(5e8)
388+ }
389+
390+ stats := mgo.GetStats()
391+ c.Assert(stats.MasterConns, Equals, 1)
392+ c.Assert(stats.SlaveConns, Equals, 2)
393+ c.Assert(stats.SocketsInUse, Equals, 0)
394+}
395+
396+func (s *S) TestSetModeEventualAfterStrong(c *C) {
397+ // Test that a strong session shifting to an eventual
398+ // one preserves the socket untouched.
399+
400+ session, err := mgo.Dial("localhost:40012")
401+ c.Assert(err, IsNil)
402+ defer session.Close()
403+
404+ // Insert something to force a connection to the master.
405+ coll := session.DB("mydb").C("mycoll")
406+ err = coll.Insert(M{"a": 1})
407+ c.Assert(err, IsNil)
408+
409+ session.SetMode(mgo.Eventual, false)
410+
411+ // Wait since the sync also uses sockets.
412+ for len(session.LiveServers()) != 3 {
413+ c.Log("Waiting for cluster sync to finish...")
414+ time.Sleep(5e8)
415+ }
416+
417+ // Master socket should still be reserved.
418+ stats := mgo.GetStats()
419+ c.Assert(stats.SocketsInUse, Equals, 1)
420+
421+ // Confirm it's the master even though it's Eventual by now.
422+ result := M{}
423+ cmd := session.DB("admin").C("$cmd")
424+ err = cmd.Find(M{"ismaster": 1}).One(&result)
425+ c.Assert(err, IsNil)
426+ c.Assert(result["ismaster"], Equals, true)
427+
428+ session.SetMode(mgo.Eventual, true)
429+
430+ stats = mgo.GetStats()
431+ c.Assert(stats.SocketsInUse, Equals, 0)
432+}
433+
434+func (s *S) TestPrimaryShutdownStrong(c *C) {
435+ if *fast {
436+ c.Skip("-fast")
437+ }
438+
439+ session, err := mgo.Dial("localhost:40021")
440+ c.Assert(err, IsNil)
441+ defer session.Close()
442+
443+ // With strong consistency, this will open a socket to the master.
444+ result := &struct{ Host string }{}
445+ err = session.Run("serverStatus", result)
446+ c.Assert(err, IsNil)
447+
448+ // Kill the master.
449+ host := result.Host
450+ s.Stop(host)
451+
452+ // This must fail, since the connection was broken.
453+ err = session.Run("serverStatus", result)
454+ c.Assert(err, Equals, io.EOF)
455+
456+ // With strong consistency, it fails again until reset.
457+ err = session.Run("serverStatus", result)
458+ c.Assert(err, Equals, io.EOF)
459+
460+ session.Refresh()
461+
462+ // Now we should be able to talk to the new master.
463+ // Increase the timeout since this may take quite a while.
464+ session.SetSyncTimeout(3 * time.Minute)
465+
466+ err = session.Run("serverStatus", result)
467+ c.Assert(err, IsNil)
468+ c.Assert(result.Host, Not(Equals), host)
469+
470+ // Insert some data to confirm it's indeed a master.
471+ err = session.DB("mydb").C("mycoll").Insert(M{"n": 42})
472+ c.Assert(err, IsNil)
473+}
474+
475+func (s *S) TestPrimaryHiccup(c *C) {
476+ if *fast {
477+ c.Skip("-fast")
478+ }
479+
480+ session, err := mgo.Dial("localhost:40021")
481+ c.Assert(err, IsNil)
482+ defer session.Close()
483+
484+ // With strong consistency, this will open a socket to the master.
485+ result := &struct{ Host string }{}
486+ err = session.Run("serverStatus", result)
487+ c.Assert(err, IsNil)
488+
489+ // Establish a few extra sessions to create spare sockets to
490+ // the master. This increases a bit the chances of getting an
491+ // incorrect cached socket.
492+ var sessions []*mgo.Session
493+ for i := 0; i < 20; i++ {
494+ sessions = append(sessions, session.Copy())
495+ err = sessions[len(sessions)-1].Run("serverStatus", result)
496+ c.Assert(err, IsNil)
497+ }
498+ for i := range sessions {
499+ sessions[i].Close()
500+ }
501+
502+ // Kill the master, but bring it back immediatelly.
503+ host := result.Host
504+ s.Stop(host)
505+ s.StartAll()
506+
507+ // This must fail, since the connection was broken.
508+ err = session.Run("serverStatus", result)
509+ c.Assert(err, Equals, io.EOF)
510+
511+ // With strong consistency, it fails again until reset.
512+ err = session.Run("serverStatus", result)
513+ c.Assert(err, Equals, io.EOF)
514+
515+ session.Refresh()
516+
517+ // Now we should be able to talk to the new master.
518+ // Increase the timeout since this may take quite a while.
519+ session.SetSyncTimeout(3 * time.Minute)
520+
521+ // Insert some data to confirm it's indeed a master.
522+ err = session.DB("mydb").C("mycoll").Insert(M{"n": 42})
523+ c.Assert(err, IsNil)
524+}
525+
526+func (s *S) TestPrimaryShutdownMonotonic(c *C) {
527+ if *fast {
528+ c.Skip("-fast")
529+ }
530+
531+ session, err := mgo.Dial("localhost:40021")
532+ c.Assert(err, IsNil)
533+ defer session.Close()
534+
535+ session.SetMode(mgo.Monotonic, true)
536+
537+ // Insert something to force a switch to the master.
538+ coll := session.DB("mydb").C("mycoll")
539+ err = coll.Insert(M{"a": 1})
540+ c.Assert(err, IsNil)
541+
542+ // Wait a bit for this to be synchronized to slaves.
543+ time.Sleep(3 * time.Second)
544+
545+ result := &struct{ Host string }{}
546+ err = session.Run("serverStatus", result)
547+ c.Assert(err, IsNil)
548+
549+ // Kill the master.
550+ host := result.Host
551+ s.Stop(host)
552+
553+ // This must fail, since the connection was broken.
554+ err = session.Run("serverStatus", result)
555+ c.Assert(err, Equals, io.EOF)
556+
557+ // With monotonic consistency, it fails again until reset.
558+ err = session.Run("serverStatus", result)
559+ c.Assert(err, Equals, io.EOF)
560+
561+ session.Refresh()
562+
563+ // Now we should be able to talk to the new master.
564+ err = session.Run("serverStatus", result)
565+ c.Assert(err, IsNil)
566+ c.Assert(result.Host, Not(Equals), host)
567+}
568+
569+func (s *S) TestPrimaryShutdownMonotonicWithSlave(c *C) {
570+ if *fast {
571+ c.Skip("-fast")
572+ }
573+
574+ session, err := mgo.Dial("localhost:40021")
575+ c.Assert(err, IsNil)
576+ defer session.Close()
577+
578+ ssresult := &struct{ Host string }{}
579+ imresult := &struct{ IsMaster bool }{}
580+
581+ // Figure the master while still using the strong session.
582+ err = session.Run("serverStatus", ssresult)
583+ c.Assert(err, IsNil)
584+ err = session.Run("isMaster", imresult)
585+ c.Assert(err, IsNil)
586+ master := ssresult.Host
587+ c.Assert(imresult.IsMaster, Equals, true, Commentf("%s is not the master", master))
588+
589+ // Create new monotonic session with an explicit address to ensure
590+ // a slave is synchronized before the master, otherwise a connection
591+ // with the master may be used below for lack of other options.
592+ var addr string
593+ switch {
594+ case strings.HasSuffix(ssresult.Host, ":40021"):
595+ addr = "localhost:40022"
596+ case strings.HasSuffix(ssresult.Host, ":40022"):
597+ addr = "localhost:40021"
598+ case strings.HasSuffix(ssresult.Host, ":40023"):
599+ addr = "localhost:40021"
600+ default:
601+ c.Fatal("Unknown host: ", ssresult.Host)
602+ }
603+
604+ session, err = mgo.Dial(addr)
605+ c.Assert(err, IsNil)
606+ defer session.Close()
607+
608+ session.SetMode(mgo.Monotonic, true)
609+
610+ // Check the address of the socket associated with the monotonic session.
611+ c.Log("Running serverStatus and isMaster with monotonic session")
612+ err = session.Run("serverStatus", ssresult)
613+ c.Assert(err, IsNil)
614+ err = session.Run("isMaster", imresult)
615+ c.Assert(err, IsNil)
616+ slave := ssresult.Host
617+ c.Assert(imresult.IsMaster, Equals, false, Commentf("%s is not a slave", slave))
618+
619+ c.Assert(master, Not(Equals), slave)
620+
621+ // Kill the master.
622+ s.Stop(master)
623+
624+ // Session must still be good, since we were talking to a slave.
625+ err = session.Run("serverStatus", ssresult)
626+ c.Assert(err, IsNil)
627+
628+ c.Assert(ssresult.Host, Equals, slave,
629+ Commentf("Monotonic session moved from %s to %s", slave, ssresult.Host))
630+
631+ // If we try to insert something, it'll have to hold until the new
632+ // master is available to move the connection, and work correctly.
633+ coll := session.DB("mydb").C("mycoll")
634+ err = coll.Insert(M{"a": 1})
635+ c.Assert(err, IsNil)
636+
637+ // Must now be talking to the new master.
638+ err = session.Run("serverStatus", ssresult)
639+ c.Assert(err, IsNil)
640+ err = session.Run("isMaster", imresult)
641+ c.Assert(err, IsNil)
642+ c.Assert(imresult.IsMaster, Equals, true, Commentf("%s is not the master", master))
643+
644+ // ... which is not the old one, since it's still dead.
645+ c.Assert(ssresult.Host, Not(Equals), master)
646+}
647+
648+func (s *S) TestPrimaryShutdownEventual(c *C) {
649+ if *fast {
650+ c.Skip("-fast")
651+ }
652+
653+ session, err := mgo.Dial("localhost:40021")
654+ c.Assert(err, IsNil)
655+ defer session.Close()
656+
657+ result := &struct{ Host string }{}
658+ err = session.Run("serverStatus", result)
659+ c.Assert(err, IsNil)
660+ master := result.Host
661+
662+ session.SetMode(mgo.Eventual, true)
663+
664+ // Should connect to the master when needed.
665+ coll := session.DB("mydb").C("mycoll")
666+ err = coll.Insert(M{"a": 1})
667+ c.Assert(err, IsNil)
668+
669+ // Wait a bit for this to be synchronized to slaves.
670+ time.Sleep(3 * time.Second)
671+
672+ // Kill the master.
673+ s.Stop(master)
674+
675+ // Should still work, with the new master now.
676+ coll = session.DB("mydb").C("mycoll")
677+ err = coll.Insert(M{"a": 1})
678+ c.Assert(err, IsNil)
679+
680+ err = session.Run("serverStatus", result)
681+ c.Assert(err, IsNil)
682+ c.Assert(result.Host, Not(Equals), master)
683+}
684+
685+func (s *S) TestPreserveSocketCountOnSync(c *C) {
686+ if *fast {
687+ c.Skip("-fast")
688+ }
689+
690+ session, err := mgo.Dial("localhost:40011")
691+ c.Assert(err, IsNil)
692+ defer session.Close()
693+
694+ stats := mgo.GetStats()
695+ for stats.MasterConns+stats.SlaveConns != 3 {
696+ stats = mgo.GetStats()
697+ c.Log("Waiting for all connections to be established...")
698+ time.Sleep(5e8)
699+ }
700+
701+ c.Assert(stats.SocketsAlive, Equals, 3)
702+
703+ // Kill the master (with rs1, 'a' is always the master).
704+ s.Stop("localhost:40011")
705+
706+ // Wait for the logic to run for a bit and bring it back.
707+ startedAll := make(chan bool)
708+ go func() {
709+ time.Sleep(5e9)
710+ s.StartAll()
711+ startedAll <- true
712+ }()
713+
714+ // Do not allow the test to return before the goroutine above is done.
715+ defer func() {
716+ <-startedAll
717+ }()
718+
719+ // Do an action to kick the resync logic in, and also to
720+ // wait until the cluster recognizes the server is back.
721+ result := struct{ Ok bool }{}
722+ err = session.Run("getLastError", &result)
723+ c.Assert(err, IsNil)
724+ c.Assert(result.Ok, Equals, true)
725+
726+ for i := 0; i != 20; i++ {
727+ stats = mgo.GetStats()
728+ if stats.SocketsAlive == 3 {
729+ break
730+ }
731+ c.Logf("Waiting for 3 sockets alive, have %d", stats.SocketsAlive)
732+ time.Sleep(5e8)
733+ }
734+
735+ // Ensure the number of sockets is preserved after syncing.
736+ stats = mgo.GetStats()
737+ c.Assert(stats.SocketsAlive, Equals, 3)
738+ c.Assert(stats.SocketsInUse, Equals, 1)
739+ c.Assert(stats.SocketRefs, Equals, 1)
740+}
741+
742+// Connect to the master of a deployment with a single server,
743+// run an insert, and then ensure the insert worked and that a
744+// single connection was established.
745+func (s *S) TestTopologySyncWithSingleMaster(c *C) {
746+ // Use hostname here rather than IP, to make things trickier.
747+ session, err := mgo.Dial("localhost:40001")
748+ c.Assert(err, IsNil)
749+ defer session.Close()
750+
751+ coll := session.DB("mydb").C("mycoll")
752+ err = coll.Insert(M{"a": 1, "b": 2})
753+ c.Assert(err, IsNil)
754+
755+ // One connection used for discovery. Master socket recycled for
756+ // insert. Socket is reserved after insert.
757+ stats := mgo.GetStats()
758+ c.Assert(stats.MasterConns, Equals, 1)
759+ c.Assert(stats.SlaveConns, Equals, 0)
760+ c.Assert(stats.SocketsInUse, Equals, 1)
761+
762+ // Refresh session and socket must be released.
763+ session.Refresh()
764+ stats = mgo.GetStats()
765+ c.Assert(stats.SocketsInUse, Equals, 0)
766+}
767+
768+func (s *S) TestTopologySyncWithSlaveSeed(c *C) {
769+ // That's supposed to be a slave. Must run discovery
770+ // and find out master to insert successfully.
771+ session, err := mgo.Dial("localhost:40012")
772+ c.Assert(err, IsNil)
773+ defer session.Close()
774+
775+ coll := session.DB("mydb").C("mycoll")
776+ coll.Insert(M{"a": 1, "b": 2})
777+
778+ result := struct{ Ok bool }{}
779+ err = session.Run("getLastError", &result)
780+ c.Assert(err, IsNil)
781+ c.Assert(result.Ok, Equals, true)
782+
783+ // One connection to each during discovery. Master
784+ // socket recycled for insert.
785+ stats := mgo.GetStats()
786+ c.Assert(stats.MasterConns, Equals, 1)
787+ c.Assert(stats.SlaveConns, Equals, 2)
788+
789+ // Only one socket reference alive, in the master socket owned
790+ // by the above session.
791+ c.Assert(stats.SocketsInUse, Equals, 1)
792+
793+ // Refresh it, and it must be gone.
794+ session.Refresh()
795+ stats = mgo.GetStats()
796+ c.Assert(stats.SocketsInUse, Equals, 0)
797+}
798+
799+func (s *S) TestSyncTimeout(c *C) {
800+ if *fast {
801+ c.Skip("-fast")
802+ }
803+
804+ session, err := mgo.Dial("localhost:40001")
805+ c.Assert(err, IsNil)
806+ defer session.Close()
807+
808+ s.Stop("localhost:40001")
809+
810+ timeout := 3 * time.Second
811+ session.SetSyncTimeout(timeout)
812+ started := time.Now()
813+
814+ // Do something.
815+ result := struct{ Ok bool }{}
816+ err = session.Run("getLastError", &result)
817+ c.Assert(err, ErrorMatches, "no reachable servers")
818+ c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true)
819+ c.Assert(started.After(time.Now().Add(-timeout*2)), Equals, true)
820+}
821+
822+func (s *S) TestDialWithTimeout(c *C) {
823+ if *fast {
824+ c.Skip("-fast")
825+ }
826+
827+ timeout := 2 * time.Second
828+ started := time.Now()
829+
830+ // 40009 isn't used by the test servers.
831+ session, err := mgo.DialWithTimeout("localhost:40009", timeout)
832+ if session != nil {
833+ session.Close()
834+ }
835+ c.Assert(err, ErrorMatches, "no reachable servers")
836+ c.Assert(session, IsNil)
837+ c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true)
838+ c.Assert(started.After(time.Now().Add(-timeout*2)), Equals, true)
839+}
840+
841+func (s *S) TestSocketTimeout(c *C) {
842+ if *fast {
843+ c.Skip("-fast")
844+ }
845+
846+ session, err := mgo.Dial("localhost:40001")
847+ c.Assert(err, IsNil)
848+ defer session.Close()
849+
850+ s.Freeze("localhost:40001")
851+
852+ timeout := 3 * time.Second
853+ session.SetSocketTimeout(timeout)
854+ started := time.Now()
855+
856+ // Do something.
857+ result := struct{ Ok bool }{}
858+ err = session.Run("getLastError", &result)
859+ c.Assert(err, ErrorMatches, ".*: i/o timeout")
860+ c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true)
861+ c.Assert(started.After(time.Now().Add(-timeout*2)), Equals, true)
862+}
863+
864+func (s *S) TestSocketTimeoutOnDial(c *C) {
865+ if *fast {
866+ c.Skip("-fast")
867+ }
868+
869+ timeout := 1 * time.Second
870+
871+ defer mgo.HackSyncSocketTimeout(timeout)()
872+
873+ s.Freeze("localhost:40001")
874+
875+ started := time.Now()
876+
877+ session, err := mgo.DialWithTimeout("localhost:40001", timeout)
878+ c.Assert(err, ErrorMatches, "no reachable servers")
879+ c.Assert(session, IsNil)
880+
881+ c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true)
882+ c.Assert(started.After(time.Now().Add(-20*time.Second)), Equals, true)
883+}
884+
885+func (s *S) TestSocketTimeoutOnInactiveSocket(c *C) {
886+ if *fast {
887+ c.Skip("-fast")
888+ }
889+
890+ session, err := mgo.Dial("localhost:40001")
891+ c.Assert(err, IsNil)
892+ defer session.Close()
893+
894+ timeout := 2 * time.Second
895+ session.SetSocketTimeout(timeout)
896+
897+ // Do something that relies on the timeout and works.
898+ c.Assert(session.Ping(), IsNil)
899+
900+ // Freeze and wait for the timeout to go by.
901+ s.Freeze("localhost:40001")
902+ time.Sleep(timeout + 500*time.Millisecond)
903+ s.Thaw("localhost:40001")
904+
905+ // Do something again. The timeout above should not have killed
906+ // the socket as there was nothing to be done.
907+ c.Assert(session.Ping(), IsNil)
908+}
909+
910+func (s *S) TestDialWithReplicaSetName(c *C) {
911+ seedLists := [][]string{
912+ // rs1 primary and rs2 primary
913+ []string{"localhost:40011", "localhost:40021"},
914+ // rs1 primary and rs2 secondary
915+ []string{"localhost:40011", "localhost:40022"},
916+ // rs1 secondary and rs2 primary
917+ []string{"localhost:40012", "localhost:40021"},
918+ // rs1 secondary and rs2 secondary
919+ []string{"localhost:40012", "localhost:40022"},
920+ }
921+
922+ rs2Members := []string{":40021", ":40022", ":40023"}
923+
924+ verifySyncedServers := func(session *mgo.Session, numServers int) {
925+ // wait for the server(s) to be synced
926+ for len(session.LiveServers()) != numServers {
927+ c.Log("Waiting for cluster sync to finish...")
928+ time.Sleep(5e8)
929+ }
930+
931+ // ensure none of the rs2 set members are communicated with
932+ for _, addr := range session.LiveServers() {
933+ for _, rs2Member := range rs2Members {
934+ c.Assert(strings.HasSuffix(addr, rs2Member), Equals, false)
935+ }
936+ }
937+ }
938+
939+ // only communication with rs1 members is expected
940+ for _, seedList := range seedLists {
941+ info := mgo.DialInfo{
942+ Addrs: seedList,
943+ Timeout: 5 * time.Second,
944+ ReplicaSetName: "rs1",
945+ }
946+
947+ session, err := mgo.DialWithInfo(&info)
948+ c.Assert(err, IsNil)
949+ verifySyncedServers(session, 3)
950+ session.Close()
951+
952+ info.Direct = true
953+ session, err = mgo.DialWithInfo(&info)
954+ c.Assert(err, IsNil)
955+ verifySyncedServers(session, 1)
956+ session.Close()
957+
958+ connectionUrl := fmt.Sprintf("mongodb://%v/?replicaSet=rs1", strings.Join(seedList, ","))
959+ session, err = mgo.Dial(connectionUrl)
960+ c.Assert(err, IsNil)
961+ verifySyncedServers(session, 3)
962+ session.Close()
963+
964+ connectionUrl += "&connect=direct"
965+ session, err = mgo.Dial(connectionUrl)
966+ c.Assert(err, IsNil)
967+ verifySyncedServers(session, 1)
968+ session.Close()
969+ }
970+
971+}
972+
973+func (s *S) TestDirect(c *C) {
974+ session, err := mgo.Dial("localhost:40012?connect=direct")
975+ c.Assert(err, IsNil)
976+ defer session.Close()
977+
978+ // We know that server is a slave.
979+ session.SetMode(mgo.Monotonic, true)
980+
981+ result := &struct{ Host string }{}
982+ err = session.Run("serverStatus", result)
983+ c.Assert(err, IsNil)
984+ c.Assert(strings.HasSuffix(result.Host, ":40012"), Equals, true)
985+
986+ stats := mgo.GetStats()
987+ c.Assert(stats.SocketsAlive, Equals, 1)
988+ c.Assert(stats.SocketsInUse, Equals, 1)
989+ c.Assert(stats.SocketRefs, Equals, 1)
990+
991+ // We've got no master, so it'll timeout.
992+ session.SetSyncTimeout(5e8 * time.Nanosecond)
993+
994+ coll := session.DB("mydb").C("mycoll")
995+ err = coll.Insert(M{"test": 1})
996+ c.Assert(err, ErrorMatches, "no reachable servers")
997+
998+ // Writing to the local database is okay.
999+ coll = session.DB("local").C("mycoll")
1000+ defer coll.RemoveAll(nil)
1001+ id := bson.NewObjectId()
1002+ err = coll.Insert(M{"_id": id})
1003+ c.Assert(err, IsNil)
1004+
1005+ // Data was stored in the right server.
1006+ n, err := coll.Find(M{"_id": id}).Count()
1007+ c.Assert(err, IsNil)
1008+ c.Assert(n, Equals, 1)
1009+
1010+ // Server hasn't changed.
1011+ result.Host = ""
1012+ err = session.Run("serverStatus", result)
1013+ c.Assert(err, IsNil)
1014+ c.Assert(strings.HasSuffix(result.Host, ":40012"), Equals, true)
1015+}
1016+
1017+func (s *S) TestDirectToUnknownStateMember(c *C) {
1018+ session, err := mgo.Dial("localhost:40041?connect=direct")
1019+ c.Assert(err, IsNil)
1020+ defer session.Close()
1021+
1022+ session.SetMode(mgo.Monotonic, true)
1023+
1024+ result := &struct{ Host string }{}
1025+ err = session.Run("serverStatus", result)
1026+ c.Assert(err, IsNil)
1027+ c.Assert(strings.HasSuffix(result.Host, ":40041"), Equals, true)
1028+
1029+ // We've got no master, so it'll timeout.
1030+ session.SetSyncTimeout(5e8 * time.Nanosecond)
1031+
1032+ coll := session.DB("mydb").C("mycoll")
1033+ err = coll.Insert(M{"test": 1})
1034+ c.Assert(err, ErrorMatches, "no reachable servers")
1035+
1036+ // Slave is still reachable.
1037+ result.Host = ""
1038+ err = session.Run("serverStatus", result)
1039+ c.Assert(err, IsNil)
1040+ c.Assert(strings.HasSuffix(result.Host, ":40041"), Equals, true)
1041+}
1042+
1043+func (s *S) TestFailFast(c *C) {
1044+ info := mgo.DialInfo{
1045+ Addrs: []string{"localhost:99999"},
1046+ Timeout: 5 * time.Second,
1047+ FailFast: true,
1048+ }
1049+
1050+ started := time.Now()
1051+
1052+ _, err := mgo.DialWithInfo(&info)
1053+ c.Assert(err, ErrorMatches, "no reachable servers")
1054+
1055+ c.Assert(started.After(time.Now().Add(-time.Second)), Equals, true)
1056+}
1057+
1058+type OpCounters struct {
1059+ Insert int
1060+ Query int
1061+ Update int
1062+ Delete int
1063+ GetMore int
1064+ Command int
1065+}
1066+
1067+func getOpCounters(server string) (c *OpCounters, err error) {
1068+ session, err := mgo.Dial(server + "?connect=direct")
1069+ if err != nil {
1070+ return nil, err
1071+ }
1072+ defer session.Close()
1073+ session.SetMode(mgo.Monotonic, true)
1074+ result := struct{ OpCounters }{}
1075+ err = session.Run("serverStatus", &result)
1076+ return &result.OpCounters, err
1077+}
1078+
1079+func (s *S) TestMonotonicSlaveOkFlagWithMongos(c *C) {
1080+ session, err := mgo.Dial("localhost:40021")
1081+ c.Assert(err, IsNil)
1082+ defer session.Close()
1083+
1084+ ssresult := &struct{ Host string }{}
1085+ imresult := &struct{ IsMaster bool }{}
1086+
1087+ // Figure the master while still using the strong session.
1088+ err = session.Run("serverStatus", ssresult)
1089+ c.Assert(err, IsNil)
1090+ err = session.Run("isMaster", imresult)
1091+ c.Assert(err, IsNil)
1092+ master := ssresult.Host
1093+ c.Assert(imresult.IsMaster, Equals, true, Commentf("%s is not the master", master))
1094+
1095+ // Collect op counters for everyone.
1096+ opc21a, err := getOpCounters("localhost:40021")
1097+ c.Assert(err, IsNil)
1098+ opc22a, err := getOpCounters("localhost:40022")
1099+ c.Assert(err, IsNil)
1100+ opc23a, err := getOpCounters("localhost:40023")
1101+ c.Assert(err, IsNil)
1102+
1103+ // Do a SlaveOk query through MongoS
1104+
1105+ mongos, err := mgo.Dial("localhost:40202")
1106+ c.Assert(err, IsNil)
1107+ defer mongos.Close()
1108+
1109+ mongos.SetMode(mgo.Monotonic, true)
1110+
1111+ coll := mongos.DB("mydb").C("mycoll")
1112+ result := &struct{}{}
1113+ for i := 0; i != 5; i++ {
1114+ err := coll.Find(nil).One(result)
1115+ c.Assert(err, Equals, mgo.ErrNotFound)
1116+ }
1117+
1118+ // Collect op counters for everyone again.
1119+ opc21b, err := getOpCounters("localhost:40021")
1120+ c.Assert(err, IsNil)
1121+ opc22b, err := getOpCounters("localhost:40022")
1122+ c.Assert(err, IsNil)
1123+ opc23b, err := getOpCounters("localhost:40023")
1124+ c.Assert(err, IsNil)
1125+
1126+ masterPort := master[strings.Index(master, ":")+1:]
1127+
1128+ var masterDelta, slaveDelta int
1129+ switch masterPort {
1130+ case "40021":
1131+ masterDelta = opc21b.Query - opc21a.Query
1132+ slaveDelta = (opc22b.Query - opc22a.Query) + (opc23b.Query - opc23a.Query)
1133+ case "40022":
1134+ masterDelta = opc22b.Query - opc22a.Query
1135+ slaveDelta = (opc21b.Query - opc21a.Query) + (opc23b.Query - opc23a.Query)
1136+ case "40023":
1137+ masterDelta = opc23b.Query - opc23a.Query
1138+ slaveDelta = (opc21b.Query - opc21a.Query) + (opc22b.Query - opc22a.Query)
1139+ default:
1140+ c.Fatal("Uh?")
1141+ }
1142+
1143+ c.Check(masterDelta, Equals, 0) // Just the counting itself.
1144+ c.Check(slaveDelta, Equals, 5) // The counting for both, plus 5 queries above.
1145+}
1146+
1147+func (s *S) TestRemovalOfClusterMember(c *C) {
1148+ if *fast {
1149+ c.Skip("-fast")
1150+ }
1151+
1152+ master, err := mgo.Dial("localhost:40021")
1153+ c.Assert(err, IsNil)
1154+ defer master.Close()
1155+
1156+ // Wait for cluster to fully sync up.
1157+ for i := 0; i < 10; i++ {
1158+ if len(master.LiveServers()) == 3 {
1159+ break
1160+ }
1161+ time.Sleep(5e8)
1162+ }
1163+ if len(master.LiveServers()) != 3 {
1164+ c.Fatalf("Test started with bad cluster state: %v", master.LiveServers())
1165+ }
1166+
1167+ result := &struct {
1168+ IsMaster bool
1169+ Me string
1170+ }{}
1171+ slave := master.Copy()
1172+ slave.SetMode(mgo.Monotonic, true) // Monotonic can hold a non-master socket persistently.
1173+ err = slave.Run("isMaster", result)
1174+ c.Assert(err, IsNil)
1175+ c.Assert(result.IsMaster, Equals, false)
1176+ slaveAddr := result.Me
1177+
1178+ defer func() {
1179+ master.Refresh()
1180+ master.Run(bson.D{{"$eval", `rs.add("` + slaveAddr + `")`}}, nil)
1181+ master.Close()
1182+ slave.Close()
1183+ }()
1184+
1185+ c.Logf("========== Removing slave: %s ==========", slaveAddr)
1186+
1187+ master.Run(bson.D{{"$eval", `rs.remove("` + slaveAddr + `")`}}, nil)
1188+
1189+ master.Refresh()
1190+
1191+ // Give the cluster a moment to catch up by doing a roundtrip to the master.
1192+ err = master.Ping()
1193+ c.Assert(err, IsNil)
1194+
1195+ time.Sleep(3e9)
1196+
1197+ // This must fail since the slave has been taken off the cluster.
1198+ err = slave.Ping()
1199+ c.Assert(err, NotNil)
1200+
1201+ for i := 0; i < 15; i++ {
1202+ if len(master.LiveServers()) == 2 {
1203+ break
1204+ }
1205+ time.Sleep(time.Second)
1206+ }
1207+ live := master.LiveServers()
1208+ if len(live) != 2 {
1209+ c.Errorf("Removed server still considered live: %#s", live)
1210+ }
1211+
1212+ c.Log("========== Test succeeded. ==========")
1213+}
1214+
1215+func (s *S) TestPoolLimitSimple(c *C) {
1216+ for test := 0; test < 2; test++ {
1217+ var session *mgo.Session
1218+ var err error
1219+ if test == 0 {
1220+ session, err = mgo.Dial("localhost:40001")
1221+ c.Assert(err, IsNil)
1222+ session.SetPoolLimit(1)
1223+ } else {
1224+ session, err = mgo.Dial("localhost:40001?maxPoolSize=1")
1225+ c.Assert(err, IsNil)
1226+ }
1227+ defer session.Close()
1228+
1229+ // Put one socket in use.
1230+ c.Assert(session.Ping(), IsNil)
1231+
1232+ done := make(chan time.Duration)
1233+
1234+ // Now block trying to get another one due to the pool limit.
1235+ go func() {
1236+ copy := session.Copy()
1237+ defer copy.Close()
1238+ started := time.Now()
1239+ c.Check(copy.Ping(), IsNil)
1240+ done <- time.Now().Sub(started)
1241+ }()
1242+
1243+ time.Sleep(300 * time.Millisecond)
1244+
1245+ // Put the one socket back in the pool, freeing it for the copy.
1246+ session.Refresh()
1247+ delay := <-done
1248+ c.Assert(delay > 300*time.Millisecond, Equals, true, Commentf("Delay: %s", delay))
1249+ }
1250+}
1251+
1252+func (s *S) TestPoolLimitMany(c *C) {
1253+ if *fast {
1254+ c.Skip("-fast")
1255+ }
1256+
1257+ session, err := mgo.Dial("localhost:40011")
1258+ c.Assert(err, IsNil)
1259+ defer session.Close()
1260+
1261+ stats := mgo.GetStats()
1262+ for stats.MasterConns+stats.SlaveConns != 3 {
1263+ stats = mgo.GetStats()
1264+ c.Log("Waiting for all connections to be established...")
1265+ time.Sleep(500 * time.Millisecond)
1266+ }
1267+ c.Assert(stats.SocketsAlive, Equals, 3)
1268+
1269+ const poolLimit = 64
1270+ session.SetPoolLimit(poolLimit)
1271+
1272+ // Consume the whole limit for the master.
1273+ var master []*mgo.Session
1274+ for i := 0; i < poolLimit; i++ {
1275+ s := session.Copy()
1276+ defer s.Close()
1277+ c.Assert(s.Ping(), IsNil)
1278+ master = append(master, s)
1279+ }
1280+
1281+ before := time.Now()
1282+ go func() {
1283+ time.Sleep(3e9)
1284+ master[0].Refresh()
1285+ }()
1286+
1287+ // Then, a single ping must block, since it would need another
1288+ // connection to the master, over the limit. Once the goroutine
1289+ // above releases its socket, it should move on.
1290+ session.Ping()
1291+ delay := time.Now().Sub(before)
1292+ c.Assert(delay > 3e9, Equals, true)
1293+ c.Assert(delay < 6e9, Equals, true)
1294+}
1295+
1296+func (s *S) TestSetModeEventualIterBug(c *C) {
1297+ session1, err := mgo.Dial("localhost:40011")
1298+ c.Assert(err, IsNil)
1299+ defer session1.Close()
1300+
1301+ session1.SetMode(mgo.Eventual, false)
1302+
1303+ coll1 := session1.DB("mydb").C("mycoll")
1304+
1305+ const N = 100
1306+ for i := 0; i < N; i++ {
1307+ err = coll1.Insert(M{"_id": i})
1308+ c.Assert(err, IsNil)
1309+ }
1310+
1311+ c.Logf("Waiting until secondary syncs")
1312+ for {
1313+ n, err := coll1.Count()
1314+ c.Assert(err, IsNil)
1315+ if n == N {
1316+ c.Logf("Found all")
1317+ break
1318+ }
1319+ }
1320+
1321+ session2, err := mgo.Dial("localhost:40011")
1322+ c.Assert(err, IsNil)
1323+ defer session2.Close()
1324+
1325+ session2.SetMode(mgo.Eventual, false)
1326+
1327+ coll2 := session2.DB("mydb").C("mycoll")
1328+
1329+ i := 0
1330+ iter := coll2.Find(nil).Batch(10).Iter()
1331+ var result struct{}
1332+ for iter.Next(&result) {
1333+ i++
1334+ }
1335+ c.Assert(iter.Close(), Equals, nil)
1336+ c.Assert(i, Equals, N)
1337+}
1338+
1339+func (s *S) TestCustomDialOld(c *C) {
1340+ dials := make(chan bool, 16)
1341+ dial := func(addr net.Addr) (net.Conn, error) {
1342+ tcpaddr, ok := addr.(*net.TCPAddr)
1343+ if !ok {
1344+ return nil, fmt.Errorf("unexpected address type: %T", addr)
1345+ }
1346+ dials <- true
1347+ return net.DialTCP("tcp", nil, tcpaddr)
1348+ }
1349+ info := mgo.DialInfo{
1350+ Addrs: []string{"localhost:40012"},
1351+ Dial: dial,
1352+ }
1353+
1354+ // Use hostname here rather than IP, to make things trickier.
1355+ session, err := mgo.DialWithInfo(&info)
1356+ c.Assert(err, IsNil)
1357+ defer session.Close()
1358+
1359+ const N = 3
1360+ for i := 0; i < N; i++ {
1361+ select {
1362+ case <-dials:
1363+ case <-time.After(5 * time.Second):
1364+ c.Fatalf("expected %d dials, got %d", N, i)
1365+ }
1366+ }
1367+ select {
1368+ case <-dials:
1369+ c.Fatalf("got more dials than expected")
1370+ case <-time.After(100 * time.Millisecond):
1371+ }
1372+}
1373+
1374+func (s *S) TestCustomDialNew(c *C) {
1375+ dials := make(chan bool, 16)
1376+ dial := func(addr *mgo.ServerAddr) (net.Conn, error) {
1377+ dials <- true
1378+ if addr.TCPAddr().Port == 40012 {
1379+ c.Check(addr.String(), Equals, "localhost:40012")
1380+ }
1381+ return net.DialTCP("tcp", nil, addr.TCPAddr())
1382+ }
1383+ info := mgo.DialInfo{
1384+ Addrs: []string{"localhost:40012"},
1385+ DialServer: dial,
1386+ }
1387+
1388+ // Use hostname here rather than IP, to make things trickier.
1389+ session, err := mgo.DialWithInfo(&info)
1390+ c.Assert(err, IsNil)
1391+ defer session.Close()
1392+
1393+ const N = 3
1394+ for i := 0; i < N; i++ {
1395+ select {
1396+ case <-dials:
1397+ case <-time.After(5 * time.Second):
1398+ c.Fatalf("expected %d dials, got %d", N, i)
1399+ }
1400+ }
1401+ select {
1402+ case <-dials:
1403+ c.Fatalf("got more dials than expected")
1404+ case <-time.After(100 * time.Millisecond):
1405+ }
1406+}
1407+
1408+func (s *S) TestPrimaryShutdownOnAuthShard(c *C) {
1409+ if *fast {
1410+ c.Skip("-fast")
1411+ }
1412+
1413+ // Dial the shard.
1414+ session, err := mgo.Dial("localhost:40203")
1415+ c.Assert(err, IsNil)
1416+ defer session.Close()
1417+
1418+ // Login and insert something to make it more realistic.
1419+ session.DB("admin").Login("root", "rapadura")
1420+ coll := session.DB("mydb").C("mycoll")
1421+ err = coll.Insert(bson.M{"n": 1})
1422+ c.Assert(err, IsNil)
1423+
1424+ // Dial the replica set to figure the master out.
1425+ rs, err := mgo.Dial("root:rapadura@localhost:40031")
1426+ c.Assert(err, IsNil)
1427+ defer rs.Close()
1428+
1429+ // With strong consistency, this will open a socket to the master.
1430+ result := &struct{ Host string }{}
1431+ err = rs.Run("serverStatus", result)
1432+ c.Assert(err, IsNil)
1433+
1434+ // Kill the master.
1435+ host := result.Host
1436+ s.Stop(host)
1437+
1438+ // This must fail, since the connection was broken.
1439+ err = rs.Run("serverStatus", result)
1440+ c.Assert(err, Equals, io.EOF)
1441+
1442+ // This won't work because the master just died.
1443+ err = coll.Insert(bson.M{"n": 2})
1444+ c.Assert(err, NotNil)
1445+
1446+ // Refresh session and wait for re-election.
1447+ session.Refresh()
1448+ for i := 0; i < 60; i++ {
1449+ err = coll.Insert(bson.M{"n": 3})
1450+ if err == nil {
1451+ break
1452+ }
1453+ c.Logf("Waiting for replica set to elect a new master. Last error: %v", err)
1454+ time.Sleep(500 * time.Millisecond)
1455+ }
1456+ c.Assert(err, IsNil)
1457+
1458+ count, err := coll.Count()
1459+ c.Assert(count > 1, Equals, true)
1460+}
1461+
1462+func (s *S) TestNearestSecondary(c *C) {
1463+ defer mgo.HackPingDelay(3 * time.Second)()
1464+
1465+ rs1a := "127.0.0.1:40011"
1466+ rs1b := "127.0.0.1:40012"
1467+ rs1c := "127.0.0.1:40013"
1468+ s.Freeze(rs1b)
1469+
1470+ session, err := mgo.Dial(rs1a)
1471+ c.Assert(err, IsNil)
1472+ defer session.Close()
1473+
1474+ // Wait for the sync up to run through the first couple of servers.
1475+ for len(session.LiveServers()) != 2 {
1476+ c.Log("Waiting for two servers to be alive...")
1477+ time.Sleep(100 * time.Millisecond)
1478+ }
1479+
1480+ // Extra delay to ensure the third server gets penalized.
1481+ time.Sleep(500 * time.Millisecond)
1482+
1483+ // Release third server.
1484+ s.Thaw(rs1b)
1485+
1486+ // Wait for it to come up.
1487+ for len(session.LiveServers()) != 3 {
1488+ c.Log("Waiting for all servers to be alive...")
1489+ time.Sleep(100 * time.Millisecond)
1490+ }
1491+
1492+ session.SetMode(mgo.Monotonic, true)
1493+ var result struct{ Host string }
1494+
1495+ // See which slave picks the line, several times to avoid chance.
1496+ for i := 0; i < 10; i++ {
1497+ session.Refresh()
1498+ err = session.Run("serverStatus", &result)
1499+ c.Assert(err, IsNil)
1500+ c.Assert(hostPort(result.Host), Equals, hostPort(rs1c))
1501+ }
1502+
1503+ if *fast {
1504+ // Don't hold back for several seconds.
1505+ return
1506+ }
1507+
1508+ // Now hold the other server for long enough to penalize it.
1509+ s.Freeze(rs1c)
1510+ time.Sleep(5 * time.Second)
1511+ s.Thaw(rs1c)
1512+
1513+ // Wait for the ping to be processed.
1514+ time.Sleep(500 * time.Millisecond)
1515+
1516+ // Repeating the test should now pick the former server consistently.
1517+ for i := 0; i < 10; i++ {
1518+ session.Refresh()
1519+ err = session.Run("serverStatus", &result)
1520+ c.Assert(err, IsNil)
1521+ c.Assert(hostPort(result.Host), Equals, hostPort(rs1b))
1522+ }
1523+}
1524+
1525+func (s *S) TestConnectCloseConcurrency(c *C) {
1526+ restore := mgo.HackPingDelay(500 * time.Millisecond)
1527+ defer restore()
1528+ var wg sync.WaitGroup
1529+ const n = 500
1530+ wg.Add(n)
1531+ for i := 0; i < n; i++ {
1532+ go func() {
1533+ defer wg.Done()
1534+ session, err := mgo.Dial("localhost:40001")
1535+ if err != nil {
1536+ c.Fatal(err)
1537+ }
1538+ time.Sleep(1)
1539+ session.Close()
1540+ }()
1541+ }
1542+ wg.Wait()
1543+}
1544+
1545+func (s *S) TestSelectServers(c *C) {
1546+ if !s.versionAtLeast(2, 2) {
1547+ c.Skip("read preferences introduced in 2.2")
1548+ }
1549+
1550+ session, err := mgo.Dial("localhost:40011")
1551+ c.Assert(err, IsNil)
1552+ defer session.Close()
1553+
1554+ session.SetMode(mgo.Eventual, true)
1555+
1556+ var result struct{ Host string }
1557+
1558+ session.Refresh()
1559+ session.SelectServers(bson.D{{"rs1", "b"}})
1560+ err = session.Run("serverStatus", &result)
1561+ c.Assert(err, IsNil)
1562+ c.Assert(hostPort(result.Host), Equals, "40012")
1563+
1564+ session.Refresh()
1565+ session.SelectServers(bson.D{{"rs1", "c"}})
1566+ err = session.Run("serverStatus", &result)
1567+ c.Assert(err, IsNil)
1568+ c.Assert(hostPort(result.Host), Equals, "40013")
1569+}
1570+
1571+func (s *S) TestSelectServersWithMongos(c *C) {
1572+ if !s.versionAtLeast(2, 2) {
1573+ c.Skip("read preferences introduced in 2.2")
1574+ }
1575+
1576+ session, err := mgo.Dial("localhost:40021")
1577+ c.Assert(err, IsNil)
1578+ defer session.Close()
1579+
1580+ ssresult := &struct{ Host string }{}
1581+ imresult := &struct{ IsMaster bool }{}
1582+
1583+ // Figure the master while still using the strong session.
1584+ err = session.Run("serverStatus", ssresult)
1585+ c.Assert(err, IsNil)
1586+ err = session.Run("isMaster", imresult)
1587+ c.Assert(err, IsNil)
1588+ master := ssresult.Host
1589+ c.Assert(imresult.IsMaster, Equals, true, Commentf("%s is not the master", master))
1590+
1591+ var slave1, slave2 string
1592+ switch hostPort(master) {
1593+ case "40021":
1594+ slave1, slave2 = "b", "c"
1595+ case "40022":
1596+ slave1, slave2 = "a", "c"
1597+ case "40023":
1598+ slave1, slave2 = "a", "b"
1599+ }
1600+
1601+ // Collect op counters for everyone.
1602+ opc21a, err := getOpCounters("localhost:40021")
1603+ c.Assert(err, IsNil)
1604+ opc22a, err := getOpCounters("localhost:40022")
1605+ c.Assert(err, IsNil)
1606+ opc23a, err := getOpCounters("localhost:40023")
1607+ c.Assert(err, IsNil)
1608+
1609+ // Do a SlaveOk query through MongoS
1610+ mongos, err := mgo.Dial("localhost:40202")
1611+ c.Assert(err, IsNil)
1612+ defer mongos.Close()
1613+
1614+ mongos.SetMode(mgo.Monotonic, true)
1615+
1616+ mongos.Refresh()
1617+ mongos.SelectServers(bson.D{{"rs2", slave1}})
1618+ coll := mongos.DB("mydb").C("mycoll")
1619+ result := &struct{}{}
1620+ for i := 0; i != 5; i++ {
1621+ err := coll.Find(nil).One(result)
1622+ c.Assert(err, Equals, mgo.ErrNotFound)
1623+ }
1624+
1625+ mongos.Refresh()
1626+ mongos.SelectServers(bson.D{{"rs2", slave2}})
1627+ coll = mongos.DB("mydb").C("mycoll")
1628+ for i := 0; i != 7; i++ {
1629+ err := coll.Find(nil).One(result)
1630+ c.Assert(err, Equals, mgo.ErrNotFound)
1631+ }
1632+
1633+ // Collect op counters for everyone again.
1634+ opc21b, err := getOpCounters("localhost:40021")
1635+ c.Assert(err, IsNil)
1636+ opc22b, err := getOpCounters("localhost:40022")
1637+ c.Assert(err, IsNil)
1638+ opc23b, err := getOpCounters("localhost:40023")
1639+ c.Assert(err, IsNil)
1640+
1641+ switch hostPort(master) {
1642+ case "40021":
1643+ c.Check(opc21b.Query-opc21a.Query, Equals, 0)
1644+ c.Check(opc22b.Query-opc22a.Query, Equals, 5)
1645+ c.Check(opc23b.Query-opc23a.Query, Equals, 7)
1646+ case "40022":
1647+ c.Check(opc21b.Query-opc21a.Query, Equals, 5)
1648+ c.Check(opc22b.Query-opc22a.Query, Equals, 0)
1649+ c.Check(opc23b.Query-opc23a.Query, Equals, 7)
1650+ case "40023":
1651+ c.Check(opc21b.Query-opc21a.Query, Equals, 5)
1652+ c.Check(opc22b.Query-opc22a.Query, Equals, 7)
1653+ c.Check(opc23b.Query-opc23a.Query, Equals, 0)
1654+ default:
1655+ c.Fatal("Uh?")
1656+ }
1657+}
vendor/gopkg.in/mgo.v2/doc.go+31−0
@@ -0,0 +1,31 @@
1+// Package mgo offers a rich MongoDB driver for Go.
2+//
3+// Details about the mgo project (pronounced as "mango") are found
4+// in its web page:
5+//
6+// http://labix.org/mgo
7+//
8+// Usage of the driver revolves around the concept of sessions. To
9+// get started, obtain a session using the Dial function:
10+//
11+// session, err := mgo.Dial(url)
12+//
13+// This will establish one or more connections with the cluster of
14+// servers defined by the url parameter. From then on, the cluster
15+// may be queried with multiple consistency rules (see SetMode) and
16+// documents retrieved with statements such as:
17+//
18+// c := session.DB(database).C(collection)
19+// err := c.Find(query).One(&result)
20+//
21+// New sessions are typically created by calling session.Copy on the
22+// initial session obtained at dial time. These new sessions will share
23+// the same cluster information and connection cache, and may be easily
24+// handed into other methods and functions for organizing logic.
25+// Every session created must have its Close method called at the end
26+// of its life time, so its resources may be put back in the pool or
27+// collected, depending on the case.
28+//
29+// For more details, see the documentation for the types and methods.
30+//
31+package mgo
vendor/gopkg.in/mgo.v2/export_test.go+33−0
@@ -0,0 +1,33 @@
1+package mgo
2+
3+import (
4+ "time"
5+)
6+
7+func HackPingDelay(newDelay time.Duration) (restore func()) {
8+ globalMutex.Lock()
9+ defer globalMutex.Unlock()
10+
11+ oldDelay := pingDelay
12+ restore = func() {
13+ globalMutex.Lock()
14+ pingDelay = oldDelay
15+ globalMutex.Unlock()
16+ }
17+ pingDelay = newDelay
18+ return
19+}
20+
21+func HackSyncSocketTimeout(newTimeout time.Duration) (restore func()) {
22+ globalMutex.Lock()
23+ defer globalMutex.Unlock()
24+
25+ oldTimeout := syncSocketTimeout
26+ restore = func() {
27+ globalMutex.Lock()
28+ syncSocketTimeout = oldTimeout
29+ globalMutex.Unlock()
30+ }
31+ syncSocketTimeout = newTimeout
32+ return
33+}
vendor/gopkg.in/mgo.v2/gridfs.go+755−0
@@ -0,0 +1,755 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo
28+
29+import (
30+ "crypto/md5"
31+ "encoding/hex"
32+ "errors"
33+ "hash"
34+ "io"
35+ "os"
36+ "sync"
37+ "time"
38+
39+ "gopkg.in/mgo.v2/bson"
40+)
41+
42+type GridFS struct {
43+ Files *Collection
44+ Chunks *Collection
45+}
46+
47+type gfsFileMode int
48+
49+const (
50+ gfsClosed gfsFileMode = 0
51+ gfsReading gfsFileMode = 1
52+ gfsWriting gfsFileMode = 2
53+)
54+
55+type GridFile struct {
56+ m sync.Mutex
57+ c sync.Cond
58+ gfs *GridFS
59+ mode gfsFileMode
60+ err error
61+
62+ chunk int
63+ offset int64
64+
65+ wpending int
66+ wbuf []byte
67+ wsum hash.Hash
68+
69+ rbuf []byte
70+ rcache *gfsCachedChunk
71+
72+ doc gfsFile
73+}
74+
75+type gfsFile struct {
76+ Id interface{} "_id"
77+ ChunkSize int "chunkSize"
78+ UploadDate time.Time "uploadDate"
79+ Length int64 ",minsize"
80+ MD5 string
81+ Filename string ",omitempty"
82+ ContentType string "contentType,omitempty"
83+ Metadata *bson.Raw ",omitempty"
84+}
85+
86+type gfsChunk struct {
87+ Id interface{} "_id"
88+ FilesId interface{} "files_id"
89+ N int
90+ Data []byte
91+}
92+
93+type gfsCachedChunk struct {
94+ wait sync.Mutex
95+ n int
96+ data []byte
97+ err error
98+}
99+
100+func newGridFS(db *Database, prefix string) *GridFS {
101+ return &GridFS{db.C(prefix + ".files"), db.C(prefix + ".chunks")}
102+}
103+
104+func (gfs *GridFS) newFile() *GridFile {
105+ file := &GridFile{gfs: gfs}
106+ file.c.L = &file.m
107+ //runtime.SetFinalizer(file, finalizeFile)
108+ return file
109+}
110+
111+func finalizeFile(file *GridFile) {
112+ file.Close()
113+}
114+
115+// Create creates a new file with the provided name in the GridFS. If the file
116+// name already exists, a new version will be inserted with an up-to-date
117+// uploadDate that will cause it to be atomically visible to the Open and
118+// OpenId methods. If the file name is not important, an empty name may be
119+// provided and the file Id used instead.
120+//
121+// It's important to Close files whether they are being written to
122+// or read from, and to check the err result to ensure the operation
123+// completed successfully.
124+//
125+// A simple example inserting a new file:
126+//
127+// func check(err error) {
128+// if err != nil {
129+// panic(err.String())
130+// }
131+// }
132+// file, err := db.GridFS("fs").Create("myfile.txt")
133+// check(err)
134+// n, err := file.Write([]byte("Hello world!"))
135+// check(err)
136+// err = file.Close()
137+// check(err)
138+// fmt.Printf("%d bytes written\n", n)
139+//
140+// The io.Writer interface is implemented by *GridFile and may be used to
141+// help on the file creation. For example:
142+//
143+// file, err := db.GridFS("fs").Create("myfile.txt")
144+// check(err)
145+// messages, err := os.Open("/var/log/messages")
146+// check(err)
147+// defer messages.Close()
148+// err = io.Copy(file, messages)
149+// check(err)
150+// err = file.Close()
151+// check(err)
152+//
153+func (gfs *GridFS) Create(name string) (file *GridFile, err error) {
154+ file = gfs.newFile()
155+ file.mode = gfsWriting
156+ file.wsum = md5.New()
157+ file.doc = gfsFile{Id: bson.NewObjectId(), ChunkSize: 255 * 1024, Filename: name}
158+ return
159+}
160+
161+// OpenId returns the file with the provided id, for reading.
162+// If the file isn't found, err will be set to mgo.ErrNotFound.
163+//
164+// It's important to Close files whether they are being written to
165+// or read from, and to check the err result to ensure the operation
166+// completed successfully.
167+//
168+// The following example will print the first 8192 bytes from the file:
169+//
170+// func check(err error) {
171+// if err != nil {
172+// panic(err.String())
173+// }
174+// }
175+// file, err := db.GridFS("fs").OpenId(objid)
176+// check(err)
177+// b := make([]byte, 8192)
178+// n, err := file.Read(b)
179+// check(err)
180+// fmt.Println(string(b))
181+// check(err)
182+// err = file.Close()
183+// check(err)
184+// fmt.Printf("%d bytes read\n", n)
185+//
186+// The io.Reader interface is implemented by *GridFile and may be used to
187+// deal with it. As an example, the following snippet will dump the whole
188+// file into the standard output:
189+//
190+// file, err := db.GridFS("fs").OpenId(objid)
191+// check(err)
192+// err = io.Copy(os.Stdout, file)
193+// check(err)
194+// err = file.Close()
195+// check(err)
196+//
197+func (gfs *GridFS) OpenId(id interface{}) (file *GridFile, err error) {
198+ var doc gfsFile
199+ err = gfs.Files.Find(bson.M{"_id": id}).One(&doc)
200+ if err != nil {
201+ return
202+ }
203+ file = gfs.newFile()
204+ file.mode = gfsReading
205+ file.doc = doc
206+ return
207+}
208+
209+// Open returns the most recently uploaded file with the provided
210+// name, for reading. If the file isn't found, err will be set
211+// to mgo.ErrNotFound.
212+//
213+// It's important to Close files whether they are being written to
214+// or read from, and to check the err result to ensure the operation
215+// completed successfully.
216+//
217+// The following example will print the first 8192 bytes from the file:
218+//
219+// file, err := db.GridFS("fs").Open("myfile.txt")
220+// check(err)
221+// b := make([]byte, 8192)
222+// n, err := file.Read(b)
223+// check(err)
224+// fmt.Println(string(b))
225+// check(err)
226+// err = file.Close()
227+// check(err)
228+// fmt.Printf("%d bytes read\n", n)
229+//
230+// The io.Reader interface is implemented by *GridFile and may be used to
231+// deal with it. As an example, the following snippet will dump the whole
232+// file into the standard output:
233+//
234+// file, err := db.GridFS("fs").Open("myfile.txt")
235+// check(err)
236+// err = io.Copy(os.Stdout, file)
237+// check(err)
238+// err = file.Close()
239+// check(err)
240+//
241+func (gfs *GridFS) Open(name string) (file *GridFile, err error) {
242+ var doc gfsFile
243+ err = gfs.Files.Find(bson.M{"filename": name}).Sort("-uploadDate").One(&doc)
244+ if err != nil {
245+ return
246+ }
247+ file = gfs.newFile()
248+ file.mode = gfsReading
249+ file.doc = doc
250+ return
251+}
252+
253+// OpenNext opens the next file from iter for reading, sets *file to it,
254+// and returns true on the success case. If no more documents are available
255+// on iter or an error occurred, *file is set to nil and the result is false.
256+// Errors will be available via iter.Err().
257+//
258+// The iter parameter must be an iterator on the GridFS files collection.
259+// Using the GridFS.Find method is an easy way to obtain such an iterator,
260+// but any iterator on the collection will work.
261+//
262+// If the provided *file is non-nil, OpenNext will close it before attempting
263+// to iterate to the next element. This means that in a loop one only
264+// has to worry about closing files when breaking out of the loop early
265+// (break, return, or panic).
266+//
267+// For example:
268+//
269+// gfs := db.GridFS("fs")
270+// query := gfs.Find(nil).Sort("filename")
271+// iter := query.Iter()
272+// var f *mgo.GridFile
273+// for gfs.OpenNext(iter, &f) {
274+// fmt.Printf("Filename: %s\n", f.Name())
275+// }
276+// if iter.Close() != nil {
277+// panic(iter.Close())
278+// }
279+//
280+func (gfs *GridFS) OpenNext(iter *Iter, file **GridFile) bool {
281+ if *file != nil {
282+ // Ignoring the error here shouldn't be a big deal
283+ // as we're reading the file and the loop iteration
284+ // for this file is finished.
285+ _ = (*file).Close()
286+ }
287+ var doc gfsFile
288+ if !iter.Next(&doc) {
289+ *file = nil
290+ return false
291+ }
292+ f := gfs.newFile()
293+ f.mode = gfsReading
294+ f.doc = doc
295+ *file = f
296+ return true
297+}
298+
299+// Find runs query on GridFS's files collection and returns
300+// the resulting Query.
301+//
302+// This logic:
303+//
304+// gfs := db.GridFS("fs")
305+// iter := gfs.Find(nil).Iter()
306+//
307+// Is equivalent to:
308+//
309+// files := db.C("fs" + ".files")
310+// iter := files.Find(nil).Iter()
311+//
312+func (gfs *GridFS) Find(query interface{}) *Query {
313+ return gfs.Files.Find(query)
314+}
315+
316+// RemoveId deletes the file with the provided id from the GridFS.
317+func (gfs *GridFS) RemoveId(id interface{}) error {
318+ err := gfs.Files.Remove(bson.M{"_id": id})
319+ if err != nil {
320+ return err
321+ }
322+ _, err = gfs.Chunks.RemoveAll(bson.D{{"files_id", id}})
323+ return err
324+}
325+
326+type gfsDocId struct {
327+ Id interface{} "_id"
328+}
329+
330+// Remove deletes all files with the provided name from the GridFS.
331+func (gfs *GridFS) Remove(name string) (err error) {
332+ iter := gfs.Files.Find(bson.M{"filename": name}).Select(bson.M{"_id": 1}).Iter()
333+ var doc gfsDocId
334+ for iter.Next(&doc) {
335+ if e := gfs.RemoveId(doc.Id); e != nil {
336+ err = e
337+ }
338+ }
339+ if err == nil {
340+ err = iter.Close()
341+ }
342+ return err
343+}
344+
345+func (file *GridFile) assertMode(mode gfsFileMode) {
346+ switch file.mode {
347+ case mode:
348+ return
349+ case gfsWriting:
350+ panic("GridFile is open for writing")
351+ case gfsReading:
352+ panic("GridFile is open for reading")
353+ case gfsClosed:
354+ panic("GridFile is closed")
355+ default:
356+ panic("internal error: missing GridFile mode")
357+ }
358+}
359+
360+// SetChunkSize sets size of saved chunks. Once the file is written to, it
361+// will be split in blocks of that size and each block saved into an
362+// independent chunk document. The default chunk size is 256kb.
363+//
364+// It is a runtime error to call this function once the file has started
365+// being written to.
366+func (file *GridFile) SetChunkSize(bytes int) {
367+ file.assertMode(gfsWriting)
368+ debugf("GridFile %p: setting chunk size to %d", file, bytes)
369+ file.m.Lock()
370+ file.doc.ChunkSize = bytes
371+ file.m.Unlock()
372+}
373+
374+// Id returns the current file Id.
375+func (file *GridFile) Id() interface{} {
376+ return file.doc.Id
377+}
378+
379+// SetId changes the current file Id.
380+//
381+// It is a runtime error to call this function once the file has started
382+// being written to, or when the file is not open for writing.
383+func (file *GridFile) SetId(id interface{}) {
384+ file.assertMode(gfsWriting)
385+ file.m.Lock()
386+ file.doc.Id = id
387+ file.m.Unlock()
388+}
389+
390+// Name returns the optional file name. An empty string will be returned
391+// in case it is unset.
392+func (file *GridFile) Name() string {
393+ return file.doc.Filename
394+}
395+
396+// SetName changes the optional file name. An empty string may be used to
397+// unset it.
398+//
399+// It is a runtime error to call this function when the file is not open
400+// for writing.
401+func (file *GridFile) SetName(name string) {
402+ file.assertMode(gfsWriting)
403+ file.m.Lock()
404+ file.doc.Filename = name
405+ file.m.Unlock()
406+}
407+
408+// ContentType returns the optional file content type. An empty string will be
409+// returned in case it is unset.
410+func (file *GridFile) ContentType() string {
411+ return file.doc.ContentType
412+}
413+
414+// ContentType changes the optional file content type. An empty string may be
415+// used to unset it.
416+//
417+// It is a runtime error to call this function when the file is not open
418+// for writing.
419+func (file *GridFile) SetContentType(ctype string) {
420+ file.assertMode(gfsWriting)
421+ file.m.Lock()
422+ file.doc.ContentType = ctype
423+ file.m.Unlock()
424+}
425+
426+// GetMeta unmarshals the optional "metadata" field associated with the
427+// file into the result parameter. The meaning of keys under that field
428+// is user-defined. For example:
429+//
430+// result := struct{ INode int }{}
431+// err = file.GetMeta(&result)
432+// if err != nil {
433+// panic(err.String())
434+// }
435+// fmt.Printf("inode: %d\n", result.INode)
436+//
437+func (file *GridFile) GetMeta(result interface{}) (err error) {
438+ file.m.Lock()
439+ if file.doc.Metadata != nil {
440+ err = bson.Unmarshal(file.doc.Metadata.Data, result)
441+ }
442+ file.m.Unlock()
443+ return
444+}
445+
446+// SetMeta changes the optional "metadata" field associated with the
447+// file. The meaning of keys under that field is user-defined.
448+// For example:
449+//
450+// file.SetMeta(bson.M{"inode": inode})
451+//
452+// It is a runtime error to call this function when the file is not open
453+// for writing.
454+func (file *GridFile) SetMeta(metadata interface{}) {
455+ file.assertMode(gfsWriting)
456+ data, err := bson.Marshal(metadata)
457+ file.m.Lock()
458+ if err != nil && file.err == nil {
459+ file.err = err
460+ } else {
461+ file.doc.Metadata = &bson.Raw{Data: data}
462+ }
463+ file.m.Unlock()
464+}
465+
466+// Size returns the file size in bytes.
467+func (file *GridFile) Size() (bytes int64) {
468+ file.m.Lock()
469+ bytes = file.doc.Length
470+ file.m.Unlock()
471+ return
472+}
473+
474+// MD5 returns the file MD5 as a hex-encoded string.
475+func (file *GridFile) MD5() (md5 string) {
476+ return file.doc.MD5
477+}
478+
479+// UploadDate returns the file upload time.
480+func (file *GridFile) UploadDate() time.Time {
481+ return file.doc.UploadDate
482+}
483+
484+// SetUploadDate changes the file upload time.
485+//
486+// It is a runtime error to call this function when the file is not open
487+// for writing.
488+func (file *GridFile) SetUploadDate(t time.Time) {
489+ file.assertMode(gfsWriting)
490+ file.m.Lock()
491+ file.doc.UploadDate = t
492+ file.m.Unlock()
493+}
494+
495+// Close flushes any pending changes in case the file is being written
496+// to, waits for any background operations to finish, and closes the file.
497+//
498+// It's important to Close files whether they are being written to
499+// or read from, and to check the err result to ensure the operation
500+// completed successfully.
501+func (file *GridFile) Close() (err error) {
502+ file.m.Lock()
503+ defer file.m.Unlock()
504+ if file.mode == gfsWriting {
505+ if len(file.wbuf) > 0 && file.err == nil {
506+ file.insertChunk(file.wbuf)
507+ file.wbuf = file.wbuf[0:0]
508+ }
509+ file.completeWrite()
510+ } else if file.mode == gfsReading && file.rcache != nil {
511+ file.rcache.wait.Lock()
512+ file.rcache = nil
513+ }
514+ file.mode = gfsClosed
515+ debugf("GridFile %p: closed", file)
516+ return file.err
517+}
518+
519+func (file *GridFile) completeWrite() {
520+ for file.wpending > 0 {
521+ debugf("GridFile %p: waiting for %d pending chunks to complete file write", file, file.wpending)
522+ file.c.Wait()
523+ }
524+ if file.err == nil {
525+ hexsum := hex.EncodeToString(file.wsum.Sum(nil))
526+ if file.doc.UploadDate.IsZero() {
527+ file.doc.UploadDate = bson.Now()
528+ }
529+ file.doc.MD5 = hexsum
530+ file.err = file.gfs.Files.Insert(file.doc)
531+ file.gfs.Chunks.EnsureIndexKey("files_id", "n")
532+ }
533+ if file.err != nil {
534+ file.gfs.Chunks.RemoveAll(bson.D{{"files_id", file.doc.Id}})
535+ }
536+}
537+
538+// Abort cancels an in-progress write, preventing the file from being
539+// automically created and ensuring previously written chunks are
540+// removed when the file is closed.
541+//
542+// It is a runtime error to call Abort when the file was not opened
543+// for writing.
544+func (file *GridFile) Abort() {
545+ if file.mode != gfsWriting {
546+ panic("file.Abort must be called on file opened for writing")
547+ }
548+ file.err = errors.New("write aborted")
549+}
550+
551+// Write writes the provided data to the file and returns the
552+// number of bytes written and an error in case something
553+// wrong happened.
554+//
555+// The file will internally cache the data so that all but the last
556+// chunk sent to the database have the size defined by SetChunkSize.
557+// This also means that errors may be deferred until a future call
558+// to Write or Close.
559+//
560+// The parameters and behavior of this function turn the file
561+// into an io.Writer.
562+func (file *GridFile) Write(data []byte) (n int, err error) {
563+ file.assertMode(gfsWriting)
564+ file.m.Lock()
565+ debugf("GridFile %p: writing %d bytes", file, len(data))
566+ defer file.m.Unlock()
567+
568+ if file.err != nil {
569+ return 0, file.err
570+ }
571+
572+ n = len(data)
573+ file.doc.Length += int64(n)
574+ chunkSize := file.doc.ChunkSize
575+
576+ if len(file.wbuf)+len(data) < chunkSize {
577+ file.wbuf = append(file.wbuf, data...)
578+ return
579+ }
580+
581+ // First, flush file.wbuf complementing with data.
582+ if len(file.wbuf) > 0 {
583+ missing := chunkSize - len(file.wbuf)
584+ if missing > len(data) {
585+ missing = len(data)
586+ }
587+ file.wbuf = append(file.wbuf, data[:missing]...)
588+ data = data[missing:]
589+ file.insertChunk(file.wbuf)
590+ file.wbuf = file.wbuf[0:0]
591+ }
592+
593+ // Then, flush all chunks from data without copying.
594+ for len(data) > chunkSize {
595+ size := chunkSize
596+ if size > len(data) {
597+ size = len(data)
598+ }
599+ file.insertChunk(data[:size])
600+ data = data[size:]
601+ }
602+
603+ // And append the rest for a future call.
604+ file.wbuf = append(file.wbuf, data...)
605+
606+ return n, file.err
607+}
608+
609+func (file *GridFile) insertChunk(data []byte) {
610+ n := file.chunk
611+ file.chunk++
612+ debugf("GridFile %p: adding to checksum: %q", file, string(data))
613+ file.wsum.Write(data)
614+
615+ for file.doc.ChunkSize*file.wpending >= 1024*1024 {
616+ // Hold on.. we got a MB pending.
617+ file.c.Wait()
618+ if file.err != nil {
619+ return
620+ }
621+ }
622+
623+ file.wpending++
624+
625+ debugf("GridFile %p: inserting chunk %d with %d bytes", file, n, len(data))
626+
627+ // We may not own the memory of data, so rather than
628+ // simply copying it, we'll marshal the document ahead of time.
629+ data, err := bson.Marshal(gfsChunk{bson.NewObjectId(), file.doc.Id, n, data})
630+ if err != nil {
631+ file.err = err
632+ return
633+ }
634+
635+ go func() {
636+ err := file.gfs.Chunks.Insert(bson.Raw{Data: data})
637+ file.m.Lock()
638+ file.wpending--
639+ if err != nil && file.err == nil {
640+ file.err = err
641+ }
642+ file.c.Broadcast()
643+ file.m.Unlock()
644+ }()
645+}
646+
647+// Seek sets the offset for the next Read or Write on file to
648+// offset, interpreted according to whence: 0 means relative to
649+// the origin of the file, 1 means relative to the current offset,
650+// and 2 means relative to the end. It returns the new offset and
651+// an error, if any.
652+func (file *GridFile) Seek(offset int64, whence int) (pos int64, err error) {
653+ file.m.Lock()
654+ debugf("GridFile %p: seeking for %s (whence=%d)", file, offset, whence)
655+ defer file.m.Unlock()
656+ switch whence {
657+ case os.SEEK_SET:
658+ case os.SEEK_CUR:
659+ offset += file.offset
660+ case os.SEEK_END:
661+ offset += file.doc.Length
662+ default:
663+ panic("unsupported whence value")
664+ }
665+ if offset > file.doc.Length {
666+ return file.offset, errors.New("seek past end of file")
667+ }
668+ if offset == file.doc.Length {
669+ // If we're seeking to the end of the file,
670+ // no need to read anything. This enables
671+ // a client to find the size of the file using only the
672+ // io.ReadSeeker interface with low overhead.
673+ file.offset = offset
674+ return file.offset, nil
675+ }
676+ chunk := int(offset / int64(file.doc.ChunkSize))
677+ if chunk+1 == file.chunk && offset >= file.offset {
678+ file.rbuf = file.rbuf[int(offset-file.offset):]
679+ file.offset = offset
680+ return file.offset, nil
681+ }
682+ file.offset = offset
683+ file.chunk = chunk
684+ file.rbuf = nil
685+ file.rbuf, err = file.getChunk()
686+ if err == nil {
687+ file.rbuf = file.rbuf[int(file.offset-int64(chunk)*int64(file.doc.ChunkSize)):]
688+ }
689+ return file.offset, err
690+}
691+
692+// Read reads into b the next available data from the file and
693+// returns the number of bytes written and an error in case
694+// something wrong happened. At the end of the file, n will
695+// be zero and err will be set to os.EOF.
696+//
697+// The parameters and behavior of this function turn the file
698+// into an io.Reader.
699+func (file *GridFile) Read(b []byte) (n int, err error) {
700+ file.assertMode(gfsReading)
701+ file.m.Lock()
702+ debugf("GridFile %p: reading at offset %d into buffer of length %d", file, file.offset, len(b))
703+ defer file.m.Unlock()
704+ if file.offset == file.doc.Length {
705+ return 0, io.EOF
706+ }
707+ for err == nil {
708+ i := copy(b, file.rbuf)
709+ n += i
710+ file.offset += int64(i)
711+ file.rbuf = file.rbuf[i:]
712+ if i == len(b) || file.offset == file.doc.Length {
713+ break
714+ }
715+ b = b[i:]
716+ file.rbuf, err = file.getChunk()
717+ }
718+ return n, err
719+}
720+
721+func (file *GridFile) getChunk() (data []byte, err error) {
722+ cache := file.rcache
723+ file.rcache = nil
724+ if cache != nil && cache.n == file.chunk {
725+ debugf("GridFile %p: Getting chunk %d from cache", file, file.chunk)
726+ cache.wait.Lock()
727+ data, err = cache.data, cache.err
728+ } else {
729+ debugf("GridFile %p: Fetching chunk %d", file, file.chunk)
730+ var doc gfsChunk
731+ err = file.gfs.Chunks.Find(bson.D{{"files_id", file.doc.Id}, {"n", file.chunk}}).One(&doc)
732+ data = doc.Data
733+ }
734+ file.chunk++
735+ if int64(file.chunk)*int64(file.doc.ChunkSize) < file.doc.Length {
736+ // Read the next one in background.
737+ cache = &gfsCachedChunk{n: file.chunk}
738+ cache.wait.Lock()
739+ debugf("GridFile %p: Scheduling chunk %d for background caching", file, file.chunk)
740+ // Clone the session to avoid having it closed in between.
741+ chunks := file.gfs.Chunks
742+ session := chunks.Database.Session.Clone()
743+ go func(id interface{}, n int) {
744+ defer session.Close()
745+ chunks = chunks.With(session)
746+ var doc gfsChunk
747+ cache.err = chunks.Find(bson.D{{"files_id", id}, {"n", n}}).One(&doc)
748+ cache.data = doc.Data
749+ cache.wait.Unlock()
750+ }(file.doc.Id, file.chunk)
751+ file.rcache = cache
752+ }
753+ debugf("Returning err: %#v", err)
754+ return
755+}
vendor/gopkg.in/mgo.v2/gridfs_test.go+708−0
@@ -0,0 +1,708 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo_test
28+
29+import (
30+ "io"
31+ "os"
32+ "time"
33+
34+ . "gopkg.in/check.v1"
35+ "gopkg.in/mgo.v2"
36+ "gopkg.in/mgo.v2/bson"
37+)
38+
39+func (s *S) TestGridFSCreate(c *C) {
40+ session, err := mgo.Dial("localhost:40011")
41+ c.Assert(err, IsNil)
42+ defer session.Close()
43+
44+ db := session.DB("mydb")
45+
46+ before := bson.Now()
47+
48+ gfs := db.GridFS("fs")
49+ file, err := gfs.Create("")
50+ c.Assert(err, IsNil)
51+
52+ n, err := file.Write([]byte("some data"))
53+ c.Assert(err, IsNil)
54+ c.Assert(n, Equals, 9)
55+
56+ err = file.Close()
57+ c.Assert(err, IsNil)
58+
59+ after := bson.Now()
60+
61+ // Check the file information.
62+ result := M{}
63+ err = db.C("fs.files").Find(nil).One(result)
64+ c.Assert(err, IsNil)
65+
66+ fileId, ok := result["_id"].(bson.ObjectId)
67+ c.Assert(ok, Equals, true)
68+ c.Assert(fileId.Valid(), Equals, true)
69+ result["_id"] = "<id>"
70+
71+ ud, ok := result["uploadDate"].(time.Time)
72+ c.Assert(ok, Equals, true)
73+ c.Assert(ud.After(before) && ud.Before(after), Equals, true)
74+ result["uploadDate"] = "<timestamp>"
75+
76+ expected := M{
77+ "_id": "<id>",
78+ "length": 9,
79+ "chunkSize": 255 * 1024,
80+ "uploadDate": "<timestamp>",
81+ "md5": "1e50210a0202497fb79bc38b6ade6c34",
82+ }
83+ c.Assert(result, DeepEquals, expected)
84+
85+ // Check the chunk.
86+ result = M{}
87+ err = db.C("fs.chunks").Find(nil).One(result)
88+ c.Assert(err, IsNil)
89+
90+ chunkId, ok := result["_id"].(bson.ObjectId)
91+ c.Assert(ok, Equals, true)
92+ c.Assert(chunkId.Valid(), Equals, true)
93+ result["_id"] = "<id>"
94+
95+ expected = M{
96+ "_id": "<id>",
97+ "files_id": fileId,
98+ "n": 0,
99+ "data": []byte("some data"),
100+ }
101+ c.Assert(result, DeepEquals, expected)
102+
103+ // Check that an index was created.
104+ indexes, err := db.C("fs.chunks").Indexes()
105+ c.Assert(err, IsNil)
106+ c.Assert(len(indexes), Equals, 2)
107+ c.Assert(indexes[1].Key, DeepEquals, []string{"files_id", "n"})
108+}
109+
110+func (s *S) TestGridFSFileDetails(c *C) {
111+ session, err := mgo.Dial("localhost:40011")
112+ c.Assert(err, IsNil)
113+ defer session.Close()
114+
115+ db := session.DB("mydb")
116+
117+ gfs := db.GridFS("fs")
118+
119+ file, err := gfs.Create("myfile1.txt")
120+ c.Assert(err, IsNil)
121+
122+ n, err := file.Write([]byte("some"))
123+ c.Assert(err, IsNil)
124+ c.Assert(n, Equals, 4)
125+
126+ c.Assert(file.Size(), Equals, int64(4))
127+
128+ n, err = file.Write([]byte(" data"))
129+ c.Assert(err, IsNil)
130+ c.Assert(n, Equals, 5)
131+
132+ c.Assert(file.Size(), Equals, int64(9))
133+
134+ id, _ := file.Id().(bson.ObjectId)
135+ c.Assert(id.Valid(), Equals, true)
136+ c.Assert(file.Name(), Equals, "myfile1.txt")
137+ c.Assert(file.ContentType(), Equals, "")
138+
139+ var info interface{}
140+ err = file.GetMeta(&info)
141+ c.Assert(err, IsNil)
142+ c.Assert(info, IsNil)
143+
144+ file.SetId("myid")
145+ file.SetName("myfile2.txt")
146+ file.SetContentType("text/plain")
147+ file.SetMeta(M{"any": "thing"})
148+
149+ c.Assert(file.Id(), Equals, "myid")
150+ c.Assert(file.Name(), Equals, "myfile2.txt")
151+ c.Assert(file.ContentType(), Equals, "text/plain")
152+
153+ err = file.GetMeta(&info)
154+ c.Assert(err, IsNil)
155+ c.Assert(info, DeepEquals, bson.M{"any": "thing"})
156+
157+ err = file.Close()
158+ c.Assert(err, IsNil)
159+
160+ c.Assert(file.MD5(), Equals, "1e50210a0202497fb79bc38b6ade6c34")
161+
162+ ud := file.UploadDate()
163+ now := time.Now()
164+ c.Assert(ud.Before(now), Equals, true)
165+ c.Assert(ud.After(now.Add(-3*time.Second)), Equals, true)
166+
167+ result := M{}
168+ err = db.C("fs.files").Find(nil).One(result)
169+ c.Assert(err, IsNil)
170+
171+ result["uploadDate"] = "<timestamp>"
172+
173+ expected := M{
174+ "_id": "myid",
175+ "length": 9,
176+ "chunkSize": 255 * 1024,
177+ "uploadDate": "<timestamp>",
178+ "md5": "1e50210a0202497fb79bc38b6ade6c34",
179+ "filename": "myfile2.txt",
180+ "contentType": "text/plain",
181+ "metadata": M{"any": "thing"},
182+ }
183+ c.Assert(result, DeepEquals, expected)
184+}
185+
186+func (s *S) TestGridFSSetUploadDate(c *C) {
187+ session, err := mgo.Dial("localhost:40011")
188+ c.Assert(err, IsNil)
189+ defer session.Close()
190+
191+ db := session.DB("mydb")
192+
193+ gfs := db.GridFS("fs")
194+ file, err := gfs.Create("")
195+ c.Assert(err, IsNil)
196+
197+ t := time.Date(2014, 1, 1, 1, 1, 1, 0, time.Local)
198+ file.SetUploadDate(t)
199+
200+ err = file.Close()
201+ c.Assert(err, IsNil)
202+
203+ // Check the file information.
204+ result := M{}
205+ err = db.C("fs.files").Find(nil).One(result)
206+ c.Assert(err, IsNil)
207+
208+ ud := result["uploadDate"].(time.Time)
209+ if !ud.Equal(t) {
210+ c.Fatalf("want upload date %s, got %s", t, ud)
211+ }
212+}
213+
214+func (s *S) TestGridFSCreateWithChunking(c *C) {
215+ session, err := mgo.Dial("localhost:40011")
216+ c.Assert(err, IsNil)
217+ defer session.Close()
218+
219+ db := session.DB("mydb")
220+
221+ gfs := db.GridFS("fs")
222+
223+ file, err := gfs.Create("")
224+ c.Assert(err, IsNil)
225+
226+ file.SetChunkSize(5)
227+
228+ // Smaller than the chunk size.
229+ n, err := file.Write([]byte("abc"))
230+ c.Assert(err, IsNil)
231+ c.Assert(n, Equals, 3)
232+
233+ // Boundary in the middle.
234+ n, err = file.Write([]byte("defg"))
235+ c.Assert(err, IsNil)
236+ c.Assert(n, Equals, 4)
237+
238+ // Boundary at the end.
239+ n, err = file.Write([]byte("hij"))
240+ c.Assert(err, IsNil)
241+ c.Assert(n, Equals, 3)
242+
243+ // Larger than the chunk size, with 3 chunks.
244+ n, err = file.Write([]byte("klmnopqrstuv"))
245+ c.Assert(err, IsNil)
246+ c.Assert(n, Equals, 12)
247+
248+ err = file.Close()
249+ c.Assert(err, IsNil)
250+
251+ // Check the file information.
252+ result := M{}
253+ err = db.C("fs.files").Find(nil).One(result)
254+ c.Assert(err, IsNil)
255+
256+ fileId, _ := result["_id"].(bson.ObjectId)
257+ c.Assert(fileId.Valid(), Equals, true)
258+ result["_id"] = "<id>"
259+ result["uploadDate"] = "<timestamp>"
260+
261+ expected := M{
262+ "_id": "<id>",
263+ "length": 22,
264+ "chunkSize": 5,
265+ "uploadDate": "<timestamp>",
266+ "md5": "44a66044834cbe55040089cabfc102d5",
267+ }
268+ c.Assert(result, DeepEquals, expected)
269+
270+ // Check the chunks.
271+ iter := db.C("fs.chunks").Find(nil).Sort("n").Iter()
272+ dataChunks := []string{"abcde", "fghij", "klmno", "pqrst", "uv"}
273+ for i := 0; ; i++ {
274+ result = M{}
275+ if !iter.Next(result) {
276+ if i != 5 {
277+ c.Fatalf("Expected 5 chunks, got %d", i)
278+ }
279+ break
280+ }
281+ c.Assert(iter.Close(), IsNil)
282+
283+ result["_id"] = "<id>"
284+
285+ expected = M{
286+ "_id": "<id>",
287+ "files_id": fileId,
288+ "n": i,
289+ "data": []byte(dataChunks[i]),
290+ }
291+ c.Assert(result, DeepEquals, expected)
292+ }
293+}
294+
295+func (s *S) TestGridFSAbort(c *C) {
296+ session, err := mgo.Dial("localhost:40011")
297+ c.Assert(err, IsNil)
298+ defer session.Close()
299+
300+ db := session.DB("mydb")
301+
302+ gfs := db.GridFS("fs")
303+ file, err := gfs.Create("")
304+ c.Assert(err, IsNil)
305+
306+ file.SetChunkSize(5)
307+
308+ n, err := file.Write([]byte("some data"))
309+ c.Assert(err, IsNil)
310+ c.Assert(n, Equals, 9)
311+
312+ var count int
313+ for i := 0; i < 10; i++ {
314+ count, err = db.C("fs.chunks").Count()
315+ if count > 0 || err != nil {
316+ break
317+ }
318+ }
319+ c.Assert(err, IsNil)
320+ c.Assert(count, Equals, 1)
321+
322+ file.Abort()
323+
324+ err = file.Close()
325+ c.Assert(err, ErrorMatches, "write aborted")
326+
327+ count, err = db.C("fs.chunks").Count()
328+ c.Assert(err, IsNil)
329+ c.Assert(count, Equals, 0)
330+}
331+
332+func (s *S) TestGridFSCloseConflict(c *C) {
333+ session, err := mgo.Dial("localhost:40011")
334+ c.Assert(err, IsNil)
335+ defer session.Close()
336+
337+ db := session.DB("mydb")
338+
339+ db.C("fs.files").EnsureIndex(mgo.Index{Key: []string{"filename"}, Unique: true})
340+
341+ // For a closing-time conflict
342+ err = db.C("fs.files").Insert(M{"filename": "foo.txt"})
343+ c.Assert(err, IsNil)
344+
345+ gfs := db.GridFS("fs")
346+ file, err := gfs.Create("foo.txt")
347+ c.Assert(err, IsNil)
348+
349+ _, err = file.Write([]byte("some data"))
350+ c.Assert(err, IsNil)
351+
352+ err = file.Close()
353+ c.Assert(mgo.IsDup(err), Equals, true)
354+
355+ count, err := db.C("fs.chunks").Count()
356+ c.Assert(err, IsNil)
357+ c.Assert(count, Equals, 0)
358+}
359+
360+func (s *S) TestGridFSOpenNotFound(c *C) {
361+ session, err := mgo.Dial("localhost:40011")
362+ c.Assert(err, IsNil)
363+ defer session.Close()
364+
365+ db := session.DB("mydb")
366+
367+ gfs := db.GridFS("fs")
368+ file, err := gfs.OpenId("non-existent")
369+ c.Assert(err == mgo.ErrNotFound, Equals, true)
370+ c.Assert(file, IsNil)
371+
372+ file, err = gfs.Open("non-existent")
373+ c.Assert(err == mgo.ErrNotFound, Equals, true)
374+ c.Assert(file, IsNil)
375+}
376+
377+func (s *S) TestGridFSReadAll(c *C) {
378+ session, err := mgo.Dial("localhost:40011")
379+ c.Assert(err, IsNil)
380+ defer session.Close()
381+
382+ db := session.DB("mydb")
383+
384+ gfs := db.GridFS("fs")
385+ file, err := gfs.Create("")
386+ c.Assert(err, IsNil)
387+ id := file.Id()
388+
389+ file.SetChunkSize(5)
390+
391+ n, err := file.Write([]byte("abcdefghijklmnopqrstuv"))
392+ c.Assert(err, IsNil)
393+ c.Assert(n, Equals, 22)
394+
395+ err = file.Close()
396+ c.Assert(err, IsNil)
397+
398+ file, err = gfs.OpenId(id)
399+ c.Assert(err, IsNil)
400+
401+ b := make([]byte, 30)
402+ n, err = file.Read(b)
403+ c.Assert(n, Equals, 22)
404+ c.Assert(err, IsNil)
405+
406+ n, err = file.Read(b)
407+ c.Assert(n, Equals, 0)
408+ c.Assert(err == io.EOF, Equals, true)
409+
410+ err = file.Close()
411+ c.Assert(err, IsNil)
412+}
413+
414+func (s *S) TestGridFSReadChunking(c *C) {
415+ session, err := mgo.Dial("localhost:40011")
416+ c.Assert(err, IsNil)
417+ defer session.Close()
418+
419+ db := session.DB("mydb")
420+
421+ gfs := db.GridFS("fs")
422+
423+ file, err := gfs.Create("")
424+ c.Assert(err, IsNil)
425+
426+ id := file.Id()
427+
428+ file.SetChunkSize(5)
429+
430+ n, err := file.Write([]byte("abcdefghijklmnopqrstuv"))
431+ c.Assert(err, IsNil)
432+ c.Assert(n, Equals, 22)
433+
434+ err = file.Close()
435+ c.Assert(err, IsNil)
436+
437+ file, err = gfs.OpenId(id)
438+ c.Assert(err, IsNil)
439+
440+ b := make([]byte, 30)
441+
442+ // Smaller than the chunk size.
443+ n, err = file.Read(b[:3])
444+ c.Assert(err, IsNil)
445+ c.Assert(n, Equals, 3)
446+ c.Assert(b[:3], DeepEquals, []byte("abc"))
447+
448+ // Boundary in the middle.
449+ n, err = file.Read(b[:4])
450+ c.Assert(err, IsNil)
451+ c.Assert(n, Equals, 4)
452+ c.Assert(b[:4], DeepEquals, []byte("defg"))
453+
454+ // Boundary at the end.
455+ n, err = file.Read(b[:3])
456+ c.Assert(err, IsNil)
457+ c.Assert(n, Equals, 3)
458+ c.Assert(b[:3], DeepEquals, []byte("hij"))
459+
460+ // Larger than the chunk size, with 3 chunks.
461+ n, err = file.Read(b)
462+ c.Assert(err, IsNil)
463+ c.Assert(n, Equals, 12)
464+ c.Assert(b[:12], DeepEquals, []byte("klmnopqrstuv"))
465+
466+ n, err = file.Read(b)
467+ c.Assert(n, Equals, 0)
468+ c.Assert(err == io.EOF, Equals, true)
469+
470+ err = file.Close()
471+ c.Assert(err, IsNil)
472+}
473+
474+func (s *S) TestGridFSOpen(c *C) {
475+ session, err := mgo.Dial("localhost:40011")
476+ c.Assert(err, IsNil)
477+ defer session.Close()
478+
479+ db := session.DB("mydb")
480+
481+ gfs := db.GridFS("fs")
482+
483+ file, err := gfs.Create("myfile.txt")
484+ c.Assert(err, IsNil)
485+ file.Write([]byte{'1'})
486+ file.Close()
487+
488+ file, err = gfs.Create("myfile.txt")
489+ c.Assert(err, IsNil)
490+ file.Write([]byte{'2'})
491+ file.Close()
492+
493+ file, err = gfs.Open("myfile.txt")
494+ c.Assert(err, IsNil)
495+ defer file.Close()
496+
497+ var b [1]byte
498+
499+ _, err = file.Read(b[:])
500+ c.Assert(err, IsNil)
501+ c.Assert(string(b[:]), Equals, "2")
502+}
503+
504+func (s *S) TestGridFSSeek(c *C) {
505+ session, err := mgo.Dial("localhost:40011")
506+ c.Assert(err, IsNil)
507+ defer session.Close()
508+
509+ db := session.DB("mydb")
510+
511+ gfs := db.GridFS("fs")
512+ file, err := gfs.Create("")
513+ c.Assert(err, IsNil)
514+ id := file.Id()
515+
516+ file.SetChunkSize(5)
517+
518+ n, err := file.Write([]byte("abcdefghijklmnopqrstuv"))
519+ c.Assert(err, IsNil)
520+ c.Assert(n, Equals, 22)
521+
522+ err = file.Close()
523+ c.Assert(err, IsNil)
524+
525+ b := make([]byte, 5)
526+
527+ file, err = gfs.OpenId(id)
528+ c.Assert(err, IsNil)
529+
530+ o, err := file.Seek(3, os.SEEK_SET)
531+ c.Assert(err, IsNil)
532+ c.Assert(o, Equals, int64(3))
533+ _, err = file.Read(b)
534+ c.Assert(err, IsNil)
535+ c.Assert(b, DeepEquals, []byte("defgh"))
536+
537+ o, err = file.Seek(5, os.SEEK_CUR)
538+ c.Assert(err, IsNil)
539+ c.Assert(o, Equals, int64(13))
540+ _, err = file.Read(b)
541+ c.Assert(err, IsNil)
542+ c.Assert(b, DeepEquals, []byte("nopqr"))
543+
544+ o, err = file.Seek(0, os.SEEK_END)
545+ c.Assert(err, IsNil)
546+ c.Assert(o, Equals, int64(22))
547+ n, err = file.Read(b)
548+ c.Assert(err, Equals, io.EOF)
549+ c.Assert(n, Equals, 0)
550+
551+ o, err = file.Seek(-10, os.SEEK_END)
552+ c.Assert(err, IsNil)
553+ c.Assert(o, Equals, int64(12))
554+ _, err = file.Read(b)
555+ c.Assert(err, IsNil)
556+ c.Assert(b, DeepEquals, []byte("mnopq"))
557+
558+ o, err = file.Seek(8, os.SEEK_SET)
559+ c.Assert(err, IsNil)
560+ c.Assert(o, Equals, int64(8))
561+ _, err = file.Read(b)
562+ c.Assert(err, IsNil)
563+ c.Assert(b, DeepEquals, []byte("ijklm"))
564+
565+ // Trivial seek forward within same chunk. Already
566+ // got the data, shouldn't touch the database.
567+ sent := mgo.GetStats().SentOps
568+ o, err = file.Seek(1, os.SEEK_CUR)
569+ c.Assert(err, IsNil)
570+ c.Assert(o, Equals, int64(14))
571+ c.Assert(mgo.GetStats().SentOps, Equals, sent)
572+ _, err = file.Read(b)
573+ c.Assert(err, IsNil)
574+ c.Assert(b, DeepEquals, []byte("opqrs"))
575+
576+ // Try seeking past end of file.
577+ file.Seek(3, os.SEEK_SET)
578+ o, err = file.Seek(23, os.SEEK_SET)
579+ c.Assert(err, ErrorMatches, "seek past end of file")
580+ c.Assert(o, Equals, int64(3))
581+}
582+
583+func (s *S) TestGridFSRemoveId(c *C) {
584+ session, err := mgo.Dial("localhost:40011")
585+ c.Assert(err, IsNil)
586+ defer session.Close()
587+
588+ db := session.DB("mydb")
589+
590+ gfs := db.GridFS("fs")
591+
592+ file, err := gfs.Create("myfile.txt")
593+ c.Assert(err, IsNil)
594+ file.Write([]byte{'1'})
595+ file.Close()
596+
597+ file, err = gfs.Create("myfile.txt")
598+ c.Assert(err, IsNil)
599+ file.Write([]byte{'2'})
600+ id := file.Id()
601+ file.Close()
602+
603+ err = gfs.RemoveId(id)
604+ c.Assert(err, IsNil)
605+
606+ file, err = gfs.Open("myfile.txt")
607+ c.Assert(err, IsNil)
608+ defer file.Close()
609+
610+ var b [1]byte
611+
612+ _, err = file.Read(b[:])
613+ c.Assert(err, IsNil)
614+ c.Assert(string(b[:]), Equals, "1")
615+
616+ n, err := db.C("fs.chunks").Find(M{"files_id": id}).Count()
617+ c.Assert(err, IsNil)
618+ c.Assert(n, Equals, 0)
619+}
620+
621+func (s *S) TestGridFSRemove(c *C) {
622+ session, err := mgo.Dial("localhost:40011")
623+ c.Assert(err, IsNil)
624+ defer session.Close()
625+
626+ db := session.DB("mydb")
627+
628+ gfs := db.GridFS("fs")
629+
630+ file, err := gfs.Create("myfile.txt")
631+ c.Assert(err, IsNil)
632+ file.Write([]byte{'1'})
633+ file.Close()
634+
635+ file, err = gfs.Create("myfile.txt")
636+ c.Assert(err, IsNil)
637+ file.Write([]byte{'2'})
638+ file.Close()
639+
640+ err = gfs.Remove("myfile.txt")
641+ c.Assert(err, IsNil)
642+
643+ _, err = gfs.Open("myfile.txt")
644+ c.Assert(err == mgo.ErrNotFound, Equals, true)
645+
646+ n, err := db.C("fs.chunks").Find(nil).Count()
647+ c.Assert(err, IsNil)
648+ c.Assert(n, Equals, 0)
649+}
650+
651+func (s *S) TestGridFSOpenNext(c *C) {
652+ session, err := mgo.Dial("localhost:40011")
653+ c.Assert(err, IsNil)
654+ defer session.Close()
655+
656+ db := session.DB("mydb")
657+
658+ gfs := db.GridFS("fs")
659+
660+ file, err := gfs.Create("myfile1.txt")
661+ c.Assert(err, IsNil)
662+ file.Write([]byte{'1'})
663+ file.Close()
664+
665+ file, err = gfs.Create("myfile2.txt")
666+ c.Assert(err, IsNil)
667+ file.Write([]byte{'2'})
668+ file.Close()
669+
670+ var f *mgo.GridFile
671+ var b [1]byte
672+
673+ iter := gfs.Find(nil).Sort("-filename").Iter()
674+
675+ ok := gfs.OpenNext(iter, &f)
676+ c.Assert(ok, Equals, true)
677+ c.Check(f.Name(), Equals, "myfile2.txt")
678+
679+ _, err = f.Read(b[:])
680+ c.Assert(err, IsNil)
681+ c.Assert(string(b[:]), Equals, "2")
682+
683+ ok = gfs.OpenNext(iter, &f)
684+ c.Assert(ok, Equals, true)
685+ c.Check(f.Name(), Equals, "myfile1.txt")
686+
687+ _, err = f.Read(b[:])
688+ c.Assert(err, IsNil)
689+ c.Assert(string(b[:]), Equals, "1")
690+
691+ ok = gfs.OpenNext(iter, &f)
692+ c.Assert(ok, Equals, false)
693+ c.Assert(iter.Close(), IsNil)
694+ c.Assert(f, IsNil)
695+
696+ // Do it again with a more restrictive query to make sure
697+ // it's actually taken into account.
698+ iter = gfs.Find(bson.M{"filename": "myfile1.txt"}).Iter()
699+
700+ ok = gfs.OpenNext(iter, &f)
701+ c.Assert(ok, Equals, true)
702+ c.Check(f.Name(), Equals, "myfile1.txt")
703+
704+ ok = gfs.OpenNext(iter, &f)
705+ c.Assert(ok, Equals, false)
706+ c.Assert(iter.Close(), IsNil)
707+ c.Assert(f, IsNil)
708+}
vendor/gopkg.in/mgo.v2/internal/sasl/sasl.c+77−0
@@ -0,0 +1,77 @@
1+// +build !windows
2+
3+#include <stdlib.h>
4+#include <stdio.h>
5+#include <string.h>
6+#include <sasl/sasl.h>
7+
8+static int mgo_sasl_simple(void *context, int id, const char **result, unsigned int *len)
9+{
10+ if (!result) {
11+ return SASL_BADPARAM;
12+ }
13+ switch (id) {
14+ case SASL_CB_USER:
15+ *result = (char *)context;
16+ break;
17+ case SASL_CB_AUTHNAME:
18+ *result = (char *)context;
19+ break;
20+ case SASL_CB_LANGUAGE:
21+ *result = NULL;
22+ break;
23+ default:
24+ return SASL_BADPARAM;
25+ }
26+ if (len) {
27+ *len = *result ? strlen(*result) : 0;
28+ }
29+ return SASL_OK;
30+}
31+
32+typedef int (*callback)(void);
33+
34+static int mgo_sasl_secret(sasl_conn_t *conn, void *context, int id, sasl_secret_t **result)
35+{
36+ if (!conn || !result || id != SASL_CB_PASS) {
37+ return SASL_BADPARAM;
38+ }
39+ *result = (sasl_secret_t *)context;
40+ return SASL_OK;
41+}
42+
43+sasl_callback_t *mgo_sasl_callbacks(const char *username, const char *password)
44+{
45+ sasl_callback_t *cb = malloc(4 * sizeof(sasl_callback_t));
46+ int n = 0;
47+
48+ size_t len = strlen(password);
49+ sasl_secret_t *secret = (sasl_secret_t*)malloc(sizeof(sasl_secret_t) + len);
50+ if (!secret) {
51+ free(cb);
52+ return NULL;
53+ }
54+ strcpy((char *)secret->data, password);
55+ secret->len = len;
56+
57+ cb[n].id = SASL_CB_PASS;
58+ cb[n].proc = (callback)&mgo_sasl_secret;
59+ cb[n].context = secret;
60+ n++;
61+
62+ cb[n].id = SASL_CB_USER;
63+ cb[n].proc = (callback)&mgo_sasl_simple;
64+ cb[n].context = (char*)username;
65+ n++;
66+
67+ cb[n].id = SASL_CB_AUTHNAME;
68+ cb[n].proc = (callback)&mgo_sasl_simple;
69+ cb[n].context = (char*)username;
70+ n++;
71+
72+ cb[n].id = SASL_CB_LIST_END;
73+ cb[n].proc = NULL;
74+ cb[n].context = NULL;
75+
76+ return cb;
77+}
vendor/gopkg.in/mgo.v2/internal/sasl/sasl.go+138−0
@@ -0,0 +1,138 @@
1+// Package sasl is an implementation detail of the mgo package.
2+//
3+// This package is not meant to be used by itself.
4+//
5+
6+// +build !windows
7+
8+package sasl
9+
10+// #cgo LDFLAGS: -lsasl2
11+//
12+// struct sasl_conn {};
13+//
14+// #include <stdlib.h>
15+// #include <sasl/sasl.h>
16+//
17+// sasl_callback_t *mgo_sasl_callbacks(const char *username, const char *password);
18+//
19+import "C"
20+
21+import (
22+ "fmt"
23+ "strings"
24+ "sync"
25+ "unsafe"
26+)
27+
28+type saslStepper interface {
29+ Step(serverData []byte) (clientData []byte, done bool, err error)
30+ Close()
31+}
32+
33+type saslSession struct {
34+ conn *C.sasl_conn_t
35+ step int
36+ mech string
37+
38+ cstrings []*C.char
39+ callbacks *C.sasl_callback_t
40+}
41+
42+var initError error
43+var initOnce sync.Once
44+
45+func initSASL() {
46+ rc := C.sasl_client_init(nil)
47+ if rc != C.SASL_OK {
48+ initError = saslError(rc, nil, "cannot initialize SASL library")
49+ }
50+}
51+
52+func New(username, password, mechanism, service, host string) (saslStepper, error) {
53+ initOnce.Do(initSASL)
54+ if initError != nil {
55+ return nil, initError
56+ }
57+
58+ ss := &saslSession{mech: mechanism}
59+ if service == "" {
60+ service = "mongodb"
61+ }
62+ if i := strings.Index(host, ":"); i >= 0 {
63+ host = host[:i]
64+ }
65+ ss.callbacks = C.mgo_sasl_callbacks(ss.cstr(username), ss.cstr(password))
66+ rc := C.sasl_client_new(ss.cstr(service), ss.cstr(host), nil, nil, ss.callbacks, 0, &ss.conn)
67+ if rc != C.SASL_OK {
68+ ss.Close()
69+ return nil, saslError(rc, nil, "cannot create new SASL client")
70+ }
71+ return ss, nil
72+}
73+
74+func (ss *saslSession) cstr(s string) *C.char {
75+ cstr := C.CString(s)
76+ ss.cstrings = append(ss.cstrings, cstr)
77+ return cstr
78+}
79+
80+func (ss *saslSession) Close() {
81+ for _, cstr := range ss.cstrings {
82+ C.free(unsafe.Pointer(cstr))
83+ }
84+ ss.cstrings = nil
85+
86+ if ss.callbacks != nil {
87+ C.free(unsafe.Pointer(ss.callbacks))
88+ }
89+
90+ // The documentation of SASL dispose makes it clear that this should only
91+ // be done when the connection is done, not when the authentication phase
92+ // is done, because an encryption layer may have been negotiated.
93+ // Even then, we'll do this for now, because it's simpler and prevents
94+ // keeping track of this state for every socket. If it breaks, we'll fix it.
95+ C.sasl_dispose(&ss.conn)
96+}
97+
98+func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, err error) {
99+ ss.step++
100+ if ss.step > 10 {
101+ return nil, false, fmt.Errorf("too many SASL steps without authentication")
102+ }
103+ var cclientData *C.char
104+ var cclientDataLen C.uint
105+ var rc C.int
106+ if ss.step == 1 {
107+ var mechanism *C.char // ignored - must match cred
108+ rc = C.sasl_client_start(ss.conn, ss.cstr(ss.mech), nil, &cclientData, &cclientDataLen, &mechanism)
109+ } else {
110+ var cserverData *C.char
111+ var cserverDataLen C.uint
112+ if len(serverData) > 0 {
113+ cserverData = (*C.char)(unsafe.Pointer(&serverData[0]))
114+ cserverDataLen = C.uint(len(serverData))
115+ }
116+ rc = C.sasl_client_step(ss.conn, cserverData, cserverDataLen, nil, &cclientData, &cclientDataLen)
117+ }
118+ if cclientData != nil && cclientDataLen > 0 {
119+ clientData = C.GoBytes(unsafe.Pointer(cclientData), C.int(cclientDataLen))
120+ }
121+ if rc == C.SASL_OK {
122+ return clientData, true, nil
123+ }
124+ if rc == C.SASL_CONTINUE {
125+ return clientData, false, nil
126+ }
127+ return nil, false, saslError(rc, ss.conn, "cannot establish SASL session")
128+}
129+
130+func saslError(rc C.int, conn *C.sasl_conn_t, msg string) error {
131+ var detail string
132+ if conn == nil {
133+ detail = C.GoString(C.sasl_errstring(rc, nil, nil))
134+ } else {
135+ detail = C.GoString(C.sasl_errdetail(conn))
136+ }
137+ return fmt.Errorf(msg + ": " + detail)
138+}
vendor/gopkg.in/mgo.v2/internal/sasl/sasl_windows.c+118−0
@@ -0,0 +1,118 @@
1+#include "sasl_windows.h"
2+
3+static const LPSTR SSPI_PACKAGE_NAME = "kerberos";
4+
5+SECURITY_STATUS SEC_ENTRY sspi_acquire_credentials_handle(CredHandle *cred_handle, char *username, char *password, char *domain)
6+{
7+ SEC_WINNT_AUTH_IDENTITY auth_identity;
8+ SECURITY_INTEGER ignored;
9+
10+ auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
11+ auth_identity.User = (LPSTR) username;
12+ auth_identity.UserLength = strlen(username);
13+ auth_identity.Password = (LPSTR) password;
14+ auth_identity.PasswordLength = strlen(password);
15+ auth_identity.Domain = (LPSTR) domain;
16+ auth_identity.DomainLength = strlen(domain);
17+ return call_sspi_acquire_credentials_handle(NULL, SSPI_PACKAGE_NAME, SECPKG_CRED_OUTBOUND, NULL, &auth_identity, NULL, NULL, cred_handle, &ignored);
18+}
19+
20+int sspi_step(CredHandle *cred_handle, int has_context, CtxtHandle *context, PVOID *buffer, ULONG *buffer_length, char *target)
21+{
22+ SecBufferDesc inbuf;
23+ SecBuffer in_bufs[1];
24+ SecBufferDesc outbuf;
25+ SecBuffer out_bufs[1];
26+
27+ if (has_context > 0) {
28+ // If we already have a context, we now have data to send.
29+ // Put this data in an inbuf.
30+ inbuf.ulVersion = SECBUFFER_VERSION;
31+ inbuf.cBuffers = 1;
32+ inbuf.pBuffers = in_bufs;
33+ in_bufs[0].pvBuffer = *buffer;
34+ in_bufs[0].cbBuffer = *buffer_length;
35+ in_bufs[0].BufferType = SECBUFFER_TOKEN;
36+ }
37+
38+ outbuf.ulVersion = SECBUFFER_VERSION;
39+ outbuf.cBuffers = 1;
40+ outbuf.pBuffers = out_bufs;
41+ out_bufs[0].pvBuffer = NULL;
42+ out_bufs[0].cbBuffer = 0;
43+ out_bufs[0].BufferType = SECBUFFER_TOKEN;
44+
45+ ULONG context_attr = 0;
46+
47+ int ret = call_sspi_initialize_security_context(cred_handle,
48+ has_context > 0 ? context : NULL,
49+ (LPSTR) target,
50+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MUTUAL_AUTH,
51+ 0,
52+ SECURITY_NETWORK_DREP,
53+ has_context > 0 ? &inbuf : NULL,
54+ 0,
55+ context,
56+ &outbuf,
57+ &context_attr,
58+ NULL);
59+
60+ *buffer = malloc(out_bufs[0].cbBuffer);
61+ *buffer_length = out_bufs[0].cbBuffer;
62+ memcpy(*buffer, out_bufs[0].pvBuffer, *buffer_length);
63+
64+ return ret;
65+}
66+
67+int sspi_send_client_authz_id(CtxtHandle *context, PVOID *buffer, ULONG *buffer_length, char *user_plus_realm)
68+{
69+ SecPkgContext_Sizes sizes;
70+ SECURITY_STATUS status = call_sspi_query_context_attributes(context, SECPKG_ATTR_SIZES, &sizes);
71+
72+ if (status != SEC_E_OK) {
73+ return status;
74+ }
75+
76+ size_t user_plus_realm_length = strlen(user_plus_realm);
77+ int msgSize = 4 + user_plus_realm_length;
78+ char *msg = malloc((sizes.cbSecurityTrailer + msgSize + sizes.cbBlockSize) * sizeof(char));
79+ msg[sizes.cbSecurityTrailer + 0] = 1;
80+ msg[sizes.cbSecurityTrailer + 1] = 0;
81+ msg[sizes.cbSecurityTrailer + 2] = 0;
82+ msg[sizes.cbSecurityTrailer + 3] = 0;
83+ memcpy(&msg[sizes.cbSecurityTrailer + 4], user_plus_realm, user_plus_realm_length);
84+
85+ SecBuffer wrapBufs[3];
86+ SecBufferDesc wrapBufDesc;
87+ wrapBufDesc.cBuffers = 3;
88+ wrapBufDesc.pBuffers = wrapBufs;
89+ wrapBufDesc.ulVersion = SECBUFFER_VERSION;
90+
91+ wrapBufs[0].cbBuffer = sizes.cbSecurityTrailer;
92+ wrapBufs[0].BufferType = SECBUFFER_TOKEN;
93+ wrapBufs[0].pvBuffer = msg;
94+
95+ wrapBufs[1].cbBuffer = msgSize;
96+ wrapBufs[1].BufferType = SECBUFFER_DATA;
97+ wrapBufs[1].pvBuffer = msg + sizes.cbSecurityTrailer;
98+
99+ wrapBufs[2].cbBuffer = sizes.cbBlockSize;
100+ wrapBufs[2].BufferType = SECBUFFER_PADDING;
101+ wrapBufs[2].pvBuffer = msg + sizes.cbSecurityTrailer + msgSize;
102+
103+ status = call_sspi_encrypt_message(context, SECQOP_WRAP_NO_ENCRYPT, &wrapBufDesc, 0);
104+ if (status != SEC_E_OK) {
105+ free(msg);
106+ return status;
107+ }
108+
109+ *buffer_length = wrapBufs[0].cbBuffer + wrapBufs[1].cbBuffer + wrapBufs[2].cbBuffer;
110+ *buffer = malloc(*buffer_length);
111+
112+ memcpy(*buffer, wrapBufs[0].pvBuffer, wrapBufs[0].cbBuffer);
113+ memcpy(*buffer + wrapBufs[0].cbBuffer, wrapBufs[1].pvBuffer, wrapBufs[1].cbBuffer);
114+ memcpy(*buffer + wrapBufs[0].cbBuffer + wrapBufs[1].cbBuffer, wrapBufs[2].pvBuffer, wrapBufs[2].cbBuffer);
115+
116+ free(msg);
117+ return SEC_E_OK;
118+}
vendor/gopkg.in/mgo.v2/internal/sasl/sasl_windows.go+140−0
@@ -0,0 +1,140 @@
1+package sasl
2+
3+// #include "sasl_windows.h"
4+import "C"
5+
6+import (
7+ "fmt"
8+ "strings"
9+ "sync"
10+ "unsafe"
11+)
12+
13+type saslStepper interface {
14+ Step(serverData []byte) (clientData []byte, done bool, err error)
15+ Close()
16+}
17+
18+type saslSession struct {
19+ // Credentials
20+ mech string
21+ service string
22+ host string
23+ userPlusRealm string
24+ target string
25+ domain string
26+
27+ // Internal state
28+ authComplete bool
29+ errored bool
30+ step int
31+
32+ // C internal state
33+ credHandle C.CredHandle
34+ context C.CtxtHandle
35+ hasContext C.int
36+
37+ // Keep track of pointers we need to explicitly free
38+ stringsToFree []*C.char
39+}
40+
41+var initError error
42+var initOnce sync.Once
43+
44+func initSSPI() {
45+ rc := C.load_secur32_dll()
46+ if rc != 0 {
47+ initError = fmt.Errorf("Error loading libraries: %v", rc)
48+ }
49+}
50+
51+func New(username, password, mechanism, service, host string) (saslStepper, error) {
52+ initOnce.Do(initSSPI)
53+ ss := &saslSession{mech: mechanism, hasContext: 0, userPlusRealm: username}
54+ if service == "" {
55+ service = "mongodb"
56+ }
57+ if i := strings.Index(host, ":"); i >= 0 {
58+ host = host[:i]
59+ }
60+ ss.service = service
61+ ss.host = host
62+
63+ usernameComponents := strings.Split(username, "@")
64+ if len(usernameComponents) < 2 {
65+ return nil, fmt.Errorf("Username '%v' doesn't contain a realm!", username)
66+ }
67+ user := usernameComponents[0]
68+ ss.domain = usernameComponents[1]
69+ ss.target = fmt.Sprintf("%s/%s", ss.service, ss.host)
70+
71+ var status C.SECURITY_STATUS
72+ // Step 0: call AcquireCredentialsHandle to get a nice SSPI CredHandle
73+ if len(password) > 0 {
74+ status = C.sspi_acquire_credentials_handle(&ss.credHandle, ss.cstr(user), ss.cstr(password), ss.cstr(ss.domain))
75+ } else {
76+ status = C.sspi_acquire_credentials_handle(&ss.credHandle, ss.cstr(user), nil, ss.cstr(ss.domain))
77+ }
78+ if status != C.SEC_E_OK {
79+ ss.errored = true
80+ return nil, fmt.Errorf("Couldn't create new SSPI client, error code %v", status)
81+ }
82+ return ss, nil
83+}
84+
85+func (ss *saslSession) cstr(s string) *C.char {
86+ cstr := C.CString(s)
87+ ss.stringsToFree = append(ss.stringsToFree, cstr)
88+ return cstr
89+}
90+
91+func (ss *saslSession) Close() {
92+ for _, cstr := range ss.stringsToFree {
93+ C.free(unsafe.Pointer(cstr))
94+ }
95+}
96+
97+func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, err error) {
98+ ss.step++
99+ if ss.step > 10 {
100+ return nil, false, fmt.Errorf("too many SSPI steps without authentication")
101+ }
102+ var buffer C.PVOID
103+ var bufferLength C.ULONG
104+ if len(serverData) > 0 {
105+ buffer = (C.PVOID)(unsafe.Pointer(&serverData[0]))
106+ bufferLength = C.ULONG(len(serverData))
107+ }
108+ var status C.int
109+ if ss.authComplete {
110+ // Step 3: last bit of magic to use the correct server credentials
111+ status = C.sspi_send_client_authz_id(&ss.context, &buffer, &bufferLength, ss.cstr(ss.userPlusRealm))
112+ } else {
113+ // Step 1 + Step 2: set up security context with the server and TGT
114+ status = C.sspi_step(&ss.credHandle, ss.hasContext, &ss.context, &buffer, &bufferLength, ss.cstr(ss.target))
115+ }
116+ if buffer != C.PVOID(nil) {
117+ defer C.free(unsafe.Pointer(buffer))
118+ }
119+ if status != C.SEC_E_OK && status != C.SEC_I_CONTINUE_NEEDED {
120+ ss.errored = true
121+ return nil, false, ss.handleSSPIErrorCode(status)
122+ }
123+
124+ clientData = C.GoBytes(unsafe.Pointer(buffer), C.int(bufferLength))
125+ if status == C.SEC_E_OK {
126+ ss.authComplete = true
127+ return clientData, true, nil
128+ } else {
129+ ss.hasContext = 1
130+ return clientData, false, nil
131+ }
132+}
133+
134+func (ss *saslSession) handleSSPIErrorCode(code C.int) error {
135+ switch {
136+ case code == C.SEC_E_TARGET_UNKNOWN:
137+ return fmt.Errorf("Target %v@%v not found", ss.target, ss.domain)
138+ }
139+ return fmt.Errorf("Unknown error doing step %v, error code %v", ss.step, code)
140+}
vendor/gopkg.in/mgo.v2/internal/sasl/sasl_windows.h+7−0
@@ -0,0 +1,7 @@
1+#include <windows.h>
2+
3+#include "sspi_windows.h"
4+
5+SECURITY_STATUS SEC_ENTRY sspi_acquire_credentials_handle(CredHandle* cred_handle, char* username, char* password, char* domain);
6+int sspi_step(CredHandle* cred_handle, int has_context, CtxtHandle* context, PVOID* buffer, ULONG* buffer_length, char* target);
7+int sspi_send_client_authz_id(CtxtHandle* context, PVOID* buffer, ULONG* buffer_length, char* user_plus_realm);
vendor/gopkg.in/mgo.v2/internal/sasl/sspi_windows.c+96−0
@@ -0,0 +1,96 @@
1+// Code adapted from the NodeJS kerberos library:
2+//
3+// https://github.com/christkv/kerberos/tree/master/lib/win32/kerberos_sspi.c
4+//
5+// Under the terms of the Apache License, Version 2.0:
6+//
7+// http://www.apache.org/licenses/LICENSE-2.0
8+//
9+#include <stdlib.h>
10+
11+#include "sspi_windows.h"
12+
13+static HINSTANCE sspi_secur32_dll = NULL;
14+
15+int load_secur32_dll()
16+{
17+ sspi_secur32_dll = LoadLibrary("secur32.dll");
18+ if (sspi_secur32_dll == NULL) {
19+ return GetLastError();
20+ }
21+ return 0;
22+}
23+
24+SECURITY_STATUS SEC_ENTRY call_sspi_encrypt_message(PCtxtHandle phContext, unsigned long fQOP, PSecBufferDesc pMessage, unsigned long MessageSeqNo)
25+{
26+ if (sspi_secur32_dll == NULL) {
27+ return -1;
28+ }
29+ encryptMessage_fn pfn_encryptMessage = (encryptMessage_fn) GetProcAddress(sspi_secur32_dll, "EncryptMessage");
30+ if (!pfn_encryptMessage) {
31+ return -2;
32+ }
33+ return (*pfn_encryptMessage)(phContext, fQOP, pMessage, MessageSeqNo);
34+}
35+
36+SECURITY_STATUS SEC_ENTRY call_sspi_acquire_credentials_handle(
37+ LPSTR pszPrincipal, LPSTR pszPackage, unsigned long fCredentialUse,
38+ void *pvLogonId, void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument,
39+ PCredHandle phCredential, PTimeStamp ptsExpiry)
40+{
41+ if (sspi_secur32_dll == NULL) {
42+ return -1;
43+ }
44+ acquireCredentialsHandle_fn pfn_acquireCredentialsHandle;
45+#ifdef _UNICODE
46+ pfn_acquireCredentialsHandle = (acquireCredentialsHandle_fn) GetProcAddress(sspi_secur32_dll, "AcquireCredentialsHandleW");
47+#else
48+ pfn_acquireCredentialsHandle = (acquireCredentialsHandle_fn) GetProcAddress(sspi_secur32_dll, "AcquireCredentialsHandleA");
49+#endif
50+ if (!pfn_acquireCredentialsHandle) {
51+ return -2;
52+ }
53+ return (*pfn_acquireCredentialsHandle)(
54+ pszPrincipal, pszPackage, fCredentialUse, pvLogonId, pAuthData,
55+ pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry);
56+}
57+
58+SECURITY_STATUS SEC_ENTRY call_sspi_initialize_security_context(
59+ PCredHandle phCredential, PCtxtHandle phContext, LPSTR pszTargetName,
60+ unsigned long fContextReq, unsigned long Reserved1, unsigned long TargetDataRep,
61+ PSecBufferDesc pInput, unsigned long Reserved2, PCtxtHandle phNewContext,
62+ PSecBufferDesc pOutput, unsigned long *pfContextAttr, PTimeStamp ptsExpiry)
63+{
64+ if (sspi_secur32_dll == NULL) {
65+ return -1;
66+ }
67+ initializeSecurityContext_fn pfn_initializeSecurityContext;
68+#ifdef _UNICODE
69+ pfn_initializeSecurityContext = (initializeSecurityContext_fn) GetProcAddress(sspi_secur32_dll, "InitializeSecurityContextW");
70+#else
71+ pfn_initializeSecurityContext = (initializeSecurityContext_fn) GetProcAddress(sspi_secur32_dll, "InitializeSecurityContextA");
72+#endif
73+ if (!pfn_initializeSecurityContext) {
74+ return -2;
75+ }
76+ return (*pfn_initializeSecurityContext)(
77+ phCredential, phContext, pszTargetName, fContextReq, Reserved1, TargetDataRep,
78+ pInput, Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry);
79+}
80+
81+SECURITY_STATUS SEC_ENTRY call_sspi_query_context_attributes(PCtxtHandle phContext, unsigned long ulAttribute, void *pBuffer)
82+{
83+ if (sspi_secur32_dll == NULL) {
84+ return -1;
85+ }
86+ queryContextAttributes_fn pfn_queryContextAttributes;
87+#ifdef _UNICODE
88+ pfn_queryContextAttributes = (queryContextAttributes_fn) GetProcAddress(sspi_secur32_dll, "QueryContextAttributesW");
89+#else
90+ pfn_queryContextAttributes = (queryContextAttributes_fn) GetProcAddress(sspi_secur32_dll, "QueryContextAttributesA");
91+#endif
92+ if (!pfn_queryContextAttributes) {
93+ return -2;
94+ }
95+ return (*pfn_queryContextAttributes)(phContext, ulAttribute, pBuffer);
96+}
vendor/gopkg.in/mgo.v2/internal/sasl/sspi_windows.h+70−0
@@ -0,0 +1,70 @@
1+// Code adapted from the NodeJS kerberos library:
2+//
3+// https://github.com/christkv/kerberos/tree/master/lib/win32/kerberos_sspi.h
4+//
5+// Under the terms of the Apache License, Version 2.0:
6+//
7+// http://www.apache.org/licenses/LICENSE-2.0
8+//
9+#ifndef SSPI_WINDOWS_H
10+#define SSPI_WINDOWS_H
11+
12+#define SECURITY_WIN32 1
13+
14+#include <windows.h>
15+#include <sspi.h>
16+
17+int load_secur32_dll();
18+
19+SECURITY_STATUS SEC_ENTRY call_sspi_encrypt_message(PCtxtHandle phContext, unsigned long fQOP, PSecBufferDesc pMessage, unsigned long MessageSeqNo);
20+
21+typedef DWORD (WINAPI *encryptMessage_fn)(PCtxtHandle phContext, ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo);
22+
23+SECURITY_STATUS SEC_ENTRY call_sspi_acquire_credentials_handle(
24+ LPSTR pszPrincipal, // Name of principal
25+ LPSTR pszPackage, // Name of package
26+ unsigned long fCredentialUse, // Flags indicating use
27+ void *pvLogonId, // Pointer to logon ID
28+ void *pAuthData, // Package specific data
29+ SEC_GET_KEY_FN pGetKeyFn, // Pointer to GetKey() func
30+ void *pvGetKeyArgument, // Value to pass to GetKey()
31+ PCredHandle phCredential, // (out) Cred Handle
32+ PTimeStamp ptsExpiry // (out) Lifetime (optional)
33+);
34+
35+typedef DWORD (WINAPI *acquireCredentialsHandle_fn)(
36+ LPSTR pszPrincipal, LPSTR pszPackage, unsigned long fCredentialUse,
37+ void *pvLogonId, void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument,
38+ PCredHandle phCredential, PTimeStamp ptsExpiry
39+);
40+
41+SECURITY_STATUS SEC_ENTRY call_sspi_initialize_security_context(
42+ PCredHandle phCredential, // Cred to base context
43+ PCtxtHandle phContext, // Existing context (OPT)
44+ LPSTR pszTargetName, // Name of target
45+ unsigned long fContextReq, // Context Requirements
46+ unsigned long Reserved1, // Reserved, MBZ
47+ unsigned long TargetDataRep, // Data rep of target
48+ PSecBufferDesc pInput, // Input Buffers
49+ unsigned long Reserved2, // Reserved, MBZ
50+ PCtxtHandle phNewContext, // (out) New Context handle
51+ PSecBufferDesc pOutput, // (inout) Output Buffers
52+ unsigned long *pfContextAttr, // (out) Context attrs
53+ PTimeStamp ptsExpiry // (out) Life span (OPT)
54+);
55+
56+typedef DWORD (WINAPI *initializeSecurityContext_fn)(
57+ PCredHandle phCredential, PCtxtHandle phContext, LPSTR pszTargetName, unsigned long fContextReq,
58+ unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput, unsigned long Reserved2,
59+ PCtxtHandle phNewContext, PSecBufferDesc pOutput, unsigned long *pfContextAttr, PTimeStamp ptsExpiry);
60+
61+SECURITY_STATUS SEC_ENTRY call_sspi_query_context_attributes(
62+ PCtxtHandle phContext, // Context to query
63+ unsigned long ulAttribute, // Attribute to query
64+ void *pBuffer // Buffer for attributes
65+);
66+
67+typedef DWORD (WINAPI *queryContextAttributes_fn)(
68+ PCtxtHandle phContext, unsigned long ulAttribute, void *pBuffer);
69+
70+#endif // SSPI_WINDOWS_H
vendor/gopkg.in/mgo.v2/internal/scram/scram.go+266−0
@@ -0,0 +1,266 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2014 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+// Pacakage scram implements a SCRAM-{SHA-1,etc} client per RFC5802.
28+//
29+// http://tools.ietf.org/html/rfc5802
30+//
31+package scram
32+
33+import (
34+ "bytes"
35+ "crypto/hmac"
36+ "crypto/rand"
37+ "encoding/base64"
38+ "fmt"
39+ "hash"
40+ "strconv"
41+ "strings"
42+)
43+
44+// Client implements a SCRAM-* client (SCRAM-SHA-1, SCRAM-SHA-256, etc).
45+//
46+// A Client may be used within a SASL conversation with logic resembling:
47+//
48+// var in []byte
49+// var client = scram.NewClient(sha1.New, user, pass)
50+// for client.Step(in) {
51+// out := client.Out()
52+// // send out to server
53+// in := serverOut
54+// }
55+// if client.Err() != nil {
56+// // auth failed
57+// }
58+//
59+type Client struct {
60+ newHash func() hash.Hash
61+
62+ user string
63+ pass string
64+ step int
65+ out bytes.Buffer
66+ err error
67+
68+ clientNonce []byte
69+ serverNonce []byte
70+ saltedPass []byte
71+ authMsg bytes.Buffer
72+}
73+
74+// NewClient returns a new SCRAM-* client with the provided hash algorithm.
75+//
76+// For SCRAM-SHA-1, for example, use:
77+//
78+// client := scram.NewClient(sha1.New, user, pass)
79+//
80+func NewClient(newHash func() hash.Hash, user, pass string) *Client {
81+ c := &Client{
82+ newHash: newHash,
83+ user: user,
84+ pass: pass,
85+ }
86+ c.out.Grow(256)
87+ c.authMsg.Grow(256)
88+ return c
89+}
90+
91+// Out returns the data to be sent to the server in the current step.
92+func (c *Client) Out() []byte {
93+ if c.out.Len() == 0 {
94+ return nil
95+ }
96+ return c.out.Bytes()
97+}
98+
99+// Err returns the error that ocurred, or nil if there were no errors.
100+func (c *Client) Err() error {
101+ return c.err
102+}
103+
104+// SetNonce sets the client nonce to the provided value.
105+// If not set, the nonce is generated automatically out of crypto/rand on the first step.
106+func (c *Client) SetNonce(nonce []byte) {
107+ c.clientNonce = nonce
108+}
109+
110+var escaper = strings.NewReplacer("=", "=3D", ",", "=2C")
111+
112+// Step processes the incoming data from the server and makes the
113+// next round of data for the server available via Client.Out.
114+// Step returns false if there are no errors and more data is
115+// still expected.
116+func (c *Client) Step(in []byte) bool {
117+ c.out.Reset()
118+ if c.step > 2 || c.err != nil {
119+ return false
120+ }
121+ c.step++
122+ switch c.step {
123+ case 1:
124+ c.err = c.step1(in)
125+ case 2:
126+ c.err = c.step2(in)
127+ case 3:
128+ c.err = c.step3(in)
129+ }
130+ return c.step > 2 || c.err != nil
131+}
132+
133+func (c *Client) step1(in []byte) error {
134+ if len(c.clientNonce) == 0 {
135+ const nonceLen = 6
136+ buf := make([]byte, nonceLen + b64.EncodedLen(nonceLen))
137+ if _, err := rand.Read(buf[:nonceLen]); err != nil {
138+ return fmt.Errorf("cannot read random SCRAM-SHA-1 nonce from operating system: %v", err)
139+ }
140+ c.clientNonce = buf[nonceLen:]
141+ b64.Encode(c.clientNonce, buf[:nonceLen])
142+ }
143+ c.authMsg.WriteString("n=")
144+ escaper.WriteString(&c.authMsg, c.user)
145+ c.authMsg.WriteString(",r=")
146+ c.authMsg.Write(c.clientNonce)
147+
148+ c.out.WriteString("n,,")
149+ c.out.Write(c.authMsg.Bytes())
150+ return nil
151+}
152+
153+var b64 = base64.StdEncoding
154+
155+func (c *Client) step2(in []byte) error {
156+ c.authMsg.WriteByte(',')
157+ c.authMsg.Write(in)
158+
159+ fields := bytes.Split(in, []byte(","))
160+ if len(fields) != 3 {
161+ return fmt.Errorf("expected 3 fields in first SCRAM-SHA-1 server message, got %d: %q", len(fields), in)
162+ }
163+ if !bytes.HasPrefix(fields[0], []byte("r=")) || len(fields[0]) < 2 {
164+ return fmt.Errorf("server sent an invalid SCRAM-SHA-1 nonce: %q", fields[0])
165+ }
166+ if !bytes.HasPrefix(fields[1], []byte("s=")) || len(fields[1]) < 6 {
167+ return fmt.Errorf("server sent an invalid SCRAM-SHA-1 salt: %q", fields[1])
168+ }
169+ if !bytes.HasPrefix(fields[2], []byte("i=")) || len(fields[2]) < 6 {
170+ return fmt.Errorf("server sent an invalid SCRAM-SHA-1 iteration count: %q", fields[2])
171+ }
172+
173+ c.serverNonce = fields[0][2:]
174+ if !bytes.HasPrefix(c.serverNonce, c.clientNonce) {
175+ return fmt.Errorf("server SCRAM-SHA-1 nonce is not prefixed by client nonce: got %q, want %q+\"...\"", c.serverNonce, c.clientNonce)
176+ }
177+
178+ salt := make([]byte, b64.DecodedLen(len(fields[1][2:])))
179+ n, err := b64.Decode(salt, fields[1][2:])
180+ if err != nil {
181+ return fmt.Errorf("cannot decode SCRAM-SHA-1 salt sent by server: %q", fields[1])
182+ }
183+ salt = salt[:n]
184+ iterCount, err := strconv.Atoi(string(fields[2][2:]))
185+ if err != nil {
186+ return fmt.Errorf("server sent an invalid SCRAM-SHA-1 iteration count: %q", fields[2])
187+ }
188+ c.saltPassword(salt, iterCount)
189+
190+ c.authMsg.WriteString(",c=biws,r=")
191+ c.authMsg.Write(c.serverNonce)
192+
193+ c.out.WriteString("c=biws,r=")
194+ c.out.Write(c.serverNonce)
195+ c.out.WriteString(",p=")
196+ c.out.Write(c.clientProof())
197+ return nil
198+}
199+
200+func (c *Client) step3(in []byte) error {
201+ var isv, ise bool
202+ var fields = bytes.Split(in, []byte(","))
203+ if len(fields) == 1 {
204+ isv = bytes.HasPrefix(fields[0], []byte("v="))
205+ ise = bytes.HasPrefix(fields[0], []byte("e="))
206+ }
207+ if ise {
208+ return fmt.Errorf("SCRAM-SHA-1 authentication error: %s", fields[0][2:])
209+ } else if !isv {
210+ return fmt.Errorf("unsupported SCRAM-SHA-1 final message from server: %q", in)
211+ }
212+ if !bytes.Equal(c.serverSignature(), fields[0][2:]) {
213+ return fmt.Errorf("cannot authenticate SCRAM-SHA-1 server signature: %q", fields[0][2:])
214+ }
215+ return nil
216+}
217+
218+func (c *Client) saltPassword(salt []byte, iterCount int) {
219+ mac := hmac.New(c.newHash, []byte(c.pass))
220+ mac.Write(salt)
221+ mac.Write([]byte{0, 0, 0, 1})
222+ ui := mac.Sum(nil)
223+ hi := make([]byte, len(ui))
224+ copy(hi, ui)
225+ for i := 1; i < iterCount; i++ {
226+ mac.Reset()
227+ mac.Write(ui)
228+ mac.Sum(ui[:0])
229+ for j, b := range ui {
230+ hi[j] ^= b
231+ }
232+ }
233+ c.saltedPass = hi
234+}
235+
236+func (c *Client) clientProof() []byte {
237+ mac := hmac.New(c.newHash, c.saltedPass)
238+ mac.Write([]byte("Client Key"))
239+ clientKey := mac.Sum(nil)
240+ hash := c.newHash()
241+ hash.Write(clientKey)
242+ storedKey := hash.Sum(nil)
243+ mac = hmac.New(c.newHash, storedKey)
244+ mac.Write(c.authMsg.Bytes())
245+ clientProof := mac.Sum(nil)
246+ for i, b := range clientKey {
247+ clientProof[i] ^= b
248+ }
249+ clientProof64 := make([]byte, b64.EncodedLen(len(clientProof)))
250+ b64.Encode(clientProof64, clientProof)
251+ return clientProof64
252+}
253+
254+func (c *Client) serverSignature() []byte {
255+ mac := hmac.New(c.newHash, c.saltedPass)
256+ mac.Write([]byte("Server Key"))
257+ serverKey := mac.Sum(nil)
258+
259+ mac = hmac.New(c.newHash, serverKey)
260+ mac.Write(c.authMsg.Bytes())
261+ serverSignature := mac.Sum(nil)
262+
263+ encoded := make([]byte, b64.EncodedLen(len(serverSignature)))
264+ b64.Encode(encoded, serverSignature)
265+ return encoded
266+}
vendor/gopkg.in/mgo.v2/internal/scram/scram_test.go+67−0
@@ -0,0 +1,67 @@
1+package scram_test
2+
3+import (
4+ "crypto/sha1"
5+ "testing"
6+
7+ . "gopkg.in/check.v1"
8+ "gopkg.in/mgo.v2/internal/scram"
9+ "strings"
10+)
11+
12+var _ = Suite(&S{})
13+
14+func Test(t *testing.T) { TestingT(t) }
15+
16+type S struct{}
17+
18+var tests = [][]string{{
19+ "U: user pencil",
20+ "N: fyko+d2lbbFgONRv9qkxdawL",
21+ "C: n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL",
22+ "S: r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096",
23+ "C: c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=",
24+ "S: v=rmF9pqV8S7suAoZWja4dJRkFsKQ=",
25+}, {
26+ "U: root fe8c89e308ec08763df36333cbf5d3a2",
27+ "N: OTcxNDk5NjM2MzE5",
28+ "C: n,,n=root,r=OTcxNDk5NjM2MzE5",
29+ "S: r=OTcxNDk5NjM2MzE581Ra3provgG0iDsMkDiIAlrh4532dDLp,s=XRDkVrFC9JuL7/F4tG0acQ==,i=10000",
30+ "C: c=biws,r=OTcxNDk5NjM2MzE581Ra3provgG0iDsMkDiIAlrh4532dDLp,p=6y1jp9R7ETyouTXS9fW9k5UHdBc=",
31+ "S: v=LBnd9dUJRxdqZiEq91NKP3z/bHA=",
32+}}
33+
34+func (s *S) TestExamples(c *C) {
35+ for _, steps := range tests {
36+ if len(steps) < 2 || len(steps[0]) < 3 || !strings.HasPrefix(steps[0], "U: ") {
37+ c.Fatalf("Invalid test: %#v", steps)
38+ }
39+ auth := strings.Fields(steps[0][3:])
40+ client := scram.NewClient(sha1.New, auth[0], auth[1])
41+ first, done := true, false
42+ c.Logf("-----")
43+ c.Logf("%s", steps[0])
44+ for _, step := range steps[1:] {
45+ c.Logf("%s", step)
46+ switch step[:3] {
47+ case "N: ":
48+ client.SetNonce([]byte(step[3:]))
49+ case "C: ":
50+ if first {
51+ first = false
52+ done = client.Step(nil)
53+ }
54+ c.Assert(done, Equals, false)
55+ c.Assert(client.Err(), IsNil)
56+ c.Assert(string(client.Out()), Equals, step[3:])
57+ case "S: ":
58+ first = false
59+ done = client.Step([]byte(step[3:]))
60+ default:
61+ panic("invalid test line: " + step)
62+ }
63+ }
64+ c.Assert(done, Equals, true)
65+ c.Assert(client.Err(), IsNil)
66+ }
67+}
vendor/gopkg.in/mgo.v2/log.go+133−0
@@ -0,0 +1,133 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo
28+
29+import (
30+ "fmt"
31+ "sync"
32+)
33+
34+// ---------------------------------------------------------------------------
35+// Logging integration.
36+
37+// Avoid importing the log type information unnecessarily. There's a small cost
38+// associated with using an interface rather than the type. Depending on how
39+// often the logger is plugged in, it would be worth using the type instead.
40+type log_Logger interface {
41+ Output(calldepth int, s string) error
42+}
43+
44+var (
45+ globalLogger log_Logger
46+ globalDebug bool
47+ globalMutex sync.Mutex
48+)
49+
50+// RACE WARNING: There are known data races when logging, which are manually
51+// silenced when the race detector is in use. These data races won't be
52+// observed in typical use, because logging is supposed to be set up once when
53+// the application starts. Having raceDetector as a constant, the compiler
54+// should elide the locks altogether in actual use.
55+
56+// Specify the *log.Logger object where log messages should be sent to.
57+func SetLogger(logger log_Logger) {
58+ if raceDetector {
59+ globalMutex.Lock()
60+ defer globalMutex.Unlock()
61+ }
62+ globalLogger = logger
63+}
64+
65+// Enable the delivery of debug messages to the logger. Only meaningful
66+// if a logger is also set.
67+func SetDebug(debug bool) {
68+ if raceDetector {
69+ globalMutex.Lock()
70+ defer globalMutex.Unlock()
71+ }
72+ globalDebug = debug
73+}
74+
75+func log(v ...interface{}) {
76+ if raceDetector {
77+ globalMutex.Lock()
78+ defer globalMutex.Unlock()
79+ }
80+ if globalLogger != nil {
81+ globalLogger.Output(2, fmt.Sprint(v...))
82+ }
83+}
84+
85+func logln(v ...interface{}) {
86+ if raceDetector {
87+ globalMutex.Lock()
88+ defer globalMutex.Unlock()
89+ }
90+ if globalLogger != nil {
91+ globalLogger.Output(2, fmt.Sprintln(v...))
92+ }
93+}
94+
95+func logf(format string, v ...interface{}) {
96+ if raceDetector {
97+ globalMutex.Lock()
98+ defer globalMutex.Unlock()
99+ }
100+ if globalLogger != nil {
101+ globalLogger.Output(2, fmt.Sprintf(format, v...))
102+ }
103+}
104+
105+func debug(v ...interface{}) {
106+ if raceDetector {
107+ globalMutex.Lock()
108+ defer globalMutex.Unlock()
109+ }
110+ if globalDebug && globalLogger != nil {
111+ globalLogger.Output(2, fmt.Sprint(v...))
112+ }
113+}
114+
115+func debugln(v ...interface{}) {
116+ if raceDetector {
117+ globalMutex.Lock()
118+ defer globalMutex.Unlock()
119+ }
120+ if globalDebug && globalLogger != nil {
121+ globalLogger.Output(2, fmt.Sprintln(v...))
122+ }
123+}
124+
125+func debugf(format string, v ...interface{}) {
126+ if raceDetector {
127+ globalMutex.Lock()
128+ defer globalMutex.Unlock()
129+ }
130+ if globalDebug && globalLogger != nil {
131+ globalLogger.Output(2, fmt.Sprintf(format, v...))
132+ }
133+}
vendor/gopkg.in/mgo.v2/queue.go+91−0
@@ -0,0 +1,91 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo
28+
29+type queue struct {
30+ elems []interface{}
31+ nelems, popi, pushi int
32+}
33+
34+func (q *queue) Len() int {
35+ return q.nelems
36+}
37+
38+func (q *queue) Push(elem interface{}) {
39+ //debugf("Pushing(pushi=%d popi=%d cap=%d): %#v\n",
40+ // q.pushi, q.popi, len(q.elems), elem)
41+ if q.nelems == len(q.elems) {
42+ q.expand()
43+ }
44+ q.elems[q.pushi] = elem
45+ q.nelems++
46+ q.pushi = (q.pushi + 1) % len(q.elems)
47+ //debugf(" Pushed(pushi=%d popi=%d cap=%d): %#v\n",
48+ // q.pushi, q.popi, len(q.elems), elem)
49+}
50+
51+func (q *queue) Pop() (elem interface{}) {
52+ //debugf("Popping(pushi=%d popi=%d cap=%d)\n",
53+ // q.pushi, q.popi, len(q.elems))
54+ if q.nelems == 0 {
55+ return nil
56+ }
57+ elem = q.elems[q.popi]
58+ q.elems[q.popi] = nil // Help GC.
59+ q.nelems--
60+ q.popi = (q.popi + 1) % len(q.elems)
61+ //debugf(" Popped(pushi=%d popi=%d cap=%d): %#v\n",
62+ // q.pushi, q.popi, len(q.elems), elem)
63+ return elem
64+}
65+
66+func (q *queue) expand() {
67+ curcap := len(q.elems)
68+ var newcap int
69+ if curcap == 0 {
70+ newcap = 8
71+ } else if curcap < 1024 {
72+ newcap = curcap * 2
73+ } else {
74+ newcap = curcap + (curcap / 4)
75+ }
76+ elems := make([]interface{}, newcap)
77+
78+ if q.popi == 0 {
79+ copy(elems, q.elems)
80+ q.pushi = curcap
81+ } else {
82+ newpopi := newcap - (curcap - q.popi)
83+ copy(elems, q.elems[:q.popi])
84+ copy(elems[newpopi:], q.elems[q.popi:])
85+ q.popi = newpopi
86+ }
87+ for i := range q.elems {
88+ q.elems[i] = nil // Help GC.
89+ }
90+ q.elems = elems
91+}
vendor/gopkg.in/mgo.v2/queue_test.go+101−0
@@ -0,0 +1,101 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo
28+
29+import (
30+ . "gopkg.in/check.v1"
31+)
32+
33+type QS struct{}
34+
35+var _ = Suite(&QS{})
36+
37+func (s *QS) TestSequentialGrowth(c *C) {
38+ q := queue{}
39+ n := 2048
40+ for i := 0; i != n; i++ {
41+ q.Push(i)
42+ }
43+ for i := 0; i != n; i++ {
44+ c.Assert(q.Pop(), Equals, i)
45+ }
46+}
47+
48+var queueTestLists = [][]int{
49+ // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
50+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
51+
52+ // {8, 9, 10, 11, ... 2, 3, 4, 5, 6, 7}
53+ {0, 1, 2, 3, 4, 5, 6, 7, -1, -1, 8, 9, 10, 11},
54+
55+ // {8, 9, 10, 11, ... 2, 3, 4, 5, 6, 7}
56+ {0, 1, 2, 3, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11},
57+
58+ // {0, 1, 2, 3, 4, 5, 6, 7, 8}
59+ {0, 1, 2, 3, 4, 5, 6, 7, 8,
60+ -1, -1, -1, -1, -1, -1, -1, -1, -1,
61+ 0, 1, 2, 3, 4, 5, 6, 7, 8},
62+}
63+
64+func (s *QS) TestQueueTestLists(c *C) {
65+ test := []int{}
66+ testi := 0
67+ reset := func() {
68+ test = test[0:0]
69+ testi = 0
70+ }
71+ push := func(i int) {
72+ test = append(test, i)
73+ }
74+ pop := func() (i int) {
75+ if testi == len(test) {
76+ return -1
77+ }
78+ i = test[testi]
79+ testi++
80+ return
81+ }
82+
83+ for _, list := range queueTestLists {
84+ reset()
85+ q := queue{}
86+ for _, n := range list {
87+ if n == -1 {
88+ c.Assert(q.Pop(), Equals, pop(), Commentf("With list %#v", list))
89+ } else {
90+ q.Push(n)
91+ push(n)
92+ }
93+ }
94+
95+ for n := pop(); n != -1; n = pop() {
96+ c.Assert(q.Pop(), Equals, n, Commentf("With list %#v", list))
97+ }
98+
99+ c.Assert(q.Pop(), Equals, nil, Commentf("With list %#v", list))
100+ }
101+}
vendor/gopkg.in/mgo.v2/raceoff.go+5−0
@@ -0,0 +1,5 @@
1+// +build !race
2+
3+package mgo
4+
5+const raceDetector = false
vendor/gopkg.in/mgo.v2/raceon.go+5−0
@@ -0,0 +1,5 @@
1+// +build race
2+
3+package mgo
4+
5+const raceDetector = true
vendor/gopkg.in/mgo.v2/saslimpl.go+11−0
@@ -0,0 +1,11 @@
1+//+build sasl
2+
3+package mgo
4+
5+import (
6+ "gopkg.in/mgo.v2/internal/sasl"
7+)
8+
9+func saslNew(cred Credential, host string) (saslStepper, error) {
10+ return sasl.New(cred.Username, cred.Password, cred.Mechanism, cred.Service, host)
11+}
vendor/gopkg.in/mgo.v2/saslstub.go+11−0
@@ -0,0 +1,11 @@
1+//+build !sasl
2+
3+package mgo
4+
5+import (
6+ "fmt"
7+)
8+
9+func saslNew(cred Credential, host string) (saslStepper, error) {
10+ return nil, fmt.Errorf("SASL support not enabled during build (-tags sasl)")
11+}
vendor/gopkg.in/mgo.v2/server.go+448−0
@@ -0,0 +1,448 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo
28+
29+import (
30+ "errors"
31+ "net"
32+ "sort"
33+ "sync"
34+ "time"
35+
36+ "gopkg.in/mgo.v2/bson"
37+)
38+
39+// ---------------------------------------------------------------------------
40+// Mongo server encapsulation.
41+
42+type mongoServer struct {
43+ sync.RWMutex
44+ Addr string
45+ ResolvedAddr string
46+ tcpaddr *net.TCPAddr
47+ unusedSockets []*mongoSocket
48+ liveSockets []*mongoSocket
49+ closed bool
50+ abended bool
51+ sync chan bool
52+ dial dialer
53+ pingValue time.Duration
54+ pingIndex int
55+ pingCount uint32
56+ pingWindow [6]time.Duration
57+ info *mongoServerInfo
58+}
59+
60+type dialer struct {
61+ old func(addr net.Addr) (net.Conn, error)
62+ new func(addr *ServerAddr) (net.Conn, error)
63+}
64+
65+func (dial dialer) isSet() bool {
66+ return dial.old != nil || dial.new != nil
67+}
68+
69+type mongoServerInfo struct {
70+ Master bool
71+ Mongos bool
72+ Tags bson.D
73+ MaxWireVersion int
74+ SetName string
75+}
76+
77+var defaultServerInfo mongoServerInfo
78+
79+func newServer(addr string, tcpaddr *net.TCPAddr, sync chan bool, dial dialer) *mongoServer {
80+ server := &mongoServer{
81+ Addr: addr,
82+ ResolvedAddr: tcpaddr.String(),
83+ tcpaddr: tcpaddr,
84+ sync: sync,
85+ dial: dial,
86+ info: &defaultServerInfo,
87+ }
88+ // Once so the server gets a ping value, then loop in background.
89+ server.pinger(false)
90+ go server.pinger(true)
91+ return server
92+}
93+
94+var errPoolLimit = errors.New("per-server connection limit reached")
95+var errServerClosed = errors.New("server was closed")
96+
97+// AcquireSocket returns a socket for communicating with the server.
98+// This will attempt to reuse an old connection, if one is available. Otherwise,
99+// it will establish a new one. The returned socket is owned by the call site,
100+// and will return to the cache when the socket has its Release method called
101+// the same number of times as AcquireSocket + Acquire were called for it.
102+// If the poolLimit argument is greater than zero and the number of sockets in
103+// use in this server is greater than the provided limit, errPoolLimit is
104+// returned.
105+func (server *mongoServer) AcquireSocket(poolLimit int, timeout time.Duration) (socket *mongoSocket, abended bool, err error) {
106+ for {
107+ server.Lock()
108+ abended = server.abended
109+ if server.closed {
110+ server.Unlock()
111+ return nil, abended, errServerClosed
112+ }
113+ n := len(server.unusedSockets)
114+ if poolLimit > 0 && len(server.liveSockets)-n >= poolLimit {
115+ server.Unlock()
116+ return nil, false, errPoolLimit
117+ }
118+ if n > 0 {
119+ socket = server.unusedSockets[n-1]
120+ server.unusedSockets[n-1] = nil // Help GC.
121+ server.unusedSockets = server.unusedSockets[:n-1]
122+ info := server.info
123+ server.Unlock()
124+ err = socket.InitialAcquire(info, timeout)
125+ if err != nil {
126+ continue
127+ }
128+ } else {
129+ server.Unlock()
130+ socket, err = server.Connect(timeout)
131+ if err == nil {
132+ server.Lock()
133+ // We've waited for the Connect, see if we got
134+ // closed in the meantime
135+ if server.closed {
136+ server.Unlock()
137+ socket.Release()
138+ socket.Close()
139+ return nil, abended, errServerClosed
140+ }
141+ server.liveSockets = append(server.liveSockets, socket)
142+ server.Unlock()
143+ }
144+ }
145+ return
146+ }
147+ panic("unreachable")
148+}
149+
150+// Connect establishes a new connection to the server. This should
151+// generally be done through server.AcquireSocket().
152+func (server *mongoServer) Connect(timeout time.Duration) (*mongoSocket, error) {
153+ server.RLock()
154+ master := server.info.Master
155+ dial := server.dial
156+ server.RUnlock()
157+
158+ logf("Establishing new connection to %s (timeout=%s)...", server.Addr, timeout)
159+ var conn net.Conn
160+ var err error
161+ switch {
162+ case !dial.isSet():
163+ // Cannot do this because it lacks timeout support. :-(
164+ //conn, err = net.DialTCP("tcp", nil, server.tcpaddr)
165+ conn, err = net.DialTimeout("tcp", server.ResolvedAddr, timeout)
166+ case dial.old != nil:
167+ conn, err = dial.old(server.tcpaddr)
168+ case dial.new != nil:
169+ conn, err = dial.new(&ServerAddr{server.Addr, server.tcpaddr})
170+ default:
171+ panic("dialer is set, but both dial.old and dial.new are nil")
172+ }
173+ if err != nil {
174+ logf("Connection to %s failed: %v", server.Addr, err.Error())
175+ return nil, err
176+ }
177+ logf("Connection to %s established.", server.Addr)
178+
179+ stats.conn(+1, master)
180+ return newSocket(server, conn, timeout), nil
181+}
182+
183+// Close forces closing all sockets that are alive, whether
184+// they're currently in use or not.
185+func (server *mongoServer) Close() {
186+ server.Lock()
187+ server.closed = true
188+ liveSockets := server.liveSockets
189+ unusedSockets := server.unusedSockets
190+ server.liveSockets = nil
191+ server.unusedSockets = nil
192+ server.Unlock()
193+ logf("Connections to %s closing (%d live sockets).", server.Addr, len(liveSockets))
194+ for i, s := range liveSockets {
195+ s.Close()
196+ liveSockets[i] = nil
197+ }
198+ for i := range unusedSockets {
199+ unusedSockets[i] = nil
200+ }
201+}
202+
203+// RecycleSocket puts socket back into the unused cache.
204+func (server *mongoServer) RecycleSocket(socket *mongoSocket) {
205+ server.Lock()
206+ if !server.closed {
207+ server.unusedSockets = append(server.unusedSockets, socket)
208+ }
209+ server.Unlock()
210+}
211+
212+func removeSocket(sockets []*mongoSocket, socket *mongoSocket) []*mongoSocket {
213+ for i, s := range sockets {
214+ if s == socket {
215+ copy(sockets[i:], sockets[i+1:])
216+ n := len(sockets) - 1
217+ sockets[n] = nil
218+ sockets = sockets[:n]
219+ break
220+ }
221+ }
222+ return sockets
223+}
224+
225+// AbendSocket notifies the server that the given socket has terminated
226+// abnormally, and thus should be discarded rather than cached.
227+func (server *mongoServer) AbendSocket(socket *mongoSocket) {
228+ server.Lock()
229+ server.abended = true
230+ if server.closed {
231+ server.Unlock()
232+ return
233+ }
234+ server.liveSockets = removeSocket(server.liveSockets, socket)
235+ server.unusedSockets = removeSocket(server.unusedSockets, socket)
236+ server.Unlock()
237+ // Maybe just a timeout, but suggest a cluster sync up just in case.
238+ select {
239+ case server.sync <- true:
240+ default:
241+ }
242+}
243+
244+func (server *mongoServer) SetInfo(info *mongoServerInfo) {
245+ server.Lock()
246+ server.info = info
247+ server.Unlock()
248+}
249+
250+func (server *mongoServer) Info() *mongoServerInfo {
251+ server.Lock()
252+ info := server.info
253+ server.Unlock()
254+ return info
255+}
256+
257+func (server *mongoServer) hasTags(serverTags []bson.D) bool {
258+NextTagSet:
259+ for _, tags := range serverTags {
260+ NextReqTag:
261+ for _, req := range tags {
262+ for _, has := range server.info.Tags {
263+ if req.Name == has.Name {
264+ if req.Value == has.Value {
265+ continue NextReqTag
266+ }
267+ continue NextTagSet
268+ }
269+ }
270+ continue NextTagSet
271+ }
272+ return true
273+ }
274+ return false
275+}
276+
277+var pingDelay = 5 * time.Second
278+
279+func (server *mongoServer) pinger(loop bool) {
280+ var delay time.Duration
281+ if raceDetector {
282+ // This variable is only ever touched by tests.
283+ globalMutex.Lock()
284+ delay = pingDelay
285+ globalMutex.Unlock()
286+ } else {
287+ delay = pingDelay
288+ }
289+ op := queryOp{
290+ collection: "admin.$cmd",
291+ query: bson.D{{"ping", 1}},
292+ flags: flagSlaveOk,
293+ limit: -1,
294+ }
295+ for {
296+ if loop {
297+ time.Sleep(delay)
298+ }
299+ op := op
300+ socket, _, err := server.AcquireSocket(0, 3*delay)
301+ if err == nil {
302+ start := time.Now()
303+ _, _ = socket.SimpleQuery(&op)
304+ delay := time.Now().Sub(start)
305+
306+ server.pingWindow[server.pingIndex] = delay
307+ server.pingIndex = (server.pingIndex + 1) % len(server.pingWindow)
308+ server.pingCount++
309+ var max time.Duration
310+ for i := 0; i < len(server.pingWindow) && uint32(i) < server.pingCount; i++ {
311+ if server.pingWindow[i] > max {
312+ max = server.pingWindow[i]
313+ }
314+ }
315+ socket.Release()
316+ server.Lock()
317+ if server.closed {
318+ loop = false
319+ }
320+ server.pingValue = max
321+ server.Unlock()
322+ logf("Ping for %s is %d ms", server.Addr, max/time.Millisecond)
323+ } else if err == errServerClosed {
324+ return
325+ }
326+ if !loop {
327+ return
328+ }
329+ }
330+}
331+
332+type mongoServerSlice []*mongoServer
333+
334+func (s mongoServerSlice) Len() int {
335+ return len(s)
336+}
337+
338+func (s mongoServerSlice) Less(i, j int) bool {
339+ return s[i].ResolvedAddr < s[j].ResolvedAddr
340+}
341+
342+func (s mongoServerSlice) Swap(i, j int) {
343+ s[i], s[j] = s[j], s[i]
344+}
345+
346+func (s mongoServerSlice) Sort() {
347+ sort.Sort(s)
348+}
349+
350+func (s mongoServerSlice) Search(resolvedAddr string) (i int, ok bool) {
351+ n := len(s)
352+ i = sort.Search(n, func(i int) bool {
353+ return s[i].ResolvedAddr >= resolvedAddr
354+ })
355+ return i, i != n && s[i].ResolvedAddr == resolvedAddr
356+}
357+
358+type mongoServers struct {
359+ slice mongoServerSlice
360+}
361+
362+func (servers *mongoServers) Search(resolvedAddr string) (server *mongoServer) {
363+ if i, ok := servers.slice.Search(resolvedAddr); ok {
364+ return servers.slice[i]
365+ }
366+ return nil
367+}
368+
369+func (servers *mongoServers) Add(server *mongoServer) {
370+ servers.slice = append(servers.slice, server)
371+ servers.slice.Sort()
372+}
373+
374+func (servers *mongoServers) Remove(other *mongoServer) (server *mongoServer) {
375+ if i, found := servers.slice.Search(other.ResolvedAddr); found {
376+ server = servers.slice[i]
377+ copy(servers.slice[i:], servers.slice[i+1:])
378+ n := len(servers.slice) - 1
379+ servers.slice[n] = nil // Help GC.
380+ servers.slice = servers.slice[:n]
381+ }
382+ return
383+}
384+
385+func (servers *mongoServers) Slice() []*mongoServer {
386+ return ([]*mongoServer)(servers.slice)
387+}
388+
389+func (servers *mongoServers) Get(i int) *mongoServer {
390+ return servers.slice[i]
391+}
392+
393+func (servers *mongoServers) Len() int {
394+ return len(servers.slice)
395+}
396+
397+func (servers *mongoServers) Empty() bool {
398+ return len(servers.slice) == 0
399+}
400+
401+// BestFit returns the best guess of what would be the most interesting
402+// server to perform operations on at this point in time.
403+func (servers *mongoServers) BestFit(serverTags []bson.D) *mongoServer {
404+ var best *mongoServer
405+ for _, next := range servers.slice {
406+ if best == nil {
407+ best = next
408+ best.RLock()
409+ if serverTags != nil && !next.info.Mongos && !best.hasTags(serverTags) {
410+ best.RUnlock()
411+ best = nil
412+ }
413+ continue
414+ }
415+ next.RLock()
416+ swap := false
417+ switch {
418+ case serverTags != nil && !next.info.Mongos && !next.hasTags(serverTags):
419+ // Must have requested tags.
420+ case next.info.Master != best.info.Master:
421+ // Prefer slaves.
422+ swap = best.info.Master
423+ case absDuration(next.pingValue-best.pingValue) > 15*time.Millisecond:
424+ // Prefer nearest server.
425+ swap = next.pingValue < best.pingValue
426+ case len(next.liveSockets)-len(next.unusedSockets) < len(best.liveSockets)-len(best.unusedSockets):
427+ // Prefer servers with less connections.
428+ swap = true
429+ }
430+ if swap {
431+ best.RUnlock()
432+ best = next
433+ } else {
434+ next.RUnlock()
435+ }
436+ }
437+ if best != nil {
438+ best.RUnlock()
439+ }
440+ return best
441+}
442+
443+func absDuration(d time.Duration) time.Duration {
444+ if d < 0 {
445+ return -d
446+ }
447+ return d
448+}
vendor/gopkg.in/mgo.v2/session.go+4224−0
@@ -0,0 +1,4224 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo
28+
29+import (
30+ "crypto/md5"
31+ "encoding/hex"
32+ "errors"
33+ "fmt"
34+ "math"
35+ "net"
36+ "net/url"
37+ "reflect"
38+ "sort"
39+ "strconv"
40+ "strings"
41+ "sync"
42+ "time"
43+
44+ "gopkg.in/mgo.v2/bson"
45+)
46+
47+type mode int
48+
49+const (
50+ Eventual mode = 0
51+ Monotonic mode = 1
52+ Strong mode = 2
53+)
54+
55+// When changing the Session type, check if newSession and copySession
56+// need to be updated too.
57+
58+type Session struct {
59+ m sync.RWMutex
60+ cluster_ *mongoCluster
61+ slaveSocket *mongoSocket
62+ masterSocket *mongoSocket
63+ slaveOk bool
64+ consistency mode
65+ queryConfig query
66+ safeOp *queryOp
67+ syncTimeout time.Duration
68+ sockTimeout time.Duration
69+ defaultdb string
70+ sourcedb string
71+ dialCred *Credential
72+ creds []Credential
73+ poolLimit int
74+}
75+
76+type Database struct {
77+ Session *Session
78+ Name string
79+}
80+
81+type Collection struct {
82+ Database *Database
83+ Name string // "collection"
84+ FullName string // "db.collection"
85+}
86+
87+type Query struct {
88+ m sync.Mutex
89+ session *Session
90+ query // Enables default settings in session.
91+}
92+
93+type query struct {
94+ op queryOp
95+ prefetch float64
96+ limit int32
97+}
98+
99+type getLastError struct {
100+ CmdName int "getLastError,omitempty"
101+ W interface{} "w,omitempty"
102+ WTimeout int "wtimeout,omitempty"
103+ FSync bool "fsync,omitempty"
104+ J bool "j,omitempty"
105+}
106+
107+type Iter struct {
108+ m sync.Mutex
109+ gotReply sync.Cond
110+ session *Session
111+ server *mongoServer
112+ docData queue
113+ err error
114+ op getMoreOp
115+ prefetch float64
116+ limit int32
117+ docsToReceive int
118+ docsBeforeMore int
119+ timeout time.Duration
120+ timedout bool
121+}
122+
123+var (
124+ ErrNotFound = errors.New("not found")
125+ ErrCursor = errors.New("invalid cursor")
126+)
127+
128+const defaultPrefetch = 0.25
129+
130+// Dial establishes a new session to the cluster identified by the given seed
131+// server(s). The session will enable communication with all of the servers in
132+// the cluster, so the seed servers are used only to find out about the cluster
133+// topology.
134+//
135+// Dial will timeout after 10 seconds if a server isn't reached. The returned
136+// session will timeout operations after one minute by default if servers
137+// aren't available. To customize the timeout, see DialWithTimeout,
138+// SetSyncTimeout, and SetSocketTimeout.
139+//
140+// This method is generally called just once for a given cluster. Further
141+// sessions to the same cluster are then established using the New or Copy
142+// methods on the obtained session. This will make them share the underlying
143+// cluster, and manage the pool of connections appropriately.
144+//
145+// Once the session is not useful anymore, Close must be called to release the
146+// resources appropriately.
147+//
148+// The seed servers must be provided in the following format:
149+//
150+// [mongodb://][user:pass@]host1[:port1][,host2[:port2],...][/database][?options]
151+//
152+// For example, it may be as simple as:
153+//
154+// localhost
155+//
156+// Or more involved like:
157+//
158+// mongodb://myuser:mypass@localhost:40001,otherhost:40001/mydb
159+//
160+// If the port number is not provided for a server, it defaults to 27017.
161+//
162+// The username and password provided in the URL will be used to authenticate
163+// into the database named after the slash at the end of the host names, or
164+// into the "admin" database if none is provided. The authentication information
165+// will persist in sessions obtained through the New method as well.
166+//
167+// The following connection options are supported after the question mark:
168+//
169+// connect=direct
170+//
171+// Disables the automatic replica set server discovery logic, and
172+// forces the use of servers provided only (even if secondaries).
173+// Note that to talk to a secondary the consistency requirements
174+// must be relaxed to Monotonic or Eventual via SetMode.
175+//
176+//
177+// authSource=<db>
178+//
179+// Informs the database used to establish credentials and privileges
180+// with a MongoDB server. Defaults to the database name provided via
181+// the URL path, and "admin" if that's unset.
182+//
183+//
184+// authMechanism=<mechanism>
185+//
186+// Defines the protocol for credential negotiation. Defaults to "MONGODB-CR",
187+// which is the default username/password challenge-response mechanism.
188+//
189+//
190+// gssapiServiceName=<name>
191+//
192+// Defines the service name to use when authenticating with the GSSAPI
193+// mechanism. Defaults to "mongodb".
194+//
195+// maxPoolSize=<limit>
196+//
197+// Defines the per-server socket pool limit. Defaults to 4096.
198+// See Session.SetPoolLimit for details.
199+//
200+//
201+// Relevant documentation:
202+//
203+// http://docs.mongodb.org/manual/reference/connection-string/
204+//
205+func Dial(url string) (*Session, error) {
206+ session, err := DialWithTimeout(url, 10*time.Second)
207+ if err == nil {
208+ session.SetSyncTimeout(1 * time.Minute)
209+ session.SetSocketTimeout(1 * time.Minute)
210+ }
211+ return session, err
212+}
213+
214+// DialWithTimeout works like Dial, but uses timeout as the amount of time to
215+// wait for a server to respond when first connecting and also on follow up
216+// operations in the session. If timeout is zero, the call may block
217+// forever waiting for a connection to be made.
218+//
219+// See SetSyncTimeout for customizing the timeout for the session.
220+func DialWithTimeout(url string, timeout time.Duration) (*Session, error) {
221+ info, err := ParseURL(url)
222+ if err != nil {
223+ return nil, err
224+ }
225+ info.Timeout = timeout
226+ return DialWithInfo(info)
227+}
228+
229+// ParseURL parses a MongoDB URL as accepted by the Dial function and returns
230+// a value suitable for providing into DialWithInfo.
231+//
232+// See Dial for more details on the format of url.
233+func ParseURL(url string) (*DialInfo, error) {
234+ uinfo, err := extractURL(url)
235+ if err != nil {
236+ return nil, err
237+ }
238+ direct := false
239+ mechanism := ""
240+ service := ""
241+ source := ""
242+ setName := ""
243+ poolLimit := 0
244+ for k, v := range uinfo.options {
245+ switch k {
246+ case "authSource":
247+ source = v
248+ case "authMechanism":
249+ mechanism = v
250+ case "gssapiServiceName":
251+ service = v
252+ case "replicaSet":
253+ setName = v
254+ case "maxPoolSize":
255+ poolLimit, err = strconv.Atoi(v)
256+ if err != nil {
257+ return nil, errors.New("bad value for maxPoolSize: " + v)
258+ }
259+ case "connect":
260+ if v == "direct" {
261+ direct = true
262+ break
263+ }
264+ if v == "replicaSet" {
265+ break
266+ }
267+ fallthrough
268+ default:
269+ return nil, errors.New("unsupported connection URL option: " + k + "=" + v)
270+ }
271+ }
272+ info := DialInfo{
273+ Addrs: uinfo.addrs,
274+ Direct: direct,
275+ Database: uinfo.db,
276+ Username: uinfo.user,
277+ Password: uinfo.pass,
278+ Mechanism: mechanism,
279+ Service: service,
280+ Source: source,
281+ PoolLimit: poolLimit,
282+ ReplicaSetName: setName,
283+ }
284+ return &info, nil
285+}
286+
287+// DialInfo holds options for establishing a session with a MongoDB cluster.
288+// To use a URL, see the Dial function.
289+type DialInfo struct {
290+ // Addrs holds the addresses for the seed servers.
291+ Addrs []string
292+
293+ // Direct informs whether to establish connections only with the
294+ // specified seed servers, or to obtain information for the whole
295+ // cluster and establish connections with further servers too.
296+ Direct bool
297+
298+ // Timeout is the amount of time to wait for a server to respond when
299+ // first connecting and on follow up operations in the session. If
300+ // timeout is zero, the call may block forever waiting for a connection
301+ // to be established.
302+ Timeout time.Duration
303+
304+ // FailFast will cause connection and query attempts to fail faster when
305+ // the server is unavailable, instead of retrying until the configured
306+ // timeout period. Note that an unavailable server may silently drop
307+ // packets instead of rejecting them, in which case it's impossible to
308+ // distinguish it from a slow server, so the timeout stays relevant.
309+ FailFast bool
310+
311+ // Database is the default database name used when the Session.DB method
312+ // is called with an empty name, and is also used during the intial
313+ // authentication if Source is unset.
314+ Database string
315+
316+ // ReplicaSetName, if specified, will prevent the obtained session from
317+ // communicating with any server which is not part of a replica set
318+ // with the given name. The default is to communicate with any server
319+ // specified or discovered via the servers contacted.
320+ ReplicaSetName string
321+
322+ // Source is the database used to establish credentials and privileges
323+ // with a MongoDB server. Defaults to the value of Database, if that is
324+ // set, or "admin" otherwise.
325+ Source string
326+
327+ // Service defines the service name to use when authenticating with the GSSAPI
328+ // mechanism. Defaults to "mongodb".
329+ Service string
330+
331+ // ServiceHost defines which hostname to use when authenticating
332+ // with the GSSAPI mechanism. If not specified, defaults to the MongoDB
333+ // server's address.
334+ ServiceHost string
335+
336+ // Mechanism defines the protocol for credential negotiation.
337+ // Defaults to "MONGODB-CR".
338+ Mechanism string
339+
340+ // Username and Password inform the credentials for the initial authentication
341+ // done on the database defined by the Source field. See Session.Login.
342+ Username string
343+ Password string
344+
345+ // PoolLimit defines the per-server socket pool limit. Defaults to 4096.
346+ // See Session.SetPoolLimit for details.
347+ PoolLimit int
348+
349+ // DialServer optionally specifies the dial function for establishing
350+ // connections with the MongoDB servers.
351+ DialServer func(addr *ServerAddr) (net.Conn, error)
352+
353+ // WARNING: This field is obsolete. See DialServer above.
354+ Dial func(addr net.Addr) (net.Conn, error)
355+}
356+
357+// ServerAddr represents the address for establishing a connection to an
358+// individual MongoDB server.
359+type ServerAddr struct {
360+ str string
361+ tcp *net.TCPAddr
362+}
363+
364+// String returns the address that was provided for the server before resolution.
365+func (addr *ServerAddr) String() string {
366+ return addr.str
367+}
368+
369+// TCPAddr returns the resolved TCP address for the server.
370+func (addr *ServerAddr) TCPAddr() *net.TCPAddr {
371+ return addr.tcp
372+}
373+
374+// DialWithInfo establishes a new session to the cluster identified by info.
375+func DialWithInfo(info *DialInfo) (*Session, error) {
376+ addrs := make([]string, len(info.Addrs))
377+ for i, addr := range info.Addrs {
378+ p := strings.LastIndexAny(addr, "]:")
379+ if p == -1 || addr[p] != ':' {
380+ // XXX This is untested. The test suite doesn't use the standard port.
381+ addr += ":27017"
382+ }
383+ addrs[i] = addr
384+ }
385+ cluster := newCluster(addrs, info.Direct, info.FailFast, dialer{info.Dial, info.DialServer}, info.ReplicaSetName)
386+ session := newSession(Eventual, cluster, info.Timeout)
387+ session.defaultdb = info.Database
388+ if session.defaultdb == "" {
389+ session.defaultdb = "test"
390+ }
391+ session.sourcedb = info.Source
392+ if session.sourcedb == "" {
393+ session.sourcedb = info.Database
394+ if session.sourcedb == "" {
395+ session.sourcedb = "admin"
396+ }
397+ }
398+ if info.Username != "" {
399+ source := session.sourcedb
400+ if info.Source == "" &&
401+ (info.Mechanism == "GSSAPI" || info.Mechanism == "PLAIN" || info.Mechanism == "MONGODB-X509") {
402+ source = "$external"
403+ }
404+ session.dialCred = &Credential{
405+ Username: info.Username,
406+ Password: info.Password,
407+ Mechanism: info.Mechanism,
408+ Service: info.Service,
409+ ServiceHost: info.ServiceHost,
410+ Source: source,
411+ }
412+ session.creds = []Credential{*session.dialCred}
413+ }
414+ if info.PoolLimit > 0 {
415+ session.poolLimit = info.PoolLimit
416+ }
417+ cluster.Release()
418+
419+ // People get confused when we return a session that is not actually
420+ // established to any servers yet (e.g. what if url was wrong). So,
421+ // ping the server to ensure there's someone there, and abort if it
422+ // fails.
423+ if err := session.Ping(); err != nil {
424+ session.Close()
425+ return nil, err
426+ }
427+ session.SetMode(Strong, true)
428+ return session, nil
429+}
430+
431+func isOptSep(c rune) bool {
432+ return c == ';' || c == '&'
433+}
434+
435+type urlInfo struct {
436+ addrs []string
437+ user string
438+ pass string
439+ db string
440+ options map[string]string
441+}
442+
443+func extractURL(s string) (*urlInfo, error) {
444+ if strings.HasPrefix(s, "mongodb://") {
445+ s = s[10:]
446+ }
447+ info := &urlInfo{options: make(map[string]string)}
448+ if c := strings.Index(s, "?"); c != -1 {
449+ for _, pair := range strings.FieldsFunc(s[c+1:], isOptSep) {
450+ l := strings.SplitN(pair, "=", 2)
451+ if len(l) != 2 || l[0] == "" || l[1] == "" {
452+ return nil, errors.New("connection option must be key=value: " + pair)
453+ }
454+ info.options[l[0]] = l[1]
455+ }
456+ s = s[:c]
457+ }
458+ if c := strings.Index(s, "@"); c != -1 {
459+ pair := strings.SplitN(s[:c], ":", 2)
460+ if len(pair) > 2 || pair[0] == "" {
461+ return nil, errors.New("credentials must be provided as user:pass@host")
462+ }
463+ var err error
464+ info.user, err = url.QueryUnescape(pair[0])
465+ if err != nil {
466+ return nil, fmt.Errorf("cannot unescape username in URL: %q", pair[0])
467+ }
468+ if len(pair) > 1 {
469+ info.pass, err = url.QueryUnescape(pair[1])
470+ if err != nil {
471+ return nil, fmt.Errorf("cannot unescape password in URL")
472+ }
473+ }
474+ s = s[c+1:]
475+ }
476+ if c := strings.Index(s, "/"); c != -1 {
477+ info.db = s[c+1:]
478+ s = s[:c]
479+ }
480+ info.addrs = strings.Split(s, ",")
481+ return info, nil
482+}
483+
484+func newSession(consistency mode, cluster *mongoCluster, timeout time.Duration) (session *Session) {
485+ cluster.Acquire()
486+ session = &Session{
487+ cluster_: cluster,
488+ syncTimeout: timeout,
489+ sockTimeout: timeout,
490+ poolLimit: 4096,
491+ }
492+ debugf("New session %p on cluster %p", session, cluster)
493+ session.SetMode(consistency, true)
494+ session.SetSafe(&Safe{})
495+ session.queryConfig.prefetch = defaultPrefetch
496+ return session
497+}
498+
499+func copySession(session *Session, keepCreds bool) (s *Session) {
500+ cluster := session.cluster()
501+ cluster.Acquire()
502+ if session.masterSocket != nil {
503+ session.masterSocket.Acquire()
504+ }
505+ if session.slaveSocket != nil {
506+ session.slaveSocket.Acquire()
507+ }
508+ var creds []Credential
509+ if keepCreds {
510+ creds = make([]Credential, len(session.creds))
511+ copy(creds, session.creds)
512+ } else if session.dialCred != nil {
513+ creds = []Credential{*session.dialCred}
514+ }
515+ scopy := *session
516+ scopy.m = sync.RWMutex{}
517+ scopy.creds = creds
518+ s = &scopy
519+ debugf("New session %p on cluster %p (copy from %p)", s, cluster, session)
520+ return s
521+}
522+
523+// LiveServers returns a list of server addresses which are
524+// currently known to be alive.
525+func (s *Session) LiveServers() (addrs []string) {
526+ s.m.RLock()
527+ addrs = s.cluster().LiveServers()
528+ s.m.RUnlock()
529+ return addrs
530+}
531+
532+// DB returns a value representing the named database. If name
533+// is empty, the database name provided in the dialed URL is
534+// used instead. If that is also empty, "test" is used as a
535+// fallback in a way equivalent to the mongo shell.
536+//
537+// Creating this value is a very lightweight operation, and
538+// involves no network communication.
539+func (s *Session) DB(name string) *Database {
540+ if name == "" {
541+ name = s.defaultdb
542+ }
543+ return &Database{s, name}
544+}
545+
546+// C returns a value representing the named collection.
547+//
548+// Creating this value is a very lightweight operation, and
549+// involves no network communication.
550+func (db *Database) C(name string) *Collection {
551+ return &Collection{db, name, db.Name + "." + name}
552+}
553+
554+// With returns a copy of db that uses session s.
555+func (db *Database) With(s *Session) *Database {
556+ newdb := *db
557+ newdb.Session = s
558+ return &newdb
559+}
560+
561+// With returns a copy of c that uses session s.
562+func (c *Collection) With(s *Session) *Collection {
563+ newdb := *c.Database
564+ newdb.Session = s
565+ newc := *c
566+ newc.Database = &newdb
567+ return &newc
568+}
569+
570+// GridFS returns a GridFS value representing collections in db that
571+// follow the standard GridFS specification.
572+// The provided prefix (sometimes known as root) will determine which
573+// collections to use, and is usually set to "fs" when there is a
574+// single GridFS in the database.
575+//
576+// See the GridFS Create, Open, and OpenId methods for more details.
577+//
578+// Relevant documentation:
579+//
580+// http://www.mongodb.org/display/DOCS/GridFS
581+// http://www.mongodb.org/display/DOCS/GridFS+Tools
582+// http://www.mongodb.org/display/DOCS/GridFS+Specification
583+//
584+func (db *Database) GridFS(prefix string) *GridFS {
585+ return newGridFS(db, prefix)
586+}
587+
588+// Run issues the provided command on the db database and unmarshals
589+// its result in the respective argument. The cmd argument may be either
590+// a string with the command name itself, in which case an empty document of
591+// the form bson.M{cmd: 1} will be used, or it may be a full command document.
592+//
593+// Note that MongoDB considers the first marshalled key as the command
594+// name, so when providing a command with options, it's important to
595+// use an ordering-preserving document, such as a struct value or an
596+// instance of bson.D. For instance:
597+//
598+// db.Run(bson.D{{"create", "mycollection"}, {"size", 1024}})
599+//
600+// For privilleged commands typically run on the "admin" database, see
601+// the Run method in the Session type.
602+//
603+// Relevant documentation:
604+//
605+// http://www.mongodb.org/display/DOCS/Commands
606+// http://www.mongodb.org/display/DOCS/List+of+Database+CommandSkips
607+//
608+func (db *Database) Run(cmd interface{}, result interface{}) error {
609+ socket, err := db.Session.acquireSocket(true)
610+ if err != nil {
611+ return err
612+ }
613+ defer socket.Release()
614+
615+ // This is an optimized form of db.C("$cmd").Find(cmd).One(result).
616+ return db.run(socket, cmd, result)
617+}
618+
619+// Credential holds details to authenticate with a MongoDB server.
620+type Credential struct {
621+ // Username and Password hold the basic details for authentication.
622+ // Password is optional with some authentication mechanisms.
623+ Username string
624+ Password string
625+
626+ // Source is the database used to establish credentials and privileges
627+ // with a MongoDB server. Defaults to the default database provided
628+ // during dial, or "admin" if that was unset.
629+ Source string
630+
631+ // Service defines the service name to use when authenticating with the GSSAPI
632+ // mechanism. Defaults to "mongodb".
633+ Service string
634+
635+ // ServiceHost defines which hostname to use when authenticating
636+ // with the GSSAPI mechanism. If not specified, defaults to the MongoDB
637+ // server's address.
638+ ServiceHost string
639+
640+ // Mechanism defines the protocol for credential negotiation.
641+ // Defaults to "MONGODB-CR".
642+ Mechanism string
643+}
644+
645+// Login authenticates with MongoDB using the provided credential. The
646+// authentication is valid for the whole session and will stay valid until
647+// Logout is explicitly called for the same database, or the session is
648+// closed.
649+func (db *Database) Login(user, pass string) error {
650+ return db.Session.Login(&Credential{Username: user, Password: pass, Source: db.Name})
651+}
652+
653+// Login authenticates with MongoDB using the provided credential. The
654+// authentication is valid for the whole session and will stay valid until
655+// Logout is explicitly called for the same database, or the session is
656+// closed.
657+func (s *Session) Login(cred *Credential) error {
658+ socket, err := s.acquireSocket(true)
659+ if err != nil {
660+ return err
661+ }
662+ defer socket.Release()
663+
664+ credCopy := *cred
665+ if cred.Source == "" {
666+ if cred.Mechanism == "GSSAPI" {
667+ credCopy.Source = "$external"
668+ } else {
669+ credCopy.Source = s.sourcedb
670+ }
671+ }
672+ err = socket.Login(credCopy)
673+ if err != nil {
674+ return err
675+ }
676+
677+ s.m.Lock()
678+ s.creds = append(s.creds, credCopy)
679+ s.m.Unlock()
680+ return nil
681+}
682+
683+func (s *Session) socketLogin(socket *mongoSocket) error {
684+ for _, cred := range s.creds {
685+ if err := socket.Login(cred); err != nil {
686+ return err
687+ }
688+ }
689+ return nil
690+}
691+
692+// Logout removes any established authentication credentials for the database.
693+func (db *Database) Logout() {
694+ session := db.Session
695+ dbname := db.Name
696+ session.m.Lock()
697+ found := false
698+ for i, cred := range session.creds {
699+ if cred.Source == dbname {
700+ copy(session.creds[i:], session.creds[i+1:])
701+ session.creds = session.creds[:len(session.creds)-1]
702+ found = true
703+ break
704+ }
705+ }
706+ if found {
707+ if session.masterSocket != nil {
708+ session.masterSocket.Logout(dbname)
709+ }
710+ if session.slaveSocket != nil {
711+ session.slaveSocket.Logout(dbname)
712+ }
713+ }
714+ session.m.Unlock()
715+}
716+
717+// LogoutAll removes all established authentication credentials for the session.
718+func (s *Session) LogoutAll() {
719+ s.m.Lock()
720+ for _, cred := range s.creds {
721+ if s.masterSocket != nil {
722+ s.masterSocket.Logout(cred.Source)
723+ }
724+ if s.slaveSocket != nil {
725+ s.slaveSocket.Logout(cred.Source)
726+ }
727+ }
728+ s.creds = s.creds[0:0]
729+ s.m.Unlock()
730+}
731+
732+// User represents a MongoDB user.
733+//
734+// Relevant documentation:
735+//
736+// http://docs.mongodb.org/manual/reference/privilege-documents/
737+// http://docs.mongodb.org/manual/reference/user-privileges/
738+//
739+type User struct {
740+ // Username is how the user identifies itself to the system.
741+ Username string `bson:"user"`
742+
743+ // Password is the plaintext password for the user. If set,
744+ // the UpsertUser method will hash it into PasswordHash and
745+ // unset it before the user is added to the database.
746+ Password string `bson:",omitempty"`
747+
748+ // PasswordHash is the MD5 hash of Username+":mongo:"+Password.
749+ PasswordHash string `bson:"pwd,omitempty"`
750+
751+ // CustomData holds arbitrary data admins decide to associate
752+ // with this user, such as the full name or employee id.
753+ CustomData interface{} `bson:"customData,omitempty"`
754+
755+ // Roles indicates the set of roles the user will be provided.
756+ // See the Role constants.
757+ Roles []Role `bson:"roles"`
758+
759+ // OtherDBRoles allows assigning roles in other databases from
760+ // user documents inserted in the admin database. This field
761+ // only works in the admin database.
762+ OtherDBRoles map[string][]Role `bson:"otherDBRoles,omitempty"`
763+
764+ // UserSource indicates where to look for this user's credentials.
765+ // It may be set to a database name, or to "$external" for
766+ // consulting an external resource such as Kerberos. UserSource
767+ // must not be set if Password or PasswordHash are present.
768+ //
769+ // WARNING: This setting was only ever supported in MongoDB 2.4,
770+ // and is now obsolete.
771+ UserSource string `bson:"userSource,omitempty"`
772+}
773+
774+type Role string
775+
776+const (
777+ // Relevant documentation:
778+ //
779+ // http://docs.mongodb.org/manual/reference/user-privileges/
780+ //
781+ RoleRoot Role = "root"
782+ RoleRead Role = "read"
783+ RoleReadAny Role = "readAnyDatabase"
784+ RoleReadWrite Role = "readWrite"
785+ RoleReadWriteAny Role = "readWriteAnyDatabase"
786+ RoleDBAdmin Role = "dbAdmin"
787+ RoleDBAdminAny Role = "dbAdminAnyDatabase"
788+ RoleUserAdmin Role = "userAdmin"
789+ RoleUserAdminAny Role = "userAdminAnyDatabase"
790+ RoleClusterAdmin Role = "clusterAdmin"
791+)
792+
793+// UpsertUser updates the authentication credentials and the roles for
794+// a MongoDB user within the db database. If the named user doesn't exist
795+// it will be created.
796+//
797+// This method should only be used from MongoDB 2.4 and on. For older
798+// MongoDB releases, use the obsolete AddUser method instead.
799+//
800+// Relevant documentation:
801+//
802+// http://docs.mongodb.org/manual/reference/user-privileges/
803+// http://docs.mongodb.org/manual/reference/privilege-documents/
804+//
805+func (db *Database) UpsertUser(user *User) error {
806+ if user.Username == "" {
807+ return fmt.Errorf("user has no Username")
808+ }
809+ if (user.Password != "" || user.PasswordHash != "") && user.UserSource != "" {
810+ return fmt.Errorf("user has both Password/PasswordHash and UserSource set")
811+ }
812+ if len(user.OtherDBRoles) > 0 && db.Name != "admin" && db.Name != "$external" {
813+ return fmt.Errorf("user with OtherDBRoles is only supported in the admin or $external databases")
814+ }
815+
816+ // Attempt to run this using 2.6+ commands.
817+ rundb := db
818+ if user.UserSource != "" {
819+ // Compatibility logic for the userSource field of MongoDB <= 2.4.X
820+ rundb = db.Session.DB(user.UserSource)
821+ }
822+ err := rundb.runUserCmd("updateUser", user)
823+ // retry with createUser when isAuthError in order to enable the "localhost exception"
824+ if isNotFound(err) || isAuthError(err) {
825+ return rundb.runUserCmd("createUser", user)
826+ }
827+ if !isNoCmd(err) {
828+ return err
829+ }
830+
831+ // Command does not exist. Fallback to pre-2.6 behavior.
832+ var set, unset bson.D
833+ if user.Password != "" {
834+ psum := md5.New()
835+ psum.Write([]byte(user.Username + ":mongo:" + user.Password))
836+ set = append(set, bson.DocElem{"pwd", hex.EncodeToString(psum.Sum(nil))})
837+ unset = append(unset, bson.DocElem{"userSource", 1})
838+ } else if user.PasswordHash != "" {
839+ set = append(set, bson.DocElem{"pwd", user.PasswordHash})
840+ unset = append(unset, bson.DocElem{"userSource", 1})
841+ }
842+ if user.UserSource != "" {
843+ set = append(set, bson.DocElem{"userSource", user.UserSource})
844+ unset = append(unset, bson.DocElem{"pwd", 1})
845+ }
846+ if user.Roles != nil || user.OtherDBRoles != nil {
847+ set = append(set, bson.DocElem{"roles", user.Roles})
848+ if len(user.OtherDBRoles) > 0 {
849+ set = append(set, bson.DocElem{"otherDBRoles", user.OtherDBRoles})
850+ } else {
851+ unset = append(unset, bson.DocElem{"otherDBRoles", 1})
852+ }
853+ }
854+ users := db.C("system.users")
855+ err = users.Update(bson.D{{"user", user.Username}}, bson.D{{"$unset", unset}, {"$set", set}})
856+ if err == ErrNotFound {
857+ set = append(set, bson.DocElem{"user", user.Username})
858+ if user.Roles == nil && user.OtherDBRoles == nil {
859+ // Roles must be sent, as it's the way MongoDB distinguishes
860+ // old-style documents from new-style documents in pre-2.6.
861+ set = append(set, bson.DocElem{"roles", user.Roles})
862+ }
863+ err = users.Insert(set)
864+ }
865+ return err
866+}
867+
868+func isNoCmd(err error) bool {
869+ e, ok := err.(*QueryError)
870+ return ok && (e.Code == 59 || e.Code == 13390 || strings.HasPrefix(e.Message, "no such cmd:"))
871+}
872+
873+func isNotFound(err error) bool {
874+ e, ok := err.(*QueryError)
875+ return ok && e.Code == 11
876+}
877+
878+func isAuthError(err error) bool {
879+ e, ok := err.(*QueryError)
880+ return ok && e.Code == 13
881+}
882+
883+func (db *Database) runUserCmd(cmdName string, user *User) error {
884+ cmd := make(bson.D, 0, 16)
885+ cmd = append(cmd, bson.DocElem{cmdName, user.Username})
886+ if user.Password != "" {
887+ cmd = append(cmd, bson.DocElem{"pwd", user.Password})
888+ }
889+ var roles []interface{}
890+ for _, role := range user.Roles {
891+ roles = append(roles, role)
892+ }
893+ for db, dbroles := range user.OtherDBRoles {
894+ for _, role := range dbroles {
895+ roles = append(roles, bson.D{{"role", role}, {"db", db}})
896+ }
897+ }
898+ if roles != nil || user.Roles != nil || cmdName == "createUser" {
899+ cmd = append(cmd, bson.DocElem{"roles", roles})
900+ }
901+ err := db.Run(cmd, nil)
902+ if !isNoCmd(err) && user.UserSource != "" && (user.UserSource != "$external" || db.Name != "$external") {
903+ return fmt.Errorf("MongoDB 2.6+ does not support the UserSource setting")
904+ }
905+ return err
906+}
907+
908+// AddUser creates or updates the authentication credentials of user within
909+// the db database.
910+//
911+// WARNING: This method is obsolete and should only be used with MongoDB 2.2
912+// or earlier. For MongoDB 2.4 and on, use UpsertUser instead.
913+func (db *Database) AddUser(username, password string, readOnly bool) error {
914+ // Try to emulate the old behavior on 2.6+
915+ user := &User{Username: username, Password: password}
916+ if db.Name == "admin" {
917+ if readOnly {
918+ user.Roles = []Role{RoleReadAny}
919+ } else {
920+ user.Roles = []Role{RoleReadWriteAny}
921+ }
922+ } else {
923+ if readOnly {
924+ user.Roles = []Role{RoleRead}
925+ } else {
926+ user.Roles = []Role{RoleReadWrite}
927+ }
928+ }
929+ err := db.runUserCmd("updateUser", user)
930+ if isNotFound(err) {
931+ return db.runUserCmd("createUser", user)
932+ }
933+ if !isNoCmd(err) {
934+ return err
935+ }
936+
937+ // Command doesn't exist. Fallback to pre-2.6 behavior.
938+ psum := md5.New()
939+ psum.Write([]byte(username + ":mongo:" + password))
940+ digest := hex.EncodeToString(psum.Sum(nil))
941+ c := db.C("system.users")
942+ _, err = c.Upsert(bson.M{"user": username}, bson.M{"$set": bson.M{"user": username, "pwd": digest, "readOnly": readOnly}})
943+ return err
944+}
945+
946+// RemoveUser removes the authentication credentials of user from the database.
947+func (db *Database) RemoveUser(user string) error {
948+ err := db.Run(bson.D{{"dropUser", user}}, nil)
949+ if isNoCmd(err) {
950+ users := db.C("system.users")
951+ return users.Remove(bson.M{"user": user})
952+ }
953+ if isNotFound(err) {
954+ return ErrNotFound
955+ }
956+ return err
957+}
958+
959+type indexSpec struct {
960+ Name, NS string
961+ Key bson.D
962+ Unique bool ",omitempty"
963+ DropDups bool "dropDups,omitempty"
964+ Background bool ",omitempty"
965+ Sparse bool ",omitempty"
966+ Bits, Min, Max int ",omitempty"
967+ BucketSize float64 "bucketSize,omitempty"
968+ ExpireAfter int "expireAfterSeconds,omitempty"
969+ Weights bson.D ",omitempty"
970+ DefaultLanguage string "default_language,omitempty"
971+ LanguageOverride string "language_override,omitempty"
972+}
973+
974+type Index struct {
975+ Key []string // Index key fields; prefix name with dash (-) for descending order
976+ Unique bool // Prevent two documents from having the same index key
977+ DropDups bool // Drop documents with the same index key as a previously indexed one
978+ Background bool // Build index in background and return immediately
979+ Sparse bool // Only index documents containing the Key fields
980+
981+ // If ExpireAfter is defined the server will periodically delete
982+ // documents with indexed time.Time older than the provided delta.
983+ ExpireAfter time.Duration
984+
985+ // Name holds the stored index name. On creation this field is ignored and the index name
986+ // is automatically computed by EnsureIndex based on the index key
987+ Name string
988+
989+ // Properties for spatial indexes.
990+ Bits, Min, Max int
991+ BucketSize float64
992+
993+ // Properties for text indexes.
994+ DefaultLanguage string
995+ LanguageOverride string
996+
997+ // Weights defines the significance of provided fields relative to other
998+ // fields in a text index. The score for a given word in a document is derived
999+ // from the weighted sum of the frequency for each of the indexed fields in
1000+ // that document. The default field weight is 1.
1001+ Weights map[string]int
1002+}
1003+
1004+type indexKeyInfo struct {
1005+ name string
1006+ key bson.D
1007+ weights bson.D
1008+}
1009+
1010+func parseIndexKey(key []string) (*indexKeyInfo, error) {
1011+ var keyInfo indexKeyInfo
1012+ isText := false
1013+ var order interface{}
1014+ for _, field := range key {
1015+ raw := field
1016+ if keyInfo.name != "" {
1017+ keyInfo.name += "_"
1018+ }
1019+ var kind string
1020+ if field != "" {
1021+ if field[0] == '$' {
1022+ if c := strings.Index(field, ":"); c > 1 && c < len(field)-1 {
1023+ kind = field[1:c]
1024+ field = field[c+1:]
1025+ keyInfo.name += field + "_" + kind
1026+ } else {
1027+ field = "\x00"
1028+ }
1029+ }
1030+ switch field[0] {
1031+ case 0:
1032+ // Logic above failed. Reset and error.
1033+ field = ""
1034+ case '@':
1035+ order = "2d"
1036+ field = field[1:]
1037+ // The shell used to render this field as key_ instead of key_2d,
1038+ // and mgo followed suit. This has been fixed in recent server
1039+ // releases, and mgo followed as well.
1040+ keyInfo.name += field + "_2d"
1041+ case '-':
1042+ order = -1
1043+ field = field[1:]
1044+ keyInfo.name += field + "_-1"
1045+ case '+':
1046+ field = field[1:]
1047+ fallthrough
1048+ default:
1049+ if kind == "" {
1050+ order = 1
1051+ keyInfo.name += field + "_1"
1052+ } else {
1053+ order = kind
1054+ }
1055+ }
1056+ }
1057+ if field == "" || kind != "" && order != kind {
1058+ return nil, fmt.Errorf(`invalid index key: want "[$<kind>:][-]<field name>", got %q`, raw)
1059+ }
1060+ if kind == "text" {
1061+ if !isText {
1062+ keyInfo.key = append(keyInfo.key, bson.DocElem{"_fts", "text"}, bson.DocElem{"_ftsx", 1})
1063+ isText = true
1064+ }
1065+ keyInfo.weights = append(keyInfo.weights, bson.DocElem{field, 1})
1066+ } else {
1067+ keyInfo.key = append(keyInfo.key, bson.DocElem{field, order})
1068+ }
1069+ }
1070+ if keyInfo.name == "" {
1071+ return nil, errors.New("invalid index key: no fields provided")
1072+ }
1073+ return &keyInfo, nil
1074+}
1075+
1076+// EnsureIndexKey ensures an index with the given key exists, creating it
1077+// if necessary.
1078+//
1079+// This example:
1080+//
1081+// err := collection.EnsureIndexKey("a", "b")
1082+//
1083+// Is equivalent to:
1084+//
1085+// err := collection.EnsureIndex(mgo.Index{Key: []string{"a", "b"}})
1086+//
1087+// See the EnsureIndex method for more details.
1088+func (c *Collection) EnsureIndexKey(key ...string) error {
1089+ return c.EnsureIndex(Index{Key: key})
1090+}
1091+
1092+// EnsureIndex ensures an index with the given key exists, creating it with
1093+// the provided parameters if necessary. EnsureIndex does not modify a previously
1094+// existent index with a matching key. The old index must be dropped first instead.
1095+//
1096+// Once EnsureIndex returns successfully, following requests for the same index
1097+// will not contact the server unless Collection.DropIndex is used to drop the
1098+// same index, or Session.ResetIndexCache is called.
1099+//
1100+// For example:
1101+//
1102+// index := Index{
1103+// Key: []string{"lastname", "firstname"},
1104+// Unique: true,
1105+// DropDups: true,
1106+// Background: true, // See notes.
1107+// Sparse: true,
1108+// }
1109+// err := collection.EnsureIndex(index)
1110+//
1111+// The Key value determines which fields compose the index. The index ordering
1112+// will be ascending by default. To obtain an index with a descending order,
1113+// the field name should be prefixed by a dash (e.g. []string{"-time"}). It can
1114+// also be optionally prefixed by an index kind, as in "$text:summary" or
1115+// "$2d:-point". The key string format is:
1116+//
1117+// [$<kind>:][-]<field name>
1118+//
1119+// If the Unique field is true, the index must necessarily contain only a single
1120+// document per Key. With DropDups set to true, documents with the same key
1121+// as a previously indexed one will be dropped rather than an error returned.
1122+//
1123+// If Background is true, other connections will be allowed to proceed using
1124+// the collection without the index while it's being built. Note that the
1125+// session executing EnsureIndex will be blocked for as long as it takes for
1126+// the index to be built.
1127+//
1128+// If Sparse is true, only documents containing the provided Key fields will be
1129+// included in the index. When using a sparse index for sorting, only indexed
1130+// documents will be returned.
1131+//
1132+// If ExpireAfter is non-zero, the server will periodically scan the collection
1133+// and remove documents containing an indexed time.Time field with a value
1134+// older than ExpireAfter. See the documentation for details:
1135+//
1136+// http://docs.mongodb.org/manual/tutorial/expire-data
1137+//
1138+// Other kinds of indexes are also supported through that API. Here is an example:
1139+//
1140+// index := Index{
1141+// Key: []string{"$2d:loc"},
1142+// Bits: 26,
1143+// }
1144+// err := collection.EnsureIndex(index)
1145+//
1146+// The example above requests the creation of a "2d" index for the "loc" field.
1147+//
1148+// The 2D index bounds may be changed using the Min and Max attributes of the
1149+// Index value. The default bound setting of (-180, 180) is suitable for
1150+// latitude/longitude pairs.
1151+//
1152+// The Bits parameter sets the precision of the 2D geohash values. If not
1153+// provided, 26 bits are used, which is roughly equivalent to 1 foot of
1154+// precision for the default (-180, 180) index bounds.
1155+//
1156+// Relevant documentation:
1157+//
1158+// http://www.mongodb.org/display/DOCS/Indexes
1159+// http://www.mongodb.org/display/DOCS/Indexing+Advice+and+FAQ
1160+// http://www.mongodb.org/display/DOCS/Indexing+as+a+Background+Operation
1161+// http://www.mongodb.org/display/DOCS/Geospatial+Indexing
1162+// http://www.mongodb.org/display/DOCS/Multikeys
1163+//
1164+func (c *Collection) EnsureIndex(index Index) error {
1165+ keyInfo, err := parseIndexKey(index.Key)
1166+ if err != nil {
1167+ return err
1168+ }
1169+
1170+ session := c.Database.Session
1171+ cacheKey := c.FullName + "\x00" + keyInfo.name
1172+ if session.cluster().HasCachedIndex(cacheKey) {
1173+ return nil
1174+ }
1175+
1176+ spec := indexSpec{
1177+ Name: keyInfo.name,
1178+ NS: c.FullName,
1179+ Key: keyInfo.key,
1180+ Unique: index.Unique,
1181+ DropDups: index.DropDups,
1182+ Background: index.Background,
1183+ Sparse: index.Sparse,
1184+ Bits: index.Bits,
1185+ Min: index.Min,
1186+ Max: index.Max,
1187+ BucketSize: index.BucketSize,
1188+ ExpireAfter: int(index.ExpireAfter / time.Second),
1189+ Weights: keyInfo.weights,
1190+ DefaultLanguage: index.DefaultLanguage,
1191+ LanguageOverride: index.LanguageOverride,
1192+ }
1193+
1194+NextField:
1195+ for name, weight := range index.Weights {
1196+ for i, elem := range spec.Weights {
1197+ if elem.Name == name {
1198+ spec.Weights[i].Value = weight
1199+ continue NextField
1200+ }
1201+ }
1202+ panic("weight provided for field that is not part of index key: " + name)
1203+ }
1204+
1205+ cloned := session.Clone()
1206+ defer cloned.Close()
1207+ cloned.SetMode(Strong, false)
1208+ cloned.EnsureSafe(&Safe{})
1209+ db := c.Database.With(cloned)
1210+
1211+ // Try with a command first.
1212+ err = db.Run(bson.D{{"createIndexes", c.Name}, {"indexes", []indexSpec{spec}}}, nil)
1213+ if isNoCmd(err) {
1214+ // Command not yet supported. Insert into the indexes collection instead.
1215+ err = db.C("system.indexes").Insert(&spec)
1216+ }
1217+ if err == nil {
1218+ session.cluster().CacheIndex(cacheKey, true)
1219+ }
1220+ return err
1221+}
1222+
1223+// DropIndex removes the index with key from the collection.
1224+//
1225+// The key value determines which fields compose the index. The index ordering
1226+// will be ascending by default. To obtain an index with a descending order,
1227+// the field name should be prefixed by a dash (e.g. []string{"-time"}).
1228+//
1229+// For example:
1230+//
1231+// err := collection.DropIndex("lastname", "firstname")
1232+//
1233+// See the EnsureIndex method for more details on indexes.
1234+func (c *Collection) DropIndex(key ...string) error {
1235+ keyInfo, err := parseIndexKey(key)
1236+ if err != nil {
1237+ return err
1238+ }
1239+
1240+ session := c.Database.Session
1241+ cacheKey := c.FullName + "\x00" + keyInfo.name
1242+ session.cluster().CacheIndex(cacheKey, false)
1243+
1244+ session = session.Clone()
1245+ defer session.Close()
1246+ session.SetMode(Strong, false)
1247+
1248+ db := c.Database.With(session)
1249+ result := struct {
1250+ ErrMsg string
1251+ Ok bool
1252+ }{}
1253+ err = db.Run(bson.D{{"dropIndexes", c.Name}, {"index", keyInfo.name}}, &result)
1254+ if err != nil {
1255+ return err
1256+ }
1257+ if !result.Ok {
1258+ return errors.New(result.ErrMsg)
1259+ }
1260+ return nil
1261+}
1262+
1263+// Indexes returns a list of all indexes for the collection.
1264+//
1265+// For example, this snippet would drop all available indexes:
1266+//
1267+// indexes, err := collection.Indexes()
1268+// if err != nil {
1269+// return err
1270+// }
1271+// for _, index := range indexes {
1272+// err = collection.DropIndex(index.Key...)
1273+// if err != nil {
1274+// return err
1275+// }
1276+// }
1277+//
1278+// See the EnsureIndex method for more details on indexes.
1279+func (c *Collection) Indexes() (indexes []Index, err error) {
1280+ // Clone session and set it to Monotonic mode so that the server
1281+ // used for the query may be safely obtained afterwards, if
1282+ // necessary for iteration when a cursor is received.
1283+ session := c.Database.Session
1284+ cloned := session.Clone()
1285+ cloned.SetMode(Monotonic, false)
1286+ defer cloned.Close()
1287+
1288+ batchSize := int(cloned.queryConfig.op.limit)
1289+
1290+ // Try with a command.
1291+ var result struct {
1292+ Indexes []bson.Raw
1293+
1294+ Cursor struct {
1295+ FirstBatch []bson.Raw "firstBatch"
1296+ NS string
1297+ Id int64
1298+ }
1299+ }
1300+ var iter *Iter
1301+ err = c.Database.With(cloned).Run(bson.D{{"listIndexes", c.Name}, {"cursor", bson.D{{"batchSize", batchSize}}}}, &result)
1302+ if err == nil {
1303+ firstBatch := result.Indexes
1304+ if firstBatch == nil {
1305+ firstBatch = result.Cursor.FirstBatch
1306+ }
1307+ ns := strings.SplitN(result.Cursor.NS, ".", 2)
1308+ if len(ns) < 2 {
1309+ iter = c.With(cloned).NewIter(nil, firstBatch, result.Cursor.Id, nil)
1310+ } else {
1311+ iter = cloned.DB(ns[0]).C(ns[1]).NewIter(nil, firstBatch, result.Cursor.Id, nil)
1312+ }
1313+ } else if isNoCmd(err) {
1314+ // Command not yet supported. Query the database instead.
1315+ iter = c.Database.C("system.indexes").Find(bson.M{"ns": c.FullName}).Iter()
1316+ } else {
1317+ return nil, err
1318+ }
1319+
1320+ var spec indexSpec
1321+ for iter.Next(&spec) {
1322+ indexes = append(indexes, indexFromSpec(spec))
1323+ }
1324+ if err = iter.Close(); err != nil {
1325+ return nil, err
1326+ }
1327+ sort.Sort(indexSlice(indexes))
1328+ return indexes, nil
1329+}
1330+
1331+func indexFromSpec(spec indexSpec) Index {
1332+ return Index{
1333+ Name: spec.Name,
1334+ Key: simpleIndexKey(spec.Key),
1335+ Unique: spec.Unique,
1336+ DropDups: spec.DropDups,
1337+ Background: spec.Background,
1338+ Sparse: spec.Sparse,
1339+ ExpireAfter: time.Duration(spec.ExpireAfter) * time.Second,
1340+ }
1341+}
1342+
1343+type indexSlice []Index
1344+
1345+func (idxs indexSlice) Len() int { return len(idxs) }
1346+func (idxs indexSlice) Less(i, j int) bool { return idxs[i].Name < idxs[j].Name }
1347+func (idxs indexSlice) Swap(i, j int) { idxs[i], idxs[j] = idxs[j], idxs[i] }
1348+
1349+func simpleIndexKey(realKey bson.D) (key []string) {
1350+ for i := range realKey {
1351+ field := realKey[i].Name
1352+ vi, ok := realKey[i].Value.(int)
1353+ if !ok {
1354+ vf, _ := realKey[i].Value.(float64)
1355+ vi = int(vf)
1356+ }
1357+ if vi == 1 {
1358+ key = append(key, field)
1359+ continue
1360+ }
1361+ if vi == -1 {
1362+ key = append(key, "-"+field)
1363+ continue
1364+ }
1365+ if vs, ok := realKey[i].Value.(string); ok {
1366+ key = append(key, "$"+vs+":"+field)
1367+ continue
1368+ }
1369+ panic("Got unknown index key type for field " + field)
1370+ }
1371+ return
1372+}
1373+
1374+// ResetIndexCache() clears the cache of previously ensured indexes.
1375+// Following requests to EnsureIndex will contact the server.
1376+func (s *Session) ResetIndexCache() {
1377+ s.cluster().ResetIndexCache()
1378+}
1379+
1380+// New creates a new session with the same parameters as the original
1381+// session, including consistency, batch size, prefetching, safety mode,
1382+// etc. The returned session will use sockets from the pool, so there's
1383+// a chance that writes just performed in another session may not yet
1384+// be visible.
1385+//
1386+// Login information from the original session will not be copied over
1387+// into the new session unless it was provided through the initial URL
1388+// for the Dial function.
1389+//
1390+// See the Copy and Clone methods.
1391+//
1392+func (s *Session) New() *Session {
1393+ s.m.Lock()
1394+ scopy := copySession(s, false)
1395+ s.m.Unlock()
1396+ scopy.Refresh()
1397+ return scopy
1398+}
1399+
1400+// Copy works just like New, but preserves the exact authentication
1401+// information from the original session.
1402+func (s *Session) Copy() *Session {
1403+ s.m.Lock()
1404+ scopy := copySession(s, true)
1405+ s.m.Unlock()
1406+ scopy.Refresh()
1407+ return scopy
1408+}
1409+
1410+// Clone works just like Copy, but also reuses the same socket as the original
1411+// session, in case it had already reserved one due to its consistency
1412+// guarantees. This behavior ensures that writes performed in the old session
1413+// are necessarily observed when using the new session, as long as it was a
1414+// strong or monotonic session. That said, it also means that long operations
1415+// may cause other goroutines using the original session to wait.
1416+func (s *Session) Clone() *Session {
1417+ s.m.Lock()
1418+ scopy := copySession(s, true)
1419+ s.m.Unlock()
1420+ return scopy
1421+}
1422+
1423+// Close terminates the session. It's a runtime error to use a session
1424+// after it has been closed.
1425+func (s *Session) Close() {
1426+ s.m.Lock()
1427+ if s.cluster_ != nil {
1428+ debugf("Closing session %p", s)
1429+ s.unsetSocket()
1430+ s.cluster_.Release()
1431+ s.cluster_ = nil
1432+ }
1433+ s.m.Unlock()
1434+}
1435+
1436+func (s *Session) cluster() *mongoCluster {
1437+ if s.cluster_ == nil {
1438+ panic("Session already closed")
1439+ }
1440+ return s.cluster_
1441+}
1442+
1443+// Refresh puts back any reserved sockets in use and restarts the consistency
1444+// guarantees according to the current consistency setting for the session.
1445+func (s *Session) Refresh() {
1446+ s.m.Lock()
1447+ s.slaveOk = s.consistency != Strong
1448+ s.unsetSocket()
1449+ s.m.Unlock()
1450+}
1451+
1452+// SetMode changes the consistency mode for the session.
1453+//
1454+// In the Strong consistency mode reads and writes will always be made to
1455+// the primary server using a unique connection so that reads and writes are
1456+// fully consistent, ordered, and observing the most up-to-date data.
1457+// This offers the least benefits in terms of distributing load, but the
1458+// most guarantees. See also Monotonic and Eventual.
1459+//
1460+// In the Monotonic consistency mode reads may not be entirely up-to-date,
1461+// but they will always see the history of changes moving forward, the data
1462+// read will be consistent across sequential queries in the same session,
1463+// and modifications made within the session will be observed in following
1464+// queries (read-your-writes).
1465+//
1466+// In practice, the Monotonic mode is obtained by performing initial reads
1467+// on a unique connection to an arbitrary secondary, if one is available,
1468+// and once the first write happens, the session connection is switched over
1469+// to the primary server. This manages to distribute some of the reading
1470+// load with secondaries, while maintaining some useful guarantees.
1471+//
1472+// In the Eventual consistency mode reads will be made to any secondary in the
1473+// cluster, if one is available, and sequential reads will not necessarily
1474+// be made with the same connection. This means that data may be observed
1475+// out of order. Writes will of course be issued to the primary, but
1476+// independent writes in the same Eventual session may also be made with
1477+// independent connections, so there are also no guarantees in terms of
1478+// write ordering (no read-your-writes guarantees either).
1479+//
1480+// The Eventual mode is the fastest and most resource-friendly, but is
1481+// also the one offering the least guarantees about ordering of the data
1482+// read and written.
1483+//
1484+// If refresh is true, in addition to ensuring the session is in the given
1485+// consistency mode, the consistency guarantees will also be reset (e.g.
1486+// a Monotonic session will be allowed to read from secondaries again).
1487+// This is equivalent to calling the Refresh function.
1488+//
1489+// Shifting between Monotonic and Strong modes will keep a previously
1490+// reserved connection for the session unless refresh is true or the
1491+// connection is unsuitable (to a secondary server in a Strong session).
1492+func (s *Session) SetMode(consistency mode, refresh bool) {
1493+ s.m.Lock()
1494+ debugf("Session %p: setting mode %d with refresh=%v (master=%p, slave=%p)", s, consistency, refresh, s.masterSocket, s.slaveSocket)
1495+ s.consistency = consistency
1496+ if refresh {
1497+ s.slaveOk = s.consistency != Strong
1498+ s.unsetSocket()
1499+ } else if s.consistency == Strong {
1500+ s.slaveOk = false
1501+ } else if s.masterSocket == nil {
1502+ s.slaveOk = true
1503+ }
1504+ s.m.Unlock()
1505+}
1506+
1507+// Mode returns the current consistency mode for the session.
1508+func (s *Session) Mode() mode {
1509+ s.m.RLock()
1510+ mode := s.consistency
1511+ s.m.RUnlock()
1512+ return mode
1513+}
1514+
1515+// SetSyncTimeout sets the amount of time an operation with this session
1516+// will wait before returning an error in case a connection to a usable
1517+// server can't be established. Set it to zero to wait forever. The
1518+// default value is 7 seconds.
1519+func (s *Session) SetSyncTimeout(d time.Duration) {
1520+ s.m.Lock()
1521+ s.syncTimeout = d
1522+ s.m.Unlock()
1523+}
1524+
1525+// SetSocketTimeout sets the amount of time to wait for a non-responding
1526+// socket to the database before it is forcefully closed.
1527+func (s *Session) SetSocketTimeout(d time.Duration) {
1528+ s.m.Lock()
1529+ s.sockTimeout = d
1530+ if s.masterSocket != nil {
1531+ s.masterSocket.SetTimeout(d)
1532+ }
1533+ if s.slaveSocket != nil {
1534+ s.slaveSocket.SetTimeout(d)
1535+ }
1536+ s.m.Unlock()
1537+}
1538+
1539+// SetCursorTimeout changes the standard timeout period that the server
1540+// enforces on created cursors. The only supported value right now is
1541+// 0, which disables the timeout. The standard server timeout is 10 minutes.
1542+func (s *Session) SetCursorTimeout(d time.Duration) {
1543+ s.m.Lock()
1544+ if d == 0 {
1545+ s.queryConfig.op.flags |= flagNoCursorTimeout
1546+ } else {
1547+ panic("SetCursorTimeout: only 0 (disable timeout) supported for now")
1548+ }
1549+ s.m.Unlock()
1550+}
1551+
1552+// SetPoolLimit sets the maximum number of sockets in use in a single server
1553+// before this session will block waiting for a socket to be available.
1554+// The default limit is 4096.
1555+//
1556+// This limit must be set to cover more than any expected workload of the
1557+// application. It is a bad practice and an unsupported use case to use the
1558+// database driver to define the concurrency limit of an application. Prevent
1559+// such concurrency "at the door" instead, by properly restricting the amount
1560+// of used resources and number of goroutines before they are created.
1561+func (s *Session) SetPoolLimit(limit int) {
1562+ s.m.Lock()
1563+ s.poolLimit = limit
1564+ s.m.Unlock()
1565+}
1566+
1567+// SetBatch sets the default batch size used when fetching documents from the
1568+// database. It's possible to change this setting on a per-query basis as
1569+// well, using the Query.Batch method.
1570+//
1571+// The default batch size is defined by the database itself. As of this
1572+// writing, MongoDB will use an initial size of min(100 docs, 4MB) on the
1573+// first batch, and 4MB on remaining ones.
1574+func (s *Session) SetBatch(n int) {
1575+ if n == 1 {
1576+ // Server interprets 1 as -1 and closes the cursor (!?)
1577+ n = 2
1578+ }
1579+ s.m.Lock()
1580+ s.queryConfig.op.limit = int32(n)
1581+ s.m.Unlock()
1582+}
1583+
1584+// SetPrefetch sets the default point at which the next batch of results will be
1585+// requested. When there are p*batch_size remaining documents cached in an
1586+// Iter, the next batch will be requested in background. For instance, when
1587+// using this:
1588+//
1589+// session.SetBatch(200)
1590+// session.SetPrefetch(0.25)
1591+//
1592+// and there are only 50 documents cached in the Iter to be processed, the
1593+// next batch of 200 will be requested. It's possible to change this setting on
1594+// a per-query basis as well, using the Prefetch method of Query.
1595+//
1596+// The default prefetch value is 0.25.
1597+func (s *Session) SetPrefetch(p float64) {
1598+ s.m.Lock()
1599+ s.queryConfig.prefetch = p
1600+ s.m.Unlock()
1601+}
1602+
1603+// See SetSafe for details on the Safe type.
1604+type Safe struct {
1605+ W int // Min # of servers to ack before success
1606+ WMode string // Write mode for MongoDB 2.0+ (e.g. "majority")
1607+ WTimeout int // Milliseconds to wait for W before timing out
1608+ FSync bool // Should servers sync to disk before returning success
1609+ J bool // Wait for next group commit if journaling; no effect otherwise
1610+}
1611+
1612+// Safe returns the current safety mode for the session.
1613+func (s *Session) Safe() (safe *Safe) {
1614+ s.m.Lock()
1615+ defer s.m.Unlock()
1616+ if s.safeOp != nil {
1617+ cmd := s.safeOp.query.(*getLastError)
1618+ safe = &Safe{WTimeout: cmd.WTimeout, FSync: cmd.FSync, J: cmd.J}
1619+ switch w := cmd.W.(type) {
1620+ case string:
1621+ safe.WMode = w
1622+ case int:
1623+ safe.W = w
1624+ }
1625+ }
1626+ return
1627+}
1628+
1629+// SetSafe changes the session safety mode.
1630+//
1631+// If the safe parameter is nil, the session is put in unsafe mode, and writes
1632+// become fire-and-forget, without error checking. The unsafe mode is faster
1633+// since operations won't hold on waiting for a confirmation.
1634+//
1635+// If the safe parameter is not nil, any changing query (insert, update, ...)
1636+// will be followed by a getLastError command with the specified parameters,
1637+// to ensure the request was correctly processed.
1638+//
1639+// The safe.W parameter determines how many servers should confirm a write
1640+// before the operation is considered successful. If set to 0 or 1, the
1641+// command will return as soon as the primary is done with the request.
1642+// If safe.WTimeout is greater than zero, it determines how many milliseconds
1643+// to wait for the safe.W servers to respond before returning an error.
1644+//
1645+// Starting with MongoDB 2.0.0 the safe.WMode parameter can be used instead
1646+// of W to request for richer semantics. If set to "majority" the server will
1647+// wait for a majority of members from the replica set to respond before
1648+// returning. Custom modes may also be defined within the server to create
1649+// very detailed placement schemas. See the data awareness documentation in
1650+// the links below for more details (note that MongoDB internally reuses the
1651+// "w" field name for WMode).
1652+//
1653+// If safe.FSync is true and journaling is disabled, the servers will be
1654+// forced to sync all files to disk immediately before returning. If the
1655+// same option is true but journaling is enabled, the server will instead
1656+// await for the next group commit before returning.
1657+//
1658+// Since MongoDB 2.0.0, the safe.J option can also be used instead of FSync
1659+// to force the server to wait for a group commit in case journaling is
1660+// enabled. The option has no effect if the server has journaling disabled.
1661+//
1662+// For example, the following statement will make the session check for
1663+// errors, without imposing further constraints:
1664+//
1665+// session.SetSafe(&mgo.Safe{})
1666+//
1667+// The following statement will force the server to wait for a majority of
1668+// members of a replica set to return (MongoDB 2.0+ only):
1669+//
1670+// session.SetSafe(&mgo.Safe{WMode: "majority"})
1671+//
1672+// The following statement, on the other hand, ensures that at least two
1673+// servers have flushed the change to disk before confirming the success
1674+// of operations:
1675+//
1676+// session.EnsureSafe(&mgo.Safe{W: 2, FSync: true})
1677+//
1678+// The following statement, on the other hand, disables the verification
1679+// of errors entirely:
1680+//
1681+// session.SetSafe(nil)
1682+//
1683+// See also the EnsureSafe method.
1684+//
1685+// Relevant documentation:
1686+//
1687+// http://www.mongodb.org/display/DOCS/getLastError+Command
1688+// http://www.mongodb.org/display/DOCS/Verifying+Propagation+of+Writes+with+getLastError
1689+// http://www.mongodb.org/display/DOCS/Data+Center+Awareness
1690+//
1691+func (s *Session) SetSafe(safe *Safe) {
1692+ s.m.Lock()
1693+ s.safeOp = nil
1694+ s.ensureSafe(safe)
1695+ s.m.Unlock()
1696+}
1697+
1698+// EnsureSafe compares the provided safety parameters with the ones
1699+// currently in use by the session and picks the most conservative
1700+// choice for each setting.
1701+//
1702+// That is:
1703+//
1704+// - safe.WMode is always used if set.
1705+// - safe.W is used if larger than the current W and WMode is empty.
1706+// - safe.FSync is always used if true.
1707+// - safe.J is used if FSync is false.
1708+// - safe.WTimeout is used if set and smaller than the current WTimeout.
1709+//
1710+// For example, the following statement will ensure the session is
1711+// at least checking for errors, without enforcing further constraints.
1712+// If a more conservative SetSafe or EnsureSafe call was previously done,
1713+// the following call will be ignored.
1714+//
1715+// session.EnsureSafe(&mgo.Safe{})
1716+//
1717+// See also the SetSafe method for details on what each option means.
1718+//
1719+// Relevant documentation:
1720+//
1721+// http://www.mongodb.org/display/DOCS/getLastError+Command
1722+// http://www.mongodb.org/display/DOCS/Verifying+Propagation+of+Writes+with+getLastError
1723+// http://www.mongodb.org/display/DOCS/Data+Center+Awareness
1724+//
1725+func (s *Session) EnsureSafe(safe *Safe) {
1726+ s.m.Lock()
1727+ s.ensureSafe(safe)
1728+ s.m.Unlock()
1729+}
1730+
1731+func (s *Session) ensureSafe(safe *Safe) {
1732+ if safe == nil {
1733+ return
1734+ }
1735+
1736+ var w interface{}
1737+ if safe.WMode != "" {
1738+ w = safe.WMode
1739+ } else if safe.W > 0 {
1740+ w = safe.W
1741+ }
1742+
1743+ var cmd getLastError
1744+ if s.safeOp == nil {
1745+ cmd = getLastError{1, w, safe.WTimeout, safe.FSync, safe.J}
1746+ } else {
1747+ // Copy. We don't want to mutate the existing query.
1748+ cmd = *(s.safeOp.query.(*getLastError))
1749+ if cmd.W == nil {
1750+ cmd.W = w
1751+ } else if safe.WMode != "" {
1752+ cmd.W = safe.WMode
1753+ } else if i, ok := cmd.W.(int); ok && safe.W > i {
1754+ cmd.W = safe.W
1755+ }
1756+ if safe.WTimeout > 0 && safe.WTimeout < cmd.WTimeout {
1757+ cmd.WTimeout = safe.WTimeout
1758+ }
1759+ if safe.FSync {
1760+ cmd.FSync = true
1761+ cmd.J = false
1762+ } else if safe.J && !cmd.FSync {
1763+ cmd.J = true
1764+ }
1765+ }
1766+ s.safeOp = &queryOp{
1767+ query: &cmd,
1768+ collection: "admin.$cmd",
1769+ limit: -1,
1770+ }
1771+}
1772+
1773+// Run issues the provided command on the "admin" database and
1774+// and unmarshals its result in the respective argument. The cmd
1775+// argument may be either a string with the command name itself, in
1776+// which case an empty document of the form bson.M{cmd: 1} will be used,
1777+// or it may be a full command document.
1778+//
1779+// Note that MongoDB considers the first marshalled key as the command
1780+// name, so when providing a command with options, it's important to
1781+// use an ordering-preserving document, such as a struct value or an
1782+// instance of bson.D. For instance:
1783+//
1784+// db.Run(bson.D{{"create", "mycollection"}, {"size", 1024}})
1785+//
1786+// For commands on arbitrary databases, see the Run method in
1787+// the Database type.
1788+//
1789+// Relevant documentation:
1790+//
1791+// http://www.mongodb.org/display/DOCS/Commands
1792+// http://www.mongodb.org/display/DOCS/List+of+Database+CommandSkips
1793+//
1794+func (s *Session) Run(cmd interface{}, result interface{}) error {
1795+ return s.DB("admin").Run(cmd, result)
1796+}
1797+
1798+// SelectServers restricts communication to servers configured with the
1799+// given tags. For example, the following statement restricts servers
1800+// used for reading operations to those with both tag "disk" set to
1801+// "ssd" and tag "rack" set to 1:
1802+//
1803+// session.SelectSlaves(bson.D{{"disk", "ssd"}, {"rack", 1}})
1804+//
1805+// Multiple sets of tags may be provided, in which case the used server
1806+// must match all tags within any one set.
1807+//
1808+// If a connection was previously assigned to the session due to the
1809+// current session mode (see Session.SetMode), the tag selection will
1810+// only be enforced after the session is refreshed.
1811+//
1812+// Relevant documentation:
1813+//
1814+// http://docs.mongodb.org/manual/tutorial/configure-replica-set-tag-sets
1815+//
1816+func (s *Session) SelectServers(tags ...bson.D) {
1817+ s.m.Lock()
1818+ s.queryConfig.op.serverTags = tags
1819+ s.m.Unlock()
1820+}
1821+
1822+// Ping runs a trivial ping command just to get in touch with the server.
1823+func (s *Session) Ping() error {
1824+ return s.Run("ping", nil)
1825+}
1826+
1827+// Fsync flushes in-memory writes to disk on the server the session
1828+// is established with. If async is true, the call returns immediately,
1829+// otherwise it returns after the flush has been made.
1830+func (s *Session) Fsync(async bool) error {
1831+ return s.Run(bson.D{{"fsync", 1}, {"async", async}}, nil)
1832+}
1833+
1834+// FsyncLock locks all writes in the specific server the session is
1835+// established with and returns. Any writes attempted to the server
1836+// after it is successfully locked will block until FsyncUnlock is
1837+// called for the same server.
1838+//
1839+// This method works on secondaries as well, preventing the oplog from
1840+// being flushed while the server is locked, but since only the server
1841+// connected to is locked, for locking specific secondaries it may be
1842+// necessary to establish a connection directly to the secondary (see
1843+// Dial's connect=direct option).
1844+//
1845+// As an important caveat, note that once a write is attempted and
1846+// blocks, follow up reads will block as well due to the way the
1847+// lock is internally implemented in the server. More details at:
1848+//
1849+// https://jira.mongodb.org/browse/SERVER-4243
1850+//
1851+// FsyncLock is often used for performing consistent backups of
1852+// the database files on disk.
1853+//
1854+// Relevant documentation:
1855+//
1856+// http://www.mongodb.org/display/DOCS/fsync+Command
1857+// http://www.mongodb.org/display/DOCS/Backups
1858+//
1859+func (s *Session) FsyncLock() error {
1860+ return s.Run(bson.D{{"fsync", 1}, {"lock", true}}, nil)
1861+}
1862+
1863+// FsyncUnlock releases the server for writes. See FsyncLock for details.
1864+func (s *Session) FsyncUnlock() error {
1865+ return s.DB("admin").C("$cmd.sys.unlock").Find(nil).One(nil) // WTF?
1866+}
1867+
1868+// Find prepares a query using the provided document. The document may be a
1869+// map or a struct value capable of being marshalled with bson. The map
1870+// may be a generic one using interface{} for its key and/or values, such as
1871+// bson.M, or it may be a properly typed map. Providing nil as the document
1872+// is equivalent to providing an empty document such as bson.M{}.
1873+//
1874+// Further details of the query may be tweaked using the resulting Query value,
1875+// and then executed to retrieve results using methods such as One, For,
1876+// Iter, or Tail.
1877+//
1878+// In case the resulting document includes a field named $err or errmsg, which
1879+// are standard ways for MongoDB to return query errors, the returned err will
1880+// be set to a *QueryError value including the Err message and the Code. In
1881+// those cases, the result argument is still unmarshalled into with the
1882+// received document so that any other custom values may be obtained if
1883+// desired.
1884+//
1885+// Relevant documentation:
1886+//
1887+// http://www.mongodb.org/display/DOCS/Querying
1888+// http://www.mongodb.org/display/DOCS/Advanced+Queries
1889+//
1890+func (c *Collection) Find(query interface{}) *Query {
1891+ session := c.Database.Session
1892+ session.m.RLock()
1893+ q := &Query{session: session, query: session.queryConfig}
1894+ session.m.RUnlock()
1895+ q.op.query = query
1896+ q.op.collection = c.FullName
1897+ return q
1898+}
1899+
1900+type repairCmd struct {
1901+ RepairCursor string `bson:"repairCursor"`
1902+ Cursor *repairCmdCursor ",omitempty"
1903+}
1904+
1905+type repairCmdCursor struct {
1906+ BatchSize int `bson:"batchSize,omitempty"`
1907+}
1908+
1909+// Repair returns an iterator that goes over all recovered documents in the
1910+// collection, in a best-effort manner. This is most useful when there are
1911+// damaged data files. Multiple copies of the same document may be returned
1912+// by the iterator.
1913+//
1914+// Repair is supported in MongoDB 2.7.8 and later.
1915+func (c *Collection) Repair() *Iter {
1916+ // Clone session and set it to Monotonic mode so that the server
1917+ // used for the query may be safely obtained afterwards, if
1918+ // necessary for iteration when a cursor is received.
1919+ session := c.Database.Session
1920+ cloned := session.Clone()
1921+ cloned.SetMode(Monotonic, false)
1922+ defer cloned.Close()
1923+
1924+ batchSize := int(cloned.queryConfig.op.limit)
1925+
1926+ var result struct {
1927+ Cursor struct {
1928+ FirstBatch []bson.Raw "firstBatch"
1929+ Id int64
1930+ }
1931+ }
1932+
1933+ cmd := repairCmd{
1934+ RepairCursor: c.Name,
1935+ Cursor: &repairCmdCursor{batchSize},
1936+ }
1937+
1938+ clonedc := c.With(cloned)
1939+ err := clonedc.Database.Run(cmd, &result)
1940+ return clonedc.NewIter(session, result.Cursor.FirstBatch, result.Cursor.Id, err)
1941+}
1942+
1943+// FindId is a convenience helper equivalent to:
1944+//
1945+// query := collection.Find(bson.M{"_id": id})
1946+//
1947+// See the Find method for more details.
1948+func (c *Collection) FindId(id interface{}) *Query {
1949+ return c.Find(bson.D{{"_id", id}})
1950+}
1951+
1952+type Pipe struct {
1953+ session *Session
1954+ collection *Collection
1955+ pipeline interface{}
1956+ allowDisk bool
1957+ batchSize int
1958+}
1959+
1960+type pipeCmd struct {
1961+ Aggregate string
1962+ Pipeline interface{}
1963+ Cursor *pipeCmdCursor ",omitempty"
1964+ Explain bool ",omitempty"
1965+ AllowDisk bool "allowDiskUse,omitempty"
1966+}
1967+
1968+type pipeCmdCursor struct {
1969+ BatchSize int `bson:"batchSize,omitempty"`
1970+}
1971+
1972+// Pipe prepares a pipeline to aggregate. The pipeline document
1973+// must be a slice built in terms of the aggregation framework language.
1974+//
1975+// For example:
1976+//
1977+// pipe := collection.Pipe([]bson.M{{"$match": bson.M{"name": "Otavio"}}})
1978+// iter := pipe.Iter()
1979+//
1980+// Relevant documentation:
1981+//
1982+// http://docs.mongodb.org/manual/reference/aggregation
1983+// http://docs.mongodb.org/manual/applications/aggregation
1984+// http://docs.mongodb.org/manual/tutorial/aggregation-examples
1985+//
1986+func (c *Collection) Pipe(pipeline interface{}) *Pipe {
1987+ session := c.Database.Session
1988+ session.m.RLock()
1989+ batchSize := int(session.queryConfig.op.limit)
1990+ session.m.RUnlock()
1991+ return &Pipe{
1992+ session: session,
1993+ collection: c,
1994+ pipeline: pipeline,
1995+ batchSize: batchSize,
1996+ }
1997+}
1998+
1999+// Iter executes the pipeline and returns an iterator capable of going
2000+// over all the generated results.
2001+func (p *Pipe) Iter() *Iter {
2002+ // Clone session and set it to Monotonic mode so that the server
2003+ // used for the query may be safely obtained afterwards, if
2004+ // necessary for iteration when a cursor is received.
2005+ cloned := p.session.Clone()
2006+ cloned.SetMode(Monotonic, false)
2007+ defer cloned.Close()
2008+ c := p.collection.With(cloned)
2009+
2010+ var result struct {
2011+ // 2.4, no cursors.
2012+ Result []bson.Raw
2013+
2014+ // 2.6+, with cursors.
2015+ Cursor struct {
2016+ FirstBatch []bson.Raw "firstBatch"
2017+ Id int64
2018+ }
2019+ }
2020+
2021+ cmd := pipeCmd{
2022+ Aggregate: c.Name,
2023+ Pipeline: p.pipeline,
2024+ AllowDisk: p.allowDisk,
2025+ Cursor: &pipeCmdCursor{p.batchSize},
2026+ }
2027+ err := c.Database.Run(cmd, &result)
2028+ if e, ok := err.(*QueryError); ok && e.Message == `unrecognized field "cursor` {
2029+ cmd.Cursor = nil
2030+ cmd.AllowDisk = false
2031+ err = c.Database.Run(cmd, &result)
2032+ }
2033+ firstBatch := result.Result
2034+ if firstBatch == nil {
2035+ firstBatch = result.Cursor.FirstBatch
2036+ }
2037+ return c.NewIter(p.session, firstBatch, result.Cursor.Id, err)
2038+}
2039+
2040+// NewIter returns a newly created iterator with the provided parameters.
2041+// Using this method is not recommended unless the desired functionality
2042+// is not yet exposed via a more convenient interface (Find, Pipe, etc).
2043+//
2044+// The optional session parameter associates the lifetime of the returned
2045+// iterator to an arbitrary session. If nil, the iterator will be bound to
2046+// c's session.
2047+//
2048+// Documents in firstBatch will be individually provided by the returned
2049+// iterator before documents from cursorId are made available. If cursorId
2050+// is zero, only the documents in firstBatch are provided.
2051+//
2052+// If err is not nil, the iterator's Err method will report it after
2053+// exhausting documents in firstBatch.
2054+//
2055+// NewIter must be called right after the cursor id is obtained, and must not
2056+// be called on a collection in Eventual mode, because the cursor id is
2057+// associated with the specific server that returned it. The provided session
2058+// parameter may be in any mode or state, though.
2059+//
2060+func (c *Collection) NewIter(session *Session, firstBatch []bson.Raw, cursorId int64, err error) *Iter {
2061+ var server *mongoServer
2062+ csession := c.Database.Session
2063+ csession.m.RLock()
2064+ socket := csession.masterSocket
2065+ if socket == nil {
2066+ socket = csession.slaveSocket
2067+ }
2068+ if socket != nil {
2069+ server = socket.Server()
2070+ }
2071+ csession.m.RUnlock()
2072+
2073+ if server == nil {
2074+ if csession.Mode() == Eventual {
2075+ panic("Collection.NewIter called in Eventual mode")
2076+ }
2077+ if err == nil {
2078+ err = errors.New("server not available")
2079+ }
2080+ }
2081+
2082+ if session == nil {
2083+ session = csession
2084+ }
2085+
2086+ iter := &Iter{
2087+ session: session,
2088+ server: server,
2089+ timeout: -1,
2090+ err: err,
2091+ }
2092+ iter.gotReply.L = &iter.m
2093+ for _, doc := range firstBatch {
2094+ iter.docData.Push(doc.Data)
2095+ }
2096+ if cursorId != 0 {
2097+ iter.op.cursorId = cursorId
2098+ iter.op.collection = c.FullName
2099+ iter.op.replyFunc = iter.replyFunc()
2100+ }
2101+ return iter
2102+}
2103+
2104+// All works like Iter.All.
2105+func (p *Pipe) All(result interface{}) error {
2106+ return p.Iter().All(result)
2107+}
2108+
2109+// One executes the pipeline and unmarshals the first item from the
2110+// result set into the result parameter.
2111+// It returns ErrNotFound if no items are generated by the pipeline.
2112+func (p *Pipe) One(result interface{}) error {
2113+ iter := p.Iter()
2114+ if iter.Next(result) {
2115+ return nil
2116+ }
2117+ if err := iter.Err(); err != nil {
2118+ return err
2119+ }
2120+ return ErrNotFound
2121+}
2122+
2123+// Explain returns a number of details about how the MongoDB server would
2124+// execute the requested pipeline, such as the number of objects examined,
2125+// the number of times the read lock was yielded to allow writes to go in,
2126+// and so on.
2127+//
2128+// For example:
2129+//
2130+// var m bson.M
2131+// err := collection.Pipe(pipeline).Explain(&m)
2132+// if err == nil {
2133+// fmt.Printf("Explain: %#v\n", m)
2134+// }
2135+//
2136+func (p *Pipe) Explain(result interface{}) error {
2137+ c := p.collection
2138+ cmd := pipeCmd{
2139+ Aggregate: c.Name,
2140+ Pipeline: p.pipeline,
2141+ AllowDisk: p.allowDisk,
2142+ Explain: true,
2143+ }
2144+ return c.Database.Run(cmd, result)
2145+}
2146+
2147+// AllowDiskUse enables writing to the "<dbpath>/_tmp" server directory so
2148+// that aggregation pipelines do not have to be held entirely in memory.
2149+func (p *Pipe) AllowDiskUse() *Pipe {
2150+ p.allowDisk = true
2151+ return p
2152+}
2153+
2154+// Batch sets the batch size used when fetching documents from the database.
2155+// It's possible to change this setting on a per-session basis as well, using
2156+// the Batch method of Session.
2157+//
2158+// The default batch size is defined by the database server.
2159+func (p *Pipe) Batch(n int) *Pipe {
2160+ p.batchSize = n
2161+ return p
2162+}
2163+
2164+type LastError struct {
2165+ Err string
2166+ Code, N, Waited int
2167+ FSyncFiles int `bson:"fsyncFiles"`
2168+ WTimeout bool
2169+ UpdatedExisting bool `bson:"updatedExisting"`
2170+ UpsertedId interface{} `bson:"upserted"`
2171+}
2172+
2173+func (err *LastError) Error() string {
2174+ return err.Err
2175+}
2176+
2177+type queryError struct {
2178+ Err string "$err"
2179+ ErrMsg string
2180+ Assertion string
2181+ Code int
2182+ AssertionCode int "assertionCode"
2183+ LastError *LastError "lastErrorObject"
2184+}
2185+
2186+type QueryError struct {
2187+ Code int
2188+ Message string
2189+ Assertion bool
2190+}
2191+
2192+func (err *QueryError) Error() string {
2193+ return err.Message
2194+}
2195+
2196+// IsDup returns whether err informs of a duplicate key error because
2197+// a primary key index or a secondary unique index already has an entry
2198+// with the given value.
2199+func IsDup(err error) bool {
2200+ // Besides being handy, helps with MongoDB bugs SERVER-7164 and SERVER-11493.
2201+ // What follows makes me sad. Hopefully conventions will be more clear over time.
2202+ switch e := err.(type) {
2203+ case *LastError:
2204+ return e.Code == 11000 || e.Code == 11001 || e.Code == 12582 || e.Code == 16460 && strings.Contains(e.Err, " E11000 ")
2205+ case *QueryError:
2206+ return e.Code == 11000 || e.Code == 11001 || e.Code == 12582
2207+ }
2208+ return false
2209+}
2210+
2211+// Insert inserts one or more documents in the respective collection. In
2212+// case the session is in safe mode (see the SetSafe method) and an error
2213+// happens while inserting the provided documents, the returned error will
2214+// be of type *LastError.
2215+func (c *Collection) Insert(docs ...interface{}) error {
2216+ _, err := c.writeQuery(&insertOp{c.FullName, docs, 0})
2217+ return err
2218+}
2219+
2220+// Update finds a single document matching the provided selector document
2221+// and modifies it according to the update document.
2222+// If the session is in safe mode (see SetSafe) a ErrNotFound error is
2223+// returned if a document isn't found, or a value of type *LastError
2224+// when some other error is detected.
2225+//
2226+// Relevant documentation:
2227+//
2228+// http://www.mongodb.org/display/DOCS/Updating
2229+// http://www.mongodb.org/display/DOCS/Atomic+Operations
2230+//
2231+func (c *Collection) Update(selector interface{}, update interface{}) error {
2232+ lerr, err := c.writeQuery(&updateOp{c.FullName, selector, update, 0})
2233+ if err == nil && lerr != nil && !lerr.UpdatedExisting {
2234+ return ErrNotFound
2235+ }
2236+ return err
2237+}
2238+
2239+// UpdateId is a convenience helper equivalent to:
2240+//
2241+// err := collection.Update(bson.M{"_id": id}, update)
2242+//
2243+// See the Update method for more details.
2244+func (c *Collection) UpdateId(id interface{}, update interface{}) error {
2245+ return c.Update(bson.D{{"_id", id}}, update)
2246+}
2247+
2248+// ChangeInfo holds details about the outcome of an update operation.
2249+type ChangeInfo struct {
2250+ Updated int // Number of existing documents updated
2251+ Removed int // Number of documents removed
2252+ UpsertedId interface{} // Upserted _id field, when not explicitly provided
2253+}
2254+
2255+// UpdateAll finds all documents matching the provided selector document
2256+// and modifies them according to the update document.
2257+// If the session is in safe mode (see SetSafe) details of the executed
2258+// operation are returned in info or an error of type *LastError when
2259+// some problem is detected. It is not an error for the update to not be
2260+// applied on any documents because the selector doesn't match.
2261+//
2262+// Relevant documentation:
2263+//
2264+// http://www.mongodb.org/display/DOCS/Updating
2265+// http://www.mongodb.org/display/DOCS/Atomic+Operations
2266+//
2267+func (c *Collection) UpdateAll(selector interface{}, update interface{}) (info *ChangeInfo, err error) {
2268+ lerr, err := c.writeQuery(&updateOp{c.FullName, selector, update, 2})
2269+ if err == nil && lerr != nil {
2270+ info = &ChangeInfo{Updated: lerr.N}
2271+ }
2272+ return info, err
2273+}
2274+
2275+// Upsert finds a single document matching the provided selector document
2276+// and modifies it according to the update document. If no document matching
2277+// the selector is found, the update document is applied to the selector
2278+// document and the result is inserted in the collection.
2279+// If the session is in safe mode (see SetSafe) details of the executed
2280+// operation are returned in info, or an error of type *LastError when
2281+// some problem is detected.
2282+//
2283+// Relevant documentation:
2284+//
2285+// http://www.mongodb.org/display/DOCS/Updating
2286+// http://www.mongodb.org/display/DOCS/Atomic+Operations
2287+//
2288+func (c *Collection) Upsert(selector interface{}, update interface{}) (info *ChangeInfo, err error) {
2289+ lerr, err := c.writeQuery(&updateOp{c.FullName, selector, update, 1})
2290+ if err == nil && lerr != nil {
2291+ info = &ChangeInfo{}
2292+ if lerr.UpdatedExisting {
2293+ info.Updated = lerr.N
2294+ } else {
2295+ info.UpsertedId = lerr.UpsertedId
2296+ }
2297+ }
2298+ return info, err
2299+}
2300+
2301+// UpsertId is a convenience helper equivalent to:
2302+//
2303+// info, err := collection.Upsert(bson.M{"_id": id}, update)
2304+//
2305+// See the Upsert method for more details.
2306+func (c *Collection) UpsertId(id interface{}, update interface{}) (info *ChangeInfo, err error) {
2307+ return c.Upsert(bson.D{{"_id", id}}, update)
2308+}
2309+
2310+// Remove finds a single document matching the provided selector document
2311+// and removes it from the database.
2312+// If the session is in safe mode (see SetSafe) a ErrNotFound error is
2313+// returned if a document isn't found, or a value of type *LastError
2314+// when some other error is detected.
2315+//
2316+// Relevant documentation:
2317+//
2318+// http://www.mongodb.org/display/DOCS/Removing
2319+//
2320+func (c *Collection) Remove(selector interface{}) error {
2321+ lerr, err := c.writeQuery(&deleteOp{c.FullName, selector, 1})
2322+ if err == nil && lerr != nil && lerr.N == 0 {
2323+ return ErrNotFound
2324+ }
2325+ return err
2326+}
2327+
2328+// RemoveId is a convenience helper equivalent to:
2329+//
2330+// err := collection.Remove(bson.M{"_id": id})
2331+//
2332+// See the Remove method for more details.
2333+func (c *Collection) RemoveId(id interface{}) error {
2334+ return c.Remove(bson.D{{"_id", id}})
2335+}
2336+
2337+// RemoveAll finds all documents matching the provided selector document
2338+// and removes them from the database. In case the session is in safe mode
2339+// (see the SetSafe method) and an error happens when attempting the change,
2340+// the returned error will be of type *LastError.
2341+//
2342+// Relevant documentation:
2343+//
2344+// http://www.mongodb.org/display/DOCS/Removing
2345+//
2346+func (c *Collection) RemoveAll(selector interface{}) (info *ChangeInfo, err error) {
2347+ lerr, err := c.writeQuery(&deleteOp{c.FullName, selector, 0})
2348+ if err == nil && lerr != nil {
2349+ info = &ChangeInfo{Removed: lerr.N}
2350+ }
2351+ return info, err
2352+}
2353+
2354+// DropDatabase removes the entire database including all of its collections.
2355+func (db *Database) DropDatabase() error {
2356+ return db.Run(bson.D{{"dropDatabase", 1}}, nil)
2357+}
2358+
2359+// DropCollection removes the entire collection including all of its documents.
2360+func (c *Collection) DropCollection() error {
2361+ return c.Database.Run(bson.D{{"drop", c.Name}}, nil)
2362+}
2363+
2364+// The CollectionInfo type holds metadata about a collection.
2365+//
2366+// Relevant documentation:
2367+//
2368+// http://www.mongodb.org/display/DOCS/createCollection+Command
2369+// http://www.mongodb.org/display/DOCS/Capped+Collections
2370+//
2371+type CollectionInfo struct {
2372+ // DisableIdIndex prevents the automatic creation of the index
2373+ // on the _id field for the collection.
2374+ DisableIdIndex bool
2375+
2376+ // ForceIdIndex enforces the automatic creation of the index
2377+ // on the _id field for the collection. Capped collections,
2378+ // for example, do not have such an index by default.
2379+ ForceIdIndex bool
2380+
2381+ // If Capped is true new documents will replace old ones when
2382+ // the collection is full. MaxBytes must necessarily be set
2383+ // to define the size when the collection wraps around.
2384+ // MaxDocs optionally defines the number of documents when it
2385+ // wraps, but MaxBytes still needs to be set.
2386+ Capped bool
2387+ MaxBytes int
2388+ MaxDocs int
2389+}
2390+
2391+// Create explicitly creates the c collection with details of info.
2392+// MongoDB creates collections automatically on use, so this method
2393+// is only necessary when creating collection with non-default
2394+// characteristics, such as capped collections.
2395+//
2396+// Relevant documentation:
2397+//
2398+// http://www.mongodb.org/display/DOCS/createCollection+Command
2399+// http://www.mongodb.org/display/DOCS/Capped+Collections
2400+//
2401+func (c *Collection) Create(info *CollectionInfo) error {
2402+ cmd := make(bson.D, 0, 4)
2403+ cmd = append(cmd, bson.DocElem{"create", c.Name})
2404+ if info.Capped {
2405+ if info.MaxBytes < 1 {
2406+ return fmt.Errorf("Collection.Create: with Capped, MaxBytes must also be set")
2407+ }
2408+ cmd = append(cmd, bson.DocElem{"capped", true})
2409+ cmd = append(cmd, bson.DocElem{"size", info.MaxBytes})
2410+ if info.MaxDocs > 0 {
2411+ cmd = append(cmd, bson.DocElem{"max", info.MaxDocs})
2412+ }
2413+ }
2414+ if info.DisableIdIndex {
2415+ cmd = append(cmd, bson.DocElem{"autoIndexId", false})
2416+ }
2417+ if info.ForceIdIndex {
2418+ cmd = append(cmd, bson.DocElem{"autoIndexId", true})
2419+ }
2420+ return c.Database.Run(cmd, nil)
2421+}
2422+
2423+// Batch sets the batch size used when fetching documents from the database.
2424+// It's possible to change this setting on a per-session basis as well, using
2425+// the Batch method of Session.
2426+//
2427+// The default batch size is defined by the database itself. As of this
2428+// writing, MongoDB will use an initial size of min(100 docs, 4MB) on the
2429+// first batch, and 4MB on remaining ones.
2430+func (q *Query) Batch(n int) *Query {
2431+ if n == 1 {
2432+ // Server interprets 1 as -1 and closes the cursor (!?)
2433+ n = 2
2434+ }
2435+ q.m.Lock()
2436+ q.op.limit = int32(n)
2437+ q.m.Unlock()
2438+ return q
2439+}
2440+
2441+// Prefetch sets the point at which the next batch of results will be requested.
2442+// When there are p*batch_size remaining documents cached in an Iter, the next
2443+// batch will be requested in background. For instance, when using this:
2444+//
2445+// query.Batch(200).Prefetch(0.25)
2446+//
2447+// and there are only 50 documents cached in the Iter to be processed, the
2448+// next batch of 200 will be requested. It's possible to change this setting on
2449+// a per-session basis as well, using the SetPrefetch method of Session.
2450+//
2451+// The default prefetch value is 0.25.
2452+func (q *Query) Prefetch(p float64) *Query {
2453+ q.m.Lock()
2454+ q.prefetch = p
2455+ q.m.Unlock()
2456+ return q
2457+}
2458+
2459+// Skip skips over the n initial documents from the query results. Note that
2460+// this only makes sense with capped collections where documents are naturally
2461+// ordered by insertion time, or with sorted results.
2462+func (q *Query) Skip(n int) *Query {
2463+ q.m.Lock()
2464+ q.op.skip = int32(n)
2465+ q.m.Unlock()
2466+ return q
2467+}
2468+
2469+// Limit restricts the maximum number of documents retrieved to n, and also
2470+// changes the batch size to the same value. Once n documents have been
2471+// returned by Next, the following call will return ErrNotFound.
2472+func (q *Query) Limit(n int) *Query {
2473+ q.m.Lock()
2474+ switch {
2475+ case n == 1:
2476+ q.limit = 1
2477+ q.op.limit = -1
2478+ case n == math.MinInt32: // -MinInt32 == -MinInt32
2479+ q.limit = math.MaxInt32
2480+ q.op.limit = math.MinInt32 + 1
2481+ case n < 0:
2482+ q.limit = int32(-n)
2483+ q.op.limit = int32(n)
2484+ default:
2485+ q.limit = int32(n)
2486+ q.op.limit = int32(n)
2487+ }
2488+ q.m.Unlock()
2489+ return q
2490+}
2491+
2492+// Select enables selecting which fields should be retrieved for the results
2493+// found. For example, the following query would only retrieve the name field:
2494+//
2495+// err := collection.Find(nil).Select(bson.M{"name": 1}).One(&result)
2496+//
2497+// Relevant documentation:
2498+//
2499+// http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields
2500+//
2501+func (q *Query) Select(selector interface{}) *Query {
2502+ q.m.Lock()
2503+ q.op.selector = selector
2504+ q.m.Unlock()
2505+ return q
2506+}
2507+
2508+// Sort asks the database to order returned documents according to the
2509+// provided field names. A field name may be prefixed by - (minus) for
2510+// it to be sorted in reverse order.
2511+//
2512+// For example:
2513+//
2514+// query1 := collection.Find(nil).Sort("firstname", "lastname")
2515+// query2 := collection.Find(nil).Sort("-age")
2516+// query3 := collection.Find(nil).Sort("$natural")
2517+// query4 := collection.Find(nil).Select(bson.M{"score": bson.M{"$meta": "textScore"}}).Sort("$textScore:score")
2518+//
2519+// Relevant documentation:
2520+//
2521+// http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order
2522+//
2523+func (q *Query) Sort(fields ...string) *Query {
2524+ q.m.Lock()
2525+ var order bson.D
2526+ for _, field := range fields {
2527+ n := 1
2528+ var kind string
2529+ if field != "" {
2530+ if field[0] == '$' {
2531+ if c := strings.Index(field, ":"); c > 1 && c < len(field)-1 {
2532+ kind = field[1:c]
2533+ field = field[c+1:]
2534+ }
2535+ }
2536+ switch field[0] {
2537+ case '+':
2538+ field = field[1:]
2539+ case '-':
2540+ n = -1
2541+ field = field[1:]
2542+ }
2543+ }
2544+ if field == "" {
2545+ panic("Sort: empty field name")
2546+ }
2547+ if kind == "textScore" {
2548+ order = append(order, bson.DocElem{field, bson.M{"$meta": kind}})
2549+ } else {
2550+ order = append(order, bson.DocElem{field, n})
2551+ }
2552+ }
2553+ q.op.options.OrderBy = order
2554+ q.op.hasOptions = true
2555+ q.m.Unlock()
2556+ return q
2557+}
2558+
2559+// Explain returns a number of details about how the MongoDB server would
2560+// execute the requested query, such as the number of objects examined,
2561+// the number of times the read lock was yielded to allow writes to go in,
2562+// and so on.
2563+//
2564+// For example:
2565+//
2566+// m := bson.M{}
2567+// err := collection.Find(bson.M{"filename": name}).Explain(m)
2568+// if err == nil {
2569+// fmt.Printf("Explain: %#v\n", m)
2570+// }
2571+//
2572+// Relevant documentation:
2573+//
2574+// http://www.mongodb.org/display/DOCS/Optimization
2575+// http://www.mongodb.org/display/DOCS/Query+Optimizer
2576+//
2577+func (q *Query) Explain(result interface{}) error {
2578+ q.m.Lock()
2579+ clone := &Query{session: q.session, query: q.query}
2580+ q.m.Unlock()
2581+ clone.op.options.Explain = true
2582+ clone.op.hasOptions = true
2583+ if clone.op.limit > 0 {
2584+ clone.op.limit = -q.op.limit
2585+ }
2586+ iter := clone.Iter()
2587+ if iter.Next(result) {
2588+ return nil
2589+ }
2590+ return iter.Close()
2591+}
2592+
2593+// Hint will include an explicit "hint" in the query to force the server
2594+// to use a specified index, potentially improving performance in some
2595+// situations. The provided parameters are the fields that compose the
2596+// key of the index to be used. For details on how the indexKey may be
2597+// built, see the EnsureIndex method.
2598+//
2599+// For example:
2600+//
2601+// query := collection.Find(bson.M{"firstname": "Joe", "lastname": "Winter"})
2602+// query.Hint("lastname", "firstname")
2603+//
2604+// Relevant documentation:
2605+//
2606+// http://www.mongodb.org/display/DOCS/Optimization
2607+// http://www.mongodb.org/display/DOCS/Query+Optimizer
2608+//
2609+func (q *Query) Hint(indexKey ...string) *Query {
2610+ q.m.Lock()
2611+ keyInfo, err := parseIndexKey(indexKey)
2612+ q.op.options.Hint = keyInfo.key
2613+ q.op.hasOptions = true
2614+ q.m.Unlock()
2615+ if err != nil {
2616+ panic(err)
2617+ }
2618+ return q
2619+}
2620+
2621+// SetMaxScan constrains the query to stop after scanning the specified
2622+// number of documents.
2623+//
2624+// This modifier is generally used to prevent potentially long running
2625+// queries from disrupting performance by scanning through too much data.
2626+func (q *Query) SetMaxScan(n int) *Query {
2627+ q.m.Lock()
2628+ q.op.options.MaxScan = n
2629+ q.op.hasOptions = true
2630+ q.m.Unlock()
2631+ return q
2632+}
2633+
2634+// SetMaxTime constrains the query to stop after running for the specified time.
2635+//
2636+// When the time limit is reached MongoDB automatically cancels the query.
2637+// This can be used to efficiently prevent and identify unexpectedly slow queries.
2638+//
2639+// A few important notes about the mechanism enforcing this limit:
2640+//
2641+// - Requests can block behind locking operations on the server, and that blocking
2642+// time is not accounted for. In other words, the timer starts ticking only after
2643+// the actual start of the query when it initially acquires the appropriate lock;
2644+//
2645+// - Operations are interrupted only at interrupt points where an operation can be
2646+// safely aborted – the total execution time may exceed the specified value;
2647+//
2648+// - The limit can be applied to both CRUD operations and commands, but not all
2649+// commands are interruptible;
2650+//
2651+// - While iterating over results, computing follow up batches is included in the
2652+// total time and the iteration continues until the alloted time is over, but
2653+// network roundtrips are not taken into account for the limit.
2654+//
2655+// - This limit does not override the inactive cursor timeout for idle cursors
2656+// (default is 10 min).
2657+//
2658+// This mechanism was introduced in MongoDB 2.6.
2659+//
2660+// Relevant documentation:
2661+//
2662+// http://blog.mongodb.org/post/83621787773/maxtimems-and-query-optimizer-introspection-in
2663+//
2664+func (q *Query) SetMaxTime(d time.Duration) *Query {
2665+ q.m.Lock()
2666+ q.op.options.MaxTimeMS = int(d / time.Millisecond)
2667+ q.op.hasOptions = true
2668+ q.m.Unlock()
2669+ return q
2670+}
2671+
2672+// Snapshot will force the performed query to make use of an available
2673+// index on the _id field to prevent the same document from being returned
2674+// more than once in a single iteration. This might happen without this
2675+// setting in situations when the document changes in size and thus has to
2676+// be moved while the iteration is running.
2677+//
2678+// Because snapshot mode traverses the _id index, it may not be used with
2679+// sorting or explicit hints. It also cannot use any other index for the
2680+// query.
2681+//
2682+// Even with snapshot mode, items inserted or deleted during the query may
2683+// or may not be returned; that is, this mode is not a true point-in-time
2684+// snapshot.
2685+//
2686+// The same effect of Snapshot may be obtained by using any unique index on
2687+// field(s) that will not be modified (best to use Hint explicitly too).
2688+// A non-unique index (such as creation time) may be made unique by
2689+// appending _id to the index when creating it.
2690+//
2691+// Relevant documentation:
2692+//
2693+// http://www.mongodb.org/display/DOCS/How+to+do+Snapshotted+Queries+in+the+Mongo+Database
2694+//
2695+func (q *Query) Snapshot() *Query {
2696+ q.m.Lock()
2697+ q.op.options.Snapshot = true
2698+ q.op.hasOptions = true
2699+ q.m.Unlock()
2700+ return q
2701+}
2702+
2703+// Comment adds a comment to the query to identify it in the database profiler output.
2704+//
2705+// Relevant documentation:
2706+//
2707+// http://docs.mongodb.org/manual/reference/operator/meta/comment
2708+// http://docs.mongodb.org/manual/reference/command/profile
2709+// http://docs.mongodb.org/manual/administration/analyzing-mongodb-performance/#database-profiling
2710+//
2711+func (q *Query) Comment(comment string) *Query {
2712+ q.m.Lock()
2713+ q.op.options.Comment = comment
2714+ q.op.hasOptions = true
2715+ q.m.Unlock()
2716+ return q
2717+}
2718+
2719+// LogReplay enables an option that optimizes queries that are typically
2720+// made on the MongoDB oplog for replaying it. This is an internal
2721+// implementation aspect and most likely uninteresting for other uses.
2722+// It has seen at least one use case, though, so it's exposed via the API.
2723+func (q *Query) LogReplay() *Query {
2724+ q.m.Lock()
2725+ q.op.flags |= flagLogReplay
2726+ q.m.Unlock()
2727+ return q
2728+}
2729+
2730+func checkQueryError(fullname string, d []byte) error {
2731+ l := len(d)
2732+ if l < 16 {
2733+ return nil
2734+ }
2735+ if d[5] == '$' && d[6] == 'e' && d[7] == 'r' && d[8] == 'r' && d[9] == '\x00' && d[4] == '\x02' {
2736+ goto Error
2737+ }
2738+ if len(fullname) < 5 || fullname[len(fullname)-5:] != ".$cmd" {
2739+ return nil
2740+ }
2741+ for i := 0; i+8 < l; i++ {
2742+ if d[i] == '\x02' && d[i+1] == 'e' && d[i+2] == 'r' && d[i+3] == 'r' && d[i+4] == 'm' && d[i+5] == 's' && d[i+6] == 'g' && d[i+7] == '\x00' {
2743+ goto Error
2744+ }
2745+ }
2746+ return nil
2747+
2748+Error:
2749+ result := &queryError{}
2750+ bson.Unmarshal(d, result)
2751+ if result.LastError != nil {
2752+ return result.LastError
2753+ }
2754+ if result.Err == "" && result.ErrMsg == "" {
2755+ return nil
2756+ }
2757+ if result.AssertionCode != 0 && result.Assertion != "" {
2758+ return &QueryError{Code: result.AssertionCode, Message: result.Assertion, Assertion: true}
2759+ }
2760+ if result.Err != "" {
2761+ return &QueryError{Code: result.Code, Message: result.Err}
2762+ }
2763+ return &QueryError{Code: result.Code, Message: result.ErrMsg}
2764+}
2765+
2766+// One executes the query and unmarshals the first obtained document into the
2767+// result argument. The result must be a struct or map value capable of being
2768+// unmarshalled into by gobson. This function blocks until either a result
2769+// is available or an error happens. For example:
2770+//
2771+// err := collection.Find(bson.M{"a", 1}).One(&result)
2772+//
2773+// In case the resulting document includes a field named $err or errmsg, which
2774+// are standard ways for MongoDB to return query errors, the returned err will
2775+// be set to a *QueryError value including the Err message and the Code. In
2776+// those cases, the result argument is still unmarshalled into with the
2777+// received document so that any other custom values may be obtained if
2778+// desired.
2779+//
2780+func (q *Query) One(result interface{}) (err error) {
2781+ q.m.Lock()
2782+ session := q.session
2783+ op := q.op // Copy.
2784+ q.m.Unlock()
2785+
2786+ socket, err := session.acquireSocket(true)
2787+ if err != nil {
2788+ return err
2789+ }
2790+ defer socket.Release()
2791+
2792+ op.flags |= session.slaveOkFlag()
2793+ op.limit = -1
2794+
2795+ data, err := socket.SimpleQuery(&op)
2796+ if err != nil {
2797+ return err
2798+ }
2799+ if data == nil {
2800+ return ErrNotFound
2801+ }
2802+ if result != nil {
2803+ err = bson.Unmarshal(data, result)
2804+ if err == nil {
2805+ debugf("Query %p document unmarshaled: %#v", q, result)
2806+ } else {
2807+ debugf("Query %p document unmarshaling failed: %#v", q, err)
2808+ return err
2809+ }
2810+ }
2811+ return checkQueryError(op.collection, data)
2812+}
2813+
2814+// run duplicates the behavior of collection.Find(query).One(&result)
2815+// as performed by Database.Run, specializing the logic for running
2816+// database commands on a given socket.
2817+func (db *Database) run(socket *mongoSocket, cmd, result interface{}) (err error) {
2818+ // Database.Run:
2819+ if name, ok := cmd.(string); ok {
2820+ cmd = bson.D{{name, 1}}
2821+ }
2822+
2823+ // Collection.Find:
2824+ session := db.Session
2825+ session.m.RLock()
2826+ op := session.queryConfig.op // Copy.
2827+ session.m.RUnlock()
2828+ op.query = cmd
2829+ op.collection = db.Name + ".$cmd"
2830+
2831+ // Query.One:
2832+ op.flags |= session.slaveOkFlag()
2833+ op.limit = -1
2834+
2835+ data, err := socket.SimpleQuery(&op)
2836+ if err != nil {
2837+ return err
2838+ }
2839+ if data == nil {
2840+ return ErrNotFound
2841+ }
2842+ if result != nil {
2843+ err = bson.Unmarshal(data, result)
2844+ if err == nil {
2845+ var res bson.M
2846+ bson.Unmarshal(data, &res)
2847+ debugf("Run command unmarshaled: %#v, result: %#v", op, res)
2848+ } else {
2849+ debugf("Run command unmarshaling failed: %#v", op, err)
2850+ return err
2851+ }
2852+ }
2853+ return checkQueryError(op.collection, data)
2854+}
2855+
2856+// The DBRef type implements support for the database reference MongoDB
2857+// convention as supported by multiple drivers. This convention enables
2858+// cross-referencing documents between collections and databases using
2859+// a structure which includes a collection name, a document id, and
2860+// optionally a database name.
2861+//
2862+// See the FindRef methods on Session and on Database.
2863+//
2864+// Relevant documentation:
2865+//
2866+// http://www.mongodb.org/display/DOCS/Database+References
2867+//
2868+type DBRef struct {
2869+ Collection string `bson:"$ref"`
2870+ Id interface{} `bson:"$id"`
2871+ Database string `bson:"$db,omitempty"`
2872+}
2873+
2874+// NOTE: Order of fields for DBRef above does matter, per documentation.
2875+
2876+// FindRef returns a query that looks for the document in the provided
2877+// reference. If the reference includes the DB field, the document will
2878+// be retrieved from the respective database.
2879+//
2880+// See also the DBRef type and the FindRef method on Session.
2881+//
2882+// Relevant documentation:
2883+//
2884+// http://www.mongodb.org/display/DOCS/Database+References
2885+//
2886+func (db *Database) FindRef(ref *DBRef) *Query {
2887+ var c *Collection
2888+ if ref.Database == "" {
2889+ c = db.C(ref.Collection)
2890+ } else {
2891+ c = db.Session.DB(ref.Database).C(ref.Collection)
2892+ }
2893+ return c.FindId(ref.Id)
2894+}
2895+
2896+// FindRef returns a query that looks for the document in the provided
2897+// reference. For a DBRef to be resolved correctly at the session level
2898+// it must necessarily have the optional DB field defined.
2899+//
2900+// See also the DBRef type and the FindRef method on Database.
2901+//
2902+// Relevant documentation:
2903+//
2904+// http://www.mongodb.org/display/DOCS/Database+References
2905+//
2906+func (s *Session) FindRef(ref *DBRef) *Query {
2907+ if ref.Database == "" {
2908+ panic(errors.New(fmt.Sprintf("Can't resolve database for %#v", ref)))
2909+ }
2910+ c := s.DB(ref.Database).C(ref.Collection)
2911+ return c.FindId(ref.Id)
2912+}
2913+
2914+// CollectionNames returns the collection names present in the db database.
2915+func (db *Database) CollectionNames() (names []string, err error) {
2916+ // Clone session and set it to Monotonic mode so that the server
2917+ // used for the query may be safely obtained afterwards, if
2918+ // necessary for iteration when a cursor is received.
2919+ session := db.Session
2920+ cloned := session.Clone()
2921+ cloned.SetMode(Monotonic, false)
2922+ defer cloned.Close()
2923+
2924+ batchSize := int(cloned.queryConfig.op.limit)
2925+
2926+ // Try with a command.
2927+ var result struct {
2928+ Collections []bson.Raw
2929+
2930+ Cursor struct {
2931+ FirstBatch []bson.Raw "firstBatch"
2932+ NS string
2933+ Id int64
2934+ }
2935+ }
2936+ err = db.With(cloned).Run(bson.D{{"listCollections", 1}, {"cursor", bson.D{{"batchSize", batchSize}}}}, &result)
2937+ if err == nil {
2938+ firstBatch := result.Collections
2939+ if firstBatch == nil {
2940+ firstBatch = result.Cursor.FirstBatch
2941+ }
2942+ var iter *Iter
2943+ ns := strings.SplitN(result.Cursor.NS, ".", 2)
2944+ if len(ns) < 2 {
2945+ iter = db.With(cloned).C("").NewIter(nil, firstBatch, result.Cursor.Id, nil)
2946+ } else {
2947+ iter = cloned.DB(ns[0]).C(ns[1]).NewIter(nil, firstBatch, result.Cursor.Id, nil)
2948+ }
2949+ var coll struct{ Name string }
2950+ for iter.Next(&coll) {
2951+ names = append(names, coll.Name)
2952+ }
2953+ if err := iter.Close(); err != nil {
2954+ return nil, err
2955+ }
2956+ sort.Strings(names)
2957+ return names, err
2958+ }
2959+ if err != nil && !isNoCmd(err) {
2960+ return nil, err
2961+ }
2962+
2963+ // Command not yet supported. Query the database instead.
2964+ nameIndex := len(db.Name) + 1
2965+ iter := db.C("system.namespaces").Find(nil).Iter()
2966+ var coll struct{ Name string }
2967+ for iter.Next(&coll) {
2968+ if strings.Index(coll.Name, "$") < 0 || strings.Index(coll.Name, ".oplog.$") >= 0 {
2969+ names = append(names, coll.Name[nameIndex:])
2970+ }
2971+ }
2972+ if err := iter.Close(); err != nil {
2973+ return nil, err
2974+ }
2975+ sort.Strings(names)
2976+ return names, nil
2977+}
2978+
2979+type dbNames struct {
2980+ Databases []struct {
2981+ Name string
2982+ Empty bool
2983+ }
2984+}
2985+
2986+// DatabaseNames returns the names of non-empty databases present in the cluster.
2987+func (s *Session) DatabaseNames() (names []string, err error) {
2988+ var result dbNames
2989+ err = s.Run("listDatabases", &result)
2990+ if err != nil {
2991+ return nil, err
2992+ }
2993+ for _, db := range result.Databases {
2994+ if !db.Empty {
2995+ names = append(names, db.Name)
2996+ }
2997+ }
2998+ sort.Strings(names)
2999+ return names, nil
3000+}
3001+
3002+// Iter executes the query and returns an iterator capable of going over all
3003+// the results. Results will be returned in batches of configurable
3004+// size (see the Batch method) and more documents will be requested when a
3005+// configurable number of documents is iterated over (see the Prefetch method).
3006+func (q *Query) Iter() *Iter {
3007+ q.m.Lock()
3008+ session := q.session
3009+ op := q.op
3010+ prefetch := q.prefetch
3011+ limit := q.limit
3012+ q.m.Unlock()
3013+
3014+ iter := &Iter{
3015+ session: session,
3016+ prefetch: prefetch,
3017+ limit: limit,
3018+ timeout: -1,
3019+ }
3020+ iter.gotReply.L = &iter.m
3021+ iter.op.collection = op.collection
3022+ iter.op.limit = op.limit
3023+ iter.op.replyFunc = iter.replyFunc()
3024+ iter.docsToReceive++
3025+ op.replyFunc = iter.op.replyFunc
3026+ op.flags |= session.slaveOkFlag()
3027+
3028+ socket, err := session.acquireSocket(true)
3029+ if err != nil {
3030+ iter.err = err
3031+ } else {
3032+ iter.server = socket.Server()
3033+ err = socket.Query(&op)
3034+ if err != nil {
3035+ // Must lock as the query above may call replyFunc.
3036+ iter.m.Lock()
3037+ iter.err = err
3038+ iter.m.Unlock()
3039+ }
3040+ socket.Release()
3041+ }
3042+ return iter
3043+}
3044+
3045+// Tail returns a tailable iterator. Unlike a normal iterator, a
3046+// tailable iterator may wait for new values to be inserted in the
3047+// collection once the end of the current result set is reached,
3048+// A tailable iterator may only be used with capped collections.
3049+//
3050+// The timeout parameter indicates how long Next will block waiting
3051+// for a result before timing out. If set to -1, Next will not
3052+// timeout, and will continue waiting for a result for as long as
3053+// the cursor is valid and the session is not closed. If set to 0,
3054+// Next times out as soon as it reaches the end of the result set.
3055+// Otherwise, Next will wait for at least the given number of
3056+// seconds for a new document to be available before timing out.
3057+//
3058+// On timeouts, Next will unblock and return false, and the Timeout
3059+// method will return true if called. In these cases, Next may still
3060+// be called again on the same iterator to check if a new value is
3061+// available at the current cursor position, and again it will block
3062+// according to the specified timeoutSecs. If the cursor becomes
3063+// invalid, though, both Next and Timeout will return false and
3064+// the query must be restarted.
3065+//
3066+// The following example demonstrates timeout handling and query
3067+// restarting:
3068+//
3069+// iter := collection.Find(nil).Sort("$natural").Tail(5 * time.Second)
3070+// for {
3071+// for iter.Next(&result) {
3072+// fmt.Println(result.Id)
3073+// lastId = result.Id
3074+// }
3075+// if iter.Err() != nil {
3076+// return iter.Close()
3077+// }
3078+// if iter.Timeout() {
3079+// continue
3080+// }
3081+// query := collection.Find(bson.M{"_id": bson.M{"$gt": lastId}})
3082+// iter = query.Sort("$natural").Tail(5 * time.Second)
3083+// }
3084+// iter.Close()
3085+//
3086+// Relevant documentation:
3087+//
3088+// http://www.mongodb.org/display/DOCS/Tailable+Cursors
3089+// http://www.mongodb.org/display/DOCS/Capped+Collections
3090+// http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order
3091+//
3092+func (q *Query) Tail(timeout time.Duration) *Iter {
3093+ q.m.Lock()
3094+ session := q.session
3095+ op := q.op
3096+ prefetch := q.prefetch
3097+ q.m.Unlock()
3098+
3099+ iter := &Iter{session: session, prefetch: prefetch}
3100+ iter.gotReply.L = &iter.m
3101+ iter.timeout = timeout
3102+ iter.op.collection = op.collection
3103+ iter.op.limit = op.limit
3104+ iter.op.replyFunc = iter.replyFunc()
3105+ iter.docsToReceive++
3106+ op.replyFunc = iter.op.replyFunc
3107+ op.flags |= flagTailable | flagAwaitData | session.slaveOkFlag()
3108+
3109+ socket, err := session.acquireSocket(true)
3110+ if err != nil {
3111+ iter.err = err
3112+ } else {
3113+ iter.server = socket.Server()
3114+ err = socket.Query(&op)
3115+ if err != nil {
3116+ // Must lock as the query above may call replyFunc.
3117+ iter.m.Lock()
3118+ iter.err = err
3119+ iter.m.Unlock()
3120+ }
3121+ socket.Release()
3122+ }
3123+ return iter
3124+}
3125+
3126+func (s *Session) slaveOkFlag() (flag queryOpFlags) {
3127+ s.m.RLock()
3128+ if s.slaveOk {
3129+ flag = flagSlaveOk
3130+ }
3131+ s.m.RUnlock()
3132+ return
3133+}
3134+
3135+// Err returns nil if no errors happened during iteration, or the actual
3136+// error otherwise.
3137+//
3138+// In case a resulting document included a field named $err or errmsg, which are
3139+// standard ways for MongoDB to report an improper query, the returned value has
3140+// a *QueryError type, and includes the Err message and the Code.
3141+func (iter *Iter) Err() error {
3142+ iter.m.Lock()
3143+ err := iter.err
3144+ iter.m.Unlock()
3145+ if err == ErrNotFound {
3146+ return nil
3147+ }
3148+ return err
3149+}
3150+
3151+// Close kills the server cursor used by the iterator, if any, and returns
3152+// nil if no errors happened during iteration, or the actual error otherwise.
3153+//
3154+// Server cursors are automatically closed at the end of an iteration, which
3155+// means close will do nothing unless the iteration was interrupted before
3156+// the server finished sending results to the driver. If Close is not called
3157+// in such a situation, the cursor will remain available at the server until
3158+// the default cursor timeout period is reached. No further problems arise.
3159+//
3160+// Close is idempotent. That means it can be called repeatedly and will
3161+// return the same result every time.
3162+//
3163+// In case a resulting document included a field named $err or errmsg, which are
3164+// standard ways for MongoDB to report an improper query, the returned value has
3165+// a *QueryError type.
3166+func (iter *Iter) Close() error {
3167+ iter.m.Lock()
3168+ cursorId := iter.op.cursorId
3169+ iter.op.cursorId = 0
3170+ err := iter.err
3171+ iter.m.Unlock()
3172+ if cursorId == 0 {
3173+ if err == ErrNotFound {
3174+ return nil
3175+ }
3176+ return err
3177+ }
3178+ socket, err := iter.acquireSocket()
3179+ if err == nil {
3180+ // TODO Batch kills.
3181+ err = socket.Query(&killCursorsOp{[]int64{cursorId}})
3182+ socket.Release()
3183+ }
3184+
3185+ iter.m.Lock()
3186+ if err != nil && (iter.err == nil || iter.err == ErrNotFound) {
3187+ iter.err = err
3188+ } else if iter.err != ErrNotFound {
3189+ err = iter.err
3190+ }
3191+ iter.m.Unlock()
3192+ return err
3193+}
3194+
3195+// Timeout returns true if Next returned false due to a timeout of
3196+// a tailable cursor. In those cases, Next may be called again to continue
3197+// the iteration at the previous cursor position.
3198+func (iter *Iter) Timeout() bool {
3199+ iter.m.Lock()
3200+ result := iter.timedout
3201+ iter.m.Unlock()
3202+ return result
3203+}
3204+
3205+// Next retrieves the next document from the result set, blocking if necessary.
3206+// This method will also automatically retrieve another batch of documents from
3207+// the server when the current one is exhausted, or before that in background
3208+// if pre-fetching is enabled (see the Query.Prefetch and Session.SetPrefetch
3209+// methods).
3210+//
3211+// Next returns true if a document was successfully unmarshalled onto result,
3212+// and false at the end of the result set or if an error happened.
3213+// When Next returns false, the Err method should be called to verify if
3214+// there was an error during iteration.
3215+//
3216+// For example:
3217+//
3218+// iter := collection.Find(nil).Iter()
3219+// for iter.Next(&result) {
3220+// fmt.Printf("Result: %v\n", result.Id)
3221+// }
3222+// if err := iter.Close(); err != nil {
3223+// return err
3224+// }
3225+//
3226+func (iter *Iter) Next(result interface{}) bool {
3227+ iter.m.Lock()
3228+ iter.timedout = false
3229+ timeout := time.Time{}
3230+ for iter.err == nil && iter.docData.Len() == 0 && (iter.docsToReceive > 0 || iter.op.cursorId != 0) {
3231+ if iter.docsToReceive == 0 {
3232+ if iter.timeout >= 0 {
3233+ if timeout.IsZero() {
3234+ timeout = time.Now().Add(iter.timeout)
3235+ }
3236+ if time.Now().After(timeout) {
3237+ iter.timedout = true
3238+ iter.m.Unlock()
3239+ return false
3240+ }
3241+ }
3242+ iter.getMore()
3243+ if iter.err != nil {
3244+ break
3245+ }
3246+ }
3247+ iter.gotReply.Wait()
3248+ }
3249+
3250+ // Exhaust available data before reporting any errors.
3251+ if docData, ok := iter.docData.Pop().([]byte); ok {
3252+ close := false
3253+ if iter.limit > 0 {
3254+ iter.limit--
3255+ if iter.limit == 0 {
3256+ if iter.docData.Len() > 0 {
3257+ iter.m.Unlock()
3258+ panic(fmt.Errorf("data remains after limit exhausted: %d", iter.docData.Len()))
3259+ }
3260+ iter.err = ErrNotFound
3261+ close = true
3262+ }
3263+ }
3264+ if iter.op.cursorId != 0 && iter.err == nil {
3265+ iter.docsBeforeMore--
3266+ if iter.docsBeforeMore == -1 {
3267+ iter.getMore()
3268+ }
3269+ }
3270+ iter.m.Unlock()
3271+
3272+ if close {
3273+ iter.Close()
3274+ }
3275+ err := bson.Unmarshal(docData, result)
3276+ if err != nil {
3277+ debugf("Iter %p document unmarshaling failed: %#v", iter, err)
3278+ iter.m.Lock()
3279+ if iter.err == nil {
3280+ iter.err = err
3281+ }
3282+ iter.m.Unlock()
3283+ return false
3284+ }
3285+ debugf("Iter %p document unmarshaled: %#v", iter, result)
3286+ // XXX Only have to check first document for a query error?
3287+ err = checkQueryError(iter.op.collection, docData)
3288+ if err != nil {
3289+ iter.m.Lock()
3290+ if iter.err == nil {
3291+ iter.err = err
3292+ }
3293+ iter.m.Unlock()
3294+ return false
3295+ }
3296+ return true
3297+ } else if iter.err != nil {
3298+ debugf("Iter %p returning false: %s", iter, iter.err)
3299+ iter.m.Unlock()
3300+ return false
3301+ } else if iter.op.cursorId == 0 {
3302+ iter.err = ErrNotFound
3303+ debugf("Iter %p exhausted with cursor=0", iter)
3304+ iter.m.Unlock()
3305+ return false
3306+ }
3307+
3308+ panic("unreachable")
3309+}
3310+
3311+// All retrieves all documents from the result set into the provided slice
3312+// and closes the iterator.
3313+//
3314+// The result argument must necessarily be the address for a slice. The slice
3315+// may be nil or previously allocated.
3316+//
3317+// WARNING: Obviously, All must not be used with result sets that may be
3318+// potentially large, since it may consume all memory until the system
3319+// crashes. Consider building the query with a Limit clause to ensure the
3320+// result size is bounded.
3321+//
3322+// For instance:
3323+//
3324+// var result []struct{ Value int }
3325+// iter := collection.Find(nil).Limit(100).Iter()
3326+// err := iter.All(&result)
3327+// if err != nil {
3328+// return err
3329+// }
3330+//
3331+func (iter *Iter) All(result interface{}) error {
3332+ resultv := reflect.ValueOf(result)
3333+ if resultv.Kind() != reflect.Ptr || resultv.Elem().Kind() != reflect.Slice {
3334+ panic("result argument must be a slice address")
3335+ }
3336+ slicev := resultv.Elem()
3337+ slicev = slicev.Slice(0, slicev.Cap())
3338+ elemt := slicev.Type().Elem()
3339+ i := 0
3340+ for {
3341+ if slicev.Len() == i {
3342+ elemp := reflect.New(elemt)
3343+ if !iter.Next(elemp.Interface()) {
3344+ break
3345+ }
3346+ slicev = reflect.Append(slicev, elemp.Elem())
3347+ slicev = slicev.Slice(0, slicev.Cap())
3348+ } else {
3349+ if !iter.Next(slicev.Index(i).Addr().Interface()) {
3350+ break
3351+ }
3352+ }
3353+ i++
3354+ }
3355+ resultv.Elem().Set(slicev.Slice(0, i))
3356+ return iter.Close()
3357+}
3358+
3359+// All works like Iter.All.
3360+func (q *Query) All(result interface{}) error {
3361+ return q.Iter().All(result)
3362+}
3363+
3364+// The For method is obsolete and will be removed in a future release.
3365+// See Iter as an elegant replacement.
3366+func (q *Query) For(result interface{}, f func() error) error {
3367+ return q.Iter().For(result, f)
3368+}
3369+
3370+// The For method is obsolete and will be removed in a future release.
3371+// See Iter as an elegant replacement.
3372+func (iter *Iter) For(result interface{}, f func() error) (err error) {
3373+ valid := false
3374+ v := reflect.ValueOf(result)
3375+ if v.Kind() == reflect.Ptr {
3376+ v = v.Elem()
3377+ switch v.Kind() {
3378+ case reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
3379+ valid = v.IsNil()
3380+ }
3381+ }
3382+ if !valid {
3383+ panic("For needs a pointer to nil reference value. See the documentation.")
3384+ }
3385+ zero := reflect.Zero(v.Type())
3386+ for {
3387+ v.Set(zero)
3388+ if !iter.Next(result) {
3389+ break
3390+ }
3391+ err = f()
3392+ if err != nil {
3393+ return err
3394+ }
3395+ }
3396+ return iter.Err()
3397+}
3398+
3399+// acquireSocket acquires a socket from the same server that the iterator
3400+// cursor was obtained from.
3401+//
3402+// WARNING: This method must not be called with iter.m locked. Acquiring the
3403+// socket depends on the cluster sync loop, and the cluster sync loop might
3404+// attempt actions which cause replyFunc to be called, inducing a deadlock.
3405+func (iter *Iter) acquireSocket() (*mongoSocket, error) {
3406+ socket, err := iter.session.acquireSocket(true)
3407+ if err != nil {
3408+ return nil, err
3409+ }
3410+ if socket.Server() != iter.server {
3411+ // Socket server changed during iteration. This may happen
3412+ // with Eventual sessions, if a Refresh is done, or if a
3413+ // monotonic session gets a write and shifts from secondary
3414+ // to primary. Our cursor is in a specific server, though.
3415+ iter.session.m.Lock()
3416+ sockTimeout := iter.session.sockTimeout
3417+ iter.session.m.Unlock()
3418+ socket.Release()
3419+ socket, _, err = iter.server.AcquireSocket(0, sockTimeout)
3420+ if err != nil {
3421+ return nil, err
3422+ }
3423+ err := iter.session.socketLogin(socket)
3424+ if err != nil {
3425+ socket.Release()
3426+ return nil, err
3427+ }
3428+ }
3429+ return socket, nil
3430+}
3431+
3432+func (iter *Iter) getMore() {
3433+ // Increment now so that unlocking the iterator won't cause a
3434+ // different goroutine to get here as well.
3435+ iter.docsToReceive++
3436+ iter.m.Unlock()
3437+ socket, err := iter.acquireSocket()
3438+ iter.m.Lock()
3439+ if err != nil {
3440+ iter.err = err
3441+ return
3442+ }
3443+ defer socket.Release()
3444+
3445+ debugf("Iter %p requesting more documents", iter)
3446+ if iter.limit > 0 {
3447+ // The -1 below accounts for the fact docsToReceive was incremented above.
3448+ limit := iter.limit - int32(iter.docsToReceive-1) - int32(iter.docData.Len())
3449+ if limit < iter.op.limit {
3450+ iter.op.limit = limit
3451+ }
3452+ }
3453+ if err := socket.Query(&iter.op); err != nil {
3454+ iter.docsToReceive--
3455+ iter.err = err
3456+ }
3457+}
3458+
3459+type countCmd struct {
3460+ Count string
3461+ Query interface{}
3462+ Limit int32 ",omitempty"
3463+ Skip int32 ",omitempty"
3464+}
3465+
3466+// Count returns the total number of documents in the result set.
3467+func (q *Query) Count() (n int, err error) {
3468+ q.m.Lock()
3469+ session := q.session
3470+ op := q.op
3471+ limit := q.limit
3472+ q.m.Unlock()
3473+
3474+ c := strings.Index(op.collection, ".")
3475+ if c < 0 {
3476+ return 0, errors.New("Bad collection name: " + op.collection)
3477+ }
3478+
3479+ dbname := op.collection[:c]
3480+ cname := op.collection[c+1:]
3481+ query := op.query
3482+ if query == nil {
3483+ query = bson.D{}
3484+ }
3485+ result := struct{ N int }{}
3486+ err = session.DB(dbname).Run(countCmd{cname, query, limit, op.skip}, &result)
3487+ return result.N, err
3488+}
3489+
3490+// Count returns the total number of documents in the collection.
3491+func (c *Collection) Count() (n int, err error) {
3492+ return c.Find(nil).Count()
3493+}
3494+
3495+type distinctCmd struct {
3496+ Collection string "distinct"
3497+ Key string
3498+ Query interface{} ",omitempty"
3499+}
3500+
3501+// Distinct unmarshals into result the list of distinct values for the given key.
3502+//
3503+// For example:
3504+//
3505+// var result []int
3506+// err := collection.Find(bson.M{"gender": "F"}).Distinct("age", &result)
3507+//
3508+// Relevant documentation:
3509+//
3510+// http://www.mongodb.org/display/DOCS/Aggregation
3511+//
3512+func (q *Query) Distinct(key string, result interface{}) error {
3513+ q.m.Lock()
3514+ session := q.session
3515+ op := q.op // Copy.
3516+ q.m.Unlock()
3517+
3518+ c := strings.Index(op.collection, ".")
3519+ if c < 0 {
3520+ return errors.New("Bad collection name: " + op.collection)
3521+ }
3522+
3523+ dbname := op.collection[:c]
3524+ cname := op.collection[c+1:]
3525+
3526+ var doc struct{ Values bson.Raw }
3527+ err := session.DB(dbname).Run(distinctCmd{cname, key, op.query}, &doc)
3528+ if err != nil {
3529+ return err
3530+ }
3531+ return doc.Values.Unmarshal(result)
3532+}
3533+
3534+type mapReduceCmd struct {
3535+ Collection string "mapreduce"
3536+ Map string ",omitempty"
3537+ Reduce string ",omitempty"
3538+ Finalize string ",omitempty"
3539+ Limit int32 ",omitempty"
3540+ Out interface{}
3541+ Query interface{} ",omitempty"
3542+ Sort interface{} ",omitempty"
3543+ Scope interface{} ",omitempty"
3544+ Verbose bool ",omitempty"
3545+}
3546+
3547+type mapReduceResult struct {
3548+ Results bson.Raw
3549+ Result bson.Raw
3550+ TimeMillis int64 "timeMillis"
3551+ Counts struct{ Input, Emit, Output int }
3552+ Ok bool
3553+ Err string
3554+ Timing *MapReduceTime
3555+}
3556+
3557+type MapReduce struct {
3558+ Map string // Map Javascript function code (required)
3559+ Reduce string // Reduce Javascript function code (required)
3560+ Finalize string // Finalize Javascript function code (optional)
3561+ Out interface{} // Output collection name or document. If nil, results are inlined into the result parameter.
3562+ Scope interface{} // Optional global scope for Javascript functions
3563+ Verbose bool
3564+}
3565+
3566+type MapReduceInfo struct {
3567+ InputCount int // Number of documents mapped
3568+ EmitCount int // Number of times reduce called emit
3569+ OutputCount int // Number of documents in resulting collection
3570+ Database string // Output database, if results are not inlined
3571+ Collection string // Output collection, if results are not inlined
3572+ Time int64 // Time to run the job, in nanoseconds
3573+ VerboseTime *MapReduceTime // Only defined if Verbose was true
3574+}
3575+
3576+type MapReduceTime struct {
3577+ Total int64 // Total time, in nanoseconds
3578+ Map int64 "mapTime" // Time within map function, in nanoseconds
3579+ EmitLoop int64 "emitLoop" // Time within the emit/map loop, in nanoseconds
3580+}
3581+
3582+// MapReduce executes a map/reduce job for documents covered by the query.
3583+// That kind of job is suitable for very flexible bulk aggregation of data
3584+// performed at the server side via Javascript functions.
3585+//
3586+// Results from the job may be returned as a result of the query itself
3587+// through the result parameter in case they'll certainly fit in memory
3588+// and in a single document. If there's the possibility that the amount
3589+// of data might be too large, results must be stored back in an alternative
3590+// collection or even a separate database, by setting the Out field of the
3591+// provided MapReduce job. In that case, provide nil as the result parameter.
3592+//
3593+// These are some of the ways to set Out:
3594+//
3595+// nil
3596+// Inline results into the result parameter.
3597+//
3598+// bson.M{"replace": "mycollection"}
3599+// The output will be inserted into a collection which replaces any
3600+// existing collection with the same name.
3601+//
3602+// bson.M{"merge": "mycollection"}
3603+// This option will merge new data into the old output collection. In
3604+// other words, if the same key exists in both the result set and the
3605+// old collection, the new key will overwrite the old one.
3606+//
3607+// bson.M{"reduce": "mycollection"}
3608+// If documents exist for a given key in the result set and in the old
3609+// collection, then a reduce operation (using the specified reduce
3610+// function) will be performed on the two values and the result will be
3611+// written to the output collection. If a finalize function was
3612+// provided, this will be run after the reduce as well.
3613+//
3614+// bson.M{...., "db": "mydb"}
3615+// Any of the above options can have the "db" key included for doing
3616+// the respective action in a separate database.
3617+//
3618+// The following is a trivial example which will count the number of
3619+// occurrences of a field named n on each document in a collection, and
3620+// will return results inline:
3621+//
3622+// job := &mgo.MapReduce{
3623+// Map: "function() { emit(this.n, 1) }",
3624+// Reduce: "function(key, values) { return Array.sum(values) }",
3625+// }
3626+// var result []struct { Id int "_id"; Value int }
3627+// _, err := collection.Find(nil).MapReduce(job, &result)
3628+// if err != nil {
3629+// return err
3630+// }
3631+// for _, item := range result {
3632+// fmt.Println(item.Value)
3633+// }
3634+//
3635+// This function is compatible with MongoDB 1.7.4+.
3636+//
3637+// Relevant documentation:
3638+//
3639+// http://www.mongodb.org/display/DOCS/MapReduce
3640+//
3641+func (q *Query) MapReduce(job *MapReduce, result interface{}) (info *MapReduceInfo, err error) {
3642+ q.m.Lock()
3643+ session := q.session
3644+ op := q.op // Copy.
3645+ limit := q.limit
3646+ q.m.Unlock()
3647+
3648+ c := strings.Index(op.collection, ".")
3649+ if c < 0 {
3650+ return nil, errors.New("Bad collection name: " + op.collection)
3651+ }
3652+
3653+ dbname := op.collection[:c]
3654+ cname := op.collection[c+1:]
3655+
3656+ cmd := mapReduceCmd{
3657+ Collection: cname,
3658+ Map: job.Map,
3659+ Reduce: job.Reduce,
3660+ Finalize: job.Finalize,
3661+ Out: fixMROut(job.Out),
3662+ Scope: job.Scope,
3663+ Verbose: job.Verbose,
3664+ Query: op.query,
3665+ Sort: op.options.OrderBy,
3666+ Limit: limit,
3667+ }
3668+
3669+ if cmd.Out == nil {
3670+ cmd.Out = bson.D{{"inline", 1}}
3671+ }
3672+
3673+ var doc mapReduceResult
3674+ err = session.DB(dbname).Run(&cmd, &doc)
3675+ if err != nil {
3676+ return nil, err
3677+ }
3678+ if doc.Err != "" {
3679+ return nil, errors.New(doc.Err)
3680+ }
3681+
3682+ info = &MapReduceInfo{
3683+ InputCount: doc.Counts.Input,
3684+ EmitCount: doc.Counts.Emit,
3685+ OutputCount: doc.Counts.Output,
3686+ Time: doc.TimeMillis * 1e6,
3687+ }
3688+
3689+ if doc.Result.Kind == 0x02 {
3690+ err = doc.Result.Unmarshal(&info.Collection)
3691+ info.Database = dbname
3692+ } else if doc.Result.Kind == 0x03 {
3693+ var v struct{ Collection, Db string }
3694+ err = doc.Result.Unmarshal(&v)
3695+ info.Collection = v.Collection
3696+ info.Database = v.Db
3697+ }
3698+
3699+ if doc.Timing != nil {
3700+ info.VerboseTime = doc.Timing
3701+ info.VerboseTime.Total *= 1e6
3702+ info.VerboseTime.Map *= 1e6
3703+ info.VerboseTime.EmitLoop *= 1e6
3704+ }
3705+
3706+ if err != nil {
3707+ return nil, err
3708+ }
3709+ if result != nil {
3710+ return info, doc.Results.Unmarshal(result)
3711+ }
3712+ return info, nil
3713+}
3714+
3715+// The "out" option in the MapReduce command must be ordered. This was
3716+// found after the implementation was accepting maps for a long time,
3717+// so rather than breaking the API, we'll fix the order if necessary.
3718+// Details about the order requirement may be seen in MongoDB's code:
3719+//
3720+// http://goo.gl/L8jwJX
3721+//
3722+func fixMROut(out interface{}) interface{} {
3723+ outv := reflect.ValueOf(out)
3724+ if outv.Kind() != reflect.Map || outv.Type().Key() != reflect.TypeOf("") {
3725+ return out
3726+ }
3727+ outs := make(bson.D, outv.Len())
3728+
3729+ outTypeIndex := -1
3730+ for i, k := range outv.MapKeys() {
3731+ ks := k.String()
3732+ outs[i].Name = ks
3733+ outs[i].Value = outv.MapIndex(k).Interface()
3734+ switch ks {
3735+ case "normal", "replace", "merge", "reduce", "inline":
3736+ outTypeIndex = i
3737+ }
3738+ }
3739+ if outTypeIndex > 0 {
3740+ outs[0], outs[outTypeIndex] = outs[outTypeIndex], outs[0]
3741+ }
3742+ return outs
3743+}
3744+
3745+// Change holds fields for running a findAndModify MongoDB command via
3746+// the Query.Apply method.
3747+type Change struct {
3748+ Update interface{} // The update document
3749+ Upsert bool // Whether to insert in case the document isn't found
3750+ Remove bool // Whether to remove the document found rather than updating
3751+ ReturnNew bool // Should the modified document be returned rather than the old one
3752+}
3753+
3754+type findModifyCmd struct {
3755+ Collection string "findAndModify"
3756+ Query, Update, Sort, Fields interface{} ",omitempty"
3757+ Upsert, Remove, New bool ",omitempty"
3758+}
3759+
3760+type valueResult struct {
3761+ Value bson.Raw
3762+ LastError LastError "lastErrorObject"
3763+}
3764+
3765+// Apply runs the findAndModify MongoDB command, which allows updating, upserting
3766+// or removing a document matching a query and atomically returning either the old
3767+// version (the default) or the new version of the document (when ReturnNew is true).
3768+// If no objects are found Apply returns ErrNotFound.
3769+//
3770+// The Sort and Select query methods affect the result of Apply. In case
3771+// multiple documents match the query, Sort enables selecting which document to
3772+// act upon by ordering it first. Select enables retrieving only a selection
3773+// of fields of the new or old document.
3774+//
3775+// This simple example increments a counter and prints its new value:
3776+//
3777+// change := mgo.Change{
3778+// Update: bson.M{"$inc": bson.M{"n": 1}},
3779+// ReturnNew: true,
3780+// }
3781+// info, err = col.Find(M{"_id": id}).Apply(change, &doc)
3782+// fmt.Println(doc.N)
3783+//
3784+// This method depends on MongoDB >= 2.0 to work properly.
3785+//
3786+// Relevant documentation:
3787+//
3788+// http://www.mongodb.org/display/DOCS/findAndModify+Command
3789+// http://www.mongodb.org/display/DOCS/Updating
3790+// http://www.mongodb.org/display/DOCS/Atomic+Operations
3791+//
3792+func (q *Query) Apply(change Change, result interface{}) (info *ChangeInfo, err error) {
3793+ q.m.Lock()
3794+ session := q.session
3795+ op := q.op // Copy.
3796+ q.m.Unlock()
3797+
3798+ c := strings.Index(op.collection, ".")
3799+ if c < 0 {
3800+ return nil, errors.New("bad collection name: " + op.collection)
3801+ }
3802+
3803+ dbname := op.collection[:c]
3804+ cname := op.collection[c+1:]
3805+
3806+ cmd := findModifyCmd{
3807+ Collection: cname,
3808+ Update: change.Update,
3809+ Upsert: change.Upsert,
3810+ Remove: change.Remove,
3811+ New: change.ReturnNew,
3812+ Query: op.query,
3813+ Sort: op.options.OrderBy,
3814+ Fields: op.selector,
3815+ }
3816+
3817+ session = session.Clone()
3818+ defer session.Close()
3819+ session.SetMode(Strong, false)
3820+
3821+ var doc valueResult
3822+ err = session.DB(dbname).Run(&cmd, &doc)
3823+ if err != nil {
3824+ if qerr, ok := err.(*QueryError); ok && qerr.Message == "No matching object found" {
3825+ return nil, ErrNotFound
3826+ }
3827+ return nil, err
3828+ }
3829+ if doc.LastError.N == 0 {
3830+ return nil, ErrNotFound
3831+ }
3832+ if doc.Value.Kind != 0x0A && result != nil {
3833+ err = doc.Value.Unmarshal(result)
3834+ if err != nil {
3835+ return nil, err
3836+ }
3837+ }
3838+ info = &ChangeInfo{}
3839+ lerr := &doc.LastError
3840+ if lerr.UpdatedExisting {
3841+ info.Updated = lerr.N
3842+ } else if change.Remove {
3843+ info.Removed = lerr.N
3844+ } else if change.Upsert {
3845+ info.UpsertedId = lerr.UpsertedId
3846+ }
3847+ return info, nil
3848+}
3849+
3850+// The BuildInfo type encapsulates details about the running MongoDB server.
3851+//
3852+// Note that the VersionArray field was introduced in MongoDB 2.0+, but it is
3853+// internally assembled from the Version information for previous versions.
3854+// In both cases, VersionArray is guaranteed to have at least 4 entries.
3855+type BuildInfo struct {
3856+ Version string
3857+ VersionArray []int `bson:"versionArray"` // On MongoDB 2.0+; assembled from Version otherwise
3858+ GitVersion string `bson:"gitVersion"`
3859+ OpenSSLVersion string `bson:"OpenSSLVersion"`
3860+ SysInfo string `bson:"sysInfo"`
3861+ Bits int
3862+ Debug bool
3863+ MaxObjectSize int `bson:"maxBsonObjectSize"`
3864+}
3865+
3866+// VersionAtLeast returns whether the BuildInfo version is greater than or
3867+// equal to the provided version number. If more than one number is
3868+// provided, numbers will be considered as major, minor, and so on.
3869+func (bi *BuildInfo) VersionAtLeast(version ...int) bool {
3870+ for i := range version {
3871+ if i == len(bi.VersionArray) {
3872+ return false
3873+ }
3874+ if bi.VersionArray[i] < version[i] {
3875+ return false
3876+ }
3877+ }
3878+ return true
3879+}
3880+
3881+// BuildInfo retrieves the version and other details about the
3882+// running MongoDB server.
3883+func (s *Session) BuildInfo() (info BuildInfo, err error) {
3884+ err = s.Run(bson.D{{"buildInfo", "1"}}, &info)
3885+ if len(info.VersionArray) == 0 {
3886+ for _, a := range strings.Split(info.Version, ".") {
3887+ i, err := strconv.Atoi(a)
3888+ if err != nil {
3889+ break
3890+ }
3891+ info.VersionArray = append(info.VersionArray, i)
3892+ }
3893+ }
3894+ for len(info.VersionArray) < 4 {
3895+ info.VersionArray = append(info.VersionArray, 0)
3896+ }
3897+ if i := strings.IndexByte(info.GitVersion, ' '); i >= 0 {
3898+ // Strip off the " modules: enterprise" suffix. This is a _git version_.
3899+ // That information may be moved to another field if people need it.
3900+ info.GitVersion = info.GitVersion[:i]
3901+ }
3902+ return
3903+}
3904+
3905+// ---------------------------------------------------------------------------
3906+// Internal session handling helpers.
3907+
3908+func (s *Session) acquireSocket(slaveOk bool) (*mongoSocket, error) {
3909+
3910+ // Read-only lock to check for previously reserved socket.
3911+ s.m.RLock()
3912+ if s.masterSocket != nil {
3913+ socket := s.masterSocket
3914+ socket.Acquire()
3915+ s.m.RUnlock()
3916+ return socket, nil
3917+ }
3918+ if s.slaveSocket != nil && s.slaveOk && slaveOk {
3919+ socket := s.slaveSocket
3920+ socket.Acquire()
3921+ s.m.RUnlock()
3922+ return socket, nil
3923+ }
3924+ s.m.RUnlock()
3925+
3926+ // No go. We may have to request a new socket and change the session,
3927+ // so try again but with an exclusive lock now.
3928+ s.m.Lock()
3929+ defer s.m.Unlock()
3930+
3931+ if s.masterSocket != nil {
3932+ s.masterSocket.Acquire()
3933+ return s.masterSocket, nil
3934+ }
3935+ if s.slaveSocket != nil && s.slaveOk && slaveOk {
3936+ s.slaveSocket.Acquire()
3937+ return s.slaveSocket, nil
3938+ }
3939+
3940+ // Still not good. We need a new socket.
3941+ sock, err := s.cluster().AcquireSocket(slaveOk && s.slaveOk, s.syncTimeout, s.sockTimeout, s.queryConfig.op.serverTags, s.poolLimit)
3942+ if err != nil {
3943+ return nil, err
3944+ }
3945+
3946+ // Authenticate the new socket.
3947+ if err = s.socketLogin(sock); err != nil {
3948+ sock.Release()
3949+ return nil, err
3950+ }
3951+
3952+ // Keep track of the new socket, if necessary.
3953+ // Note that, as a special case, if the Eventual session was
3954+ // not refreshed (s.slaveSocket != nil), it means the developer
3955+ // asked to preserve an existing reserved socket, so we'll
3956+ // keep a master one around too before a Refresh happens.
3957+ if s.consistency != Eventual || s.slaveSocket != nil {
3958+ s.setSocket(sock)
3959+ }
3960+
3961+ // Switch over a Monotonic session to the master.
3962+ if !slaveOk && s.consistency == Monotonic {
3963+ s.slaveOk = false
3964+ }
3965+
3966+ return sock, nil
3967+}
3968+
3969+// setSocket binds socket to this section.
3970+func (s *Session) setSocket(socket *mongoSocket) {
3971+ info := socket.Acquire()
3972+ if info.Master {
3973+ if s.masterSocket != nil {
3974+ panic("setSocket(master) with existing master socket reserved")
3975+ }
3976+ s.masterSocket = socket
3977+ } else {
3978+ if s.slaveSocket != nil {
3979+ panic("setSocket(slave) with existing slave socket reserved")
3980+ }
3981+ s.slaveSocket = socket
3982+ }
3983+}
3984+
3985+// unsetSocket releases any slave and/or master sockets reserved.
3986+func (s *Session) unsetSocket() {
3987+ if s.masterSocket != nil {
3988+ s.masterSocket.Release()
3989+ }
3990+ if s.slaveSocket != nil {
3991+ s.slaveSocket.Release()
3992+ }
3993+ s.masterSocket = nil
3994+ s.slaveSocket = nil
3995+}
3996+
3997+func (iter *Iter) replyFunc() replyFunc {
3998+ return func(err error, op *replyOp, docNum int, docData []byte) {
3999+ iter.m.Lock()
4000+ iter.docsToReceive--
4001+ if err != nil {
4002+ iter.err = err
4003+ debugf("Iter %p received an error: %s", iter, err.Error())
4004+ } else if docNum == -1 {
4005+ debugf("Iter %p received no documents (cursor=%d).", iter, op.cursorId)
4006+ if op != nil && op.cursorId != 0 {
4007+ // It's a tailable cursor.
4008+ iter.op.cursorId = op.cursorId
4009+ } else if op != nil && op.cursorId == 0 && op.flags&1 == 1 {
4010+ // Cursor likely timed out.
4011+ iter.err = ErrCursor
4012+ } else {
4013+ iter.err = ErrNotFound
4014+ }
4015+ } else {
4016+ rdocs := int(op.replyDocs)
4017+ if docNum == 0 {
4018+ iter.docsToReceive += rdocs - 1
4019+ docsToProcess := iter.docData.Len() + rdocs
4020+ if iter.limit == 0 || int32(docsToProcess) < iter.limit {
4021+ iter.docsBeforeMore = docsToProcess - int(iter.prefetch*float64(rdocs))
4022+ } else {
4023+ iter.docsBeforeMore = -1
4024+ }
4025+ iter.op.cursorId = op.cursorId
4026+ }
4027+ // XXX Handle errors and flags.
4028+ debugf("Iter %p received reply document %d/%d (cursor=%d)", iter, docNum+1, rdocs, op.cursorId)
4029+ iter.docData.Push(docData)
4030+ }
4031+ iter.gotReply.Broadcast()
4032+ iter.m.Unlock()
4033+ }
4034+}
4035+
4036+type writeCmdResult struct {
4037+ Ok bool
4038+ N int
4039+ NModified int `bson:"nModified"`
4040+ Upserted []struct {
4041+ Index int
4042+ Id interface{} `_id`
4043+ }
4044+ Errors []struct {
4045+ Ok bool
4046+ Index int
4047+ Code int
4048+ N int
4049+ ErrMsg string
4050+ } `bson:"writeErrors"`
4051+ ConcernError struct {
4052+ Code int
4053+ ErrMsg string
4054+ } `bson:"writeConcernError"`
4055+}
4056+
4057+// writeQuery runs the given modifying operation, potentially followed up
4058+// by a getLastError command in case the session is in safe mode. The
4059+// LastError result is made available in lerr, and if lerr.Err is set it
4060+// will also be returned as err.
4061+func (c *Collection) writeQuery(op interface{}) (lerr *LastError, err error) {
4062+ s := c.Database.Session
4063+ dbname := c.Database.Name
4064+ socket, err := s.acquireSocket(dbname == "local")
4065+ if err != nil {
4066+ return nil, err
4067+ }
4068+ defer socket.Release()
4069+
4070+ s.m.RLock()
4071+ safeOp := s.safeOp
4072+ s.m.RUnlock()
4073+
4074+ // TODO Enable this path for wire version 2 as well.
4075+ if socket.ServerInfo().MaxWireVersion >= 3 {
4076+ // Servers with a more recent write protocol benefit from write commands.
4077+ if op, ok := op.(*insertOp); ok && len(op.documents) > 1000 {
4078+ var firstErr error
4079+ // Maximum batch size is 1000. Must split out in separate operations for compatibility.
4080+ all := op.documents
4081+ for i := 0; i < len(all); i += 1000 {
4082+ l := i + 1000
4083+ if l > len(all) {
4084+ l = len(all)
4085+ }
4086+ op.documents = all[i:l]
4087+ _, err := c.writeCommand(socket, safeOp, op)
4088+ if err != nil {
4089+ if op.flags&1 != 0 {
4090+ if firstErr == nil {
4091+ firstErr = err
4092+ }
4093+ } else {
4094+ return nil, err
4095+ }
4096+ }
4097+ }
4098+ return nil, firstErr
4099+ }
4100+ return c.writeCommand(socket, safeOp, op)
4101+ }
4102+
4103+ if safeOp == nil {
4104+ return nil, socket.Query(op)
4105+ }
4106+
4107+ var mutex sync.Mutex
4108+ var replyData []byte
4109+ var replyErr error
4110+ mutex.Lock()
4111+ query := *safeOp // Copy the data.
4112+ query.collection = dbname + ".$cmd"
4113+ query.replyFunc = func(err error, reply *replyOp, docNum int, docData []byte) {
4114+ replyData = docData
4115+ replyErr = err
4116+ mutex.Unlock()
4117+ }
4118+ err = socket.Query(op, &query)
4119+ if err != nil {
4120+ return nil, err
4121+ }
4122+ mutex.Lock() // Wait.
4123+ if replyErr != nil {
4124+ return nil, replyErr // XXX TESTME
4125+ }
4126+ if hasErrMsg(replyData) {
4127+ // Looks like getLastError itself failed.
4128+ err = checkQueryError(query.collection, replyData)
4129+ if err != nil {
4130+ return nil, err
4131+ }
4132+ }
4133+ result := &LastError{}
4134+ bson.Unmarshal(replyData, &result)
4135+ debugf("Result from writing query: %#v", result)
4136+ if result.Err != "" {
4137+ return result, result
4138+ }
4139+ return result, nil
4140+}
4141+
4142+func (c *Collection) writeCommand(socket *mongoSocket, safeOp *queryOp, op interface{}) (lerr *LastError, err error) {
4143+ var writeConcern interface{}
4144+ if safeOp == nil {
4145+ writeConcern = bson.D{{"w", 0}}
4146+ } else {
4147+ writeConcern = safeOp.query.(*getLastError)
4148+ }
4149+
4150+ var cmd bson.D
4151+ switch op := op.(type) {
4152+ case *insertOp:
4153+ // http://docs.mongodb.org/manual/reference/command/insert
4154+ cmd = bson.D{
4155+ {"insert", c.Name},
4156+ {"documents", op.documents},
4157+ {"writeConcern", writeConcern},
4158+ {"ordered", op.flags&1 == 0},
4159+ }
4160+ case *updateOp:
4161+ // http://docs.mongodb.org/manual/reference/command/update
4162+ selector := op.selector
4163+ if selector == nil {
4164+ selector = bson.D{}
4165+ }
4166+ cmd = bson.D{
4167+ {"update", c.Name},
4168+ {"updates", []bson.D{{{"q", selector}, {"u", op.update}, {"upsert", op.flags&1 != 0}, {"multi", op.flags&2 != 0}}}},
4169+ {"writeConcern", writeConcern},
4170+ //{"ordered", <bool>},
4171+ }
4172+ case *deleteOp:
4173+ // http://docs.mongodb.org/manual/reference/command/delete
4174+ selector := op.selector
4175+ if selector == nil {
4176+ selector = bson.D{}
4177+ }
4178+ cmd = bson.D{
4179+ {"delete", c.Name},
4180+ {"deletes", []bson.D{{{"q", selector}, {"limit", op.flags & 1}}}},
4181+ {"writeConcern", writeConcern},
4182+ //{"ordered", <bool>},
4183+ }
4184+ }
4185+
4186+ var result writeCmdResult
4187+ err = c.Database.run(socket, cmd, &result)
4188+ debugf("Write command result: %#v (err=%v)", result, err)
4189+ lerr = &LastError{
4190+ UpdatedExisting: result.N > 0 && len(result.Upserted) == 0,
4191+ N: result.N,
4192+ }
4193+ if len(result.Upserted) > 0 {
4194+ lerr.UpsertedId = result.Upserted[0].Id
4195+ }
4196+ if len(result.Errors) > 0 {
4197+ e := result.Errors[0]
4198+ if !e.Ok {
4199+ lerr.Code = e.Code
4200+ lerr.Err = e.ErrMsg
4201+ err = lerr
4202+ }
4203+ } else if result.ConcernError.Code != 0 {
4204+ e := result.ConcernError
4205+ lerr.Code = e.Code
4206+ lerr.Err = e.ErrMsg
4207+ err = lerr
4208+ }
4209+
4210+ if err == nil && safeOp == nil {
4211+ return nil, nil
4212+ }
4213+ return lerr, err
4214+}
4215+
4216+func hasErrMsg(d []byte) bool {
4217+ l := len(d)
4218+ for i := 0; i+8 < l; i++ {
4219+ if d[i] == '\x02' && d[i+1] == 'e' && d[i+2] == 'r' && d[i+3] == 'r' && d[i+4] == 'm' && d[i+5] == 's' && d[i+6] == 'g' && d[i+7] == '\x00' {
4220+ return true
4221+ }
4222+ }
4223+ return false
4224+}
vendor/gopkg.in/mgo.v2/session_test.go+3704−0
@@ -0,0 +1,3704 @@
1+// mgo - MongoDB driver for Go
2+//
3+// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
4+//
5+// All rights reserved.
6+//
7+// Redistribution and use in source and binary forms, with or without
8+// modification, are permitted provided that the following conditions are met:
9+//
10+// 1. Redistributions of source code must retain the above copyright notice, this
11+// list of conditions and the following disclaimer.
12+// 2. Redistributions in binary form must reproduce the above copyright notice,
13+// this list of conditions and the following disclaimer in the documentation
14+// and/or other materials provided with the distribution.
15+//
16+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+package mgo_test
28+
29+import (
30+ "flag"
31+ "fmt"
32+ "math"
33+ "reflect"
34+ "runtime"
35+ "sort"
36+ "strconv"
37+ "strings"
38+ "time"
39+
40+ . "gopkg.in/check.v1"
41+ "gopkg.in/mgo.v2"
42+ "gopkg.in/mgo.v2/bson"
43+)
44+
45+func (s *S) TestRunString(c *C) {
46+ session, err := mgo.Dial("localhost:40001")
47+ c.Assert(err, IsNil)
48+ defer session.Close()
49+
50+ result := struct{ Ok int }{}
51+ err = session.Run("ping", &result)
52+ c.Assert(err, IsNil)
53+ c.Assert(result.Ok, Equals, 1)
54+}
55+
56+func (s *S) TestRunValue(c *C) {
57+ session, err := mgo.Dial("localhost:40001")
58+ c.Assert(err, IsNil)
59+ defer session.Close()
60+
61+ result := struct{ Ok int }{}
62+ err = session.Run(M{"ping": 1}, &result)
63+ c.Assert(err, IsNil)
64+ c.Assert(result.Ok, Equals, 1)
65+}
66+
67+func (s *S) TestPing(c *C) {
68+ session, err := mgo.Dial("localhost:40001")
69+ c.Assert(err, IsNil)
70+ defer session.Close()
71+
72+ // Just ensure the nonce has been received.
73+ result := struct{}{}
74+ err = session.Run("ping", &result)
75+
76+ mgo.ResetStats()
77+
78+ err = session.Ping()
79+ c.Assert(err, IsNil)
80+
81+ // Pretty boring.
82+ stats := mgo.GetStats()
83+ c.Assert(stats.SentOps, Equals, 1)
84+ c.Assert(stats.ReceivedOps, Equals, 1)
85+}
86+
87+func (s *S) TestURLSingle(c *C) {
88+ session, err := mgo.Dial("mongodb://localhost:40001/")
89+ c.Assert(err, IsNil)
90+ defer session.Close()
91+
92+ result := struct{ Ok int }{}
93+ err = session.Run("ping", &result)
94+ c.Assert(err, IsNil)
95+ c.Assert(result.Ok, Equals, 1)
96+}
97+
98+func (s *S) TestURLMany(c *C) {
99+ session, err := mgo.Dial("mongodb://localhost:40011,localhost:40012/")
100+ c.Assert(err, IsNil)
101+ defer session.Close()
102+
103+ result := struct{ Ok int }{}
104+ err = session.Run("ping", &result)
105+ c.Assert(err, IsNil)
106+ c.Assert(result.Ok, Equals, 1)
107+}
108+
109+func (s *S) TestURLParsing(c *C) {
110+ urls := []string{
111+ "localhost:40001?foo=1&bar=2",
112+ "localhost:40001?foo=1;bar=2",
113+ }
114+ for _, url := range urls {
115+ session, err := mgo.Dial(url)
116+ if session != nil {
117+ session.Close()
118+ }
119+ c.Assert(err, ErrorMatches, "unsupported connection URL option: (foo=1|bar=2)")
120+ }
121+}
122+
123+func (s *S) TestInsertFindOne(c *C) {
124+ session, err := mgo.Dial("localhost:40001")
125+ c.Assert(err, IsNil)
126+ defer session.Close()
127+
128+ coll := session.DB("mydb").C("mycoll")
129+ err = coll.Insert(M{"a": 1, "b": 2})
130+ c.Assert(err, IsNil)
131+ err = coll.Insert(M{"a": 1, "b": 3})
132+ c.Assert(err, IsNil)
133+
134+ result := struct{ A, B int }{}
135+
136+ err = coll.Find(M{"a": 1}).Sort("b").One(&result)
137+ c.Assert(err, IsNil)
138+ c.Assert(result.A, Equals, 1)
139+ c.Assert(result.B, Equals, 2)
140+}
141+
142+func (s *S) TestInsertFindOneNil(c *C) {
143+ session, err := mgo.Dial("localhost:40002")
144+ c.Assert(err, IsNil)
145+ defer session.Close()
146+
147+ coll := session.DB("mydb").C("mycoll")
148+ err = coll.Find(nil).One(nil)
149+ c.Assert(err, ErrorMatches, "unauthorized.*|not authorized.*")
150+}
151+
152+func (s *S) TestInsertFindOneMap(c *C) {
153+ session, err := mgo.Dial("localhost:40001")
154+ c.Assert(err, IsNil)
155+ defer session.Close()
156+
157+ coll := session.DB("mydb").C("mycoll")
158+ err = coll.Insert(M{"a": 1, "b": 2})
159+ c.Assert(err, IsNil)
160+ result := make(M)
161+ err = coll.Find(M{"a": 1}).One(result)
162+ c.Assert(err, IsNil)
163+ c.Assert(result["a"], Equals, 1)
164+ c.Assert(result["b"], Equals, 2)
165+}
166+
167+func (s *S) TestInsertFindAll(c *C) {
168+ session, err := mgo.Dial("localhost:40001")
169+ c.Assert(err, IsNil)
170+ defer session.Close()
171+
172+ coll := session.DB("mydb").C("mycoll")
173+ err = coll.Insert(M{"a": 1, "b": 2})
174+ c.Assert(err, IsNil)
175+ err = coll.Insert(M{"a": 3, "b": 4})
176+ c.Assert(err, IsNil)
177+
178+ type R struct{ A, B int }
179+ var result []R
180+
181+ assertResult := func() {
182+ c.Assert(len(result), Equals, 2)
183+ c.Assert(result[0].A, Equals, 1)
184+ c.Assert(result[0].B, Equals, 2)
185+ c.Assert(result[1].A, Equals, 3)
186+ c.Assert(result[1].B, Equals, 4)
187+ }
188+
189+ // nil slice
190+ err = coll.Find(nil).Sort("a").All(&result)
191+ c.Assert(err, IsNil)
192+ assertResult()
193+
194+ // Previously allocated slice
195+ allocd := make([]R, 5)
196+ result = allocd
197+ err = coll.Find(nil).Sort("a").All(&result)
198+ c.Assert(err, IsNil)
199+ assertResult()
200+
201+ // Ensure result is backed by the originally allocated array
202+ c.Assert(&result[0], Equals, &allocd[0])
203+
204+ // Non-pointer slice error
205+ f := func() { coll.Find(nil).All(result) }
206+ c.Assert(f, Panics, "result argument must be a slice address")
207+
208+ // Non-slice error
209+ f = func() { coll.Find(nil).All(new(int)) }
210+ c.Assert(f, Panics, "result argument must be a slice address")
211+}
212+
213+func (s *S) TestFindRef(c *C) {
214+ session, err := mgo.Dial("localhost:40001")
215+ c.Assert(err, IsNil)
216+ defer session.Close()
217+
218+ db1 := session.DB("db1")
219+ db1col1 := db1.C("col1")
220+
221+ db2 := session.DB("db2")
222+ db2col1 := db2.C("col1")
223+
224+ err = db1col1.Insert(M{"_id": 1, "n": 1})
225+ c.Assert(err, IsNil)
226+ err = db1col1.Insert(M{"_id": 2, "n": 2})
227+ c.Assert(err, IsNil)
228+ err = db2col1.Insert(M{"_id": 2, "n": 3})
229+ c.Assert(err, IsNil)
230+
231+ result := struct{ N int }{}
232+
233+ ref1 := &mgo.DBRef{Collection: "col1", Id: 1}
234+ ref2 := &mgo.DBRef{Collection: "col1", Id: 2, Database: "db2"}
235+
236+ err = db1.FindRef(ref1).One(&result)
237+ c.Assert(err, IsNil)
238+ c.Assert(result.N, Equals, 1)
239+
240+ err = db1.FindRef(ref2).One(&result)
241+ c.Assert(err, IsNil)
242+ c.Assert(result.N, Equals, 3)
243+
244+ err = db2.FindRef(ref1).One(&result)
245+ c.Assert(err, Equals, mgo.ErrNotFound)
246+
247+ err = db2.FindRef(ref2).One(&result)
248+ c.Assert(err, IsNil)
249+ c.Assert(result.N, Equals, 3)
250+
251+ err = session.FindRef(ref2).One(&result)
252+ c.Assert(err, IsNil)
253+ c.Assert(result.N, Equals, 3)
254+
255+ f := func() { session.FindRef(ref1).One(&result) }
256+ c.Assert(f, PanicMatches, "Can't resolve database for &mgo.DBRef{Collection:\"col1\", Id:1, Database:\"\"}")
257+}
258+
259+func (s *S) TestDatabaseAndCollectionNames(c *C) {
260+ session, err := mgo.Dial("localhost:40001")
261+ c.Assert(err, IsNil)
262+ defer session.Close()
263+
264+ db1 := session.DB("db1")
265+ db1col1 := db1.C("col1")
266+ db1col2 := db1.C("col2")
267+
268+ db2 := session.DB("db2")
269+ db2col1 := db2.C("col3")
270+
271+ err = db1col1.Insert(M{"_id": 1})
272+ c.Assert(err, IsNil)
273+ err = db1col2.Insert(M{"_id": 1})
274+ c.Assert(err, IsNil)
275+ err = db2col1.Insert(M{"_id": 1})
276+ c.Assert(err, IsNil)
277+
278+ names, err := session.DatabaseNames()
279+ c.Assert(err, IsNil)
280+ if !reflect.DeepEqual(names, []string{"db1", "db2"}) {
281+ // 2.4+ has "local" as well.
282+ c.Assert(names, DeepEquals, []string{"db1", "db2", "local"})
283+ }
284+
285+ // Try to exercise cursor logic. 2.8.0-rc3 still ignores this.
286+ session.SetBatch(2)
287+
288+ names, err = db1.CollectionNames()
289+ c.Assert(err, IsNil)
290+ c.Assert(names, DeepEquals, []string{"col1", "col2", "system.indexes"})
291+
292+ names, err = db2.CollectionNames()
293+ c.Assert(err, IsNil)
294+ c.Assert(names, DeepEquals, []string{"col3", "system.indexes"})
295+}
296+
297+func (s *S) TestSelect(c *C) {
298+ session, err := mgo.Dial("localhost:40001")
299+ c.Assert(err, IsNil)
300+ defer session.Close()
301+
302+ coll := session.DB("mydb").C("mycoll")
303+ coll.Insert(M{"a": 1, "b": 2})
304+
305+ result := struct{ A, B int }{}
306+
307+ err = coll.Find(M{"a": 1}).Select(M{"b": 1}).One(&result)
308+ c.Assert(err, IsNil)
309+ c.Assert(result.A, Equals, 0)
310+ c.Assert(result.B, Equals, 2)
311+}
312+
313+func (s *S) TestInlineMap(c *C) {
314+ session, err := mgo.Dial("localhost:40001")
315+ c.Assert(err, IsNil)
316+ defer session.Close()
317+
318+ coll := session.DB("mydb").C("mycoll")
319+
320+ var v, result1 struct {
321+ A int
322+ M map[string]int ",inline"
323+ }
324+
325+ v.A = 1
326+ v.M = map[string]int{"b": 2}
327+ err = coll.Insert(v)
328+ c.Assert(err, IsNil)
329+
330+ noId := M{"_id": 0}
331+
332+ err = coll.Find(nil).Select(noId).One(&result1)
333+ c.Assert(err, IsNil)
334+ c.Assert(result1.A, Equals, 1)
335+ c.Assert(result1.M, DeepEquals, map[string]int{"b": 2})
336+
337+ var result2 M
338+ err = coll.Find(nil).Select(noId).One(&result2)
339+ c.Assert(err, IsNil)
340+ c.Assert(result2, DeepEquals, M{"a": 1, "b": 2})
341+
342+}
343+
344+func (s *S) TestUpdate(c *C) {
345+ session, err := mgo.Dial("localhost:40001")
346+ c.Assert(err, IsNil)
347+ defer session.Close()
348+
349+ coll := session.DB("mydb").C("mycoll")
350+
351+ ns := []int{40, 41, 42, 43, 44, 45, 46}
352+ for _, n := range ns {
353+ err := coll.Insert(M{"k": n, "n": n})
354+ c.Assert(err, IsNil)
355+ }
356+
357+ // No changes is a no-op and shouldn't return an error.
358+ err = coll.Update(M{"k": 42}, M{"$set": M{"n": 42}})
359+ c.Assert(err, IsNil)
360+
361+ err = coll.Update(M{"k": 42}, M{"$inc": M{"n": 1}})
362+ c.Assert(err, IsNil)
363+
364+ result := make(M)
365+ err = coll.Find(M{"k": 42}).One(result)
366+ c.Assert(err, IsNil)
367+ c.Assert(result["n"], Equals, 43)
368+
369+ err = coll.Update(M{"k": 47}, M{"k": 47, "n": 47})
370+ c.Assert(err, Equals, mgo.ErrNotFound)
371+
372+ err = coll.Find(M{"k": 47}).One(result)
373+ c.Assert(err, Equals, mgo.ErrNotFound)
374+}
375+
376+func (s *S) TestUpdateId(c *C) {
377+ session, err := mgo.Dial("localhost:40001")
378+ c.Assert(err, IsNil)
379+ defer session.Close()
380+
381+ coll := session.DB("mydb").C("mycoll")
382+
383+ ns := []int{40, 41, 42, 43, 44, 45, 46}
384+ for _, n := range ns {
385+ err := coll.Insert(M{"_id": n, "n": n})
386+ c.Assert(err, IsNil)
387+ }
388+
389+ err = coll.UpdateId(42, M{"$inc": M{"n": 1}})
390+ c.Assert(err, IsNil)
391+
392+ result := make(M)
393+ err = coll.FindId(42).One(result)
394+ c.Assert(err, IsNil)
395+ c.Assert(result["n"], Equals, 43)
396+
397+ err = coll.UpdateId(47, M{"k": 47, "n": 47})
398+ c.Assert(err, Equals, mgo.ErrNotFound)
399+
400+ err = coll.FindId(47).One(result)
401+ c.Assert(err, Equals, mgo.ErrNotFound)
402+}
403+
404+func (s *S) TestUpdateNil(c *C) {
405+ session, err := mgo.Dial("localhost:40001")
406+ c.Assert(err, IsNil)
407+ defer session.Close()
408+
409+ coll := session.DB("mydb").C("mycoll")
410+
411+ err = coll.Insert(M{"k": 42, "n": 42})
412+ c.Assert(err, IsNil)
413+ err = coll.Update(nil, M{"$inc": M{"n": 1}})
414+ c.Assert(err, IsNil)
415+
416+ result := make(M)
417+ err = coll.Find(M{"k": 42}).One(result)
418+ c.Assert(err, IsNil)
419+ c.Assert(result["n"], Equals, 43)
420+
421+ err = coll.Insert(M{"k": 45, "n": 45})
422+ c.Assert(err, IsNil)
423+ _, err = coll.UpdateAll(nil, M{"$inc": M{"n": 1}})
424+ c.Assert(err, IsNil)
425+
426+ err = coll.Find(M{"k": 42}).One(result)
427+ c.Assert(err, IsNil)
428+ c.Assert(result["n"], Equals, 44)
429+ err = coll.Find(M{"k": 45}).One(result)
430+ c.Assert(err, IsNil)
431+ c.Assert(result["n"], Equals, 46)
432+}
433+
434+func (s *S) TestUpsert(c *C) {
435+ session, err := mgo.Dial("localhost:40001")
436+ c.Assert(err, IsNil)
437+ defer session.Close()
438+
439+ coll := session.DB("mydb").C("mycoll")
440+
441+ ns := []int{40, 41, 42, 43, 44, 45, 46}
442+ for _, n := range ns {
443+ err := coll.Insert(M{"k": n, "n": n})
444+ c.Assert(err, IsNil)
445+ }
446+
447+ info, err := coll.Upsert(M{"k": 42}, M{"k": 42, "n": 24})
448+ c.Assert(err, IsNil)
449+ c.Assert(info.Updated, Equals, 1)
450+ c.Assert(info.UpsertedId, IsNil)
451+
452+ result := M{}
453+ err = coll.Find(M{"k": 42}).One(result)
454+ c.Assert(err, IsNil)
455+ c.Assert(result["n"], Equals, 24)
456+
457+ // Insert with internally created id.
458+ info, err = coll.Upsert(M{"k": 47}, M{"k": 47, "n": 47})
459+ c.Assert(err, IsNil)
460+ c.Assert(info.Updated, Equals, 0)
461+ c.Assert(info.UpsertedId, NotNil)
462+
463+ err = coll.Find(M{"k": 47}).One(result)
464+ c.Assert(err, IsNil)
465+ c.Assert(result["n"], Equals, 47)
466+
467+ result = M{}
468+ err = coll.Find(M{"_id": info.UpsertedId}).One(result)
469+ c.Assert(err, IsNil)
470+ c.Assert(result["n"], Equals, 47)
471+
472+ // Insert with provided id.
473+ info, err = coll.Upsert(M{"k": 48}, M{"k": 48, "n": 48, "_id": 48})
474+ c.Assert(err, IsNil)
475+ c.Assert(info.Updated, Equals, 0)
476+ if s.versionAtLeast(2, 6) {
477+ c.Assert(info.UpsertedId, Equals, 48)
478+ } else {
479+ c.Assert(info.UpsertedId, IsNil) // Unfortunate, but that's what Mongo gave us.
480+ }
481+
482+ err = coll.Find(M{"k": 48}).One(result)
483+ c.Assert(err, IsNil)
484+ c.Assert(result["n"], Equals, 48)
485+}
486+
487+func (s *S) TestUpsertId(c *C) {
488+ session, err := mgo.Dial("localhost:40001")
489+ c.Assert(err, IsNil)
490+ defer session.Close()
491+
492+ coll := session.DB("mydb").C("mycoll")
493+
494+ ns := []int{40, 41, 42, 43, 44, 45, 46}
495+ for _, n := range ns {
496+ err := coll.Insert(M{"_id": n, "n": n})
497+ c.Assert(err, IsNil)
498+ }
499+
500+ info, err := coll.UpsertId(42, M{"n": 24})
501+ c.Assert(err, IsNil)
502+ c.Assert(info.Updated, Equals, 1)
503+ c.Assert(info.UpsertedId, IsNil)
504+
505+ result := M{}
506+ err = coll.FindId(42).One(result)
507+ c.Assert(err, IsNil)
508+ c.Assert(result["n"], Equals, 24)
509+
510+ info, err = coll.UpsertId(47, M{"_id": 47, "n": 47})
511+ c.Assert(err, IsNil)
512+ c.Assert(info.Updated, Equals, 0)
513+ if s.versionAtLeast(2, 6) {
514+ c.Assert(info.UpsertedId, Equals, 47)
515+ } else {
516+ c.Assert(info.UpsertedId, IsNil)
517+ }
518+
519+ err = coll.FindId(47).One(result)
520+ c.Assert(err, IsNil)
521+ c.Assert(result["n"], Equals, 47)
522+}
523+
524+func (s *S) TestUpdateAll(c *C) {
525+ session, err := mgo.Dial("localhost:40001")
526+ c.Assert(err, IsNil)
527+ defer session.Close()
528+
529+ coll := session.DB("mydb").C("mycoll")
530+
531+ ns := []int{40, 41, 42, 43, 44, 45, 46}
532+ for _, n := range ns {
533+ err := coll.Insert(M{"k": n, "n": n})
534+ c.Assert(err, IsNil)
535+ }
536+
537+ // Don't actually modify the documents. Should still report 4 matching updates.
538+ info, err := coll.UpdateAll(M{"k": M{"$gt": 42}}, M{"$unset": M{"missing": 1}})
539+ c.Assert(err, IsNil)
540+ c.Assert(info.Updated, Equals, 4)
541+
542+ info, err = coll.UpdateAll(M{"k": M{"$gt": 42}}, M{"$inc": M{"n": 1}})
543+ c.Assert(err, IsNil)
544+ c.Assert(info.Updated, Equals, 4)
545+
546+ result := make(M)
547+ err = coll.Find(M{"k": 42}).One(result)
548+ c.Assert(err, IsNil)
549+ c.Assert(result["n"], Equals, 42)
550+
551+ err = coll.Find(M{"k": 43}).One(result)
552+ c.Assert(err, IsNil)
553+ c.Assert(result["n"], Equals, 44)
554+
555+ err = coll.Find(M{"k": 44}).One(result)
556+ c.Assert(err, IsNil)
557+ c.Assert(result["n"], Equals, 45)
558+
559+ if !s.versionAtLeast(2, 6) {
560+ // 2.6 made this invalid.
561+ info, err = coll.UpdateAll(M{"k": 47}, M{"k": 47, "n": 47})
562+ c.Assert(err, Equals, nil)
563+ c.Assert(info.Updated, Equals, 0)
564+ }
565+}
566+
567+func (s *S) TestRemove(c *C) {
568+ session, err := mgo.Dial("localhost:40001")
569+ c.Assert(err, IsNil)
570+ defer session.Close()
571+
572+ coll := session.DB("mydb").C("mycoll")
573+
574+ ns := []int{40, 41, 42, 43, 44, 45, 46}
575+ for _, n := range ns {
576+ err := coll.Insert(M{"n": n})
577+ c.Assert(err, IsNil)
578+ }
579+
580+ err = coll.Remove(M{"n": M{"$gt": 42}})
581+ c.Assert(err, IsNil)
582+
583+ result := &struct{ N int }{}
584+ err = coll.Find(M{"n": 42}).One(result)
585+ c.Assert(err, IsNil)
586+ c.Assert(result.N, Equals, 42)
587+
588+ err = coll.Find(M{"n": 43}).One(result)
589+ c.Assert(err, Equals, mgo.ErrNotFound)
590+
591+ err = coll.Find(M{"n": 44}).One(result)
592+ c.Assert(err, IsNil)
593+ c.Assert(result.N, Equals, 44)
594+}
595+
596+func (s *S) TestRemoveId(c *C) {
597+ session, err := mgo.Dial("localhost:40001")
598+ c.Assert(err, IsNil)
599+ defer session.Close()
600+
601+ coll := session.DB("mydb").C("mycoll")
602+
603+ err = coll.Insert(M{"_id": 40}, M{"_id": 41}, M{"_id": 42})
604+ c.Assert(err, IsNil)
605+
606+ err = coll.RemoveId(41)
607+ c.Assert(err, IsNil)
608+
609+ c.Assert(coll.FindId(40).One(nil), IsNil)
610+ c.Assert(coll.FindId(41).One(nil), Equals, mgo.ErrNotFound)
611+ c.Assert(coll.FindId(42).One(nil), IsNil)
612+}
613+
614+func (s *S) TestRemoveUnsafe(c *C) {
615+ session, err := mgo.Dial("localhost:40001")
616+ c.Assert(err, IsNil)
617+ defer session.Close()
618+
619+ session.SetSafe(nil)
620+
621+ coll := session.DB("mydb").C("mycoll")
622+
623+ err = coll.Insert(M{"_id": 40}, M{"_id": 41}, M{"_id": 42})
624+ c.Assert(err, IsNil)
625+
626+ err = coll.RemoveId(41)
627+ c.Assert(err, IsNil)
628+
629+ c.Assert(coll.FindId(40).One(nil), IsNil)
630+ c.Assert(coll.FindId(41).One(nil), Equals, mgo.ErrNotFound)
631+ c.Assert(coll.FindId(42).One(nil), IsNil)
632+}
633+
634+func (s *S) TestRemoveAll(c *C) {
635+ session, err := mgo.Dial("localhost:40001")
636+ c.Assert(err, IsNil)
637+ defer session.Close()
638+
639+ coll := session.DB("mydb").C("mycoll")
640+
641+ ns := []int{40, 41, 42, 43, 44, 45, 46}
642+ for _, n := range ns {
643+ err := coll.Insert(M{"n": n})
644+ c.Assert(err, IsNil)
645+ }
646+
647+ info, err := coll.RemoveAll(M{"n": M{"$gt": 42}})
648+ c.Assert(err, IsNil)
649+ c.Assert(info.Updated, Equals, 0)
650+ c.Assert(info.Removed, Equals, 4)
651+ c.Assert(info.UpsertedId, IsNil)
652+
653+ result := &struct{ N int }{}
654+ err = coll.Find(M{"n": 42}).One(result)
655+ c.Assert(err, IsNil)
656+ c.Assert(result.N, Equals, 42)
657+
658+ err = coll.Find(M{"n": 43}).One(result)
659+ c.Assert(err, Equals, mgo.ErrNotFound)
660+
661+ err = coll.Find(M{"n": 44}).One(result)
662+ c.Assert(err, Equals, mgo.ErrNotFound)
663+
664+ info, err = coll.RemoveAll(nil)
665+ c.Assert(err, IsNil)
666+ c.Assert(info.Updated, Equals, 0)
667+ c.Assert(info.Removed, Equals, 3)
668+ c.Assert(info.UpsertedId, IsNil)
669+
670+ n, err := coll.Find(nil).Count()
671+ c.Assert(err, IsNil)
672+ c.Assert(n, Equals, 0)
673+}
674+
675+func (s *S) TestDropDatabase(c *C) {
676+ session, err := mgo.Dial("localhost:40001")
677+ c.Assert(err, IsNil)
678+ defer session.Close()
679+
680+ db1 := session.DB("db1")
681+ db1.C("col").Insert(M{"_id": 1})
682+
683+ db2 := session.DB("db2")
684+ db2.C("col").Insert(M{"_id": 1})
685+
686+ err = db1.DropDatabase()
687+ c.Assert(err, IsNil)
688+
689+ names, err := session.DatabaseNames()
690+ c.Assert(err, IsNil)
691+ if !reflect.DeepEqual(names, []string{"db2"}) {
692+ // 2.4+ has "local" as well.
693+ c.Assert(names, DeepEquals, []string{"db2", "local"})
694+ }
695+
696+ err = db2.DropDatabase()
697+ c.Assert(err, IsNil)
698+
699+ names, err = session.DatabaseNames()
700+ c.Assert(err, IsNil)
701+ if !reflect.DeepEqual(names, []string(nil)) {
702+ // 2.4+ has "local" as well.
703+ c.Assert(names, DeepEquals, []string{"local"})
704+ }
705+}
706+
707+func (s *S) TestDropCollection(c *C) {
708+ session, err := mgo.Dial("localhost:40001")
709+ c.Assert(err, IsNil)
710+ defer session.Close()
711+
712+ db := session.DB("db1")
713+ db.C("col1").Insert(M{"_id": 1})
714+ db.C("col2").Insert(M{"_id": 1})
715+
716+ err = db.C("col1").DropCollection()
717+ c.Assert(err, IsNil)
718+
719+ names, err := db.CollectionNames()
720+ c.Assert(err, IsNil)
721+ c.Assert(names, DeepEquals, []string{"col2", "system.indexes"})
722+
723+ err = db.C("col2").DropCollection()
724+ c.Assert(err, IsNil)
725+
726+ names, err = db.CollectionNames()
727+ c.Assert(err, IsNil)
728+ c.Assert(names, DeepEquals, []string{"system.indexes"})
729+}
730+
731+func (s *S) TestCreateCollectionCapped(c *C) {
732+ session, err := mgo.Dial("localhost:40001")
733+ c.Assert(err, IsNil)
734+ defer session.Close()
735+
736+ coll := session.DB("mydb").C("mycoll")
737+
738+ info := &mgo.CollectionInfo{
739+ Capped: true,
740+ MaxBytes: 1024,
741+ MaxDocs: 3,
742+ }
743+ err = coll.Create(info)
744+ c.Assert(err, IsNil)
745+
746+ ns := []int{1, 2, 3, 4, 5}
747+ for _, n := range ns {
748+ err := coll.Insert(M{"n": n})
749+ c.Assert(err, IsNil)
750+ }
751+
752+ n, err := coll.Find(nil).Count()
753+ c.Assert(err, IsNil)
754+ c.Assert(n, Equals, 3)
755+}
756+
757+func (s *S) TestCreateCollectionNoIndex(c *C) {
758+ session, err := mgo.Dial("localhost:40001")
759+ c.Assert(err, IsNil)
760+ defer session.Close()
761+
762+ coll := session.DB("mydb").C("mycoll")
763+
764+ info := &mgo.CollectionInfo{
765+ DisableIdIndex: true,
766+ }
767+ err = coll.Create(info)
768+ c.Assert(err, IsNil)
769+
770+ err = coll.Insert(M{"n": 1})
771+ c.Assert(err, IsNil)
772+
773+ indexes, err := coll.Indexes()
774+ c.Assert(indexes, HasLen, 0)
775+}
776+
777+func (s *S) TestCreateCollectionForceIndex(c *C) {
778+ session, err := mgo.Dial("localhost:40001")
779+ c.Assert(err, IsNil)
780+ defer session.Close()
781+
782+ coll := session.DB("mydb").C("mycoll")
783+
784+ info := &mgo.CollectionInfo{
785+ ForceIdIndex: true,
786+ Capped: true,
787+ MaxBytes: 1024,
788+ }
789+ err = coll.Create(info)
790+ c.Assert(err, IsNil)
791+
792+ err = coll.Insert(M{"n": 1})
793+ c.Assert(err, IsNil)
794+
795+ indexes, err := coll.Indexes()
796+ c.Assert(indexes, HasLen, 1)
797+}
798+
799+func (s *S) TestIsDupValues(c *C) {
800+ c.Assert(mgo.IsDup(nil), Equals, false)
801+ c.Assert(mgo.IsDup(&mgo.LastError{Code: 1}), Equals, false)
802+ c.Assert(mgo.IsDup(&mgo.QueryError{Code: 1}), Equals, false)
803+ c.Assert(mgo.IsDup(&mgo.LastError{Code: 11000}), Equals, true)
804+ c.Assert(mgo.IsDup(&mgo.QueryError{Code: 11000}), Equals, true)
805+ c.Assert(mgo.IsDup(&mgo.LastError{Code: 11001}), Equals, true)
806+ c.Assert(mgo.IsDup(&mgo.QueryError{Code: 11001}), Equals, true)
807+ c.Assert(mgo.IsDup(&mgo.LastError{Code: 12582}), Equals, true)
808+ c.Assert(mgo.IsDup(&mgo.QueryError{Code: 12582}), Equals, true)
809+ lerr := &mgo.LastError{Code: 16460, Err: "error inserting 1 documents to shard ... caused by :: E11000 duplicate key error index: ..."}
810+ c.Assert(mgo.IsDup(lerr), Equals, true)
811+}
812+
813+func (s *S) TestIsDupPrimary(c *C) {
814+ session, err := mgo.Dial("localhost:40001")
815+ c.Assert(err, IsNil)
816+ defer session.Close()
817+
818+ coll := session.DB("mydb").C("mycoll")
819+
820+ err = coll.Insert(M{"_id": 1})
821+ c.Assert(err, IsNil)
822+ err = coll.Insert(M{"_id": 1})
823+ c.Assert(err, ErrorMatches, ".*duplicate key error.*")
824+ c.Assert(mgo.IsDup(err), Equals, true)
825+}
826+
827+func (s *S) TestIsDupUnique(c *C) {
828+ session, err := mgo.Dial("localhost:40001")
829+ c.Assert(err, IsNil)
830+ defer session.Close()
831+
832+ index := mgo.Index{
833+ Key: []string{"a", "b"},
834+ Unique: true,
835+ }
836+
837+ coll := session.DB("mydb").C("mycoll")
838+
839+ err = coll.EnsureIndex(index)
840+ c.Assert(err, IsNil)
841+
842+ err = coll.Insert(M{"a": 1, "b": 1})
843+ c.Assert(err, IsNil)
844+ err = coll.Insert(M{"a": 1, "b": 1})
845+ c.Assert(err, ErrorMatches, ".*duplicate key error.*")
846+ c.Assert(mgo.IsDup(err), Equals, true)
847+}
848+
849+func (s *S) TestIsDupCapped(c *C) {
850+ session, err := mgo.Dial("localhost:40001")
851+ c.Assert(err, IsNil)
852+ defer session.Close()
853+
854+ coll := session.DB("mydb").C("mycoll")
855+
856+ info := &mgo.CollectionInfo{
857+ ForceIdIndex: true,
858+ Capped: true,
859+ MaxBytes: 1024,
860+ }
861+ err = coll.Create(info)
862+ c.Assert(err, IsNil)
863+
864+ err = coll.Insert(M{"_id": 1})
865+ c.Assert(err, IsNil)
866+ err = coll.Insert(M{"_id": 1})
867+ // The error was different for capped collections before 2.6.
868+ c.Assert(err, ErrorMatches, ".*duplicate key.*")
869+ // The issue is reduced by using IsDup.
870+ c.Assert(mgo.IsDup(err), Equals, true)
871+}
872+
873+func (s *S) TestIsDupFindAndModify(c *C) {
874+ session, err := mgo.Dial("localhost:40001")
875+ c.Assert(err, IsNil)
876+ defer session.Close()
877+
878+ coll := session.DB("mydb").C("mycoll")
879+
880+ err = coll.EnsureIndex(mgo.Index{Key: []string{"n"}, Unique: true})
881+ c.Assert(err, IsNil)
882+
883+ err = coll.Insert(M{"n": 1})
884+ c.Assert(err, IsNil)
885+ err = coll.Insert(M{"n": 2})
886+ c.Assert(err, IsNil)
887+ _, err = coll.Find(M{"n": 1}).Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}}, bson.M{})
888+ c.Assert(err, ErrorMatches, ".*duplicate key error.*")
889+ c.Assert(mgo.IsDup(err), Equals, true)
890+}
891+
892+func (s *S) TestFindAndModify(c *C) {
893+ session, err := mgo.Dial("localhost:40011")
894+ c.Assert(err, IsNil)
895+ defer session.Close()
896+
897+ coll := session.DB("mydb").C("mycoll")
898+
899+ err = coll.Insert(M{"n": 42})
900+
901+ session.SetMode(mgo.Monotonic, true)
902+
903+ result := M{}
904+ info, err := coll.Find(M{"n": 42}).Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}}, result)
905+ c.Assert(err, IsNil)
906+ c.Assert(result["n"], Equals, 42)
907+ c.Assert(info.Updated, Equals, 1)
908+ c.Assert(info.Removed, Equals, 0)
909+ c.Assert(info.UpsertedId, IsNil)
910+
911+ // A nil result parameter should be acceptable.
912+ info, err = coll.Find(M{"n": 43}).Apply(mgo.Change{Update: M{"$unset": M{"missing": 1}}}, nil)
913+ c.Assert(err, IsNil)
914+ c.Assert(info.Updated, Equals, 1)
915+ c.Assert(info.Removed, Equals, 0)
916+ c.Assert(info.UpsertedId, IsNil)
917+
918+ result = M{}
919+ info, err = coll.Find(M{"n": 43}).Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}, ReturnNew: true}, result)
920+ c.Assert(err, IsNil)
921+ c.Assert(result["n"], Equals, 44)
922+ c.Assert(info.Updated, Equals, 1)
923+ c.Assert(info.Removed, Equals, 0)
924+ c.Assert(info.UpsertedId, IsNil)
925+
926+ result = M{}
927+ info, err = coll.Find(M{"n": 50}).Apply(mgo.Change{Upsert: true, Update: M{"n": 51, "o": 52}}, result)
928+ c.Assert(err, IsNil)
929+ c.Assert(result["n"], IsNil)
930+ c.Assert(info.Updated, Equals, 0)
931+ c.Assert(info.Removed, Equals, 0)
932+ c.Assert(info.UpsertedId, NotNil)
933+
934+ result = M{}
935+ info, err = coll.Find(nil).Sort("-n").Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}, ReturnNew: true}, result)
936+ c.Assert(err, IsNil)
937+ c.Assert(result["n"], Equals, 52)
938+ c.Assert(info.Updated, Equals, 1)
939+ c.Assert(info.Removed, Equals, 0)
940+ c.Assert(info.UpsertedId, IsNil)
941+
942+ result = M{}
943+ info, err = coll.Find(M{"n": 52}).Select(M{"o": 1}).Apply(mgo.Change{Remove: true}, result)
944+ c.Assert(err, IsNil)
945+ c.Assert(result["n"], IsNil)
946+ c.Assert(result["o"], Equals, 52)
947+ c.Assert(info.Updated, Equals, 0)
948+ c.Assert(info.Removed, Equals, 1)
949+ c.Assert(info.UpsertedId, IsNil)
950+
951+ result = M{}
952+ info, err = coll.Find(M{"n": 60}).Apply(mgo.Change{Remove: true}, result)
953+ c.Assert(err, Equals, mgo.ErrNotFound)
954+ c.Assert(len(result), Equals, 0)
955+ c.Assert(info, IsNil)
956+}
957+
958+func (s *S) TestFindAndModifyBug997828(c *C) {
959+ session, err := mgo.Dial("localhost:40001")
960+ c.Assert(err, IsNil)
961+ defer session.Close()
962+
963+ coll := session.DB("mydb").C("mycoll")
964+
965+ err = coll.Insert(M{"n": "not-a-number"})
966+
967+ result := make(M)
968+ _, err = coll.Find(M{"n": "not-a-number"}).Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}}, result)
969+ c.Assert(err, ErrorMatches, `(exception: )?Cannot apply \$inc .*`)
970+ if s.versionAtLeast(2, 1) {
971+ qerr, _ := err.(*mgo.QueryError)
972+ c.Assert(qerr, NotNil, Commentf("err: %#v", err))
973+ if s.versionAtLeast(2, 6) {
974+ // Oh, the dance of error codes. :-(
975+ c.Assert(qerr.Code, Equals, 16837)
976+ } else {
977+ c.Assert(qerr.Code, Equals, 10140)
978+ }
979+ } else {
980+ lerr, _ := err.(*mgo.LastError)
981+ c.Assert(lerr, NotNil, Commentf("err: %#v", err))
982+ c.Assert(lerr.Code, Equals, 10140)
983+ }
984+}
985+
986+func (s *S) TestCountCollection(c *C) {
987+ session, err := mgo.Dial("localhost:40001")
988+ c.Assert(err, IsNil)
989+ defer session.Close()
990+
991+ coll := session.DB("mydb").C("mycoll")
992+
993+ ns := []int{40, 41, 42}
994+ for _, n := range ns {
995+ err := coll.Insert(M{"n": n})
996+ c.Assert(err, IsNil)
997+ }
998+
999+ n, err := coll.Count()
1000+ c.Assert(err, IsNil)
1001+ c.Assert(n, Equals, 3)
1002+}
1003+
1004+func (s *S) TestCountQuery(c *C) {
1005+ session, err := mgo.Dial("localhost:40001")
1006+ c.Assert(err, IsNil)
1007+ defer session.Close()
1008+
1009+ coll := session.DB("mydb").C("mycoll")
1010+
1011+ ns := []int{40, 41, 42}
1012+ for _, n := range ns {
1013+ err := coll.Insert(M{"n": n})
1014+ c.Assert(err, IsNil)
1015+ }
1016+
1017+ n, err := coll.Find(M{"n": M{"$gt": 40}}).Count()
1018+ c.Assert(err, IsNil)
1019+ c.Assert(n, Equals, 2)
1020+}
1021+
1022+func (s *S) TestCountQuerySorted(c *C) {
1023+ session, err := mgo.Dial("localhost:40001")
1024+ c.Assert(err, IsNil)
1025+ defer session.Close()
1026+
1027+ coll := session.DB("mydb").C("mycoll")
1028+
1029+ ns := []int{40, 41, 42}
1030+ for _, n := range ns {
1031+ err := coll.Insert(M{"n": n})
1032+ c.Assert(err, IsNil)
1033+ }
1034+
1035+ n, err := coll.Find(M{"n": M{"$gt": 40}}).Sort("n").Count()
1036+ c.Assert(err, IsNil)
1037+ c.Assert(n, Equals, 2)
1038+}
1039+
1040+func (s *S) TestCountSkipLimit(c *C) {
1041+ session, err := mgo.Dial("localhost:40001")
1042+ c.Assert(err, IsNil)
1043+ defer session.Close()
1044+
1045+ coll := session.DB("mydb").C("mycoll")
1046+
1047+ ns := []int{40, 41, 42, 43, 44}
1048+ for _, n := range ns {
1049+ err := coll.Insert(M{"n": n})
1050+ c.Assert(err, IsNil)
1051+ }
1052+
1053+ n, err := coll.Find(nil).Skip(1).Limit(3).Count()
1054+ c.Assert(err, IsNil)
1055+ c.Assert(n, Equals, 3)
1056+
1057+ n, err = coll.Find(nil).Skip(1).Limit(5).Count()
1058+ c.Assert(err, IsNil)
1059+ c.Assert(n, Equals, 4)
1060+}
1061+
1062+func (s *S) TestQueryExplain(c *C) {
1063+ session, err := mgo.Dial("localhost:40001")
1064+ c.Assert(err, IsNil)
1065+ defer session.Close()
1066+
1067+ coll := session.DB("mydb").C("mycoll")
1068+
1069+ ns := []int{40, 41, 42}
1070+ for _, n := range ns {
1071+ err := coll.Insert(M{"n": n})
1072+ c.Assert(err, IsNil)
1073+ }
1074+
1075+ m := M{}
1076+ query := coll.Find(nil).Limit(2)
1077+ err = query.Explain(m)
1078+ c.Assert(err, IsNil)
1079+ if m["queryPlanner"] != nil {
1080+ c.Assert(m["executionStats"].(M)["totalDocsExamined"], Equals, 2)
1081+ } else {
1082+ c.Assert(m["cursor"], Equals, "BasicCursor")
1083+ c.Assert(m["nscanned"], Equals, 2)
1084+ c.Assert(m["n"], Equals, 2)
1085+ }
1086+
1087+ n := 0
1088+ var result M
1089+ iter := query.Iter()
1090+ for iter.Next(&result) {
1091+ n++
1092+ }
1093+ c.Assert(iter.Close(), IsNil)
1094+ c.Assert(n, Equals, 2)
1095+}
1096+
1097+func (s *S) TestQuerySetMaxScan(c *C) {
1098+ session, err := mgo.Dial("localhost:40001")
1099+ c.Assert(err, IsNil)
1100+ defer session.Close()
1101+ coll := session.DB("mydb").C("mycoll")
1102+
1103+ ns := []int{40, 41, 42}
1104+ for _, n := range ns {
1105+ err := coll.Insert(M{"n": n})
1106+ c.Assert(err, IsNil)
1107+ }
1108+
1109+ query := coll.Find(nil).SetMaxScan(2)
1110+ var result []M
1111+ err = query.All(&result)
1112+ c.Assert(err, IsNil)
1113+ c.Assert(result, HasLen, 2)
1114+}
1115+
1116+func (s *S) TestQuerySetMaxTime(c *C) {
1117+ if !s.versionAtLeast(2, 6) {
1118+ c.Skip("SetMaxTime only supported in 2.6+")
1119+ }
1120+
1121+ session, err := mgo.Dial("localhost:40001")
1122+ c.Assert(err, IsNil)
1123+ defer session.Close()
1124+ coll := session.DB("mydb").C("mycoll")
1125+
1126+ for i := 0; i < 1000; i++ {
1127+ err := coll.Insert(M{"n": i})
1128+ c.Assert(err, IsNil)
1129+ }
1130+
1131+ query := coll.Find(nil)
1132+ query.SetMaxTime(1 * time.Millisecond)
1133+ query.Batch(2)
1134+ var result []M
1135+ err = query.All(&result)
1136+ c.Assert(err, ErrorMatches, "operation exceeded time limit")
1137+}
1138+
1139+func (s *S) TestQueryHint(c *C) {
1140+ session, err := mgo.Dial("localhost:40001")
1141+ c.Assert(err, IsNil)
1142+ defer session.Close()
1143+
1144+ coll := session.DB("mydb").C("mycoll")
1145+ coll.EnsureIndexKey("a")
1146+
1147+ m := M{}
1148+ err = coll.Find(nil).Hint("a").Explain(m)
1149+ c.Assert(err, IsNil)
1150+
1151+ if m["queryPlanner"] != nil {
1152+ m = m["queryPlanner"].(M)
1153+ m = m["winningPlan"].(M)
1154+ m = m["inputStage"].(M)
1155+ c.Assert(m["indexName"], Equals, "a_1")
1156+ } else {
1157+ c.Assert(m["indexBounds"], NotNil)
1158+ c.Assert(m["indexBounds"].(M)["a"], NotNil)
1159+ }
1160+}
1161+
1162+func (s *S) TestQueryComment(c *C) {
1163+ session, err := mgo.Dial("localhost:40001")
1164+ c.Assert(err, IsNil)
1165+ defer session.Close()
1166+
1167+ db := session.DB("mydb")
1168+ coll := db.C("mycoll")
1169+
1170+ err = db.Run(bson.M{"profile": 2}, nil)
1171+ c.Assert(err, IsNil)
1172+
1173+ ns := []int{40, 41, 42}
1174+ for _, n := range ns {
1175+ err := coll.Insert(M{"n": n})
1176+ c.Assert(err, IsNil)
1177+ }
1178+
1179+ query := coll.Find(bson.M{"n": 41})
1180+ query.Comment("some comment")
1181+ err = query.One(nil)
1182+ c.Assert(err, IsNil)
1183+
1184+ query = coll.Find(bson.M{"n": 41})
1185+ query.Comment("another comment")
1186+ err = query.One(nil)
1187+ c.Assert(err, IsNil)
1188+
1189+ n, err := session.DB("mydb").C("system.profile").Find(bson.M{"query.$query.n": 41, "query.$comment": "some comment"}).Count()
1190+ c.Assert(err, IsNil)
1191+ c.Assert(n, Equals, 1)
1192+}
1193+
1194+func (s *S) TestFindOneNotFound(c *C) {
1195+ session, err := mgo.Dial("localhost:40001")
1196+ c.Assert(err, IsNil)
1197+ defer session.Close()
1198+
1199+ coll := session.DB("mydb").C("mycoll")
1200+
1201+ result := struct{ A, B int }{}
1202+ err = coll.Find(M{"a": 1}).One(&result)
1203+ c.Assert(err, Equals, mgo.ErrNotFound)
1204+ c.Assert(err, ErrorMatches, "not found")
1205+ c.Assert(err == mgo.ErrNotFound, Equals, true)
1206+}
1207+
1208+func (s *S) TestFindNil(c *C) {
1209+ session, err := mgo.Dial("localhost:40001")
1210+ c.Assert(err, IsNil)
1211+ defer session.Close()
1212+
1213+ coll := session.DB("mydb").C("mycoll")
1214+ err = coll.Insert(M{"n": 1})
1215+ c.Assert(err, IsNil)
1216+
1217+ result := struct{ N int }{}
1218+
1219+ err = coll.Find(nil).One(&result)
1220+ c.Assert(err, IsNil)
1221+ c.Assert(result.N, Equals, 1)
1222+}
1223+
1224+func (s *S) TestFindId(c *C) {
1225+ session, err := mgo.Dial("localhost:40001")
1226+ c.Assert(err, IsNil)
1227+ defer session.Close()
1228+
1229+ coll := session.DB("mydb").C("mycoll")
1230+ err = coll.Insert(M{"_id": 41, "n": 41})
1231+ c.Assert(err, IsNil)
1232+ err = coll.Insert(M{"_id": 42, "n": 42})
1233+ c.Assert(err, IsNil)
1234+
1235+ result := struct{ N int }{}
1236+
1237+ err = coll.FindId(42).One(&result)
1238+ c.Assert(err, IsNil)
1239+ c.Assert(result.N, Equals, 42)
1240+}
1241+
1242+func (s *S) TestFindIterAll(c *C) {
1243+ session, err := mgo.Dial("localhost:40001")
1244+ c.Assert(err, IsNil)
1245+ defer session.Close()
1246+
1247+ coll := session.DB("mydb").C("mycoll")
1248+
1249+ ns := []int{40, 41, 42, 43, 44, 45, 46}
1250+ for _, n := range ns {
1251+ coll.Insert(M{"n": n})
1252+ }
1253+
1254+ session.Refresh() // Release socket.
1255+
1256+ mgo.ResetStats()
1257+
1258+ iter := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2).Iter()
1259+ result := struct{ N int }{}
1260+ for i := 2; i < 7; i++ {
1261+ ok := iter.Next(&result)
1262+ c.Assert(ok, Equals, true)
1263+ c.Assert(result.N, Equals, ns[i])
1264+ if i == 1 {
1265+ stats := mgo.GetStats()
1266+ c.Assert(stats.ReceivedDocs, Equals, 2)
1267+ }
1268+ }
1269+
1270+ ok := iter.Next(&result)
1271+ c.Assert(ok, Equals, false)
1272+ c.Assert(iter.Close(), IsNil)
1273+
1274+ session.Refresh() // Release socket.
1275+
1276+ stats := mgo.GetStats()
1277+ c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 2*GET_MORE_OP
1278+ c.Assert(stats.ReceivedOps, Equals, 3) // and their REPLY_OPs.
1279+ c.Assert(stats.ReceivedDocs, Equals, 5)
1280+ c.Assert(stats.SocketsInUse, Equals, 0)
1281+}
1282+
1283+func (s *S) TestFindIterTwiceWithSameQuery(c *C) {
1284+ session, err := mgo.Dial("localhost:40001")
1285+ c.Assert(err, IsNil)
1286+ defer session.Close()
1287+
1288+ coll := session.DB("mydb").C("mycoll")
1289+
1290+ for i := 40; i != 47; i++ {
1291+ coll.Insert(M{"n": i})
1292+ }
1293+
1294+ query := coll.Find(M{}).Sort("n")
1295+
1296+ result1 := query.Skip(1).Iter()
1297+ result2 := query.Skip(2).Iter()
1298+
1299+ result := struct{ N int }{}
1300+ ok := result2.Next(&result)
1301+ c.Assert(ok, Equals, true)
1302+ c.Assert(result.N, Equals, 42)
1303+ ok = result1.Next(&result)
1304+ c.Assert(ok, Equals, true)
1305+ c.Assert(result.N, Equals, 41)
1306+}
1307+
1308+func (s *S) TestFindIterWithoutResults(c *C) {
1309+ session, err := mgo.Dial("localhost:40001")
1310+ c.Assert(err, IsNil)
1311+ defer session.Close()
1312+
1313+ coll := session.DB("mydb").C("mycoll")
1314+ coll.Insert(M{"n": 42})
1315+
1316+ iter := coll.Find(M{"n": 0}).Iter()
1317+
1318+ result := struct{ N int }{}
1319+ ok := iter.Next(&result)
1320+ c.Assert(ok, Equals, false)
1321+ c.Assert(iter.Close(), IsNil)
1322+ c.Assert(result.N, Equals, 0)
1323+}
1324+
1325+func (s *S) TestFindIterLimit(c *C) {
1326+ session, err := mgo.Dial("localhost:40001")
1327+ c.Assert(err, IsNil)
1328+ defer session.Close()
1329+
1330+ coll := session.DB("mydb").C("mycoll")
1331+
1332+ ns := []int{40, 41, 42, 43, 44, 45, 46}
1333+ for _, n := range ns {
1334+ coll.Insert(M{"n": n})
1335+ }
1336+
1337+ session.Refresh() // Release socket.
1338+
1339+ mgo.ResetStats()
1340+
1341+ query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Limit(3)
1342+ iter := query.Iter()
1343+
1344+ result := struct{ N int }{}
1345+ for i := 2; i < 5; i++ {
1346+ ok := iter.Next(&result)
1347+ c.Assert(ok, Equals, true)
1348+ c.Assert(result.N, Equals, ns[i])
1349+ }
1350+
1351+ ok := iter.Next(&result)
1352+ c.Assert(ok, Equals, false)
1353+ c.Assert(iter.Close(), IsNil)
1354+
1355+ session.Refresh() // Release socket.
1356+
1357+ stats := mgo.GetStats()
1358+ c.Assert(stats.SentOps, Equals, 2) // 1*QUERY_OP + 1*KILL_CURSORS_OP
1359+ c.Assert(stats.ReceivedOps, Equals, 1) // and its REPLY_OP
1360+ c.Assert(stats.ReceivedDocs, Equals, 3)
1361+ c.Assert(stats.SocketsInUse, Equals, 0)
1362+}
1363+
1364+var cursorTimeout = flag.Bool("cursor-timeout", false, "Enable cursor timeout test")
1365+
1366+func (s *S) TestFindIterCursorTimeout(c *C) {
1367+ if !*cursorTimeout {
1368+ c.Skip("-cursor-timeout")
1369+ }
1370+ session, err := mgo.Dial("localhost:40001")
1371+ c.Assert(err, IsNil)
1372+ defer session.Close()
1373+
1374+ type Doc struct {
1375+ Id int "_id"
1376+ }
1377+
1378+ coll := session.DB("test").C("test")
1379+ coll.Remove(nil)
1380+ for i := 0; i < 100; i++ {
1381+ err = coll.Insert(Doc{i})
1382+ c.Assert(err, IsNil)
1383+ }
1384+
1385+ session.SetBatch(1)
1386+ iter := coll.Find(nil).Iter()
1387+ var doc Doc
1388+ if !iter.Next(&doc) {
1389+ c.Fatalf("iterator failed to return any documents")
1390+ }
1391+
1392+ for i := 10; i > 0; i-- {
1393+ c.Logf("Sleeping... %d minutes to go...", i)
1394+ time.Sleep(1*time.Minute + 2*time.Second)
1395+ }
1396+
1397+ // Drain any existing documents that were fetched.
1398+ if !iter.Next(&doc) {
1399+ c.Fatalf("iterator with timed out cursor failed to return previously cached document")
1400+ }
1401+ if iter.Next(&doc) {
1402+ c.Fatalf("timed out cursor returned document")
1403+ }
1404+
1405+ c.Assert(iter.Err(), Equals, mgo.ErrCursor)
1406+}
1407+
1408+func (s *S) TestTooManyItemsLimitBug(c *C) {
1409+ if *fast {
1410+ c.Skip("-fast")
1411+ }
1412+
1413+ session, err := mgo.Dial("localhost:40001")
1414+ c.Assert(err, IsNil)
1415+ defer session.Close()
1416+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU()))
1417+
1418+ mgo.SetDebug(false)
1419+ coll := session.DB("mydb").C("mycoll")
1420+ words := strings.Split("foo bar baz", " ")
1421+ for i := 0; i < 5; i++ {
1422+ words = append(words, words...)
1423+ }
1424+ doc := bson.D{{"words", words}}
1425+ inserts := 10000
1426+ limit := 5000
1427+ iters := 0
1428+ c.Assert(inserts > limit, Equals, true)
1429+ for i := 0; i < inserts; i++ {
1430+ err := coll.Insert(&doc)
1431+ c.Assert(err, IsNil)
1432+ }
1433+ iter := coll.Find(nil).Limit(limit).Iter()
1434+ for iter.Next(&doc) {
1435+ if iters%100 == 0 {
1436+ c.Logf("Seen %d docments", iters)
1437+ }
1438+ iters++
1439+ }
1440+ c.Assert(iter.Close(), IsNil)
1441+ c.Assert(iters, Equals, limit)
1442+}
1443+
1444+func serverCursorsOpen(session *mgo.Session) int {
1445+ var result struct {
1446+ Cursors struct {
1447+ TotalOpen int `bson:"totalOpen"`
1448+ TimedOut int `bson:"timedOut"`
1449+ }
1450+ }
1451+ err := session.Run("serverStatus", &result)
1452+ if err != nil {
1453+ panic(err)
1454+ }
1455+ return result.Cursors.TotalOpen
1456+}
1457+
1458+func (s *S) TestFindIterLimitWithMore(c *C) {
1459+ session, err := mgo.Dial("localhost:40001")
1460+ c.Assert(err, IsNil)
1461+ defer session.Close()
1462+
1463+ coll := session.DB("mydb").C("mycoll")
1464+
1465+ // Insane amounts of logging otherwise due to the
1466+ // amount of data being shuffled.
1467+ mgo.SetDebug(false)
1468+ defer mgo.SetDebug(true)
1469+
1470+ // Should amount to more than 4MB bson payload,
1471+ // the default limit per result chunk.
1472+ const total = 4096
1473+ var d struct{ A [1024]byte }
1474+ docs := make([]interface{}, total)
1475+ for i := 0; i < total; i++ {
1476+ docs[i] = &d
1477+ }
1478+ err = coll.Insert(docs...)
1479+ c.Assert(err, IsNil)
1480+
1481+ n, err := coll.Count()
1482+ c.Assert(err, IsNil)
1483+ c.Assert(n, Equals, total)
1484+
1485+ // First, try restricting to a single chunk with a negative limit.
1486+ nresults := 0
1487+ iter := coll.Find(nil).Limit(-total).Iter()
1488+ var discard struct{}
1489+ for iter.Next(&discard) {
1490+ nresults++
1491+ }
1492+ if nresults < total/2 || nresults >= total {
1493+ c.Fatalf("Bad result size with negative limit: %d", nresults)
1494+ }
1495+
1496+ cursorsOpen := serverCursorsOpen(session)
1497+
1498+ // Try again, with a positive limit. Should reach the end now,
1499+ // using multiple chunks.
1500+ nresults = 0
1501+ iter = coll.Find(nil).Limit(total).Iter()
1502+ for iter.Next(&discard) {
1503+ nresults++
1504+ }
1505+ c.Assert(nresults, Equals, total)
1506+
1507+ // Ensure the cursor used is properly killed.
1508+ c.Assert(serverCursorsOpen(session), Equals, cursorsOpen)
1509+
1510+ // Edge case, -MinInt == -MinInt.
1511+ nresults = 0
1512+ iter = coll.Find(nil).Limit(math.MinInt32).Iter()
1513+ for iter.Next(&discard) {
1514+ nresults++
1515+ }
1516+ if nresults < total/2 || nresults >= total {
1517+ c.Fatalf("Bad result size with MinInt32 limit: %d", nresults)
1518+ }
1519+}
1520+
1521+func (s *S) TestFindIterLimitWithBatch(c *C) {
1522+ session, err := mgo.Dial("localhost:40001")
1523+ c.Assert(err, IsNil)
1524+ defer session.Close()
1525+
1526+ coll := session.DB("mydb").C("mycoll")
1527+
1528+ ns := []int{40, 41, 42, 43, 44, 45, 46}
1529+ for _, n := range ns {
1530+ coll.Insert(M{"n": n})
1531+ }
1532+
1533+ // Ping the database to ensure the nonce has been received already.
1534+ c.Assert(session.Ping(), IsNil)
1535+
1536+ session.Refresh() // Release socket.
1537+
1538+ mgo.ResetStats()
1539+
1540+ query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Limit(3).Batch(2)
1541+ iter := query.Iter()
1542+ result := struct{ N int }{}
1543+ for i := 2; i < 5; i++ {
1544+ ok := iter.Next(&result)
1545+ c.Assert(ok, Equals, true)
1546+ c.Assert(result.N, Equals, ns[i])
1547+ if i == 3 {
1548+ stats := mgo.GetStats()
1549+ c.Assert(stats.ReceivedDocs, Equals, 2)
1550+ }
1551+ }
1552+
1553+ ok := iter.Next(&result)
1554+ c.Assert(ok, Equals, false)
1555+ c.Assert(iter.Close(), IsNil)
1556+
1557+ session.Refresh() // Release socket.
1558+
1559+ stats := mgo.GetStats()
1560+ c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 1*GET_MORE_OP + 1*KILL_CURSORS_OP
1561+ c.Assert(stats.ReceivedOps, Equals, 2) // and its REPLY_OPs
1562+ c.Assert(stats.ReceivedDocs, Equals, 3)
1563+ c.Assert(stats.SocketsInUse, Equals, 0)
1564+}
1565+
1566+func (s *S) TestFindIterSortWithBatch(c *C) {
1567+ session, err := mgo.Dial("localhost:40001")
1568+ c.Assert(err, IsNil)
1569+ defer session.Close()
1570+
1571+ coll := session.DB("mydb").C("mycoll")
1572+
1573+ ns := []int{40, 41, 42, 43, 44, 45, 46}
1574+ for _, n := range ns {
1575+ coll.Insert(M{"n": n})
1576+ }
1577+
1578+ // Without this, the logic above breaks because Mongo refuses to
1579+ // return a cursor with an in-memory sort.
1580+ coll.EnsureIndexKey("n")
1581+
1582+ // Ping the database to ensure the nonce has been received already.
1583+ c.Assert(session.Ping(), IsNil)
1584+
1585+ session.Refresh() // Release socket.
1586+
1587+ mgo.ResetStats()
1588+
1589+ query := coll.Find(M{"n": M{"$lte": 44}}).Sort("-n").Batch(2)
1590+ iter := query.Iter()
1591+ ns = []int{46, 45, 44, 43, 42, 41, 40}
1592+ result := struct{ N int }{}
1593+ for i := 2; i < len(ns); i++ {
1594+ c.Logf("i=%d", i)
1595+ ok := iter.Next(&result)
1596+ c.Assert(ok, Equals, true)
1597+ c.Assert(result.N, Equals, ns[i])
1598+ if i == 3 {
1599+ stats := mgo.GetStats()
1600+ c.Assert(stats.ReceivedDocs, Equals, 2)
1601+ }
1602+ }
1603+
1604+ ok := iter.Next(&result)
1605+ c.Assert(ok, Equals, false)
1606+ c.Assert(iter.Close(), IsNil)
1607+
1608+ session.Refresh() // Release socket.
1609+
1610+ stats := mgo.GetStats()
1611+ c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 2*GET_MORE_OP
1612+ c.Assert(stats.ReceivedOps, Equals, 3) // and its REPLY_OPs
1613+ c.Assert(stats.ReceivedDocs, Equals, 5)
1614+ c.Assert(stats.SocketsInUse, Equals, 0)
1615+}
1616+
1617+// Test tailable cursors in a situation where Next has to sleep to
1618+// respect the timeout requested on Tail.
1619+func (s *S) TestFindTailTimeoutWithSleep(c *C) {
1620+ if *fast {
1621+ c.Skip("-fast")
1622+ }
1623+
1624+ session, err := mgo.Dial("localhost:40001")
1625+ c.Assert(err, IsNil)
1626+ defer session.Close()
1627+
1628+ cresult := struct{ ErrMsg string }{}
1629+
1630+ db := session.DB("mydb")
1631+ err = db.Run(bson.D{{"create", "mycoll"}, {"capped", true}, {"size", 1024}}, &cresult)
1632+ c.Assert(err, IsNil)
1633+ c.Assert(cresult.ErrMsg, Equals, "")
1634+ coll := db.C("mycoll")
1635+
1636+ ns := []int{40, 41, 42, 43, 44, 45, 46}
1637+ for _, n := range ns {
1638+ coll.Insert(M{"n": n})
1639+ }
1640+
1641+ session.Refresh() // Release socket.
1642+
1643+ mgo.ResetStats()
1644+
1645+ timeout := 3 * time.Second
1646+
1647+ query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2)
1648+ iter := query.Tail(timeout)
1649+
1650+ n := len(ns)
1651+ result := struct{ N int }{}
1652+ for i := 2; i != n; i++ {
1653+ ok := iter.Next(&result)
1654+ c.Assert(ok, Equals, true)
1655+ c.Assert(iter.Err(), IsNil)
1656+ c.Assert(iter.Timeout(), Equals, false)
1657+ c.Assert(result.N, Equals, ns[i])
1658+ if i == 3 { // The batch boundary.
1659+ stats := mgo.GetStats()
1660+ c.Assert(stats.ReceivedDocs, Equals, 2)
1661+ }
1662+ }
1663+
1664+ mgo.ResetStats()
1665+
1666+ // The following call to Next will block.
1667+ go func() {
1668+ // The internal AwaitData timing of MongoDB is around 2 seconds,
1669+ // so this should force mgo to sleep at least once by itself to
1670+ // respect the requested timeout.
1671+ time.Sleep(timeout + 5e8*time.Nanosecond)
1672+ session := session.New()
1673+ defer session.Close()
1674+ coll := session.DB("mydb").C("mycoll")
1675+ coll.Insert(M{"n": 47})
1676+ }()
1677+
1678+ c.Log("Will wait for Next with N=47...")
1679+ ok := iter.Next(&result)
1680+ c.Assert(ok, Equals, true)
1681+ c.Assert(iter.Err(), IsNil)
1682+ c.Assert(iter.Timeout(), Equals, false)
1683+ c.Assert(result.N, Equals, 47)
1684+ c.Log("Got Next with N=47!")
1685+
1686+ // The following may break because it depends a bit on the internal
1687+ // timing used by MongoDB's AwaitData logic. If it does, the problem
1688+ // will be observed as more GET_MORE_OPs than predicted:
1689+ // 1*QUERY for nonce + 1*GET_MORE_OP on Next + 1*GET_MORE_OP on Next after sleep +
1690+ // 1*INSERT_OP + 1*QUERY_OP for getLastError on insert of 47
1691+ stats := mgo.GetStats()
1692+ if s.versionAtLeast(3, 0) { // TODO Will be 2.6 when write commands are enabled for it.
1693+ c.Assert(stats.SentOps, Equals, 4)
1694+ } else {
1695+ c.Assert(stats.SentOps, Equals, 5)
1696+ }
1697+ c.Assert(stats.ReceivedOps, Equals, 4) // REPLY_OPs for 1*QUERY_OP for nonce + 2*GET_MORE_OPs + 1*QUERY_OP
1698+ c.Assert(stats.ReceivedDocs, Equals, 3) // nonce + N=47 result + getLastError response
1699+
1700+ c.Log("Will wait for a result which will never come...")
1701+
1702+ started := time.Now()
1703+ ok = iter.Next(&result)
1704+ c.Assert(ok, Equals, false)
1705+ c.Assert(iter.Err(), IsNil)
1706+ c.Assert(iter.Timeout(), Equals, true)
1707+ c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true)
1708+
1709+ c.Log("Will now reuse the timed out tail cursor...")
1710+
1711+ coll.Insert(M{"n": 48})
1712+ ok = iter.Next(&result)
1713+ c.Assert(ok, Equals, true)
1714+ c.Assert(iter.Close(), IsNil)
1715+ c.Assert(iter.Timeout(), Equals, false)
1716+ c.Assert(result.N, Equals, 48)
1717+}
1718+
1719+// Test tailable cursors in a situation where Next never gets to sleep once
1720+// to respect the timeout requested on Tail.
1721+func (s *S) TestFindTailTimeoutNoSleep(c *C) {
1722+ session, err := mgo.Dial("localhost:40001")
1723+ c.Assert(err, IsNil)
1724+ defer session.Close()
1725+
1726+ cresult := struct{ ErrMsg string }{}
1727+
1728+ db := session.DB("mydb")
1729+ err = db.Run(bson.D{{"create", "mycoll"}, {"capped", true}, {"size", 1024}}, &cresult)
1730+ c.Assert(err, IsNil)
1731+ c.Assert(cresult.ErrMsg, Equals, "")
1732+ coll := db.C("mycoll")
1733+
1734+ ns := []int{40, 41, 42, 43, 44, 45, 46}
1735+ for _, n := range ns {
1736+ coll.Insert(M{"n": n})
1737+ }
1738+
1739+ session.Refresh() // Release socket.
1740+
1741+ mgo.ResetStats()
1742+
1743+ timeout := 1 * time.Second
1744+
1745+ query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2)
1746+ iter := query.Tail(timeout)
1747+
1748+ n := len(ns)
1749+ result := struct{ N int }{}
1750+ for i := 2; i != n; i++ {
1751+ ok := iter.Next(&result)
1752+ c.Assert(ok, Equals, true)
1753+ c.Assert(iter.Err(), IsNil)
1754+ c.Assert(iter.Timeout(), Equals, false)
1755+ c.Assert(result.N, Equals, ns[i])
1756+ if i == 3 { // The batch boundary.
1757+ stats := mgo.GetStats()
1758+ c.Assert(stats.ReceivedDocs, Equals, 2)
1759+ }
1760+ }
1761+
1762+ mgo.ResetStats()
1763+
1764+ // The following call to Next will block.
1765+ go func() {
1766+ // The internal AwaitData timing of MongoDB is around 2 seconds,
1767+ // so this item should arrive within the AwaitData threshold.
1768+ time.Sleep(5e8)
1769+ session := session.New()
1770+ defer session.Close()
1771+ coll := session.DB("mydb").C("mycoll")
1772+ coll.Insert(M{"n": 47})
1773+ }()
1774+
1775+ c.Log("Will wait for Next with N=47...")
1776+ ok := iter.Next(&result)
1777+ c.Assert(ok, Equals, true)
1778+ c.Assert(iter.Err(), IsNil)
1779+ c.Assert(iter.Timeout(), Equals, false)
1780+ c.Assert(result.N, Equals, 47)
1781+ c.Log("Got Next with N=47!")
1782+
1783+ // The following may break because it depends a bit on the internal
1784+ // timing used by MongoDB's AwaitData logic. If it does, the problem
1785+ // will be observed as more GET_MORE_OPs than predicted:
1786+ // 1*QUERY_OP for nonce + 1*GET_MORE_OP on Next +
1787+ // 1*INSERT_OP + 1*QUERY_OP for getLastError on insert of 47
1788+ stats := mgo.GetStats()
1789+ if s.versionAtLeast(3, 0) { // TODO Will be 2.6 when write commands are enabled for it.
1790+ c.Assert(stats.SentOps, Equals, 3)
1791+ } else {
1792+ c.Assert(stats.SentOps, Equals, 4)
1793+ }
1794+ c.Assert(stats.ReceivedOps, Equals, 3) // REPLY_OPs for 1*QUERY_OP for nonce + 1*GET_MORE_OPs and 1*QUERY_OP
1795+ c.Assert(stats.ReceivedDocs, Equals, 3) // nonce + N=47 result + getLastError response
1796+
1797+ c.Log("Will wait for a result which will never come...")
1798+
1799+ started := time.Now()
1800+ ok = iter.Next(&result)
1801+ c.Assert(ok, Equals, false)
1802+ c.Assert(iter.Err(), IsNil)
1803+ c.Assert(iter.Timeout(), Equals, true)
1804+ c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true)
1805+
1806+ c.Log("Will now reuse the timed out tail cursor...")
1807+
1808+ coll.Insert(M{"n": 48})
1809+ ok = iter.Next(&result)
1810+ c.Assert(ok, Equals, true)
1811+ c.Assert(iter.Close(), IsNil)
1812+ c.Assert(iter.Timeout(), Equals, false)
1813+ c.Assert(result.N, Equals, 48)
1814+}
1815+
1816+// Test tailable cursors in a situation where Next never gets to sleep once
1817+// to respect the timeout requested on Tail.
1818+func (s *S) TestFindTailNoTimeout(c *C) {
1819+ if *fast {
1820+ c.Skip("-fast")
1821+ }
1822+
1823+ session, err := mgo.Dial("localhost:40001")
1824+ c.Assert(err, IsNil)
1825+ defer session.Close()
1826+
1827+ cresult := struct{ ErrMsg string }{}
1828+
1829+ db := session.DB("mydb")
1830+ err = db.Run(bson.D{{"create", "mycoll"}, {"capped", true}, {"size", 1024}}, &cresult)
1831+ c.Assert(err, IsNil)
1832+ c.Assert(cresult.ErrMsg, Equals, "")
1833+ coll := db.C("mycoll")
1834+
1835+ ns := []int{40, 41, 42, 43, 44, 45, 46}
1836+ for _, n := range ns {
1837+ coll.Insert(M{"n": n})
1838+ }
1839+
1840+ session.Refresh() // Release socket.
1841+
1842+ mgo.ResetStats()
1843+
1844+ query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2)
1845+ iter := query.Tail(-1)
1846+ c.Assert(err, IsNil)
1847+
1848+ n := len(ns)
1849+ result := struct{ N int }{}
1850+ for i := 2; i != n; i++ {
1851+ ok := iter.Next(&result)
1852+ c.Assert(ok, Equals, true)
1853+ c.Assert(result.N, Equals, ns[i])
1854+ if i == 3 { // The batch boundary.
1855+ stats := mgo.GetStats()
1856+ c.Assert(stats.ReceivedDocs, Equals, 2)
1857+ }
1858+ }
1859+
1860+ mgo.ResetStats()
1861+
1862+ // The following call to Next will block.
1863+ go func() {
1864+ time.Sleep(5e8)
1865+ session := session.New()
1866+ defer session.Close()
1867+ coll := session.DB("mydb").C("mycoll")
1868+ coll.Insert(M{"n": 47})
1869+ }()
1870+
1871+ c.Log("Will wait for Next with N=47...")
1872+ ok := iter.Next(&result)
1873+ c.Assert(ok, Equals, true)
1874+ c.Assert(iter.Err(), IsNil)
1875+ c.Assert(iter.Timeout(), Equals, false)
1876+ c.Assert(result.N, Equals, 47)
1877+ c.Log("Got Next with N=47!")
1878+
1879+ // The following may break because it depends a bit on the internal
1880+ // timing used by MongoDB's AwaitData logic. If it does, the problem
1881+ // will be observed as more GET_MORE_OPs than predicted:
1882+ // 1*QUERY_OP for nonce + 1*GET_MORE_OP on Next +
1883+ // 1*INSERT_OP + 1*QUERY_OP for getLastError on insert of 47
1884+ stats := mgo.GetStats()
1885+ if s.versionAtLeast(3, 0) { // TODO Will be 2.6 when write commands are enabled for it.
1886+ c.Assert(stats.SentOps, Equals, 3)
1887+ } else {
1888+ c.Assert(stats.SentOps, Equals, 4)
1889+ }
1890+ c.Assert(stats.ReceivedOps, Equals, 3) // REPLY_OPs for 1*QUERY_OP for nonce + 1*GET_MORE_OPs and 1*QUERY_OP
1891+ c.Assert(stats.ReceivedDocs, Equals, 3) // nonce + N=47 result + getLastError response
1892+
1893+ c.Log("Will wait for a result which will never come...")
1894+
1895+ gotNext := make(chan bool)
1896+ go func() {
1897+ ok := iter.Next(&result)
1898+ gotNext <- ok
1899+ }()
1900+
1901+ select {
1902+ case ok := <-gotNext:
1903+ c.Fatalf("Next returned: %v", ok)
1904+ case <-time.After(3e9):
1905+ // Good. Should still be sleeping at that point.
1906+ }
1907+
1908+ // Closing the session should cause Next to return.
1909+ session.Close()
1910+
1911+ select {
1912+ case ok := <-gotNext:
1913+ c.Assert(ok, Equals, false)
1914+ c.Assert(iter.Err(), ErrorMatches, "Closed explicitly")
1915+ c.Assert(iter.Timeout(), Equals, false)
1916+ case <-time.After(1e9):
1917+ c.Fatal("Closing the session did not unblock Next")
1918+ }
1919+}
1920+
1921+func (s *S) TestIterNextResetsResult(c *C) {
1922+ session, err := mgo.Dial("localhost:40001")
1923+ c.Assert(err, IsNil)
1924+ defer session.Close()
1925+
1926+ coll := session.DB("mydb").C("mycoll")
1927+
1928+ ns := []int{1, 2, 3}
1929+ for _, n := range ns {
1930+ coll.Insert(M{"n" + strconv.Itoa(n): n})
1931+ }
1932+
1933+ query := coll.Find(nil).Sort("$natural")
1934+
1935+ i := 0
1936+ var sresult *struct{ N1, N2, N3 int }
1937+ iter := query.Iter()
1938+ for iter.Next(&sresult) {
1939+ switch i {
1940+ case 0:
1941+ c.Assert(sresult.N1, Equals, 1)
1942+ c.Assert(sresult.N2+sresult.N3, Equals, 0)
1943+ case 1:
1944+ c.Assert(sresult.N2, Equals, 2)
1945+ c.Assert(sresult.N1+sresult.N3, Equals, 0)
1946+ case 2:
1947+ c.Assert(sresult.N3, Equals, 3)
1948+ c.Assert(sresult.N1+sresult.N2, Equals, 0)
1949+ }
1950+ i++
1951+ }
1952+ c.Assert(iter.Close(), IsNil)
1953+
1954+ i = 0
1955+ var mresult M
1956+ iter = query.Iter()
1957+ for iter.Next(&mresult) {
1958+ delete(mresult, "_id")
1959+ switch i {
1960+ case 0:
1961+ c.Assert(mresult, DeepEquals, M{"n1": 1})
1962+ case 1:
1963+ c.Assert(mresult, DeepEquals, M{"n2": 2})
1964+ case 2:
1965+ c.Assert(mresult, DeepEquals, M{"n3": 3})
1966+ }
1967+ i++
1968+ }
1969+ c.Assert(iter.Close(), IsNil)
1970+
1971+ i = 0
1972+ var iresult interface{}
1973+ iter = query.Iter()
1974+ for iter.Next(&iresult) {
1975+ mresult, ok := iresult.(bson.M)
1976+ c.Assert(ok, Equals, true, Commentf("%#v", iresult))
1977+ delete(mresult, "_id")
1978+ switch i {
1979+ case 0:
1980+ c.Assert(mresult, DeepEquals, bson.M{"n1": 1})
1981+ case 1:
1982+ c.Assert(mresult, DeepEquals, bson.M{"n2": 2})
1983+ case 2:
1984+ c.Assert(mresult, DeepEquals, bson.M{"n3": 3})
1985+ }
1986+ i++
1987+ }
1988+ c.Assert(iter.Close(), IsNil)
1989+}
1990+
1991+func (s *S) TestFindForOnIter(c *C) {
1992+ session, err := mgo.Dial("localhost:40001")
1993+ c.Assert(err, IsNil)
1994+ defer session.Close()
1995+
1996+ coll := session.DB("mydb").C("mycoll")
1997+
1998+ ns := []int{40, 41, 42, 43, 44, 45, 46}
1999+ for _, n := range ns {
2000+ coll.Insert(M{"n": n})
2001+ }
2002+
2003+ session.Refresh() // Release socket.
2004+
2005+ mgo.ResetStats()
2006+
2007+ query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2)
2008+ iter := query.Iter()
2009+
2010+ i := 2
2011+ var result *struct{ N int }
2012+ err = iter.For(&result, func() error {
2013+ c.Assert(i < 7, Equals, true)
2014+ c.Assert(result.N, Equals, ns[i])
2015+ if i == 1 {
2016+ stats := mgo.GetStats()
2017+ c.Assert(stats.ReceivedDocs, Equals, 2)
2018+ }
2019+ i++
2020+ return nil
2021+ })
2022+ c.Assert(err, IsNil)
2023+
2024+ session.Refresh() // Release socket.
2025+
2026+ stats := mgo.GetStats()
2027+ c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 2*GET_MORE_OP
2028+ c.Assert(stats.ReceivedOps, Equals, 3) // and their REPLY_OPs.
2029+ c.Assert(stats.ReceivedDocs, Equals, 5)
2030+ c.Assert(stats.SocketsInUse, Equals, 0)
2031+}
2032+
2033+func (s *S) TestFindFor(c *C) {
2034+ session, err := mgo.Dial("localhost:40001")
2035+ c.Assert(err, IsNil)
2036+ defer session.Close()
2037+
2038+ coll := session.DB("mydb").C("mycoll")
2039+
2040+ ns := []int{40, 41, 42, 43, 44, 45, 46}
2041+ for _, n := range ns {
2042+ coll.Insert(M{"n": n})
2043+ }
2044+
2045+ session.Refresh() // Release socket.
2046+
2047+ mgo.ResetStats()
2048+
2049+ query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2)
2050+
2051+ i := 2
2052+ var result *struct{ N int }
2053+ err = query.For(&result, func() error {
2054+ c.Assert(i < 7, Equals, true)
2055+ c.Assert(result.N, Equals, ns[i])
2056+ if i == 1 {
2057+ stats := mgo.GetStats()
2058+ c.Assert(stats.ReceivedDocs, Equals, 2)
2059+ }
2060+ i++
2061+ return nil
2062+ })
2063+ c.Assert(err, IsNil)
2064+
2065+ session.Refresh() // Release socket.
2066+
2067+ stats := mgo.GetStats()
2068+ c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 2*GET_MORE_OP
2069+ c.Assert(stats.ReceivedOps, Equals, 3) // and their REPLY_OPs.
2070+ c.Assert(stats.ReceivedDocs, Equals, 5)
2071+ c.Assert(stats.SocketsInUse, Equals, 0)
2072+}
2073+
2074+func (s *S) TestFindForStopOnError(c *C) {
2075+ session, err := mgo.Dial("localhost:40001")
2076+ c.Assert(err, IsNil)
2077+ defer session.Close()
2078+
2079+ coll := session.DB("mydb").C("mycoll")
2080+
2081+ ns := []int{40, 41, 42, 43, 44, 45, 46}
2082+ for _, n := range ns {
2083+ coll.Insert(M{"n": n})
2084+ }
2085+
2086+ query := coll.Find(M{"n": M{"$gte": 42}})
2087+ i := 2
2088+ var result *struct{ N int }
2089+ err = query.For(&result, func() error {
2090+ c.Assert(i < 4, Equals, true)
2091+ c.Assert(result.N, Equals, ns[i])
2092+ if i == 3 {
2093+ return fmt.Errorf("stop!")
2094+ }
2095+ i++
2096+ return nil
2097+ })
2098+ c.Assert(err, ErrorMatches, "stop!")
2099+}
2100+
2101+func (s *S) TestFindForResetsResult(c *C) {
2102+ session, err := mgo.Dial("localhost:40001")
2103+ c.Assert(err, IsNil)
2104+ defer session.Close()
2105+
2106+ coll := session.DB("mydb").C("mycoll")
2107+
2108+ ns := []int{1, 2, 3}
2109+ for _, n := range ns {
2110+ coll.Insert(M{"n" + strconv.Itoa(n): n})
2111+ }
2112+
2113+ query := coll.Find(nil).Sort("$natural")
2114+
2115+ i := 0
2116+ var sresult *struct{ N1, N2, N3 int }
2117+ err = query.For(&sresult, func() error {
2118+ switch i {
2119+ case 0:
2120+ c.Assert(sresult.N1, Equals, 1)
2121+ c.Assert(sresult.N2+sresult.N3, Equals, 0)
2122+ case 1:
2123+ c.Assert(sresult.N2, Equals, 2)
2124+ c.Assert(sresult.N1+sresult.N3, Equals, 0)
2125+ case 2:
2126+ c.Assert(sresult.N3, Equals, 3)
2127+ c.Assert(sresult.N1+sresult.N2, Equals, 0)
2128+ }
2129+ i++
2130+ return nil
2131+ })
2132+ c.Assert(err, IsNil)
2133+
2134+ i = 0
2135+ var mresult M
2136+ err = query.For(&mresult, func() error {
2137+ delete(mresult, "_id")
2138+ switch i {
2139+ case 0:
2140+ c.Assert(mresult, DeepEquals, M{"n1": 1})
2141+ case 1:
2142+ c.Assert(mresult, DeepEquals, M{"n2": 2})
2143+ case 2:
2144+ c.Assert(mresult, DeepEquals, M{"n3": 3})
2145+ }
2146+ i++
2147+ return nil
2148+ })
2149+ c.Assert(err, IsNil)
2150+
2151+ i = 0
2152+ var iresult interface{}
2153+ err = query.For(&iresult, func() error {
2154+ mresult, ok := iresult.(bson.M)
2155+ c.Assert(ok, Equals, true, Commentf("%#v", iresult))
2156+ delete(mresult, "_id")
2157+ switch i {
2158+ case 0:
2159+ c.Assert(mresult, DeepEquals, bson.M{"n1": 1})
2160+ case 1:
2161+ c.Assert(mresult, DeepEquals, bson.M{"n2": 2})
2162+ case 2:
2163+ c.Assert(mresult, DeepEquals, bson.M{"n3": 3})
2164+ }
2165+ i++
2166+ return nil
2167+ })
2168+ c.Assert(err, IsNil)
2169+}
2170+
2171+func (s *S) TestFindIterSnapshot(c *C) {
2172+ session, err := mgo.Dial("localhost:40001")
2173+ c.Assert(err, IsNil)
2174+ defer session.Close()
2175+
2176+ // Insane amounts of logging otherwise due to the
2177+ // amount of data being shuffled.
2178+ mgo.SetDebug(false)
2179+ defer mgo.SetDebug(true)
2180+
2181+ coll := session.DB("mydb").C("mycoll")
2182+
2183+ var a [1024000]byte
2184+
2185+ for n := 0; n < 10; n++ {
2186+ err := coll.Insert(M{"_id": n, "n": n, "a1": &a})
2187+ c.Assert(err, IsNil)
2188+ }
2189+
2190+ query := coll.Find(M{"n": M{"$gt": -1}}).Batch(2).Prefetch(0)
2191+ query.Snapshot()
2192+ iter := query.Iter()
2193+
2194+ seen := map[int]bool{}
2195+ result := struct {
2196+ Id int "_id"
2197+ }{}
2198+ for iter.Next(&result) {
2199+ if len(seen) == 2 {
2200+ // Grow all entries so that they have to move.
2201+ // Backwards so that the order is inverted.
2202+ for n := 10; n >= 0; n-- {
2203+ _, err := coll.Upsert(M{"_id": n}, M{"$set": M{"a2": &a}})
2204+ c.Assert(err, IsNil)
2205+ }
2206+ }
2207+ if seen[result.Id] {
2208+ c.Fatalf("seen duplicated key: %d", result.Id)
2209+ }
2210+ seen[result.Id] = true
2211+ }
2212+ c.Assert(iter.Close(), IsNil)
2213+}
2214+
2215+func (s *S) TestSort(c *C) {
2216+ session, err := mgo.Dial("localhost:40001")
2217+ c.Assert(err, IsNil)
2218+ defer session.Close()
2219+
2220+ coll := session.DB("mydb").C("mycoll")
2221+
2222+ coll.Insert(M{"a": 1, "b": 1})
2223+ coll.Insert(M{"a": 2, "b": 2})
2224+ coll.Insert(M{"a": 2, "b": 1})
2225+ coll.Insert(M{"a": 0, "b": 1})
2226+ coll.Insert(M{"a": 2, "b": 0})
2227+ coll.Insert(M{"a": 0, "b": 2})
2228+ coll.Insert(M{"a": 1, "b": 2})
2229+ coll.Insert(M{"a": 0, "b": 0})
2230+ coll.Insert(M{"a": 1, "b": 0})
2231+
2232+ query := coll.Find(M{})
2233+ query.Sort("-a") // Should be ignored.
2234+ query.Sort("-b", "a")
2235+ iter := query.Iter()
2236+
2237+ l := make([]int, 18)
2238+ r := struct{ A, B int }{}
2239+ for i := 0; i != len(l); i += 2 {
2240+ ok := iter.Next(&r)
2241+ c.Assert(ok, Equals, true)
2242+ c.Assert(err, IsNil)
2243+ l[i] = r.A
2244+ l[i+1] = r.B
2245+ }
2246+
2247+ c.Assert(l, DeepEquals, []int{0, 2, 1, 2, 2, 2, 0, 1, 1, 1, 2, 1, 0, 0, 1, 0, 2, 0})
2248+}
2249+
2250+func (s *S) TestSortWithBadArgs(c *C) {
2251+ session, err := mgo.Dial("localhost:40001")
2252+ c.Assert(err, IsNil)
2253+ defer session.Close()
2254+
2255+ coll := session.DB("mydb").C("mycoll")
2256+
2257+ f1 := func() { coll.Find(nil).Sort("") }
2258+ f2 := func() { coll.Find(nil).Sort("+") }
2259+ f3 := func() { coll.Find(nil).Sort("foo", "-") }
2260+
2261+ for _, f := range []func(){f1, f2, f3} {
2262+ c.Assert(f, PanicMatches, "Sort: empty field name")
2263+ }
2264+}
2265+
2266+func (s *S) TestSortScoreText(c *C) {
2267+ session, err := mgo.Dial("localhost:40001")
2268+ c.Assert(err, IsNil)
2269+ defer session.Close()
2270+
2271+ coll := session.DB("mydb").C("mycoll")
2272+
2273+ err = coll.EnsureIndex(mgo.Index{
2274+ Key: []string{"$text:a", "$text:b"},
2275+ })
2276+ c.Assert(err, IsNil)
2277+
2278+ err = coll.Insert(M{
2279+ "a": "none",
2280+ "b": "twice: foo foo",
2281+ })
2282+ c.Assert(err, IsNil)
2283+ err = coll.Insert(M{
2284+ "a": "just once: foo",
2285+ "b": "none",
2286+ })
2287+ c.Assert(err, IsNil)
2288+ err = coll.Insert(M{
2289+ "a": "many: foo foo foo",
2290+ "b": "none",
2291+ })
2292+ c.Assert(err, IsNil)
2293+ err = coll.Insert(M{
2294+ "a": "none",
2295+ "b": "none",
2296+ "c": "ignore: foo",
2297+ })
2298+ c.Assert(err, IsNil)
2299+
2300+ query := coll.Find(M{"$text": M{"$search": "foo"}})
2301+ query.Select(M{"score": M{"$meta": "textScore"}})
2302+ query.Sort("$textScore:score")
2303+ iter := query.Iter()
2304+
2305+ var r struct{ A, B string }
2306+ var results []string
2307+ for iter.Next(&r) {
2308+ results = append(results, r.A, r.B)
2309+ }
2310+
2311+ c.Assert(results, DeepEquals, []string{
2312+ "many: foo foo foo", "none",
2313+ "none", "twice: foo foo",
2314+ "just once: foo", "none",
2315+ })
2316+}
2317+
2318+func (s *S) TestPrefetching(c *C) {
2319+ session, err := mgo.Dial("localhost:40001")
2320+ c.Assert(err, IsNil)
2321+ defer session.Close()
2322+
2323+ coll := session.DB("mydb").C("mycoll")
2324+
2325+ const total = 600
2326+ mgo.SetDebug(false)
2327+ docs := make([]interface{}, total)
2328+ for i := 0; i != total; i++ {
2329+ docs[i] = bson.D{{"n", i}}
2330+ }
2331+ err = coll.Insert(docs...)
2332+ c.Assert(err, IsNil)
2333+
2334+ for testi := 0; testi < 5; testi++ {
2335+ mgo.ResetStats()
2336+
2337+ var iter *mgo.Iter
2338+ var beforeMore int
2339+
2340+ switch testi {
2341+ case 0: // The default session value.
2342+ session.SetBatch(100)
2343+ iter = coll.Find(M{}).Iter()
2344+ beforeMore = 75
2345+
2346+ case 2: // Changing the session value.
2347+ session.SetBatch(100)
2348+ session.SetPrefetch(0.27)
2349+ iter = coll.Find(M{}).Iter()
2350+ beforeMore = 73
2351+
2352+ case 1: // Changing via query methods.
2353+ iter = coll.Find(M{}).Prefetch(0.27).Batch(100).Iter()
2354+ beforeMore = 73
2355+
2356+ case 3: // With prefetch on first document.
2357+ iter = coll.Find(M{}).Prefetch(1.0).Batch(100).Iter()
2358+ beforeMore = 0
2359+
2360+ case 4: // Without prefetch.
2361+ iter = coll.Find(M{}).Prefetch(0).Batch(100).Iter()
2362+ beforeMore = 100
2363+ }
2364+
2365+ pings := 0
2366+ for batchi := 0; batchi < len(docs)/100-1; batchi++ {
2367+ c.Logf("Iterating over %d documents on batch %d", beforeMore, batchi)
2368+ var result struct{ N int }
2369+ for i := 0; i < beforeMore; i++ {
2370+ ok := iter.Next(&result)
2371+ c.Assert(ok, Equals, true, Commentf("iter.Err: %v", iter.Err()))
2372+ }
2373+ beforeMore = 99
2374+ c.Logf("Done iterating.")
2375+
2376+ session.Run("ping", nil) // Roundtrip to settle down.
2377+ pings++
2378+
2379+ stats := mgo.GetStats()
2380+ c.Assert(stats.ReceivedDocs, Equals, (batchi+1)*100+pings)
2381+
2382+ c.Logf("Iterating over one more document on batch %d", batchi)
2383+ ok := iter.Next(&result)
2384+ c.Assert(ok, Equals, true, Commentf("iter.Err: %v", iter.Err()))
2385+ c.Logf("Done iterating.")
2386+
2387+ session.Run("ping", nil) // Roundtrip to settle down.
2388+ pings++
2389+
2390+ stats = mgo.GetStats()
2391+ c.Assert(stats.ReceivedDocs, Equals, (batchi+2)*100+pings)
2392+ }
2393+ }
2394+}
2395+
2396+func (s *S) TestSafeSetting(c *C) {
2397+ session, err := mgo.Dial("localhost:40001")
2398+ c.Assert(err, IsNil)
2399+ defer session.Close()
2400+
2401+ // Check the default
2402+ safe := session.Safe()
2403+ c.Assert(safe.W, Equals, 0)
2404+ c.Assert(safe.WMode, Equals, "")
2405+ c.Assert(safe.WTimeout, Equals, 0)
2406+ c.Assert(safe.FSync, Equals, false)
2407+ c.Assert(safe.J, Equals, false)
2408+
2409+ // Tweak it
2410+ session.SetSafe(&mgo.Safe{W: 1, WTimeout: 2, FSync: true})
2411+ safe = session.Safe()
2412+ c.Assert(safe.W, Equals, 1)
2413+ c.Assert(safe.WMode, Equals, "")
2414+ c.Assert(safe.WTimeout, Equals, 2)
2415+ c.Assert(safe.FSync, Equals, true)
2416+ c.Assert(safe.J, Equals, false)
2417+
2418+ // Reset it again.
2419+ session.SetSafe(&mgo.Safe{})
2420+ safe = session.Safe()
2421+ c.Assert(safe.W, Equals, 0)
2422+ c.Assert(safe.WMode, Equals, "")
2423+ c.Assert(safe.WTimeout, Equals, 0)
2424+ c.Assert(safe.FSync, Equals, false)
2425+ c.Assert(safe.J, Equals, false)
2426+
2427+ // Ensure safety to something more conservative.
2428+ session.SetSafe(&mgo.Safe{W: 5, WTimeout: 6, J: true})
2429+ safe = session.Safe()
2430+ c.Assert(safe.W, Equals, 5)
2431+ c.Assert(safe.WMode, Equals, "")
2432+ c.Assert(safe.WTimeout, Equals, 6)
2433+ c.Assert(safe.FSync, Equals, false)
2434+ c.Assert(safe.J, Equals, true)
2435+
2436+ // Ensure safety to something less conservative won't change it.
2437+ session.EnsureSafe(&mgo.Safe{W: 4, WTimeout: 7})
2438+ safe = session.Safe()
2439+ c.Assert(safe.W, Equals, 5)
2440+ c.Assert(safe.WMode, Equals, "")
2441+ c.Assert(safe.WTimeout, Equals, 6)
2442+ c.Assert(safe.FSync, Equals, false)
2443+ c.Assert(safe.J, Equals, true)
2444+
2445+ // But to something more conservative will.
2446+ session.EnsureSafe(&mgo.Safe{W: 6, WTimeout: 4, FSync: true})
2447+ safe = session.Safe()
2448+ c.Assert(safe.W, Equals, 6)
2449+ c.Assert(safe.WMode, Equals, "")
2450+ c.Assert(safe.WTimeout, Equals, 4)
2451+ c.Assert(safe.FSync, Equals, true)
2452+ c.Assert(safe.J, Equals, false)
2453+
2454+ // Even more conservative.
2455+ session.EnsureSafe(&mgo.Safe{WMode: "majority", WTimeout: 2})
2456+ safe = session.Safe()
2457+ c.Assert(safe.W, Equals, 0)
2458+ c.Assert(safe.WMode, Equals, "majority")
2459+ c.Assert(safe.WTimeout, Equals, 2)
2460+ c.Assert(safe.FSync, Equals, true)
2461+ c.Assert(safe.J, Equals, false)
2462+
2463+ // WMode always overrides, whatever it is, but J doesn't.
2464+ session.EnsureSafe(&mgo.Safe{WMode: "something", J: true})
2465+ safe = session.Safe()
2466+ c.Assert(safe.W, Equals, 0)
2467+ c.Assert(safe.WMode, Equals, "something")
2468+ c.Assert(safe.WTimeout, Equals, 2)
2469+ c.Assert(safe.FSync, Equals, true)
2470+ c.Assert(safe.J, Equals, false)
2471+
2472+ // EnsureSafe with nil does nothing.
2473+ session.EnsureSafe(nil)
2474+ safe = session.Safe()
2475+ c.Assert(safe.W, Equals, 0)
2476+ c.Assert(safe.WMode, Equals, "something")
2477+ c.Assert(safe.WTimeout, Equals, 2)
2478+ c.Assert(safe.FSync, Equals, true)
2479+ c.Assert(safe.J, Equals, false)
2480+
2481+ // Changing the safety of a cloned session doesn't touch the original.
2482+ clone := session.Clone()
2483+ defer clone.Close()
2484+ clone.EnsureSafe(&mgo.Safe{WMode: "foo"})
2485+ safe = session.Safe()
2486+ c.Assert(safe.WMode, Equals, "something")
2487+}
2488+
2489+func (s *S) TestSafeInsert(c *C) {
2490+ session, err := mgo.Dial("localhost:40001")
2491+ c.Assert(err, IsNil)
2492+ defer session.Close()
2493+
2494+ coll := session.DB("mydb").C("mycoll")
2495+
2496+ // Insert an element with a predefined key.
2497+ err = coll.Insert(M{"_id": 1})
2498+ c.Assert(err, IsNil)
2499+
2500+ mgo.ResetStats()
2501+
2502+ // Session should be safe by default, so inserting it again must fail.
2503+ err = coll.Insert(M{"_id": 1})
2504+ c.Assert(err, ErrorMatches, ".*E11000 duplicate.*")
2505+ c.Assert(err.(*mgo.LastError).Code, Equals, 11000)
2506+
2507+ // It must have sent two operations (INSERT_OP + getLastError QUERY_OP)
2508+ stats := mgo.GetStats()
2509+
2510+ // TODO Will be 2.6 when write commands are enabled for it.
2511+ if s.versionAtLeast(3, 0) {
2512+ c.Assert(stats.SentOps, Equals, 1)
2513+ } else {
2514+ c.Assert(stats.SentOps, Equals, 2)
2515+ }
2516+
2517+ mgo.ResetStats()
2518+
2519+ // If we disable safety, though, it won't complain.
2520+ session.SetSafe(nil)
2521+ err = coll.Insert(M{"_id": 1})
2522+ c.Assert(err, IsNil)
2523+
2524+ // Must have sent a single operation this time (just the INSERT_OP)
2525+ stats = mgo.GetStats()
2526+ c.Assert(stats.SentOps, Equals, 1)
2527+}
2528+
2529+func (s *S) TestSafeParameters(c *C) {
2530+ session, err := mgo.Dial("localhost:40011")
2531+ c.Assert(err, IsNil)
2532+ defer session.Close()
2533+
2534+ coll := session.DB("mydb").C("mycoll")
2535+
2536+ // Tweak the safety parameters to something unachievable.
2537+ session.SetSafe(&mgo.Safe{W: 4, WTimeout: 100})
2538+ err = coll.Insert(M{"_id": 1})
2539+ c.Assert(err, ErrorMatches, "timeout|timed out waiting for slaves|Not enough data-bearing nodes|waiting for replication timed out") // :-(
2540+ if !s.versionAtLeast(2, 6) {
2541+ // 2.6 turned it into a query error.
2542+ c.Assert(err.(*mgo.LastError).WTimeout, Equals, true)
2543+ }
2544+}
2545+
2546+func (s *S) TestQueryErrorOne(c *C) {
2547+ session, err := mgo.Dial("localhost:40001")
2548+ c.Assert(err, IsNil)
2549+ defer session.Close()
2550+
2551+ coll := session.DB("mydb").C("mycoll")
2552+
2553+ result := struct {
2554+ Err string "$err"
2555+ }{}
2556+
2557+ err = coll.Find(M{"a": 1}).Select(M{"a": M{"b": 1}}).One(&result)
2558+ c.Assert(err, ErrorMatches, ".*Unsupported projection option:.*")
2559+ c.Assert(err.(*mgo.QueryError).Message, Matches, ".*Unsupported projection option:.*")
2560+ if s.versionAtLeast(2, 6) {
2561+ // Oh, the dance of error codes. :-(
2562+ c.Assert(err.(*mgo.QueryError).Code, Equals, 17287)
2563+ } else {
2564+ c.Assert(err.(*mgo.QueryError).Code, Equals, 13097)
2565+ }
2566+
2567+ // The result should be properly unmarshalled with QueryError
2568+ c.Assert(result.Err, Matches, ".*Unsupported projection option:.*")
2569+}
2570+
2571+func (s *S) TestQueryErrorNext(c *C) {
2572+ session, err := mgo.Dial("localhost:40001")
2573+ c.Assert(err, IsNil)
2574+ defer session.Close()
2575+
2576+ coll := session.DB("mydb").C("mycoll")
2577+
2578+ result := struct {
2579+ Err string "$err"
2580+ }{}
2581+
2582+ iter := coll.Find(M{"a": 1}).Select(M{"a": M{"b": 1}}).Iter()
2583+
2584+ ok := iter.Next(&result)
2585+ c.Assert(ok, Equals, false)
2586+
2587+ err = iter.Close()
2588+ c.Assert(err, ErrorMatches, ".*Unsupported projection option:.*")
2589+ c.Assert(err.(*mgo.QueryError).Message, Matches, ".*Unsupported projection option:.*")
2590+ if s.versionAtLeast(2, 6) {
2591+ // Oh, the dance of error codes. :-(
2592+ c.Assert(err.(*mgo.QueryError).Code, Equals, 17287)
2593+ } else {
2594+ c.Assert(err.(*mgo.QueryError).Code, Equals, 13097)
2595+ }
2596+ c.Assert(iter.Err(), Equals, err)
2597+
2598+ // The result should be properly unmarshalled with QueryError
2599+ c.Assert(result.Err, Matches, ".*Unsupported projection option:.*")
2600+}
2601+
2602+var indexTests = []struct {
2603+ index mgo.Index
2604+ expected M
2605+}{{
2606+ mgo.Index{
2607+ Key: []string{"a"},
2608+ Background: true,
2609+ },
2610+ M{
2611+ "name": "a_1",
2612+ "key": M{"a": 1},
2613+ "ns": "mydb.mycoll",
2614+ "background": true,
2615+ },
2616+}, {
2617+ mgo.Index{
2618+ Key: []string{"a", "-b"},
2619+ Unique: true,
2620+ DropDups: true,
2621+ },
2622+ M{
2623+ "name": "a_1_b_-1",
2624+ "key": M{"a": 1, "b": -1},
2625+ "ns": "mydb.mycoll",
2626+ "unique": true,
2627+ "dropDups": true,
2628+ },
2629+}, {
2630+ mgo.Index{
2631+ Key: []string{"@loc_old"}, // Obsolete
2632+ Min: -500,
2633+ Max: 500,
2634+ Bits: 32,
2635+ },
2636+ M{
2637+ "name": "loc_old_2d",
2638+ "key": M{"loc_old": "2d"},
2639+ "ns": "mydb.mycoll",
2640+ "min": -500,
2641+ "max": 500,
2642+ "bits": 32,
2643+ },
2644+}, {
2645+ mgo.Index{
2646+ Key: []string{"$2d:loc"},
2647+ Min: -500,
2648+ Max: 500,
2649+ Bits: 32,
2650+ },
2651+ M{
2652+ "name": "loc_2d",
2653+ "key": M{"loc": "2d"},
2654+ "ns": "mydb.mycoll",
2655+ "min": -500,
2656+ "max": 500,
2657+ "bits": 32,
2658+ },
2659+}, {
2660+ mgo.Index{
2661+ Key: []string{"$geoHaystack:loc", "type"},
2662+ BucketSize: 1,
2663+ },
2664+ M{
2665+ "name": "loc_geoHaystack_type_1",
2666+ "key": M{"loc": "geoHaystack", "type": 1},
2667+ "ns": "mydb.mycoll",
2668+ "bucketSize": 1.0,
2669+ },
2670+}, {
2671+ mgo.Index{
2672+ Key: []string{"$text:a", "$text:b"},
2673+ Weights: map[string]int{"b": 42},
2674+ },
2675+ M{
2676+ "name": "a_text_b_text",
2677+ "key": M{"_fts": "text", "_ftsx": 1},
2678+ "ns": "mydb.mycoll",
2679+ "weights": M{"a": 1, "b": 42},
2680+ "default_language": "english",
2681+ "language_override": "language",
2682+ "textIndexVersion": 2,
2683+ },
2684+}, {
2685+ mgo.Index{
2686+ Key: []string{"$text:a"},
2687+ DefaultLanguage: "portuguese",
2688+ LanguageOverride: "idioma",
2689+ },
2690+ M{
2691+ "name": "a_text",
2692+ "key": M{"_fts": "text", "_ftsx": 1},
2693+ "ns": "mydb.mycoll",
2694+ "weights": M{"a": 1},
2695+ "default_language": "portuguese",
2696+ "language_override": "idioma",
2697+ "textIndexVersion": 2,
2698+ },
2699+}, {
2700+ mgo.Index{
2701+ Key: []string{"$text:$**"},
2702+ },
2703+ M{
2704+ "name": "$**_text",
2705+ "key": M{"_fts": "text", "_ftsx": 1},
2706+ "ns": "mydb.mycoll",
2707+ "weights": M{"$**": 1},
2708+ "default_language": "english",
2709+ "language_override": "language",
2710+ "textIndexVersion": 2,
2711+ },
2712+}}
2713+
2714+func (s *S) TestEnsureIndex(c *C) {
2715+ session, err := mgo.Dial("localhost:40001")
2716+ c.Assert(err, IsNil)
2717+ defer session.Close()
2718+
2719+ coll := session.DB("mydb").C("mycoll")
2720+ idxs := session.DB("mydb").C("system.indexes")
2721+
2722+ for _, test := range indexTests {
2723+ if !s.versionAtLeast(2, 4) && test.expected["weights"] != nil {
2724+ // No text indexes until 2.4.
2725+ continue
2726+ }
2727+
2728+ err = coll.EnsureIndex(test.index)
2729+ c.Assert(err, IsNil)
2730+
2731+ obtained := M{}
2732+ err = idxs.Find(M{"name": test.expected["name"]}).One(obtained)
2733+ c.Assert(err, IsNil)
2734+
2735+ delete(obtained, "v")
2736+
2737+ if s.versionAtLeast(2, 7) {
2738+ // Was deprecated in 2.6, and not being reported by 2.7+.
2739+ delete(test.expected, "dropDups")
2740+ }
2741+
2742+ c.Assert(obtained, DeepEquals, test.expected)
2743+
2744+ err = coll.DropIndex(test.index.Key...)
2745+ c.Assert(err, IsNil)
2746+ }
2747+}
2748+
2749+func (s *S) TestEnsureIndexWithBadInfo(c *C) {
2750+ session, err := mgo.Dial("localhost:40001")
2751+ c.Assert(err, IsNil)
2752+ defer session.Close()
2753+
2754+ coll := session.DB("mydb").C("mycoll")
2755+
2756+ err = coll.EnsureIndex(mgo.Index{})
2757+ c.Assert(err, ErrorMatches, "invalid index key:.*")
2758+
2759+ err = coll.EnsureIndex(mgo.Index{Key: []string{""}})
2760+ c.Assert(err, ErrorMatches, "invalid index key:.*")
2761+}
2762+
2763+func (s *S) TestEnsureIndexWithUnsafeSession(c *C) {
2764+ session, err := mgo.Dial("localhost:40001")
2765+ c.Assert(err, IsNil)
2766+ defer session.Close()
2767+
2768+ session.SetSafe(nil)
2769+
2770+ coll := session.DB("mydb").C("mycoll")
2771+
2772+ err = coll.Insert(M{"a": 1})
2773+ c.Assert(err, IsNil)
2774+
2775+ err = coll.Insert(M{"a": 1})
2776+ c.Assert(err, IsNil)
2777+
2778+ // Should fail since there are duplicated entries.
2779+ index := mgo.Index{
2780+ Key: []string{"a"},
2781+ Unique: true,
2782+ }
2783+
2784+ err = coll.EnsureIndex(index)
2785+ c.Assert(err, ErrorMatches, ".*duplicate key error.*")
2786+}
2787+
2788+func (s *S) TestEnsureIndexKey(c *C) {
2789+ session, err := mgo.Dial("localhost:40001")
2790+ c.Assert(err, IsNil)
2791+ defer session.Close()
2792+
2793+ coll := session.DB("mydb").C("mycoll")
2794+
2795+ err = coll.EnsureIndexKey("a")
2796+ c.Assert(err, IsNil)
2797+
2798+ err = coll.EnsureIndexKey("a", "-b")
2799+ c.Assert(err, IsNil)
2800+
2801+ sysidx := session.DB("mydb").C("system.indexes")
2802+
2803+ result1 := M{}
2804+ err = sysidx.Find(M{"name": "a_1"}).One(result1)
2805+ c.Assert(err, IsNil)
2806+
2807+ result2 := M{}
2808+ err = sysidx.Find(M{"name": "a_1_b_-1"}).One(result2)
2809+ c.Assert(err, IsNil)
2810+
2811+ delete(result1, "v")
2812+ expected1 := M{
2813+ "name": "a_1",
2814+ "key": M{"a": 1},
2815+ "ns": "mydb.mycoll",
2816+ }
2817+ c.Assert(result1, DeepEquals, expected1)
2818+
2819+ delete(result2, "v")
2820+ expected2 := M{
2821+ "name": "a_1_b_-1",
2822+ "key": M{"a": 1, "b": -1},
2823+ "ns": "mydb.mycoll",
2824+ }
2825+ c.Assert(result2, DeepEquals, expected2)
2826+}
2827+
2828+func (s *S) TestEnsureIndexDropIndex(c *C) {
2829+ session, err := mgo.Dial("localhost:40001")
2830+ c.Assert(err, IsNil)
2831+ defer session.Close()
2832+
2833+ coll := session.DB("mydb").C("mycoll")
2834+
2835+ err = coll.EnsureIndexKey("a")
2836+ c.Assert(err, IsNil)
2837+
2838+ err = coll.EnsureIndexKey("-b")
2839+ c.Assert(err, IsNil)
2840+
2841+ err = coll.DropIndex("-b")
2842+ c.Assert(err, IsNil)
2843+
2844+ sysidx := session.DB("mydb").C("system.indexes")
2845+ dummy := &struct{}{}
2846+
2847+ err = sysidx.Find(M{"name": "a_1"}).One(dummy)
2848+ c.Assert(err, IsNil)
2849+
2850+ err = sysidx.Find(M{"name": "b_1"}).One(dummy)
2851+ c.Assert(err, Equals, mgo.ErrNotFound)
2852+
2853+ err = coll.DropIndex("a")
2854+ c.Assert(err, IsNil)
2855+
2856+ err = sysidx.Find(M{"name": "a_1"}).One(dummy)
2857+ c.Assert(err, Equals, mgo.ErrNotFound)
2858+
2859+ err = coll.DropIndex("a")
2860+ c.Assert(err, ErrorMatches, "index not found.*")
2861+}
2862+
2863+func (s *S) TestEnsureIndexCaching(c *C) {
2864+ session, err := mgo.Dial("localhost:40001")
2865+ c.Assert(err, IsNil)
2866+ defer session.Close()
2867+
2868+ coll := session.DB("mydb").C("mycoll")
2869+
2870+ err = coll.EnsureIndexKey("a")
2871+ c.Assert(err, IsNil)
2872+
2873+ mgo.ResetStats()
2874+
2875+ // Second EnsureIndex should be cached and do nothing.
2876+ err = coll.EnsureIndexKey("a")
2877+ c.Assert(err, IsNil)
2878+
2879+ stats := mgo.GetStats()
2880+ c.Assert(stats.SentOps, Equals, 0)
2881+
2882+ // Resetting the cache should make it contact the server again.
2883+ session.ResetIndexCache()
2884+
2885+ err = coll.EnsureIndexKey("a")
2886+ c.Assert(err, IsNil)
2887+
2888+ stats = mgo.GetStats()
2889+ c.Assert(stats.SentOps > 0, Equals, true)
2890+
2891+ // Dropping the index should also drop the cached index key.
2892+ err = coll.DropIndex("a")
2893+ c.Assert(err, IsNil)
2894+
2895+ mgo.ResetStats()
2896+
2897+ err = coll.EnsureIndexKey("a")
2898+ c.Assert(err, IsNil)
2899+
2900+ stats = mgo.GetStats()
2901+ c.Assert(stats.SentOps > 0, Equals, true)
2902+}
2903+
2904+func (s *S) TestEnsureIndexGetIndexes(c *C) {
2905+ session, err := mgo.Dial("localhost:40001")
2906+ c.Assert(err, IsNil)
2907+ defer session.Close()
2908+
2909+ coll := session.DB("mydb").C("mycoll")
2910+
2911+ err = coll.EnsureIndexKey("-b")
2912+ c.Assert(err, IsNil)
2913+
2914+ err = coll.EnsureIndexKey("a")
2915+ c.Assert(err, IsNil)
2916+
2917+ // Obsolete.
2918+ err = coll.EnsureIndexKey("@c")
2919+ c.Assert(err, IsNil)
2920+
2921+ err = coll.EnsureIndexKey("$2d:d")
2922+ c.Assert(err, IsNil)
2923+
2924+ // Try to exercise cursor logic. 2.8.0-rc3 still ignores this.
2925+ session.SetBatch(2)
2926+
2927+ indexes, err := coll.Indexes()
2928+ c.Assert(err, IsNil)
2929+
2930+ c.Assert(indexes[0].Name, Equals, "_id_")
2931+ c.Assert(indexes[1].Name, Equals, "a_1")
2932+ c.Assert(indexes[1].Key, DeepEquals, []string{"a"})
2933+ c.Assert(indexes[2].Name, Equals, "b_-1")
2934+ c.Assert(indexes[2].Key, DeepEquals, []string{"-b"})
2935+ c.Assert(indexes[3].Name, Equals, "c_2d")
2936+ c.Assert(indexes[3].Key, DeepEquals, []string{"$2d:c"})
2937+ c.Assert(indexes[4].Name, Equals, "d_2d")
2938+ c.Assert(indexes[4].Key, DeepEquals, []string{"$2d:d"})
2939+}
2940+
2941+func (s *S) TestEnsureIndexEvalGetIndexes(c *C) {
2942+ session, err := mgo.Dial("localhost:40001")
2943+ c.Assert(err, IsNil)
2944+ defer session.Close()
2945+
2946+ coll := session.DB("mydb").C("mycoll")
2947+
2948+ err = session.Run(bson.D{{"eval", "db.getSiblingDB('mydb').mycoll.ensureIndex({b: -1})"}}, nil)
2949+ c.Assert(err, IsNil)
2950+ err = session.Run(bson.D{{"eval", "db.getSiblingDB('mydb').mycoll.ensureIndex({a: 1})"}}, nil)
2951+ c.Assert(err, IsNil)
2952+ err = session.Run(bson.D{{"eval", "db.getSiblingDB('mydb').mycoll.ensureIndex({c: -1, e: 1})"}}, nil)
2953+ c.Assert(err, IsNil)
2954+ err = session.Run(bson.D{{"eval", "db.getSiblingDB('mydb').mycoll.ensureIndex({d: '2d'})"}}, nil)
2955+ c.Assert(err, IsNil)
2956+
2957+ indexes, err := coll.Indexes()
2958+ c.Assert(err, IsNil)
2959+
2960+ c.Assert(indexes[0].Name, Equals, "_id_")
2961+ c.Assert(indexes[1].Name, Equals, "a_1")
2962+ c.Assert(indexes[1].Key, DeepEquals, []string{"a"})
2963+ c.Assert(indexes[2].Name, Equals, "b_-1")
2964+ c.Assert(indexes[2].Key, DeepEquals, []string{"-b"})
2965+ c.Assert(indexes[3].Name, Equals, "c_-1_e_1")
2966+ c.Assert(indexes[3].Key, DeepEquals, []string{"-c", "e"})
2967+ if s.versionAtLeast(2, 2) {
2968+ c.Assert(indexes[4].Name, Equals, "d_2d")
2969+ c.Assert(indexes[4].Key, DeepEquals, []string{"$2d:d"})
2970+ } else {
2971+ c.Assert(indexes[4].Name, Equals, "d_")
2972+ c.Assert(indexes[4].Key, DeepEquals, []string{"$2d:d"})
2973+ }
2974+}
2975+
2976+var testTTL = flag.Bool("test-ttl", false, "test TTL collections (may take 1 minute)")
2977+
2978+func (s *S) TestEnsureIndexExpireAfter(c *C) {
2979+ session, err := mgo.Dial("localhost:40001")
2980+ c.Assert(err, IsNil)
2981+ defer session.Close()
2982+
2983+ session.SetSafe(nil)
2984+
2985+ coll := session.DB("mydb").C("mycoll")
2986+
2987+ err = coll.Insert(M{"n": 1, "t": time.Now().Add(-120 * time.Second)})
2988+ c.Assert(err, IsNil)
2989+ err = coll.Insert(M{"n": 2, "t": time.Now()})
2990+ c.Assert(err, IsNil)
2991+
2992+ // Should fail since there are duplicated entries.
2993+ index := mgo.Index{
2994+ Key: []string{"t"},
2995+ ExpireAfter: 1 * time.Minute,
2996+ }
2997+
2998+ err = coll.EnsureIndex(index)
2999+ c.Assert(err, IsNil)
3000+
3001+ indexes, err := coll.Indexes()
3002+ c.Assert(err, IsNil)
3003+ c.Assert(indexes[1].Name, Equals, "t_1")
3004+ c.Assert(indexes[1].ExpireAfter, Equals, 1*time.Minute)
3005+
3006+ if *testTTL {
3007+ worked := false
3008+ stop := time.Now().Add(70 * time.Second)
3009+ for time.Now().Before(stop) {
3010+ n, err := coll.Count()
3011+ c.Assert(err, IsNil)
3012+ if n == 1 {
3013+ worked = true
3014+ break
3015+ }
3016+ c.Assert(n, Equals, 2)
3017+ c.Logf("Still has 2 entries...")
3018+ time.Sleep(1 * time.Second)
3019+ }
3020+ if !worked {
3021+ c.Fatalf("TTL index didn't work")
3022+ }
3023+ }
3024+}
3025+
3026+func (s *S) TestDistinct(c *C) {
3027+ session, err := mgo.Dial("localhost:40001")
3028+ c.Assert(err, IsNil)
3029+ defer session.Close()
3030+
3031+ coll := session.DB("mydb").C("mycoll")
3032+
3033+ for _, i := range []int{1, 4, 6, 2, 2, 3, 4} {
3034+ coll.Insert(M{"n": i})
3035+ }
3036+
3037+ var result []int
3038+ err = coll.Find(M{"n": M{"$gt": 2}}).Sort("n").Distinct("n", &result)
3039+
3040+ sort.IntSlice(result).Sort()
3041+ c.Assert(result, DeepEquals, []int{3, 4, 6})
3042+}
3043+
3044+func (s *S) TestMapReduce(c *C) {
3045+ session, err := mgo.Dial("localhost:40001")
3046+ c.Assert(err, IsNil)
3047+ defer session.Close()
3048+
3049+ coll := session.DB("mydb").C("mycoll")
3050+
3051+ for _, i := range []int{1, 4, 6, 2, 2, 3, 4} {
3052+ coll.Insert(M{"n": i})
3053+ }
3054+
3055+ job := &mgo.MapReduce{
3056+ Map: "function() { emit(this.n, 1); }",
3057+ Reduce: "function(key, values) { return Array.sum(values); }",
3058+ }
3059+ var result []struct {
3060+ Id int "_id"
3061+ Value int
3062+ }
3063+
3064+ info, err := coll.Find(M{"n": M{"$gt": 2}}).MapReduce(job, &result)
3065+ c.Assert(err, IsNil)
3066+ c.Assert(info.InputCount, Equals, 4)
3067+ c.Assert(info.EmitCount, Equals, 4)
3068+ c.Assert(info.OutputCount, Equals, 3)
3069+ c.Assert(info.VerboseTime, IsNil)
3070+
3071+ expected := map[int]int{3: 1, 4: 2, 6: 1}
3072+ for _, item := range result {
3073+ c.Logf("Item: %#v", &item)
3074+ c.Assert(item.Value, Equals, expected[item.Id])
3075+ expected[item.Id] = -1
3076+ }
3077+}
3078+
3079+func (s *S) TestMapReduceFinalize(c *C) {
3080+ session, err := mgo.Dial("localhost:40001")
3081+ c.Assert(err, IsNil)
3082+ defer session.Close()
3083+
3084+ coll := session.DB("mydb").C("mycoll")
3085+
3086+ for _, i := range []int{1, 4, 6, 2, 2, 3, 4} {
3087+ coll.Insert(M{"n": i})
3088+ }
3089+
3090+ job := &mgo.MapReduce{
3091+ Map: "function() { emit(this.n, 1) }",
3092+ Reduce: "function(key, values) { return Array.sum(values) }",
3093+ Finalize: "function(key, count) { return {count: count} }",
3094+ }
3095+ var result []struct {
3096+ Id int "_id"
3097+ Value struct{ Count int }
3098+ }
3099+ _, err = coll.Find(nil).MapReduce(job, &result)
3100+ c.Assert(err, IsNil)
3101+
3102+ expected := map[int]int{1: 1, 2: 2, 3: 1, 4: 2, 6: 1}
3103+ for _, item := range result {
3104+ c.Logf("Item: %#v", &item)
3105+ c.Assert(item.Value.Count, Equals, expected[item.Id])
3106+ expected[item.Id] = -1
3107+ }
3108+}
3109+
3110+func (s *S) TestMapReduceToCollection(c *C) {
3111+ session, err := mgo.Dial("localhost:40001")
3112+ c.Assert(err, IsNil)
3113+ defer session.Close()

591 more lines aren’t shown here.

vendor/gopkg.in/mgo.v2/socket.go+677−0

This diff is large: its 678 lines aren’t shown here.

vendor/gopkg.in/mgo.v2/stats.go+147−0

This diff is large: its 148 lines aren’t shown here.

vendor/gopkg.in/mgo.v2/suite_test.go+254−0

This diff is large: its 255 lines aren’t shown here.

vendor/gopkg.in/mgo.v2/syscall_test.go+15−0

This diff is large: its 16 lines aren’t shown here.

vendor/gopkg.in/mgo.v2/syscall_windows_test.go+11−0

This diff is large: its 12 lines aren’t shown here.

version_test.go+5−1

This diff is large: its 13 lines aren’t shown here.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t