Skip to content

Commit 64b9ea4

Browse files
p-mongop
authored andcommitted
Fix RUBY-1980 Ensure cursor IDs are always encoded as 64-bit integers in BSON for getMore (#1559)
1 parent ad24ff5 commit 64b9ea4

File tree

12 files changed

+69
-18
lines changed

12 files changed

+69
-18
lines changed

lib/mongo/collection/view/readable.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -602,15 +602,15 @@ def parallel_scan(cursor_count, options = {})
602602
cmd.execute(server).cursor_ids.map do |cursor_id|
603603
result = if server.features.find_command_enabled?
604604
Operation::GetMore.new({
605-
:selector => {:getMore => cursor_id,
605+
:selector => {:getMore => BSON::Int64.new(cursor_id),
606606
:collection => collection.name},
607607
:db_name => database.name,
608608
:session => session,
609609
}).execute(server)
610610
else
611611
Operation::GetMore.new({
612612
:to_return => 0,
613-
:cursor_id => cursor_id,
613+
:cursor_id => BSON::Int64.new(cursor_id),
614614
:db_name => database.name,
615615
:coll_name => collection.name
616616
}).execute(server)

lib/mongo/cursor/builder/get_more_command.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ def specification
5757
private
5858

5959
def get_more_command
60-
command = { :getMore => cursor.id, :collection => collection_name }
60+
command = {
61+
:getMore => BSON::Int64.new(cursor.id),
62+
:collection => collection_name,
63+
}
6164
command[:batchSize] = batch_size.abs if batch_size && batch_size != 0
6265
# If the max_await_time_ms option is set, then we set maxTimeMS on
6366
# the get more command.

lib/mongo/cursor/builder/op_get_more.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ def initialize(cursor)
5050
def specification
5151
{
5252
:to_return => to_return,
53-
:cursor_id => cursor.id,
53+
:cursor_id => BSON::Int64.new(cursor.id),
5454
:db_name => database.name,
55-
:coll_name => collection_name
55+
:coll_name => collection_name,
5656
}
5757
end
5858
end

lib/mongo/protocol/get_more.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ class Upconverter
110110
# The get more constant.
111111
#
112112
# @since 2.2.0
113+
# @deprecated
113114
GET_MORE = 'getMore'.freeze
114115

115116
# @return [ String ] collection The name of the collection.
@@ -148,7 +149,7 @@ def initialize(collection, cursor_id, number_to_return)
148149
# @since 2.1.0
149150
def command
150151
document = BSON::Document.new
151-
document.store(GET_MORE, cursor_id)
152+
document.store('getMore', BSON::Int64.new(cursor_id))
152153
document.store(Message::BATCH_SIZE, number_to_return)
153154
document.store(Message::COLLECTION, collection)
154155
document

lib/mongo/protocol/serializers.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,16 @@ def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys
102102
# Serializes and de-serializes one 32-bit integer.
103103
module Int32
104104

105-
# Serializes a fixnum to a 4-byte 32-bit integer
105+
# Serializes a number to a 32-bit integer
106106
#
107107
# @param buffer [ String ] Buffer to receive the serialized Int32.
108-
# @param value [ Fixnum ] 32-bit integer to be serialized.
108+
# @param value [ Integer | BSON::Int32 ] 32-bit integer to be serialized.
109109
#
110110
# @return [String] Buffer with serialized value.
111111
def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
112+
if value.is_a?(BSON::Int32)
113+
value = value.value
114+
end
112115
buffer.put_int32(value)
113116
end
114117

@@ -127,13 +130,16 @@ def self.deserialize(buffer)
127130
# Serializes and de-serializes one 64-bit integer.
128131
module Int64
129132

130-
# Serializes a fixnum to an 8-byte 64-bit integer
133+
# Serializes a number to a 64-bit integer
131134
#
132135
# @param buffer [ String ] Buffer to receive the serialized Int64.
133-
# @param value [ Fixnum ] 64-bit integer to be serialized.
136+
# @param value [ Integer | BSON::Int64 ] 64-bit integer to be serialized.
134137
#
135138
# @return [ String ] Buffer with serialized value.
136139
def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
140+
if value.is_a?(BSON::Int64)
141+
value = value.value
142+
end
137143
buffer.put_int64(value)
138144
end
139145

mongo.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@ Gem::Specification.new do |s|
3232
s.require_paths = ['lib']
3333
s.bindir = 'bin'
3434

35-
s.add_dependency 'bson', '>=4.4.2', '<5.0.0'
35+
s.add_dependency 'bson', '>=4.6.0', '<5.0.0'
3636
end

spec/integration/get_more_spec.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
require 'spec_helper'
2+
3+
describe 'getMore operation' do
4+
# https://jira.mongodb.org/browse/RUBY-1987
5+
min_server_fcv '3.2'
6+
7+
let(:collection) do
8+
subscribed_client['get_more_spec']
9+
end
10+
11+
let(:scope) do
12+
collection.find.batch_size(1).each
13+
end
14+
15+
before do
16+
collection.delete_many
17+
collection.insert_one(a: 1)
18+
#collection.insert_one(a: 2)
19+
EventSubscriber.clear_events!
20+
end
21+
22+
let(:get_more_command) do
23+
event = EventSubscriber.single_command_started_event('getMore')
24+
event.command['getMore']
25+
end
26+
27+
it 'sends cursor id as int64' do
28+
scope.to_a
29+
30+
expect(get_more_command).to be_a(BSON::Int64)
31+
end
32+
end

spec/mongo/bulk_write_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1924,11 +1924,11 @@
19241924
end
19251925

19261926
let(:first_txn_number) do
1927-
started_events[-2].command['txnNumber'].instance_variable_get(:@integer)
1927+
started_events[-2].command['txnNumber'].value
19281928
end
19291929

19301930
let(:second_txn_number) do
1931-
started_events[-1].command['txnNumber'].instance_variable_get(:@integer)
1931+
started_events[-1].command['txnNumber'].value
19321932
end
19331933

19341934
it 'inserts the documents' do

spec/mongo/cursor/builder/get_more_command_spec.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
describe '#specification' do
66

77
let(:reply) do
8-
Mongo::Protocol::Reply.allocate
8+
Mongo::Protocol::Reply.allocate.tap do |reply|
9+
allow(reply).to receive(:cursor_id).and_return(8000)
10+
end
911
end
1012

1113
let(:result) do
@@ -54,7 +56,7 @@
5456
end
5557

5658
it 'includes getMore with cursor id' do
57-
expect(selector[:getMore]).to eq(cursor.id)
59+
expect(selector[:getMore]).to eq(BSON::Int64.new(8000))
5860
end
5961

6062
it 'includes the collection name' do

spec/mongo/cursor/builder/op_get_more_spec.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
describe '#specification' do
66

77
let(:reply) do
8-
Mongo::Protocol::Reply.allocate
8+
Mongo::Protocol::Reply.allocate.tap do |reply|
9+
allow(reply).to receive(:cursor_id).and_return(8000)
10+
end
911
end
1012

1113
let(:result) do
@@ -38,7 +40,7 @@
3840
end
3941

4042
it 'includes the cursor id' do
41-
expect(specification[:cursor_id]).to eq(cursor.id)
43+
expect(specification[:cursor_id]).to eq(BSON::Int64.new(8000))
4244
end
4345

4446
it 'includes the database name' do

0 commit comments

Comments
 (0)