cql-rb 1.2.2 → 2.0.0.pre0

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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +4 -0
  3. data/README.md +139 -17
  4. data/lib/cql/client.rb +237 -8
  5. data/lib/cql/client/asynchronous_client.rb +138 -54
  6. data/lib/cql/client/asynchronous_prepared_statement.rb +41 -6
  7. data/lib/cql/client/authenticators.rb +46 -0
  8. data/lib/cql/client/batch.rb +115 -0
  9. data/lib/cql/client/connector.rb +255 -0
  10. data/lib/cql/client/execute_options_decoder.rb +25 -9
  11. data/lib/cql/client/keyspace_changer.rb +5 -5
  12. data/lib/cql/client/peer_discovery.rb +33 -0
  13. data/lib/cql/client/query_result.rb +124 -1
  14. data/lib/cql/client/request_runner.rb +4 -2
  15. data/lib/cql/client/synchronous_client.rb +14 -2
  16. data/lib/cql/client/synchronous_prepared_statement.rb +19 -1
  17. data/lib/cql/future.rb +97 -50
  18. data/lib/cql/io/connection.rb +0 -1
  19. data/lib/cql/io/io_reactor.rb +1 -1
  20. data/lib/cql/protocol.rb +8 -1
  21. data/lib/cql/protocol/cql_protocol_handler.rb +2 -2
  22. data/lib/cql/protocol/decoding.rb +10 -15
  23. data/lib/cql/protocol/frame_decoder.rb +2 -1
  24. data/lib/cql/protocol/frame_encoder.rb +5 -4
  25. data/lib/cql/protocol/requests/auth_response_request.rb +31 -0
  26. data/lib/cql/protocol/requests/batch_request.rb +59 -0
  27. data/lib/cql/protocol/requests/credentials_request.rb +1 -1
  28. data/lib/cql/protocol/requests/execute_request.rb +45 -17
  29. data/lib/cql/protocol/requests/options_request.rb +1 -1
  30. data/lib/cql/protocol/requests/prepare_request.rb +1 -1
  31. data/lib/cql/protocol/requests/query_request.rb +97 -5
  32. data/lib/cql/protocol/requests/register_request.rb +1 -1
  33. data/lib/cql/protocol/requests/startup_request.rb +4 -4
  34. data/lib/cql/protocol/response.rb +2 -2
  35. data/lib/cql/protocol/responses/auth_challenge_response.rb +25 -0
  36. data/lib/cql/protocol/responses/auth_success_response.rb +25 -0
  37. data/lib/cql/protocol/responses/authenticate_response.rb +1 -1
  38. data/lib/cql/protocol/responses/detailed_error_response.rb +1 -1
  39. data/lib/cql/protocol/responses/error_response.rb +3 -2
  40. data/lib/cql/protocol/responses/event_response.rb +3 -2
  41. data/lib/cql/protocol/responses/prepared_result_response.rb +10 -6
  42. data/lib/cql/protocol/responses/raw_rows_result_response.rb +27 -0
  43. data/lib/cql/protocol/responses/ready_response.rb +1 -1
  44. data/lib/cql/protocol/responses/result_response.rb +2 -2
  45. data/lib/cql/protocol/responses/rows_result_response.rb +43 -23
  46. data/lib/cql/protocol/responses/schema_change_event_response.rb +1 -1
  47. data/lib/cql/protocol/responses/schema_change_result_response.rb +1 -1
  48. data/lib/cql/protocol/responses/set_keyspace_result_response.rb +1 -1
  49. data/lib/cql/protocol/responses/status_change_event_response.rb +1 -1
  50. data/lib/cql/protocol/responses/supported_response.rb +1 -1
  51. data/lib/cql/protocol/responses/void_result_response.rb +1 -1
  52. data/lib/cql/protocol/type_converter.rb +2 -2
  53. data/lib/cql/uuid.rb +2 -2
  54. data/lib/cql/version.rb +1 -1
  55. data/spec/cql/client/asynchronous_client_spec.rb +493 -50
  56. data/spec/cql/client/asynchronous_prepared_statement_spec.rb +193 -11
  57. data/spec/cql/client/authenticators_spec.rb +56 -0
  58. data/spec/cql/client/batch_spec.rb +277 -0
  59. data/spec/cql/client/connector_spec.rb +606 -0
  60. data/spec/cql/client/execute_options_decoder_spec.rb +95 -0
  61. data/spec/cql/client/keyspace_changer_spec.rb +8 -8
  62. data/spec/cql/client/peer_discovery_spec.rb +92 -0
  63. data/spec/cql/client/query_result_spec.rb +352 -0
  64. data/spec/cql/client/request_runner_spec.rb +31 -5
  65. data/spec/cql/client/synchronous_client_spec.rb +44 -1
  66. data/spec/cql/client/synchronous_prepared_statement_spec.rb +63 -1
  67. data/spec/cql/future_spec.rb +50 -2
  68. data/spec/cql/protocol/cql_protocol_handler_spec.rb +16 -5
  69. data/spec/cql/protocol/decoding_spec.rb +16 -6
  70. data/spec/cql/protocol/encoding_spec.rb +3 -1
  71. data/spec/cql/protocol/frame_encoder_spec.rb +99 -50
  72. data/spec/cql/protocol/requests/auth_response_request_spec.rb +62 -0
  73. data/spec/cql/protocol/requests/batch_request_spec.rb +155 -0
  74. data/spec/cql/protocol/requests/credentials_request_spec.rb +1 -1
  75. data/spec/cql/protocol/requests/execute_request_spec.rb +184 -71
  76. data/spec/cql/protocol/requests/options_request_spec.rb +1 -1
  77. data/spec/cql/protocol/requests/prepare_request_spec.rb +1 -1
  78. data/spec/cql/protocol/requests/query_request_spec.rb +255 -32
  79. data/spec/cql/protocol/requests/register_request_spec.rb +1 -1
  80. data/spec/cql/protocol/requests/startup_request_spec.rb +12 -6
  81. data/spec/cql/protocol/responses/auth_challenge_response_spec.rb +31 -0
  82. data/spec/cql/protocol/responses/auth_success_response_spec.rb +31 -0
  83. data/spec/cql/protocol/responses/authenticate_response_spec.rb +2 -1
  84. data/spec/cql/protocol/responses/detailed_error_response_spec.rb +14 -7
  85. data/spec/cql/protocol/responses/error_response_spec.rb +4 -2
  86. data/spec/cql/protocol/responses/event_response_spec.rb +7 -4
  87. data/spec/cql/protocol/responses/prepared_result_response_spec.rb +89 -34
  88. data/spec/cql/protocol/responses/raw_rows_result_response_spec.rb +66 -0
  89. data/spec/cql/protocol/responses/ready_response_spec.rb +1 -1
  90. data/spec/cql/protocol/responses/result_response_spec.rb +19 -7
  91. data/spec/cql/protocol/responses/rows_result_response_spec.rb +56 -11
  92. data/spec/cql/protocol/responses/schema_change_event_response_spec.rb +2 -1
  93. data/spec/cql/protocol/responses/schema_change_result_response_spec.rb +2 -1
  94. data/spec/cql/protocol/responses/set_keyspace_result_response_spec.rb +1 -1
  95. data/spec/cql/protocol/responses/status_change_event_response_spec.rb +2 -1
  96. data/spec/cql/protocol/responses/supported_response_spec.rb +2 -1
  97. data/spec/cql/protocol/responses/topology_change_event_response_spec.rb +2 -1
  98. data/spec/cql/protocol/responses/void_result_response_spec.rb +1 -1
  99. data/spec/cql/protocol/type_converter_spec.rb +21 -4
  100. data/spec/cql/uuid_spec.rb +10 -3
  101. data/spec/integration/client_spec.rb +251 -28
  102. data/spec/integration/protocol_spec.rb +213 -62
  103. data/spec/integration/regression_spec.rb +4 -1
  104. data/spec/integration/uuid_spec.rb +4 -1
  105. data/spec/support/fake_io_reactor.rb +5 -5
  106. metadata +36 -7
  107. data/lib/cql/client/connection_helper.rb +0 -181
  108. data/spec/cql/client/connection_helper_spec.rb +0 -429
@@ -5,25 +5,26 @@ module Cql
5
5
  # @private
6
6
  class AsynchronousClient < Client
7
7
  def initialize(options={})
8
- compressor = options[:compressor]
8
+ @compressor = options[:compressor]
9
+ @cql_version = options[:cql_version]
9
10
  @logger = options[:logger] || NullLogger.new
10
- @io_reactor = options[:io_reactor] || Io::IoReactor.new(protocol_handler_factory(compressor))
11
+ @protocol_version = options[:protocol_version] || 2
12
+ @io_reactor = options[:io_reactor] || Io::IoReactor.new(protocol_handler_factory)
11
13
  @hosts = extract_hosts(options)
12
14
  @initial_keyspace = options[:keyspace]
15
+ @connections_per_node = options[:connections_per_node] || 1
13
16
  @lock = Mutex.new
14
- @connected = false
15
- @connecting = false
16
- @closing = false
17
17
  @request_runner = RequestRunner.new
18
18
  @keyspace_changer = KeyspaceChanger.new
19
19
  @connection_manager = ConnectionManager.new
20
- port = options[:port] || DEFAULT_PORT
21
- credentials = options[:credentials]
22
- connections_per_node = options[:connections_per_node] || 1
23
- connection_timeout = options[:connection_timeout] || DEFAULT_CONNECTION_TIMEOUT
24
- default_consistency = options[:default_consistency] || DEFAULT_CONSISTENCY
25
- @execute_options_decoder = ExecuteOptionsDecoder.new(default_consistency)
26
- @connection_helper = ConnectionHelper.new(@io_reactor, port, credentials, connections_per_node, connection_timeout, compressor, @logger)
20
+ @execute_options_decoder = ExecuteOptionsDecoder.new(options[:default_consistency] || DEFAULT_CONSISTENCY)
21
+ @port = options[:port] || DEFAULT_PORT
22
+ @connection_timeout = options[:connection_timeout] || DEFAULT_CONNECTION_TIMEOUT
23
+ @credentials = options[:credentials]
24
+ @auth_provider = options[:auth_provider] || @credentials && PlainTextAuthProvider.new(*@credentials.values_at(:username, :password))
25
+ @connected = false
26
+ @connecting = false
27
+ @closing = false
27
28
  end
28
29
 
29
30
  def connect
@@ -32,12 +33,15 @@ module Cql
32
33
  return @connected_future if can_execute?
33
34
  @connecting = true
34
35
  @connected_future = begin
35
- f = @connection_helper.connect(@hosts, @initial_keyspace)
36
- f.on_value do |connections|
36
+ f = @io_reactor.start
37
+ f = f.flat_map { connect_with_protocol_version_fallback }
38
+ f = f.flat_map { |connections| connect_to_all_peers(connections) }
39
+ f = f.flat_map do |connections|
37
40
  @connection_manager.add_connections(connections)
38
41
  register_event_listener(@connection_manager.random_connection)
39
42
  end
40
- f.map { self }
43
+ f = f.flat_map { use_keyspace(@connection_manager.snapshot, @initial_keyspace) }
44
+ f.map(self)
41
45
  end
42
46
  end
43
47
  @connected_future.on_complete(&method(:connected))
@@ -49,15 +53,16 @@ module Cql
49
53
  return @closed_future if @closing
50
54
  @closing = true
51
55
  @closed_future = begin
52
- f = @io_reactor.stop
53
56
  if @connecting
54
- ff = @connected_future
55
- ff = f.flat_map { ff }
56
- ff = f.fallback { ff }
57
+ f = @connected_future.recover
58
+ f = f.flat_map { @io_reactor.stop }
59
+ f = f.map(self)
60
+ f
57
61
  else
58
- ff = f
62
+ f = @io_reactor.stop
63
+ f = f.map(self)
64
+ f
59
65
  end
60
- ff.map { self }
61
66
  end
62
67
  end
63
68
  @closed_future.on_complete(&method(:closed))
@@ -74,20 +79,25 @@ module Cql
74
79
 
75
80
  def use(keyspace)
76
81
  with_failure_handler do
77
- connections = @connection_manager.select { |c| c.keyspace != keyspace }
78
- if connections.any?
79
- futures = connections.map { |connection| @keyspace_changer.use_keyspace(keyspace, connection) }
80
- Future.all(*futures).map { nil }
81
- else
82
- Future.resolved
83
- end
82
+ connections = @connection_manager.reject { |c| c.keyspace == keyspace }
83
+ return Future.resolved if connections.empty?
84
+ use_keyspace(connections, keyspace).map(nil)
84
85
  end
85
86
  end
86
87
 
87
- def execute(cql, options_or_consistency=nil)
88
+ def execute(cql, *args)
88
89
  with_failure_handler do
89
- consistency, timeout, trace = @execute_options_decoder.decode_options(options_or_consistency)
90
- execute_request(Protocol::QueryRequest.new(cql, consistency, trace), timeout)
90
+ options_or_consistency = nil
91
+ if args.last.is_a?(Symbol) || args.last.is_a?(Hash)
92
+ options_or_consistency = args.pop
93
+ end
94
+ options = @execute_options_decoder.decode_options(options_or_consistency)
95
+ request = Protocol::QueryRequest.new(cql, args, options[:type_hints], options[:consistency], options[:serial_consistency], options[:page_size], options[:paging_state], options[:trace])
96
+ f = execute_request(request, options[:timeout])
97
+ if options.include?(:page_size)
98
+ f = f.map { |result| AsynchronousQueryPagedQueryResult.new(self, request, result, options) }
99
+ end
100
+ f
91
101
  end
92
102
  end
93
103
 
@@ -97,15 +107,32 @@ module Cql
97
107
  end
98
108
  end
99
109
 
110
+ def batch(type=:logged, options=nil)
111
+ if type.is_a?(Hash)
112
+ options = type
113
+ type = :logged
114
+ end
115
+ b = AsynchronousBatch.new(type, @execute_options_decoder, @connection_manager, options)
116
+ if block_given?
117
+ yield b
118
+ b.execute
119
+ else
120
+ b
121
+ end
122
+ end
123
+
100
124
  private
101
125
 
126
+ DEFAULT_CQL_VERSIONS = {1 => '3.0.0'}
127
+ DEFAULT_CQL_VERSIONS.default = '3.1.0'
128
+ DEFAULT_CQL_VERSIONS.freeze
102
129
  DEFAULT_CONSISTENCY = :quorum
103
130
  DEFAULT_PORT = 9042
104
131
  DEFAULT_CONNECTION_TIMEOUT = 10
105
132
  MAX_RECONNECTION_ATTEMPTS = 5
106
133
 
107
- def protocol_handler_factory(compressor)
108
- lambda { |connection, timeout| Protocol::CqlProtocolHandler.new(connection, timeout, compressor) }
134
+ def protocol_handler_factory
135
+ lambda { |connection, timeout| Protocol::CqlProtocolHandler.new(connection, timeout, @protocol_version, @compressor) }
109
136
  end
110
137
 
111
138
  def extract_hosts(options)
@@ -118,6 +145,52 @@ module Cql
118
145
  end
119
146
  end
120
147
 
148
+ def create_cluster_connector
149
+ cql_version = @cql_version || DEFAULT_CQL_VERSIONS[@protocol_version]
150
+ authentication_step = @protocol_version == 1 ? CredentialsAuthenticationStep.new(@credentials) : SaslAuthenticationStep.new(@auth_provider)
151
+ ClusterConnector.new(
152
+ Connector.new([
153
+ ConnectStep.new(@io_reactor, @port, @connection_timeout, @logger),
154
+ CacheOptionsStep.new,
155
+ InitializeStep.new(cql_version, @compressor, @logger),
156
+ authentication_step,
157
+ CachePropertiesStep.new,
158
+ ]),
159
+ @logger
160
+ )
161
+ end
162
+
163
+ def connect_with_protocol_version_fallback
164
+ f = create_cluster_connector.connect_all(@hosts, @connections_per_node)
165
+ f.fallback do |error|
166
+ if error.is_a?(QueryError) && error.code == 0x0a && @protocol_version > 1
167
+ @logger.warn('Could not connect using protocol version %d (will try again with %d): %s' % [@protocol_version, @protocol_version - 1, error.message])
168
+ @protocol_version -= 1
169
+ connect_with_protocol_version_fallback
170
+ else
171
+ raise error
172
+ end
173
+ end
174
+ end
175
+
176
+ def connect_to_all_peers(seed_connections, initial_keyspace=@initial_keyspace)
177
+ @logger.debug('Looking for additional nodes')
178
+ peer_discovery = PeerDiscovery.new(seed_connections)
179
+ peer_discovery.new_hosts.flat_map do |hosts|
180
+ if hosts.empty?
181
+ @logger.debug('No additional nodes found')
182
+ Future.resolved(seed_connections)
183
+ else
184
+ @logger.debug('%d additional nodes found' % hosts.size)
185
+ f = create_cluster_connector.connect_all(hosts, @connections_per_node)
186
+ f = f.map do |discovered_connections|
187
+ seed_connections + discovered_connections
188
+ end
189
+ f.recover(seed_connections)
190
+ end
191
+ end
192
+ end
193
+
121
194
  def connected(f)
122
195
  if f.resolved?
123
196
  @lock.synchronize do
@@ -163,40 +236,51 @@ module Cql
163
236
  Future.failed(e)
164
237
  end
165
238
 
239
+ def use_keyspace(connections, keyspace)
240
+ futures = connections.map { |connection| @keyspace_changer.use_keyspace(connection, keyspace) }
241
+ Future.all(*futures)
242
+ end
243
+
166
244
  def register_event_listener(connection)
167
245
  register_request = Protocol::RegisterRequest.new(Protocol::TopologyChangeEventResponse::TYPE, Protocol::StatusChangeEventResponse::TYPE)
168
- execute_request(register_request, nil, connection)
169
- connection.on_closed do
170
- if connected?
171
- begin
172
- register_event_listener(@connection_manager.random_connection)
173
- rescue NotConnectedError
174
- # we had started closing down after the connection check
246
+ f = execute_request(register_request, nil, connection)
247
+ f.on_value do
248
+ connection.on_closed do
249
+ if connected?
250
+ begin
251
+ register_event_listener(@connection_manager.random_connection)
252
+ rescue NotConnectedError
253
+ # we had started closing down after the connection check
254
+ end
175
255
  end
176
256
  end
177
- end
178
- connection.on_event do |event|
179
- if event.change == 'UP' || event.change == 'NEW_NODE'
180
- @logger.debug('Received %s event' % event.change)
181
- unless @looking_for_nodes
182
- @looking_for_nodes = true
183
- handle_topology_change.on_complete do |f|
184
- @looking_for_nodes = false
257
+ connection.on_event do |event|
258
+ if event.change == 'UP' || event.change == 'NEW_NODE'
259
+ @logger.debug('Received %s event' % event.change)
260
+ unless @looking_for_nodes
261
+ @looking_for_nodes = true
262
+ handle_topology_change.on_complete do |f|
263
+ @looking_for_nodes = false
264
+ end
185
265
  end
186
266
  end
187
267
  end
188
268
  end
269
+ f
189
270
  end
190
271
 
191
272
  def handle_topology_change(remaning_attempts=MAX_RECONNECTION_ATTEMPTS)
192
273
  with_failure_handler do
193
274
  seed_connections = @connection_manager.snapshot
194
- f = @connection_helper.discover_peers(seed_connections, keyspace)
195
- f.flat_map do |connections|
196
- connected_connections = connections.select(&:connected?)
197
- if connected_connections.any?
198
- @connection_manager.add_connections(connected_connections)
199
- Future.resolved
275
+ f = connect_to_all_peers(seed_connections, keyspace)
276
+ f.flat_map do |all_connections|
277
+ new_connections = all_connections - seed_connections
278
+ if new_connections.size > 0
279
+ f = use_keyspace(new_connections, keyspace)
280
+ f.on_value do
281
+ @connection_manager.add_connections(new_connections)
282
+ end
283
+ f
200
284
  elsif remaning_attempts > 0
201
285
  timeout = 2**(MAX_RECONNECTION_ATTEMPTS - remaning_attempts)
202
286
  @logger.debug('Scheduling new peer discovery in %ds' % timeout)
@@ -18,7 +18,7 @@ module Cql
18
18
  futures = connection_manager.map do |connection|
19
19
  statement.prepare(connection)
20
20
  end
21
- Future.all(*futures).map { statement }
21
+ Future.all(*futures).map(statement)
22
22
  rescue => e
23
23
  Future.failed(e)
24
24
  end
@@ -36,6 +36,21 @@ module Cql
36
36
  Future.failed(e)
37
37
  end
38
38
 
39
+ def batch(type=:logged, options=nil)
40
+ if type.is_a?(Hash)
41
+ options = type
42
+ type = :logged
43
+ end
44
+ b = AsynchronousBatch.new(type, @execute_options_decoder, @connection_manager, options)
45
+ pb = AsynchronousPreparedStatementBatch.new(self, b)
46
+ if block_given?
47
+ yield pb
48
+ pb.execute
49
+ else
50
+ pb
51
+ end
52
+ end
53
+
39
54
  # @private
40
55
  def prepare(connection)
41
56
  prepare_request = Protocol::PrepareRequest.new(@cql)
@@ -46,25 +61,45 @@ module Cql
46
61
  # is that we assign the same data multiple times
47
62
  @raw_metadata = response.metadata
48
63
  @metadata = ResultMetadata.new(@raw_metadata)
64
+ @raw_result_metadata = response.result_metadata
65
+ if @raw_result_metadata
66
+ @result_metadata = ResultMetadata.new(@raw_result_metadata)
67
+ end
49
68
  end
50
69
  hex_id = response.id.each_byte.map { |x| x.to_s(16).rjust(2, '0') }.join('')
51
70
  @logger.debug('Statement %s prepared on node %s (%s:%d)' % [hex_id, connection[:host_id].to_s, connection.host, connection.port])
52
71
  end
53
- f.map { self }
72
+ f.map(self)
73
+ end
74
+
75
+ # @private
76
+ def add_to_batch(batch, connection, bound_args)
77
+ statement_id = connection[self]
78
+ unless statement_id
79
+ raise NotPreparedError
80
+ end
81
+ unless bound_args.size == @raw_metadata.size
82
+ raise ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}"
83
+ end
84
+ batch.add_prepared(statement_id, @raw_metadata, bound_args)
54
85
  end
55
86
 
56
87
  private
57
88
 
58
89
  def run(args, connection)
59
- statement_id = connection[self]
60
90
  bound_args = args.shift(@raw_metadata.size)
61
91
  unless bound_args.size == @raw_metadata.size && args.size <= 1
62
92
  raise ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}"
63
93
  end
64
- consistency, timeout, trace = @execute_options_decoder.decode_options(args.last)
94
+ options = @execute_options_decoder.decode_options(args.last)
65
95
  statement_id = connection[self]
66
- request = Protocol::ExecuteRequest.new(statement_id, @raw_metadata, bound_args, consistency, trace)
67
- @request_runner.execute(connection, request, timeout)
96
+ request_metadata = @raw_result_metadata.nil?
97
+ 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])
98
+ f = @request_runner.execute(connection, request, options[:timeout], @raw_result_metadata)
99
+ if options.include?(:page_size)
100
+ f = f.map { |result| AsynchronousPreparedPagedQueryResult.new(self, request, result, options) }
101
+ end
102
+ f
68
103
  end
69
104
  end
70
105
  end
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ module Cql
4
+ module Client
5
+ # Auth provider used for Cassandra's built in authentication.
6
+ #
7
+ # There is no need to create instances of this class to pass as `:auth_provider`
8
+ # to {Cql::Client.connect}, instead use the `:credentials` option and one
9
+ # will be created automatically for you.
10
+ class PlainTextAuthProvider
11
+ def initialize(username, password)
12
+ @username = username
13
+ @password = password
14
+ end
15
+
16
+ def create_authenticator(authentication_class)
17
+ if authentication_class == PASSWORD_AUTHENTICATOR_FQCN
18
+ PlainTextAuthenticator.new(@username, @password)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ PASSWORD_AUTHENTICATOR_FQCN = 'org.apache.cassandra.auth.PasswordAuthenticator'.freeze
25
+ end
26
+
27
+ # Authenticator used for Cassandra's built in authentication,
28
+ # see {Cql::Client::PlainTextAuthProvider}
29
+ class PlainTextAuthenticator
30
+ def initialize(username, password)
31
+ @username = username
32
+ @password = password
33
+ end
34
+
35
+ def initial_response
36
+ "\x00#{@username}\x00#{@password}"
37
+ end
38
+
39
+ def challenge_response(token)
40
+ end
41
+
42
+ def authentication_successful(token)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,115 @@
1
+ # encoding: utf-8
2
+
3
+ module Cql
4
+ module Client
5
+ # @private
6
+ class AsynchronousBatch < Batch
7
+ def initialize(type, execute_options_decoder, connection_manager, options=nil)
8
+ raise ArgumentError, "Unknown batch type: #{type}" unless BATCH_TYPES.include?(type)
9
+ @type = type
10
+ @execute_options_decoder = execute_options_decoder
11
+ @connection_manager = connection_manager
12
+ @options = options
13
+ @request_runner = RequestRunner.new
14
+ @parts = []
15
+ end
16
+
17
+ def add(*args)
18
+ @parts << args
19
+ nil
20
+ end
21
+
22
+ def execute(options=nil)
23
+ options = @execute_options_decoder.decode_options(@options, options)
24
+ connection = nil
25
+ attempts = 0
26
+ begin
27
+ connection = @connection_manager.random_connection
28
+ request = Protocol::BatchRequest.new(BATCH_TYPES[@type], options[:consistency], options[:trace])
29
+ @parts.each do |cql_or_statement, *bound_args|
30
+ if cql_or_statement.is_a?(String)
31
+ type_hints = nil
32
+ if bound_args.last.is_a?(Hash) && bound_args.last.include?(:type_hints)
33
+ bound_args = bound_args.dup
34
+ type_hints = bound_args.pop[:type_hints]
35
+ end
36
+ request.add_query(cql_or_statement, bound_args, type_hints)
37
+ else
38
+ cql_or_statement.add_to_batch(request, connection, bound_args)
39
+ end
40
+ end
41
+ rescue NotPreparedError
42
+ attempts += 1
43
+ if attempts < 3
44
+ retry
45
+ else
46
+ raise
47
+ end
48
+ end
49
+ @request_runner.execute(connection, request, options[:timeout])
50
+ end
51
+
52
+ private
53
+
54
+ BATCH_TYPES = {
55
+ :logged => Protocol::BatchRequest::LOGGED_TYPE,
56
+ :unlogged => Protocol::BatchRequest::UNLOGGED_TYPE,
57
+ :counter => Protocol::BatchRequest::COUNTER_TYPE,
58
+ }.freeze
59
+ end
60
+
61
+ # @private
62
+ class SynchronousBatch < Batch
63
+ include SynchronousBacktrace
64
+
65
+ def initialize(asynchronous_batch)
66
+ @asynchronous_batch = asynchronous_batch
67
+ end
68
+
69
+ def async
70
+ @asynchronous_batch
71
+ end
72
+
73
+ def add(*args)
74
+ @asynchronous_batch.add(*args)
75
+ end
76
+
77
+ def execute(options=nil)
78
+ synchronous_backtrace { @asynchronous_batch.execute(options).value }
79
+ end
80
+ end
81
+
82
+ # @private
83
+ class AsynchronousPreparedStatementBatch < PreparedStatementBatch
84
+ def initialize(prepared_statement, batch)
85
+ @prepared_statement = prepared_statement
86
+ @batch = batch
87
+ end
88
+
89
+ def add(*args)
90
+ @batch.add(@prepared_statement, *args)
91
+ end
92
+
93
+ def execute(options=nil)
94
+ @batch.execute(options)
95
+ end
96
+ end
97
+
98
+ # @private
99
+ class SynchronousPreparedStatementBatch < PreparedStatementBatch
100
+ include SynchronousBacktrace
101
+
102
+ def initialize(asynchronous_batch)
103
+ @asynchronous_batch = asynchronous_batch
104
+ end
105
+
106
+ def add(*args)
107
+ @asynchronous_batch.add(*args)
108
+ end
109
+
110
+ def execute(options=nil)
111
+ synchronous_backtrace { @asynchronous_batch.execute(options).value }
112
+ end
113
+ end
114
+ end
115
+ end