mongo 1.3.0 → 1.12.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/{LICENSE.txt → LICENSE} +1 -1
  4. data/README.md +122 -271
  5. data/Rakefile +25 -209
  6. data/VERSION +1 -0
  7. data/bin/mongo_console +31 -9
  8. data/lib/mongo/bulk_write_collection_view.rb +387 -0
  9. data/lib/mongo/collection.rb +576 -269
  10. data/lib/mongo/collection_writer.rb +364 -0
  11. data/lib/mongo/connection/node.rb +249 -0
  12. data/lib/mongo/connection/pool.rb +340 -0
  13. data/lib/mongo/connection/pool_manager.rb +320 -0
  14. data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
  15. data/lib/mongo/connection/socket/socket_util.rb +37 -0
  16. data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
  17. data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
  18. data/lib/mongo/connection/socket/unix_socket.rb +39 -0
  19. data/lib/mongo/connection/socket.rb +18 -0
  20. data/lib/mongo/connection.rb +7 -875
  21. data/lib/mongo/cursor.rb +403 -117
  22. data/lib/mongo/db.rb +444 -243
  23. data/lib/mongo/exception.rb +145 -0
  24. data/lib/mongo/functional/authentication.rb +455 -0
  25. data/lib/mongo/functional/logging.rb +85 -0
  26. data/lib/mongo/functional/read_preference.rb +183 -0
  27. data/lib/mongo/functional/scram.rb +556 -0
  28. data/lib/mongo/functional/uri_parser.rb +409 -0
  29. data/lib/mongo/functional/write_concern.rb +66 -0
  30. data/lib/mongo/functional.rb +20 -0
  31. data/lib/mongo/gridfs/grid.rb +30 -24
  32. data/lib/mongo/gridfs/grid_ext.rb +6 -10
  33. data/lib/mongo/gridfs/grid_file_system.rb +38 -20
  34. data/lib/mongo/gridfs/grid_io.rb +84 -75
  35. data/lib/mongo/gridfs.rb +18 -0
  36. data/lib/mongo/legacy.rb +140 -0
  37. data/lib/mongo/mongo_client.rb +697 -0
  38. data/lib/mongo/mongo_replica_set_client.rb +535 -0
  39. data/lib/mongo/mongo_sharded_client.rb +159 -0
  40. data/lib/mongo/networking.rb +372 -0
  41. data/lib/mongo/{util → utils}/conversions.rb +29 -8
  42. data/lib/mongo/{util → utils}/core_ext.rb +28 -18
  43. data/lib/mongo/{util → utils}/server_version.rb +4 -6
  44. data/lib/mongo/{util → utils}/support.rb +29 -31
  45. data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
  46. data/lib/mongo/utils.rb +19 -0
  47. data/lib/mongo.rb +51 -50
  48. data/mongo.gemspec +29 -32
  49. data/test/functional/authentication_test.rb +39 -0
  50. data/test/functional/bulk_api_stress_test.rb +133 -0
  51. data/test/functional/bulk_write_collection_view_test.rb +1198 -0
  52. data/test/functional/client_test.rb +627 -0
  53. data/test/functional/collection_test.rb +2175 -0
  54. data/test/functional/collection_writer_test.rb +83 -0
  55. data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
  56. data/test/functional/cursor_fail_test.rb +57 -0
  57. data/test/functional/cursor_message_test.rb +56 -0
  58. data/test/functional/cursor_test.rb +683 -0
  59. data/test/functional/db_api_test.rb +835 -0
  60. data/test/functional/db_connection_test.rb +25 -0
  61. data/test/functional/db_test.rb +348 -0
  62. data/test/functional/grid_file_system_test.rb +285 -0
  63. data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
  64. data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
  65. data/test/functional/pool_test.rb +136 -0
  66. data/test/functional/safe_test.rb +98 -0
  67. data/test/functional/ssl_test.rb +29 -0
  68. data/test/functional/support_test.rb +62 -0
  69. data/test/functional/timeout_test.rb +60 -0
  70. data/test/functional/uri_test.rb +446 -0
  71. data/test/functional/write_concern_test.rb +118 -0
  72. data/test/helpers/general.rb +50 -0
  73. data/test/helpers/test_unit.rb +476 -0
  74. data/test/replica_set/authentication_test.rb +37 -0
  75. data/test/replica_set/basic_test.rb +189 -0
  76. data/test/replica_set/client_test.rb +393 -0
  77. data/test/replica_set/connection_test.rb +138 -0
  78. data/test/replica_set/count_test.rb +66 -0
  79. data/test/replica_set/cursor_test.rb +220 -0
  80. data/test/replica_set/insert_test.rb +157 -0
  81. data/test/replica_set/max_values_test.rb +151 -0
  82. data/test/replica_set/pinning_test.rb +105 -0
  83. data/test/replica_set/query_test.rb +73 -0
  84. data/test/replica_set/read_preference_test.rb +219 -0
  85. data/test/replica_set/refresh_test.rb +211 -0
  86. data/test/replica_set/replication_ack_test.rb +95 -0
  87. data/test/replica_set/ssl_test.rb +32 -0
  88. data/test/sharded_cluster/basic_test.rb +203 -0
  89. data/test/shared/authentication/basic_auth_shared.rb +260 -0
  90. data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
  91. data/test/shared/authentication/gssapi_shared.rb +176 -0
  92. data/test/shared/authentication/sasl_plain_shared.rb +96 -0
  93. data/test/shared/authentication/scram_shared.rb +92 -0
  94. data/test/shared/ssl_shared.rb +235 -0
  95. data/test/test_helper.rb +53 -94
  96. data/test/threading/basic_test.rb +120 -0
  97. data/test/tools/mongo_config.rb +708 -0
  98. data/test/tools/mongo_config_test.rb +160 -0
  99. data/test/unit/client_test.rb +381 -0
  100. data/test/unit/collection_test.rb +89 -53
  101. data/test/unit/connection_test.rb +282 -32
  102. data/test/unit/cursor_test.rb +206 -8
  103. data/test/unit/db_test.rb +55 -13
  104. data/test/unit/grid_test.rb +43 -16
  105. data/test/unit/mongo_sharded_client_test.rb +48 -0
  106. data/test/unit/node_test.rb +93 -0
  107. data/test/unit/pool_manager_test.rb +111 -0
  108. data/test/unit/read_pref_test.rb +406 -0
  109. data/test/unit/read_test.rb +159 -0
  110. data/test/unit/safe_test.rb +69 -36
  111. data/test/unit/sharding_pool_manager_test.rb +84 -0
  112. data/test/unit/write_concern_test.rb +175 -0
  113. data.tar.gz.sig +3 -0
  114. metadata +227 -216
  115. metadata.gz.sig +0 -0
  116. data/docs/CREDITS.md +0 -123
  117. data/docs/FAQ.md +0 -116
  118. data/docs/GridFS.md +0 -158
  119. data/docs/HISTORY.md +0 -244
  120. data/docs/RELEASES.md +0 -33
  121. data/docs/REPLICA_SETS.md +0 -72
  122. data/docs/TUTORIAL.md +0 -247
  123. data/docs/WRITE_CONCERN.md +0 -28
  124. data/lib/mongo/exceptions.rb +0 -71
  125. data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
  126. data/lib/mongo/repl_set_connection.rb +0 -342
  127. data/lib/mongo/test.rb +0 -20
  128. data/lib/mongo/util/pool.rb +0 -177
  129. data/lib/mongo/util/uri_parser.rb +0 -185
  130. data/test/async/collection_test.rb +0 -224
  131. data/test/async/connection_test.rb +0 -24
  132. data/test/async/cursor_test.rb +0 -162
  133. data/test/async/worker_pool_test.rb +0 -99
  134. data/test/auxillary/1.4_features.rb +0 -166
  135. data/test/auxillary/authentication_test.rb +0 -68
  136. data/test/auxillary/autoreconnect_test.rb +0 -41
  137. data/test/auxillary/fork_test.rb +0 -30
  138. data/test/auxillary/repl_set_auth_test.rb +0 -58
  139. data/test/auxillary/slave_connection_test.rb +0 -36
  140. data/test/auxillary/threaded_authentication_test.rb +0 -101
  141. data/test/bson/binary_test.rb +0 -15
  142. data/test/bson/bson_test.rb +0 -649
  143. data/test/bson/byte_buffer_test.rb +0 -208
  144. data/test/bson/hash_with_indifferent_access_test.rb +0 -38
  145. data/test/bson/json_test.rb +0 -17
  146. data/test/bson/object_id_test.rb +0 -154
  147. data/test/bson/ordered_hash_test.rb +0 -204
  148. data/test/bson/timestamp_test.rb +0 -24
  149. data/test/collection_test.rb +0 -910
  150. data/test/connection_test.rb +0 -309
  151. data/test/cursor_fail_test.rb +0 -75
  152. data/test/cursor_message_test.rb +0 -43
  153. data/test/cursor_test.rb +0 -483
  154. data/test/db_api_test.rb +0 -726
  155. data/test/db_connection_test.rb +0 -15
  156. data/test/db_test.rb +0 -287
  157. data/test/grid_file_system_test.rb +0 -243
  158. data/test/load/resque/load.rb +0 -21
  159. data/test/load/resque/processor.rb +0 -26
  160. data/test/load/thin/load.rb +0 -24
  161. data/test/load/unicorn/load.rb +0 -23
  162. data/test/load/unicorn/unicorn.rb +0 -29
  163. data/test/replica_sets/connect_test.rb +0 -94
  164. data/test/replica_sets/connection_string_test.rb +0 -32
  165. data/test/replica_sets/count_test.rb +0 -35
  166. data/test/replica_sets/insert_test.rb +0 -53
  167. data/test/replica_sets/pooled_insert_test.rb +0 -55
  168. data/test/replica_sets/query_secondaries.rb +0 -96
  169. data/test/replica_sets/query_test.rb +0 -51
  170. data/test/replica_sets/replication_ack_test.rb +0 -66
  171. data/test/replica_sets/rs_test_helper.rb +0 -27
  172. data/test/safe_test.rb +0 -68
  173. data/test/support/hash_with_indifferent_access.rb +0 -186
  174. data/test/support/keys.rb +0 -45
  175. data/test/support_test.rb +0 -18
  176. data/test/threading/threading_with_large_pool_test.rb +0 -90
  177. data/test/threading_test.rb +0 -87
  178. data/test/tools/auth_repl_set_manager.rb +0 -14
  179. data/test/tools/load.rb +0 -58
  180. data/test/tools/repl_set_manager.rb +0 -266
  181. data/test/tools/sharding_manager.rb +0 -202
  182. data/test/tools/test.rb +0 -4
  183. data/test/unit/pool_test.rb +0 -9
  184. data/test/unit/repl_set_connection_test.rb +0 -59
  185. data/test/uri_test.rb +0 -91
data/lib/mongo/db.rb CHANGED
@@ -1,36 +1,36 @@
1
- # encoding: UTF-8
2
-
3
- # --
4
- # Copyright (C) 2008-2011 10gen Inc.
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
5
2
  #
6
3
  # Licensed under the Apache License, Version 2.0 (the "License");
7
4
  # you may not use this file except in compliance with the License.
8
5
  # You may obtain a copy of the License at
9
6
  #
10
- # http://www.apache.org/licenses/LICENSE-2.0
7
+ # http://www.apache.org/licenses/LICENSE-2.0
11
8
  #
12
9
  # Unless required by applicable law or agreed to in writing, software
13
10
  # distributed under the License is distributed on an "AS IS" BASIS,
14
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
12
  # See the License for the specific language governing permissions and
16
13
  # limitations under the License.
17
- # ++
18
-
19
- require 'socket'
20
- require 'timeout'
21
- require 'thread'
22
14
 
23
15
  module Mongo
24
16
 
25
17
  # A MongoDB database.
26
18
  class DB
27
-
28
- SYSTEM_NAMESPACE_COLLECTION = "system.namespaces"
29
- SYSTEM_INDEX_COLLECTION = "system.indexes"
30
- SYSTEM_PROFILE_COLLECTION = "system.profile"
31
- SYSTEM_USER_COLLECTION = "system.users"
32
- SYSTEM_JS_COLLECTION = "system.js"
33
- SYSTEM_COMMAND_COLLECTION = "$cmd"
19
+ include Mongo::WriteConcern
20
+
21
+ SYSTEM_NAMESPACE_COLLECTION = 'system.namespaces'
22
+ SYSTEM_INDEX_COLLECTION = 'system.indexes'
23
+ SYSTEM_PROFILE_COLLECTION = 'system.profile'
24
+ SYSTEM_USER_COLLECTION = 'system.users'
25
+ SYSTEM_JS_COLLECTION = 'system.js'
26
+ SYSTEM_COMMAND_COLLECTION = '$cmd'
27
+ MAX_TIME_MS_CODE = 50
28
+
29
+ PROFILE_LEVEL = {
30
+ :off => 0,
31
+ :slow_only => 1,
32
+ :all => 2
33
+ }
34
34
 
35
35
  # Counter for generating unique request ids.
36
36
  @@current_request_id = 0
@@ -40,98 +40,137 @@ module Mongo
40
40
  # collection that already exists, raises an error.
41
41
  #
42
42
  # Strict mode is disabled by default, but enabled (+true+) at any time.
43
- attr_writer :strict
43
+ #
44
+ # @deprecated Support for strict will be removed in version 2.0 of the driver.
45
+ def strict=(value)
46
+ unless ENV['TEST_MODE']
47
+ warn "Support for strict mode has been deprecated and will be " +
48
+ "removed in version 2.0 of the driver."
49
+ end
50
+ @strict = value
51
+ end
44
52
 
45
53
  # Returns the value of the +strict+ flag.
46
- def strict?; @strict; end
54
+ #
55
+ # @deprecated Support for strict will be removed in version 2.0 of the driver.
56
+ def strict?
57
+ @strict
58
+ end
47
59
 
48
- # The name of the database and the local safe option.
49
- attr_reader :name, :safe
60
+ # The name of the database and the local write concern options.
61
+ attr_reader :name, :write_concern
50
62
 
51
- # The Mongo::Connection instance connecting to the MongoDB server.
52
- attr_reader :connection
63
+ # The Mongo::MongoClient instance connecting to the MongoDB server.
64
+ attr_reader :client
65
+
66
+ # for backward compatibility
67
+ alias_method :connection, :client
53
68
 
54
69
  # The length of time that Collection.ensure_index should cache index calls
55
70
  attr_accessor :cache_time
56
71
 
72
+ # Read Preference
73
+ attr_accessor :read, :tag_sets, :acceptable_latency
74
+
57
75
  # Instances of DB are normally obtained by calling Mongo#db.
58
76
  #
59
77
  # @param [String] name the database name.
60
- # @param [Mongo::Connection] connection a connection object pointing to MongoDB. Note
61
- # that databases are usually instantiated via the Connection class. See the examples below.
78
+ # @param [Mongo::MongoClient] client a connection object pointing to MongoDB. Note
79
+ # that databases are usually instantiated via the MongoClient class. See the examples below.
62
80
  #
63
- # @option opts [Boolean] :strict (False) If true, collections must exist to be accessed and must
64
- # not exist to be created. See DB#collection and DB#create_collection.
81
+ # @option opts [Boolean] :strict (False) [DEPRECATED] If true, collections existence checks are
82
+ # performed during a number of relevant operations. See DB#collection, DB#create_collection and
83
+ # DB#drop_collection.
65
84
  #
66
- # @option opts [Object, #create_pk(doc)] :pk (Mongo::ObjectId) A primary key factory object,
85
+ # @option opts [Object, #create_pk(doc)] :pk (BSON::ObjectId) A primary key factory object,
67
86
  # which should take a hash and return a hash which merges the original hash with any primary key
68
87
  # fields the factory wishes to inject. (NOTE: if the object already has a primary key,
69
88
  # the factory should not inject a new key).
70
89
  #
71
- # @option opts [Boolean, Hash] :safe (false) Set the default safe-mode options
72
- # propagated to Collection objects instantiated off of this DB. If no
73
- # value is provided, the default value set on this instance's Connection object will be used. This
74
- # default can be overridden upon instantiation of any collection by explicity setting a :safe value
75
- # on initialization
76
- # @option opts [Integer] :cache_time (300) Set the time that all ensure_index calls should cache the command.
90
+ # @option opts [String, Integer, Symbol] :w (1) Set default number of nodes to which a write
91
+ # should be acknowledged.
92
+ # @option opts [Integer] :wtimeout (nil) Set replica set acknowledgement timeout.
93
+ # @option opts [Boolean] :j (false) If true, block until write operations have been committed
94
+ # to the journal. Cannot be used in combination with 'fsync'. Prior to MongoDB 2.6 this option was
95
+ # ignored if the server was running without journaling. Starting with MongoDB 2.6, write operations will
96
+ # fail with an exception if this option is used when the server is running without journaling.
97
+ # @option opts [Boolean] :fsync (false) If true, and the server is running without journaling, blocks until
98
+ # the server has synced all data files to disk. If the server is running with journaling, this acts the same as
99
+ # the 'j' option, blocking until write operations have been committed to the journal.
100
+ # Cannot be used in combination with 'j'.
101
+ #
102
+ # Notes on write concern:
103
+ # These write concern options are propagated to Collection objects instantiated off of this DB. If no
104
+ # options are provided, the default write concern set on this instance's MongoClient object will be used. This
105
+ # default can be overridden upon instantiation of any collection by explicitly setting write concern options
106
+ # on initialization or at the time of an operation.
77
107
  #
78
- # @core databases constructor_details
79
- def initialize(name, connection, opts={})
80
- @name = Mongo::Support.validate_db_name(name)
81
- @connection = connection
108
+ # @option opts [Integer] :cache_time (300) Set the time that all ensure_index calls should cache the command.
109
+
110
+ def initialize(name, client, opts={})
111
+ # A database name of '$external' is permitted for some auth types
112
+ Support.validate_db_name(name) unless name == '$external'
113
+
114
+ @name = name
115
+ @client = client
82
116
  @strict = opts[:strict]
83
117
  @pk_factory = opts[:pk]
84
- @safe = opts.fetch(:safe, @connection.safe)
118
+
119
+ @write_concern = get_write_concern(opts, client)
120
+
121
+ @read = opts[:read] || @client.read
122
+ ReadPreference::validate(@read)
123
+
124
+ @tag_sets = opts.fetch(:tag_sets, @client.tag_sets)
125
+ @acceptable_latency = opts.fetch(:acceptable_latency,
126
+ @client.acceptable_latency)
127
+
85
128
  @cache_time = opts[:cache_time] || 300 #5 minutes.
86
129
  end
87
130
 
88
- # Authenticate with the given username and password. Note that mongod
89
- # must be started with the --auth option for authentication to be enabled.
90
- #
91
- # @param [String] username
92
- # @param [String] password
93
- # @param [Boolean] save_auth
94
- # Save this authentication to the connection object using Connection#add_auth. This
95
- # will ensure that the authentication will be applied on database reconnect. Note
96
- # that this value must be true when using connection pooling.
131
+ # Authenticate with the given username and password.
132
+ #
133
+ # @param username [String] The username.
134
+ # @param password [String] The user's password. This is not required for
135
+ # some authentication mechanisms.
136
+ # @param save_auth [Boolean]
137
+ # Save this authentication to the client object using
138
+ # MongoClient#add_auth. This will ensure that the authentication will
139
+ # be applied to all sockets and upon database reconnect.
140
+ # @param source [String] Database with user credentials. This should be
141
+ # used to authenticate against a database when the credentials exist
142
+ # elsewhere.
143
+ # @param mechanism [String] The authentication mechanism to be used.
144
+ # @param extra [Hash] A optional hash of extra options to be stored with
145
+ # the credential set.
146
+ #
147
+ # @note The ability to disable the save_auth option has been deprecated.
148
+ # With save_auth=false specified, driver authentication behavior during
149
+ # failovers and reconnections becomes unreliable. This option still
150
+ # exists for API compatibility, but it no longer has any effect if
151
+ # disabled and now always uses the default behavior (safe_auth=true).
152
+ #
153
+ # @raise [AuthenticationError] Raised if authentication fails.
154
+ # @return [Boolean] The result of the authentication operation.
155
+ def authenticate(username, password=nil, save_auth=nil, source=nil, mechanism=nil, extra=nil)
156
+ warn "[DEPRECATED] Disabling the 'save_auth' option no longer has " +
157
+ "any effect. Please see the API documentation for more details " +
158
+ "on this change." unless save_auth.nil?
159
+ @client.add_auth(self.name, username, password, source, mechanism, extra)
160
+ true
161
+ end
162
+
163
+ # Deauthorizes use for this database for this client connection. Also removes
164
+ # the saved authentication in the MongoClient class associated with this
165
+ # database.
97
166
  #
98
167
  # @return [Boolean]
99
- #
100
- # @raise [AuthenticationError]
101
- #
102
- # @core authenticate authenticate-instance_method
103
- def authenticate(username, password, save_auth=true)
104
- if @connection.pool_size > 1
105
- if !save_auth
106
- raise MongoArgumentError, "If using connection pooling, :save_auth must be set to true."
107
- end
108
- @connection.authenticate_pools
109
- end
110
-
111
- issue_authentication(username, password, save_auth)
112
- end
113
-
114
- def issue_authentication(username, password, save_auth=true, opts={})
115
- doc = command({:getnonce => 1}, :check_response => false, :socket => opts[:socket])
116
- raise MongoDBError, "Error retrieving nonce: #{doc}" unless ok?(doc)
117
- nonce = doc['nonce']
118
-
119
- auth = BSON::OrderedHash.new
120
- auth['authenticate'] = 1
121
- auth['user'] = username
122
- auth['nonce'] = nonce
123
- auth['key'] = Mongo::Support.auth_key(username, password, nonce)
124
- if ok?(self.command(auth, :check_response => false, :socket => opts[:socket]))
125
- if save_auth
126
- @connection.add_auth(@name, username, password)
127
- end
128
- true
129
- else
130
- raise(Mongo::AuthenticationError, "Failed to authenticate user '#{username}' on db '#{self.name}'")
131
- end
168
+ def logout(opts={})
169
+ @client.remove_auth(self.name)
170
+ true
132
171
  end
133
172
 
134
- # Adds a stored Javascript function to the database which can executed
173
+ # Adds a stored Javascript function to the database which can executed
135
174
  # server-side in map_reduce, db.eval and $where clauses.
136
175
  #
137
176
  # @param [String] function_name
@@ -141,7 +180,7 @@ module Mongo
141
180
  def add_stored_function(function_name, code)
142
181
  self[SYSTEM_JS_COLLECTION].save(
143
182
  {
144
- "_id" => function_name,
183
+ "_id" => function_name,
145
184
  :value => BSON::Code.new(code)
146
185
  }
147
186
  )
@@ -154,26 +193,50 @@ module Mongo
154
193
  #
155
194
  # @return [Boolean]
156
195
  def remove_stored_function(function_name)
157
- if self[SYSTEM_JS_COLLECTION].find_one({"_id" => function_name})
158
- self[SYSTEM_JS_COLLECTION].remove({"_id" => function_name}, :safe => true)
159
- else
160
- return false
161
- end
196
+ return false unless self[SYSTEM_JS_COLLECTION].find_one({"_id" => function_name})
197
+ self[SYSTEM_JS_COLLECTION].remove({"_id" => function_name}, :w => 1)
162
198
  end
163
199
 
164
200
  # Adds a user to this database for use with authentication. If the user already
165
- # exists in the system, the password will be updated.
201
+ # exists in the system, the password and any additional fields provided in opts
202
+ # will be updated.
166
203
  #
167
204
  # @param [String] username
168
205
  # @param [String] password
206
+ # @param [Boolean] read_only
207
+ # Create a read-only user.
208
+ #
209
+ # @param [Hash] opts
210
+ # Optional fields for the user document (e.g. +userSource+, or +roles+)
211
+ #
212
+ # See {http://docs.mongodb.org/manual/reference/privilege-documents}
213
+ # for more information.
214
+ #
215
+ # @note The use of the opts argument to provide or update additional fields
216
+ # on the user document requires MongoDB >= 2.4.0
169
217
  #
170
218
  # @return [Hash] an object representing the user.
171
- def add_user(username, password)
172
- users = self[SYSTEM_USER_COLLECTION]
173
- user = users.find_one({:user => username}) || {:user => username}
174
- user['pwd'] = Mongo::Support.hash_password(username, password)
175
- users.save(user)
176
- return user
219
+ def add_user(username, password=nil, read_only=false, opts={})
220
+ user_info = command(:usersInfo => username)
221
+ if user_info.key?('users') && !user_info['users'].empty?
222
+ create_or_update_user(:updateUser, username, password, read_only, opts)
223
+ else
224
+ create_or_update_user(:createUser, username, password, read_only, opts)
225
+ end
226
+ # MongoDB >= 2.5.3 requires the use of commands to manage users.
227
+ # "Command not found" error didn't return an error code (59) before
228
+ # MongoDB 2.4.7 so we assume that a nil error code means the usersInfo
229
+ # command doesn't exist and we should fall back to the legacy add user code.
230
+ rescue OperationFailure => ex
231
+ if Mongo::ErrorCode::COMMAND_NOT_FOUND_CODES.include?(ex.error_code)
232
+ legacy_add_user(username, password, read_only, opts)
233
+ elsif ex.error_code == Mongo::ErrorCode::UNAUTHORIZED
234
+ # In MongoDB > 2.7 the localhost exception was narrowed, and the usersInfo
235
+ # command is no longer allowed. In this case, add the first user.
236
+ create_or_update_user(:createUser, username, password, read_only, opts)
237
+ else
238
+ raise ex
239
+ end
177
240
  end
178
241
 
179
242
  # Remove the given user from this database. Returns false if the user
@@ -183,35 +246,12 @@ module Mongo
183
246
  #
184
247
  # @return [Boolean]
185
248
  def remove_user(username)
186
- if self[SYSTEM_USER_COLLECTION].find_one({:user => username})
187
- self[SYSTEM_USER_COLLECTION].remove({:user => username}, :safe => true)
188
- else
189
- return false
190
- end
191
- end
192
-
193
- # Deauthorizes use for this database for this connection. Also removes
194
- # any saved authentication in the connection class associated with this
195
- # database.
196
- #
197
- # @raise [MongoDBError] if logging out fails.
198
- #
199
- # @return [Boolean]
200
- def logout(opts={})
201
- if @connection.pool_size > 1
202
- @connection.logout_pools(@name)
203
- end
204
-
205
- issue_logout(opts)
206
- end
207
-
208
- def issue_logout(opts={})
209
- doc = command({:logout => 1}, :socket => opts[:socket])
210
- if ok?(doc)
211
- @connection.remove_auth(@name)
212
- true
213
- else
214
- raise MongoDBError, "error logging out: #{doc.inspect}"
249
+ begin
250
+ command(:dropUser => username)
251
+ rescue OperationFailure => ex
252
+ raise ex unless Mongo::ErrorCode::COMMAND_NOT_FOUND_CODES.include?(ex.error_code)
253
+ response = self[SYSTEM_USER_COLLECTION].remove({:user => username}, :w => 1)
254
+ response.key?('n') && response['n'] > 0 ? response : false
215
255
  end
216
256
  end
217
257
 
@@ -219,9 +259,14 @@ module Mongo
219
259
  #
220
260
  # @return [Array]
221
261
  def collection_names
222
- names = collections_info.collect { |doc| doc['name'] || '' }
223
- names = names.delete_if {|name| name.index(@name).nil? || name.index('$')}
224
- names.map {|name| name.sub(@name + '.', '')}
262
+ if @client.wire_version_feature?(Mongo::MongoClient::MONGODB_3_0)
263
+ names = collections_info.collect { |doc| doc['name'] || '' }
264
+ names.delete_if do |name|
265
+ name.index('$')
266
+ end
267
+ else
268
+ legacy_collection_names
269
+ end
225
270
  end
226
271
 
227
272
  # Get an array of Collection instances, one for each collection in this database.
@@ -237,13 +282,33 @@ module Mongo
237
282
  # a cursor which can be iterated over. For each collection, a hash
238
283
  # will be yielded containing a 'name' string and, optionally, an 'options' hash.
239
284
  #
240
- # @param [String] coll_name return info for the specifed collection only.
285
+ # @param [String] coll_name return info for the specified collection only.
241
286
  #
242
- # @return [Mongo::Cursor]
287
+ # @return [Array] List of collection info.
243
288
  def collections_info(coll_name=nil)
244
- selector = {}
245
- selector[:name] = full_collection_name(coll_name) if coll_name
246
- Cursor.new(Collection.new(SYSTEM_NAMESPACE_COLLECTION, self), :selector => selector)
289
+ if @client.wire_version_feature?(Mongo::MongoClient::MONGODB_3_0)
290
+ cmd = BSON::OrderedHash[:listCollections, 1]
291
+ cmd.merge!(:filter => { :name => coll_name }) if coll_name
292
+ result = self.command(cmd, :cursor => {})
293
+ if result.key?('cursor')
294
+ cursor_info = result['cursor']
295
+ pinned_pool = @client.pinned_pool
296
+ pinned_pool = pinned_pool[:pool] if pinned_pool.respond_to?(:keys)
297
+
298
+ seed = {
299
+ :cursor_id => cursor_info['id'],
300
+ :first_batch => cursor_info['firstBatch'],
301
+ :pool => pinned_pool,
302
+ :ns => cursor_info['ns']
303
+ }
304
+
305
+ Cursor.new(Collection.new('$cmd', self), seed).to_a
306
+ else
307
+ result['collections']
308
+ end
309
+ else
310
+ legacy_collections_info(coll_name).to_a
311
+ end
247
312
  end
248
313
 
249
314
  # Create a collection.
@@ -251,52 +316,58 @@ module Mongo
251
316
  # new collection. If +strict+ is true, will raise an error if
252
317
  # collection +name+ already exists.
253
318
  #
254
- # @param [String] name the name of the new collection.
319
+ # @param [String, Symbol] name the name of the new collection.
255
320
  #
256
321
  # @option opts [Boolean] :capped (False) created a capped collection.
257
322
  #
258
- # @option opts [Integer] :size (Nil) If +capped+ is +true+, specifies the maximum number of
259
- # bytes for the capped collection. If +false+, specifies the number of bytes allocated
323
+ # @option opts [Integer] :size (Nil) If +capped+ is +true+,
324
+ # specifies the maximum number of bytes for the capped collection.
325
+ # If +false+, specifies the number of bytes allocated
260
326
  # for the initial extent of the collection.
261
327
  #
262
- # @option opts [Integer] :max (Nil) If +capped+ is +true+, indicates the maximum number of records
263
- # in a capped collection.
328
+ # @option opts [Integer] :max (Nil) If +capped+ is +true+, indicates
329
+ # the maximum number of records in a capped collection.
264
330
  #
265
- # @raise [MongoDBError] raised under two conditions: either we're in +strict+ mode and the collection
331
+ # @raise [MongoDBError] raised under two conditions:
332
+ # either we're in +strict+ mode and the collection
266
333
  # already exists or collection creation fails on the server.
267
334
  #
335
+ # @note Note that the options listed may be subset of those available.
336
+ # Please see the MongoDB documentation for a full list of supported options by server version.
337
+ #
268
338
  # @return [Mongo::Collection]
269
339
  def create_collection(name, opts={})
270
- # Does the collection already exist?
271
- if collection_names.include?(name)
272
- if strict?
273
- raise MongoDBError, "Collection #{name} already exists. Currently in strict mode."
274
- else
275
- return Collection.new(name, self, opts)
276
- end
340
+ name = name.to_s
341
+ if strict? && collection_names.include?(name)
342
+ raise MongoDBError, "Collection '#{name}' already exists. (strict=true)"
277
343
  end
278
344
 
279
- # Create a new collection.
280
- oh = BSON::OrderedHash.new
281
- oh[:create] = name
282
- doc = command(oh.merge(opts || {}))
283
- return Collection.new(name, self, :pk => @pk_factory) if ok?(doc)
345
+ begin
346
+ cmd = BSON::OrderedHash.new
347
+ cmd[:create] = name
348
+ doc = command(cmd.merge(opts || {}))
349
+ return Collection.new(name, self, :pk => @pk_factory) if ok?(doc)
350
+ rescue OperationFailure => e
351
+ return Collection.new(name, self, :pk => @pk_factory) if e.message =~ /exists/
352
+ raise e
353
+ end
284
354
  raise MongoDBError, "Error creating collection: #{doc.inspect}"
285
355
  end
286
356
 
287
357
  # Get a collection by name.
288
358
  #
289
- # @param [String] name the collection name.
359
+ # @param [String, Symbol] name the collection name.
290
360
  # @param [Hash] opts any valid options that can be passed to Collection#new.
291
361
  #
292
- # @raise [MongoDBError] if collection does not already exist and we're in +strict+ mode.
362
+ # @raise [MongoDBError] if collection does not already exist and we're in
363
+ # +strict+ mode.
293
364
  #
294
365
  # @return [Mongo::Collection]
295
366
  def collection(name, opts={})
296
- if strict? && !collection_names.include?(name)
297
- raise Mongo::MongoDBError, "Collection #{name} doesn't exist. Currently in strict mode."
367
+ if strict? && !collection_names.include?(name.to_s)
368
+ raise MongoDBError, "Collection '#{name}' doesn't exist. (strict=true)"
298
369
  else
299
- opts[:safe] = opts.fetch(:safe, @safe)
370
+ opts = opts.dup
300
371
  opts.merge!(:pk => @pk_factory) unless opts[:pk]
301
372
  Collection.new(name, self, opts)
302
373
  end
@@ -305,13 +376,16 @@ module Mongo
305
376
 
306
377
  # Drop a collection by +name+.
307
378
  #
308
- # @param [String] name
379
+ # @param [String, Symbol] name
309
380
  #
310
381
  # @return [Boolean] +true+ on success or +false+ if the collection name doesn't exist.
311
382
  def drop_collection(name)
312
- return true unless collection_names.include?(name)
313
-
314
- ok?(command(:drop => name))
383
+ return false if strict? && !collection_names.include?(name.to_s)
384
+ begin
385
+ ok?(command(:drop => name))
386
+ rescue OperationFailure
387
+ false
388
+ end
315
389
  end
316
390
 
317
391
  # Run the getlasterror command with the specified replication options.
@@ -319,6 +393,7 @@ module Mongo
319
393
  # @option opts [Boolean] :fsync (false)
320
394
  # @option opts [Integer] :w (nil)
321
395
  # @option opts [Integer] :wtimeout (nil)
396
+ # @option opts [Boolean] :j (false)
322
397
  #
323
398
  # @return [Hash] the entire response to getlasterror.
324
399
  #
@@ -328,7 +403,7 @@ module Mongo
328
403
  cmd[:getlasterror] = 1
329
404
  cmd.merge!(opts)
330
405
  doc = command(cmd, :check_response => false)
331
- raise MongoDBError, "error retrieving last error: #{doc.inspect}" unless ok?(doc)
406
+ raise MongoDBError, "Error retrieving last error: #{doc.inspect}" unless ok?(doc)
332
407
  doc
333
408
  end
334
409
 
@@ -340,19 +415,15 @@ module Mongo
340
415
  get_last_error['err'] != nil
341
416
  end
342
417
 
343
- # Get the most recent error to have occured on this database.
418
+ # Get the most recent error to have occurred on this database.
344
419
  #
345
- # This command only returns errors that have occured since the last call to
420
+ # This command only returns errors that have occurred since the last call to
346
421
  # DB#reset_error_history - returns +nil+ if there is no such error.
347
422
  #
348
423
  # @return [String, Nil] the text of the error or +nil+ if no error has occurred.
349
424
  def previous_error
350
425
  error = command(:getpreverror => 1)
351
- if error["err"]
352
- error
353
- else
354
- nil
355
- end
426
+ error["err"] ? error : nil
356
427
  end
357
428
 
358
429
  # Reset the error history of this database
@@ -379,19 +450,22 @@ module Mongo
379
450
  # Evaluate a JavaScript expression in MongoDB.
380
451
  #
381
452
  # @param [String, Code] code a JavaScript expression to evaluate server-side.
382
- # @param [Integer, Hash] args any additional argument to be passed to the +code+ expression when
453
+ # @param [Integer, Hash] args any additional argument to be passed to the +code+ expression when
383
454
  # it's run on the server.
384
455
  #
456
+ # @note the eval command is deprecated in MongoDB 3.0 and will be removed in a future server version.
457
+ #
385
458
  # @return [String] the return value of the function.
386
459
  def eval(code, *args)
387
- if not code.is_a? BSON::Code
460
+ unless code.is_a?(BSON::Code)
388
461
  code = BSON::Code.new(code)
389
462
  end
390
463
 
391
- oh = BSON::OrderedHash.new
392
- oh[:$eval] = code
393
- oh[:args] = args
394
- doc = command(oh)
464
+ cmd = BSON::OrderedHash.new
465
+ cmd[:$eval] = code
466
+ cmd.merge!(args.pop) if args.last.respond_to?(:keys) && args.last.key?(:nolock)
467
+ cmd[:args] = args
468
+ doc = command(cmd)
395
469
  doc['retval']
396
470
  end
397
471
 
@@ -404,10 +478,10 @@ module Mongo
404
478
  #
405
479
  # @raise MongoDBError if there's an error renaming the collection.
406
480
  def rename_collection(from, to)
407
- oh = BSON::OrderedHash.new
408
- oh[:renameCollection] = "#{@name}.#{from}"
409
- oh[:to] = "#{@name}.#{to}"
410
- doc = DB.new('admin', @connection).command(oh, :check_response => false)
481
+ cmd = BSON::OrderedHash.new
482
+ cmd[:renameCollection] = "#{@name}.#{from}"
483
+ cmd[:to] = "#{@name}.#{to}"
484
+ doc = DB.new('admin', @client).command(cmd, :check_response => false)
411
485
  ok?(doc) || raise(MongoDBError, "Error renaming collection: #{doc.inspect}")
412
486
  end
413
487
 
@@ -419,12 +493,12 @@ module Mongo
419
493
  #
420
494
  # @return [True] returns +true+ on success.
421
495
  #
422
- # @raise MongoDBError if there's an error renaming the collection.
496
+ # @raise MongoDBError if there's an error dropping the index.
423
497
  def drop_index(collection_name, index_name)
424
- oh = BSON::OrderedHash.new
425
- oh[:deleteIndexes] = collection_name
426
- oh[:index] = index_name.to_s
427
- doc = command(oh, :check_response => false)
498
+ cmd = BSON::OrderedHash.new
499
+ cmd[:deleteIndexes] = collection_name
500
+ cmd[:index] = index_name.to_s
501
+ doc = command(cmd, :check_response => false)
428
502
  ok?(doc) || raise(MongoDBError, "Error with drop_index command: #{doc.inspect}")
429
503
  end
430
504
 
@@ -433,22 +507,40 @@ module Mongo
433
507
  #
434
508
  # @param [String] collection_name
435
509
  #
436
- # @return [Hash] keys are index names and the values are lists of [key, direction] pairs
510
+ # @return [Hash] keys are index names and the values are lists of [key, type] pairs
437
511
  # defining the index.
438
512
  def index_information(collection_name)
439
- sel = {:ns => full_collection_name(collection_name)}
440
- info = {}
441
- Cursor.new(Collection.new(SYSTEM_INDEX_COLLECTION, self), :selector => sel).each do |index|
442
- info[index['name']] = index
513
+ if @client.wire_version_feature?(Mongo::MongoClient::MONGODB_3_0)
514
+ result = self.command({ :listIndexes => collection_name }, :cursor => {})
515
+ if result.key?('cursor')
516
+ cursor_info = result['cursor']
517
+ pinned_pool = @client.pinned_pool
518
+ pinned_pool = pinned_pool[:pool] if pinned_pool.respond_to?(:keys)
519
+
520
+ seed = {
521
+ :cursor_id => cursor_info['id'],
522
+ :first_batch => cursor_info['firstBatch'],
523
+ :pool => pinned_pool,
524
+ :ns => cursor_info['ns']
525
+ }
526
+
527
+ indexes = Cursor.new(Collection.new('$cmd', self), seed).to_a
528
+ else
529
+ indexes = result['indexes']
530
+ end
531
+ else
532
+ indexes = legacy_list_indexes(collection_name)
533
+ end
534
+ indexes.reduce({}) do |info, index|
535
+ info.merge!(index['name'] => index)
443
536
  end
444
- info
445
537
  end
446
538
 
447
539
  # Return stats on this database. Uses MongoDB's dbstats command.
448
540
  #
449
541
  # @return [Hash]
450
542
  def stats
451
- self.command({:dbstats => 1})
543
+ self.command(:dbstats => 1)
452
544
  end
453
545
 
454
546
  # Return +true+ if the supplied +doc+ contains an 'ok' field with the value 1.
@@ -470,38 +562,75 @@ module Mongo
470
562
  # to see how it works.
471
563
  #
472
564
  # @param [OrderedHash, Hash] selector an OrderedHash, or a standard Hash with just one
473
- # key, specifying the command to be performed. In Ruby 1.9, OrderedHash isn't necessary since
474
- # hashes are ordered by default.
565
+ # key, specifying the command to be performed. In Ruby 1.9 and above, OrderedHash isn't necessary
566
+ # because hashes are ordered by default.
475
567
  #
476
568
  # @option opts [Boolean] :check_response (true) If +true+, raises an exception if the
477
- # command fails.
569
+ # command fails.
478
570
  # @option opts [Socket] :socket a socket to use for sending the command. This is mainly for internal use.
571
+ # @option opts [:primary, :secondary] :read Read preference for this command. See Collection#find for
572
+ # more details.
573
+ # @option opts [String] :comment (nil) a comment to include in profiling logs
574
+ # @option opts [Boolean] :compile_regex (true) whether BSON regex objects should be compiled into Ruby regexes.
575
+ # If false, a BSON::Regex object will be returned instead.
479
576
  #
480
577
  # @return [Hash]
481
- #
482
- # @core commands command_instance-method
483
578
  def command(selector, opts={})
484
- check_response = opts.fetch(:check_response, true)
485
- socket = opts[:socket]
486
- raise MongoArgumentError, "command must be given a selector" unless selector.is_a?(Hash) && !selector.empty?
487
- if selector.keys.length > 1 && RUBY_VERSION < '1.9' && selector.class != BSON::OrderedHash
488
- raise MongoArgumentError, "DB#command requires an OrderedHash when hash contains multiple keys"
579
+ raise MongoArgumentError, "Command must be given a selector" unless selector.respond_to?(:keys) && !selector.empty?
580
+
581
+ opts = opts.dup
582
+ # deletes :check_response and returns the value, if nil defaults to the block result
583
+ check_response = opts.delete(:check_response) { true }
584
+
585
+ # build up the command hash
586
+ command = opts.key?(:socket) ? { :socket => opts.delete(:socket) } : {}
587
+ command.merge!(:comment => opts.delete(:comment)) if opts.key?(:comment)
588
+ command.merge!(:compile_regex => opts.delete(:compile_regex)) if opts.key?(:compile_regex)
589
+ command[:limit] = -1
590
+ command[:read] = Mongo::ReadPreference::cmd_read_pref(opts.delete(:read), selector) if opts.key?(:read)
591
+
592
+ if RUBY_VERSION < '1.9' && selector.class != BSON::OrderedHash
593
+ if selector.keys.length > 1
594
+ raise MongoArgumentError, "DB#command requires an OrderedHash when hash contains multiple keys"
595
+ end
596
+ if opts.keys.size > 0
597
+ # extra opts will be merged into the selector, so make sure it's an OH in versions < 1.9
598
+ selector = selector.dup
599
+ selector = BSON::OrderedHash.new.merge!(selector)
600
+ end
489
601
  end
490
602
 
603
+ # arbitrary opts are merged into the selector
604
+ command[:selector] = selector.merge!(opts)
605
+
491
606
  begin
492
- result = Cursor.new(system_command_collection,
493
- :limit => -1, :selector => selector, :socket => socket).next_document
607
+ result = Cursor.new(system_command_collection, command).next_document
494
608
  rescue OperationFailure => ex
495
- raise OperationFailure, "Database command '#{selector.keys.first}' failed: #{ex.message}"
609
+ if check_response
610
+ raise ex.class.new("Database command '#{selector.keys.first}' failed: #{ex.message}", ex.error_code, ex.result)
611
+ else
612
+ result = ex.result
613
+ end
496
614
  end
497
615
 
498
- if result.nil?
499
- raise OperationFailure, "Database command '#{selector.keys.first}' failed: returned null."
500
- elsif (check_response && !ok?(result))
501
- raise OperationFailure, "Database command '#{selector.keys.first}' failed: #{result.inspect}"
502
- else
503
- result
616
+ raise OperationFailure,
617
+ "Database command '#{selector.keys.first}' failed: returned null." unless result
618
+
619
+ if check_response && (!ok?(result) || result['writeErrors'] || result['writeConcernError'])
620
+ message = "Database command '#{selector.keys.first}' failed: ("
621
+ message << result.map do |key, value|
622
+ "#{key}: '#{value}'"
623
+ end.join('; ')
624
+ message << ').'
625
+ code = result['code'] || result['assertionCode']
626
+ if result['writeErrors']
627
+ code = result['writeErrors'].first['code']
628
+ end
629
+ raise ExecutionTimeout.new(message, code, result) if code == MAX_TIME_MS_CODE
630
+ raise OperationFailure.new(message, code, result)
504
631
  end
632
+
633
+ result
505
634
  end
506
635
 
507
636
  # A shortcut returning db plus dot plus collection name.
@@ -524,9 +653,8 @@ module Mongo
524
653
  #
525
654
  # @raise [MongoArgumentError] if the primary key factory has already been set.
526
655
  def pk_factory=(pk_factory)
527
- if @pk_factory
528
- raise MongoArgumentError, "Cannot change primary key factory once it's been set"
529
- end
656
+ raise MongoArgumentError,
657
+ "Cannot change primary key factory once it's been set" if @pk_factory
530
658
 
531
659
  @pk_factory = pk_factory
532
660
  end
@@ -535,23 +663,16 @@ module Mongo
535
663
  # get the results using DB#profiling_info.
536
664
  #
537
665
  # @return [Symbol] :off, :slow_only, or :all
538
- #
539
- # @core profiling profiling_level-instance_method
540
666
  def profiling_level
541
- oh = BSON::OrderedHash.new
542
- oh[:profile] = -1
543
- doc = command(oh, :check_response => false)
544
- raise "Error with profile command: #{doc.inspect}" unless ok?(doc) && doc['was'].kind_of?(Numeric)
545
- case doc['was'].to_i
546
- when 0
547
- :off
548
- when 1
549
- :slow_only
550
- when 2
551
- :all
552
- else
553
- raise "Error: illegal profiling level value #{doc['was']}"
554
- end
667
+ cmd = BSON::OrderedHash.new
668
+ cmd[:profile] = -1
669
+ doc = command(cmd, :check_response => false)
670
+
671
+ raise "Error with profile command: #{doc.inspect}" unless ok?(doc)
672
+
673
+ level_sym = PROFILE_LEVEL.invert[doc['was'].to_i]
674
+ raise "Error: illegal profiling level value #{doc['was']}" unless level_sym
675
+ level_sym
555
676
  end
556
677
 
557
678
  # Set this database's profiling level. If profiling is enabled, you can
@@ -559,18 +680,9 @@ module Mongo
559
680
  #
560
681
  # @param [Symbol] level acceptable options are +:off+, +:slow_only+, or +:all+.
561
682
  def profiling_level=(level)
562
- oh = BSON::OrderedHash.new
563
- oh[:profile] = case level
564
- when :off
565
- 0
566
- when :slow_only
567
- 1
568
- when :all
569
- 2
570
- else
571
- raise "Error: illegal profiling level value #{level}"
572
- end
573
- doc = command(oh, :check_response => false)
683
+ cmd = BSON::OrderedHash.new
684
+ cmd[:profile] = PROFILE_LEVEL[level]
685
+ doc = command(cmd, :check_response => false)
574
686
  ok?(doc) || raise(MongoDBError, "Error with profile command: #{doc.inspect}")
575
687
  end
576
688
 
@@ -590,11 +702,16 @@ module Mongo
590
702
  # @raise [MongoDBError] if the command fails or there's a problem with the validation
591
703
  # data, or if the collection is invalid.
592
704
  def validate_collection(name)
593
- doc = command({:validate => name}, :check_response => false)
705
+ cmd = BSON::OrderedHash.new
706
+ cmd[:validate] = name
707
+ cmd[:full] = true
708
+ doc = command(cmd, :check_response => false)
709
+
594
710
  raise MongoDBError, "Error with validate command: #{doc.inspect}" unless ok?(doc)
595
- result = doc['result']
596
- raise MongoDBError, "Error with validation data: #{doc.inspect}" unless result.kind_of?(String)
597
- raise MongoDBError, "Error: invalid collection #{name}: #{doc.inspect}" if result =~ /\b(exception|corrupt)\b/i
711
+
712
+ if (doc.has_key?('valid') && !doc['valid']) || (doc['result'] =~ /\b(exception|corrupt)\b/i)
713
+ raise MongoDBError, "Error: invalid collection #{name}: #{doc.inspect}"
714
+ end
598
715
  doc
599
716
  end
600
717
 
@@ -603,5 +720,89 @@ module Mongo
603
720
  def system_command_collection
604
721
  Collection.new(SYSTEM_COMMAND_COLLECTION, self)
605
722
  end
723
+
724
+ # Create a new user.
725
+ #
726
+ # @param username [String] The username.
727
+ # @param password [String] The user's password.
728
+ # @param read_only [Boolean] Create a read-only user (deprecated in MongoDB >= 2.6)
729
+ # @param opts [Hash]
730
+ #
731
+ # @private
732
+ def create_or_update_user(command, username, password, read_only, opts)
733
+ if read_only || !opts.key?(:roles)
734
+ warn "Creating a user with the read_only option or without roles is " +
735
+ "deprecated in MongoDB >= 2.6"
736
+ end
737
+
738
+ # The password is always salted and hashed by the driver.
739
+ if opts.key?(:digestPassword)
740
+ raise MongoArgumentError,
741
+ "The digestPassword option is not available via DB#add_user. " +
742
+ "Use DB#command(:createUser => ...) instead for this option."
743
+ end
744
+
745
+ opts = opts.dup
746
+ pwd = Mongo::Authentication.hash_password(username, password) if password
747
+ cmd_opts = pwd ? { :pwd => pwd } : {}
748
+ # specify that the server shouldn't digest the password because the driver does
749
+ cmd_opts[:digestPassword] = false
750
+ unless opts.key?(:roles)
751
+ if name == 'admin'
752
+ roles = read_only ? ['readAnyDatabase'] : ['root']
753
+ else
754
+ roles = read_only ? ['read'] : ["dbOwner"]
755
+ end
756
+ cmd_opts[:roles] = roles
757
+ end
758
+ cmd_opts[:writeConcern] =
759
+ opts.key?(:writeConcern) ? opts.delete(:writeConcern) : { :w => 1 }
760
+ cmd_opts.merge!(opts)
761
+ command({ command => username }, cmd_opts)
762
+ end
763
+
764
+ # Create a user in MongoDB versions < 2.5.3.
765
+ # Called by #add_user if the 'usersInfo' command fails.
766
+ #
767
+ # @param username [String] The username.
768
+ # @param password [String] (nil) The user's password.
769
+ # @param read_only [Boolean] (false) Create a read-only user.
770
+ # @param opts [Hash]
771
+ #
772
+ # @private
773
+ def legacy_add_user(username, password=nil, read_only=false, opts={})
774
+ users = self[SYSTEM_USER_COLLECTION]
775
+ user = users.find_one(:user => username) || {:user => username}
776
+ user['pwd'] =
777
+ Mongo::Authentication.hash_password(username, password) if password
778
+ user['readOnly'] = true if read_only
779
+ user.merge!(opts)
780
+ begin
781
+ users.save(user)
782
+ rescue OperationFailure => ex
783
+ # adding first admin user fails GLE in MongoDB 2.2
784
+ raise ex unless ex.message =~ /login/
785
+ end
786
+ user
787
+ end
788
+
789
+ def legacy_list_indexes(collection_name)
790
+ sel = {:ns => full_collection_name(collection_name)}
791
+ Cursor.new(Collection.new(SYSTEM_INDEX_COLLECTION, self), :selector => sel)
792
+ end
793
+
794
+ def legacy_collections_info(coll_name=nil)
795
+ selector = {}
796
+ selector[:name] = full_collection_name(coll_name) if coll_name
797
+ Cursor.new(Collection.new(SYSTEM_NAMESPACE_COLLECTION, self), :selector => selector)
798
+ end
799
+
800
+ def legacy_collection_names
801
+ names = legacy_collections_info.collect { |doc| doc['name'] || '' }
802
+ names = names.delete_if do |name|
803
+ name.index(@name).nil? || name.index('$')
804
+ end
805
+ names.map {|name| name.sub(@name + '.', '')}
806
+ end
606
807
  end
607
808
  end