cql-rb 1.0.0.pre1 → 1.0.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
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: