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,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)
|