cql-rb 1.2.2 → 2.0.0.pre0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +4 -0
- data/README.md +139 -17
- data/lib/cql/client.rb +237 -8
- data/lib/cql/client/asynchronous_client.rb +138 -54
- data/lib/cql/client/asynchronous_prepared_statement.rb +41 -6
- data/lib/cql/client/authenticators.rb +46 -0
- data/lib/cql/client/batch.rb +115 -0
- data/lib/cql/client/connector.rb +255 -0
- data/lib/cql/client/execute_options_decoder.rb +25 -9
- data/lib/cql/client/keyspace_changer.rb +5 -5
- data/lib/cql/client/peer_discovery.rb +33 -0
- data/lib/cql/client/query_result.rb +124 -1
- data/lib/cql/client/request_runner.rb +4 -2
- data/lib/cql/client/synchronous_client.rb +14 -2
- data/lib/cql/client/synchronous_prepared_statement.rb +19 -1
- data/lib/cql/future.rb +97 -50
- data/lib/cql/io/connection.rb +0 -1
- data/lib/cql/io/io_reactor.rb +1 -1
- data/lib/cql/protocol.rb +8 -1
- data/lib/cql/protocol/cql_protocol_handler.rb +2 -2
- data/lib/cql/protocol/decoding.rb +10 -15
- data/lib/cql/protocol/frame_decoder.rb +2 -1
- data/lib/cql/protocol/frame_encoder.rb +5 -4
- data/lib/cql/protocol/requests/auth_response_request.rb +31 -0
- data/lib/cql/protocol/requests/batch_request.rb +59 -0
- data/lib/cql/protocol/requests/credentials_request.rb +1 -1
- data/lib/cql/protocol/requests/execute_request.rb +45 -17
- data/lib/cql/protocol/requests/options_request.rb +1 -1
- data/lib/cql/protocol/requests/prepare_request.rb +1 -1
- data/lib/cql/protocol/requests/query_request.rb +97 -5
- data/lib/cql/protocol/requests/register_request.rb +1 -1
- data/lib/cql/protocol/requests/startup_request.rb +4 -4
- data/lib/cql/protocol/response.rb +2 -2
- data/lib/cql/protocol/responses/auth_challenge_response.rb +25 -0
- data/lib/cql/protocol/responses/auth_success_response.rb +25 -0
- data/lib/cql/protocol/responses/authenticate_response.rb +1 -1
- data/lib/cql/protocol/responses/detailed_error_response.rb +1 -1
- data/lib/cql/protocol/responses/error_response.rb +3 -2
- data/lib/cql/protocol/responses/event_response.rb +3 -2
- data/lib/cql/protocol/responses/prepared_result_response.rb +10 -6
- data/lib/cql/protocol/responses/raw_rows_result_response.rb +27 -0
- data/lib/cql/protocol/responses/ready_response.rb +1 -1
- data/lib/cql/protocol/responses/result_response.rb +2 -2
- data/lib/cql/protocol/responses/rows_result_response.rb +43 -23
- data/lib/cql/protocol/responses/schema_change_event_response.rb +1 -1
- data/lib/cql/protocol/responses/schema_change_result_response.rb +1 -1
- data/lib/cql/protocol/responses/set_keyspace_result_response.rb +1 -1
- data/lib/cql/protocol/responses/status_change_event_response.rb +1 -1
- data/lib/cql/protocol/responses/supported_response.rb +1 -1
- data/lib/cql/protocol/responses/void_result_response.rb +1 -1
- data/lib/cql/protocol/type_converter.rb +2 -2
- data/lib/cql/uuid.rb +2 -2
- data/lib/cql/version.rb +1 -1
- data/spec/cql/client/asynchronous_client_spec.rb +493 -50
- data/spec/cql/client/asynchronous_prepared_statement_spec.rb +193 -11
- data/spec/cql/client/authenticators_spec.rb +56 -0
- data/spec/cql/client/batch_spec.rb +277 -0
- data/spec/cql/client/connector_spec.rb +606 -0
- data/spec/cql/client/execute_options_decoder_spec.rb +95 -0
- data/spec/cql/client/keyspace_changer_spec.rb +8 -8
- data/spec/cql/client/peer_discovery_spec.rb +92 -0
- data/spec/cql/client/query_result_spec.rb +352 -0
- data/spec/cql/client/request_runner_spec.rb +31 -5
- data/spec/cql/client/synchronous_client_spec.rb +44 -1
- data/spec/cql/client/synchronous_prepared_statement_spec.rb +63 -1
- data/spec/cql/future_spec.rb +50 -2
- data/spec/cql/protocol/cql_protocol_handler_spec.rb +16 -5
- data/spec/cql/protocol/decoding_spec.rb +16 -6
- data/spec/cql/protocol/encoding_spec.rb +3 -1
- data/spec/cql/protocol/frame_encoder_spec.rb +99 -50
- data/spec/cql/protocol/requests/auth_response_request_spec.rb +62 -0
- data/spec/cql/protocol/requests/batch_request_spec.rb +155 -0
- data/spec/cql/protocol/requests/credentials_request_spec.rb +1 -1
- data/spec/cql/protocol/requests/execute_request_spec.rb +184 -71
- data/spec/cql/protocol/requests/options_request_spec.rb +1 -1
- data/spec/cql/protocol/requests/prepare_request_spec.rb +1 -1
- data/spec/cql/protocol/requests/query_request_spec.rb +255 -32
- data/spec/cql/protocol/requests/register_request_spec.rb +1 -1
- data/spec/cql/protocol/requests/startup_request_spec.rb +12 -6
- data/spec/cql/protocol/responses/auth_challenge_response_spec.rb +31 -0
- data/spec/cql/protocol/responses/auth_success_response_spec.rb +31 -0
- data/spec/cql/protocol/responses/authenticate_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/detailed_error_response_spec.rb +14 -7
- data/spec/cql/protocol/responses/error_response_spec.rb +4 -2
- data/spec/cql/protocol/responses/event_response_spec.rb +7 -4
- data/spec/cql/protocol/responses/prepared_result_response_spec.rb +89 -34
- data/spec/cql/protocol/responses/raw_rows_result_response_spec.rb +66 -0
- data/spec/cql/protocol/responses/ready_response_spec.rb +1 -1
- data/spec/cql/protocol/responses/result_response_spec.rb +19 -7
- data/spec/cql/protocol/responses/rows_result_response_spec.rb +56 -11
- data/spec/cql/protocol/responses/schema_change_event_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/schema_change_result_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/set_keyspace_result_response_spec.rb +1 -1
- data/spec/cql/protocol/responses/status_change_event_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/supported_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/topology_change_event_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/void_result_response_spec.rb +1 -1
- data/spec/cql/protocol/type_converter_spec.rb +21 -4
- data/spec/cql/uuid_spec.rb +10 -3
- data/spec/integration/client_spec.rb +251 -28
- data/spec/integration/protocol_spec.rb +213 -62
- data/spec/integration/regression_spec.rb +4 -1
- data/spec/integration/uuid_spec.rb +4 -1
- data/spec/support/fake_io_reactor.rb +5 -5
- metadata +36 -7
- data/lib/cql/client/connection_helper.rb +0 -181
- data/spec/cql/client/connection_helper_spec.rb +0 -429
@@ -9,12 +9,13 @@ module Cql
|
|
9
9
|
@code, @message = args
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.decode!(buffer, trace_id=nil)
|
12
|
+
def self.decode!(protocol_version, buffer, length, trace_id=nil)
|
13
13
|
code = read_int!(buffer)
|
14
14
|
message = read_string!(buffer)
|
15
15
|
case code
|
16
16
|
when 0x1000, 0x1100, 0x1200, 0x2400, 0x2500
|
17
|
-
|
17
|
+
new_length = length - 4 - 4 - message.bytesize
|
18
|
+
DetailedErrorResponse.decode!(code, message, protocol_version, buffer, new_length)
|
18
19
|
else
|
19
20
|
new(code, message)
|
20
21
|
end
|
@@ -3,11 +3,12 @@
|
|
3
3
|
module Cql
|
4
4
|
module Protocol
|
5
5
|
class EventResponse < ResultResponse
|
6
|
-
def self.decode!(buffer, trace_id=nil)
|
6
|
+
def self.decode!(protocol_version, buffer, length, trace_id=nil)
|
7
7
|
type = read_string!(buffer)
|
8
8
|
impl = EVENT_TYPES[type]
|
9
9
|
raise UnsupportedEventTypeError, %(Unsupported event type: "#{type}") unless impl
|
10
|
-
|
10
|
+
new_length = length - 4 - type.bytesize
|
11
|
+
impl.decode!(protocol_version, buffer, new_length, trace_id)
|
11
12
|
end
|
12
13
|
|
13
14
|
private
|
@@ -3,17 +3,21 @@
|
|
3
3
|
module Cql
|
4
4
|
module Protocol
|
5
5
|
class PreparedResultResponse < ResultResponse
|
6
|
-
attr_reader :id, :metadata
|
6
|
+
attr_reader :id, :metadata, :result_metadata
|
7
7
|
|
8
|
-
def initialize(id, metadata, trace_id)
|
8
|
+
def initialize(id, metadata, result_metadata, trace_id)
|
9
9
|
super(trace_id)
|
10
|
-
@id, @metadata = id, metadata
|
10
|
+
@id, @metadata, @result_metadata = id, metadata, result_metadata
|
11
11
|
end
|
12
12
|
|
13
|
-
def self.decode!(buffer, trace_id=nil)
|
13
|
+
def self.decode!(protocol_version, buffer, length, trace_id=nil)
|
14
14
|
id = read_short_bytes!(buffer)
|
15
|
-
metadata = RowsResultResponse.read_metadata!(buffer)
|
16
|
-
|
15
|
+
metadata, _ = RowsResultResponse.read_metadata!(protocol_version, buffer)
|
16
|
+
result_metadata = nil
|
17
|
+
if protocol_version > 1
|
18
|
+
result_metadata, _, _ = RowsResultResponse.read_metadata!(protocol_version, buffer)
|
19
|
+
end
|
20
|
+
new(id, metadata, result_metadata, trace_id)
|
17
21
|
end
|
18
22
|
|
19
23
|
def eql?(other)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Cql
|
4
|
+
module Protocol
|
5
|
+
class RawRowsResultResponse < RowsResultResponse
|
6
|
+
def initialize(protocol_version, raw_rows, paging_state, trace_id)
|
7
|
+
super(nil, nil, paging_state, trace_id)
|
8
|
+
@protocol_version = protocol_version
|
9
|
+
@raw_rows = raw_rows
|
10
|
+
end
|
11
|
+
|
12
|
+
def materialize(metadata)
|
13
|
+
@metadata = metadata
|
14
|
+
@rows = RowsResultResponse.read_rows!(@protocol_version, @raw_rows, @metadata)
|
15
|
+
end
|
16
|
+
|
17
|
+
def rows
|
18
|
+
raise UnmaterializedRowsError, 'Not materialized!' unless @rows
|
19
|
+
@rows
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
%(RESULT ROWS (raw))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -9,11 +9,11 @@ module Cql
|
|
9
9
|
@trace_id = trace_id
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.decode!(buffer, trace_id=nil)
|
12
|
+
def self.decode!(protocol_version, buffer, length, trace_id=nil)
|
13
13
|
kind = read_int!(buffer)
|
14
14
|
impl = RESULT_TYPES[kind]
|
15
15
|
raise UnsupportedResultKindError, %(Unsupported result kind: #{kind}) unless impl
|
16
|
-
impl.decode!(buffer, trace_id)
|
16
|
+
impl.decode!(protocol_version, buffer, length - 4, trace_id)
|
17
17
|
end
|
18
18
|
|
19
19
|
def void?
|
@@ -3,16 +3,23 @@
|
|
3
3
|
module Cql
|
4
4
|
module Protocol
|
5
5
|
class RowsResultResponse < ResultResponse
|
6
|
-
attr_reader :rows, :metadata
|
6
|
+
attr_reader :rows, :metadata, :paging_state
|
7
7
|
|
8
|
-
def initialize(rows, metadata, trace_id)
|
8
|
+
def initialize(rows, metadata, paging_state, trace_id)
|
9
9
|
super(trace_id)
|
10
|
-
@rows, @metadata = rows, metadata
|
10
|
+
@rows, @metadata, @paging_state = rows, metadata, paging_state
|
11
11
|
end
|
12
12
|
|
13
|
-
def self.decode!(buffer, trace_id=nil)
|
14
|
-
|
15
|
-
|
13
|
+
def self.decode!(protocol_version, buffer, length, trace_id=nil)
|
14
|
+
original_buffer_length = buffer.length
|
15
|
+
column_specs, columns_count, paging_state = read_metadata!(protocol_version, buffer)
|
16
|
+
if column_specs.nil?
|
17
|
+
consumed_bytes = original_buffer_length - buffer.length
|
18
|
+
remaining_bytes = ByteBuffer.new(buffer.read(length - consumed_bytes))
|
19
|
+
RawRowsResultResponse.new(protocol_version, remaining_bytes, paging_state, trace_id)
|
20
|
+
else
|
21
|
+
new(read_rows!(protocol_version, buffer, column_specs), column_specs, paging_state, trace_id)
|
22
|
+
end
|
16
23
|
end
|
17
24
|
|
18
25
|
def to_s
|
@@ -43,6 +50,12 @@ module Cql
|
|
43
50
|
:inet,
|
44
51
|
].freeze
|
45
52
|
|
53
|
+
TYPE_CONVERTER = TypeConverter.new
|
54
|
+
|
55
|
+
GLOBAL_TABLES_SPEC_FLAG = 0x01
|
56
|
+
HAS_MORE_PAGES_FLAG = 0x02
|
57
|
+
NO_METADATA_FLAG = 0x04
|
58
|
+
|
46
59
|
def self.read_column_type!(buffer)
|
47
60
|
id, type = read_option!(buffer) do |id, b|
|
48
61
|
if id > 0 && id <= 0x10
|
@@ -64,35 +77,42 @@ module Cql
|
|
64
77
|
type
|
65
78
|
end
|
66
79
|
|
67
|
-
def self.read_metadata!(buffer)
|
80
|
+
def self.read_metadata!(protocol_version, buffer)
|
68
81
|
flags = read_int!(buffer)
|
69
82
|
columns_count = read_int!(buffer)
|
70
|
-
|
71
|
-
|
72
|
-
|
83
|
+
paging_state = nil
|
84
|
+
column_specs = nil
|
85
|
+
if flags & HAS_MORE_PAGES_FLAG != 0
|
86
|
+
paging_state = read_bytes!(buffer)
|
73
87
|
end
|
74
|
-
|
75
|
-
if
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
88
|
+
if flags & NO_METADATA_FLAG == 0
|
89
|
+
if flags & GLOBAL_TABLES_SPEC_FLAG != 0
|
90
|
+
global_keyspace_name = read_string!(buffer)
|
91
|
+
global_table_name = read_string!(buffer)
|
92
|
+
end
|
93
|
+
column_specs = columns_count.times.map do
|
94
|
+
if global_keyspace_name
|
95
|
+
keyspace_name = global_keyspace_name
|
96
|
+
table_name = global_table_name
|
97
|
+
else
|
98
|
+
keyspace_name = read_string!(buffer)
|
99
|
+
table_name = read_string!(buffer)
|
100
|
+
end
|
101
|
+
column_name = read_string!(buffer)
|
102
|
+
type = read_column_type!(buffer)
|
103
|
+
[keyspace_name, table_name, column_name, type]
|
81
104
|
end
|
82
|
-
column_name = read_string!(buffer)
|
83
|
-
type = read_column_type!(buffer)
|
84
|
-
[keyspace_name, table_name, column_name, type]
|
85
105
|
end
|
106
|
+
[column_specs, columns_count, paging_state]
|
86
107
|
end
|
87
108
|
|
88
|
-
def self.read_rows!(buffer, column_specs)
|
89
|
-
type_converter = TypeConverter.new
|
109
|
+
def self.read_rows!(protocol_version, buffer, column_specs)
|
90
110
|
rows_count = read_int!(buffer)
|
91
111
|
rows = []
|
92
112
|
rows_count.times do |row_index|
|
93
113
|
row = {}
|
94
114
|
column_specs.each do |column_spec|
|
95
|
-
row[column_spec[2]] =
|
115
|
+
row[column_spec[2]] = TYPE_CONVERTER.from_bytes(buffer, column_spec[3])
|
96
116
|
end
|
97
117
|
rows << row
|
98
118
|
end
|
@@ -10,7 +10,7 @@ module Cql
|
|
10
10
|
@change, @keyspace, @table = change, keyspace, table
|
11
11
|
end
|
12
12
|
|
13
|
-
def self.decode!(buffer, trace_id=nil)
|
13
|
+
def self.decode!(protocol_version, buffer, length, trace_id=nil)
|
14
14
|
new(read_string!(buffer), read_string!(buffer), read_string!(buffer), trace_id)
|
15
15
|
end
|
16
16
|
|
@@ -43,7 +43,7 @@ module Cql
|
|
43
43
|
when :list, :set
|
44
44
|
_, sub_type = type
|
45
45
|
if value
|
46
|
-
raw =
|
46
|
+
raw = ''
|
47
47
|
write_short(raw, value.size)
|
48
48
|
value.each do |element|
|
49
49
|
to_bytes(raw, sub_type, element, 2)
|
@@ -55,7 +55,7 @@ module Cql
|
|
55
55
|
when :map
|
56
56
|
_, key_type, value_type = type
|
57
57
|
if value
|
58
|
-
raw =
|
58
|
+
raw = ''
|
59
59
|
write_short(raw, value.size)
|
60
60
|
value.each do |key, value|
|
61
61
|
to_bytes(raw, key_type, key, 2)
|
data/lib/cql/uuid.rb
CHANGED
@@ -39,7 +39,7 @@ module Cql
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def hash
|
42
|
-
@h
|
42
|
+
@h = (@n & 0xffffffffffffffff) ^ ((@n >> 64) & 0xffffffffffffffff)
|
43
43
|
end
|
44
44
|
|
45
45
|
# Returns the numerical representation of this UUID
|
@@ -52,7 +52,7 @@ module Cql
|
|
52
52
|
|
53
53
|
# @private
|
54
54
|
def eql?(other)
|
55
|
-
other.
|
55
|
+
other.respond_to?(:value) && self.value == other.value
|
56
56
|
end
|
57
57
|
alias_method :==, :eql?
|
58
58
|
|
data/lib/cql/version.rb
CHANGED
@@ -10,10 +10,14 @@ module Cql
|
|
10
10
|
described_class.new(connection_options)
|
11
11
|
end
|
12
12
|
|
13
|
-
let :
|
13
|
+
let :default_connection_options do
|
14
14
|
{:host => 'example.com', :port => 12321, :io_reactor => io_reactor, :logger => logger}
|
15
15
|
end
|
16
16
|
|
17
|
+
let :connection_options do
|
18
|
+
default_connection_options
|
19
|
+
end
|
20
|
+
|
17
21
|
let :io_reactor do
|
18
22
|
FakeIoReactor.new
|
19
23
|
end
|
@@ -109,9 +113,11 @@ module Cql
|
|
109
113
|
Protocol::ReadyResponse.new
|
110
114
|
when Protocol::QueryRequest
|
111
115
|
case request.cql
|
116
|
+
when /USE\s+"?(\S+)"?/
|
117
|
+
Cql::Protocol::SetKeyspaceResultResponse.new($1, nil)
|
112
118
|
when /FROM system\.local/
|
113
119
|
row = {'host_id' => connection[:spec_host_id], 'data_center' => connection[:spec_data_center]}
|
114
|
-
Protocol::RowsResultResponse.new([row], local_metadata, nil)
|
120
|
+
Protocol::RowsResultResponse.new([row], local_metadata, nil, nil)
|
115
121
|
when /FROM system\.peers/
|
116
122
|
other_host_ids = connections.reject { |c| c[:spec_host_id] == connection[:spec_host_id] }.map { |c| c[:spec_host_id] }
|
117
123
|
until other_host_ids.size >= min_peers[0]
|
@@ -126,7 +132,7 @@ module Cql
|
|
126
132
|
'rpc_address' => bind_all_rpc_addresses ? IPAddr.new('0.0.0.0') : ip
|
127
133
|
}
|
128
134
|
end
|
129
|
-
Protocol::RowsResultResponse.new(rows, peer_metadata, nil)
|
135
|
+
Protocol::RowsResultResponse.new(rows, peer_metadata, nil, nil)
|
130
136
|
end
|
131
137
|
end
|
132
138
|
end
|
@@ -146,6 +152,16 @@ module Cql
|
|
146
152
|
connections.should have(1).item
|
147
153
|
end
|
148
154
|
|
155
|
+
it 'starts the IO reactor' do
|
156
|
+
client.connect.value
|
157
|
+
io_reactor.should be_running
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'fails when the IO reactor fails to start' do
|
161
|
+
io_reactor.stub(:start).and_return(Future.failed(StandardError.new('bork')))
|
162
|
+
expect { client.connect.value }.to raise_error('bork')
|
163
|
+
end
|
164
|
+
|
149
165
|
context 'when connecting to multiple hosts' do
|
150
166
|
before do
|
151
167
|
client.close.value
|
@@ -193,6 +209,129 @@ module Cql
|
|
193
209
|
end
|
194
210
|
end
|
195
211
|
|
212
|
+
context 'when negotiating protocol version' do
|
213
|
+
let :client do
|
214
|
+
described_class.new(connection_options.merge(protocol_version: 7))
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'tries decreasing protocol versions until one succeeds' do
|
218
|
+
counter = 0
|
219
|
+
handle_request do |request|
|
220
|
+
if counter < 3
|
221
|
+
counter += 1
|
222
|
+
Protocol::ErrorResponse.new(0x0a, 'Bork version, dummy!')
|
223
|
+
elsif counter == 3
|
224
|
+
counter += 1
|
225
|
+
Protocol::SupportedResponse.new('CQL_VERSION' => %w[3.0.0], 'COMPRESSION' => %w[lz4 snappy])
|
226
|
+
else
|
227
|
+
Protocol::RowsResultResponse.new([], [], nil, nil)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
client.connect.value
|
231
|
+
client.should be_connected
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'logs when it tries the next protocol version' do
|
235
|
+
logger.stub(:warn)
|
236
|
+
counter = 0
|
237
|
+
handle_request do |request|
|
238
|
+
if counter < 3
|
239
|
+
counter += 1
|
240
|
+
Protocol::ErrorResponse.new(0x0a, 'Bork version, dummy!')
|
241
|
+
elsif counter == 3
|
242
|
+
counter += 1
|
243
|
+
Protocol::SupportedResponse.new('CQL_VERSION' => %w[3.0.0], 'COMPRESSION' => %w[lz4 snappy])
|
244
|
+
else
|
245
|
+
Protocol::RowsResultResponse.new([], [], nil, nil)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
client.connect.value
|
249
|
+
logger.should have_received(:warn).with(/could not connect using protocol version 7 \(will try again with 6\): bork version, dummy!/i)
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'gives up when the protocol version is zero' do
|
253
|
+
handle_request do |request|
|
254
|
+
Protocol::ErrorResponse.new(0x0a, 'Bork version, dummy!')
|
255
|
+
end
|
256
|
+
expect { client.connect.value }.to raise_error(QueryError)
|
257
|
+
client.should_not be_connected
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'gives up when a non-protocol version related error is raised' do
|
261
|
+
counter = 0
|
262
|
+
handle_request do |request|
|
263
|
+
if counter == 4
|
264
|
+
Protocol::ErrorResponse.new(0x0a, 'Bork version, dummy!')
|
265
|
+
else
|
266
|
+
counter += 1
|
267
|
+
Protocol::ErrorResponse.new(0x1001, 'Get off my lawn!')
|
268
|
+
end
|
269
|
+
end
|
270
|
+
expect { client.connect.value }.to raise_error(/Get off my lawn/)
|
271
|
+
client.should_not be_connected
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'uses different authentication mechanisms for different protocol versions' do
|
275
|
+
client = described_class.new(connection_options.merge(protocol_version: 3, credentials: {username: 'foo', password: 'bar'}))
|
276
|
+
auth_requests = []
|
277
|
+
handle_request do |request|
|
278
|
+
case request
|
279
|
+
when Protocol::OptionsRequest
|
280
|
+
Protocol::SupportedResponse.new('CQL_VERSION' => %w[3.0.0], 'COMPRESSION' => %w[lz4 snappy])
|
281
|
+
when Protocol::StartupRequest
|
282
|
+
Protocol::AuthenticateResponse.new('org.apache.cassandra.auth.PasswordAuthenticator')
|
283
|
+
when Protocol::AuthResponseRequest
|
284
|
+
auth_requests << request
|
285
|
+
Protocol::ErrorResponse.new(0x0a, 'Bork version, dummy!')
|
286
|
+
when Protocol::CredentialsRequest
|
287
|
+
auth_requests << request
|
288
|
+
Protocol::ErrorResponse.new(0x0a, 'Bork version, dummy!')
|
289
|
+
else
|
290
|
+
Protocol::ErrorResponse.new(0x0a, 'Bork version, dummy!')
|
291
|
+
end
|
292
|
+
end
|
293
|
+
client.connect.value rescue nil
|
294
|
+
auth_requests[0].should be_a(Protocol::AuthResponseRequest)
|
295
|
+
auth_requests[1].should be_a(Protocol::AuthResponseRequest)
|
296
|
+
auth_requests[2].should be_a(Protocol::CredentialsRequest)
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'fails authenticating when an auth provider has been specified but the protocol is negotiated to v1' do
|
300
|
+
client = described_class.new(connection_options.merge(protocol_version: 2, auth_provider: double(:auth_provider)))
|
301
|
+
counter = 0
|
302
|
+
handle_request do |request|
|
303
|
+
if counter == 0
|
304
|
+
counter += 1
|
305
|
+
Protocol::ErrorResponse.new(0x0a, 'Bork version, dummy!')
|
306
|
+
else
|
307
|
+
case request
|
308
|
+
when Protocol::OptionsRequest
|
309
|
+
Protocol::SupportedResponse.new('CQL_VERSION' => %w[3.0.0], 'COMPRESSION' => %w[lz4 snappy])
|
310
|
+
when Protocol::StartupRequest
|
311
|
+
Protocol::AuthenticateResponse.new('org.apache.cassandra.auth.PasswordAuthenticator')
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
expect { client.connect.value }.to raise_error(AuthenticationError)
|
316
|
+
counter.should == 1
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'defaults to different CQL versions for the different protocols' do
|
320
|
+
cql_versions = []
|
321
|
+
handle_request do |request|
|
322
|
+
if request.is_a?(Protocol::StartupRequest)
|
323
|
+
cql_versions << request.options['CQL_VERSION']
|
324
|
+
end
|
325
|
+
nil
|
326
|
+
end
|
327
|
+
[1, 2, 3].map do |protocol_version|
|
328
|
+
client = described_class.new(connection_options.merge(protocol_version: protocol_version))
|
329
|
+
client.connect.value
|
330
|
+
end
|
331
|
+
cql_versions.should == ['3.0.0', '3.1.0', '3.1.0']
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
196
335
|
it 'returns itself' do
|
197
336
|
client.connect.value.should equal(client)
|
198
337
|
end
|
@@ -213,6 +352,13 @@ module Cql
|
|
213
352
|
client.keyspace.should be_nil
|
214
353
|
end
|
215
354
|
|
355
|
+
it 'sends the specified CQL version in the startup message' do
|
356
|
+
c = described_class.new(connection_options.merge(cql_version: '5.0.1'))
|
357
|
+
c.connect.value
|
358
|
+
request = requests.find { |rq| rq.is_a?(Protocol::StartupRequest) }
|
359
|
+
request.options.should include('CQL_VERSION' => '5.0.1')
|
360
|
+
end
|
361
|
+
|
216
362
|
it 'enables compression when a compressor is specified' do
|
217
363
|
handle_request do |request|
|
218
364
|
case request
|
@@ -227,22 +373,54 @@ module Cql
|
|
227
373
|
request.options.should include('COMPRESSION' => 'lz4')
|
228
374
|
end
|
229
375
|
|
376
|
+
it 'does not enable compression when the algorithm is not supported' do
|
377
|
+
handle_request do |request|
|
378
|
+
case request
|
379
|
+
when Protocol::OptionsRequest
|
380
|
+
Protocol::SupportedResponse.new('CQL_VERSION' => %w[3.0.0], 'COMPRESSION' => %w[snappy])
|
381
|
+
end
|
382
|
+
end
|
383
|
+
compressor = double(:compressor, algorithm: 'lz4')
|
384
|
+
c = described_class.new(connection_options.merge(compressor: compressor))
|
385
|
+
c.connect.value
|
386
|
+
request = requests.find { |rq| rq.is_a?(Protocol::StartupRequest) }
|
387
|
+
request.options.should_not include('COMPRESSION' => 'lz4')
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'logs a warning when compression was disabled because the algorithm was not supported' do
|
391
|
+
logger.stub(:warn)
|
392
|
+
handle_request do |request|
|
393
|
+
case request
|
394
|
+
when Protocol::OptionsRequest
|
395
|
+
Protocol::SupportedResponse.new('CQL_VERSION' => %w[3.0.0], 'COMPRESSION' => %w[snappy])
|
396
|
+
end
|
397
|
+
end
|
398
|
+
compressor = double(:compressor, algorithm: 'lz4')
|
399
|
+
c = described_class.new(connection_options.merge(compressor: compressor))
|
400
|
+
c.connect.value
|
401
|
+
logger.should have_received(:warn).with(/not supported/)
|
402
|
+
end
|
403
|
+
|
230
404
|
it 'changes to the keyspace given as an option' do
|
231
405
|
c = described_class.new(connection_options.merge(:keyspace => 'hello_world'))
|
232
406
|
c.connect.value
|
233
|
-
request = requests.find { |rq| rq == Protocol::QueryRequest.new('USE hello_world', :one) }
|
407
|
+
request = requests.find { |rq| rq == Protocol::QueryRequest.new('USE hello_world', nil, nil, :one) }
|
234
408
|
request.should_not be_nil, 'expected a USE request to have been sent'
|
235
409
|
end
|
236
410
|
|
237
411
|
it 'validates the keyspace name before sending the USE command' do
|
238
412
|
c = described_class.new(connection_options.merge(:keyspace => 'system; DROP KEYSPACE system'))
|
239
413
|
expect { c.connect.value }.to raise_error(InvalidKeyspaceNameError)
|
240
|
-
requests.should_not include(Protocol::QueryRequest.new('USE system; DROP KEYSPACE system', :one))
|
414
|
+
requests.should_not include(Protocol::QueryRequest.new('USE system; DROP KEYSPACE system', nil, nil, :one))
|
241
415
|
end
|
242
416
|
|
243
417
|
context 'with automatic peer discovery' do
|
244
418
|
include_context 'peer discovery setup'
|
245
419
|
|
420
|
+
let :connection_options do
|
421
|
+
default_connection_options.merge(keyspace: 'foo')
|
422
|
+
end
|
423
|
+
|
246
424
|
it 'connects to the other nodes in the cluster' do
|
247
425
|
client.connect.value
|
248
426
|
connections.should have(3).items
|
@@ -290,6 +468,15 @@ module Cql
|
|
290
468
|
client.connect.value
|
291
469
|
connections.should have(2).items
|
292
470
|
end
|
471
|
+
|
472
|
+
it 'makes sure the new connections use the specified initial keyspace' do
|
473
|
+
client.connect.value
|
474
|
+
use_keyspace_requests = connections.map { |c| c.requests.find { |r| r.is_a?(Protocol::QueryRequest) && r.cql.include?('USE') }}
|
475
|
+
use_keyspace_requests.should have(3).items
|
476
|
+
use_keyspace_requests.each do |rq|
|
477
|
+
rq.cql.should match(/USE foo/)
|
478
|
+
end
|
479
|
+
end
|
293
480
|
end
|
294
481
|
|
295
482
|
it 're-raises any errors raised' do
|
@@ -322,21 +509,36 @@ module Cql
|
|
322
509
|
end
|
323
510
|
|
324
511
|
context 'when the server requests authentication' do
|
512
|
+
let :auth_provider do
|
513
|
+
PlainTextAuthProvider.new('foo', 'bar')
|
514
|
+
end
|
515
|
+
|
325
516
|
def accepting_request_handler(request, *)
|
326
517
|
case request
|
327
518
|
when Protocol::StartupRequest
|
328
|
-
Protocol::AuthenticateResponse.new('
|
519
|
+
Protocol::AuthenticateResponse.new('org.apache.cassandra.auth.PasswordAuthenticator')
|
329
520
|
when Protocol::CredentialsRequest
|
330
521
|
Protocol::ReadyResponse.new
|
522
|
+
when Protocol::AuthResponseRequest
|
523
|
+
Protocol::AuthSuccessResponse.new('hello!')
|
331
524
|
end
|
332
525
|
end
|
333
526
|
|
334
527
|
def denying_request_handler(request, *)
|
335
528
|
case request
|
336
529
|
when Protocol::StartupRequest
|
337
|
-
Protocol::AuthenticateResponse.new('
|
530
|
+
Protocol::AuthenticateResponse.new('org.apache.cassandra.auth.PasswordAuthenticator')
|
338
531
|
when Protocol::CredentialsRequest
|
339
532
|
Protocol::ErrorResponse.new(256, 'No way, José')
|
533
|
+
when Protocol::AuthResponseRequest
|
534
|
+
Protocol::ErrorResponse.new(256, 'No way, José')
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
def custom_request_handler(request, *)
|
539
|
+
case request
|
540
|
+
when Protocol::StartupRequest
|
541
|
+
Protocol::AuthenticateResponse.new('org.acme.Auth')
|
340
542
|
end
|
341
543
|
end
|
342
544
|
|
@@ -344,11 +546,34 @@ module Cql
|
|
344
546
|
handle_request(&method(:accepting_request_handler))
|
345
547
|
end
|
346
548
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
549
|
+
context 'with protocol v1' do
|
550
|
+
it 'uses an auth provider to authenticate' do
|
551
|
+
client = described_class.new(connection_options.merge(credentials: {:username => 'foo', :password => 'bar'}, protocol_version: 1))
|
552
|
+
client.connect.value
|
553
|
+
request = requests.find { |rq| rq.is_a?(Protocol::CredentialsRequest) }
|
554
|
+
request.credentials.should eql(:username => 'foo', :password => 'bar')
|
555
|
+
end
|
556
|
+
|
557
|
+
it 'fails to authenticate when only an auth provider has been specified' do
|
558
|
+
client = described_class.new(connection_options.merge(auth_provider: auth_provider, protocol_version: 1))
|
559
|
+
expect { client.connect.value }.to raise_error(AuthenticationError)
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
context 'with protocol v2' do
|
564
|
+
it 'uses an auth provider to authenticate' do
|
565
|
+
client = described_class.new(connection_options.merge(auth_provider: auth_provider))
|
566
|
+
client.connect.value
|
567
|
+
request = requests.find { |rq| rq == Protocol::AuthResponseRequest.new("\x00foo\x00bar") }
|
568
|
+
request.should_not be_nil, 'expected an auth response request to have been sent'
|
569
|
+
end
|
570
|
+
|
571
|
+
it 'creates a PlainTextAuthProvider when the :credentials options is given' do
|
572
|
+
client = described_class.new(connection_options.merge(credentials: {:username => 'foo', :password => 'bar'}))
|
573
|
+
client.connect.value
|
574
|
+
request = requests.find { |rq| rq == Protocol::AuthResponseRequest.new("\x00foo\x00bar") }
|
575
|
+
request.should_not be_nil, 'expected a auth response request to have been sent'
|
576
|
+
end
|
352
577
|
end
|
353
578
|
|
354
579
|
it 'raises an error when no credentials have been given' do
|
@@ -358,13 +583,19 @@ module Cql
|
|
358
583
|
|
359
584
|
it 'raises an error when the server responds with an error to the credentials request' do
|
360
585
|
handle_request(&method(:denying_request_handler))
|
361
|
-
client = described_class.new(connection_options.merge(
|
586
|
+
client = described_class.new(connection_options.merge(connection_options.merge(auth_provider: auth_provider)))
|
587
|
+
expect { client.connect.value }.to raise_error(AuthenticationError)
|
588
|
+
end
|
589
|
+
|
590
|
+
it 'raises an error when the server requests authentication that the auth provider does not support' do
|
591
|
+
handle_request(&method(:custom_request_handler))
|
592
|
+
client = described_class.new(connection_options.merge(connection_options.merge(auth_provider: auth_provider)))
|
362
593
|
expect { client.connect.value }.to raise_error(AuthenticationError)
|
363
594
|
end
|
364
595
|
|
365
596
|
it 'shuts down the client when there is an authentication error' do
|
366
597
|
handle_request(&method(:denying_request_handler))
|
367
|
-
client = described_class.new(connection_options.merge(
|
598
|
+
client = described_class.new(connection_options.merge(connection_options.merge(auth_provider: auth_provider)))
|
368
599
|
client.connect.value rescue nil
|
369
600
|
client.should_not be_connected
|
370
601
|
io_reactor.should_not be_running
|
@@ -403,6 +634,38 @@ module Cql
|
|
403
634
|
client.close.value
|
404
635
|
expect { client.connect.value }.to raise_error(ClientError)
|
405
636
|
end
|
637
|
+
|
638
|
+
it 'waits for #connect to complete before attempting to close' do
|
639
|
+
order = []
|
640
|
+
reactor_start_promise = Promise.new
|
641
|
+
io_reactor.stub(:start).and_return(reactor_start_promise.future)
|
642
|
+
io_reactor.stub(:stop).and_return(Future.resolved)
|
643
|
+
connected = client.connect
|
644
|
+
connected.on_value { order << :connected }
|
645
|
+
closed = client.close
|
646
|
+
closed.on_value { order << :closed }
|
647
|
+
connected.should_not be_completed
|
648
|
+
reactor_start_promise.fulfill
|
649
|
+
connected.value
|
650
|
+
closed.value
|
651
|
+
order.should == [:connected, :closed]
|
652
|
+
end
|
653
|
+
|
654
|
+
it 'waits for #connect to complete before attempting to close, when #connect fails' do
|
655
|
+
order = []
|
656
|
+
reactor_start_promise = Promise.new
|
657
|
+
io_reactor.stub(:start).and_return(reactor_start_promise.future)
|
658
|
+
io_reactor.stub(:stop).and_return(Future.resolved)
|
659
|
+
connected = client.connect
|
660
|
+
connected.on_failure { order << :connect_failed }
|
661
|
+
closed = client.close
|
662
|
+
closed.on_value { order << :closed }
|
663
|
+
connected.should_not be_completed
|
664
|
+
reactor_start_promise.fail(StandardError.new('bork'))
|
665
|
+
connected.value rescue nil
|
666
|
+
closed.value
|
667
|
+
order.should == [:connect_failed, :closed]
|
668
|
+
end
|
406
669
|
end
|
407
670
|
|
408
671
|
describe '#use' do
|
@@ -414,7 +677,7 @@ module Cql
|
|
414
677
|
end
|
415
678
|
client.connect.value
|
416
679
|
client.use('system').value
|
417
|
-
last_request.should == Protocol::QueryRequest.new('USE system', :one)
|
680
|
+
last_request.should == Protocol::QueryRequest.new('USE system', nil, nil, :one)
|
418
681
|
end
|
419
682
|
|
420
683
|
it 'executes a USE query for each connection' do
|
@@ -428,9 +691,9 @@ module Cql
|
|
428
691
|
c.use('system').value
|
429
692
|
last_requests = connections.select { |c| c.host =~ /^h\d\.example\.com$/ }.sort_by(&:host).map { |c| c.requests.last }
|
430
693
|
last_requests.should == [
|
431
|
-
Protocol::QueryRequest.new('USE system', :one),
|
432
|
-
Protocol::QueryRequest.new('USE system', :one),
|
433
|
-
Protocol::QueryRequest.new('USE system', :one)
|
694
|
+
Protocol::QueryRequest.new('USE system', nil, nil, :one),
|
695
|
+
Protocol::QueryRequest.new('USE system', nil, nil, :one),
|
696
|
+
Protocol::QueryRequest.new('USE system', nil, nil, :one),
|
434
697
|
]
|
435
698
|
end
|
436
699
|
|
@@ -449,17 +712,6 @@ module Cql
|
|
449
712
|
client.connect.value
|
450
713
|
expect { client.use('system; DROP KEYSPACE system').value }.to raise_error(InvalidKeyspaceNameError)
|
451
714
|
end
|
452
|
-
|
453
|
-
it 'allows the keyspace name to be quoted' do
|
454
|
-
handle_request do |request|
|
455
|
-
if request.is_a?(Protocol::QueryRequest) && request.cql == 'USE "system"'
|
456
|
-
Protocol::SetKeyspaceResultResponse.new('system', nil)
|
457
|
-
end
|
458
|
-
end
|
459
|
-
client.connect.value
|
460
|
-
client.use('"system"').value
|
461
|
-
client.keyspace.should == "system"
|
462
|
-
end
|
463
715
|
end
|
464
716
|
|
465
717
|
describe '#execute' do
|
@@ -473,24 +725,46 @@ module Cql
|
|
473
725
|
|
474
726
|
it 'asks the connection to execute the query using the default consistency level' do
|
475
727
|
client.execute(cql).value
|
476
|
-
last_request.should == Protocol::QueryRequest.new(cql, :quorum)
|
728
|
+
last_request.should == Protocol::QueryRequest.new(cql, nil, nil, :quorum)
|
477
729
|
end
|
478
730
|
|
479
731
|
it 'uses the consistency specified when the client was created' do
|
480
732
|
client = described_class.new(connection_options.merge(default_consistency: :all))
|
481
733
|
client.connect.value
|
482
734
|
client.execute(cql).value
|
483
|
-
last_request.should == Protocol::QueryRequest.new(cql, :all)
|
735
|
+
last_request.should == Protocol::QueryRequest.new(cql, nil, nil, :all)
|
484
736
|
end
|
485
737
|
|
486
738
|
it 'uses the consistency given as last argument' do
|
487
739
|
client.execute('UPDATE stuff SET thing = 1 WHERE id = 3', :three).value
|
488
|
-
last_request.should == Protocol::QueryRequest.new('UPDATE stuff SET thing = 1 WHERE id = 3', :three)
|
740
|
+
last_request.should == Protocol::QueryRequest.new('UPDATE stuff SET thing = 1 WHERE id = 3', nil, nil, :three)
|
489
741
|
end
|
490
742
|
|
491
743
|
it 'uses the consistency given as an option' do
|
492
744
|
client.execute('UPDATE stuff SET thing = 1 WHERE id = 3', consistency: :local_quorum).value
|
493
|
-
last_request.should == Protocol::QueryRequest.new('UPDATE stuff SET thing = 1 WHERE id = 3', :local_quorum)
|
745
|
+
last_request.should == Protocol::QueryRequest.new('UPDATE stuff SET thing = 1 WHERE id = 3', nil, nil, :local_quorum)
|
746
|
+
end
|
747
|
+
|
748
|
+
context 'with multiple arguments' do
|
749
|
+
it 'passes the arguments as bound values' do
|
750
|
+
client.execute('UPDATE stuff SET thing = ? WHERE id = ?', 'foo', 'bar').value
|
751
|
+
last_request.should == Protocol::QueryRequest.new('UPDATE stuff SET thing = ? WHERE id = ?', ['foo', 'bar'], nil, :quorum)
|
752
|
+
end
|
753
|
+
|
754
|
+
it 'passes the type hints option to the request' do
|
755
|
+
client.execute('UPDATE stuff SET thing = ? WHERE id = ?', 'foo', 3, type_hints: [nil, :int]).value
|
756
|
+
last_request.should == Protocol::QueryRequest.new('UPDATE stuff SET thing = ? WHERE id = ?', ['foo', 3], [nil, :int], :quorum)
|
757
|
+
end
|
758
|
+
|
759
|
+
it 'detects when the last argument is the consistency' do
|
760
|
+
client.execute('UPDATE stuff SET thing = ? WHERE id = ?', 'foo', 'bar', :each_quorum).value
|
761
|
+
last_request.should == Protocol::QueryRequest.new('UPDATE stuff SET thing = ? WHERE id = ?', ['foo', 'bar'], nil, :each_quorum)
|
762
|
+
end
|
763
|
+
|
764
|
+
it 'detects when the last arguments is an options hash' do
|
765
|
+
client.execute('UPDATE stuff SET thing = ? WHERE id = ?', 'foo', 'bar', consistency: :all, tracing: true).value
|
766
|
+
last_request.should == Protocol::QueryRequest.new('UPDATE stuff SET thing = ? WHERE id = ?', ['foo', 'bar'], nil, :all, nil, nil, nil, true)
|
767
|
+
end
|
494
768
|
end
|
495
769
|
|
496
770
|
context 'with a void CQL query' do
|
@@ -545,9 +819,9 @@ module Cql
|
|
545
819
|
|
546
820
|
last_requests = connections.select { |c| c.host =~ /^h\d\.example\.com$/ }.sort_by(&:host).map { |c| c.requests.last }
|
547
821
|
last_requests.should == [
|
548
|
-
Protocol::QueryRequest.new('USE system', :one),
|
549
|
-
Protocol::QueryRequest.new('USE system', :one),
|
550
|
-
Protocol::QueryRequest.new('USE system', :one)
|
822
|
+
Protocol::QueryRequest.new('USE system', nil, nil, :one),
|
823
|
+
Protocol::QueryRequest.new('USE system', nil, nil, :one),
|
824
|
+
Protocol::QueryRequest.new('USE system', nil, nil, :one),
|
551
825
|
]
|
552
826
|
end
|
553
827
|
end
|
@@ -568,7 +842,7 @@ module Cql
|
|
568
842
|
before do
|
569
843
|
handle_request do |request|
|
570
844
|
if request.is_a?(Protocol::QueryRequest) && request.cql =~ /FROM things/
|
571
|
-
Protocol::RowsResultResponse.new(rows, metadata, nil)
|
845
|
+
Protocol::RowsResultResponse.new(rows, metadata, nil, nil)
|
572
846
|
end
|
573
847
|
end
|
574
848
|
end
|
@@ -662,13 +936,76 @@ module Cql
|
|
662
936
|
trace_id = Uuid.new('a1028490-3f05-11e3-9531-fb72eff05fbb')
|
663
937
|
handle_request do |request|
|
664
938
|
if request.is_a?(Protocol::QueryRequest) && request.cql == cql
|
665
|
-
Protocol::RowsResultResponse.new([], [], trace_id)
|
939
|
+
Protocol::RowsResultResponse.new([], [], nil, trace_id)
|
666
940
|
end
|
667
941
|
end
|
668
942
|
result = client.execute(cql, trace: true).value
|
669
943
|
result.trace_id.should == trace_id
|
670
944
|
end
|
671
945
|
end
|
946
|
+
|
947
|
+
context 'with paging' do
|
948
|
+
let :cql do
|
949
|
+
'SELECT * FROM foo'
|
950
|
+
end
|
951
|
+
|
952
|
+
let :rows do
|
953
|
+
[['xyz', 'abc'], ['abc', 'xyz'], ['123', 'xyz']]
|
954
|
+
end
|
955
|
+
|
956
|
+
let :metadata do
|
957
|
+
[['thingies', 'things', 'thing', :text], ['thingies', 'things', 'item', :text]]
|
958
|
+
end
|
959
|
+
|
960
|
+
before do
|
961
|
+
handle_request do |request|
|
962
|
+
if request.is_a?(Protocol::QueryRequest) && request.cql == cql
|
963
|
+
if request.paging_state.nil?
|
964
|
+
Protocol::RowsResultResponse.new(rows.take(2), metadata, 'foobar', nil)
|
965
|
+
elsif request.paging_state == 'foobar'
|
966
|
+
Protocol::RowsResultResponse.new(rows.drop(2), metadata, nil, nil)
|
967
|
+
end
|
968
|
+
end
|
969
|
+
end
|
970
|
+
end
|
971
|
+
|
972
|
+
it 'sets the page size' do
|
973
|
+
client.execute(cql, page_size: 100).value
|
974
|
+
last_request.page_size.should == 100
|
975
|
+
end
|
976
|
+
|
977
|
+
it 'sets the page size and paging state' do
|
978
|
+
client.execute(cql, page_size: 100, paging_state: 'foobar').value
|
979
|
+
last_request.page_size.should == 100
|
980
|
+
last_request.paging_state.should == 'foobar'
|
981
|
+
end
|
982
|
+
|
983
|
+
it 'returns a result which can load the next page' do
|
984
|
+
result = client.execute(cql, page_size: 2).value
|
985
|
+
result.next_page.value
|
986
|
+
last_request.paging_state.should == 'foobar'
|
987
|
+
end
|
988
|
+
|
989
|
+
it 'returns a result which knows when there are no more pages' do
|
990
|
+
result = client.execute(cql, page_size: 2).value
|
991
|
+
result = result.next_page.value
|
992
|
+
result.should be_last_page
|
993
|
+
end
|
994
|
+
|
995
|
+
it 'passes the same options when loading the next page' do
|
996
|
+
sent_timeout = nil
|
997
|
+
handle_request do |_, _, _, timeout|
|
998
|
+
sent_timeout = timeout
|
999
|
+
nil
|
1000
|
+
end
|
1001
|
+
result = client.execute('SELECT * FROM something WHERE x = ? AND y = ?', 'foo', 'bar', page_size: 100, paging_state: 'foobar', trace: true, timeout: 3, type_hints: [:varchar, nil]).value
|
1002
|
+
result.next_page.value
|
1003
|
+
last_request.trace.should be_true
|
1004
|
+
last_request.values.should == ['foo', 'bar']
|
1005
|
+
last_request.type_hints.should == [:varchar, nil]
|
1006
|
+
sent_timeout.should == 3
|
1007
|
+
end
|
1008
|
+
end
|
672
1009
|
end
|
673
1010
|
|
674
1011
|
describe '#prepare' do
|
@@ -687,7 +1024,7 @@ module Cql
|
|
687
1024
|
before do
|
688
1025
|
handle_request do |request|
|
689
1026
|
if request.is_a?(Protocol::PrepareRequest)
|
690
|
-
Protocol::PreparedResultResponse.new(id, metadata, nil)
|
1027
|
+
Protocol::PreparedResultResponse.new(id, metadata, nil, nil)
|
691
1028
|
end
|
692
1029
|
end
|
693
1030
|
end
|
@@ -709,7 +1046,7 @@ module Cql
|
|
709
1046
|
it 'executes a prepared statement using the default consistency level' do
|
710
1047
|
statement = client.prepare(cql).value
|
711
1048
|
statement.execute('foo').value
|
712
|
-
last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['foo'], :quorum)
|
1049
|
+
last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['foo'], true, :quorum)
|
713
1050
|
end
|
714
1051
|
|
715
1052
|
it 'executes a prepared statement using the consistency specified when the client was created' do
|
@@ -717,7 +1054,7 @@ module Cql
|
|
717
1054
|
client.connect.value
|
718
1055
|
statement = client.prepare(cql).value
|
719
1056
|
statement.execute('foo').value
|
720
|
-
last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['foo'], :all)
|
1057
|
+
last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['foo'], true, :all)
|
721
1058
|
end
|
722
1059
|
|
723
1060
|
it 'returns a prepared statement that knows the metadata' do
|
@@ -728,7 +1065,7 @@ module Cql
|
|
728
1065
|
it 'executes a prepared statement with a specific consistency level' do
|
729
1066
|
statement = client.prepare(cql).value
|
730
1067
|
statement.execute('thing', :local_quorum).value
|
731
|
-
last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['thing'], :local_quorum)
|
1068
|
+
last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['thing'], true, :local_quorum)
|
732
1069
|
end
|
733
1070
|
|
734
1071
|
context 'when there is an error creating the request' do
|
@@ -742,7 +1079,7 @@ module Cql
|
|
742
1079
|
it 'returns a failed future' do
|
743
1080
|
handle_request do |request|
|
744
1081
|
if request.is_a?(Protocol::PrepareRequest)
|
745
|
-
Protocol::PreparedResultResponse.new(id, metadata, nil)
|
1082
|
+
Protocol::PreparedResultResponse.new(id, metadata, nil, nil)
|
746
1083
|
end
|
747
1084
|
end
|
748
1085
|
statement = client.prepare(cql).value
|
@@ -753,7 +1090,7 @@ module Cql
|
|
753
1090
|
|
754
1091
|
context 'with multiple connections' do
|
755
1092
|
let :connection_options do
|
756
|
-
|
1093
|
+
default_connection_options.merge(hosts: %w[host1 host2])
|
757
1094
|
end
|
758
1095
|
|
759
1096
|
it 'prepares the statement on all connections' do
|
@@ -764,11 +1101,102 @@ module Cql
|
|
764
1101
|
raise 'Did not receive EXECUTE requests on all connections within 5s' if (Time.now - started_at) > 5
|
765
1102
|
end
|
766
1103
|
connections.map { |c| c.requests.last }.should == [
|
767
|
-
Protocol::ExecuteRequest.new(id, metadata, ['hello'], :quorum),
|
768
|
-
Protocol::ExecuteRequest.new(id, metadata, ['hello'], :quorum),
|
1104
|
+
Protocol::ExecuteRequest.new(id, metadata, ['hello'], true, :quorum),
|
1105
|
+
Protocol::ExecuteRequest.new(id, metadata, ['hello'], true, :quorum),
|
769
1106
|
]
|
770
1107
|
end
|
771
1108
|
end
|
1109
|
+
|
1110
|
+
context 'with paging' do
|
1111
|
+
let :statement do
|
1112
|
+
client.prepare(cql).value
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
let :rows do
|
1116
|
+
[['xyz', 'abc'], ['abc', 'xyz'], ['123', 'xyz']]
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
let :metadata do
|
1120
|
+
[['thingies', 'things', 'thing', :text]]
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
let :result_metadata do
|
1124
|
+
[['thingies', 'things', 'thing', :text], ['thingies', 'things', 'item', :text]]
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
before do
|
1128
|
+
handle_request do |request|
|
1129
|
+
case request
|
1130
|
+
when Protocol::PrepareRequest
|
1131
|
+
Protocol::PreparedResultResponse.new(id, metadata, nil, nil)
|
1132
|
+
when Protocol::ExecuteRequest
|
1133
|
+
if request.paging_state.nil?
|
1134
|
+
Protocol::RowsResultResponse.new(rows.take(2), result_metadata, 'foobar', nil)
|
1135
|
+
elsif request.paging_state == 'foobar'
|
1136
|
+
Protocol::RowsResultResponse.new(rows.drop(2), result_metadata, nil, nil)
|
1137
|
+
end
|
1138
|
+
end
|
1139
|
+
end
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
it 'sets the page size' do
|
1143
|
+
statement.execute('foo', page_size: 100).value
|
1144
|
+
last_request.page_size.should == 100
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
it 'sets the page size and paging state' do
|
1148
|
+
statement.execute('foo', page_size: 100, paging_state: 'foobar').value
|
1149
|
+
last_request.page_size.should == 100
|
1150
|
+
last_request.paging_state.should == 'foobar'
|
1151
|
+
end
|
1152
|
+
end
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
describe '#batch' do
|
1156
|
+
before do
|
1157
|
+
client.connect.value
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
context 'when called witout a block' do
|
1161
|
+
it 'returns a batch' do
|
1162
|
+
batch = client.batch
|
1163
|
+
batch.add('UPDATE x SET y = 3 WHERE z = 1')
|
1164
|
+
batch.execute.value
|
1165
|
+
last_request.should be_a(Protocol::BatchRequest)
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
it 'creates a batch of the right type' do
|
1169
|
+
batch = client.batch(:unlogged)
|
1170
|
+
batch.add('UPDATE x SET y = 3 WHERE z = 1')
|
1171
|
+
batch.execute.value
|
1172
|
+
last_request.type.should == Protocol::BatchRequest::UNLOGGED_TYPE
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
it 'passes the options to the batch' do
|
1176
|
+
batch = client.batch(trace: true)
|
1177
|
+
batch.add('UPDATE x SET y = 3 WHERE z = 1')
|
1178
|
+
batch.execute.value
|
1179
|
+
last_request.trace.should be_true
|
1180
|
+
end
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
context 'when called with a block' do
|
1184
|
+
it 'yields and executes a batch' do
|
1185
|
+
f = client.batch do |batch|
|
1186
|
+
batch.add('UPDATE x SET y = 3 WHERE z = 1')
|
1187
|
+
end
|
1188
|
+
f.value
|
1189
|
+
last_request.should be_a(Protocol::BatchRequest)
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
it 'passes the options to the batch\'s #execute' do
|
1193
|
+
f = client.batch(:unlogged, trace: true) do |batch|
|
1194
|
+
batch.add('UPDATE x SET y = 3 WHERE z = 1')
|
1195
|
+
end
|
1196
|
+
f.value
|
1197
|
+
last_request.trace.should be_true
|
1198
|
+
end
|
1199
|
+
end
|
772
1200
|
end
|
773
1201
|
|
774
1202
|
context 'when not connected' do
|
@@ -815,7 +1243,7 @@ module Cql
|
|
815
1243
|
it 'complains when #execute of a prepared statement is called after #close' do
|
816
1244
|
handle_request do |request|
|
817
1245
|
if request.is_a?(Protocol::PrepareRequest)
|
818
|
-
Protocol::PreparedResultResponse.new('A' * 32, [], nil)
|
1246
|
+
Protocol::PreparedResultResponse.new('A' * 32, [], nil, nil)
|
819
1247
|
end
|
820
1248
|
end
|
821
1249
|
client.connect.value
|
@@ -829,7 +1257,7 @@ module Cql
|
|
829
1257
|
include_context 'peer discovery setup'
|
830
1258
|
|
831
1259
|
let :connection_options do
|
832
|
-
|
1260
|
+
default_connection_options.merge(hosts: %w[host1 host2 host3], keyspace: 'foo')
|
833
1261
|
end
|
834
1262
|
|
835
1263
|
before do
|
@@ -860,6 +1288,14 @@ module Cql
|
|
860
1288
|
connections.select(&:connected?).should have(3).items
|
861
1289
|
end
|
862
1290
|
|
1291
|
+
it 'makes sure the new connections use the same keyspace as the existing' do
|
1292
|
+
connections.first.close
|
1293
|
+
event = Protocol::TopologyChangeEventResponse.new('NEW_NODE', IPAddr.new('1.1.1.1'), 9999)
|
1294
|
+
connections.select(&:has_event_listener?).first.trigger_event(event)
|
1295
|
+
use_keyspace_request = connections.last.requests.find { |r| r.is_a?(Protocol::QueryRequest) && r.cql.include?('USE') }
|
1296
|
+
use_keyspace_request.cql.should == 'USE foo'
|
1297
|
+
end
|
1298
|
+
|
863
1299
|
it 'eventually reconnects even when the node doesn\'t respond at first' do
|
864
1300
|
timer_promise = Promise.new
|
865
1301
|
io_reactor.stub(:schedule_timer).and_return(timer_promise.future)
|
@@ -947,14 +1383,21 @@ module Cql
|
|
947
1383
|
logger.stub(:warn)
|
948
1384
|
io_reactor.stub(:connect).and_return(Future.failed(StandardError.new('Hurgh blurgh')))
|
949
1385
|
client.connect.value rescue nil
|
950
|
-
logger.should have_received(:warn).with(/Failed connecting to node at example\.com:
|
1386
|
+
logger.should have_received(:warn).with(/Failed connecting to node at example\.com: Hurgh blurgh/)
|
951
1387
|
end
|
952
1388
|
|
953
1389
|
it 'logs when a connection fails' do
|
954
1390
|
logger.stub(:warn)
|
955
1391
|
client.connect.value
|
1392
|
+
connections.sample.close(StandardError.new('bork'))
|
1393
|
+
logger.should have_received(:warn).with(/Connection to node .{36} at .+:\d+ in data center .+ closed unexpectedly: bork/)
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
it 'logs when a connection closes' do
|
1397
|
+
logger.stub(:info)
|
1398
|
+
client.connect.value
|
956
1399
|
connections.sample.close
|
957
|
-
logger.should have_received(:
|
1400
|
+
logger.should have_received(:info).with(/Connection to node .{36} at .+:\d+ in data center .+ closed/)
|
958
1401
|
end
|
959
1402
|
|
960
1403
|
it 'logs when it does a peer discovery' do
|