kbaum-mongo 0.18.3p
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.
- data/LICENSE.txt +202 -0
- data/README.rdoc +339 -0
- data/Rakefile +138 -0
- data/bin/bson_benchmark.rb +59 -0
- data/bin/fail_if_no_c.rb +11 -0
- data/examples/admin.rb +42 -0
- data/examples/capped.rb +22 -0
- data/examples/cursor.rb +48 -0
- data/examples/gridfs.rb +88 -0
- data/examples/index_test.rb +126 -0
- data/examples/info.rb +31 -0
- data/examples/queries.rb +70 -0
- data/examples/simple.rb +24 -0
- data/examples/strict.rb +35 -0
- data/examples/types.rb +36 -0
- data/lib/mongo/collection.rb +609 -0
- data/lib/mongo/connection.rb +672 -0
- data/lib/mongo/cursor.rb +403 -0
- data/lib/mongo/db.rb +555 -0
- data/lib/mongo/exceptions.rb +66 -0
- data/lib/mongo/gridfs/chunk.rb +91 -0
- data/lib/mongo/gridfs/grid.rb +79 -0
- data/lib/mongo/gridfs/grid_file_system.rb +101 -0
- data/lib/mongo/gridfs/grid_io.rb +338 -0
- data/lib/mongo/gridfs/grid_store.rb +580 -0
- data/lib/mongo/gridfs.rb +25 -0
- data/lib/mongo/types/binary.rb +52 -0
- data/lib/mongo/types/code.rb +36 -0
- data/lib/mongo/types/dbref.rb +40 -0
- data/lib/mongo/types/min_max_keys.rb +58 -0
- data/lib/mongo/types/objectid.rb +180 -0
- data/lib/mongo/types/regexp_of_holding.rb +45 -0
- data/lib/mongo/util/bson_c.rb +18 -0
- data/lib/mongo/util/bson_ruby.rb +606 -0
- data/lib/mongo/util/byte_buffer.rb +222 -0
- data/lib/mongo/util/conversions.rb +87 -0
- data/lib/mongo/util/ordered_hash.rb +140 -0
- data/lib/mongo/util/server_version.rb +69 -0
- data/lib/mongo/util/support.rb +26 -0
- data/lib/mongo.rb +63 -0
- data/mongo-ruby-driver.gemspec +28 -0
- data/test/auxillary/autoreconnect_test.rb +42 -0
- data/test/binary_test.rb +15 -0
- data/test/bson_test.rb +427 -0
- data/test/byte_buffer_test.rb +81 -0
- data/test/chunk_test.rb +82 -0
- data/test/collection_test.rb +515 -0
- data/test/connection_test.rb +160 -0
- data/test/conversions_test.rb +120 -0
- data/test/cursor_test.rb +379 -0
- data/test/db_api_test.rb +780 -0
- data/test/db_connection_test.rb +16 -0
- data/test/db_test.rb +272 -0
- data/test/grid_file_system_test.rb +210 -0
- data/test/grid_io_test.rb +78 -0
- data/test/grid_store_test.rb +334 -0
- data/test/grid_test.rb +87 -0
- data/test/objectid_test.rb +125 -0
- data/test/ordered_hash_test.rb +172 -0
- data/test/replica/count_test.rb +34 -0
- data/test/replica/insert_test.rb +50 -0
- data/test/replica/pooled_insert_test.rb +54 -0
- data/test/replica/query_test.rb +39 -0
- data/test/slave_connection_test.rb +36 -0
- data/test/test_helper.rb +42 -0
- data/test/threading/test_threading_large_pool.rb +90 -0
- data/test/threading_test.rb +87 -0
- data/test/unit/collection_test.rb +61 -0
- data/test/unit/connection_test.rb +117 -0
- data/test/unit/cursor_test.rb +93 -0
- data/test/unit/db_test.rb +98 -0
- metadata +127 -0
data/lib/mongo/db.rb
ADDED
@@ -0,0 +1,555 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2010 10gen Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
# ++
|
16
|
+
|
17
|
+
require 'socket'
|
18
|
+
require 'timeout'
|
19
|
+
require 'digest/md5'
|
20
|
+
require 'thread'
|
21
|
+
|
22
|
+
module Mongo
|
23
|
+
|
24
|
+
# A MongoDB database.
|
25
|
+
class DB
|
26
|
+
|
27
|
+
SYSTEM_NAMESPACE_COLLECTION = "system.namespaces"
|
28
|
+
SYSTEM_INDEX_COLLECTION = "system.indexes"
|
29
|
+
SYSTEM_PROFILE_COLLECTION = "system.profile"
|
30
|
+
SYSTEM_USER_COLLECTION = "system.users"
|
31
|
+
SYSTEM_COMMAND_COLLECTION = "$cmd"
|
32
|
+
|
33
|
+
# Counter for generating unique request ids.
|
34
|
+
@@current_request_id = 0
|
35
|
+
|
36
|
+
# Strict mode enforces collection existence checks. When +true+,
|
37
|
+
# asking for a collection that does not exist, or trying to create a
|
38
|
+
# collection that already exists, raises an error.
|
39
|
+
#
|
40
|
+
# Strict mode is disabled by default, but enabled (+true+) at any time.
|
41
|
+
attr_writer :strict
|
42
|
+
|
43
|
+
# Returns the value of the +strict+ flag.
|
44
|
+
def strict?; @strict; end
|
45
|
+
|
46
|
+
# The name of the database.
|
47
|
+
attr_reader :name
|
48
|
+
|
49
|
+
# The Mongo::Connection instance connecting to the MongoDB server.
|
50
|
+
attr_reader :connection
|
51
|
+
|
52
|
+
# Instances of DB are normally obtained by calling Mongo#db.
|
53
|
+
#
|
54
|
+
# @param [String] db_name the database name.
|
55
|
+
# @param [Mongo::Connection] connection a connection object pointing to MongoDB. Note
|
56
|
+
# that databases are usually instantiated via the Connection class. See the examples below.
|
57
|
+
#
|
58
|
+
# @option options [Boolean] strict (False) If true, collections must exist to be accessed and must
|
59
|
+
# not exist to be created. See DB#collection and DB#create_collection.
|
60
|
+
#
|
61
|
+
# @option options [Object, #create_pk(doc)] pk (Mongo::ObjectID) A primary key factory object,
|
62
|
+
# which should take a hash and return a hash which merges the original hash with any primary key
|
63
|
+
# fields the factory wishes to inject. (NOTE: if the object already has a primary key,
|
64
|
+
# the factory should not inject a new key).
|
65
|
+
#
|
66
|
+
# @core databases constructor_details
|
67
|
+
def initialize(db_name, connection, options={})
|
68
|
+
@name = validate_db_name(db_name)
|
69
|
+
@connection = connection
|
70
|
+
@strict = options[:strict]
|
71
|
+
@pk_factory = options[:pk]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Authenticate with the given username and password. Note that mongod
|
75
|
+
# must be started with the --auth option for authentication to be enabled.
|
76
|
+
#
|
77
|
+
# @param [String] username
|
78
|
+
# @param [String] password
|
79
|
+
#
|
80
|
+
# @return [Boolean]
|
81
|
+
#
|
82
|
+
# @raise [AuthenticationError]
|
83
|
+
#
|
84
|
+
# @core authenticate authenticate-instance_method
|
85
|
+
def authenticate(username, password)
|
86
|
+
doc = command(:getnonce => 1)
|
87
|
+
raise "error retrieving nonce: #{doc}" unless ok?(doc)
|
88
|
+
nonce = doc['nonce']
|
89
|
+
|
90
|
+
auth = OrderedHash.new
|
91
|
+
auth['authenticate'] = 1
|
92
|
+
auth['user'] = username
|
93
|
+
auth['nonce'] = nonce
|
94
|
+
auth['key'] = Digest::MD5.hexdigest("#{nonce}#{username}#{hash_password(username, password)}")
|
95
|
+
ok?(command(auth)) ||
|
96
|
+
raise(MongoDBError::AuthenticationError, "Failed to authenticate user '#{username}' on db '#{self.name}'")
|
97
|
+
end
|
98
|
+
|
99
|
+
# Adds a user to this database for use with authentication. If the user already
|
100
|
+
# exists in the system, the password will be updated.
|
101
|
+
#
|
102
|
+
# @param [String] username
|
103
|
+
# @param [String] password
|
104
|
+
#
|
105
|
+
# @return [Hash] an object representing the user.
|
106
|
+
def add_user(username, password)
|
107
|
+
users = self[SYSTEM_USER_COLLECTION]
|
108
|
+
user = users.find_one({:user => username}) || {:user => username}
|
109
|
+
user['pwd'] = hash_password(username, password)
|
110
|
+
users.save(user)
|
111
|
+
return user
|
112
|
+
end
|
113
|
+
|
114
|
+
# Remove the given user from this database. Returns false if the user
|
115
|
+
# doesn't exist in the system.
|
116
|
+
#
|
117
|
+
# @param [String] username
|
118
|
+
#
|
119
|
+
# @return [Boolean]
|
120
|
+
def remove_user(username)
|
121
|
+
if self[SYSTEM_USER_COLLECTION].find_one({:user => username})
|
122
|
+
self[SYSTEM_USER_COLLECTION].remove({:user => username}, :safe => true)
|
123
|
+
else
|
124
|
+
return false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Deauthorizes use for this database for this connection.
|
129
|
+
#
|
130
|
+
# @raise [MongoDBError] if logging out fails.
|
131
|
+
#
|
132
|
+
# @return [Boolean]
|
133
|
+
def logout
|
134
|
+
doc = command(:logout => 1)
|
135
|
+
return true if ok?(doc)
|
136
|
+
raise MongoDBError, "error logging out: #{doc.inspect}"
|
137
|
+
end
|
138
|
+
|
139
|
+
# Get an array of collection names in this database.
|
140
|
+
#
|
141
|
+
# @return [Array]
|
142
|
+
def collection_names
|
143
|
+
names = collections_info.collect { |doc| doc['name'] || '' }
|
144
|
+
names = names.delete_if {|name| name.index(@name).nil? || name.index('$')}
|
145
|
+
names.map {|name| name.sub(@name + '.', '')}
|
146
|
+
end
|
147
|
+
|
148
|
+
# Get an array of Collection instances, one for each collection in this database.
|
149
|
+
#
|
150
|
+
# @return [Array<Mongo::Collection>]
|
151
|
+
def collections
|
152
|
+
collection_names.map do |collection_name|
|
153
|
+
Collection.new(self, collection_name)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Get info on system namespaces (collections). This method returns
|
158
|
+
# a cursor which can be iterated over. For each collection, a hash
|
159
|
+
# will be yielded containing a 'name' string and, optionally, an 'options' hash.
|
160
|
+
#
|
161
|
+
# @param [String] coll_name return info for the specifed collection only.
|
162
|
+
#
|
163
|
+
# @return [Mongo::Cursor]
|
164
|
+
def collections_info(coll_name=nil)
|
165
|
+
selector = {}
|
166
|
+
selector[:name] = full_collection_name(coll_name) if coll_name
|
167
|
+
Cursor.new(Collection.new(self, SYSTEM_NAMESPACE_COLLECTION), :selector => selector)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Create a collection.
|
171
|
+
#
|
172
|
+
# new collection. If +strict+ is true, will raise an error if
|
173
|
+
# collection +name+ already exists.
|
174
|
+
#
|
175
|
+
# @param [String] name the name of the new collection.
|
176
|
+
#
|
177
|
+
# @option options [Boolean] :capped (False) created a capped collection.
|
178
|
+
#
|
179
|
+
# @option options [Integer] :size (Nil) If +capped+ is +true+, specifies the maximum number of
|
180
|
+
# bytes for the capped collection. If +false+, specifies the number of bytes allocated
|
181
|
+
# for the initial extent of the collection.
|
182
|
+
#
|
183
|
+
# @option options [Integer] :max (Nil) If +capped+ is +true+, indicates the maximum number of records
|
184
|
+
# in a capped collection.
|
185
|
+
#
|
186
|
+
# @raise [MongoDBError] raised under two conditions: either we're in +strict+ mode and the collection
|
187
|
+
# already exists or collection creation fails on the server.
|
188
|
+
#
|
189
|
+
# @return [Mongo::Collection]
|
190
|
+
def create_collection(name, options={})
|
191
|
+
# Does the collection already exist?
|
192
|
+
if collection_names.include?(name)
|
193
|
+
if strict?
|
194
|
+
raise MongoDBError, "Collection #{name} already exists. Currently in strict mode."
|
195
|
+
else
|
196
|
+
return Collection.new(self, name)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Create a new collection.
|
201
|
+
oh = OrderedHash.new
|
202
|
+
oh[:create] = name
|
203
|
+
doc = command(oh.merge(options || {}))
|
204
|
+
ok = doc['ok']
|
205
|
+
return Collection.new(self, name, @pk_factory) if ok.kind_of?(Numeric) && (ok.to_i == 1 || ok.to_i == 0)
|
206
|
+
raise MongoDBError, "Error creating collection: #{doc.inspect}"
|
207
|
+
end
|
208
|
+
|
209
|
+
# Get a collection by name.
|
210
|
+
#
|
211
|
+
# @param [String] name the collection name.
|
212
|
+
#
|
213
|
+
# @raise [MongoDBError] if collection does not already exist and we're in +strict+ mode.
|
214
|
+
#
|
215
|
+
# @return [Mongo::Collection]
|
216
|
+
def collection(name)
|
217
|
+
return Collection.new(self, name, @pk_factory) if !strict? || collection_names.include?(name)
|
218
|
+
raise MongoDBError, "Collection #{name} doesn't exist. Currently in strict mode."
|
219
|
+
end
|
220
|
+
alias_method :[], :collection
|
221
|
+
|
222
|
+
# Drop a collection by +name+.
|
223
|
+
#
|
224
|
+
# @param [String] name
|
225
|
+
#
|
226
|
+
# @return [Boolean] True on success or if the collection names doesn't exist.
|
227
|
+
def drop_collection(name)
|
228
|
+
return true unless collection_names.include?(name)
|
229
|
+
|
230
|
+
ok?(command(:drop => name))
|
231
|
+
end
|
232
|
+
|
233
|
+
# Get the error message from the most recently executed database
|
234
|
+
# operation for this connection.
|
235
|
+
#
|
236
|
+
# @return [String, Nil] either the text describing the error or nil if no
|
237
|
+
# error has occurred.
|
238
|
+
def error
|
239
|
+
doc = command(:getlasterror => 1)
|
240
|
+
raise MongoDBError, "error retrieving last error: #{doc}" unless ok?(doc)
|
241
|
+
doc['err']
|
242
|
+
end
|
243
|
+
|
244
|
+
# Get status information from the last operation on this connection.
|
245
|
+
#
|
246
|
+
# @return [Hash] a hash representing the status of the last db op.
|
247
|
+
def last_status
|
248
|
+
command(:getlasterror => 1)
|
249
|
+
end
|
250
|
+
|
251
|
+
# Return +true+ if an error was caused by the most recently executed
|
252
|
+
# database operation.
|
253
|
+
#
|
254
|
+
# @return [Boolean]
|
255
|
+
def error?
|
256
|
+
error != nil
|
257
|
+
end
|
258
|
+
|
259
|
+
# Get the most recent error to have occured on this database.
|
260
|
+
#
|
261
|
+
# This command only returns errors that have occured since the last call to
|
262
|
+
# DB#reset_error_history - returns +nil+ if there is no such error.
|
263
|
+
#
|
264
|
+
# @return [String, Nil] the text of the error or +nil+ if no error has occurred.
|
265
|
+
def previous_error
|
266
|
+
error = command(:getpreverror => 1)
|
267
|
+
if error["err"]
|
268
|
+
error
|
269
|
+
else
|
270
|
+
nil
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Reset the error history of this database
|
275
|
+
#
|
276
|
+
# Calls to DB#previous_error will only return errors that have occurred
|
277
|
+
# since the most recent call to this method.
|
278
|
+
#
|
279
|
+
# @return [Hash]
|
280
|
+
def reset_error_history
|
281
|
+
command(:reseterror => 1)
|
282
|
+
end
|
283
|
+
|
284
|
+
# @deprecated please use Collection#find to create queries.
|
285
|
+
#
|
286
|
+
# Returns a Cursor over the query results.
|
287
|
+
#
|
288
|
+
# Note that the query gets sent lazily; the cursor calls
|
289
|
+
# Connection#send_message when needed. If the caller never requests an
|
290
|
+
# object from the cursor, the query never gets sent.
|
291
|
+
def query(collection, query, admin=false)
|
292
|
+
Cursor.new(self, collection, query, admin)
|
293
|
+
end
|
294
|
+
|
295
|
+
# Dereference a DBRef, returning the document it points to.
|
296
|
+
#
|
297
|
+
# @param [Mongo::DBRef] dbref
|
298
|
+
#
|
299
|
+
# @return [Hash] the document indicated by the db reference.
|
300
|
+
#
|
301
|
+
# @see http://www.mongodb.org/display/DOCS/DB+Ref MongoDB DBRef spec.
|
302
|
+
def dereference(dbref)
|
303
|
+
collection(dbref.namespace).find_one("_id" => dbref.object_id)
|
304
|
+
end
|
305
|
+
|
306
|
+
# Evaluate a JavaScript expression in MongoDB.
|
307
|
+
#
|
308
|
+
# @param [String, Code] code a JavaScript expression to evaluate server-side.
|
309
|
+
# @param [Integer, Hash] args any additional argument to be passed to the +code+ expression when
|
310
|
+
# it's run on the server.
|
311
|
+
#
|
312
|
+
# @return [String] the return value of the function.
|
313
|
+
def eval(code, *args)
|
314
|
+
if not code.is_a? Code
|
315
|
+
code = Code.new(code)
|
316
|
+
end
|
317
|
+
|
318
|
+
oh = OrderedHash.new
|
319
|
+
oh[:$eval] = code
|
320
|
+
oh[:args] = args
|
321
|
+
doc = command(oh)
|
322
|
+
return doc['retval'] if ok?(doc)
|
323
|
+
raise OperationFailure, "eval failed: #{doc['errmsg']}"
|
324
|
+
end
|
325
|
+
|
326
|
+
# Rename a collection.
|
327
|
+
#
|
328
|
+
# @param [String] from original collection name.
|
329
|
+
# @param [String] to new collection name.
|
330
|
+
#
|
331
|
+
# @return [True] returns +true+ on success.
|
332
|
+
#
|
333
|
+
# @raise MongoDBError if there's an error renaming the collection.
|
334
|
+
def rename_collection(from, to)
|
335
|
+
oh = OrderedHash.new
|
336
|
+
oh[:renameCollection] = "#{@name}.#{from}"
|
337
|
+
oh[:to] = "#{@name}.#{to}"
|
338
|
+
doc = command(oh, true)
|
339
|
+
ok?(doc) || raise(MongoDBError, "Error renaming collection: #{doc.inspect}")
|
340
|
+
end
|
341
|
+
|
342
|
+
# Drop an index from a given collection. Normally called from
|
343
|
+
# Collection#drop_index or Collection#drop_indexes.
|
344
|
+
#
|
345
|
+
# @param [String] collection_name
|
346
|
+
# @param [String] index_name
|
347
|
+
#
|
348
|
+
# @return [True] returns +true+ on success.
|
349
|
+
#
|
350
|
+
# @raise MongoDBError if there's an error renaming the collection.
|
351
|
+
def drop_index(collection_name, index_name)
|
352
|
+
oh = OrderedHash.new
|
353
|
+
oh[:deleteIndexes] = collection_name
|
354
|
+
oh[:index] = index_name
|
355
|
+
doc = command(oh)
|
356
|
+
ok?(doc) || raise(MongoDBError, "Error with drop_index command: #{doc.inspect}")
|
357
|
+
end
|
358
|
+
|
359
|
+
# Get information on the indexes for the given collection.
|
360
|
+
# Normally called by Collection#index_information.
|
361
|
+
#
|
362
|
+
# @param [String] collection_name
|
363
|
+
#
|
364
|
+
# @return [Hash] keys are index names and the values are lists of [key, direction] pairs
|
365
|
+
# defining the index.
|
366
|
+
def index_information(collection_name)
|
367
|
+
sel = {:ns => full_collection_name(collection_name)}
|
368
|
+
info = {}
|
369
|
+
Cursor.new(Collection.new(self, SYSTEM_INDEX_COLLECTION), :selector => sel).each { |index|
|
370
|
+
info[index['name']] = index['key'].map {|k| k}
|
371
|
+
}
|
372
|
+
info
|
373
|
+
end
|
374
|
+
|
375
|
+
# Create a new index on the given collection.
|
376
|
+
# Normally called by Collection#create_index.
|
377
|
+
#
|
378
|
+
# @param [String] collection_name
|
379
|
+
# @param [String, Array] field_or_spec either either a single field name
|
380
|
+
# or an array of [field name, direction] pairs. Directions should be specified as
|
381
|
+
# Mongo::ASCENDING or Mongo::DESCENDING.
|
382
|
+
# @param [Boolean] unique if +true+, the created index will enforce a uniqueness constraint.
|
383
|
+
#
|
384
|
+
# @return [String] the name of the index created.
|
385
|
+
def create_index(collection_name, field_or_spec, unique=false)
|
386
|
+
self.collection(collection_name).create_index(field_or_spec, unique)
|
387
|
+
end
|
388
|
+
|
389
|
+
# Return +true+ if the supplied +doc+ contains an 'ok' field with the value 1.
|
390
|
+
#
|
391
|
+
# @param [Hash] doc
|
392
|
+
#
|
393
|
+
# @return [Boolean]
|
394
|
+
def ok?(doc)
|
395
|
+
ok = doc['ok']
|
396
|
+
ok.kind_of?(Numeric) && ok.to_i == 1
|
397
|
+
end
|
398
|
+
|
399
|
+
# Send a command to the database.
|
400
|
+
#
|
401
|
+
# Note: DB commands must start with the "command" key. For this reason,
|
402
|
+
# any selector containing more than one key must be an OrderedHash.
|
403
|
+
#
|
404
|
+
# It may be of interest hat a command in MongoDB is technically a kind of query
|
405
|
+
# that occurs on the system command collection ($cmd).
|
406
|
+
#
|
407
|
+
# @param [OrderedHash, Hash] selector an OrderedHash, or a standard Hash with just one
|
408
|
+
# key, specifying the command to be performed.
|
409
|
+
#
|
410
|
+
# @param [Boolean] admin If +true+, the command will be executed on the admin
|
411
|
+
# collection.
|
412
|
+
#
|
413
|
+
# @param [Boolean] check_response If +true+, will raise an exception if the
|
414
|
+
# command fails.
|
415
|
+
#
|
416
|
+
# @param [Socket] sock a socket to use. This is mainly for internal use.
|
417
|
+
#
|
418
|
+
# @return [Hash]
|
419
|
+
#
|
420
|
+
# @core commands command_instance-method
|
421
|
+
def command(selector, admin=false, check_response=false, sock=nil)
|
422
|
+
raise MongoArgumentError, "command must be given a selector" unless selector.is_a?(Hash) && !selector.empty?
|
423
|
+
if selector.class.eql?(Hash) && selector.keys.length > 1
|
424
|
+
raise MongoArgumentError, "DB#command requires an OrderedHash when hash contains multiple keys"
|
425
|
+
end
|
426
|
+
|
427
|
+
result = Cursor.new(system_command_collection, :admin => admin,
|
428
|
+
:limit => -1, :selector => selector, :socket => sock).next_document
|
429
|
+
|
430
|
+
if check_response && !ok?(result)
|
431
|
+
raise OperationFailure, "Database command '#{selector.keys.first}' failed."
|
432
|
+
else
|
433
|
+
result
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
# A shortcut returning db plus dot plus collection name.
|
438
|
+
#
|
439
|
+
# @param [String] collection_name
|
440
|
+
#
|
441
|
+
# @return [String]
|
442
|
+
def full_collection_name(collection_name)
|
443
|
+
"#{@name}.#{collection_name}"
|
444
|
+
end
|
445
|
+
|
446
|
+
# The primary key factory object (or +nil+).
|
447
|
+
#
|
448
|
+
# @return [Object, Nil]
|
449
|
+
def pk_factory
|
450
|
+
@pk_factory
|
451
|
+
end
|
452
|
+
|
453
|
+
# Specify a primary key factory if not already set.
|
454
|
+
#
|
455
|
+
# @raise [MongoArgumentError] if the primary key factory has already been set.
|
456
|
+
def pk_factory=(pk_factory)
|
457
|
+
if @pk_factory
|
458
|
+
raise MongoArgumentError, "Cannot change primary key factory once it's been set"
|
459
|
+
end
|
460
|
+
|
461
|
+
@pk_factory = pk_factory
|
462
|
+
end
|
463
|
+
|
464
|
+
# Return the current database profiling level. If profiling is enabled, you can
|
465
|
+
# get the results using DB#profiling_info.
|
466
|
+
#
|
467
|
+
# @return [Symbol] :off, :slow_only, or :all
|
468
|
+
#
|
469
|
+
# @core profiling profiling_level-instance_method
|
470
|
+
def profiling_level
|
471
|
+
oh = OrderedHash.new
|
472
|
+
oh[:profile] = -1
|
473
|
+
doc = command(oh)
|
474
|
+
raise "Error with profile command: #{doc.inspect}" unless ok?(doc) && doc['was'].kind_of?(Numeric)
|
475
|
+
case doc['was'].to_i
|
476
|
+
when 0
|
477
|
+
:off
|
478
|
+
when 1
|
479
|
+
:slow_only
|
480
|
+
when 2
|
481
|
+
:all
|
482
|
+
else
|
483
|
+
raise "Error: illegal profiling level value #{doc['was']}"
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
# Set this database's profiling level. If profiling is enabled, you can
|
488
|
+
# get the results using DB#profiling_info.
|
489
|
+
#
|
490
|
+
# @param [Symbol] level acceptable options are +:off+, +:slow_only+, or +:all+.
|
491
|
+
def profiling_level=(level)
|
492
|
+
oh = OrderedHash.new
|
493
|
+
oh[:profile] = case level
|
494
|
+
when :off
|
495
|
+
0
|
496
|
+
when :slow_only
|
497
|
+
1
|
498
|
+
when :all
|
499
|
+
2
|
500
|
+
else
|
501
|
+
raise "Error: illegal profiling level value #{level}"
|
502
|
+
end
|
503
|
+
doc = command(oh)
|
504
|
+
ok?(doc) || raise(MongoDBError, "Error with profile command: #{doc.inspect}")
|
505
|
+
end
|
506
|
+
|
507
|
+
# Get the current profiling information.
|
508
|
+
#
|
509
|
+
# @return [Array] a list of documents containing profiling information.
|
510
|
+
def profiling_info
|
511
|
+
Cursor.new(Collection.new(self, DB::SYSTEM_PROFILE_COLLECTION), :selector => {}).to_a
|
512
|
+
end
|
513
|
+
|
514
|
+
# Validate a named collection.
|
515
|
+
#
|
516
|
+
# @param [String] name the collection name.
|
517
|
+
#
|
518
|
+
# @return [Hash] validation information.
|
519
|
+
#
|
520
|
+
# @raise [MongoDBError] if the command fails or there's a problem with the validation
|
521
|
+
# data, or if the collection is invalid.
|
522
|
+
def validate_collection(name)
|
523
|
+
doc = command(:validate => name)
|
524
|
+
raise MongoDBError, "Error with validate command: #{doc.inspect}" unless ok?(doc)
|
525
|
+
result = doc['result']
|
526
|
+
raise MongoDBError, "Error with validation data: #{doc.inspect}" unless result.kind_of?(String)
|
527
|
+
raise MongoDBError, "Error: invalid collection #{name}: #{doc.inspect}" if result =~ /\b(exception|corrupt)\b/i
|
528
|
+
doc
|
529
|
+
end
|
530
|
+
|
531
|
+
private
|
532
|
+
|
533
|
+
def hash_password(username, plaintext)
|
534
|
+
Digest::MD5.hexdigest("#{username}:mongo:#{plaintext}")
|
535
|
+
end
|
536
|
+
|
537
|
+
def system_command_collection
|
538
|
+
Collection.new(self, SYSTEM_COMMAND_COLLECTION)
|
539
|
+
end
|
540
|
+
|
541
|
+
def validate_db_name(db_name)
|
542
|
+
unless [String, Symbol].include?(db_name.class)
|
543
|
+
raise TypeError, "db_name must be a string or symbol"
|
544
|
+
end
|
545
|
+
|
546
|
+
[" ", ".", "$", "/", "\\"].each do |invalid_char|
|
547
|
+
if db_name.include? invalid_char
|
548
|
+
raise InvalidName, "database names cannot contain the character '#{invalid_char}'"
|
549
|
+
end
|
550
|
+
end
|
551
|
+
raise InvalidName, "database name cannot be the empty string" if db_name.empty?
|
552
|
+
db_name
|
553
|
+
end
|
554
|
+
end
|
555
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2010 10gen Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
# ++
|
16
|
+
|
17
|
+
module Mongo
|
18
|
+
# Generic Mongo Ruby Driver exception class.
|
19
|
+
class MongoRubyError < StandardError; end
|
20
|
+
|
21
|
+
# Raised when MongoDB itself has returned an error.
|
22
|
+
class MongoDBError < RuntimeError; end
|
23
|
+
|
24
|
+
# Raised when configuration options cause connections, queries, etc., to fail.
|
25
|
+
class ConfigurationError < MongoRubyError; end
|
26
|
+
|
27
|
+
# Raised with fatal errors to GridFS.
|
28
|
+
class GridError < MongoRubyError; end
|
29
|
+
|
30
|
+
# Raised when invalid arguments are sent to Mongo Ruby methods.
|
31
|
+
class MongoArgumentError < MongoRubyError; end
|
32
|
+
|
33
|
+
# Raised when given a string is not valid utf-8 (Ruby 1.8 only).
|
34
|
+
class InvalidStringEncoding < MongoRubyError; end
|
35
|
+
|
36
|
+
# Raised when attempting to initialize an invalid ObjectID.
|
37
|
+
class InvalidObjectID < MongoRubyError; end
|
38
|
+
|
39
|
+
# Raised on failures in connection to the database server.
|
40
|
+
class ConnectionError < MongoRubyError; end
|
41
|
+
|
42
|
+
# Raised on failures in connection to the database server.
|
43
|
+
class ConnectionTimeoutError < MongoRubyError; end
|
44
|
+
|
45
|
+
# Raised when trying to insert a document that exceeds the 4MB limit or
|
46
|
+
# when the document contains objects that can't be serialized as BSON.
|
47
|
+
class InvalidDocument < MongoDBError; end
|
48
|
+
|
49
|
+
# Raised when authentication fails.
|
50
|
+
class AuthenticationError < MongoDBError; end
|
51
|
+
|
52
|
+
# Raised when a database operation fails.
|
53
|
+
class OperationFailure < MongoDBError; end
|
54
|
+
|
55
|
+
# Raised when a connection operation fails.
|
56
|
+
class ConnectionFailure < MongoDBError; end
|
57
|
+
|
58
|
+
# Raised when a client attempts to perform an invalid operation.
|
59
|
+
class InvalidOperation < MongoDBError; end
|
60
|
+
|
61
|
+
# Raised when an invalid name is used.
|
62
|
+
class InvalidName < RuntimeError; end
|
63
|
+
|
64
|
+
# Raised when the client supplies an invalid value to sort by.
|
65
|
+
class InvalidSortValueError < MongoRubyError; end
|
66
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2010 10gen Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
# ++
|
16
|
+
|
17
|
+
require 'mongo/types/objectid'
|
18
|
+
require 'mongo/util/byte_buffer'
|
19
|
+
require 'mongo/util/ordered_hash'
|
20
|
+
|
21
|
+
module GridFS
|
22
|
+
|
23
|
+
# A chunk stores a portion of GridStore data.
|
24
|
+
# @deprecated
|
25
|
+
class Chunk
|
26
|
+
|
27
|
+
DEFAULT_CHUNK_SIZE = 1024 * 256
|
28
|
+
|
29
|
+
attr_reader :object_id, :chunk_number
|
30
|
+
attr_accessor :data
|
31
|
+
|
32
|
+
def initialize(file, mongo_object={})
|
33
|
+
@file = file
|
34
|
+
@object_id = mongo_object['_id'] || Mongo::ObjectID.new
|
35
|
+
@chunk_number = mongo_object['n'] || 0
|
36
|
+
|
37
|
+
@data = ByteBuffer.new
|
38
|
+
case mongo_object['data']
|
39
|
+
when String
|
40
|
+
mongo_object['data'].each_byte { |b| @data.put(b) }
|
41
|
+
when ByteBuffer
|
42
|
+
@data.put_array(mongo_object['data'].to_a)
|
43
|
+
when Array
|
44
|
+
@data.put_array(mongo_object['data'])
|
45
|
+
when nil
|
46
|
+
else
|
47
|
+
raise "illegal chunk format; data is #{mongo_object['data'] ? (' ' + mongo_object['data'].class.name) : 'nil'}"
|
48
|
+
end
|
49
|
+
@data.rewind
|
50
|
+
end
|
51
|
+
|
52
|
+
def pos; @data.position; end
|
53
|
+
def pos=(pos); @data.position = pos; end
|
54
|
+
def eof?; !@data.more?; end
|
55
|
+
|
56
|
+
def size; @data.size; end
|
57
|
+
alias_method :length, :size
|
58
|
+
|
59
|
+
def truncate
|
60
|
+
if @data.position < @data.length
|
61
|
+
curr_data = @data
|
62
|
+
@data = ByteBuffer.new
|
63
|
+
@data.put_array(curr_data.to_a[0...curr_data.position])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def getc
|
68
|
+
@data.more? ? @data.get : nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def putc(byte)
|
72
|
+
@data.put(byte)
|
73
|
+
end
|
74
|
+
|
75
|
+
def save
|
76
|
+
coll = @file.chunk_collection
|
77
|
+
coll.remove({'_id' => @object_id})
|
78
|
+
coll.insert(to_mongo_object)
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_mongo_object
|
82
|
+
h = OrderedHash.new
|
83
|
+
h['_id'] = @object_id
|
84
|
+
h['files_id'] = @file.files_id
|
85
|
+
h['n'] = @chunk_number
|
86
|
+
h['data'] = data
|
87
|
+
h
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|