cql-rb 1.0.0.pre1 → 1.0.0.pre2

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.
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  _There has not yet been a stable release of this project._
4
4
 
5
+ [![Build Status](https://travis-ci.org/iconara/cql-rb.png?branch=master)](https://travis-ci.org/iconara/cql-rb)
5
6
 
6
7
  # Requirements
7
8
 
data/lib/cql/client.rb CHANGED
@@ -84,9 +84,6 @@ module Cql
84
84
  def start!
85
85
  @lock.synchronize do
86
86
  return if @started
87
- @started = true
88
- end
89
- begin
90
87
  @io_reactor.start
91
88
  hosts = @host.split(',')
92
89
  start_request = Protocol::StartupRequest.new
@@ -96,11 +93,9 @@ module Cql
96
93
  end
97
94
  end
98
95
  @connection_ids = Future.combine(*connection_futures).get
99
- use(@initial_keyspace) if @initial_keyspace
100
- rescue
101
- @started = false
102
- raise
96
+ @started = true
103
97
  end
98
+ use(@initial_keyspace) if @initial_keyspace
104
99
  self
105
100
  end
106
101
 
@@ -110,7 +105,7 @@ module Cql
110
105
  #
111
106
  def shutdown!
112
107
  @lock.synchronize do
113
- return if @shut_down
108
+ return if @shut_down || !@started
114
109
  @shut_down = true
115
110
  @started = false
116
111
  end
@@ -140,7 +135,7 @@ module Cql
140
135
  # @raise [Cql::NotConnectedError] raised when the client is not connected
141
136
  #
142
137
  def use(keyspace, connection_ids=@connection_ids)
143
- raise NotConnectedError unless @started
138
+ raise NotConnectedError unless connected?
144
139
  if check_keyspace_name!(keyspace)
145
140
  @lock.synchronize do
146
141
  connection_ids = connection_ids.select { |id| @connection_keyspaces[id] != keyspace }
@@ -165,7 +160,8 @@ module Cql
165
160
  # `nil`, but `SELECT` statements return an `Enumerable` of rows
166
161
  # (see {QueryResult}).
167
162
  #
168
- def execute(cql, consistency=:quorum)
163
+ def execute(cql, consistency=DEFAULT_CONSISTENCY_LEVEL)
164
+ raise NotConnectedError unless connected?
169
165
  result = execute_request(Protocol::QueryRequest.new(cql, consistency)).value
170
166
  ensure_keyspace!
171
167
  result
@@ -173,7 +169,8 @@ module Cql
173
169
 
174
170
  # @private
175
171
  def execute_statement(connection_id, statement_id, metadata, values, consistency)
176
- execute_request(Protocol::ExecuteRequest.new(statement_id, metadata, values, consistency), connection_id).value
172
+ raise NotConnectedError unless connected?
173
+ execute_request(Protocol::ExecuteRequest.new(statement_id, metadata, values, consistency || DEFAULT_CONSISTENCY_LEVEL), connection_id).value
177
174
  end
178
175
 
179
176
  # Returns a prepared statement that can be run over and over again with
@@ -183,12 +180,14 @@ module Cql
183
180
  # @return [Cql::PreparedStatement] an object encapsulating the prepared statement
184
181
  #
185
182
  def prepare(cql)
183
+ raise NotConnectedError unless connected?
186
184
  execute_request(Protocol::PrepareRequest.new(cql)).value
187
185
  end
188
186
 
189
187
  private
190
188
 
191
189
  KEYSPACE_NAME_PATTERN = /^\w[\w\d_]*$/
190
+ DEFAULT_CONSISTENCY_LEVEL = :quorum
192
191
 
193
192
  def check_keyspace_name!(name)
194
193
  if name !~ KEYSPACE_NAME_PATTERN
@@ -198,7 +197,6 @@ module Cql
198
197
  end
199
198
 
200
199
  def execute_request(request, connection_id=nil)
201
- raise NotConnectedError unless @started
202
200
  @io_reactor.queue_request(request, connection_id).map do |response, connection_id|
203
201
  interpret_response!(response, connection_id)
204
202
  end
@@ -233,6 +231,11 @@ module Cql
233
231
 
234
232
  public
235
233
 
234
+ # The representation of a prepared statement.
235
+ #
236
+ # Prepared statements are parsed once and stored on the server, allowing
237
+ # you to execute them over and over again but only send values for the bound
238
+ # parameters.
236
239
  #
237
240
  class PreparedStatement
238
241
  # @return [ResultMetadata]
@@ -245,8 +248,19 @@ module Cql
245
248
 
246
249
  # Execute the prepared statement with a list of values for the bound parameters.
247
250
  #
251
+ # The number of arguments must equal the number of bound parameters.
252
+ # To set the consistency level for the request you pass a consistency
253
+ # level (as a symbol) as the last argument. Needless to say, if you pass
254
+ # the value for one bound parameter too few, and then a consistency level,
255
+ # or if you pass too many values, you will get weird errors.
256
+ #
257
+ # @param args [Array] the values for the bound parameters, and optionally
258
+ # the desired consistency level, as a symbol (defaults to :quorum)
259
+ #
248
260
  def execute(*args)
249
- @client.execute_statement(@connection_id, @statement_id, @raw_metadata, args, :quorum)
261
+ bound_args = args.shift(@raw_metadata.size)
262
+ consistency_level = args.shift
263
+ @client.execute_statement(@connection_id, @statement_id, @raw_metadata, bound_args, consistency_level)
250
264
  end
251
265
  end
252
266
 
@@ -12,7 +12,7 @@ module Cql
12
12
  end
13
13
 
14
14
  def read_varint!(buffer, length=buffer.length, signed=true)
15
- raise DecodingError, "Length #{length} specifed but only #{buffer.size} bytes given" if buffer.size < length
15
+ raise DecodingError, "Length #{length} specifed but only #{buffer.bytesize} bytes given" if buffer.bytesize < length
16
16
  bytes = buffer.slice!(0, length)
17
17
  n = 0
18
18
  bytes.each_byte do |b|
@@ -25,7 +25,7 @@ module Cql
25
25
  end
26
26
 
27
27
  def read_decimal!(buffer, length=buffer.length)
28
- raise DecodingError, "Length #{length} specifed but only #{buffer.size} bytes given" if buffer.size < length
28
+ raise DecodingError, "Length #{length} specifed but only #{buffer.bytesize} bytes given" if buffer.bytesize < length
29
29
  size = read_int!(buffer)
30
30
  number_bytes = buffer.slice!(0, length - 4)
31
31
  number_string = read_varint!(number_bytes).to_s
@@ -34,34 +34,34 @@ module Cql
34
34
  end
35
35
 
36
36
  def read_long!(buffer)
37
- raise DecodingError, "Need eight bytes to decode long, only #{buffer.size} bytes given" if buffer.size < 8
37
+ raise DecodingError, "Need eight bytes to decode long, only #{buffer.bytesize} bytes given" if buffer.bytesize < 8
38
38
  top, bottom = buffer.slice!(0, 8).unpack(Formats::TWO_INTS_FORMAT)
39
39
  (top << 32) | bottom
40
40
  end
41
41
 
42
42
  def read_double!(buffer)
43
- raise DecodingError, "Need eight bytes to decode double, only #{buffer.size} bytes given" if buffer.size < 8
43
+ raise DecodingError, "Need eight bytes to decode double, only #{buffer.bytesize} bytes given" if buffer.bytesize < 8
44
44
  buffer.slice!(0, 8).unpack(Formats::DOUBLE_FORMAT).first
45
45
  end
46
46
 
47
47
  def read_float!(buffer)
48
- raise DecodingError, "Need four bytes to decode float, only #{buffer.size} bytes given" if buffer.size < 4
48
+ raise DecodingError, "Need four bytes to decode float, only #{buffer.bytesize} bytes given" if buffer.bytesize < 4
49
49
  buffer.slice!(0, 4).unpack(Formats::FLOAT_FORMAT).first
50
50
  end
51
51
 
52
52
  def read_int!(buffer)
53
- raise DecodingError, "Need four bytes to decode an int, only #{buffer.size} bytes given" if buffer.size < 4
53
+ raise DecodingError, "Need four bytes to decode an int, only #{buffer.bytesize} bytes given" if buffer.bytesize < 4
54
54
  buffer.slice!(0, 4).unpack(Formats::INT_FORMAT).first
55
55
  end
56
56
 
57
57
  def read_short!(buffer)
58
- raise DecodingError, "Need two bytes to decode a short, only #{buffer.size} bytes given" if buffer.size < 2
58
+ raise DecodingError, "Need two bytes to decode a short, only #{buffer.bytesize} bytes given" if buffer.bytesize < 2
59
59
  buffer.slice!(0, 2).unpack(Formats::SHORT_FORMAT).first
60
60
  end
61
61
 
62
62
  def read_string!(buffer)
63
63
  length = read_short!(buffer)
64
- raise DecodingError, "String length is #{length}, but only #{buffer.size} bytes given" if buffer.size < length
64
+ raise DecodingError, "String length is #{length}, but only #{buffer.bytesize} bytes given" if buffer.bytesize < length
65
65
  string = buffer.slice!(0, length)
66
66
  string.force_encoding(::Encoding::UTF_8)
67
67
  string
@@ -69,14 +69,14 @@ module Cql
69
69
 
70
70
  def read_long_string!(buffer)
71
71
  length = read_int!(buffer)
72
- raise DecodingError, "String length is #{length}, but only #{buffer.size} bytes given" if buffer.size < length
72
+ raise DecodingError, "String length is #{length}, but only #{buffer.bytesize} bytes given" if buffer.bytesize < length
73
73
  string = buffer.slice!(0, length)
74
74
  string.force_encoding(::Encoding::UTF_8)
75
75
  string
76
76
  end
77
77
 
78
78
  def read_uuid!(buffer)
79
- raise DecodingError, "UUID requires 16 bytes, but only #{buffer.size} bytes given" if buffer.size < 16
79
+ raise DecodingError, "UUID requires 16 bytes, but only #{buffer.bytesize} bytes given" if buffer.bytesize < 16
80
80
  Uuid.new(read_varint!(buffer, 16, false))
81
81
  end
82
82
 
@@ -90,7 +90,7 @@ module Cql
90
90
  def read_bytes!(buffer)
91
91
  size = read_int!(buffer)
92
92
  return nil if size & 0x80000000 == 0x80000000
93
- raise DecodingError, "Byte array length is #{size}, but only #{buffer.size} bytes given" if buffer.size < size
93
+ raise DecodingError, "Byte array length is #{size}, but only #{buffer.bytesize} bytes given" if buffer.bytesize < size
94
94
  bytes = buffer.slice!(0, size)
95
95
  bytes.force_encoding(::Encoding::BINARY)
96
96
  bytes
@@ -99,7 +99,7 @@ module Cql
99
99
  def read_short_bytes!(buffer)
100
100
  size = read_short!(buffer)
101
101
  return nil if size & 0x8000 == 0x8000
102
- raise DecodingError, "Byte array length is #{size}, but only #{buffer.size} bytes given" if buffer.size < size
102
+ raise DecodingError, "Byte array length is #{size}, but only #{buffer.bytesize} bytes given" if buffer.bytesize < size
103
103
  bytes = buffer.slice!(0, size)
104
104
  bytes.force_encoding(::Encoding::BINARY)
105
105
  bytes
@@ -116,7 +116,7 @@ module Cql
116
116
 
117
117
  def read_inet!(buffer)
118
118
  size = read_byte!(buffer)
119
- raise DecodingError, "Inet requires #{size} bytes, but only #{buffer.size} bytes given" if buffer.size < size
119
+ raise DecodingError, "Inet requires #{size} bytes, but only #{buffer.bytesize} bytes given" if buffer.bytesize < size
120
120
  ip_addr = IPAddr.new_ntoh(buffer.slice!(0, size))
121
121
  port = read_int!(buffer)
122
122
  [ip_addr, port]
@@ -14,14 +14,16 @@ module Cql
14
14
  end
15
15
 
16
16
  def write_string(buffer, str)
17
- buffer << [str.length].pack(Formats::SHORT_FORMAT)
17
+ buffer << [str.bytesize].pack(Formats::SHORT_FORMAT)
18
18
  buffer << str
19
+ buffer.force_encoding(::Encoding::BINARY)
19
20
  buffer
20
21
  end
21
22
 
22
23
  def write_long_string(buffer, str)
23
- buffer << [str.length].pack(Formats::INT_FORMAT)
24
+ buffer << [str.bytesize].pack(Formats::INT_FORMAT)
24
25
  buffer << str
26
+ buffer.force_encoding(::Encoding::BINARY)
25
27
  buffer
26
28
  end
27
29
 
@@ -39,21 +41,23 @@ module Cql
39
41
 
40
42
  def write_bytes(buffer, bytes)
41
43
  if bytes
42
- write_int(buffer, bytes.length)
44
+ write_int(buffer, bytes.bytesize)
43
45
  buffer << bytes
44
46
  else
45
47
  write_int(buffer, -1)
46
48
  end
49
+ buffer.force_encoding(::Encoding::BINARY)
47
50
  buffer
48
51
  end
49
52
 
50
53
  def write_short_bytes(buffer, bytes)
51
54
  if bytes
52
- write_short(buffer, bytes.length)
55
+ write_short(buffer, bytes.bytesize)
53
56
  buffer << bytes
54
57
  else
55
58
  write_short(buffer, -1)
56
59
  end
60
+ buffer.force_encoding(::Encoding::BINARY)
57
61
  buffer
58
62
  end
59
63
 
data/lib/cql/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Cql
4
- VERSION = '1.0.0.pre1'.freeze
4
+ VERSION = '1.0.0.pre2'.freeze
5
5
  end
@@ -123,6 +123,10 @@ module Cql
123
123
  io_reactor.should_not be_running
124
124
  end
125
125
 
126
+ it 'does nothing when called before #start!' do
127
+ client.shutdown!
128
+ end
129
+
126
130
  it 'accepts multiple calls to #shutdown!' do
127
131
  client.start!
128
132
  client.shutdown!
@@ -286,6 +290,14 @@ module Cql
286
290
  end
287
291
 
288
292
  describe '#prepare' do
293
+ let :id do
294
+ 'A' * 32
295
+ end
296
+
297
+ let :metadata do
298
+ [['stuff', 'things', 'item', :varchar]]
299
+ end
300
+
289
301
  before do
290
302
  client.start!
291
303
  end
@@ -302,8 +314,6 @@ module Cql
302
314
  end
303
315
 
304
316
  it 'executes a prepared statement' do
305
- id = 'A' * 32
306
- metadata = [['stuff', 'things', 'item', :varchar]]
307
317
  io_reactor.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
308
318
  statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?')
309
319
  statement.execute('foo')
@@ -311,20 +321,23 @@ module Cql
311
321
  end
312
322
 
313
323
  it 'returns a prepared statement that knows the metadata' do
314
- id = 'A' * 32
315
- metadata = [['stuff', 'things', 'item', :varchar]]
316
324
  io_reactor.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
317
325
  statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?')
318
326
  statement.metadata['item'].type == :varchar
319
327
  end
320
328
 
329
+ it 'executes a prepared statement with a specific consistency level' do
330
+ io_reactor.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
331
+ statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?')
332
+ statement.execute('thing', :local_quorum)
333
+ last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['thing'], :local_quorum)
334
+ end
335
+
321
336
  it 'executes a prepared statement using the right connection' do
322
337
  client.shutdown!
323
338
  io_reactor.stop.get
324
339
  io_reactor.start.get
325
340
 
326
- metadata = [['stuff', 'things', 'item', :varchar]]
327
-
328
341
  c = described_class.new(connection_options.merge(host: 'h1.example.com,h2.example.com,h3.example.com'))
329
342
  c.start!
330
343
 
@@ -142,7 +142,7 @@ module Cql
142
142
  io_reactor.start
143
143
  io_reactor.add_connection(host, port).get
144
144
  io_reactor.queue_request(Cql::Protocol::StartupRequest.new)
145
- await { server.received_bytes.size > 0 }
145
+ await { server.received_bytes.bytesize > 0 }
146
146
  server.received_bytes[3, 1].should == "\x01"
147
147
  end
148
148
 
@@ -150,7 +150,7 @@ module Cql
150
150
  io_reactor.queue_request(Cql::Protocol::StartupRequest.new)
151
151
  io_reactor.start
152
152
  io_reactor.add_connection(host, port).get
153
- await { server.received_bytes.size > 0 }
153
+ await { server.received_bytes.bytesize > 0 }
154
154
  server.received_bytes[3, 1].should == "\x01"
155
155
  end
156
156
 
@@ -52,6 +52,18 @@ module Cql
52
52
  buffer.should == "\x00\x05hello"
53
53
  end
54
54
 
55
+ it 'returns a binary string' do
56
+ str = 'I love π'.force_encoding(::Encoding::UTF_8)
57
+ Encoding.write_string(buffer, str)
58
+ buffer.encoding.should == ::Encoding::BINARY
59
+ end
60
+
61
+ it 'encodes a string with multibyte characters' do
62
+ str = 'I love π'.force_encoding(::Encoding::UTF_8)
63
+ Encoding.write_string(buffer, str)
64
+ buffer.should == "\x00\x09I love π"
65
+ end
66
+
55
67
  it 'encodes an empty string' do
56
68
  Encoding.write_string(buffer, '')
57
69
  buffer.should == "\x00\x00"
@@ -75,6 +87,18 @@ module Cql
75
87
  buffer.should start_with("\x00\x12\x4f\x80hello world hello world hello world hello")
76
88
  end
77
89
 
90
+ it 'returns a binary string' do
91
+ str = 'I love π'.force_encoding(::Encoding::UTF_8)
92
+ Encoding.write_long_string(buffer, str)
93
+ buffer.encoding.should == ::Encoding::BINARY
94
+ end
95
+
96
+ it 'encodes a string with multibyte characters' do
97
+ str = 'I love π'.force_encoding(::Encoding::UTF_8)
98
+ Encoding.write_long_string(buffer, str)
99
+ buffer.should == "\x00\x00\x00\x09I love π"
100
+ end
101
+
78
102
  it 'encodes an empty string' do
79
103
  Encoding.write_long_string(buffer, '')
80
104
  buffer.should == "\x00\x00\x00\x00"
@@ -120,6 +144,18 @@ module Cql
120
144
  buffer.should == "\x00\x04\x00\x03foo\x00\x03bar\x00\x05hello\x00\x05world"
121
145
  end
122
146
 
147
+ it 'returns a binary string' do
148
+ str = %w[I love π].map { |str| str.force_encoding(::Encoding::UTF_8) }
149
+ Encoding.write_string_list(buffer, str)
150
+ buffer.encoding.should == ::Encoding::BINARY
151
+ end
152
+
153
+ it 'encodes a string with multibyte characters' do
154
+ str = %w[I love π].map { |str| str.force_encoding(::Encoding::UTF_8) }
155
+ Encoding.write_string_list(buffer, str)
156
+ buffer.should == "\x00\x03\x00\x01I\x00\x04love\x00\x02π"
157
+ end
158
+
123
159
  it 'encodes an empty string list' do
124
160
  Encoding.write_string_list(buffer, [])
125
161
  buffer.should == "\x00\x00"
@@ -143,6 +179,18 @@ module Cql
143
179
  buffer.should == ("\x00\x00\x07\xd0" << ("\xaa" * 2000))
144
180
  end
145
181
 
182
+ it 'returns a binary string' do
183
+ str = 'I love π'.force_encoding(::Encoding::UTF_8)
184
+ Encoding.write_bytes(buffer, str)
185
+ buffer.encoding.should == ::Encoding::BINARY
186
+ end
187
+
188
+ it 'encodes a string with multibyte characters' do
189
+ str = 'I love π'.force_encoding(::Encoding::UTF_8)
190
+ Encoding.write_bytes(buffer, str)
191
+ buffer.should == "\x00\x00\x00\x09I love π"
192
+ end
193
+
146
194
  it 'encodes nil' do
147
195
  Encoding.write_bytes(buffer, nil)
148
196
  buffer.should == "\xff\xff\xff\xff"
@@ -166,6 +214,18 @@ module Cql
166
214
  buffer.should == "\x00\x03\xaa\xbb\xcc"
167
215
  end
168
216
 
217
+ it 'returns a binary string' do
218
+ str = 'I love π'.force_encoding(::Encoding::UTF_8)
219
+ Encoding.write_short_bytes(buffer, str)
220
+ buffer.encoding.should == ::Encoding::BINARY
221
+ end
222
+
223
+ it 'encodes a string with multibyte characters' do
224
+ str = 'I love π'.force_encoding(::Encoding::UTF_8)
225
+ Encoding.write_short_bytes(buffer, str)
226
+ buffer.should == "\x00\x09I love π"
227
+ end
228
+
169
229
  it 'encodes nil' do
170
230
  Encoding.write_short_bytes(buffer, nil)
171
231
  buffer.should == "\xff\xff"
@@ -56,6 +56,8 @@ describe 'A CQL client' do
56
56
  statement = client.prepare('SELECT * FROM system.schema_keyspaces WHERE keyspace_name = ?')
57
57
  result = statement.execute('system')
58
58
  result.should have(1).item
59
+ result = statement.execute('system', :one)
60
+ result.should have(1).item
59
61
  end
60
62
 
61
63
  context 'with multiple connections' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cql-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre1
4
+ version: 1.0.0.pre2
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-24 00:00:00.000000000 Z
12
+ date: 2013-03-08 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: A pure Ruby CQL3 driver for Cassandra
15
15
  email: