mongo 0.17.1 → 0.18

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.
@@ -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