cassandra-driver 1.0.0.beta.3-java → 1.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +51 -14
  3. data/lib/cassandra.rb +164 -78
  4. data/lib/cassandra/address_resolution.rb +36 -0
  5. data/lib/cassandra/address_resolution/policies.rb +2 -0
  6. data/lib/cassandra/address_resolution/policies/ec2_multi_region.rb +56 -0
  7. data/lib/cassandra/address_resolution/policies/none.rb +35 -0
  8. data/lib/cassandra/auth.rb +1 -1
  9. data/lib/cassandra/auth/providers/password.rb +1 -1
  10. data/lib/cassandra/cluster.rb +18 -5
  11. data/lib/cassandra/cluster/client.rb +175 -101
  12. data/lib/cassandra/{client/connection_manager.rb → cluster/connection_pool.rb} +5 -5
  13. data/lib/cassandra/cluster/connector.rb +142 -56
  14. data/lib/cassandra/cluster/control_connection.rb +385 -134
  15. data/lib/cassandra/{client/null_logger.rb → cluster/failed_connection.rb} +12 -14
  16. data/lib/cassandra/cluster/options.rb +13 -2
  17. data/lib/cassandra/cluster/registry.rb +19 -9
  18. data/lib/cassandra/column.rb +5 -0
  19. data/lib/cassandra/compression.rb +1 -1
  20. data/lib/cassandra/compression/compressors/lz4.rb +1 -1
  21. data/lib/cassandra/compression/compressors/snappy.rb +1 -1
  22. data/lib/cassandra/driver.rb +29 -21
  23. data/lib/cassandra/errors.rb +325 -35
  24. data/lib/cassandra/execution/options.rb +13 -6
  25. data/lib/cassandra/execution/trace.rb +4 -4
  26. data/lib/cassandra/future.rb +7 -3
  27. data/lib/cassandra/keyspace.rb +5 -0
  28. data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +13 -4
  29. data/lib/cassandra/load_balancing/policies/token_aware.rb +2 -4
  30. data/lib/cassandra/load_balancing/policies/white_list.rb +3 -6
  31. data/lib/cassandra/null_logger.rb +35 -0
  32. data/lib/cassandra/protocol.rb +0 -16
  33. data/lib/cassandra/protocol/cql_byte_buffer.rb +18 -18
  34. data/lib/cassandra/protocol/cql_protocol_handler.rb +78 -8
  35. data/lib/cassandra/protocol/frame_decoder.rb +2 -2
  36. data/lib/cassandra/protocol/frame_encoder.rb +1 -1
  37. data/lib/cassandra/protocol/requests/query_request.rb +1 -11
  38. data/lib/cassandra/protocol/response.rb +1 -1
  39. data/lib/cassandra/protocol/responses/detailed_error_response.rb +16 -1
  40. data/lib/cassandra/protocol/responses/error_response.rb +17 -0
  41. data/lib/cassandra/protocol/responses/event_response.rb +1 -1
  42. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +1 -1
  43. data/lib/cassandra/protocol/responses/result_response.rb +1 -1
  44. data/lib/cassandra/protocol/responses/rows_result_response.rb +1 -1
  45. data/lib/cassandra/protocol/type_converter.rb +4 -3
  46. data/lib/cassandra/reconnection.rb +1 -1
  47. data/lib/cassandra/result.rb +4 -6
  48. data/lib/cassandra/retry.rb +3 -5
  49. data/lib/cassandra/session.rb +14 -5
  50. data/lib/cassandra/statements/prepared.rb +5 -1
  51. data/lib/cassandra/table.rb +6 -1
  52. data/lib/cassandra/time_uuid.rb +21 -83
  53. data/lib/cassandra/util.rb +131 -1
  54. data/lib/cassandra/uuid.rb +6 -4
  55. data/lib/cassandra/uuid/generator.rb +207 -0
  56. data/lib/cassandra/version.rb +1 -1
  57. data/lib/cassandra_murmur3.jar +0 -0
  58. metadata +43 -49
  59. data/lib/cassandra/client.rb +0 -144
  60. data/lib/cassandra/client/batch.rb +0 -212
  61. data/lib/cassandra/client/client.rb +0 -591
  62. data/lib/cassandra/client/column_metadata.rb +0 -54
  63. data/lib/cassandra/client/connector.rb +0 -277
  64. data/lib/cassandra/client/execute_options_decoder.rb +0 -59
  65. data/lib/cassandra/client/peer_discovery.rb +0 -50
  66. data/lib/cassandra/client/prepared_statement.rb +0 -314
  67. data/lib/cassandra/client/query_result.rb +0 -230
  68. data/lib/cassandra/client/request_runner.rb +0 -71
  69. data/lib/cassandra/client/result_metadata.rb +0 -48
  70. data/lib/cassandra/client/void_result.rb +0 -78
@@ -1,54 +0,0 @@
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 Client
21
- # Represents metadata about a column in a query result set or prepared
22
- # statement. Apart from the keyspace, table and column names there's also
23
- # the type as a symbol (e.g. `:varchar`, `:int`, `:date`).
24
- class ColumnMetadata
25
- attr_reader :keyspace, :table, :column_name, :type
26
-
27
- # @private
28
- def initialize(*args)
29
- @keyspace, @table, @column_name, @type = args
30
- end
31
-
32
- # @private
33
- def to_ary
34
- [@keyspace, @table, @column_name, @type]
35
- end
36
-
37
- def eql?(other)
38
- self.keyspace == other.keyspace && self.table == other.table && self.column_name == other.column_name && self.type == other.type
39
- end
40
- alias_method :==, :eql?
41
-
42
- def hash
43
- @h ||= begin
44
- h = 0
45
- h = ((h & 33554431) * 31) ^ @keyspace.hash
46
- h = ((h & 33554431) * 31) ^ @table.hash
47
- h = ((h & 33554431) * 31) ^ @column_name.hash
48
- h = ((h & 33554431) * 31) ^ @type.hash
49
- h
50
- end
51
- end
52
- end
53
- end
54
- end
@@ -1,277 +0,0 @@
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 Client
21
- # @private
22
- class ClusterConnector
23
- def initialize(sequence, logger)
24
- @sequence = sequence
25
- @logger = logger
26
- end
27
-
28
- def connect_all(hosts, connections_per_node)
29
- connections = hosts.flat_map do |host|
30
- Array.new(connections_per_node) do
31
- f = @sequence.connect(host)
32
- f.on_value { |connection| register_logging(connection) }
33
- f.recover do |error|
34
- @logger.warn('Failed connecting to node at %s: %s' % [host, error.message])
35
- FailedConnection.new(error, host)
36
- end
37
- end
38
- end
39
- Ione::Future.all(*connections).map do |connections|
40
- connected_connections = connections.select(&:connected?)
41
- if connected_connections.empty?
42
- e = connections.first.error
43
- if e.is_a?(Cassandra::Errors::QueryError) && e.code == 0x100
44
- e = Errors::AuthenticationError.new(e.message)
45
- end
46
- raise e
47
- end
48
- connected_connections
49
- end
50
- end
51
-
52
- private
53
-
54
- def register_logging(connection)
55
- args = [connection[:host_id], connection.host, connection.port, connection[:data_center]]
56
- @logger.info('Connected to node %s at %s:%d in data center %s' % args)
57
- connection.on_closed do |cause|
58
- message = 'Connection to node %s at %s:%d in data center %s closed' % args
59
- if cause
60
- message << (' unexpectedly: %s' % cause.message)
61
- @logger.warn(message)
62
- else
63
- @logger.info(message)
64
- end
65
- end
66
- end
67
- end
68
-
69
- # @private
70
- class Connector
71
- def initialize(steps)
72
- @steps = steps.dup
73
- end
74
-
75
- def connect(host)
76
- pending_connection = PendingConnection.new(host)
77
- seed = Ione::Future.resolved(pending_connection)
78
- f = @steps.reduce(seed) do |chain, step|
79
- chain.flat_map do |pending_connection|
80
- step.run(pending_connection)
81
- end
82
- end
83
- f.map do |pending_connection|
84
- pending_connection.connection
85
- end
86
- end
87
- end
88
-
89
- # @private
90
- class ConnectStep
91
- def initialize(io_reactor, protocol_handler_factory, port, connection_options, logger)
92
- @io_reactor = io_reactor
93
- @protocol_handler_factory = protocol_handler_factory
94
- @port = port
95
- @connection_options = connection_options
96
- @logger = logger
97
- end
98
-
99
- def run(pending_connection)
100
- @io_reactor.connect(pending_connection.host, @port, @connection_options, &@protocol_handler_factory).map do |connection|
101
- pending_connection.with_connection(connection)
102
- end
103
- end
104
- end
105
-
106
- # @private
107
- class CacheOptionsStep
108
- def initialize(timeout = nil)
109
- @timeout = timeout
110
- end
111
-
112
- def run(pending_connection)
113
- f = pending_connection.execute(Protocol::OptionsRequest.new, @timeout)
114
- f.on_value do |supported_options|
115
- pending_connection[:cql_version] = supported_options['CQL_VERSION']
116
- pending_connection[:compression] = supported_options['COMPRESSION']
117
- end
118
-
119
- f.map(pending_connection)
120
- end
121
- end
122
-
123
- # @private
124
- class InitializeStep
125
- def initialize(compressor, logger)
126
- @compressor = compressor
127
- @logger = logger
128
- end
129
-
130
- def run(pending_connection)
131
- compression = @compressor && @compressor.algorithm
132
- supported_algorithms = pending_connection[:compression]
133
- if compression && !supported_algorithms.include?(compression)
134
- @logger.warn(%[Compression algorithm "#{compression}" not supported (server supports "#{supported_algorithms.join('", "')}")])
135
- compression = nil
136
- end
137
- @logger.debug('Using "%s" compression' % compression) if compression
138
- supported_cql_versions = pending_connection[:cql_version]
139
- cql_version = (supported_cql_versions && !supported_cql_versions.empty?) ? supported_cql_versions.first : '3.1.0'
140
-
141
- f = pending_connection.execute(Protocol::StartupRequest.new(cql_version, compression))
142
- f.map do |startup_response|
143
- if startup_response.is_a?(AuthenticationRequired)
144
- pending_connection.with_authentication_class(startup_response.authentication_class)
145
- else
146
- pending_connection
147
- end
148
- end
149
- end
150
- end
151
-
152
- # @private
153
- class SaslAuthenticationStep
154
- def initialize(auth_provider)
155
- @auth_provider = auth_provider
156
- end
157
-
158
- def run(pending_connection)
159
- if pending_connection.authentication_class
160
- begin
161
- authenticator = @auth_provider && @auth_provider.create_authenticator(pending_connection.authentication_class)
162
- if authenticator
163
- token = authenticator.initial_response
164
- challenge_cycle(pending_connection, authenticator, token)
165
- elsif @auth_provider
166
- Ione::Future.failed(Errors::AuthenticationError.new('Auth provider does not support the required authentication class "%s" and/or protocol version %d' % [pending_connection.authentication_class, @protocol_version]))
167
- else
168
- Ione::Future.failed(Errors::AuthenticationError.new('Server requested authentication, but no auth provider found'))
169
- end
170
- rescue => e
171
- Ione::Future.failed(Errors::AuthenticationError.new('Auth provider raised an error: %s' % e.message))
172
- end
173
- else
174
- Ione::Future.resolved(pending_connection)
175
- end
176
- end
177
-
178
- def challenge_cycle(pending_connection, authenticator, response_token)
179
- request = Protocol::AuthResponseRequest.new(response_token)
180
- f = pending_connection.execute(request) { |raw_response| raw_response }
181
- f.flat_map do |response|
182
- case response
183
- when Protocol::AuthChallengeResponse
184
- token = authenticator.challenge_response(response.token)
185
- challenge_cycle(pending_connection, authenticator, token)
186
- when Protocol::AuthSuccessResponse
187
- authenticator.authentication_successful(response.token) rescue nil
188
- Ione::Future.resolved(pending_connection)
189
- else
190
- Ione::Future.resolved(pending_connection)
191
- end
192
- end
193
- end
194
- end
195
-
196
- # @private
197
- class CredentialsAuthenticationStep
198
- def initialize(credentials)
199
- @credentials = credentials
200
- end
201
-
202
- def run(pending_connection)
203
- if pending_connection.authentication_class
204
- if @credentials
205
- request = Protocol::CredentialsRequest.new(@credentials)
206
- pending_connection.execute(request).map(pending_connection)
207
- else
208
- Ione::Future.failed(Errors::AuthenticationError.new('Server requested authentication, but no credentials provided'))
209
- end
210
- else
211
- Ione::Future.resolved(pending_connection)
212
- end
213
- end
214
- end
215
-
216
- # @private
217
- class CachePropertiesStep
218
- def run(pending_connection)
219
- request = Protocol::QueryRequest.new('SELECT data_center, host_id FROM system.local', nil, nil, :one)
220
- f = pending_connection.execute(request)
221
- f.on_value do |result|
222
- unless result.empty?
223
- pending_connection[:host_id] = result.first['host_id']
224
- pending_connection[:data_center] = result.first['data_center']
225
- end
226
- end
227
- f.map(pending_connection)
228
- end
229
- end
230
-
231
- # @private
232
- class PendingConnection
233
- attr_reader :host, :connection, :authentication_class
234
-
235
- def initialize(host, connection=nil, authentication_class=nil)
236
- @host = host
237
- @connection = connection
238
- @authentication_class = authentication_class
239
- @request_runner = RequestRunner.new
240
- end
241
-
242
- def with_connection(connection)
243
- self.class.new(host, connection, @authentication_class)
244
- end
245
-
246
- def with_authentication_class(authentication_class)
247
- self.class.new(host, @connection, authentication_class)
248
- end
249
-
250
- def [](key)
251
- @connection[key]
252
- end
253
-
254
- def []=(key, value)
255
- @connection[key] = value
256
- end
257
-
258
- def execute(request, timeout = nil, &block)
259
- @request_runner.execute(@connection, request, timeout, nil, &block)
260
- end
261
- end
262
-
263
- # @private
264
- class FailedConnection
265
- attr_reader :error, :host
266
-
267
- def initialize(error, host)
268
- @error = error
269
- @host = host
270
- end
271
-
272
- def connected?
273
- false
274
- end
275
- end
276
- end
277
- end
@@ -1,59 +0,0 @@
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 Client
21
- # @private
22
- class ExecuteOptionsDecoder
23
- def initialize(default_consistency)
24
- @default_consistency = default_consistency
25
- @default_options = {:consistency => @default_consistency}.freeze
26
- end
27
-
28
- def decode_options(*args)
29
- if args.empty?
30
- @default_options
31
- elsif args.size == 1
32
- decode_one(args.first)
33
- else
34
- args.each_with_object({}) do |options_or_consistency, result|
35
- result.merge!(decode_one(options_or_consistency))
36
- end
37
- end
38
- end
39
-
40
- private
41
-
42
- def decode_one(options_or_consistency)
43
- return @default_options unless options_or_consistency
44
- case options_or_consistency
45
- when Symbol
46
- {:consistency => options_or_consistency}
47
- when Hash
48
- if options_or_consistency.include?(:consistency)
49
- options_or_consistency
50
- else
51
- options = options_or_consistency.dup
52
- options[:consistency] = @default_consistency
53
- options
54
- end
55
- end
56
- end
57
- end
58
- end
59
- end
@@ -1,50 +0,0 @@
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 Client
21
- # @private
22
- class PeerDiscovery
23
- def initialize(seed_connections)
24
- @seed_connections = seed_connections
25
- @connection = seed_connections.sample
26
- @request_runner = RequestRunner.new
27
- end
28
-
29
- def new_hosts
30
- request = Protocol::QueryRequest.new('SELECT peer, data_center, host_id, rpc_address FROM system.peers', nil, nil, :one)
31
- response = @request_runner.execute(@connection, request)
32
- response.map do |result|
33
- result.each_with_object([]) do |row, new_peers|
34
- if include?(row['host_id'], row['data_center'])
35
- rpc_address = row['rpc_address'].to_s
36
- rpc_address = row['peer'].to_s if rpc_address == '0.0.0.0'
37
- new_peers << rpc_address
38
- end
39
- end
40
- end
41
- end
42
-
43
- private
44
-
45
- def include?(host_id, dc)
46
- @seed_connections.any? { |c| c[:data_center] == dc } && @seed_connections.none? { |c| c[:host_id] == host_id }
47
- end
48
- end
49
- end
50
- end
@@ -1,314 +0,0 @@
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 Client
21
- # A prepared statement are CQL queries that have been sent to the server
22
- # to be precompiled, so that when executed only their ID and not the whole
23
- # CQL string need to be sent. They support bound values, or placeholders
24
- # for values.
25
- #
26
- # Using a prepared statement for any query that you execute more than once
27
- # is highly recommended. Besides the benefit of having less network overhead,
28
- # and less processing overhead on the server side, they don't require you
29
- # to build CQL strings and escape special characters, or format non-character
30
- # data such as UUIDs, different numeric types, or collections, in the
31
- # correct way.
32
- #
33
- # You should only prepare a statement once and reuse the prepared statement
34
- # object every time you want to execute that particular query. The statement
35
- # object will make sure that it is prepared on all connections, and will
36
- # (lazily, but transparently) make sure it is prepared on any new connections.
37
- #
38
- # It is an anti-pattern to prepare the same query over and over again. It is
39
- # bad for performance, since every preparation requires a roundtrip to all
40
- # connected servers, and because of some bookeeping that is done to support
41
- # automatic preparation on new connections, it will lead to unnecessary
42
- # extra memory usage. There is no performance benefit in creating multiple
43
- # prepared statement objects for the same query.
44
- #
45
- # Prepared statement objects are completely thread safe and can be shared
46
- # across all threads in your application.
47
- #
48
- # @see Cassandra::Client::Client#prepare
49
- class PreparedStatement
50
- # Metadata describing the bound values
51
- #
52
- # @return [ResultMetadata]
53
- attr_reader :metadata
54
-
55
- # Metadata about the result (i.e. rows) that is returned when executing
56
- # this prepared statement.
57
- #
58
- # @return [ResultMetadata]
59
- attr_reader :result_metadata
60
-
61
- # Execute the prepared statement with a list of values to be bound to the
62
- # statements parameters.
63
- #
64
- # The number of arguments must equal the number of bound parameters. You
65
- # can also specify options as the last argument, or a symbol as a shortcut
66
- # for just specifying the consistency.
67
- #
68
- # Because you can specify options, or not, there is an edge case where if
69
- # the last parameter of your prepared statement is a map, and you forget
70
- # to specify a value for your map, the options will end up being sent to
71
- # Cassandra. Most other cases when you specify the wrong number of
72
- # arguments should result in an `ArgumentError` or `TypeError` being
73
- # raised.
74
- #
75
- # @example Preparing and executing an `INSERT` statement
76
- # statement = client.prepare(%(INSERT INTO metrics (id, time, value) VALUES (?, NOW(), ?)))
77
- # statement.execute(1234, 23432)
78
- # statement.execute(2345, 34543, tracing: true)
79
- # statement.execute(3456, 45654, consistency: :one)
80
- #
81
- # @example Preparing and executing a `SELECT` statement
82
- # statement = client.prepare(%(SELECT * FROM metrics WHERE id = ? AND time > ?))
83
- # result = statement.execute(1234, Time.now - 3600)
84
- # result.each do |row|
85
- # p row
86
- # end
87
- #
88
- # @param args [Array] the values for the bound parameters, and an optional
89
- # hash of options as last argument – see {Cassandra::Client::Client#execute}
90
- # for details on which options are available.
91
- # @raise [ArgumentError] raised when number of argument does not match
92
- # the number of parameters needed to be bound to the statement.
93
- # @raise [Cassandra::Errors::NotConnectedError] raised when the client is not connected
94
- # @raise [Cassandra::Errors::IoError] raised when there is an IO error, for example
95
- # if the server suddenly closes the connection
96
- # @raise [Cassandra::Errors::QueryError] raised when there is an error on the server side
97
- # @return [nil, Cassandra::Client::QueryResult, Cassandra::Client::VoidResult] Some
98
- # queries have no result and return `nil`, but `SELECT` statements
99
- # return an `Enumerable` of rows (see {Cassandra::Client::QueryResult}), and
100
- # `INSERT` and `UPDATE` return a similar type
101
- # (see {Cassandra::Client::VoidResult}).
102
- def execute(*args)
103
- end
104
-
105
- # Yields a batch when called with a block. The batch is automatically
106
- # executed at the end of the block and the result is returned.
107
- #
108
- # Returns a batch when called wihtout a block. The batch will remember
109
- # the options given and merge these with any additional options given
110
- # when {Cassandra::Client::PreparedStatementBatch#execute} is called.
111
- #
112
- # The batch yielded or returned by this method is not identical to the
113
- # regular batch objects yielded or returned by {Cassandra::Client::Client#batch}.
114
- # These prepared statement batch objects can be used only to add multiple
115
- # executions of the same prepared statement.
116
- #
117
- # Please note that the batch object returned by this method _is not thread
118
- # safe_.
119
- #
120
- # The type parameter can be ommitted and the options can then be given
121
- # as first parameter.
122
- #
123
- # @example Executing a prepared statement in a batch
124
- # statement = client.prepare(%(INSERT INTO metrics (id, time, value) VALUES (?, NOW(), ?)))
125
- # statement.batch do |batch|
126
- # batch.add(1234, 23423)
127
- # batch.add(2346, 13)
128
- # batch.add(2342, 2367)
129
- # batch.add(4562, 1231)
130
- # end
131
- #
132
- # @see Cassandra::Client::PreparedStatementBatch
133
- # @see Cassandra::Client::Client#batch
134
- # @param [Symbol] type the type of batch, must be one of `:logged`,
135
- # `:unlogged` and `:counter`. The precise meaning of these is defined
136
- # in the CQL specification.
137
- # @yieldparam [Cassandra::Client::PreparedStatementBatch] batch the batch
138
- # @return [Cassandra::Client::VoidResult, Cassandra::Client::Batch] when no block is
139
- # given the batch is returned, when a block is given the result of
140
- # executing the batch is returned (see {Cassandra::Client::Batch#execute}).
141
- def batch(type=:logged, options={})
142
- end
143
- end
144
-
145
- # @private
146
- class AsynchronousPreparedStatement < PreparedStatement
147
- # @private
148
- def initialize(cql, execute_options_decoder, connection_manager, logger)
149
- @cql = cql
150
- @execute_options_decoder = execute_options_decoder
151
- @connection_manager = connection_manager
152
- @logger = logger
153
- @request_runner = RequestRunner.new
154
- end
155
-
156
- def self.prepare(cql, execute_options_decoder, connection_manager, logger)
157
- statement = new(cql, execute_options_decoder, connection_manager, logger)
158
- futures = connection_manager.map do |connection|
159
- statement.prepare(connection)
160
- end
161
- Ione::Future.all(*futures).map(statement)
162
- rescue => e
163
- Ione::Future.failed(e)
164
- end
165
-
166
- def execute(*args)
167
- connection = @connection_manager.random_connection
168
- if connection[self]
169
- run(args, connection)
170
- else
171
- prepare(connection).flat_map do
172
- run(args, connection)
173
- end
174
- end
175
- rescue => e
176
- Ione::Future.failed(e)
177
- end
178
-
179
- def batch(type=:logged, options=nil)
180
- if type.is_a?(Hash)
181
- options = type
182
- type = :logged
183
- end
184
- b = AsynchronousBatch.new(type, @execute_options_decoder, @connection_manager, options)
185
- pb = AsynchronousPreparedStatementBatch.new(self, b)
186
- if block_given?
187
- yield pb
188
- pb.execute
189
- else
190
- pb
191
- end
192
- end
193
-
194
- # @private
195
- def prepare(connection)
196
- prepare_request = Protocol::PrepareRequest.new(@cql)
197
- f = @request_runner.execute(connection, prepare_request) do |response|
198
- connection[self] = response.id
199
- unless @raw_metadata
200
- # NOTE: this is not thread safe, but the worst that could happen
201
- # is that we assign the same data multiple times
202
- @raw_metadata = response.metadata
203
- @metadata = ResultMetadata.new(@raw_metadata)
204
- @raw_result_metadata = response.result_metadata
205
- if @raw_result_metadata
206
- @result_metadata = ResultMetadata.new(@raw_result_metadata)
207
- end
208
- end
209
- hex_id = response.id.each_byte.map { |x| x.to_s(16).rjust(2, '0') }.join('')
210
- @logger.debug('Statement %s prepared on node %s (%s:%d)' % [hex_id, connection[:host_id].to_s, connection.host, connection.port])
211
- end
212
- f.map(self)
213
- end
214
-
215
- # @private
216
- def prepared?(connection)
217
- !!connection[self]
218
- end
219
-
220
- # @private
221
- def add_to_batch(batch, connection, bound_args)
222
- statement_id = connection[self]
223
- unless statement_id
224
- raise Errors::NotPreparedError
225
- end
226
- unless bound_args.size == @raw_metadata.size
227
- raise ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}"
228
- end
229
- batch.add_prepared(statement_id, @raw_metadata, bound_args)
230
- end
231
-
232
- private
233
-
234
- def run(args, connection)
235
- bound_args = args.shift(@raw_metadata.size)
236
- unless bound_args.size == @raw_metadata.size && args.size <= 1
237
- raise ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}"
238
- end
239
- options = @execute_options_decoder.decode_options(args.last)
240
- statement_id = connection[self]
241
- request_metadata = @raw_result_metadata.nil?
242
- request = Protocol::ExecuteRequest.new(statement_id, @raw_metadata, bound_args, request_metadata, options[:consistency], options[:serial_consistency], options[:page_size], options[:paging_state], options[:trace])
243
- f = @request_runner.execute(connection, request, options[:timeout], @raw_result_metadata)
244
- if options.include?(:page_size)
245
- f = f.map { |result| AsynchronousPreparedPagedQueryResult.new(self, request, result, options) }
246
- end
247
- f
248
- end
249
- end
250
-
251
- # @private
252
- class SynchronousPreparedStatement < PreparedStatement
253
- include SynchronousBacktrace
254
-
255
- def initialize(async_statement)
256
- @async_statement = async_statement
257
- @metadata = async_statement.metadata
258
- @result_metadata = async_statement.result_metadata
259
- end
260
-
261
- def execute(*args)
262
- synchronous_backtrace do
263
- result = @async_statement.execute(*args).value
264
- result = SynchronousPagedQueryResult.new(result) if result.is_a?(PagedQueryResult)
265
- result
266
- end
267
- end
268
-
269
- def batch(type=:logged, options=nil, &block)
270
- if block_given?
271
- synchronous_backtrace { @async_statement.batch(type, options, &block).value }
272
- else
273
- SynchronousPreparedStatementBatch.new(@async_statement.batch(type, options))
274
- end
275
- end
276
-
277
- def pipeline
278
- pl = Pipeline.new(@async_statement)
279
- yield pl
280
- synchronous_backtrace { pl.value }
281
- end
282
-
283
- def async
284
- @async_statement
285
- end
286
-
287
- # @private
288
- def prepared?(connection)
289
- @async_statement.prepared?(connection)
290
- end
291
-
292
- # @private
293
- def add_to_batch(batch, connection, bound_arguments)
294
- @async_statement.add_to_batch(batch, connection, bound_arguments)
295
- end
296
- end
297
-
298
- # @private
299
- class Pipeline
300
- def initialize(async_statement)
301
- @async_statement = async_statement
302
- @futures = []
303
- end
304
-
305
- def execute(*args)
306
- @futures << @async_statement.execute(*args)
307
- end
308
-
309
- def value
310
- Ione::Future.all(*@futures).value
311
- end
312
- end
313
- end
314
- end