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,7 +9,8 @@ module Cql
|
|
9
9
|
describe '.decode!' do
|
10
10
|
context 'with rows from the same table' do
|
11
11
|
let :response do
|
12
|
-
|
12
|
+
buffer = ByteBuffer.new("\x00\x00\x00\x01\x00\x00\x00\x03\x00\ncql_rb_126\x00\x05users\x00\tuser_name\x00\r\x00\x05email\x00\r\x00\bpassword\x00\r\x00\x00\x00\x02\x00\x00\x00\x04phil\x00\x00\x00\rphil@heck.com\xFF\xFF\xFF\xFF\x00\x00\x00\x03sue\x00\x00\x00\rsue@inter.net\xFF\xFF\xFF\xFF")
|
13
|
+
described_class.decode!(1, buffer, buffer.length)
|
13
14
|
end
|
14
15
|
|
15
16
|
it 'decodes the rows as hashes of column name => column value' do
|
@@ -43,7 +44,7 @@ module Cql
|
|
43
44
|
buffer << "\x00\ncql_rb_127\x00\x06users2\x00\x05email\x00\r"
|
44
45
|
buffer << "\x00\ncql_rb_128\x00\x06users3\x00\bpassword\x00\r"
|
45
46
|
buffer << "\x00\x00\x00\x02\x00\x00\x00\x04phil\x00\x00\x00\rphil@heck.com\xFF\xFF\xFF\xFF\x00\x00\x00\x03sue\x00\x00\x00\rsue@inter.net\xFF\xFF\xFF\xFF"
|
46
|
-
described_class.decode!(buffer)
|
47
|
+
described_class.decode!(1, buffer, buffer.length)
|
47
48
|
end
|
48
49
|
|
49
50
|
it 'decodes the rows' do
|
@@ -62,6 +63,46 @@ module Cql
|
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
66
|
+
context 'when there is no metadata' do
|
67
|
+
let :response do
|
68
|
+
buffer = ByteBuffer.new
|
69
|
+
buffer << "\x00\x00\x00\x05" # flags (global_tables_spec | no_metadata)
|
70
|
+
buffer << "\x00\x00\x00\x03" # column count
|
71
|
+
buffer << "\x00\x00\x00\x02\x00\x00\x00\x04phil\x00\x00\x00\rphil@heck.com\xFF\xFF\xFF\xFF\x00\x00\x00\x03sue\x00\x00\x00\rsue@inter.net\xFF\xFF\xFF\xFF"
|
72
|
+
described_class.decode!(2, buffer, buffer.length)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'returns a RawRowsResultResponse' do
|
76
|
+
metadata = [
|
77
|
+
['cql_rb_126', 'users', 'user_name', :varchar],
|
78
|
+
['cql_rb_126', 'users', 'email', :varchar],
|
79
|
+
['cql_rb_126', 'users', 'password', :varchar]
|
80
|
+
]
|
81
|
+
response.materialize(metadata)
|
82
|
+
response.rows.should == [
|
83
|
+
{'user_name' => 'phil', 'email' => 'phil@heck.com', 'password' => nil},
|
84
|
+
{'user_name' => 'sue', 'email' => 'sue@inter.net', 'password' => nil}
|
85
|
+
]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'when there are more pages' do
|
90
|
+
let :response do
|
91
|
+
buffer = ByteBuffer.new
|
92
|
+
buffer << "\x00\x00\x00\x03" # flags (global_tables_spec | has_more_pages)
|
93
|
+
buffer << "\x00\x00\x00\x03" # column count
|
94
|
+
buffer << "\x00\x00\x00\x03foo" # paging state
|
95
|
+
buffer << "\x00\ncql_rb_126\x00\x05users"
|
96
|
+
buffer << "\x00\tuser_name\x00\r\x00\x05email\x00\r\x00\bpassword\x00\r"
|
97
|
+
buffer << "\x00\x00\x00\x02\x00\x00\x00\x04phil\x00\x00\x00\rphil@heck.com\xFF\xFF\xFF\xFF\x00\x00\x00\x03sue\x00\x00\x00\rsue@inter.net\xFF\xFF\xFF\xFF"
|
98
|
+
described_class.decode!(2, buffer, buffer.length)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'extracts the paging state' do
|
102
|
+
response.paging_state.should == 'foo'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
65
106
|
context 'with different column types' do
|
66
107
|
let :response do
|
67
108
|
# The following test was created by intercepting the frame for the
|
@@ -113,8 +154,8 @@ module Cql
|
|
113
154
|
# );
|
114
155
|
#
|
115
156
|
# SELECT * FROM lots_of_types WHERE ascii_column = 'hello';
|
116
|
-
|
117
|
-
described_class.decode!(
|
157
|
+
buffer = ByteBuffer.new("\x00\x00\x00\x01\x00\x00\x00\x12\x00\x04test\x00\rlots_of_types\x00\fascii_column\x00\x01\x00\rbigint_column\x00\x02\x00\vblob_column\x00\x03\x00\x0Eboolean_column\x00\x04\x00\x0Edecimal_column\x00\x06\x00\rdouble_column\x00\a\x00\ffloat_column\x00\b\x00\vinet_column\x00\x10\x00\nint_column\x00\t\x00\vlist_column\x00 \x00\x01\x00\nmap_column\x00!\x00\r\x00\x04\x00\nset_column\x00\"\x00\x03\x00\vtext_column\x00\r\x00\x10timestamp_column\x00\v\x00\x0Ftimeuuid_column\x00\x0F\x00\vuuid_column\x00\f\x00\x0Evarchar_column\x00\r\x00\rvarint_column\x00\x0E\x00\x00\x00\x01\x00\x00\x00\x05hello\x00\x00\x00\b\x00\x03\x98\xB1S\xC8\x7F\xAB\x00\x00\x00\x05\xFA\xB4^4V\x00\x00\x00\x01\x01\x00\x00\x00\x11\x00\x00\x00\x12\r'\xFDI\xAD\x80f\x11g\xDCfV\xAA\x00\x00\x00\b@\xC3\x88\x0F\xC2\x7F\x9DU\x00\x00\x00\x04AB\x14{\x00\x00\x00\x04\n\x00\x01\x02\x00\x00\x00\x04\x00\xBCj\xC2\x00\x00\x00\x11\x00\x03\x00\x03foo\x00\x03foo\x00\x03bar\x00\x00\x00\x14\x00\x02\x00\x03foo\x00\x01\x01\x00\x05hello\x00\x01\x00\x00\x00\x00\r\x00\x02\x00\x03\xABC!\x00\x04\xAF\xD8~\xCD\x00\x00\x00\vhello world\x00\x00\x00\b\x00\x00\x01</\xE9\xDC\xE3\x00\x00\x00\x10\xA4\xA7\t\x00$\xE1\x11\xDF\x89$\x00\x1F\xF3Y\x17\x11\x00\x00\x00\x10\xCF\xD6l\xCC\xD8WN\x90\xB1\xE5\xDF\x98\xA3\xD4\f\xD6\x00\x00\x00\x03foo\x00\x00\x00\x11\x03\x9EV \x15\f\x03\x9DK\x18\xCDI\\$?\a[")
|
158
|
+
described_class.decode!(1, buffer, buffer.length)
|
118
159
|
end
|
119
160
|
|
120
161
|
it 'decodes ASCII as an ASCII encoded string' do
|
@@ -196,7 +237,8 @@ module Cql
|
|
196
237
|
|
197
238
|
context 'with null values' do
|
198
239
|
it 'decodes nulls' do
|
199
|
-
|
240
|
+
buffer = ByteBuffer.new("\x00\x00\x00\x01\x00\x00\x00\x13\x00\x12cql_rb_client_spec\x00\rlots_of_types\x00\x02id\x00\t\x00\fascii_column\x00\x01\x00\rbigint_column\x00\x02\x00\vblob_column\x00\x03\x00\x0Eboolean_column\x00\x04\x00\x0Edecimal_column\x00\x06\x00\rdouble_column\x00\a\x00\ffloat_column\x00\b\x00\vinet_column\x00\x10\x00\nint_column\x00\t\x00\vlist_column\x00 \x00\x01\x00\nmap_column\x00!\x00\r\x00\x04\x00\nset_column\x00\"\x00\x03\x00\vtext_column\x00\r\x00\x10timestamp_column\x00\v\x00\x0Ftimeuuid_column\x00\x0F\x00\vuuid_column\x00\f\x00\x0Evarchar_column\x00\r\x00\rvarint_column\x00\x0E\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x03\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF")
|
241
|
+
response = described_class.decode!(1, buffer, buffer.length)
|
200
242
|
response.rows.first.should eql(
|
201
243
|
'id' => 3,
|
202
244
|
'ascii_column' => nil,
|
@@ -223,19 +265,22 @@ module Cql
|
|
223
265
|
|
224
266
|
context 'with COUNTER columns' do
|
225
267
|
it 'decodes COUNTER as a number' do
|
226
|
-
|
268
|
+
buffer = ByteBuffer.new("\x00\x00\x00\x01\x00\x00\x00\x03\x00\x04test\x00\x04cnts\x00\x02id\x00\r\x00\x02c1\x00\x05\x00\x02c2\x00\x05\x00\x00\x00\x01\x00\x00\x00\x04theo\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x01")
|
269
|
+
response = described_class.decode!(1, buffer, buffer.length)
|
227
270
|
response.rows.first['c1'].should == 3
|
228
271
|
end
|
229
272
|
|
230
273
|
it 'decodes a null COUNTER as nil' do
|
231
|
-
|
274
|
+
buffer = ByteBuffer.new("\x00\x00\x00\x01\x00\x00\x00\x02\x00\x12cql_rb_client_spec\x00\bcounters\x00\bcounter1\x00\x05\x00\bcounter2\x00\x05\x00\x00\x00\x01\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x01\xFF\xFF\xFF\xFF")
|
275
|
+
response = described_class.decode!(1, buffer, buffer.length)
|
232
276
|
response.rows.first['counter2'].should be_nil
|
233
277
|
end
|
234
278
|
end
|
235
279
|
|
236
280
|
context 'with an INET column' do
|
237
281
|
let :response do
|
238
|
-
|
282
|
+
buffer = ByteBuffer.new("\x00\x00\x00\x01\x00\x00\x00\x01\x00\ntest_types\x00\rlots_of_types\x00\vinet_column\x00\x10\x00\x00\x00\x02\x00\x00\x00\x04\x7F\x00\x00\x01\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
|
283
|
+
described_class.decode!(1, buffer, buffer.length)
|
239
284
|
end
|
240
285
|
|
241
286
|
it 'decodes IPv4 values' do
|
@@ -250,21 +295,21 @@ module Cql
|
|
250
295
|
context 'with an unknown column type' do
|
251
296
|
it 'raises an error when encountering an unknown column type' do
|
252
297
|
buffer = ByteBuffer.new("\x00\x00\x00\x01\x00\x00\x00\x03\x00\ncql_rb_328\x00\x05users\x00\tuser_name\x00\xff\x00\x05email\x00\r\x00\bpassword\x00\r\x00\x00\x00\x00")
|
253
|
-
expect { described_class.decode!(buffer) }.to raise_error(UnsupportedColumnTypeError)
|
298
|
+
expect { described_class.decode!(1, buffer, buffer.length) }.to raise_error(UnsupportedColumnTypeError)
|
254
299
|
end
|
255
300
|
end
|
256
301
|
end
|
257
302
|
|
258
303
|
describe '#void?' do
|
259
304
|
it 'is not void' do
|
260
|
-
response = RowsResultResponse.new([{'col' => 'foo'}], [['ks', 'tbl', 'col', :varchar]], nil)
|
305
|
+
response = RowsResultResponse.new([{'col' => 'foo'}], [['ks', 'tbl', 'col', :varchar]], nil, nil)
|
261
306
|
response.should_not be_void
|
262
307
|
end
|
263
308
|
end
|
264
309
|
|
265
310
|
describe '#to_s' do
|
266
311
|
it 'returns a string with metadata and rows' do
|
267
|
-
response = RowsResultResponse.new([{'col' => 'foo'}], [['ks', 'tbl', 'col', :varchar]], nil)
|
312
|
+
response = RowsResultResponse.new([{'col' => 'foo'}], [['ks', 'tbl', 'col', :varchar]], nil, nil)
|
268
313
|
response.to_s.should == 'RESULT ROWS [["ks", "tbl", "col", :varchar]] [{"col"=>"foo"}]'
|
269
314
|
end
|
270
315
|
end
|
@@ -8,7 +8,8 @@ module Cql
|
|
8
8
|
describe SchemaChangeEventResponse do
|
9
9
|
describe '.decode!' do
|
10
10
|
let :response do
|
11
|
-
|
11
|
+
buffer = ByteBuffer.new("\x00\aDROPPED\x00\ncql_rb_609\x00\x05users")
|
12
|
+
described_class.decode!(1, buffer, buffer.length)
|
12
13
|
end
|
13
14
|
|
14
15
|
it 'decodes the change' do
|
@@ -8,7 +8,8 @@ module Cql
|
|
8
8
|
describe SchemaChangeResultResponse do
|
9
9
|
describe '.decode!' do
|
10
10
|
let :response do
|
11
|
-
|
11
|
+
buffer = ByteBuffer.new("\x00\aUPDATED\x00\ncql_rb_973\x00\x05users")
|
12
|
+
described_class.decode!(1, buffer, buffer.length)
|
12
13
|
end
|
13
14
|
|
14
15
|
it 'decodes the description' do
|
@@ -8,7 +8,7 @@ module Cql
|
|
8
8
|
describe SetKeyspaceResultResponse do
|
9
9
|
describe '.decode!' do
|
10
10
|
let :response do
|
11
|
-
described_class.decode!(ByteBuffer.new("\x00\x06system"))
|
11
|
+
described_class.decode!(1, ByteBuffer.new("\x00\x06system"), 8)
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'decodes the keyspace' do
|
@@ -8,7 +8,8 @@ module Cql
|
|
8
8
|
describe StatusChangeEventResponse do
|
9
9
|
describe '.decode!' do
|
10
10
|
let :response do
|
11
|
-
|
11
|
+
buffer = ByteBuffer.new("\x00\x04DOWN\x04\x00\x00\x00\x00\x00\x00#R")
|
12
|
+
described_class.decode!(1, buffer, buffer.length)
|
12
13
|
end
|
13
14
|
|
14
15
|
it 'decodes the change' do
|
@@ -8,7 +8,8 @@ module Cql
|
|
8
8
|
describe SupportedResponse do
|
9
9
|
describe '.decode!' do
|
10
10
|
let :response do
|
11
|
-
|
11
|
+
buffer = ByteBuffer.new("\x00\x02\x00\x0bCQL_VERSION\x00\x01\x00\x053.0.0\x00\x0bCOMPRESSION\x00\x00")
|
12
|
+
described_class.decode!(1, buffer, buffer.length)
|
12
13
|
end
|
13
14
|
|
14
15
|
it 'decodes the options' do
|
@@ -8,7 +8,8 @@ module Cql
|
|
8
8
|
describe TopologyChangeEventResponse do
|
9
9
|
describe '.decode!' do
|
10
10
|
let :response do
|
11
|
-
|
11
|
+
buffer = ByteBuffer.new("\x00\fREMOVED_NODE\x04\x00\x00\x00\x00\x00\x00#R")
|
12
|
+
described_class.decode!(1, buffer, buffer.length)
|
12
13
|
end
|
13
14
|
|
14
15
|
it 'decodes the change' do
|
@@ -9,7 +9,7 @@ module Cql
|
|
9
9
|
describe '.decode!' do
|
10
10
|
it 'returns a new instance' do
|
11
11
|
unused_byte_buffer = nil
|
12
|
-
described_class.decode!(unused_byte_buffer).should be_a(described_class)
|
12
|
+
described_class.decode!(1, unused_byte_buffer, 0).should be_a(described_class)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -14,11 +14,28 @@ module Cql
|
|
14
14
|
''
|
15
15
|
end
|
16
16
|
|
17
|
-
TYPES = [:ascii, :bigint, :blob, :boolean, :counter, :decimal, :double, :float, :inet, :int, :text, :varchar, :timestamp, :timeuuid, :uuid, :varint].freeze
|
18
|
-
|
19
17
|
describe '#to_bytes' do
|
18
|
+
types = [
|
19
|
+
:ascii,
|
20
|
+
:bigint,
|
21
|
+
:blob,
|
22
|
+
:boolean,
|
23
|
+
:counter,
|
24
|
+
:decimal,
|
25
|
+
:double,
|
26
|
+
:float,
|
27
|
+
:inet,
|
28
|
+
:int,
|
29
|
+
:text,
|
30
|
+
:varchar,
|
31
|
+
:timestamp,
|
32
|
+
:timeuuid,
|
33
|
+
:uuid,
|
34
|
+
:varint
|
35
|
+
]
|
36
|
+
|
20
37
|
context 'when encoding normal value' do
|
21
|
-
|
38
|
+
types.each do |type|
|
22
39
|
it "encodes a null #{type.upcase}" do
|
23
40
|
converter.to_bytes(buffer, type, nil, 4).should == "\xff\xff\xff\xff"
|
24
41
|
end
|
@@ -40,7 +57,7 @@ module Cql
|
|
40
57
|
end
|
41
58
|
|
42
59
|
context 'when encoding collection values' do
|
43
|
-
|
60
|
+
types.each do |type|
|
44
61
|
it "encodes a null #{type.upcase}" do
|
45
62
|
converter.to_bytes(buffer, type, nil, 2).should == "\xff\xff"
|
46
63
|
end
|
data/spec/cql/uuid_spec.rb
CHANGED
@@ -36,8 +36,15 @@ module Cql
|
|
36
36
|
Uuid.new(276263553384940695775376958868900023510).should eql(Uuid.new('cfd66ccc-d857-4e90-b1e5-df98a3d40cd6'))
|
37
37
|
end
|
38
38
|
|
39
|
-
it '
|
40
|
-
|
39
|
+
it 'uses #value to determine equality' do
|
40
|
+
Uuid.new(276263553384940695775376958868900023510).should eql(Future.resolved(276263553384940695775376958868900023510))
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'does not attempt to call #value on object that do not respond to it' do
|
44
|
+
uuid = Uuid.new(276263553384940695775376958868900023510)
|
45
|
+
expect { uuid.should_not eql(nil) }.to_not raise_error
|
46
|
+
expect { uuid.should_not eql(123) }.to_not raise_error
|
47
|
+
expect { uuid.should_not eql('test') }.to_not raise_error
|
41
48
|
end
|
42
49
|
|
43
50
|
it 'aliases #== to #eql?' do
|
@@ -53,7 +60,7 @@ module Cql
|
|
53
60
|
|
54
61
|
describe '#hash' do
|
55
62
|
it 'calculates a 64 bit hash of the UUID' do
|
56
|
-
h = Uuid.new(
|
63
|
+
h = Uuid.new(276263553384940695775376958868900023510).hash
|
57
64
|
h.should be < 2**63
|
58
65
|
h.should be > -2**63
|
59
66
|
end
|
@@ -5,7 +5,10 @@ require 'spec_helper'
|
|
5
5
|
|
6
6
|
describe 'A CQL client' do
|
7
7
|
let :connection_options do
|
8
|
-
{
|
8
|
+
{
|
9
|
+
:host => ENV['CASSANDRA_HOST'],
|
10
|
+
:credentials => {:username => 'cassandra', :password => 'cassandra'},
|
11
|
+
}
|
9
12
|
end
|
10
13
|
|
11
14
|
let :client do
|
@@ -16,6 +19,18 @@ describe 'A CQL client' do
|
|
16
19
|
client.close rescue nil
|
17
20
|
end
|
18
21
|
|
22
|
+
def create_keyspace_and_table
|
23
|
+
begin
|
24
|
+
client.execute(%(DROP KEYSPACE cql_rb_client_spec))
|
25
|
+
rescue Cql::QueryError => e
|
26
|
+
raise e unless e.code == 0x2300
|
27
|
+
end
|
28
|
+
client.execute(%(CREATE KEYSPACE cql_rb_client_spec WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1}))
|
29
|
+
client.use('cql_rb_client_spec')
|
30
|
+
client.execute(%(CREATE TABLE users (user_id VARCHAR PRIMARY KEY, first VARCHAR, last VARCHAR, age INT)))
|
31
|
+
client.execute(%(CREATE TABLE counters (id VARCHAR PRIMARY KEY, count COUNTER)))
|
32
|
+
end
|
33
|
+
|
19
34
|
context 'with common operations' do
|
20
35
|
it 'executes a query and returns the result' do
|
21
36
|
result = client.execute('SELECT * FROM system.schema_keyspaces')
|
@@ -47,11 +62,12 @@ describe 'A CQL client' do
|
|
47
62
|
|
48
63
|
context 'when using prepared statements' do
|
49
64
|
before do
|
50
|
-
|
65
|
+
create_keyspace_and_table
|
66
|
+
client.execute(%(UPDATE users SET first = 'Sue', last = 'Smith', age = 34 WHERE user_id = 'sue'))
|
51
67
|
end
|
52
68
|
|
53
69
|
let :statement do
|
54
|
-
client.prepare('SELECT * FROM
|
70
|
+
client.prepare('SELECT * FROM users WHERE user_id = ?')
|
55
71
|
end
|
56
72
|
|
57
73
|
it 'prepares a statement' do
|
@@ -59,11 +75,39 @@ describe 'A CQL client' do
|
|
59
75
|
end
|
60
76
|
|
61
77
|
it 'executes a prepared statement' do
|
62
|
-
result = statement.execute('
|
78
|
+
result = statement.execute('sue')
|
63
79
|
result.should have(1).item
|
64
|
-
result = statement.execute('
|
80
|
+
result = statement.execute('sue', :one)
|
65
81
|
result.should have(1).item
|
66
82
|
end
|
83
|
+
|
84
|
+
it 'executes a prepared statement with no bound values' do
|
85
|
+
statement = client.prepare('SELECT * FROM users')
|
86
|
+
result = statement.execute(:one)
|
87
|
+
result.should_not be_empty
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'executes a batch' do
|
91
|
+
statement = client.prepare('UPDATE users SET first = ?, last = ?, age = ? WHERE user_id = ?')
|
92
|
+
statement.batch do |batch|
|
93
|
+
batch.add('Sam', 'Miller', 23, 'sam')
|
94
|
+
batch.add('Kim', 'Jones', 62, 'kim')
|
95
|
+
end
|
96
|
+
result = client.execute(%(SELECT * FROM users WHERE user_id = 'kim'))
|
97
|
+
result.first['last'].should == 'Jones'
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'executes a counter batch' do
|
101
|
+
statement = client.prepare('UPDATE counters SET count = count + ? WHERE id = ?')
|
102
|
+
batch = statement.batch(:counter, consistency: :quorum)
|
103
|
+
batch.add(5, 'foo')
|
104
|
+
batch.add(3, 'bar')
|
105
|
+
batch.add(6, 'foo')
|
106
|
+
batch.execute
|
107
|
+
result = client.execute('SELECT * FROM counters')
|
108
|
+
counters = result.each_with_object({}) { |row, acc| acc[row['id']] = row['count'] }
|
109
|
+
counters.should eql('foo' => 11, 'bar' => 3)
|
110
|
+
end
|
67
111
|
end
|
68
112
|
|
69
113
|
context 'with multiple connections' do
|
@@ -113,41 +157,88 @@ describe 'A CQL client' do
|
|
113
157
|
double(:client, connect: nil, close: nil)
|
114
158
|
end
|
115
159
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
160
|
+
context 'and protocol v1' do
|
161
|
+
let :authentication_enabled do
|
162
|
+
begin
|
163
|
+
Cql::Client.connect(connection_options.merge(credentials: nil, protocol_version: 1))
|
164
|
+
false
|
165
|
+
rescue Cql::AuthenticationError
|
166
|
+
true
|
167
|
+
end
|
122
168
|
end
|
123
|
-
end
|
124
169
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
170
|
+
let :credentials do
|
171
|
+
{:username => 'cassandra', :password => 'cassandra'}
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'authenticates using the credentials given in the :credentials option' do
|
175
|
+
client = Cql::Client.connect(connection_options.merge(credentials: credentials, protocol_version: 1))
|
176
|
+
client.execute('SELECT * FROM system.schema_keyspaces')
|
177
|
+
end
|
129
178
|
|
130
|
-
|
131
|
-
|
132
|
-
|
179
|
+
it 'raises an error when no credentials have been given' do
|
180
|
+
pending('authentication not configured', unless: authentication_enabled) do
|
181
|
+
expect { Cql::Client.connect(connection_options.merge(credentials: nil, protocol_version: 1)) }.to raise_error(Cql::AuthenticationError)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'raises an error when the credentials are bad' do
|
186
|
+
pending('authentication not configured', unless: authentication_enabled) do
|
187
|
+
expect {
|
188
|
+
Cql::Client.connect(connection_options.merge(credentials: {:username => 'foo', :password => 'bar'}, protocol_version: 1))
|
189
|
+
}.to raise_error(Cql::AuthenticationError)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'raises an error when only an auth provider has been given' do
|
194
|
+
pending('authentication not configured', unless: authentication_enabled) do
|
195
|
+
auth_provider = Cql::Client::PlainTextAuthProvider.new('cassandra', 'cassandra')
|
196
|
+
expect { Cql::Client.connect(connection_options.merge(credentials: nil, auth_provider: auth_provider, protocol_version: 1)) }.to raise_error(Cql::AuthenticationError)
|
197
|
+
end
|
133
198
|
end
|
134
199
|
end
|
135
200
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
Cql::Client.connect(connection_options.merge(
|
140
|
-
|
201
|
+
context 'and protocol v2' do
|
202
|
+
let :authentication_enabled do
|
203
|
+
begin
|
204
|
+
Cql::Client.connect(connection_options.merge(auth_provider: nil, credentials: nil))
|
205
|
+
false
|
206
|
+
rescue Cql::AuthenticationError
|
207
|
+
true
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'uses the auth provider given in the :auth_provider option' do
|
212
|
+
auth_provider = Cql::Client::PlainTextAuthProvider.new('cassandra', 'cassandra')
|
213
|
+
client = Cql::Client.connect(connection_options.merge(auth_provider: auth_provider, credentials: nil))
|
214
|
+
client.execute('SELECT * FROM system.schema_keyspaces')
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'falls back on creating a PlainTextAuthProvider using the credentials given in the :credentials option' do
|
218
|
+
client = Cql::Client.connect(connection_options.merge(auth_provider: nil, credentials: {:username => 'cassandra', :password => 'cassandra'}))
|
219
|
+
client.execute('SELECT * FROM system.schema_keyspaces')
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'raises an error when no auth provider or credentials have been given' do
|
223
|
+
pending('authentication not configured', unless: authentication_enabled) do
|
224
|
+
expect { Cql::Client.connect(connection_options.merge(auth_provider: nil, credentials: nil)) }.to raise_error(Cql::AuthenticationError)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'raises an error when the credentials are bad' do
|
229
|
+
pending('authentication not configured', unless: authentication_enabled) do
|
230
|
+
expect {
|
231
|
+
auth_provider = Cql::Client::PlainTextAuthProvider.new('foo', 'bar')
|
232
|
+
Cql::Client.connect(connection_options.merge(auth_provider: auth_provider, credentials: nil))
|
233
|
+
}.to raise_error(Cql::AuthenticationError)
|
234
|
+
end
|
141
235
|
end
|
142
236
|
end
|
143
237
|
end
|
144
238
|
|
145
239
|
context 'with tracing' do
|
146
240
|
before do
|
147
|
-
|
148
|
-
client.execute(%(CREATE KEYSPACE cql_rb_client_spec WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1}))
|
149
|
-
client.use('cql_rb_client_spec')
|
150
|
-
client.execute(%(CREATE TABLE users (user_id VARCHAR PRIMARY KEY, first VARCHAR, last VARCHAR, age INT)))
|
241
|
+
create_keyspace_and_table
|
151
242
|
end
|
152
243
|
|
153
244
|
it 'requests tracing and returns the trace ID, for row returning operations' do
|
@@ -187,6 +278,138 @@ describe 'A CQL client' do
|
|
187
278
|
end
|
188
279
|
end
|
189
280
|
|
281
|
+
context 'with on-the-fly bound variables' do
|
282
|
+
before do
|
283
|
+
create_keyspace_and_table
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'executes a query and sends the values separately' do
|
287
|
+
result = client.execute(%<INSERT INTO users (user_id, first, last) VALUES (?, ?, ?)>, 'sue', 'Sue', 'Smith')
|
288
|
+
result.should be_empty
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'encodes the values using the provided type hints' do
|
292
|
+
result = client.execute(%<INSERT INTO users (user_id, first, last, age) VALUES (?, ?, ?, ?)>, 'sue', 'Sue', 'Smith', 35, type_hints: [nil, nil, nil, :int])
|
293
|
+
result.should be_empty
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
context 'when batching operations' do
|
298
|
+
before do
|
299
|
+
create_keyspace_and_table
|
300
|
+
end
|
301
|
+
|
302
|
+
it 'sends the operations as a single request' do
|
303
|
+
batch = client.batch
|
304
|
+
batch.add(%(UPDATE users SET last = 'Smith' WHERE user_id = 'smith'))
|
305
|
+
batch.add(%(UPDATE users SET last = 'Jones' WHERE user_id = 'jones'))
|
306
|
+
batch.execute
|
307
|
+
result = client.execute(%(SELECT * FROM users WHERE user_id = 'jones'))
|
308
|
+
result.first.should include('last' => 'Jones')
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'accepts prepared statements' do
|
312
|
+
prepared_statement = client.prepare(%(UPDATE users SET last = ? WHERE user_id = ?))
|
313
|
+
batch = client.batch
|
314
|
+
batch.add(prepared_statement, 'Smith', 'smith')
|
315
|
+
batch.add(prepared_statement, 'Jones', 'jones')
|
316
|
+
batch.execute
|
317
|
+
result = client.execute(%(SELECT * FROM users WHERE user_id = 'jones'))
|
318
|
+
result.first.should include('last' => 'Jones')
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'accepts a mix of prepared, regular and statements with on-the-fly bound variables' do
|
322
|
+
prepared_statement = client.prepare(%(UPDATE users SET last = ? WHERE user_id = ?))
|
323
|
+
batch = client.batch
|
324
|
+
batch.add(prepared_statement, 'Smith', 'smith')
|
325
|
+
batch.add(%(UPDATE users SET last = 'Jones' WHERE user_id = 'jones'))
|
326
|
+
batch.add(%(UPDATE users SET last = ?, age = ? WHERE user_id = ?), 'Taylor', 53, 'taylor', type_hints: [nil, :int, nil])
|
327
|
+
batch.execute
|
328
|
+
result = client.execute(%(SELECT * FROM users WHERE user_id = 'jones'))
|
329
|
+
result.first.should include('last' => 'Jones')
|
330
|
+
result = client.execute(%(SELECT * FROM users WHERE user_id = 'taylor'))
|
331
|
+
result.first.should include('last' => 'Taylor')
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'yields the batch to a block and executes it afterwards' do
|
335
|
+
client.batch do |batch|
|
336
|
+
batch.add(%(UPDATE users SET last = 'Jones' WHERE user_id = 'jones'))
|
337
|
+
end
|
338
|
+
result = client.execute(%(SELECT * FROM users WHERE user_id = 'jones'))
|
339
|
+
result.first.should include('last' => 'Jones')
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'can be used for counter increments' do
|
343
|
+
client.batch(:counter) do |batch|
|
344
|
+
batch.add(%(UPDATE counters SET count = count + 1 WHERE id = 'foo'))
|
345
|
+
batch.add(%(UPDATE counters SET count = count + 2 WHERE id = 'bar'))
|
346
|
+
batch.add(%(UPDATE counters SET count = count + 3 WHERE id = 'baz'))
|
347
|
+
end
|
348
|
+
result = client.execute('SELECT * FROM counters')
|
349
|
+
counters = result.each_with_object({}) { |row, acc| acc[row['id']] = row['count'] }
|
350
|
+
counters.should eql('foo' => 1, 'bar' => 2, 'baz' => 3)
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'can be unlogged' do
|
354
|
+
client.batch(:unlogged) do |batch|
|
355
|
+
batch.add(%(UPDATE users SET last = 'Jones' WHERE user_id = 'jones'))
|
356
|
+
end
|
357
|
+
result = client.execute(%(SELECT * FROM users WHERE user_id = 'jones'))
|
358
|
+
result.first.should include('last' => 'Jones')
|
359
|
+
end
|
360
|
+
|
361
|
+
it 'can be traced' do
|
362
|
+
batch = client.batch(:unlogged)
|
363
|
+
batch.add(%(UPDATE users SET last = 'Jones' WHERE user_id = 'jones'))
|
364
|
+
result = batch.execute(trace: true)
|
365
|
+
result.trace_id.should_not be_nil
|
366
|
+
result = client.batch(:unlogged, trace: true) do |batch|
|
367
|
+
batch.add(%(UPDATE users SET last = 'Jones' WHERE user_id = 'jones'))
|
368
|
+
end
|
369
|
+
result.trace_id.should_not be_nil
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
context 'when paging large result sets' do
|
374
|
+
let :row_count do
|
375
|
+
200
|
376
|
+
end
|
377
|
+
|
378
|
+
before do
|
379
|
+
create_keyspace_and_table
|
380
|
+
statement = client.prepare('UPDATE counters SET count = count + ? WHERE id = ?')
|
381
|
+
ids = Set.new
|
382
|
+
ids << rand(234234).to_s(36) until ids.size == row_count
|
383
|
+
ids = ids.to_a
|
384
|
+
client.batch(:counter) do |batch|
|
385
|
+
row_count.times do |i|
|
386
|
+
batch.add(statement, rand(234), ids[i])
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'returns the first page, and a way to retrieve the next when using #execute' do
|
392
|
+
page_size = row_count/2 + 10
|
393
|
+
result_page = client.execute('SELECT * FROM counters', page_size: page_size)
|
394
|
+
result_page.count.should == page_size
|
395
|
+
result_page.should_not be_last_page
|
396
|
+
result_page = result_page.next_page
|
397
|
+
result_page.count.should == row_count - page_size
|
398
|
+
result_page.should be_last_page
|
399
|
+
end
|
400
|
+
|
401
|
+
it 'returns the first page, and a way to retrieve the next when using a prepared statement' do
|
402
|
+
page_size = row_count/2 + 10
|
403
|
+
statement = client.prepare('SELECT * FROM counters')
|
404
|
+
result_page = statement.execute(page_size: page_size)
|
405
|
+
result_page.count.should == page_size
|
406
|
+
result_page.should_not be_last_page
|
407
|
+
result_page = result_page.next_page
|
408
|
+
result_page.count.should == row_count - page_size
|
409
|
+
result_page.should be_last_page
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
190
413
|
context 'with error conditions' do
|
191
414
|
it 'raises an error for CQL syntax errors' do
|
192
415
|
expect { client.execute('BAD cql') }.to raise_error(Cql::CqlError)
|