cql-rb 1.2.2 → 2.0.0.pre0
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
- 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
|