mongo 1.3.0 → 1.12.5
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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +122 -271
- data/Rakefile +25 -209
- data/VERSION +1 -0
- data/bin/mongo_console +31 -9
- data/lib/mongo/bulk_write_collection_view.rb +387 -0
- data/lib/mongo/collection.rb +576 -269
- data/lib/mongo/collection_writer.rb +364 -0
- data/lib/mongo/connection/node.rb +249 -0
- data/lib/mongo/connection/pool.rb +340 -0
- data/lib/mongo/connection/pool_manager.rb +320 -0
- data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
- data/lib/mongo/connection/socket/socket_util.rb +37 -0
- data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
- data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
- data/lib/mongo/connection/socket/unix_socket.rb +39 -0
- data/lib/mongo/connection/socket.rb +18 -0
- data/lib/mongo/connection.rb +7 -875
- data/lib/mongo/cursor.rb +403 -117
- data/lib/mongo/db.rb +444 -243
- data/lib/mongo/exception.rb +145 -0
- data/lib/mongo/functional/authentication.rb +455 -0
- data/lib/mongo/functional/logging.rb +85 -0
- data/lib/mongo/functional/read_preference.rb +183 -0
- data/lib/mongo/functional/scram.rb +556 -0
- data/lib/mongo/functional/uri_parser.rb +409 -0
- data/lib/mongo/functional/write_concern.rb +66 -0
- data/lib/mongo/functional.rb +20 -0
- data/lib/mongo/gridfs/grid.rb +30 -24
- data/lib/mongo/gridfs/grid_ext.rb +6 -10
- data/lib/mongo/gridfs/grid_file_system.rb +38 -20
- data/lib/mongo/gridfs/grid_io.rb +84 -75
- data/lib/mongo/gridfs.rb +18 -0
- data/lib/mongo/legacy.rb +140 -0
- data/lib/mongo/mongo_client.rb +697 -0
- data/lib/mongo/mongo_replica_set_client.rb +535 -0
- data/lib/mongo/mongo_sharded_client.rb +159 -0
- data/lib/mongo/networking.rb +372 -0
- data/lib/mongo/{util → utils}/conversions.rb +29 -8
- data/lib/mongo/{util → utils}/core_ext.rb +28 -18
- data/lib/mongo/{util → utils}/server_version.rb +4 -6
- data/lib/mongo/{util → utils}/support.rb +29 -31
- data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
- data/lib/mongo/utils.rb +19 -0
- data/lib/mongo.rb +51 -50
- data/mongo.gemspec +29 -32
- data/test/functional/authentication_test.rb +39 -0
- data/test/functional/bulk_api_stress_test.rb +133 -0
- data/test/functional/bulk_write_collection_view_test.rb +1198 -0
- data/test/functional/client_test.rb +627 -0
- data/test/functional/collection_test.rb +2175 -0
- data/test/functional/collection_writer_test.rb +83 -0
- data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
- data/test/functional/cursor_fail_test.rb +57 -0
- data/test/functional/cursor_message_test.rb +56 -0
- data/test/functional/cursor_test.rb +683 -0
- data/test/functional/db_api_test.rb +835 -0
- data/test/functional/db_connection_test.rb +25 -0
- data/test/functional/db_test.rb +348 -0
- data/test/functional/grid_file_system_test.rb +285 -0
- data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
- data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
- data/test/functional/pool_test.rb +136 -0
- data/test/functional/safe_test.rb +98 -0
- data/test/functional/ssl_test.rb +29 -0
- data/test/functional/support_test.rb +62 -0
- data/test/functional/timeout_test.rb +60 -0
- data/test/functional/uri_test.rb +446 -0
- data/test/functional/write_concern_test.rb +118 -0
- data/test/helpers/general.rb +50 -0
- data/test/helpers/test_unit.rb +476 -0
- data/test/replica_set/authentication_test.rb +37 -0
- data/test/replica_set/basic_test.rb +189 -0
- data/test/replica_set/client_test.rb +393 -0
- data/test/replica_set/connection_test.rb +138 -0
- data/test/replica_set/count_test.rb +66 -0
- data/test/replica_set/cursor_test.rb +220 -0
- data/test/replica_set/insert_test.rb +157 -0
- data/test/replica_set/max_values_test.rb +151 -0
- data/test/replica_set/pinning_test.rb +105 -0
- data/test/replica_set/query_test.rb +73 -0
- data/test/replica_set/read_preference_test.rb +219 -0
- data/test/replica_set/refresh_test.rb +211 -0
- data/test/replica_set/replication_ack_test.rb +95 -0
- data/test/replica_set/ssl_test.rb +32 -0
- data/test/sharded_cluster/basic_test.rb +203 -0
- data/test/shared/authentication/basic_auth_shared.rb +260 -0
- data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
- data/test/shared/authentication/gssapi_shared.rb +176 -0
- data/test/shared/authentication/sasl_plain_shared.rb +96 -0
- data/test/shared/authentication/scram_shared.rb +92 -0
- data/test/shared/ssl_shared.rb +235 -0
- data/test/test_helper.rb +53 -94
- data/test/threading/basic_test.rb +120 -0
- data/test/tools/mongo_config.rb +708 -0
- data/test/tools/mongo_config_test.rb +160 -0
- data/test/unit/client_test.rb +381 -0
- data/test/unit/collection_test.rb +89 -53
- data/test/unit/connection_test.rb +282 -32
- data/test/unit/cursor_test.rb +206 -8
- data/test/unit/db_test.rb +55 -13
- data/test/unit/grid_test.rb +43 -16
- data/test/unit/mongo_sharded_client_test.rb +48 -0
- data/test/unit/node_test.rb +93 -0
- data/test/unit/pool_manager_test.rb +111 -0
- data/test/unit/read_pref_test.rb +406 -0
- data/test/unit/read_test.rb +159 -0
- data/test/unit/safe_test.rb +69 -36
- data/test/unit/sharding_pool_manager_test.rb +84 -0
- data/test/unit/write_concern_test.rb +175 -0
- data.tar.gz.sig +3 -0
- metadata +227 -216
- metadata.gz.sig +0 -0
- data/docs/CREDITS.md +0 -123
- data/docs/FAQ.md +0 -116
- data/docs/GridFS.md +0 -158
- data/docs/HISTORY.md +0 -244
- data/docs/RELEASES.md +0 -33
- data/docs/REPLICA_SETS.md +0 -72
- data/docs/TUTORIAL.md +0 -247
- data/docs/WRITE_CONCERN.md +0 -28
- data/lib/mongo/exceptions.rb +0 -71
- data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
- data/lib/mongo/repl_set_connection.rb +0 -342
- data/lib/mongo/test.rb +0 -20
- data/lib/mongo/util/pool.rb +0 -177
- data/lib/mongo/util/uri_parser.rb +0 -185
- data/test/async/collection_test.rb +0 -224
- data/test/async/connection_test.rb +0 -24
- data/test/async/cursor_test.rb +0 -162
- data/test/async/worker_pool_test.rb +0 -99
- data/test/auxillary/1.4_features.rb +0 -166
- data/test/auxillary/authentication_test.rb +0 -68
- data/test/auxillary/autoreconnect_test.rb +0 -41
- data/test/auxillary/fork_test.rb +0 -30
- data/test/auxillary/repl_set_auth_test.rb +0 -58
- data/test/auxillary/slave_connection_test.rb +0 -36
- data/test/auxillary/threaded_authentication_test.rb +0 -101
- data/test/bson/binary_test.rb +0 -15
- data/test/bson/bson_test.rb +0 -649
- data/test/bson/byte_buffer_test.rb +0 -208
- data/test/bson/hash_with_indifferent_access_test.rb +0 -38
- data/test/bson/json_test.rb +0 -17
- data/test/bson/object_id_test.rb +0 -154
- data/test/bson/ordered_hash_test.rb +0 -204
- data/test/bson/timestamp_test.rb +0 -24
- data/test/collection_test.rb +0 -910
- data/test/connection_test.rb +0 -309
- data/test/cursor_fail_test.rb +0 -75
- data/test/cursor_message_test.rb +0 -43
- data/test/cursor_test.rb +0 -483
- data/test/db_api_test.rb +0 -726
- data/test/db_connection_test.rb +0 -15
- data/test/db_test.rb +0 -287
- data/test/grid_file_system_test.rb +0 -243
- data/test/load/resque/load.rb +0 -21
- data/test/load/resque/processor.rb +0 -26
- data/test/load/thin/load.rb +0 -24
- data/test/load/unicorn/load.rb +0 -23
- data/test/load/unicorn/unicorn.rb +0 -29
- data/test/replica_sets/connect_test.rb +0 -94
- data/test/replica_sets/connection_string_test.rb +0 -32
- data/test/replica_sets/count_test.rb +0 -35
- data/test/replica_sets/insert_test.rb +0 -53
- data/test/replica_sets/pooled_insert_test.rb +0 -55
- data/test/replica_sets/query_secondaries.rb +0 -96
- data/test/replica_sets/query_test.rb +0 -51
- data/test/replica_sets/replication_ack_test.rb +0 -66
- data/test/replica_sets/rs_test_helper.rb +0 -27
- data/test/safe_test.rb +0 -68
- data/test/support/hash_with_indifferent_access.rb +0 -186
- data/test/support/keys.rb +0 -45
- data/test/support_test.rb +0 -18
- data/test/threading/threading_with_large_pool_test.rb +0 -90
- data/test/threading_test.rb +0 -87
- data/test/tools/auth_repl_set_manager.rb +0 -14
- data/test/tools/load.rb +0 -58
- data/test/tools/repl_set_manager.rb +0 -266
- data/test/tools/sharding_manager.rb +0 -202
- data/test/tools/test.rb +0 -4
- data/test/unit/pool_test.rb +0 -9
- data/test/unit/repl_set_connection_test.rb +0 -59
- data/test/uri_test.rb +0 -91
@@ -0,0 +1,372 @@
|
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongo
|
16
|
+
module Networking
|
17
|
+
|
18
|
+
STANDARD_HEADER_SIZE = 16
|
19
|
+
RESPONSE_HEADER_SIZE = 20
|
20
|
+
|
21
|
+
# Counter for generating unique request ids.
|
22
|
+
@@current_request_id = 0
|
23
|
+
|
24
|
+
# Send a message to MongoDB, adding the necessary headers.
|
25
|
+
#
|
26
|
+
# @param [Integer] operation a MongoDB opcode.
|
27
|
+
# @param [BSON::ByteBuffer] message a message to send to the database.
|
28
|
+
#
|
29
|
+
# @option opts [Symbol] :connection (:writer) The connection to which
|
30
|
+
# this message should be sent. Valid options are :writer and :reader.
|
31
|
+
#
|
32
|
+
# @return [Integer] number of bytes sent
|
33
|
+
def send_message(operation, message, opts={})
|
34
|
+
if opts.is_a?(String)
|
35
|
+
warn "MongoClient#send_message no longer takes a string log message. " +
|
36
|
+
"Logging is now handled within the Collection and Cursor classes."
|
37
|
+
opts = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
add_message_headers(message, operation)
|
41
|
+
packed_message = message.to_s
|
42
|
+
|
43
|
+
sock = nil
|
44
|
+
pool = opts.fetch(:pool, nil)
|
45
|
+
begin
|
46
|
+
if pool
|
47
|
+
#puts "send_message pool.port:#{pool.port}"
|
48
|
+
sock = pool.checkout
|
49
|
+
else
|
50
|
+
sock ||= checkout_writer
|
51
|
+
end
|
52
|
+
send_message_on_socket(packed_message, sock)
|
53
|
+
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
54
|
+
close
|
55
|
+
raise ex
|
56
|
+
ensure
|
57
|
+
if sock
|
58
|
+
sock.checkin
|
59
|
+
end
|
60
|
+
end
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
# Sends a message to the database, waits for a response, and raises
|
65
|
+
# an exception if the operation has failed.
|
66
|
+
#
|
67
|
+
# @param [Integer] operation a MongoDB opcode.
|
68
|
+
# @param [BSON::ByteBuffer] message a message to send to the database.
|
69
|
+
# @param [String] db_name the name of the database. used on call to get_last_error.
|
70
|
+
# @param [String] log_message this is currently a no-op and will be removed.
|
71
|
+
# @param [Hash] write_concern write concern.
|
72
|
+
#
|
73
|
+
# @see DB#get_last_error for valid last error params.
|
74
|
+
#
|
75
|
+
# @return [Hash] The document returned by the call to getlasterror.
|
76
|
+
def send_message_with_gle(operation, message, db_name, log_message=nil, write_concern=false)
|
77
|
+
docs = num_received = cursor_id = ''
|
78
|
+
add_message_headers(message, operation)
|
79
|
+
|
80
|
+
last_error_message = build_get_last_error_message(db_name, write_concern)
|
81
|
+
last_error_id = add_message_headers(last_error_message, Mongo::Constants::OP_QUERY)
|
82
|
+
|
83
|
+
packed_message = message.append!(last_error_message).to_s
|
84
|
+
sock = nil
|
85
|
+
begin
|
86
|
+
sock = checkout_writer
|
87
|
+
send_message_on_socket(packed_message, sock)
|
88
|
+
docs, num_received, cursor_id = receive(sock, last_error_id)
|
89
|
+
rescue ConnectionFailure, OperationFailure, OperationTimeout => ex
|
90
|
+
raise ex
|
91
|
+
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
92
|
+
close
|
93
|
+
sock = nil
|
94
|
+
raise ex
|
95
|
+
ensure
|
96
|
+
checkin(sock) if sock
|
97
|
+
sock = nil
|
98
|
+
end
|
99
|
+
|
100
|
+
if num_received == 1
|
101
|
+
error = docs[0]['err'] || docs[0]['errmsg']
|
102
|
+
if error && error.include?("not master")
|
103
|
+
close
|
104
|
+
raise ConnectionFailure.new(docs[0]['code'].to_s + ': ' + error, docs[0]['code'], docs[0])
|
105
|
+
elsif (!error.nil? && note = docs[0]['jnote'] || docs[0]['wnote']) # assignment
|
106
|
+
code = docs[0]['code'] || Mongo::ErrorCode::BAD_VALUE # as of server version 2.5.5
|
107
|
+
raise WriteConcernError.new(code.to_s + ': ' + note, code, docs[0])
|
108
|
+
elsif error
|
109
|
+
code = docs[0]['code'] || Mongo::ErrorCode::UNKNOWN_ERROR
|
110
|
+
error = "wtimeout" if error == "timeout"
|
111
|
+
raise WriteConcernError.new(code.to_s + ': ' + error, code, docs[0]) if error == "wtimeout"
|
112
|
+
raise OperationFailure.new(code.to_s + ': ' + error, code, docs[0])
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
docs[0]
|
117
|
+
end
|
118
|
+
|
119
|
+
# Sends a message to the database and waits for the response.
|
120
|
+
#
|
121
|
+
# @param [Integer] operation a MongoDB opcode.
|
122
|
+
# @param [BSON::ByteBuffer] message a message to send to the database.
|
123
|
+
# @param [String] log_message this is currently a no-op and will be removed.
|
124
|
+
# @param [Socket] socket a socket to use in lieu of checking out a new one.
|
125
|
+
# @param [Boolean] command (false) indicate whether this is a command. If this is a command,
|
126
|
+
# the message will be sent to the primary node.
|
127
|
+
# @param [Symbol] read the read preference.
|
128
|
+
# @param [Boolean] exhaust (false) indicate whether the cursor should be exhausted. Set
|
129
|
+
# this to true only when the OP_QUERY_EXHAUST flag is set.
|
130
|
+
# @param [Boolean] compile_regex whether BSON regex objects should be compiled into Ruby regexes.
|
131
|
+
#
|
132
|
+
# @return [Array]
|
133
|
+
# An array whose indexes include [0] documents returned, [1] number of document received,
|
134
|
+
# and [3] a cursor_id.
|
135
|
+
def receive_message(operation, message, log_message=nil, socket=nil, command=false,
|
136
|
+
read=:primary, exhaust=false, compile_regex=true)
|
137
|
+
request_id = add_message_headers(message, operation)
|
138
|
+
packed_message = message.to_s
|
139
|
+
opts = { :exhaust => exhaust,
|
140
|
+
:compile_regex => compile_regex }
|
141
|
+
|
142
|
+
result = ''
|
143
|
+
|
144
|
+
begin
|
145
|
+
send_message_on_socket(packed_message, socket)
|
146
|
+
result = receive(socket, request_id, opts)
|
147
|
+
rescue ConnectionFailure => ex
|
148
|
+
socket.close
|
149
|
+
checkin(socket)
|
150
|
+
raise ex
|
151
|
+
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
152
|
+
close
|
153
|
+
raise ex
|
154
|
+
rescue Exception => ex
|
155
|
+
if defined?(IRB)
|
156
|
+
close if ex.class == IRB::Abort
|
157
|
+
end
|
158
|
+
raise ex
|
159
|
+
end
|
160
|
+
result
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
def receive(sock, cursor_id, opts={})
|
166
|
+
exhaust = !!opts.delete(:exhaust)
|
167
|
+
|
168
|
+
if exhaust
|
169
|
+
docs = []
|
170
|
+
num_received = 0
|
171
|
+
|
172
|
+
while(cursor_id != 0) do
|
173
|
+
receive_header(sock, cursor_id, exhaust)
|
174
|
+
number_received, cursor_id = receive_response_header(sock)
|
175
|
+
new_docs, n = read_documents(number_received, sock, opts)
|
176
|
+
docs += new_docs
|
177
|
+
num_received += n
|
178
|
+
end
|
179
|
+
|
180
|
+
return [docs, num_received, cursor_id]
|
181
|
+
else
|
182
|
+
receive_header(sock, cursor_id, exhaust)
|
183
|
+
number_received, cursor_id = receive_response_header(sock)
|
184
|
+
docs, num_received = read_documents(number_received, sock, opts)
|
185
|
+
|
186
|
+
return [docs, num_received, cursor_id]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def receive_header(sock, expected_response, exhaust=false)
|
191
|
+
header = receive_message_on_socket(16, sock)
|
192
|
+
|
193
|
+
# unpacks to size, request_id, response_to
|
194
|
+
response_to = header.unpack('VVV')[2]
|
195
|
+
if !exhaust && expected_response != response_to
|
196
|
+
raise Mongo::ConnectionFailure, "Expected response #{expected_response} but got #{response_to}"
|
197
|
+
end
|
198
|
+
|
199
|
+
unless header.size == STANDARD_HEADER_SIZE
|
200
|
+
raise "Short read for DB response header: " +
|
201
|
+
"expected #{STANDARD_HEADER_SIZE} bytes, saw #{header.size}"
|
202
|
+
end
|
203
|
+
nil
|
204
|
+
end
|
205
|
+
|
206
|
+
def receive_response_header(sock)
|
207
|
+
header_buf = receive_message_on_socket(RESPONSE_HEADER_SIZE, sock)
|
208
|
+
if header_buf.length != RESPONSE_HEADER_SIZE
|
209
|
+
raise "Short read for DB response header; " +
|
210
|
+
"expected #{RESPONSE_HEADER_SIZE} bytes, saw #{header_buf.length}"
|
211
|
+
end
|
212
|
+
|
213
|
+
# unpacks to flags, cursor_id_a, cursor_id_b, starting_from, number_remaining
|
214
|
+
flags, cursor_id_a, cursor_id_b, _, number_remaining = header_buf.unpack('VVVVV')
|
215
|
+
|
216
|
+
check_response_flags(flags)
|
217
|
+
cursor_id = (cursor_id_b << 32) + cursor_id_a
|
218
|
+
[number_remaining, cursor_id]
|
219
|
+
end
|
220
|
+
|
221
|
+
def check_response_flags(flags)
|
222
|
+
if flags & Mongo::Constants::REPLY_CURSOR_NOT_FOUND != 0
|
223
|
+
raise Mongo::OperationFailure, "Query response returned CURSOR_NOT_FOUND. " +
|
224
|
+
"Either an invalid cursor was specified, or the cursor may have timed out on the server."
|
225
|
+
elsif flags & Mongo::Constants::REPLY_QUERY_FAILURE != 0
|
226
|
+
# Mongo query reply failures are handled in Cursor#next.
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def read_documents(number_received, sock, opts)
|
231
|
+
docs = []
|
232
|
+
number_remaining = number_received
|
233
|
+
while number_remaining > 0 do
|
234
|
+
buf = receive_message_on_socket(4, sock)
|
235
|
+
size = buf.unpack('V')[0]
|
236
|
+
buf << receive_message_on_socket(size - 4, sock)
|
237
|
+
number_remaining -= 1
|
238
|
+
docs << BSON::BSON_CODER.deserialize(buf, opts)
|
239
|
+
end
|
240
|
+
[docs, number_received]
|
241
|
+
end
|
242
|
+
|
243
|
+
def build_command_message(db_name, query, projection=nil, skip=0, limit=-1)
|
244
|
+
message = BSON::ByteBuffer.new("", max_message_size)
|
245
|
+
message.put_int(0)
|
246
|
+
BSON::BSON_RUBY.serialize_cstr(message, "#{db_name}.$cmd")
|
247
|
+
message.put_int(skip)
|
248
|
+
message.put_int(limit)
|
249
|
+
message.put_binary(BSON::BSON_CODER.serialize(query, false, false, max_bson_size).to_s)
|
250
|
+
message.put_binary(BSON::BSON_CODER.serialize(projection, false, false, max_bson_size).to_s) if projection
|
251
|
+
message
|
252
|
+
end
|
253
|
+
|
254
|
+
# Constructs a getlasterror message. This method is used exclusively by
|
255
|
+
# MongoClient#send_message_with_gle.
|
256
|
+
def build_get_last_error_message(db_name, write_concern)
|
257
|
+
gle = BSON::OrderedHash.new
|
258
|
+
gle[:getlasterror] = 1
|
259
|
+
if write_concern.is_a?(Hash)
|
260
|
+
write_concern.assert_valid_keys(:w, :wtimeout, :fsync, :j)
|
261
|
+
gle.merge!(write_concern)
|
262
|
+
gle.delete(:w) if gle[:w] == 1
|
263
|
+
end
|
264
|
+
gle[:w] = gle[:w].to_s if gle[:w].is_a?(Symbol)
|
265
|
+
build_command_message(db_name, gle)
|
266
|
+
end
|
267
|
+
|
268
|
+
# Prepares a message for transmission to MongoDB by
|
269
|
+
# constructing a valid message header.
|
270
|
+
#
|
271
|
+
# Note: this method modifies message by reference.
|
272
|
+
#
|
273
|
+
# @return [Integer] the request id used in the header
|
274
|
+
def add_message_headers(message, operation)
|
275
|
+
headers = [
|
276
|
+
# Message size.
|
277
|
+
16 + message.size,
|
278
|
+
|
279
|
+
# Unique request id.
|
280
|
+
request_id = get_request_id,
|
281
|
+
|
282
|
+
# Response id.
|
283
|
+
0,
|
284
|
+
|
285
|
+
# Opcode.
|
286
|
+
operation
|
287
|
+
].pack('VVVV')
|
288
|
+
|
289
|
+
message.prepend!(headers)
|
290
|
+
|
291
|
+
request_id
|
292
|
+
end
|
293
|
+
|
294
|
+
# Increment and return the next available request id.
|
295
|
+
#
|
296
|
+
# return [Integer]
|
297
|
+
def get_request_id
|
298
|
+
request_id = ''
|
299
|
+
@id_lock.synchronize do
|
300
|
+
request_id = @@current_request_id += 1
|
301
|
+
end
|
302
|
+
request_id
|
303
|
+
end
|
304
|
+
|
305
|
+
# Low-level method for sending a message on a socket.
|
306
|
+
# Requires a packed message and an available socket,
|
307
|
+
#
|
308
|
+
# @return [Integer] number of bytes sent
|
309
|
+
def send_message_on_socket(packed_message, socket)
|
310
|
+
begin
|
311
|
+
total_bytes_sent = socket.send(packed_message)
|
312
|
+
if total_bytes_sent != packed_message.size
|
313
|
+
packed_message.slice!(0, total_bytes_sent)
|
314
|
+
while packed_message.size > 0
|
315
|
+
byte_sent = socket.send(packed_message)
|
316
|
+
total_bytes_sent += byte_sent
|
317
|
+
packed_message.slice!(0, byte_sent)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
total_bytes_sent
|
321
|
+
rescue => ex
|
322
|
+
socket.close
|
323
|
+
raise ConnectionFailure, "Operation failed with the following exception: #{ex}:#{ex.message}"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
# Low-level method for receiving data from socket.
|
328
|
+
# Requires length and an available socket.
|
329
|
+
def receive_message_on_socket(length, socket)
|
330
|
+
begin
|
331
|
+
message = receive_data(length, socket)
|
332
|
+
rescue OperationTimeout, ConnectionFailure => ex
|
333
|
+
socket.close
|
334
|
+
|
335
|
+
if ex.class == OperationTimeout
|
336
|
+
raise OperationTimeout, "Timed out waiting on socket read."
|
337
|
+
else
|
338
|
+
raise ConnectionFailure, "Operation failed with the following exception: #{ex}"
|
339
|
+
end
|
340
|
+
end
|
341
|
+
message
|
342
|
+
end
|
343
|
+
|
344
|
+
def receive_data(length, socket)
|
345
|
+
message = new_binary_string
|
346
|
+
socket.read(length, message)
|
347
|
+
|
348
|
+
raise ConnectionFailure, "connection closed" unless message && message.length > 0
|
349
|
+
if message.length < length
|
350
|
+
chunk = new_binary_string
|
351
|
+
while message.length < length
|
352
|
+
socket.read(length - message.length, chunk)
|
353
|
+
raise ConnectionFailure, "connection closed" unless chunk.length > 0
|
354
|
+
message << chunk
|
355
|
+
end
|
356
|
+
end
|
357
|
+
message
|
358
|
+
end
|
359
|
+
|
360
|
+
if defined?(Encoding)
|
361
|
+
BINARY_ENCODING = Encoding.find("binary")
|
362
|
+
|
363
|
+
def new_binary_string
|
364
|
+
"".force_encoding(BINARY_ENCODING)
|
365
|
+
end
|
366
|
+
else
|
367
|
+
def new_binary_string
|
368
|
+
""
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
@@ -1,20 +1,17 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# --
|
4
|
-
# Copyright (C) 2008-2011 10gen Inc.
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
5
2
|
#
|
6
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
4
|
# you may not use this file except in compliance with the License.
|
8
5
|
# You may obtain a copy of the License at
|
9
6
|
#
|
10
|
-
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
8
|
#
|
12
9
|
# Unless required by applicable law or agreed to in writing, software
|
13
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
12
|
# See the License for the specific language governing permissions and
|
16
13
|
# limitations under the License.
|
17
|
-
|
14
|
+
|
18
15
|
module Mongo #:nodoc:
|
19
16
|
|
20
17
|
# Utility module to include when needing to convert certain types of
|
@@ -24,8 +21,31 @@ module Mongo #:nodoc:
|
|
24
21
|
ASCENDING_CONVERSION = ["ascending", "asc", "1"]
|
25
22
|
DESCENDING_CONVERSION = ["descending", "desc", "-1"]
|
26
23
|
|
24
|
+
# Allows sort parameters to be defined as a Hash.
|
25
|
+
# Does not allow usage of un-ordered hashes, therefore
|
26
|
+
# Ruby 1.8.x users must use BSON::OrderedHash.
|
27
|
+
#
|
28
|
+
# Example:
|
29
|
+
#
|
30
|
+
# <tt>hash_as_sort_parameters({:field1 => :asc, "field2" => :desc})</tt> =>
|
31
|
+
# <tt>{ "field1" => 1, "field2" => -1}</tt>
|
32
|
+
def hash_as_sort_parameters(value)
|
33
|
+
if RUBY_VERSION < '1.9' && !value.is_a?(BSON::OrderedHash)
|
34
|
+
raise InvalidSortValueError.new(
|
35
|
+
"Hashes used to supply sort order must maintain ordering." +
|
36
|
+
"Use BSON::OrderedHash."
|
37
|
+
)
|
38
|
+
else
|
39
|
+
order_by = value.inject({}) do |memo, (key, direction)|
|
40
|
+
memo[key.to_s] = sort_value(direction)
|
41
|
+
memo
|
42
|
+
end
|
43
|
+
end
|
44
|
+
order_by
|
45
|
+
end
|
46
|
+
|
27
47
|
# Converts the supplied +Array+ to a +Hash+ to pass to mongo as
|
28
|
-
# sorting parameters. The returned +Hash+ will vary depending
|
48
|
+
# sorting parameters. The returned +Hash+ will vary depending
|
29
49
|
# on whether the passed +Array+ is one or two dimensional.
|
30
50
|
#
|
31
51
|
# Example:
|
@@ -67,7 +87,7 @@ module Mongo #:nodoc:
|
|
67
87
|
{ str => 1 }
|
68
88
|
end
|
69
89
|
|
70
|
-
# Converts the +String+, +Symbol+, or +Integer+ to the
|
90
|
+
# Converts the +String+, +Symbol+, or +Integer+ to the
|
71
91
|
# corresponding sort value in MongoDB.
|
72
92
|
#
|
73
93
|
# Valid conversions (case-insensitive):
|
@@ -77,6 +97,7 @@ module Mongo #:nodoc:
|
|
77
97
|
#
|
78
98
|
# If the value is invalid then an error will be raised.
|
79
99
|
def sort_value(value)
|
100
|
+
return value if value.is_a?(Hash)
|
80
101
|
val = value.to_s.downcase
|
81
102
|
return 1 if ASCENDING_CONVERSION.include?(val)
|
82
103
|
return -1 if DESCENDING_CONVERSION.include?(val)
|
@@ -1,20 +1,16 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# --
|
4
|
-
# Copyright (C) 2008-2011 10gen Inc.
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
5
2
|
#
|
6
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
4
|
# you may not use this file except in compliance with the License.
|
8
5
|
# You may obtain a copy of the License at
|
9
6
|
#
|
10
|
-
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
8
|
#
|
12
9
|
# Unless required by applicable law or agreed to in writing, software
|
13
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
12
|
# See the License for the specific language governing permissions and
|
16
13
|
# limitations under the License.
|
17
|
-
# ++
|
18
14
|
|
19
15
|
#:nodoc:
|
20
16
|
class Object
|
@@ -27,17 +23,6 @@ class Object
|
|
27
23
|
|
28
24
|
end
|
29
25
|
|
30
|
-
#:nodoc:
|
31
|
-
module Enumerable
|
32
|
-
|
33
|
-
#:nodoc:
|
34
|
-
def each_with_object(memo)
|
35
|
-
each { |element| yield(element, memo) }
|
36
|
-
memo
|
37
|
-
end unless [].respond_to?(:each_with_object)
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
26
|
#:nodoc:
|
42
27
|
class Hash
|
43
28
|
|
@@ -45,7 +30,7 @@ class Hash
|
|
45
30
|
def assert_valid_keys(*valid_keys)
|
46
31
|
unknown_keys = keys - [valid_keys].flatten
|
47
32
|
raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
|
48
|
-
end
|
33
|
+
end unless instance_methods.include?(:assert_valid_keys)
|
49
34
|
|
50
35
|
end
|
51
36
|
|
@@ -58,3 +43,28 @@ class String
|
|
58
43
|
end
|
59
44
|
|
60
45
|
end
|
46
|
+
|
47
|
+
#:nodoc:
|
48
|
+
class Class
|
49
|
+
def mongo_thread_local_accessor name, options = {}
|
50
|
+
m = Module.new
|
51
|
+
m.module_eval do
|
52
|
+
class_variable_set :"@@#{name}", Hash.new {|h,k| h[k] = options[:default] }
|
53
|
+
end
|
54
|
+
m.module_eval %{
|
55
|
+
|
56
|
+
def #{name}
|
57
|
+
@@#{name}[Thread.current.object_id]
|
58
|
+
end
|
59
|
+
|
60
|
+
def #{name}=(val)
|
61
|
+
@@#{name}[Thread.current.object_id] = val
|
62
|
+
end
|
63
|
+
}
|
64
|
+
|
65
|
+
class_eval do
|
66
|
+
include m
|
67
|
+
extend m
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -1,21 +1,19 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# --
|
4
|
-
# Copyright (C) 2008-2011 10gen Inc.
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
5
2
|
#
|
6
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
4
|
# you may not use this file except in compliance with the License.
|
8
5
|
# You may obtain a copy of the License at
|
9
6
|
#
|
10
|
-
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
8
|
#
|
12
9
|
# Unless required by applicable law or agreed to in writing, software
|
13
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
12
|
# See the License for the specific language governing permissions and
|
16
13
|
# limitations under the License.
|
17
|
-
|
14
|
+
|
18
15
|
module Mongo
|
16
|
+
|
19
17
|
# Simple class for comparing server versions.
|
20
18
|
class ServerVersion
|
21
19
|
include Comparable
|
@@ -1,50 +1,23 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# --
|
4
|
-
# Copyright (C) 2008-2011 10gen Inc.
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
5
2
|
#
|
6
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
4
|
# you may not use this file except in compliance with the License.
|
8
5
|
# You may obtain a copy of the License at
|
9
6
|
#
|
10
|
-
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
8
|
#
|
12
9
|
# Unless required by applicable law or agreed to in writing, software
|
13
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
12
|
# See the License for the specific language governing permissions and
|
16
13
|
# limitations under the License.
|
17
|
-
# ++
|
18
|
-
|
19
|
-
require 'digest/md5'
|
20
14
|
|
21
15
|
module Mongo
|
22
16
|
module Support
|
17
|
+
|
23
18
|
include Mongo::Conversions
|
24
19
|
extend self
|
25
20
|
|
26
|
-
# Generate an MD5 for authentication.
|
27
|
-
#
|
28
|
-
# @param [String] username
|
29
|
-
# @param [String] password
|
30
|
-
# @param [String] nonce
|
31
|
-
#
|
32
|
-
# @return [String] a key for db authentication.
|
33
|
-
def auth_key(username, password, nonce)
|
34
|
-
Digest::MD5.hexdigest("#{nonce}#{username}#{hash_password(username, password)}")
|
35
|
-
end
|
36
|
-
|
37
|
-
# Return a hashed password for auth.
|
38
|
-
#
|
39
|
-
# @param [String] username
|
40
|
-
# @param [String] plaintext
|
41
|
-
#
|
42
|
-
# @return [String]
|
43
|
-
def hash_password(username, plaintext)
|
44
|
-
Digest::MD5.hexdigest("#{username}:mongo:#{plaintext}")
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
21
|
def validate_db_name(db_name)
|
49
22
|
unless [String, Symbol].include?(db_name.class)
|
50
23
|
raise TypeError, "db_name must be a string or symbol"
|
@@ -61,6 +34,7 @@ module Mongo
|
|
61
34
|
|
62
35
|
def format_order_clause(order)
|
63
36
|
case order
|
37
|
+
when Hash, BSON::OrderedHash then hash_as_sort_parameters(order)
|
64
38
|
when String, Symbol then string_as_sort_parameters(order)
|
65
39
|
when Array then array_as_sort_parameters(order)
|
66
40
|
else
|
@@ -69,6 +43,29 @@ module Mongo
|
|
69
43
|
end
|
70
44
|
end
|
71
45
|
|
46
|
+
def normalize_seeds(seeds)
|
47
|
+
pairs = Array(seeds)
|
48
|
+
pairs = [ seeds ] if pairs.last.is_a?(Fixnum)
|
49
|
+
pairs = pairs.collect do |hostport|
|
50
|
+
if hostport.is_a?(String)
|
51
|
+
if hostport[0,1] == '['
|
52
|
+
host, port = hostport.split(']:') << MongoClient::DEFAULT_PORT
|
53
|
+
host = host.end_with?(']') ? host[1...-1] : host[1..-1]
|
54
|
+
else
|
55
|
+
host, port = hostport.split(':') << MongoClient::DEFAULT_PORT
|
56
|
+
end
|
57
|
+
[ host, port.to_i ]
|
58
|
+
else
|
59
|
+
hostport
|
60
|
+
end
|
61
|
+
end
|
62
|
+
pairs.length > 1 ? pairs : pairs.first
|
63
|
+
end
|
64
|
+
|
65
|
+
def is_i?(value)
|
66
|
+
return !!(value =~ /^\d+$/)
|
67
|
+
end
|
68
|
+
|
72
69
|
# Determine if a database command has succeeded by
|
73
70
|
# checking the document response.
|
74
71
|
#
|
@@ -76,7 +73,8 @@ module Mongo
|
|
76
73
|
#
|
77
74
|
# @return [Boolean] true if the 'ok' key is either 1 or *true*.
|
78
75
|
def ok?(doc)
|
79
|
-
|
76
|
+
ok = doc['ok']
|
77
|
+
ok == 1 || ok == 1.0 || ok == true
|
80
78
|
end
|
81
79
|
end
|
82
80
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
#:nodoc:
|
16
|
+
module Mongo
|
17
|
+
module ThreadLocalVariableManager
|
18
|
+
def thread_local
|
19
|
+
Thread.current[:mongo_thread_locals] ||= Hash.new do |hash, key|
|
20
|
+
hash[key] = Hash.new unless hash.key? key
|
21
|
+
hash[key]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/mongo/utils.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'mongo/utils/conversions'
|
16
|
+
require 'mongo/utils/core_ext'
|
17
|
+
require 'mongo/utils/server_version'
|
18
|
+
require 'mongo/utils/support'
|
19
|
+
require 'mongo/utils/thread_local_variable_manager'
|