cql-rb 2.0.5 → 2.1.0.pre0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -0
- data/lib/cql.rb +0 -1
- data/lib/cql/client.rb +0 -2
- data/lib/cql/client/client.rb +5 -3
- data/lib/cql/client/connector.rb +4 -4
- data/lib/cql/client/prepared_statement.rb +4 -11
- data/lib/cql/protocol.rb +1 -0
- data/lib/cql/protocol/cql_protocol_handler.rb +2 -2
- data/lib/cql/protocol/custom_type_parser.rb +114 -0
- data/lib/cql/protocol/responses/detailed_error_response.rb +5 -5
- data/lib/cql/protocol/responses/error_response.rb +1 -3
- data/lib/cql/protocol/responses/rows_result_response.rb +4 -1
- data/lib/cql/protocol/type_converter.rb +82 -24
- data/lib/cql/version.rb +1 -1
- data/spec/cql/client/client_spec.rb +7 -0
- data/spec/cql/client/connector_spec.rb +6 -2
- data/spec/cql/client/peer_discovery_spec.rb +3 -3
- data/spec/cql/client/prepared_statement_spec.rb +4 -23
- data/spec/cql/protocol/cql_protocol_handler_spec.rb +0 -31
- data/spec/cql/protocol/custom_type_parser_spec.rb +43 -0
- data/spec/cql/protocol/requests/execute_request_spec.rb +161 -37
- data/spec/cql/protocol/responses/prepared_result_response_spec.rb +22 -0
- data/spec/cql/protocol/responses/rows_result_response_spec.rb +187 -0
- data/spec/cql/protocol/type_converter_spec.rb +12 -0
- data/spec/integration/client_spec.rb +60 -8
- data/spec/support/fake_io_reactor.rb +6 -5
- metadata +11 -9
- data/lib/cql/error_codes.rb +0 -96
@@ -66,6 +66,18 @@ module Cql
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
context 'when encoding user defined type values' do
|
70
|
+
it 'encodes a null value' do
|
71
|
+
converter.to_bytes(buffer, [:udt, {'name' => :string, 'shoe_size' => :int}], nil, 4).should eql_bytes("\xff\xff\xff\xff")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'when encoding custom values' do
|
76
|
+
it 'encodes a null value' do
|
77
|
+
converter.to_bytes(buffer, [:custom, 'com.example.CustomType'], nil, 4).should eql_bytes("\xff\xff\xff\xff")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
69
81
|
context 'when encoding and decoding negative numbers' do
|
70
82
|
numeric_types.each do |type|
|
71
83
|
it "encodes and decodes a -1 #{type.upcase}" do
|
@@ -19,7 +19,7 @@ describe 'A CQL client' do
|
|
19
19
|
client.close rescue nil
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
22
|
+
def create_keyspace
|
23
23
|
begin
|
24
24
|
client.execute(%(DROP KEYSPACE cql_rb_client_spec))
|
25
25
|
rescue Cql::QueryError => e
|
@@ -27,6 +27,10 @@ describe 'A CQL client' do
|
|
27
27
|
end
|
28
28
|
client.execute(%(CREATE KEYSPACE cql_rb_client_spec WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1}))
|
29
29
|
client.use('cql_rb_client_spec')
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_keyspace_and_table
|
33
|
+
create_keyspace
|
30
34
|
client.execute(%(CREATE TABLE users (user_id VARCHAR PRIMARY KEY, first VARCHAR, last VARCHAR, age INT)))
|
31
35
|
client.execute(%(CREATE TABLE counters (id VARCHAR PRIMARY KEY, count COUNTER)))
|
32
36
|
end
|
@@ -108,13 +112,6 @@ describe 'A CQL client' do
|
|
108
112
|
counters = result.each_with_object({}) { |row, acc| acc[row['id']] = row['count'] }
|
109
113
|
counters.should eql('foo' => 11, 'bar' => 3)
|
110
114
|
end
|
111
|
-
|
112
|
-
it 'handles altered tables' do
|
113
|
-
result1 = statement.execute('sue')
|
114
|
-
client.execute('ALTER TABLE users ADD password VARCHAR')
|
115
|
-
result2 = statement.execute('sue')
|
116
|
-
result2.to_a.should eql(result1.to_a)
|
117
|
-
end
|
118
115
|
end
|
119
116
|
|
120
117
|
context 'with multiple connections' do
|
@@ -445,6 +442,61 @@ describe 'A CQL client' do
|
|
445
442
|
end
|
446
443
|
end
|
447
444
|
|
445
|
+
context 'with user defined types' do
|
446
|
+
before do
|
447
|
+
release_version = client.execute('SELECT release_version FROM system.local').first['release_version']
|
448
|
+
pending 'User defined types are not available in C* before 2.1.0' unless release_version[0, 5] >= '2.1.0'
|
449
|
+
create_keyspace
|
450
|
+
client.execute(%(CREATE TYPE address (street TEXT, city TEXT, zip INT)))
|
451
|
+
client.execute(%(CREATE TYPE company (name TEXT, addresses LIST<address>)))
|
452
|
+
client.execute(%(CREATE TABLE users (id VARCHAR PRIMARY KEY, primary_address address, secondary_addresses MAP<TEXT, address>, employers SET<company>)))
|
453
|
+
end
|
454
|
+
|
455
|
+
it 'inserts records into a table with a user defined type' do
|
456
|
+
statement = client.prepare(%(INSERT INTO users (id, primary_address) VALUES (?, ?)))
|
457
|
+
statement.execute('sue', {'street' => '123 Some St.', 'city' => 'Frans Sanisco', 'zip' => 76543})
|
458
|
+
result = client.execute(%(SELECT primary_address.street AS street FROM users WHERE id = 'sue'))
|
459
|
+
result.first['street'].should == '123 Some St.'
|
460
|
+
end
|
461
|
+
|
462
|
+
it 'reads records from a table with a user defined type' do
|
463
|
+
client.execute(%(INSERT INTO users (id, primary_address) VALUES ('sue', {street: '123 Some St.', city: 'Frans Sanisco', zip: 76543})))
|
464
|
+
result = client.execute(%(SELECT primary_address FROM users WHERE id = 'sue'))
|
465
|
+
result.first['primary_address'].should eql('street' => '123 Some St.', 'city' => 'Frans Sanisco', 'zip' => 76543)
|
466
|
+
end
|
467
|
+
|
468
|
+
it 'reads records from a table with a user defined type in a collection' do
|
469
|
+
client.execute(<<-CQL)
|
470
|
+
INSERT INTO users (id, primary_address, secondary_addresses)
|
471
|
+
VALUES (
|
472
|
+
'sue',
|
473
|
+
{street: '123 Some St.', city: 'Frans Sanisco', zip: 76543},
|
474
|
+
{'secret_lair': {street: '4 Some Other St.', city: 'Gos Latos', zip: 87654}}
|
475
|
+
)
|
476
|
+
CQL
|
477
|
+
result = client.execute(%(SELECT secondary_addresses FROM users WHERE id = 'sue'))
|
478
|
+
result.first['secondary_addresses'].should eql('secret_lair' => {'street' => '4 Some Other St.', 'city' => 'Gos Latos', 'zip' => 87654})
|
479
|
+
end
|
480
|
+
|
481
|
+
it 'reads records from a table with user defined types nested in other user defined types, and collections' do
|
482
|
+
client.execute(<<-CQL)
|
483
|
+
INSERT INTO users (id, employers)
|
484
|
+
VALUES (
|
485
|
+
'sue',
|
486
|
+
{
|
487
|
+
{name: 'Acme Corp', addresses: [{street: '1 St.', city: '1 City', zip: 11111}, {street: '2 St.', city: '2 City', zip: 22222}]},
|
488
|
+
{name: 'Foo Inc.', addresses: [{street: '3 St.', city: '3 City', zip: 33333}]}
|
489
|
+
}
|
490
|
+
)
|
491
|
+
CQL
|
492
|
+
result = client.execute(%(SELECT employers FROM users WHERE id = 'sue'))
|
493
|
+
result.first['employers'].should eql(Set.new([
|
494
|
+
{'name' => 'Acme Corp', 'addresses' => [{'street' => '1 St.', 'city' => '1 City', 'zip' => 11111}, {'street' => '2 St.', 'city' => '2 City', 'zip' => 22222}]},
|
495
|
+
{'name' => 'Foo Inc.', 'addresses' => [{'street' => '3 St.', 'city' => '3 City', 'zip' => 33333}]}
|
496
|
+
]))
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
448
500
|
context 'with error conditions' do
|
449
501
|
it 'raises an error for CQL syntax errors' do
|
450
502
|
expect { client.execute('BAD cql') }.to raise_error(Cql::CqlError)
|
@@ -26,13 +26,13 @@ class FakeIoReactor
|
|
26
26
|
@before_startup_handler = handler
|
27
27
|
end
|
28
28
|
|
29
|
-
def connect(host, port,
|
29
|
+
def connect(host, port, options)
|
30
30
|
if host == '0.0.0.0'
|
31
31
|
Cql::Future.failed(Cql::Io::ConnectionError.new('Can\'t connect to 0.0.0.0'))
|
32
32
|
elsif @down_nodes.include?(host)
|
33
33
|
Cql::Future.failed(Cql::Io::ConnectionError.new('Node down'))
|
34
34
|
else
|
35
|
-
connection = FakeConnection.new(host, port,
|
35
|
+
connection = FakeConnection.new(host, port, options)
|
36
36
|
@connections << connection
|
37
37
|
@connection_listeners.each do |listener|
|
38
38
|
listener.call(connection)
|
@@ -71,12 +71,13 @@ class FakeIoReactor
|
|
71
71
|
end
|
72
72
|
|
73
73
|
class FakeConnection
|
74
|
-
attr_reader :host, :port, :timeout, :requests, :keyspace
|
74
|
+
attr_reader :host, :port, :timeout, :options, :requests, :keyspace
|
75
75
|
|
76
|
-
def initialize(host, port,
|
76
|
+
def initialize(host, port, options={}, data={})
|
77
77
|
@host = host
|
78
78
|
@port = port
|
79
|
-
@
|
79
|
+
@options = options || {}
|
80
|
+
@timeout = @options[:timeout]
|
80
81
|
@requests = []
|
81
82
|
@responses = []
|
82
83
|
@closed = false
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cql-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.1.0.pre0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Theo Hultberg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ione
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 1.2.0.pre3
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 1.2.0.pre3
|
27
27
|
description: A pure Ruby CQL3 driver for Cassandra
|
28
28
|
email:
|
29
29
|
- theo@iconara.net
|
@@ -54,10 +54,10 @@ files:
|
|
54
54
|
- lib/cql/compression.rb
|
55
55
|
- lib/cql/compression/lz4_compressor.rb
|
56
56
|
- lib/cql/compression/snappy_compressor.rb
|
57
|
-
- lib/cql/error_codes.rb
|
58
57
|
- lib/cql/protocol.rb
|
59
58
|
- lib/cql/protocol/cql_byte_buffer.rb
|
60
59
|
- lib/cql/protocol/cql_protocol_handler.rb
|
60
|
+
- lib/cql/protocol/custom_type_parser.rb
|
61
61
|
- lib/cql/protocol/frame_decoder.rb
|
62
62
|
- lib/cql/protocol/frame_encoder.rb
|
63
63
|
- lib/cql/protocol/request.rb
|
@@ -111,6 +111,7 @@ files:
|
|
111
111
|
- spec/cql/compression/snappy_compressor_spec.rb
|
112
112
|
- spec/cql/protocol/cql_byte_buffer_spec.rb
|
113
113
|
- spec/cql/protocol/cql_protocol_handler_spec.rb
|
114
|
+
- spec/cql/protocol/custom_type_parser_spec.rb
|
114
115
|
- spec/cql/protocol/frame_decoder_spec.rb
|
115
116
|
- spec/cql/protocol/frame_encoder_spec.rb
|
116
117
|
- spec/cql/protocol/requests/auth_response_request_spec.rb
|
@@ -166,9 +167,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
166
167
|
version: 1.9.3
|
167
168
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
168
169
|
requirements:
|
169
|
-
- - "
|
170
|
+
- - ">"
|
170
171
|
- !ruby/object:Gem::Version
|
171
|
-
version:
|
172
|
+
version: 1.3.1
|
172
173
|
requirements: []
|
173
174
|
rubyforge_project:
|
174
175
|
rubygems_version: 2.2.2
|
@@ -194,6 +195,7 @@ test_files:
|
|
194
195
|
- spec/cql/compression/snappy_compressor_spec.rb
|
195
196
|
- spec/cql/protocol/cql_byte_buffer_spec.rb
|
196
197
|
- spec/cql/protocol/cql_protocol_handler_spec.rb
|
198
|
+
- spec/cql/protocol/custom_type_parser_spec.rb
|
197
199
|
- spec/cql/protocol/frame_decoder_spec.rb
|
198
200
|
- spec/cql/protocol/frame_encoder_spec.rb
|
199
201
|
- spec/cql/protocol/requests/auth_response_request_spec.rb
|
data/lib/cql/error_codes.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Cql
|
4
|
-
module ErrorCodes
|
5
|
-
# Something unexpected happened. This indicates a server-side bug.
|
6
|
-
SERVER_ERROR = 0x0000
|
7
|
-
|
8
|
-
# Some client message triggered a protocol violation (for instance a QUERY
|
9
|
-
# message is sent before a STARTUP one has been sent).
|
10
|
-
PROTOCOL_ERROR = 0x000A
|
11
|
-
|
12
|
-
# CREDENTIALS request failed because Cassandra did not accept the provided credentials.
|
13
|
-
BAD_CREDENTIALS = 0x0100
|
14
|
-
|
15
|
-
# Unavailable exception.
|
16
|
-
#
|
17
|
-
# Details:
|
18
|
-
#
|
19
|
-
# * `:cl` - The consistency level of the query having triggered the exception.
|
20
|
-
# * `:required` - An int representing the number of nodes that should be alive to respect `:cl`.
|
21
|
-
# * `:alive` - An int representing the number of replica that were known to be
|
22
|
-
# alive when the request has been processed (since an unavailable
|
23
|
-
# exception has been triggered, there will be `:alive` < `:required`.
|
24
|
-
UNAVAILABLE = 0x1000
|
25
|
-
|
26
|
-
# The request cannot be processed because the coordinator node is overloaded.
|
27
|
-
OVERLOADED = 0x1001
|
28
|
-
|
29
|
-
# The request was a read request but the coordinator node is bootstrapping.
|
30
|
-
IS_BOOTSTRAPPING = 0x1002
|
31
|
-
|
32
|
-
# Error during a truncation error.
|
33
|
-
TRUNCATE_ERROR = 0x1003
|
34
|
-
|
35
|
-
# Timeout exception during a write request.
|
36
|
-
#
|
37
|
-
# Details:
|
38
|
-
#
|
39
|
-
# * `:cl` - The consistency level of the query having triggered the exception.
|
40
|
-
# * `:received` - An int representing the number of nodes having acknowledged the request.
|
41
|
-
# * `:blockfor` - The number of replica whose acknowledgement is required to achieve `:cl`.
|
42
|
-
# * `:write_type` - A string that describe the type of the write that timeouted. The value of that string can be one of:
|
43
|
-
# - `"SIMPLE"`: the write was a non-batched non-counter write.
|
44
|
-
# - `"BATCH"`: the write was a (logged) batch write. If this type is received, it means the batch log
|
45
|
-
# has been successfully written (otherwise a `"BATCH_LOG"` type would have been send instead).
|
46
|
-
# - `"UNLOGGED_BATCH"`: the write was an unlogged batch. Not batch log write has been attempted.
|
47
|
-
# - `"COUNTER"`: the write was a counter write (batched or not).
|
48
|
-
# - `"BATCH_LOG"`: the timeout occured during the write to the batch log when a (logged) batch write was requested.
|
49
|
-
WRITE_TIMEOUT = 0x1100
|
50
|
-
|
51
|
-
# Timeout exception during a read request.
|
52
|
-
#
|
53
|
-
# Details:
|
54
|
-
#
|
55
|
-
# * `:cl` - The consistency level of the query having triggered the exception
|
56
|
-
# * `:received` - An int representing the number of nodes having answered the request.
|
57
|
-
# * `:blockfor` - The number of replica whose response is required to achieve `:cl`.
|
58
|
-
# Please note that it is possible to have `:received` >= `:blockfor` if
|
59
|
-
# `:data_present` is false. And also in the (unlikely) case were `:cl` is
|
60
|
-
# achieved but the coordinator node timeout while waiting for read-repair
|
61
|
-
# acknowledgement.
|
62
|
-
# * `:data_present` - If `true`, it means the replica that was asked for data has not responded.
|
63
|
-
READ_TIMEOUT = 0x1200
|
64
|
-
|
65
|
-
# The submitted query has a syntax error.
|
66
|
-
SYNTAX_ERROR = 0x2000
|
67
|
-
|
68
|
-
# The logged user doesn't have the right to perform the query.
|
69
|
-
UNAUTHORIZED = 0x2100
|
70
|
-
|
71
|
-
# The query is syntactically correct but invalid.
|
72
|
-
INVALID = 0x2200
|
73
|
-
|
74
|
-
# The query is invalid because of some configuration issue.
|
75
|
-
CONFIG_ERROR = 0x2300
|
76
|
-
|
77
|
-
# The query attempted to create a keyspace or a table that was already existing.
|
78
|
-
#
|
79
|
-
# Details:
|
80
|
-
#
|
81
|
-
# * `:ks` - A string representing either the keyspace that already exists, or the
|
82
|
-
# keyspace in which the table that already exists is.
|
83
|
-
# * `:table` - A string representing the name of the table that already exists. If the
|
84
|
-
# query was attempting to create a keyspace, `:table` will be present but
|
85
|
-
# will be the empty string.
|
86
|
-
ALREADY_EXISTS = 0x2400
|
87
|
-
|
88
|
-
# Can be thrown while a prepared statement tries to be executed if the
|
89
|
-
# provide prepared statement ID is not known by this host.
|
90
|
-
#
|
91
|
-
# Details:
|
92
|
-
#
|
93
|
-
# * `:id` - The unknown ID.
|
94
|
-
UNPREPARED = 0x2500
|
95
|
-
end
|
96
|
-
end
|