mongo 0.17.1 → 0.18

Sign up to get free protection for your applications and to get access to all the features.
@@ -32,6 +32,7 @@ module Mongo
32
32
  def initialize(collection, options={})
33
33
  @db = collection.db
34
34
  @collection = collection
35
+ @connection = @db.connection
35
36
 
36
37
  @selector = convert_selector_for_query(options[:selector])
37
38
  @fields = convert_fields_for_query(options[:fields])
@@ -43,8 +44,9 @@ module Mongo
43
44
  @snapshot = options[:snapshot]
44
45
  @timeout = options[:timeout] || false
45
46
  @explain = options[:explain]
47
+ @socket = options[:socket]
46
48
 
47
- @full_collection_name = "#{@collection.db.name}.#{@collection.name}"
49
+ @full_collection_name = "#{@collection.db.name}.#{@collection.name}"
48
50
  @cache = []
49
51
  @closed = false
50
52
  @query_run = false
@@ -63,9 +65,12 @@ module Mongo
63
65
  # pair but it has died or something like that) then we close that
64
66
  # connection. If the db has auto connect option and a pair of
65
67
  # servers, next request will re-open on master server.
66
- @db.close if err == "not master"
68
+ if err == "not master"
69
+ raise ConnectionFailure, err
70
+ @connection.close
71
+ end
67
72
 
68
- raise err
73
+ raise OperationFailure, err
69
74
  end
70
75
 
71
76
  o
@@ -80,7 +85,7 @@ module Mongo
80
85
  command = OrderedHash["count", @collection.name,
81
86
  "query", @selector,
82
87
  "fields", @fields]
83
- response = @db.db_command(command)
88
+ response = @db.command(command)
84
89
  return response['n'].to_i if response['ok'] == 1
85
90
  return 0 if response['errmsg'] == "ns missing"
86
91
  raise OperationFailure, "Count failed: #{response['errmsg']}"
@@ -196,7 +201,7 @@ module Mongo
196
201
  message.put_int(0)
197
202
  message.put_int(1)
198
203
  message.put_long(@cursor_id)
199
- @db.send_message_with_operation(Mongo::Constants::OP_KILL_CURSORS, message, "cursor.close()")
204
+ @connection.send_message(Mongo::Constants::OP_KILL_CURSORS, message, "cursor.close()")
200
205
  end
201
206
  @cursor_id = 0
202
207
  @closed = true
@@ -209,7 +214,7 @@ module Mongo
209
214
  # See http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-Mongo::Constants::OPQUERY
210
215
  def query_opts
211
216
  timeout = @timeout ? 0 : Mongo::Constants::OP_QUERY_NO_CURSOR_TIMEOUT
212
- slave_ok = @db.slave_ok? ? Mongo::Constants::OP_QUERY_SLAVE_OK : 0
217
+ slave_ok = @connection.slave_ok? ? Mongo::Constants::OP_QUERY_SLAVE_OK : 0
213
218
  slave_ok + timeout
214
219
  end
215
220
 
@@ -291,19 +296,19 @@ module Mongo
291
296
 
292
297
  # Cursor id.
293
298
  message.put_long(@cursor_id)
294
- results, @n_received, @cursor_id = @db.receive_message_with_operation(Mongo::Constants::OP_GET_MORE, message, "cursor.get_more()")
299
+ results, @n_received, @cursor_id = @connection.receive_message(Mongo::Constants::OP_GET_MORE, message, "cursor.get_more()", @socket)
295
300
  @cache += results
296
301
  close_cursor_if_query_complete
297
302
  end
298
303
 
299
- # Run query first time we request an object from the wire
304
+ # Run query the first time we request an object from the wire
300
305
  def send_query_if_needed
301
306
  if @query_run
302
307
  false
303
308
  else
304
309
  message = construct_query_message
305
- results, @n_received, @cursor_id = @db.receive_message_with_operation(Mongo::Constants::OP_QUERY, message,
306
- (query_log_message if @db.logger))
310
+ results, @n_received, @cursor_id = @connection.receive_message(Mongo::Constants::OP_QUERY, message,
311
+ (query_log_message if @connection.logger), @socket)
307
312
  @cache += results
308
313
  @query_run = true
309
314
  close_cursor_if_query_complete
@@ -344,14 +349,11 @@ module Mongo
344
349
 
345
350
  def formatted_order_clause
346
351
  case @order
347
- when String then string_as_sort_parameters(@order)
348
- when Symbol then symbol_as_sort_parameters(@order)
349
- when Array then array_as_sort_parameters(@order)
350
- when Hash # Should be an ordered hash, but this message doesn't care
351
- warn_if_deprecated(@order)
352
- @order
352
+ when String, Symbol then string_as_sort_parameters(@order)
353
+ when Array then array_as_sort_parameters(@order)
353
354
  else
354
- raise InvalidSortValueError, "Illegal order_by, '#{@order.class.name}'; must be String, Array, Hash, or OrderedHash"
355
+ raise InvalidSortValueError, "Illegal sort clause, '#{@order.class.name}'; must be of the form " +
356
+ "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"
355
357
  end
356
358
  end
357
359
 
@@ -15,6 +15,7 @@
15
15
  # ++
16
16
 
17
17
  require 'socket'
18
+ require 'timeout'
18
19
  require 'digest/md5'
19
20
  require 'thread'
20
21
  require 'mongo/collection'
@@ -26,8 +27,6 @@ module Mongo
26
27
  # A Mongo database.
27
28
  class DB
28
29
 
29
- STANDARD_HEADER_SIZE = 16
30
- RESPONSE_HEADER_SIZE = 20
31
30
  SYSTEM_NAMESPACE_COLLECTION = "system.namespaces"
32
31
  SYSTEM_INDEX_COLLECTION = "system.indexes"
33
32
  SYSTEM_PROFILE_COLLECTION = "system.profile"
@@ -38,11 +37,10 @@ module Mongo
38
37
  @@current_request_id = 0
39
38
 
40
39
  # Strict mode enforces collection existence checks. When +true+,
41
- # asking for a collection that does not exist or trying to create a
42
- # collection that already exists raises an error.
40
+ # asking for a collection that does not exist, or trying to create a
41
+ # collection that already exists, raises an error.
43
42
  #
44
- # Strict mode is off (+false+) by default. Its value can be changed at
45
- # any time.
43
+ # Strict mode is disabled by default, but enabled (+true+) at any time.
46
44
  attr_writer :strict
47
45
 
48
46
  # Returns the value of the +strict+ flag.
@@ -51,27 +49,16 @@ module Mongo
51
49
  # The name of the database.
52
50
  attr_reader :name
53
51
 
52
+ # The Mongo::Connection instance connecting to the MongoDB server.
54
53
  attr_reader :connection
55
-
56
- # Host to which we are currently connected.
57
- attr_reader :host
58
- # Port to which we are currently connected.
59
- attr_reader :port
60
-
54
+
61
55
  # An array of [host, port] pairs.
62
56
  attr_reader :nodes
63
57
 
64
- # The database's socket. For internal (and Cursor) use only.
65
- attr_reader :socket
66
-
67
- # The logger instance if :logger is passed to initialize
58
+ # The logger instance if :logger is passed to initialize.
68
59
  attr_reader :logger
69
60
 
70
- def slave_ok?; @slave_ok; end
71
- def auto_reconnect?; @auto_reconnect; end
72
-
73
- # A primary key factory object (or +nil+). See the README.doc file or
74
- # DB#new for details.
61
+ # The primary key factory object (or +nil+).
75
62
  attr_reader :pk_factory
76
63
 
77
64
  def pk_factory=(pk_factory)
@@ -91,8 +78,7 @@ module Mongo
91
78
  # Options:
92
79
  #
93
80
  # :strict :: If true, collections must exist to be accessed and must
94
- # not exist to be created. See #collection and
95
- # #create_collection.
81
+ # not exist to be created. See #collection and #create_collection.
96
82
  #
97
83
  # :pk :: A primary key factory object that must respond to :create_pk,
98
84
  # which should take a hash and return a hash which merges the
@@ -109,80 +95,22 @@ module Mongo
109
95
  # see if the server is the master. If it is not, an error
110
96
  # is thrown.
111
97
  #
112
- # :auto_reconnect :: If the connection gets closed (for example, we
113
- # have a server pair and saw the "not master"
114
- # error, which closes the connection), then
115
- # automatically try to reconnect to the master or
116
- # to the single server we have been given. Defaults
117
- # to +false+.
118
98
  # :logger :: Optional Logger instance to which driver usage information
119
99
  # will be logged.
120
100
  #
121
- # When a DB object first connects to a pair, it will find the master
122
- # instance and connect to that one. On socket error or if we recieve a
123
- # "not master" error, we again find the master of the pair.
124
- def initialize(db_name, nodes, options={})
125
- case db_name
126
- when Symbol, String
127
- else
128
- raise TypeError, "db_name must be a string or symbol"
129
- end
130
-
131
- [" ", ".", "$", "/", "\\"].each do |invalid_char|
132
- if db_name.include? invalid_char
133
- raise InvalidName, "database names cannot contain the character '#{invalid_char}'"
134
- end
135
- end
136
- if db_name.empty?
137
- raise InvalidName, "database name cannot be the empty string"
138
- end
139
-
140
- @connection = options[:connection]
141
-
142
- @name, @nodes = db_name, nodes
143
- @strict = options[:strict]
101
+ # :auto_reconnect :: DEPRECATED. See http://www.mongodb.org/display/DOCS/Replica+Pairs+in+Ruby
102
+ def initialize(db_name, connection, options={})
103
+ @name = validate_db_name(db_name)
104
+ @connection = connection
105
+ @strict = options[:strict]
144
106
  @pk_factory = options[:pk]
145
- @slave_ok = options[:slave_ok] && @nodes.length == 1 # only OK if one node
146
- @auto_reconnect = options[:auto_reconnect]
147
- @semaphore = Mutex.new
148
- @socket = nil
149
- @logger = options[:logger]
150
- connect_to_master
151
- end
152
-
153
- def connect_to_master
154
- close if @socket
155
- @host = @port = nil
156
- @nodes.detect { |hp|
157
- @host, @port = *hp
158
- begin
159
- @socket = TCPSocket.new(@host, @port)
160
- @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
161
-
162
- # Check for master. Can't call master? because it uses mutex,
163
- # which may already be in use during this call.
164
- semaphore_is_locked = @semaphore.locked?
165
- @semaphore.unlock if semaphore_is_locked
166
- is_master = master?
167
- @semaphore.lock if semaphore_is_locked
168
-
169
- if !@slave_ok && !is_master
170
- raise ConfigurationError, "Trying to connect directly to slave; if this is what you want, specify :slave_ok => true."
171
- end
172
- @slave_ok || is_master
173
- rescue SocketError, SystemCallError, IOError => ex
174
- close if @socket
175
- false
176
- end
177
- }
178
- raise "error: failed to connect to any given host:port" unless @socket
179
107
  end
180
108
 
181
109
  # Returns true if +username+ has +password+ in
182
110
  # +SYSTEM_USER_COLLECTION+. +name+ is username, +password+ is
183
111
  # plaintext password.
184
112
  def authenticate(username, password)
185
- doc = db_command(:getnonce => 1)
113
+ doc = command(:getnonce => 1)
186
114
  raise "error retrieving nonce: #{doc}" unless ok?(doc)
187
115
  nonce = doc['nonce']
188
116
 
@@ -191,12 +119,12 @@ module Mongo
191
119
  auth['user'] = username
192
120
  auth['nonce'] = nonce
193
121
  auth['key'] = Digest::MD5.hexdigest("#{nonce}#{username}#{hash_password(username, password)}")
194
- ok?(db_command(auth))
122
+ ok?(command(auth))
195
123
  end
196
124
 
197
125
  # Deauthorizes use for this database for this connection.
198
126
  def logout
199
- doc = db_command(:logout => 1)
127
+ doc = command(:logout => 1)
200
128
  raise "error logging out: #{doc.inspect}" unless ok?(doc)
201
129
  end
202
130
 
@@ -250,7 +178,7 @@ module Mongo
250
178
  # Create new collection
251
179
  oh = OrderedHash.new
252
180
  oh[:create] = name
253
- doc = db_command(oh.merge(options || {}))
181
+ doc = command(oh.merge(options || {}))
254
182
  ok = doc['ok']
255
183
  return Collection.new(self, name, @pk_factory) if ok.kind_of?(Numeric) && (ok.to_i == 1 || ok.to_i == 0)
256
184
  raise "Error creating collection: #{doc.inspect}"
@@ -274,20 +202,20 @@ module Mongo
274
202
  def drop_collection(name)
275
203
  return true unless collection_names.include?(name)
276
204
 
277
- ok?(db_command(:drop => name))
205
+ ok?(command(:drop => name))
278
206
  end
279
207
 
280
208
  # Returns the error message from the most recently executed database
281
209
  # operation for this connection, or +nil+ if there was no error.
282
210
  def error
283
- doc = db_command(:getlasterror => 1)
211
+ doc = command(:getlasterror => 1)
284
212
  raise "error retrieving last error: #{doc}" unless ok?(doc)
285
213
  doc['err']
286
214
  end
287
215
 
288
216
  # Get status information from the last operation on this connection.
289
217
  def last_status
290
- db_command(:getlasterror => 1)
218
+ command(:getlasterror => 1)
291
219
  end
292
220
 
293
221
  # Returns +true+ if an error was caused by the most recently executed
@@ -301,7 +229,7 @@ module Mongo
301
229
  # Only returns errors that have occured since the last call to
302
230
  # DB#reset_error_history - returns +nil+ if there is no such error.
303
231
  def previous_error
304
- error = db_command(:getpreverror => 1)
232
+ error = command(:getpreverror => 1)
305
233
  if error["err"]
306
234
  error
307
235
  else
@@ -314,52 +242,7 @@ module Mongo
314
242
  # Calls to DB#previous_error will only return errors that have occurred
315
243
  # since the most recent call to this method.
316
244
  def reset_error_history
317
- db_command(:reseterror => 1)
318
- end
319
-
320
- # Returns true if this database is a master (or is not paired with any
321
- # other database), false if it is a slave.
322
- def master?
323
- doc = db_command(:ismaster => 1)
324
- is_master = doc['ismaster']
325
- ok?(doc) && is_master.kind_of?(Numeric) && is_master.to_i == 1
326
- end
327
-
328
- # Returns a string of the form "host:port" that points to the master
329
- # database. Works even if this is the master database.
330
- def master
331
- doc = db_command(:ismaster => 1)
332
- is_master = doc['ismaster']
333
- raise "Error retrieving master database: #{doc.inspect}" unless ok?(doc) && is_master.kind_of?(Numeric)
334
- case is_master.to_i
335
- when 1
336
- "#@host:#@port"
337
- else
338
- doc['remote']
339
- end
340
- end
341
-
342
- # Close the connection to the database.
343
- def close
344
- if @socket
345
- s = @socket
346
- @socket = nil
347
- s.close
348
- end
349
- end
350
-
351
- def connected?
352
- @socket != nil
353
- end
354
-
355
- def receive_full(length)
356
- message = ""
357
- while message.length < length do
358
- chunk = @socket.recv(length - message.length)
359
- raise "connection closed" unless chunk.length > 0
360
- message += chunk
361
- end
362
- message
245
+ command(:reseterror => 1)
363
246
  end
364
247
 
365
248
  # Returns a Cursor over the query results.
@@ -388,7 +271,7 @@ module Mongo
388
271
  oh = OrderedHash.new
389
272
  oh[:$eval] = code
390
273
  oh[:args] = args
391
- doc = db_command(oh)
274
+ doc = command(oh)
392
275
  return doc['retval'] if ok?(doc)
393
276
  raise OperationFailure, "eval failed: #{doc['errmsg']}"
394
277
  end
@@ -399,7 +282,7 @@ module Mongo
399
282
  oh = OrderedHash.new
400
283
  oh[:renameCollection] = "#{@name}.#{from}"
401
284
  oh[:to] = "#{@name}.#{to}"
402
- doc = db_command(oh, true)
285
+ doc = command(oh, true)
403
286
  raise "Error renaming collection: #{doc.inspect}" unless ok?(doc)
404
287
  end
405
288
 
@@ -409,7 +292,7 @@ module Mongo
409
292
  oh = OrderedHash.new
410
293
  oh[:deleteIndexes] = collection_name
411
294
  oh[:index] = name
412
- doc = db_command(oh)
295
+ doc = command(oh)
413
296
  raise "Error with drop_index command: #{doc.inspect}" unless ok?(doc)
414
297
  end
415
298
 
@@ -422,7 +305,7 @@ module Mongo
422
305
  sel = {:ns => full_collection_name(collection_name)}
423
306
  info = {}
424
307
  Cursor.new(Collection.new(self, SYSTEM_INDEX_COLLECTION), :selector => sel).each { |index|
425
- info[index['name']] = index['key'].map
308
+ info[index['name']] = index['key'].map {|k| k}
426
309
  }
427
310
  info
428
311
  end
@@ -436,53 +319,7 @@ module Mongo
436
319
  def create_index(collection_name, field_or_spec, unique=false)
437
320
  self.collection(collection_name).create_index(field_or_spec, unique)
438
321
  end
439
-
440
- # Sends a message to MongoDB.
441
- #
442
- # Takes a MongoDB opcode, +operation+, a message of class ByteBuffer,
443
- # +message+, and an optional formatted +log_message+.
444
- # Sends the message to the databse, adding the necessary headers.
445
- def send_message_with_operation(operation, message, log_message=nil)
446
- message_with_headers = add_message_headers(operation, message).to_s
447
- @logger.debug(" MONGODB #{log_message || message}") if @logger
448
- @semaphore.synchronize do
449
- send_message_on_socket(message_with_headers)
450
- end
451
- end
452
-
453
- def send_message_with_operation_raw(operation, message, log_message=nil)
454
- message_with_headers = add_message_headers_raw(operation, message)
455
- @logger.debug(" MONGODB #{log_message || message}") if @logger
456
- @semaphore.synchronize do
457
- send_message_on_socket(message_with_headers)
458
- end
459
- end
460
-
461
- # Sends a message to the database, waits for a response, and raises
462
- # and exception if the operation has failed.
463
- def send_message_with_safe_check(operation, message, log_message=nil)
464
- message_with_headers = add_message_headers(operation, message)
465
- message_with_check = last_error_message
466
- @logger.debug(" MONGODB #{log_message || message}") if @logger
467
- @semaphore.synchronize do
468
- send_message_on_socket(message_with_headers.append!(message_with_check).to_s)
469
- docs, num_received, cursor_id = receive
470
- if num_received == 1 && error = docs[0]['err']
471
- raise Mongo::OperationFailure, error
472
- end
473
- end
474
- end
475
-
476
- # Send a message to the database and waits for the response.
477
- def receive_message_with_operation(operation, message, log_message=nil)
478
- message_with_headers = add_message_headers(operation, message).to_s
479
- @logger.debug(" MONGODB #{log_message || message}") if @logger
480
- @semaphore.synchronize do
481
- send_message_on_socket(message_with_headers)
482
- receive
483
- end
484
- end
485
-
322
+
486
323
  # Return +true+ if +doc+ contains an 'ok' field with the value 1.
487
324
  def ok?(doc)
488
325
  ok = doc['ok']
@@ -492,14 +329,14 @@ module Mongo
492
329
  # DB commands need to be ordered, so selector must be an OrderedHash
493
330
  # (or a Hash with only one element). What DB commands really need is
494
331
  # that the "command" key be first.
495
- def db_command(selector, use_admin_db=false)
332
+ def command(selector, use_admin_db=false, sock=nil)
496
333
  if !selector.kind_of?(OrderedHash)
497
334
  if !selector.kind_of?(Hash) || selector.keys.length > 1
498
- raise "db_command must be given an OrderedHash when there is more than one key"
335
+ raise "command must be given an OrderedHash when there is more than one key"
499
336
  end
500
337
  end
501
338
 
502
- cursor = Cursor.new(Collection.new(self, SYSTEM_COMMAND_COLLECTION), :admin => use_admin_db, :limit => -1, :selector => selector)
339
+ cursor = Cursor.new(Collection.new(self, SYSTEM_COMMAND_COLLECTION), :admin => use_admin_db, :limit => -1, :selector => selector, :socket => sock)
503
340
  cursor.next_object
504
341
  end
505
342
 
@@ -516,14 +353,14 @@ module Mongo
516
353
  #
517
354
  # Note: DB commands must start with the "command" key. For this reason,
518
355
  # any selector containing more than one key must be an OrderedHash.
519
- def command(selector, admin=false, check_response=false)
356
+ def command(selector, admin=false, check_response=false, sock=nil)
520
357
  raise MongoArgumentError, "command must be given a selector" unless selector.is_a?(Hash) && !selector.empty?
521
358
  if selector.class.eql?(Hash) && selector.keys.length > 1
522
359
  raise MongoArgumentError, "DB#command requires an OrderedHash when hash contains multiple keys"
523
360
  end
524
361
 
525
362
  result = Cursor.new(system_command_collection, :admin => admin,
526
- :limit => -1, :selector => selector).next_object
363
+ :limit => -1, :selector => selector, :socket => sock).next_object
527
364
 
528
365
  if check_response && !ok?(result)
529
366
  raise OperationFailure, "Database command '#{selector.keys.first}' failed."
@@ -532,147 +369,38 @@ module Mongo
532
369
  end
533
370
  end
534
371
 
372
+ # DEPRECATED: please use DB#command instead.
373
+ def db_command(*args)
374
+ warn "DB#db_command has been DEPRECATED. Please use DB#command instead."
375
+ command(args[0], args[1])
376
+ end
377
+
535
378
  def full_collection_name(collection_name)
536
379
  "#{@name}.#{collection_name}"
537
380
  end
538
381
 
539
382
  private
540
383
 
541
- def receive
542
- receive_header
543
- number_received, cursor_id = receive_response_header
544
- read_documents(number_received, cursor_id)
545
- end
546
-
547
- def receive_header
548
- header = ByteBuffer.new
549
- header.put_array(receive_data_on_socket(16).unpack("C*"))
550
- unless header.size == STANDARD_HEADER_SIZE
551
- raise "Short read for DB response header: " +
552
- "expected #{STANDARD_HEADER_SIZE} bytes, saw #{header.size}"
553
- end
554
- header.rewind
555
- size = header.get_int
556
- request_id = header.get_int
557
- response_to = header.get_int
558
- op = header.get_int
559
- end
560
-
561
- def receive_response_header
562
- header_buf = ByteBuffer.new
563
- header_buf.put_array(receive_data_on_socket(RESPONSE_HEADER_SIZE).unpack("C*"))
564
- if header_buf.length != RESPONSE_HEADER_SIZE
565
- raise "Short read for DB response header; " +
566
- "expected #{RESPONSE_HEADER_SIZE} bytes, saw #{header_buf.length}"
567
- end
568
- header_buf.rewind
569
- result_flags = header_buf.get_int
570
- cursor_id = header_buf.get_long
571
- starting_from = header_buf.get_int
572
- number_remaining = header_buf.get_int
573
- [number_remaining, cursor_id]
384
+ def hash_password(username, plaintext)
385
+ Digest::MD5.hexdigest("#{username}:mongo:#{plaintext}")
574
386
  end
575
387
 
576
- def read_documents(number_received, cursor_id)
577
- docs = []
578
- number_remaining = number_received
579
- while number_remaining > 0 do
580
- buf = ByteBuffer.new
581
- buf.put_array(receive_data_on_socket(4).unpack("C*"))
582
- buf.rewind
583
- size = buf.get_int
584
- buf.put_array(receive_data_on_socket(size - 4).unpack("C*"), 4)
585
- number_remaining -= 1
586
- buf.rewind
587
- docs << BSON.new.deserialize(buf)
588
- end
589
- [docs, number_received, cursor_id]
388
+ def system_command_collection
389
+ Collection.new(self, SYSTEM_COMMAND_COLLECTION)
590
390
  end
591
391
 
592
- # Sending a message on socket.
593
- def send_message_on_socket(packed_message)
594
- connect_to_master if !connected? && @auto_reconnect
595
- begin
596
- @socket.print(packed_message)
597
- @socket.flush
598
- rescue => ex
599
- close
600
- raise ex
392
+ def validate_db_name(db_name)
393
+ unless [String, Symbol].include?(db_name.class)
394
+ raise TypeError, "db_name must be a string or symbol"
601
395
  end
602
- end
603
396
 
604
- # Receive data of specified length on socket.
605
- def receive_data_on_socket(length)
606
- message = ""
607
- while message.length < length do
608
- chunk = @socket.recv(length - message.length)
609
- raise "connection closed" unless chunk.length > 0
610
- message += chunk
397
+ [" ", ".", "$", "/", "\\"].each do |invalid_char|
398
+ if db_name.include? invalid_char
399
+ raise InvalidName, "database names cannot contain the character '#{invalid_char}'"
400
+ end
611
401
  end
612
- message
613
- end
614
-
615
- # Prepares a message for transmission to MongoDB by
616
- # constructing a valid message header.
617
- def add_message_headers(operation, message)
618
- headers = ByteBuffer.new
619
-
620
- # Message size.
621
- headers.put_int(16 + message.size)
622
-
623
- # Unique request id.
624
- headers.put_int(get_request_id)
625
-
626
- # Response id.
627
- headers.put_int(0)
628
-
629
- # Opcode.
630
- headers.put_int(operation)
631
- message.prepend!(headers)
632
- end
633
-
634
- # Increments and then returns the next available request id.
635
- # Note: this method should be called from within a lock.
636
- def get_request_id
637
- @@current_request_id += 1
638
- @@current_request_id
639
- end
640
-
641
- # Creates a getlasterror message.
642
- def last_error_message
643
- generate_last_error_message
644
- end
645
-
646
- def generate_last_error_message
647
- message = ByteBuffer.new
648
- message.put_int(0)
649
- BSON.serialize_cstr(message, "#{@name}.$cmd")
650
- message.put_int(0)
651
- message.put_int(-1)
652
- message.put_array(BSON_SERIALIZER.serialize({:getlasterror => 1}, false).unpack("C*"))
653
- add_message_headers(Mongo::Constants::OP_QUERY, message)
654
- end
655
-
656
- def reset_error_message
657
- @@reset_error_message ||= generate_reset_error_message
658
- end
659
-
660
- def generate_reset_error_message
661
- message = ByteBuffer.new
662
- message.put_int(0)
663
- BSON.serialize_cstr(message, "#{@name}.$cmd")
664
- message.put_int(0)
665
- message.put_int(-1)
666
- message.put_array(BSON_SERIALIZER.serialize({:reseterror => 1}, false).unpack("C*"))
667
- add_message_headers(Mongo::Constants::OP_QUERY, message)
668
- end
669
-
670
- def hash_password(username, plaintext)
671
- Digest::MD5.hexdigest("#{username}:mongo:#{plaintext}")
672
- end
673
-
674
- def system_command_collection
675
- Collection.new(self, SYSTEM_COMMAND_COLLECTION)
402
+ raise InvalidName, "database name cannot be the empty string" if db_name.empty?
403
+ db_name
676
404
  end
677
405
  end
678
406
  end