From 4317c04bfb845ddac3d187b3225d99f253c7a541 Mon Sep 17 00:00:00 2001 From: Sten Larsson Date: Wed, 15 Jul 2015 13:55:49 +0200 Subject: [PATCH] Prepare statement again when it is lost If an ALTER TABLE is performed the prepared statements are lost. In this case the statement should be prepared again. --- lib/cql/client/prepared_statement.rb | 15 +++++++++++---- spec/cql/client/prepared_statement_spec.rb | 19 +++++++++++++++++++ spec/integration/client_spec.rb | 7 +++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/lib/cql/client/prepared_statement.rb b/lib/cql/client/prepared_statement.rb index 32d4066..097463e 100644 --- a/lib/cql/client/prepared_statement.rb +++ b/lib/cql/client/prepared_statement.rb @@ -150,7 +150,13 @@ def self.prepare(cql, execute_options_decoder, connection_manager, logger) def execute(*args) connection = @connection_manager.random_connection if connection[self] - run(args, connection) + f = run(args, connection) + f.fallback do |e| + raise e unless e.is_a?(QueryError) && e.code == QueryError::UNPREPARED + prepare(connection).flat_map do + run(args, connection) + end + end else prepare(connection).flat_map do run(args, connection) @@ -216,11 +222,12 @@ def add_to_batch(batch, connection, bound_args) private def run(args, connection) - bound_args = args.shift(@raw_metadata.size) - unless bound_args.size == @raw_metadata.size && args.size <= 1 + bound_args = args.take(@raw_metadata.size) + remaining_args = args.drop(@raw_metadata.size) + unless bound_args.size == @raw_metadata.size && remaining_args.size <= 1 raise ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}" end - options = @execute_options_decoder.decode_options(args.last) + options = @execute_options_decoder.decode_options(remaining_args.last) statement_id = connection[self] request_metadata = @raw_result_metadata.nil? 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]) diff --git a/spec/cql/client/prepared_statement_spec.rb b/spec/cql/client/prepared_statement_spec.rb index 7cb20c5..25a82c1 100644 --- a/spec/cql/client/prepared_statement_spec.rb +++ b/spec/cql/client/prepared_statement_spec.rb @@ -295,6 +295,25 @@ def handle_request(connection, request, timeout) statement.execute(11, 'hello', trace: true).value tracing.should be_true end + + it 'prepares the statement again when it is lost' do + prepare_requests = 0 + connections.each do |c| + c[:num_prepare] = 0 + c.handle_request do |r, t| + if r.is_a?(Protocol::ExecuteRequest) && c[:num_prepare] == 1 + Protocol::ErrorResponse.new(0x2500, 'Unprepared') + else + if r == Protocol::PrepareRequest.new(cql) + c[:num_prepare] += 1 + end + handle_request(c, r, t) + end + end + end + statement.execute(11, 'hello').value + connections.map { |c| c[:num_prepare] }.should include(2) + end end describe '#batch' do diff --git a/spec/integration/client_spec.rb b/spec/integration/client_spec.rb index 0b39f86..4819e95 100644 --- a/spec/integration/client_spec.rb +++ b/spec/integration/client_spec.rb @@ -112,6 +112,13 @@ def create_keyspace_and_table counters = result.each_with_object({}) { |row, acc| acc[row['id']] = row['count'] } counters.should eql('foo' => 11, 'bar' => 3) end + + it 'handles altered tables' do + result1 = statement.execute('sue') + client.execute('ALTER TABLE users ADD password VARCHAR') + result2 = statement.execute('sue') + result2.to_a.should eql(result1.to_a) + end end context 'with multiple connections' do