mongo 2.12.1 → 2.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/mongo/address.rb +44 -30
- data/lib/mongo/collection/view/readable.rb +3 -1
- data/lib/mongo/grid/file.rb +5 -0
- data/lib/mongo/grid/file/chunk.rb +2 -0
- data/lib/mongo/grid/fs_bucket.rb +15 -13
- data/lib/mongo/grid/stream/write.rb +9 -3
- data/lib/mongo/protocol/message.rb +2 -2
- data/lib/mongo/protocol/serializers.rb +5 -2
- data/lib/mongo/version.rb +1 -1
- data/spec/integration/grid_fs_bucket_spec.rb +48 -0
- data/spec/integration/size_limit_spec.rb +4 -0
- data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +98 -0
- data/spec/integration/zlib_compression_spec.rb +25 -0
- data/spec/mongo/collection_spec.rb +24 -0
- metadata +786 -778
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32b9c6615d4d33e980ab07cba7c01992f0d370e9ee8c671fd01f9310c56c0cf2
|
4
|
+
data.tar.gz: a8344cf96ce4c9f69a92b5a8dc25ad38a70f8145d48ecf4c1eb89609a014ef06
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97cc2df80dce9acaaf2fcc5b1b55ffc9f1d6dc37d4903f522f4dcc2366a487ae9daafdd66a81c8e399f561c105e40377a2cc16ac03471dbb2a3ecab0054a2713
|
7
|
+
data.tar.gz: 758df1949b5f9018270fe11812580a91bf8fd55f56990cdd45b16127520919097ed9aeabc1a04ec1e32c393f41ec336228bbf82c5a9abd304b56b8793206a5ec
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/lib/mongo/address.rb
CHANGED
@@ -180,42 +180,44 @@ module Mongo
|
|
180
180
|
# @return [ Mongo::Socket::SSL | Mongo::Socket::TCP | Mongo::Socket::Unix ]
|
181
181
|
# The socket.
|
182
182
|
#
|
183
|
-
# @raise [
|
183
|
+
# @raise [ Mongo::Error ] If network connection failed.
|
184
184
|
#
|
185
185
|
# @since 2.0.0
|
186
186
|
def socket(socket_timeout, ssl_options = {}, options = {})
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
187
|
+
map_exceptions do
|
188
|
+
if seed.downcase =~ Unix::MATCH
|
189
|
+
specific_address = Unix.new(seed.downcase)
|
190
|
+
return specific_address.socket(socket_timeout, ssl_options, options)
|
191
|
+
end
|
192
|
+
|
193
|
+
options = {
|
194
|
+
connect_timeout: Server::CONNECT_TIMEOUT,
|
195
|
+
}.update(options)
|
191
196
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
return socket
|
214
|
-
rescue IOError, SystemCallError, Error::SocketTimeoutError, Error::SocketError => e
|
215
|
-
error = e
|
197
|
+
# When the driver connects to "localhost", it only attempts IPv4
|
198
|
+
# connections. When the driver connects to other hosts, it will
|
199
|
+
# attempt both IPv4 and IPv6 connections.
|
200
|
+
family = (host == LOCALHOST) ? ::Socket::AF_INET : ::Socket::AF_UNSPEC
|
201
|
+
error = nil
|
202
|
+
# Sometimes Socket#getaddrinfo returns the same info more than once
|
203
|
+
# (multiple identical items in the returned array). It does not make
|
204
|
+
# sense to try to connect to the same address more than once, thus
|
205
|
+
# eliminate duplicates here.
|
206
|
+
infos = ::Socket.getaddrinfo(host, nil, family, ::Socket::SOCK_STREAM)
|
207
|
+
results = infos.map do |info|
|
208
|
+
[info[4], info[3]]
|
209
|
+
end.uniq
|
210
|
+
results.each do |family, address_str|
|
211
|
+
begin
|
212
|
+
specific_address = FAMILY_MAP[family].new(address_str, port, host)
|
213
|
+
socket = specific_address.socket(socket_timeout, ssl_options, options)
|
214
|
+
return socket
|
215
|
+
rescue IOError, SystemCallError, Error::SocketTimeoutError, Error::SocketError => e
|
216
|
+
error = e
|
217
|
+
end
|
216
218
|
end
|
219
|
+
raise error
|
217
220
|
end
|
218
|
-
raise error
|
219
221
|
end
|
220
222
|
|
221
223
|
# Get the address as a string.
|
@@ -248,5 +250,17 @@ module Mongo
|
|
248
250
|
else IPv4.parse(address)
|
249
251
|
end
|
250
252
|
end
|
253
|
+
|
254
|
+
def map_exceptions
|
255
|
+
begin
|
256
|
+
yield
|
257
|
+
rescue Errno::ETIMEDOUT => e
|
258
|
+
raise Error::SocketTimeoutError, "#{e.class}: #{e} (for #{self})"
|
259
|
+
rescue IOError, SystemCallError => e
|
260
|
+
raise Error::SocketError, "#{e.class}: #{e} (for #{self})"
|
261
|
+
rescue OpenSSL::SSL::SSLError => e
|
262
|
+
raise Error::SocketError, "#{e.class}: #{e} (for #{self}) (#{SSL_ERROR})"
|
263
|
+
end
|
264
|
+
end
|
251
265
|
end
|
252
266
|
end
|
@@ -185,7 +185,9 @@ module Mongo
|
|
185
185
|
pipeline << { :'$limit' => opts[:limit] } if opts[:limit]
|
186
186
|
pipeline << { :'$group' => { _id: 1, n: { :'$sum' => 1 } } }
|
187
187
|
|
188
|
-
opts.select
|
188
|
+
opts = opts.select { |k, _| [:hint, :max_time_ms, :read, :collation, :session].include?(k) }
|
189
|
+
opts[:collation] ||= collation
|
190
|
+
|
189
191
|
first = aggregate(pipeline, opts).first
|
190
192
|
return 0 unless first
|
191
193
|
first['n'].to_i
|
data/lib/mongo/grid/file.rb
CHANGED
@@ -104,6 +104,11 @@ module Mongo
|
|
104
104
|
# chunk objects and assemble the data. If we have an IO object, then
|
105
105
|
# it's the original file data and we must split it into chunks and set
|
106
106
|
# the original data itself.
|
107
|
+
#
|
108
|
+
# @param [ IO, String, Array<BSON::Document> ] value The file object,
|
109
|
+
# file contents or chunk documents.
|
110
|
+
#
|
111
|
+
# @return [ Array<Grid::File::Chunk> ] Array of chunks.
|
107
112
|
def initialize_chunks!(value)
|
108
113
|
if value.is_a?(Array)
|
109
114
|
@chunks = value.map{ |doc| Chunk.new(doc) }
|
@@ -151,6 +151,7 @@ module Mongo
|
|
151
151
|
# @return [ String ] The assembled data.
|
152
152
|
#
|
153
153
|
# @since 2.0.0
|
154
|
+
# @api private
|
154
155
|
def assemble(chunks)
|
155
156
|
chunks.reduce(''){ |data, chunk| data << chunk.data.data }
|
156
157
|
end
|
@@ -167,6 +168,7 @@ module Mongo
|
|
167
168
|
# @return [ Array<Chunk> ] The chunks of the data.
|
168
169
|
#
|
169
170
|
# @since 2.0.0
|
171
|
+
# @api private
|
170
172
|
def split(io, file_info, offset = 0)
|
171
173
|
io = StringIO.new(io) if io.is_a?(String)
|
172
174
|
parts = Enumerator.new { |y| y << io.read(file_info.chunk_size) until io.eof? }
|
data/lib/mongo/grid/fs_bucket.rb
CHANGED
@@ -177,7 +177,7 @@ module Mongo
|
|
177
177
|
#
|
178
178
|
# @since 2.0.0
|
179
179
|
def prefix
|
180
|
-
@options[:fs_name] || @options[:bucket_name]|| DEFAULT_ROOT
|
180
|
+
@options[:fs_name] || @options[:bucket_name] || DEFAULT_ROOT
|
181
181
|
end
|
182
182
|
|
183
183
|
# Remove a single file from the GridFS.
|
@@ -230,7 +230,8 @@ module Mongo
|
|
230
230
|
#
|
231
231
|
# @since 2.1.0
|
232
232
|
def open_download_stream(id, options = nil)
|
233
|
-
|
233
|
+
options = Hash[(options || {}).map { |k, v| [k.to_sym, v] }]
|
234
|
+
read_stream(id, **options).tap do |stream|
|
234
235
|
if block_given?
|
235
236
|
begin
|
236
237
|
yield stream
|
@@ -348,15 +349,15 @@ module Mongo
|
|
348
349
|
download_to_stream(open_download_stream_by_name(filename, opts).file_id, io)
|
349
350
|
end
|
350
351
|
|
351
|
-
# Opens an upload stream to GridFS to which the contents of a
|
352
|
+
# Opens an upload stream to GridFS to which the contents of a file or
|
353
|
+
# blob can be written.
|
352
354
|
#
|
353
|
-
# @
|
354
|
-
# fs.open_upload_stream('a-file.txt')
|
355
|
-
#
|
356
|
-
# @param [ String ] filename The filename of the file to upload.
|
355
|
+
# @param [ String ] filename The name of the file in GridFS.
|
357
356
|
# @param [ Hash ] opts The options for the write stream.
|
358
357
|
#
|
359
|
-
# @option opts [ Object ] :file_id An optional unique file id.
|
358
|
+
# @option opts [ Object ] :file_id An optional unique file id.
|
359
|
+
# A BSON::ObjectId is automatically generated if a file id is not
|
360
|
+
# provided.
|
360
361
|
# @option opts [ Integer ] :chunk_size Override the default chunk size.
|
361
362
|
# @option opts [ Hash ] :metadata User data for the 'metadata' field of the files
|
362
363
|
# collection document.
|
@@ -375,7 +376,8 @@ module Mongo
|
|
375
376
|
#
|
376
377
|
# @since 2.1.0
|
377
378
|
def open_upload_stream(filename, opts = {})
|
378
|
-
|
379
|
+
opts = Hash[opts.map { |k, v| [k.to_sym, v] }]
|
380
|
+
write_stream(filename, **opts).tap do |stream|
|
379
381
|
if block_given?
|
380
382
|
begin
|
381
383
|
yield stream
|
@@ -462,12 +464,12 @@ module Mongo
|
|
462
464
|
#
|
463
465
|
# @option opts [ BSON::Document ] :file_info_doc For internal
|
464
466
|
# driver use only. A BSON document to use as file information.
|
465
|
-
def read_stream(id, opts
|
466
|
-
Stream.get(self, Stream::READ_MODE, { file_id: id }.update(options).update(opts
|
467
|
+
def read_stream(id, **opts)
|
468
|
+
Stream.get(self, Stream::READ_MODE, { file_id: id }.update(options).update(opts))
|
467
469
|
end
|
468
470
|
|
469
|
-
def write_stream(filename, opts)
|
470
|
-
Stream.get(self, Stream::WRITE_MODE, { filename: filename }.
|
471
|
+
def write_stream(filename, **opts)
|
472
|
+
Stream.get(self, Stream::WRITE_MODE, { filename: filename }.update(options).update(opts))
|
471
473
|
end
|
472
474
|
|
473
475
|
def chunks_name
|
@@ -82,12 +82,12 @@ module Mongo
|
|
82
82
|
@open = true
|
83
83
|
end
|
84
84
|
|
85
|
-
# Write to the GridFS bucket from the source stream.
|
85
|
+
# Write to the GridFS bucket from the source stream or a string.
|
86
86
|
#
|
87
87
|
# @example Write to GridFS.
|
88
88
|
# stream.write(io)
|
89
89
|
#
|
90
|
-
# @param [ IO ] io The
|
90
|
+
# @param [ String | IO ] io The string or IO object to upload from.
|
91
91
|
#
|
92
92
|
# @return [ Stream::Write ] self The write stream itself.
|
93
93
|
#
|
@@ -95,7 +95,13 @@ module Mongo
|
|
95
95
|
def write(io)
|
96
96
|
ensure_open!
|
97
97
|
@indexes ||= ensure_indexes!
|
98
|
-
@length += io.
|
98
|
+
@length += if io.respond_to?(:bytesize)
|
99
|
+
# String objects
|
100
|
+
io.bytesize
|
101
|
+
else
|
102
|
+
# IO objects
|
103
|
+
io.size
|
104
|
+
end
|
99
105
|
chunks = File::Chunk.split(io, file_info, @n)
|
100
106
|
@n += chunks.size
|
101
107
|
chunks_collection.insert_many(chunks) unless chunks.empty?
|
@@ -374,7 +374,7 @@ module Mongo
|
|
374
374
|
# each of the elements in this array using BSON types wherever possible.
|
375
375
|
#
|
376
376
|
# @return [Message] Message with deserialized array.
|
377
|
-
def self.deserialize_array(message, io, field, options)
|
377
|
+
def self.deserialize_array(message, io, field, options = {})
|
378
378
|
elements = []
|
379
379
|
count = message.instance_variable_get(field[:multi])
|
380
380
|
count.times { elements << field[:type].deserialize(io, options) }
|
@@ -392,7 +392,7 @@ module Mongo
|
|
392
392
|
# this field using BSON types wherever possible.
|
393
393
|
#
|
394
394
|
# @return [Message] Message with deserialized field.
|
395
|
-
def self.deserialize_field(message, io, field, options)
|
395
|
+
def self.deserialize_field(message, io, field, options = {})
|
396
396
|
message.instance_variable_set(
|
397
397
|
field[:name],
|
398
398
|
field[:type].deserialize(io, options)
|
@@ -440,12 +440,15 @@ module Mongo
|
|
440
440
|
# Deserializes bytes from the byte buffer.
|
441
441
|
#
|
442
442
|
# @param [ BSON::ByteBuffer ] buffer Buffer containing the value to read.
|
443
|
-
# @param [
|
443
|
+
# @param [ Hash ] options The method options.
|
444
|
+
#
|
445
|
+
# @option options [ Integer ] num_bytes Number of bytes to read.
|
444
446
|
#
|
445
447
|
# @return [ String ] The bytes.
|
446
448
|
#
|
447
449
|
# @since 2.5.0
|
448
|
-
def self.deserialize(buffer,
|
450
|
+
def self.deserialize(buffer, options = {})
|
451
|
+
num_bytes = options[:num_bytes]
|
449
452
|
buffer.get_bytes(num_bytes || buffer.length)
|
450
453
|
end
|
451
454
|
end
|
data/lib/mongo/version.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'GridFS bucket integration' do
|
4
|
+
let(:fs) do
|
5
|
+
authorized_client.database.fs
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'UTF-8 string write' do
|
9
|
+
let(:data) { "hello\u2210" }
|
10
|
+
|
11
|
+
before do
|
12
|
+
data.length.should_not == data.bytesize
|
13
|
+
end
|
14
|
+
|
15
|
+
shared_examples 'round-trips' do
|
16
|
+
it 'round-trips' do
|
17
|
+
stream = fs.open_upload_stream('test') do |stream|
|
18
|
+
stream.write(data_to_write)
|
19
|
+
end
|
20
|
+
|
21
|
+
actual = nil
|
22
|
+
fs.open_download_stream(stream.file_id) do |stream|
|
23
|
+
actual = stream.read
|
24
|
+
end
|
25
|
+
|
26
|
+
actual.encoding.name.should == 'ASCII-8BIT'
|
27
|
+
actual.should == data.dup.force_encoding('binary')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'in binary encoding' do
|
32
|
+
let(:data_to_write) do
|
33
|
+
data.force_encoding('binary').freeze
|
34
|
+
end
|
35
|
+
|
36
|
+
it_behaves_like 'round-trips'
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'in UTF-8 encoding' do
|
40
|
+
let(:data_to_write) do
|
41
|
+
data.encoding.name.should == 'UTF-8'
|
42
|
+
data.freeze
|
43
|
+
end
|
44
|
+
|
45
|
+
it_behaves_like 'round-trips'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -81,6 +81,10 @@ describe 'BSON & command size limits' do
|
|
81
81
|
end
|
82
82
|
|
83
83
|
it 'allows bulk writes of multiple documents of exactly 16 MiB each' do
|
84
|
+
if SpecConfig.instance.compressors
|
85
|
+
pending "RUBY-2234"
|
86
|
+
end
|
87
|
+
|
84
88
|
documents = []
|
85
89
|
1.upto(3) do |index|
|
86
90
|
document = { key: 'a' * (max_document_size - 28), _id: "in#{index}" }
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'BSON & command size limits' do
|
4
|
+
let(:max_document_size) { 16*1024*1024 }
|
5
|
+
|
6
|
+
before do
|
7
|
+
authorized_collection.delete_many
|
8
|
+
end
|
9
|
+
|
10
|
+
# This test uses a large document that is significantly smaller than the
|
11
|
+
# size limit. It is a basic sanity check.
|
12
|
+
it 'allows user-provided documents to be 15MiB' do
|
13
|
+
document = { key: 'a' * 15*1024*1024, _id: 'foo' }
|
14
|
+
|
15
|
+
authorized_collection.insert_one(document)
|
16
|
+
end
|
17
|
+
|
18
|
+
# This test uses a large document that is significantly larger than the
|
19
|
+
# size limit. It is a basic sanity check.
|
20
|
+
it 'fails single write of oversized documents' do
|
21
|
+
document = { key: 'a' * 17*1024*1024, _id: 'foo' }
|
22
|
+
|
23
|
+
lambda do
|
24
|
+
authorized_collection.insert_one(document)
|
25
|
+
end.should raise_error(Mongo::Error::MaxBSONSize, /The document exceeds maximum allowed BSON object size after serialization/)
|
26
|
+
end
|
27
|
+
|
28
|
+
# This test checks our bulk write splitting when documents are not close
|
29
|
+
# to the limit, but where splitting is definitely required.
|
30
|
+
it 'allows split bulk write of medium sized documents' do
|
31
|
+
# 8 documents of 4 MiB each = 32 MiB total data, should be split over
|
32
|
+
# either 2 or 3 bulk writes depending on how well the driver splits
|
33
|
+
documents = []
|
34
|
+
1.upto(8) do |index|
|
35
|
+
documents << { key: 'a' * 4*1024*1024, _id: "in#{index}" }
|
36
|
+
end
|
37
|
+
|
38
|
+
authorized_collection.insert_many(documents)
|
39
|
+
authorized_collection.count_documents({}).should == 8
|
40
|
+
end
|
41
|
+
|
42
|
+
# This test ensures that document which are too big definitely fail insertion.
|
43
|
+
it 'fails bulk write of oversized documents' do
|
44
|
+
documents = []
|
45
|
+
1.upto(3) do |index|
|
46
|
+
documents << { key: 'a' * 17*1024*1024, _id: "in#{index}" }
|
47
|
+
end
|
48
|
+
|
49
|
+
lambda do
|
50
|
+
authorized_collection.insert_many(documents)
|
51
|
+
end.should raise_error(Mongo::Error::MaxBSONSize, /The document exceeds maximum allowed BSON object size after serialization/)
|
52
|
+
authorized_collection.count_documents({}).should == 0
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'allows user-provided documents to be exactly 16MiB' do
|
56
|
+
# The document must contain the _id field, otherwise the server will
|
57
|
+
# add it which will increase the size of the document as persisted by
|
58
|
+
# the server.
|
59
|
+
document = { key: 'a' * (max_document_size - 28), _id: 'foo' }
|
60
|
+
expect(document.to_bson.length).to eq(max_document_size)
|
61
|
+
|
62
|
+
authorized_collection.insert_one(document)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'fails on the server when a document larger than 16MiB is inserted' do
|
66
|
+
document = { key: 'a' * (max_document_size - 27), _id: 'foo' }
|
67
|
+
expect(document.to_bson.length).to eq(max_document_size+1)
|
68
|
+
|
69
|
+
lambda do
|
70
|
+
authorized_collection.insert_one(document)
|
71
|
+
end.should raise_error(Mongo::Error::OperationFailure, /object to insert too large/)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'fails in the driver when a document larger than 16MiB+16KiB is inserted' do
|
75
|
+
document = { key: 'a' * (max_document_size - 27 + 16*1024), _id: 'foo' }
|
76
|
+
expect(document.to_bson.length).to eq(max_document_size+16*1024+1)
|
77
|
+
|
78
|
+
lambda do
|
79
|
+
authorized_collection.insert_one(document)
|
80
|
+
end.should raise_error(Mongo::Error::MaxBSONSize, /The document exceeds maximum allowed BSON object size after serialization/)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'allows bulk writes of multiple documents of exactly 16 MiB each' do
|
84
|
+
if SpecConfig.instance.compressors
|
85
|
+
pending "RUBY-2234"
|
86
|
+
end
|
87
|
+
|
88
|
+
documents = []
|
89
|
+
1.upto(3) do |index|
|
90
|
+
document = { key: 'a' * (max_document_size - 28), _id: "in#{index}" }
|
91
|
+
expect(document.to_bson.length).to eq(max_document_size)
|
92
|
+
documents << document
|
93
|
+
end
|
94
|
+
|
95
|
+
authorized_collection.insert_many(documents)
|
96
|
+
authorized_collection.count_documents({}).should == 3
|
97
|
+
end
|
98
|
+
end
|