jonbell-mongo 1.3.1.2

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 (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