cassandra-driver 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +8 -8
  2. data/README.md +4 -4
  3. data/lib/cassandra.rb +5 -3
  4. data/lib/cassandra/cluster/client.rb +88 -95
  5. data/lib/cassandra/cluster/control_connection.rb +14 -13
  6. data/lib/cassandra/column.rb +1 -9
  7. data/lib/cassandra/execution/options.rb +24 -1
  8. data/lib/cassandra/executors.rb +2 -0
  9. data/lib/cassandra/load_balancing.rb +0 -2
  10. data/lib/cassandra/protocol.rb +7 -5
  11. data/lib/cassandra/protocol/coder.rb +509 -0
  12. data/lib/cassandra/protocol/cql_byte_buffer.rb +4 -0
  13. data/lib/cassandra/protocol/cql_protocol_handler.rb +38 -57
  14. data/lib/cassandra/protocol/requests/auth_response_request.rb +1 -1
  15. data/lib/cassandra/protocol/requests/batch_request.rb +35 -19
  16. data/lib/cassandra/protocol/requests/credentials_request.rb +1 -1
  17. data/lib/cassandra/protocol/requests/execute_request.rb +3 -16
  18. data/lib/cassandra/protocol/requests/options_request.rb +1 -1
  19. data/lib/cassandra/protocol/requests/prepare_request.rb +1 -1
  20. data/lib/cassandra/protocol/requests/query_request.rb +5 -61
  21. data/lib/cassandra/protocol/requests/register_request.rb +1 -1
  22. data/lib/cassandra/protocol/requests/startup_request.rb +1 -1
  23. data/lib/cassandra/protocol/response.rb +0 -9
  24. data/lib/cassandra/protocol/responses/already_exists_error_response.rb +40 -0
  25. data/lib/cassandra/protocol/responses/auth_challenge_response.rb +0 -4
  26. data/lib/cassandra/protocol/responses/auth_success_response.rb +1 -5
  27. data/lib/cassandra/protocol/responses/authenticate_response.rb +0 -4
  28. data/lib/cassandra/protocol/responses/error_response.rb +0 -12
  29. data/lib/cassandra/protocol/responses/event_response.rb +0 -8
  30. data/lib/cassandra/protocol/responses/prepared_result_response.rb +0 -10
  31. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +1 -1
  32. data/lib/cassandra/protocol/responses/read_timeout_error_response.rb +42 -0
  33. data/lib/cassandra/protocol/responses/ready_response.rb +0 -4
  34. data/lib/cassandra/protocol/responses/result_response.rb +0 -7
  35. data/lib/cassandra/protocol/responses/rows_result_response.rb +0 -101
  36. data/lib/cassandra/protocol/responses/schema_change_event_response.rb +0 -3
  37. data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +0 -4
  38. data/lib/cassandra/protocol/responses/status_change_event_response.rb +0 -4
  39. data/lib/cassandra/protocol/responses/supported_response.rb +0 -4
  40. data/lib/cassandra/protocol/responses/unavailable_error_response.rb +41 -0
  41. data/lib/cassandra/protocol/responses/unprepared_error_response.rb +39 -0
  42. data/lib/cassandra/protocol/responses/void_result_response.rb +0 -4
  43. data/lib/cassandra/protocol/responses/write_timeout_error_response.rb +44 -0
  44. data/lib/cassandra/protocol/v1.rb +238 -0
  45. data/lib/cassandra/session.rb +95 -16
  46. data/lib/cassandra/statements/batch.rb +33 -6
  47. data/lib/cassandra/statements/bound.rb +3 -3
  48. data/lib/cassandra/statements/prepared.rb +38 -10
  49. data/lib/cassandra/statements/simple.rb +72 -3
  50. data/lib/cassandra/table.rb +2 -3
  51. data/lib/cassandra/util.rb +18 -0
  52. data/lib/cassandra/version.rb +1 -1
  53. metadata +8 -5
  54. data/lib/cassandra/protocol/frame_decoder.rb +0 -128
  55. data/lib/cassandra/protocol/frame_encoder.rb +0 -48
  56. data/lib/cassandra/protocol/responses/detailed_error_response.rb +0 -75
  57. data/lib/cassandra/protocol/type_converter.rb +0 -389
@@ -19,10 +19,6 @@
19
19
  module Cassandra
20
20
  module Protocol
21
21
  class VoidResultResponse < ResultResponse
22
- def self.decode(protocol_version, buffer, length, trace_id=nil)
23
- new(trace_id)
24
- end
25
-
26
22
  def to_s
27
23
  %(RESULT VOID)
28
24
  end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright 2013-2014 DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ module Protocol
21
+ class WriteTimeoutErrorResponse < ErrorResponse
22
+ attr_reader :consistency, :received, :blockfor, :write_type
23
+
24
+ def initialize(code, message, consistency, received, blockfor, write_type)
25
+ super(code, message)
26
+
27
+ write_type.downcase!
28
+
29
+ @consistency = consistency
30
+ @received = received
31
+ @blockfor = blockfor
32
+ @write_type = write_type.to_sym
33
+ end
34
+
35
+ def to_error(statement = nil)
36
+ Errors::WriteTimeoutError.new(@message, statement, @write_type, @consistency, @blockfor, @received)
37
+ end
38
+
39
+ def to_s
40
+ "#{super} #{@write_type} #{@consistency} #{@blockfor} #{@received}"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,238 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright 2013-2014 DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ module Protocol
21
+ module V1
22
+ class Encoder
23
+ HEADER_FORMAT = 'c4N'.freeze
24
+
25
+ def initialize(compressor = nil, protocol_version = 1)
26
+ @compressor = compressor
27
+ @protocol_version = protocol_version
28
+ end
29
+
30
+ def encode(buffer, request, stream_id)
31
+ flags = request.trace? ? 2 : 0
32
+ body = request.write(CqlByteBuffer.new, @protocol_version, self)
33
+
34
+ if @compressor && request.compressable? && @compressor.compress?(body)
35
+ flags |= 1
36
+ body = @compressor.compress(body)
37
+ end
38
+
39
+ header = [@protocol_version, flags, stream_id, request.opcode, body.bytesize]
40
+ buffer << header.pack(HEADER_FORMAT)
41
+ buffer << body
42
+ buffer
43
+ end
44
+
45
+ def write_parameters(buffer, params, types)
46
+ Coder.write_values_v1(buffer, params, types)
47
+ end
48
+ end
49
+
50
+ class Decoder
51
+ def initialize(handler, compressor = nil)
52
+ @handler = handler
53
+ @compressor = compressor
54
+ @state = :header
55
+ @header = nil
56
+ @length = nil
57
+ @buffer = CqlByteBuffer.new
58
+ end
59
+
60
+ def <<(data)
61
+ @buffer << data
62
+
63
+ __send__(:"decode_#{@state}", @buffer)
64
+ end
65
+
66
+ private
67
+
68
+ READY = ReadyResponse.new
69
+
70
+ def decode_header(buffer)
71
+ loop do
72
+ buffer_length = buffer.length
73
+
74
+ return if buffer_length < 8
75
+
76
+ frame_header = buffer.read_int
77
+ frame_length = buffer.read_int
78
+
79
+ if (buffer_length - 8) < frame_length
80
+ @header = frame_header
81
+ @length = frame_length
82
+ @state = :body
83
+
84
+ return
85
+ end
86
+
87
+ actual_decode(buffer, frame_header, frame_length)
88
+ end
89
+
90
+ nil
91
+ end
92
+
93
+ def decode_body(buffer)
94
+ frame_header = @header
95
+ frame_length = @length
96
+
97
+ loop do
98
+ buffer_length = buffer.length
99
+
100
+ return if buffer_length < frame_length
101
+
102
+ actual_decode(buffer, frame_header, frame_length)
103
+
104
+ if (buffer_length - frame_length) < 8
105
+ @header = nil
106
+ @length = nil
107
+ @state = :header
108
+
109
+ return
110
+ end
111
+
112
+ frame_header = buffer.read_int
113
+ frame_length = buffer.read_int
114
+ end
115
+
116
+ @header = frame_header
117
+ @length = frame_length
118
+
119
+ nil
120
+ end
121
+
122
+ def actual_decode(buffer, fields, size)
123
+ protocol_version = (fields >> 24) & 0x7f
124
+ compression = (fields >> 16) & 0x01
125
+ tracing = (fields >> 16) & 0x02
126
+ stream_id = (fields >> 8) & 0xff
127
+ stream_id = (stream_id & 0x7f) - (stream_id & 0x80)
128
+ opcode = fields & 0xff
129
+
130
+ if compression == 1
131
+ if @compressor
132
+ buffer = CqlByteBuffer.new(@compressor.decompress(buffer.read(size)))
133
+ size = buffer.size
134
+ else
135
+ raise Errors::DecodingError, 'Compressed frame received, but no compressor configured'
136
+ end
137
+ end
138
+
139
+ if tracing == 2
140
+ trace_id = buffer.read_uuid
141
+ size -= 16
142
+ else
143
+ trace_id = nil
144
+ end
145
+
146
+ extra_length = buffer.length - size
147
+ response = decode_response(opcode, protocol_version, buffer, size, trace_id)
148
+
149
+ if buffer.length > extra_length
150
+ buffer.discard(buffer.length - extra_length)
151
+ end
152
+
153
+ if stream_id == -1
154
+ @handler.notify_event_listeners(response)
155
+ else
156
+ @handler.complete_request(stream_id, response)
157
+ end
158
+ end
159
+
160
+ CODE_ERROR = 0x00
161
+ CODE_READY = 0x02
162
+ CODE_AUTHENTICATE = 0x03
163
+ CODE_SUPPORTED = 0x06
164
+ CODE_RESULT = 0x08
165
+ CODE_EVENT = 0x0c
166
+ CODE_AUTH_CHALLENGE = 0x0e
167
+ CODE_AUTH_SUCCESS = 0x10
168
+
169
+ def decode_response(opcode, protocol_version, buffer, size, trace_id)
170
+ response = case opcode
171
+ when CODE_READY then READY
172
+ when CODE_AUTHENTICATE then AuthenticateResponse.new(buffer.read_string)
173
+ when CODE_AUTH_CHALLENGE then AuthChallengeResponse.new(buffer.read_bytes)
174
+ when CODE_AUTH_SUCCESS then AuthSuccessResponse.new(buffer.read_bytes)
175
+ when CODE_SUPPORTED then SupportedResponse.new(buffer.read_string_multimap)
176
+ when CODE_ERROR
177
+ code = buffer.read_int
178
+ message = buffer.read_string
179
+
180
+ case code
181
+ when 0x1000 then UnavailableErrorResponse.new(code, message, buffer.read_consistency, buffer.read_int, buffer.read_int)
182
+ when 0x1100 then WriteTimeoutErrorResponse.new(code, message, buffer.read_consistency, buffer.read_int, buffer.read_int, buffer.read_string)
183
+ when 0x1200 then ReadTimeoutErrorResponse.new(code, message, buffer.read_consistency, buffer.read_int, buffer.read_int, (buffer.read_byte != 0))
184
+ when 0x2400 then AlreadyExistsErrorResponse.new(code, message, buffer.read_string, buffer.read_string)
185
+ when 0x2500 then UnpreparedErrorResponse.new(code, message, buffer.read_short_bytes)
186
+ else
187
+ ErrorResponse.new(code, message)
188
+ end
189
+ when CODE_RESULT
190
+ result_type = buffer.read_int
191
+ case result_type
192
+ when 0x0001 # Void
193
+ VoidResultResponse.new(trace_id)
194
+ when 0x0002 # Rows
195
+ original_buffer_length = buffer.length
196
+ column_specs, paging_state = Coder.read_metadata_v1(buffer)
197
+
198
+ if column_specs.nil?
199
+ consumed_bytes = original_buffer_length - buffer.length
200
+ remaining_bytes = CqlByteBuffer.new(buffer.read(size - consumed_bytes - 4))
201
+ RawRowsResultResponse.new(protocol_version, remaining_bytes, paging_state, trace_id)
202
+ else
203
+ RowsResultResponse.new(Coder.read_values_v1(buffer, column_specs), column_specs, paging_state, trace_id)
204
+ end
205
+ when 0x0003 # SetKeyspace
206
+ SetKeyspaceResultResponse.new(buffer.read_string, trace_id)
207
+ when 0x0004 # Prepared
208
+ id = buffer.read_short_bytes
209
+ params_metadata = Coder.read_metadata_v1(buffer).first
210
+ result_metadata = nil
211
+ result_metadata = Coder.read_metadata_v1(buffer).first if protocol_version > 1
212
+
213
+ PreparedResultResponse.new(id, params_metadata, result_metadata, trace_id)
214
+ when 0x0005 # SchemaChange
215
+ SchemaChangeResultResponse.new(buffer.read_string, buffer.read_string, buffer.read_string, trace_id)
216
+ else
217
+ raise Errors::DecodingError, "Unsupported result type: #{result_type.inspect}"
218
+ end
219
+ when CODE_EVENT
220
+ event_type = buffer.read_string
221
+ case event_type
222
+ when 'SCHEMA_CHANGE'
223
+ SchemaChangeEventResponse.new(buffer.read_string, buffer.read_string, buffer.read_string)
224
+ when 'STATUS_CHANGE'
225
+ StatusChangeEventResponse.new(buffer.read_string, *buffer.read_inet)
226
+ when 'TOPOLOGY_CHANGE'
227
+ TopologyChangeEventResponse.new(buffer.read_string, *buffer.read_inet)
228
+ else
229
+ raise Errors::DecodingError, "Unsupported event type: #{event_type.inspect}"
230
+ end
231
+ else
232
+ raise Errors::DecodingError, "Unsupported response opcode: #{opcode.inspect}"
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
@@ -33,14 +33,14 @@ module Cassandra
33
33
  @futures = futures_factory
34
34
  end
35
35
 
36
+ # @!method execute_async(statement, options = nil)
36
37
  # Executes a given statement and returns a future result
37
- # @!method execute_async(statement, *args, options = {})
38
38
  #
39
39
  # @param statement [String, Cassandra::Statements::Simple,
40
40
  # Cassandra::Statements::Bound, Cassandra::Statements::Prepared]
41
41
  # statement to execute
42
- # @param args [*Object] arguments to paramterized query or prepared
43
- # statement
42
+ #
43
+ # @param options [nil, Hash] a customizable set of options
44
44
  #
45
45
  # @option options [Symbol] :consistency consistency level for the request.
46
46
  # Must be one of {Cassandra::CONSISTENCIES}
@@ -56,35 +56,66 @@ module Cassandra
56
56
  # @option options [String] :paging_state (nil) this option is used for
57
57
  # stateless paging, where result paging is resumed some time after the
58
58
  # initial request.
59
+ # @option options [Array] :arguments (nil) positional arguments for the
60
+ # statement.
61
+ #
62
+ # @overload execute_async(statement, *args, options = nil)
63
+ # Executes a statement using the deprecated splat-style way of passing
64
+ # positional arguments/
65
+ #
66
+ # @deprecated This style will soon be removed, use the `:arguments`
67
+ # option to provide positional arguments instead.
68
+ #
69
+ # @param statement [String, Cassandra::Statements::Simple,
70
+ # Cassandra::Statements::Bound, Cassandra::Statements::Prepared]
71
+ # statement to execute
72
+ #
73
+ # @param args [*Object] **this style of positional arguments is
74
+ # deprecated, please use the `:arguments` options instead** -
75
+ # positional arguments to paramterized query or prepared statement.
76
+ #
77
+ # @param options [nil, Hash] a customizable set of options
78
+ #
79
+ # @note Last argument will be treated as `options` if it is a {Hash}.
80
+ # Therefore, make sure to pass empty `options` when executing a
81
+ # statement with the last parameter required to be a map datatype.
59
82
  #
60
83
  # @see Cassandra.cluster Options that can be specified on the cluster-level
61
84
  # and their default values.
62
85
  #
63
- # @note Last argument will be treated as `options` if it is a {Hash}.
64
- # Therefore, make sure to pass empty `options` when executing a statement
65
- # with the last parameter required to be a map datatype.
66
- #
67
- # @note Positional arguments are only supported on Apache Cassandra 2.0 and
68
- # above.
86
+ # @note Positional arguments for simple statements are only supported on
87
+ # starting with Apache Cassandra 2.0 and above.
69
88
  #
70
89
  # @return [Cassandra::Future<Cassandra::Result>]
71
90
  #
72
91
  # @see Cassandra::Session#execute A list of errors this future can be
73
92
  # resolved with
74
93
  def execute_async(statement, *args)
75
- if args.last.is_a?(::Hash)
76
- options = @options.override(args.pop)
94
+ options = nil
95
+ options = args.pop if args.last.is_a?(::Hash)
96
+
97
+ unless args.empty?
98
+ ::Kernel.warn "[WARNING] Splat style (*args) positional arguments " \
99
+ "are deprecated, use the :arguments option instead, " \
100
+ "called from #{caller.first}"
101
+
102
+ options ||= {}
103
+ options[:arguments] = args
104
+ end
105
+
106
+ if options
107
+ options = @options.override(options)
77
108
  else
78
109
  options = @options
79
110
  end
80
111
 
81
112
  case statement
82
113
  when ::String
83
- @client.query(Statements::Simple.new(statement, *args), options)
114
+ @client.query(Statements::Simple.new(statement, options.arguments), options)
84
115
  when Statements::Simple
85
116
  @client.query(statement, options)
86
117
  when Statements::Prepared
87
- @client.execute(statement.bind(*args), options)
118
+ @client.execute(statement.bind(options.arguments), options)
88
119
  when Statements::Bound
89
120
  @client.execute(statement, options)
90
121
  when Statements::Batch
@@ -92,10 +123,40 @@ module Cassandra
92
123
  else
93
124
  @futures.error(::ArgumentError.new("unsupported statement #{statement.inspect}"))
94
125
  end
126
+ rescue => e
127
+ @futures.error(e)
95
128
  end
96
129
 
130
+ # @!method execute(statement, options = nil)
97
131
  # A blocking wrapper around {Cassandra::Session#execute_async}
98
- # @!method execute(statement, *args, options = {})
132
+ #
133
+ # @param statement [String, Cassandra::Statements::Simple,
134
+ # Cassandra::Statements::Bound, Cassandra::Statements::Prepared]
135
+ # statement to execute
136
+ #
137
+ # @param options [nil, Hash] a customizable set of options
138
+ #
139
+ # @overload execute(statement, *args, options = nil)
140
+ # Executes a statement using the deprecated splat-style way of passing
141
+ # positional arguments/
142
+ #
143
+ # @deprecated This style will soon be removed, use the `:arguments`
144
+ # option to provide positional arguments instead.
145
+ #
146
+ # @param statement [String, Cassandra::Statements::Simple,
147
+ # Cassandra::Statements::Bound, Cassandra::Statements::Prepared]
148
+ # statement to execute
149
+ #
150
+ # @param args [*Object] **this style of positional arguments is
151
+ # deprecated, please use the `:arguments` options instead** -
152
+ # positional arguments to paramterized query or prepared statement.
153
+ #
154
+ # @param options [nil, Hash] a customizable set of options
155
+ #
156
+ # @note Last argument will be treated as `options` if it is a {Hash}.
157
+ # Therefore, make sure to pass empty `options` when executing a
158
+ # statement with the last parameter required to be a map datatype.
159
+ #
99
160
  # @see Cassandra::Session#execute_async
100
161
  # @see Cassandra::Future#get
101
162
  #
@@ -105,8 +166,24 @@ module Cassandra
105
166
  # @raise [Cassandra::Errors::ValidationError] if Cassandra fails to validate
106
167
  # @raise [Cassandra::Errors::TimeoutError] if request has not completed
107
168
  # within the `:timeout` given
108
- def execute(*args)
109
- execute_async(*args).get
169
+ def execute(statement, *args)
170
+ options = nil
171
+ options = args.pop if args.last.is_a?(::Hash)
172
+
173
+ unless args.empty?
174
+ ::Kernel.warn "[WARNING] Splat style (*args) positional arguments " \
175
+ "are deprecated, use the :arguments option instead, " \
176
+ "called from #{caller.first}"
177
+
178
+ options ||= {}
179
+ options[:arguments] = args
180
+ end
181
+
182
+ if options
183
+ execute_async(statement, options).get
184
+ else
185
+ execute_async(statement).get
186
+ end
110
187
  end
111
188
 
112
189
  # Prepares a given statement and returns a future prepared statement
@@ -135,6 +212,8 @@ module Cassandra
135
212
  else
136
213
  @futures.error(::ArgumentError.new("unsupported statement #{statement.inspect}"))
137
214
  end
215
+ rescue => e
216
+ @futures.error(e)
138
217
  end
139
218
 
140
219
  # A blocking wrapper around {Cassandra::Session#prepare_async}