jonbell-mongo 1.3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/LICENSE.txt +190 -0
  2. data/README.md +333 -0
  3. data/Rakefile +215 -0
  4. data/bin/mongo_console +21 -0
  5. data/docs/CREDITS.md +123 -0
  6. data/docs/FAQ.md +116 -0
  7. data/docs/GridFS.md +158 -0
  8. data/docs/HISTORY.md +263 -0
  9. data/docs/RELEASES.md +33 -0
  10. data/docs/REPLICA_SETS.md +72 -0
  11. data/docs/TUTORIAL.md +247 -0
  12. data/docs/WRITE_CONCERN.md +28 -0
  13. data/lib/mongo.rb +97 -0
  14. data/lib/mongo/collection.rb +895 -0
  15. data/lib/mongo/connection.rb +926 -0
  16. data/lib/mongo/cursor.rb +474 -0
  17. data/lib/mongo/db.rb +617 -0
  18. data/lib/mongo/exceptions.rb +71 -0
  19. data/lib/mongo/gridfs/grid.rb +107 -0
  20. data/lib/mongo/gridfs/grid_ext.rb +57 -0
  21. data/lib/mongo/gridfs/grid_file_system.rb +146 -0
  22. data/lib/mongo/gridfs/grid_io.rb +485 -0
  23. data/lib/mongo/gridfs/grid_io_fix.rb +38 -0
  24. data/lib/mongo/repl_set_connection.rb +356 -0
  25. data/lib/mongo/util/conversions.rb +89 -0
  26. data/lib/mongo/util/core_ext.rb +60 -0
  27. data/lib/mongo/util/pool.rb +177 -0
  28. data/lib/mongo/util/server_version.rb +71 -0
  29. data/lib/mongo/util/support.rb +82 -0
  30. data/lib/mongo/util/uri_parser.rb +185 -0
  31. data/mongo.gemspec +34 -0
  32. data/test/auxillary/1.4_features.rb +166 -0
  33. data/test/auxillary/authentication_test.rb +68 -0
  34. data/test/auxillary/autoreconnect_test.rb +41 -0
  35. data/test/auxillary/fork_test.rb +30 -0
  36. data/test/auxillary/repl_set_auth_test.rb +58 -0
  37. data/test/auxillary/slave_connection_test.rb +36 -0
  38. data/test/auxillary/threaded_authentication_test.rb +101 -0
  39. data/test/bson/binary_test.rb +15 -0
  40. data/test/bson/bson_test.rb +654 -0
  41. data/test/bson/byte_buffer_test.rb +208 -0
  42. data/test/bson/hash_with_indifferent_access_test.rb +38 -0
  43. data/test/bson/json_test.rb +17 -0
  44. data/test/bson/object_id_test.rb +154 -0
  45. data/test/bson/ordered_hash_test.rb +210 -0
  46. data/test/bson/timestamp_test.rb +24 -0
  47. data/test/collection_test.rb +910 -0
  48. data/test/connection_test.rb +324 -0
  49. data/test/conversions_test.rb +119 -0
  50. data/test/cursor_fail_test.rb +75 -0
  51. data/test/cursor_message_test.rb +43 -0
  52. data/test/cursor_test.rb +483 -0
  53. data/test/db_api_test.rb +738 -0
  54. data/test/db_connection_test.rb +15 -0
  55. data/test/db_test.rb +315 -0
  56. data/test/grid_file_system_test.rb +259 -0
  57. data/test/grid_io_test.rb +209 -0
  58. data/test/grid_test.rb +258 -0
  59. data/test/load/thin/load.rb +24 -0
  60. data/test/load/unicorn/load.rb +23 -0
  61. data/test/replica_sets/connect_test.rb +112 -0
  62. data/test/replica_sets/connection_string_test.rb +32 -0
  63. data/test/replica_sets/count_test.rb +35 -0
  64. data/test/replica_sets/insert_test.rb +53 -0
  65. data/test/replica_sets/pooled_insert_test.rb +55 -0
  66. data/test/replica_sets/query_secondaries.rb +108 -0
  67. data/test/replica_sets/query_test.rb +51 -0
  68. data/test/replica_sets/replication_ack_test.rb +66 -0
  69. data/test/replica_sets/rs_test_helper.rb +27 -0
  70. data/test/safe_test.rb +68 -0
  71. data/test/support/hash_with_indifferent_access.rb +186 -0
  72. data/test/support/keys.rb +45 -0
  73. data/test/support_test.rb +18 -0
  74. data/test/test_helper.rb +102 -0
  75. data/test/threading/threading_with_large_pool_test.rb +90 -0
  76. data/test/threading_test.rb +87 -0
  77. data/test/tools/auth_repl_set_manager.rb +14 -0
  78. data/test/tools/repl_set_manager.rb +266 -0
  79. data/test/unit/collection_test.rb +130 -0
  80. data/test/unit/connection_test.rb +85 -0
  81. data/test/unit/cursor_test.rb +109 -0
  82. data/test/unit/db_test.rb +94 -0
  83. data/test/unit/grid_test.rb +49 -0
  84. data/test/unit/pool_test.rb +9 -0
  85. data/test/unit/repl_set_connection_test.rb +59 -0
  86. data/test/unit/safe_test.rb +125 -0
  87. data/test/uri_test.rb +91 -0
  88. metadata +224 -0
data/lib/mongo/db.rb ADDED
@@ -0,0 +1,617 @@
1
+ # encoding: UTF-8
2
+
3
+ # --
4
+ # Copyright (C) 2008-2011 10gen Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ # ++
18
+
19
+ require 'socket'
20
+ require 'timeout'
21
+ require 'thread'
22
+
23
+ module Mongo
24
+
25
+ # A MongoDB database.
26
+ 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"
34
+
35
+ # Counter for generating unique request ids.
36
+ @@current_request_id = 0
37
+
38
+ # Strict mode enforces collection existence checks. When +true+,
39
+ # asking for a collection that does not exist, or trying to create a
40
+ # collection that already exists, raises an error.
41
+ #
42
+ # Strict mode is disabled by default, but enabled (+true+) at any time.
43
+ attr_writer :strict
44
+
45
+ # Returns the value of the +strict+ flag.
46
+ def strict?; @strict; end
47
+
48
+ # The name of the database and the local safe option.
49
+ attr_reader :name, :safe
50
+
51
+ # The Mongo::Connection instance connecting to the MongoDB server.
52
+ attr_reader :connection
53
+
54
+ # The length of time that Collection.ensure_index should cache index calls
55
+ attr_accessor :cache_time
56
+
57
+ # Instances of DB are normally obtained by calling Mongo#db.
58
+ #
59
+ # @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.
62
+ #
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.
65
+ #
66
+ # @option opts [Object, #create_pk(doc)] :pk (Mongo::ObjectId) A primary key factory object,
67
+ # which should take a hash and return a hash which merges the original hash with any primary key
68
+ # fields the factory wishes to inject. (NOTE: if the object already has a primary key,
69
+ # the factory should not inject a new key).
70
+ #
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.
77
+ #
78
+ # @core databases constructor_details
79
+ def initialize(name, connection, opts={})
80
+ @name = Mongo::Support.validate_db_name(name)
81
+ @connection = connection
82
+ @strict = opts[:strict]
83
+ @pk_factory = opts[:pk]
84
+ @safe = opts.fetch(:safe, @connection.safe)
85
+ @cache_time = opts[:cache_time] || 300 #5 minutes.
86
+ end
87
+
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.
97
+ #
98
+ # @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
132
+ end
133
+
134
+ # Adds a stored Javascript function to the database which can executed
135
+ # server-side in map_reduce, db.eval and $where clauses.
136
+ #
137
+ # @param [String] function_name
138
+ # @param [String] code
139
+ #
140
+ # @return [String] the function name saved to the database
141
+ def add_stored_function(function_name, code)
142
+ self[SYSTEM_JS_COLLECTION].save(
143
+ {
144
+ "_id" => function_name,
145
+ :value => BSON::Code.new(code)
146
+ }
147
+ )
148
+ end
149
+
150
+ # Removes stored Javascript function from the database. Returns
151
+ # false if the function does not exist
152
+ #
153
+ # @param [String] function_name
154
+ #
155
+ # @return [Boolean]
156
+ 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
162
+ end
163
+
164
+ # Adds a user to this database for use with authentication. If the user already
165
+ # exists in the system, the password will be updated.
166
+ #
167
+ # @param [String] username
168
+ # @param [String] password
169
+ #
170
+ # @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
177
+ end
178
+
179
+ # Remove the given user from this database. Returns false if the user
180
+ # doesn't exist in the system.
181
+ #
182
+ # @param [String] username
183
+ #
184
+ # @return [Boolean]
185
+ 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}"
215
+ end
216
+ end
217
+
218
+ # Get an array of collection names in this database.
219
+ #
220
+ # @return [Array]
221
+ 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 + '.', '')}
225
+ end
226
+
227
+ # Get an array of Collection instances, one for each collection in this database.
228
+ #
229
+ # @return [Array<Mongo::Collection>]
230
+ def collections
231
+ collection_names.map do |name|
232
+ Collection.new(name, self)
233
+ end
234
+ end
235
+
236
+ # Get info on system namespaces (collections). This method returns
237
+ # a cursor which can be iterated over. For each collection, a hash
238
+ # will be yielded containing a 'name' string and, optionally, an 'options' hash.
239
+ #
240
+ # @param [String] coll_name return info for the specifed collection only.
241
+ #
242
+ # @return [Mongo::Cursor]
243
+ 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)
247
+ end
248
+
249
+ # Create a collection.
250
+ #
251
+ # new collection. If +strict+ is true, will raise an error if
252
+ # collection +name+ already exists.
253
+ #
254
+ # @param [String, Symbol] name the name of the new collection.
255
+ #
256
+ # @option opts [Boolean] :capped (False) created a capped collection.
257
+ #
258
+ # @option opts [Integer] :size (Nil) If +capped+ is +true+,
259
+ # specifies the maximum number of bytes for the capped collection.
260
+ # If +false+, specifies the number of bytes allocated
261
+ # for the initial extent of the collection.
262
+ #
263
+ # @option opts [Integer] :max (Nil) If +capped+ is +true+, indicates
264
+ # the maximum number of records in a capped collection.
265
+ #
266
+ # @raise [MongoDBError] raised under two conditions:
267
+ # either we're in +strict+ mode and the collection
268
+ # already exists or collection creation fails on the server.
269
+ #
270
+ # @return [Mongo::Collection]
271
+ def create_collection(name, opts={})
272
+ if collection_names.include?(name.to_s)
273
+ if strict?
274
+ raise MongoDBError, "Collection #{name} already exists. " +
275
+ "Currently in strict mode."
276
+ else
277
+ return Collection.new(name, self, opts)
278
+ end
279
+ end
280
+
281
+ # Create a new collection.
282
+ oh = BSON::OrderedHash.new
283
+ oh[:create] = name
284
+ doc = command(oh.merge(opts || {}))
285
+ return Collection.new(name, self, :pk => @pk_factory) if ok?(doc)
286
+ raise MongoDBError, "Error creating collection: #{doc.inspect}"
287
+ end
288
+
289
+ # Get a collection by name.
290
+ #
291
+ # @param [String, Symbol] name the collection name.
292
+ # @param [Hash] opts any valid options that can be passed to Collection#new.
293
+ #
294
+ # @raise [MongoDBError] if collection does not already exist and we're in
295
+ # +strict+ mode.
296
+ #
297
+ # @return [Mongo::Collection]
298
+ def collection(name, opts={})
299
+ if strict? && !collection_names.include?(name.to_s)
300
+ raise Mongo::MongoDBError, "Collection #{name} doesn't exist. " +
301
+ "Currently in strict mode."
302
+ else
303
+ opts = opts.dup
304
+ opts[:safe] = opts.fetch(:safe, @safe)
305
+ opts.merge!(:pk => @pk_factory) unless opts[:pk]
306
+ Collection.new(name, self, opts)
307
+ end
308
+ end
309
+ alias_method :[], :collection
310
+
311
+ # Drop a collection by +name+.
312
+ #
313
+ # @param [String, Symbol] name
314
+ #
315
+ # @return [Boolean] +true+ on success or +false+ if the collection name doesn't exist.
316
+ def drop_collection(name)
317
+ return true unless collection_names.include?(name.to_s)
318
+
319
+ ok?(command(:drop => name))
320
+ end
321
+
322
+ # Run the getlasterror command with the specified replication options.
323
+ #
324
+ # @option opts [Boolean] :fsync (false)
325
+ # @option opts [Integer] :w (nil)
326
+ # @option opts [Integer] :wtimeout (nil)
327
+ #
328
+ # @return [Hash] the entire response to getlasterror.
329
+ #
330
+ # @raise [MongoDBError] if the operation fails.
331
+ def get_last_error(opts={})
332
+ cmd = BSON::OrderedHash.new
333
+ cmd[:getlasterror] = 1
334
+ cmd.merge!(opts)
335
+ doc = command(cmd, :check_response => false)
336
+ raise MongoDBError, "error retrieving last error: #{doc.inspect}" unless ok?(doc)
337
+ doc
338
+ end
339
+
340
+ # Return +true+ if an error was caused by the most recently executed
341
+ # database operation.
342
+ #
343
+ # @return [Boolean]
344
+ def error?
345
+ get_last_error['err'] != nil
346
+ end
347
+
348
+ # Get the most recent error to have occured on this database.
349
+ #
350
+ # This command only returns errors that have occured since the last call to
351
+ # DB#reset_error_history - returns +nil+ if there is no such error.
352
+ #
353
+ # @return [String, Nil] the text of the error or +nil+ if no error has occurred.
354
+ def previous_error
355
+ error = command(:getpreverror => 1)
356
+ if error["err"]
357
+ error
358
+ else
359
+ nil
360
+ end
361
+ end
362
+
363
+ # Reset the error history of this database
364
+ #
365
+ # Calls to DB#previous_error will only return errors that have occurred
366
+ # since the most recent call to this method.
367
+ #
368
+ # @return [Hash]
369
+ def reset_error_history
370
+ command(:reseterror => 1)
371
+ end
372
+
373
+ # Dereference a DBRef, returning the document it points to.
374
+ #
375
+ # @param [Mongo::DBRef] dbref
376
+ #
377
+ # @return [Hash] the document indicated by the db reference.
378
+ #
379
+ # @see http://www.mongodb.org/display/DOCS/DB+Ref MongoDB DBRef spec.
380
+ def dereference(dbref)
381
+ collection(dbref.namespace).find_one("_id" => dbref.object_id)
382
+ end
383
+
384
+ # Evaluate a JavaScript expression in MongoDB.
385
+ #
386
+ # @param [String, Code] code a JavaScript expression to evaluate server-side.
387
+ # @param [Integer, Hash] args any additional argument to be passed to the +code+ expression when
388
+ # it's run on the server.
389
+ #
390
+ # @return [String] the return value of the function.
391
+ def eval(code, *args)
392
+ if not code.is_a? BSON::Code
393
+ code = BSON::Code.new(code)
394
+ end
395
+
396
+ oh = BSON::OrderedHash.new
397
+ oh[:$eval] = code
398
+ oh[:args] = args
399
+ doc = command(oh)
400
+ doc['retval']
401
+ end
402
+
403
+ # Rename a collection.
404
+ #
405
+ # @param [String] from original collection name.
406
+ # @param [String] to new collection name.
407
+ #
408
+ # @return [True] returns +true+ on success.
409
+ #
410
+ # @raise MongoDBError if there's an error renaming the collection.
411
+ def rename_collection(from, to)
412
+ oh = BSON::OrderedHash.new
413
+ oh[:renameCollection] = "#{@name}.#{from}"
414
+ oh[:to] = "#{@name}.#{to}"
415
+ doc = DB.new('admin', @connection).command(oh, :check_response => false)
416
+ ok?(doc) || raise(MongoDBError, "Error renaming collection: #{doc.inspect}")
417
+ end
418
+
419
+ # Drop an index from a given collection. Normally called from
420
+ # Collection#drop_index or Collection#drop_indexes.
421
+ #
422
+ # @param [String] collection_name
423
+ # @param [String] index_name
424
+ #
425
+ # @return [True] returns +true+ on success.
426
+ #
427
+ # @raise MongoDBError if there's an error renaming the collection.
428
+ def drop_index(collection_name, index_name)
429
+ oh = BSON::OrderedHash.new
430
+ oh[:deleteIndexes] = collection_name
431
+ oh[:index] = index_name.to_s
432
+ doc = command(oh, :check_response => false)
433
+ ok?(doc) || raise(MongoDBError, "Error with drop_index command: #{doc.inspect}")
434
+ end
435
+
436
+ # Get information on the indexes for the given collection.
437
+ # Normally called by Collection#index_information.
438
+ #
439
+ # @param [String] collection_name
440
+ #
441
+ # @return [Hash] keys are index names and the values are lists of [key, direction] pairs
442
+ # defining the index.
443
+ def index_information(collection_name)
444
+ sel = {:ns => full_collection_name(collection_name)}
445
+ info = {}
446
+ Cursor.new(Collection.new(SYSTEM_INDEX_COLLECTION, self), :selector => sel).each do |index|
447
+ info[index['name']] = index
448
+ end
449
+ info
450
+ end
451
+
452
+ # Return stats on this database. Uses MongoDB's dbstats command.
453
+ #
454
+ # @return [Hash]
455
+ def stats
456
+ self.command({:dbstats => 1})
457
+ end
458
+
459
+ # Return +true+ if the supplied +doc+ contains an 'ok' field with the value 1.
460
+ #
461
+ # @param [Hash] doc
462
+ #
463
+ # @return [Boolean]
464
+ def ok?(doc)
465
+ Mongo::Support.ok?(doc)
466
+ end
467
+
468
+ # Send a command to the database.
469
+ #
470
+ # Note: DB commands must start with the "command" key. For this reason,
471
+ # any selector containing more than one key must be an OrderedHash.
472
+ #
473
+ # Note also that a command in MongoDB is just a kind of query
474
+ # that occurs on the system command collection ($cmd). Examine this method's implementation
475
+ # to see how it works.
476
+ #
477
+ # @param [OrderedHash, Hash] selector an OrderedHash, or a standard Hash with just one
478
+ # key, specifying the command to be performed. In Ruby 1.9, OrderedHash isn't necessary since
479
+ # hashes are ordered by default.
480
+ #
481
+ # @option opts [Boolean] :check_response (true) If +true+, raises an exception if the
482
+ # command fails.
483
+ # @option opts [Socket] :socket a socket to use for sending the command. This is mainly for internal use.
484
+ #
485
+ # @return [Hash]
486
+ #
487
+ # @core commands command_instance-method
488
+ def command(selector, opts={})
489
+ check_response = opts.fetch(:check_response, true)
490
+ socket = opts[:socket]
491
+ raise MongoArgumentError, "command must be given a selector" unless selector.is_a?(Hash) && !selector.empty?
492
+ if selector.keys.length > 1 && RUBY_VERSION < '1.9' && selector.class != BSON::OrderedHash
493
+ raise MongoArgumentError, "DB#command requires an OrderedHash when hash contains multiple keys"
494
+ end
495
+
496
+ begin
497
+ result = Cursor.new(system_command_collection,
498
+ :limit => -1, :selector => selector, :socket => socket).next_document
499
+ rescue OperationFailure => ex
500
+ raise OperationFailure, "Database command '#{selector.keys.first}' failed: #{ex.message}"
501
+ end
502
+
503
+ if result.nil?
504
+ raise OperationFailure, "Database command '#{selector.keys.first}' failed: returned null."
505
+ elsif (check_response && !ok?(result))
506
+ raise OperationFailure, "Database command '#{selector.keys.first}' failed: #{result.inspect}"
507
+ else
508
+ result
509
+ end
510
+ end
511
+
512
+ # A shortcut returning db plus dot plus collection name.
513
+ #
514
+ # @param [String] collection_name
515
+ #
516
+ # @return [String]
517
+ def full_collection_name(collection_name)
518
+ "#{@name}.#{collection_name}"
519
+ end
520
+
521
+ # The primary key factory object (or +nil+).
522
+ #
523
+ # @return [Object, Nil]
524
+ def pk_factory
525
+ @pk_factory
526
+ end
527
+
528
+ # Specify a primary key factory if not already set.
529
+ #
530
+ # @raise [MongoArgumentError] if the primary key factory has already been set.
531
+ def pk_factory=(pk_factory)
532
+ if @pk_factory
533
+ raise MongoArgumentError, "Cannot change primary key factory once it's been set"
534
+ end
535
+
536
+ @pk_factory = pk_factory
537
+ end
538
+
539
+ # Return the current database profiling level. If profiling is enabled, you can
540
+ # get the results using DB#profiling_info.
541
+ #
542
+ # @return [Symbol] :off, :slow_only, or :all
543
+ #
544
+ # @core profiling profiling_level-instance_method
545
+ def profiling_level
546
+ oh = BSON::OrderedHash.new
547
+ oh[:profile] = -1
548
+ doc = command(oh, :check_response => false)
549
+ raise "Error with profile command: #{doc.inspect}" unless ok?(doc) && doc['was'].kind_of?(Numeric)
550
+ case doc['was'].to_i
551
+ when 0
552
+ :off
553
+ when 1
554
+ :slow_only
555
+ when 2
556
+ :all
557
+ else
558
+ raise "Error: illegal profiling level value #{doc['was']}"
559
+ end
560
+ end
561
+
562
+ # Set this database's profiling level. If profiling is enabled, you can
563
+ # get the results using DB#profiling_info.
564
+ #
565
+ # @param [Symbol] level acceptable options are +:off+, +:slow_only+, or +:all+.
566
+ def profiling_level=(level)
567
+ oh = BSON::OrderedHash.new
568
+ oh[:profile] = case level
569
+ when :off
570
+ 0
571
+ when :slow_only
572
+ 1
573
+ when :all
574
+ 2
575
+ else
576
+ raise "Error: illegal profiling level value #{level}"
577
+ end
578
+ doc = command(oh, :check_response => false)
579
+ ok?(doc) || raise(MongoDBError, "Error with profile command: #{doc.inspect}")
580
+ end
581
+
582
+ # Get the current profiling information.
583
+ #
584
+ # @return [Array] a list of documents containing profiling information.
585
+ def profiling_info
586
+ Cursor.new(Collection.new(SYSTEM_PROFILE_COLLECTION, self), :selector => {}).to_a
587
+ end
588
+
589
+ # Validate a named collection.
590
+ #
591
+ # @param [String] name the collection name.
592
+ #
593
+ # @return [Hash] validation information.
594
+ #
595
+ # @raise [MongoDBError] if the command fails or there's a problem with the validation
596
+ # data, or if the collection is invalid.
597
+ def validate_collection(name)
598
+ cmd = BSON::OrderedHash.new
599
+ cmd[:validate] = name
600
+ cmd[:full] = true
601
+ doc = command(cmd, :check_response => false)
602
+ if !ok?(doc)
603
+ raise MongoDBError, "Error with validate command: #{doc.inspect}"
604
+ end
605
+ if (doc.has_key?('valid') && !doc['valid']) || (doc['result'] =~ /\b(exception|corrupt)\b/i)
606
+ raise MongoDBError, "Error: invalid collection #{name}: #{doc.inspect}"
607
+ end
608
+ doc
609
+ end
610
+
611
+ private
612
+
613
+ def system_command_collection
614
+ Collection.new(SYSTEM_COMMAND_COLLECTION, self)
615
+ end
616
+ end
617
+ end