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.
- checksums.yaml +4 -4
 - data/.yardopts +4 -0
 - data/README.md +139 -17
 - data/lib/cql/client.rb +237 -8
 - data/lib/cql/client/asynchronous_client.rb +138 -54
 - data/lib/cql/client/asynchronous_prepared_statement.rb +41 -6
 - data/lib/cql/client/authenticators.rb +46 -0
 - data/lib/cql/client/batch.rb +115 -0
 - data/lib/cql/client/connector.rb +255 -0
 - data/lib/cql/client/execute_options_decoder.rb +25 -9
 - data/lib/cql/client/keyspace_changer.rb +5 -5
 - data/lib/cql/client/peer_discovery.rb +33 -0
 - data/lib/cql/client/query_result.rb +124 -1
 - data/lib/cql/client/request_runner.rb +4 -2
 - data/lib/cql/client/synchronous_client.rb +14 -2
 - data/lib/cql/client/synchronous_prepared_statement.rb +19 -1
 - data/lib/cql/future.rb +97 -50
 - data/lib/cql/io/connection.rb +0 -1
 - data/lib/cql/io/io_reactor.rb +1 -1
 - data/lib/cql/protocol.rb +8 -1
 - data/lib/cql/protocol/cql_protocol_handler.rb +2 -2
 - data/lib/cql/protocol/decoding.rb +10 -15
 - data/lib/cql/protocol/frame_decoder.rb +2 -1
 - data/lib/cql/protocol/frame_encoder.rb +5 -4
 - data/lib/cql/protocol/requests/auth_response_request.rb +31 -0
 - data/lib/cql/protocol/requests/batch_request.rb +59 -0
 - data/lib/cql/protocol/requests/credentials_request.rb +1 -1
 - data/lib/cql/protocol/requests/execute_request.rb +45 -17
 - data/lib/cql/protocol/requests/options_request.rb +1 -1
 - data/lib/cql/protocol/requests/prepare_request.rb +1 -1
 - data/lib/cql/protocol/requests/query_request.rb +97 -5
 - data/lib/cql/protocol/requests/register_request.rb +1 -1
 - data/lib/cql/protocol/requests/startup_request.rb +4 -4
 - data/lib/cql/protocol/response.rb +2 -2
 - data/lib/cql/protocol/responses/auth_challenge_response.rb +25 -0
 - data/lib/cql/protocol/responses/auth_success_response.rb +25 -0
 - data/lib/cql/protocol/responses/authenticate_response.rb +1 -1
 - data/lib/cql/protocol/responses/detailed_error_response.rb +1 -1
 - data/lib/cql/protocol/responses/error_response.rb +3 -2
 - data/lib/cql/protocol/responses/event_response.rb +3 -2
 - data/lib/cql/protocol/responses/prepared_result_response.rb +10 -6
 - data/lib/cql/protocol/responses/raw_rows_result_response.rb +27 -0
 - data/lib/cql/protocol/responses/ready_response.rb +1 -1
 - data/lib/cql/protocol/responses/result_response.rb +2 -2
 - data/lib/cql/protocol/responses/rows_result_response.rb +43 -23
 - data/lib/cql/protocol/responses/schema_change_event_response.rb +1 -1
 - data/lib/cql/protocol/responses/schema_change_result_response.rb +1 -1
 - data/lib/cql/protocol/responses/set_keyspace_result_response.rb +1 -1
 - data/lib/cql/protocol/responses/status_change_event_response.rb +1 -1
 - data/lib/cql/protocol/responses/supported_response.rb +1 -1
 - data/lib/cql/protocol/responses/void_result_response.rb +1 -1
 - data/lib/cql/protocol/type_converter.rb +2 -2
 - data/lib/cql/uuid.rb +2 -2
 - data/lib/cql/version.rb +1 -1
 - data/spec/cql/client/asynchronous_client_spec.rb +493 -50
 - data/spec/cql/client/asynchronous_prepared_statement_spec.rb +193 -11
 - data/spec/cql/client/authenticators_spec.rb +56 -0
 - data/spec/cql/client/batch_spec.rb +277 -0
 - data/spec/cql/client/connector_spec.rb +606 -0
 - data/spec/cql/client/execute_options_decoder_spec.rb +95 -0
 - data/spec/cql/client/keyspace_changer_spec.rb +8 -8
 - data/spec/cql/client/peer_discovery_spec.rb +92 -0
 - data/spec/cql/client/query_result_spec.rb +352 -0
 - data/spec/cql/client/request_runner_spec.rb +31 -5
 - data/spec/cql/client/synchronous_client_spec.rb +44 -1
 - data/spec/cql/client/synchronous_prepared_statement_spec.rb +63 -1
 - data/spec/cql/future_spec.rb +50 -2
 - data/spec/cql/protocol/cql_protocol_handler_spec.rb +16 -5
 - data/spec/cql/protocol/decoding_spec.rb +16 -6
 - data/spec/cql/protocol/encoding_spec.rb +3 -1
 - data/spec/cql/protocol/frame_encoder_spec.rb +99 -50
 - data/spec/cql/protocol/requests/auth_response_request_spec.rb +62 -0
 - data/spec/cql/protocol/requests/batch_request_spec.rb +155 -0
 - data/spec/cql/protocol/requests/credentials_request_spec.rb +1 -1
 - data/spec/cql/protocol/requests/execute_request_spec.rb +184 -71
 - data/spec/cql/protocol/requests/options_request_spec.rb +1 -1
 - data/spec/cql/protocol/requests/prepare_request_spec.rb +1 -1
 - data/spec/cql/protocol/requests/query_request_spec.rb +255 -32
 - data/spec/cql/protocol/requests/register_request_spec.rb +1 -1
 - data/spec/cql/protocol/requests/startup_request_spec.rb +12 -6
 - data/spec/cql/protocol/responses/auth_challenge_response_spec.rb +31 -0
 - data/spec/cql/protocol/responses/auth_success_response_spec.rb +31 -0
 - data/spec/cql/protocol/responses/authenticate_response_spec.rb +2 -1
 - data/spec/cql/protocol/responses/detailed_error_response_spec.rb +14 -7
 - data/spec/cql/protocol/responses/error_response_spec.rb +4 -2
 - data/spec/cql/protocol/responses/event_response_spec.rb +7 -4
 - data/spec/cql/protocol/responses/prepared_result_response_spec.rb +89 -34
 - data/spec/cql/protocol/responses/raw_rows_result_response_spec.rb +66 -0
 - data/spec/cql/protocol/responses/ready_response_spec.rb +1 -1
 - data/spec/cql/protocol/responses/result_response_spec.rb +19 -7
 - data/spec/cql/protocol/responses/rows_result_response_spec.rb +56 -11
 - data/spec/cql/protocol/responses/schema_change_event_response_spec.rb +2 -1
 - data/spec/cql/protocol/responses/schema_change_result_response_spec.rb +2 -1
 - data/spec/cql/protocol/responses/set_keyspace_result_response_spec.rb +1 -1
 - data/spec/cql/protocol/responses/status_change_event_response_spec.rb +2 -1
 - data/spec/cql/protocol/responses/supported_response_spec.rb +2 -1
 - data/spec/cql/protocol/responses/topology_change_event_response_spec.rb +2 -1
 - data/spec/cql/protocol/responses/void_result_response_spec.rb +1 -1
 - data/spec/cql/protocol/type_converter_spec.rb +21 -4
 - data/spec/cql/uuid_spec.rb +10 -3
 - data/spec/integration/client_spec.rb +251 -28
 - data/spec/integration/protocol_spec.rb +213 -62
 - data/spec/integration/regression_spec.rb +4 -1
 - data/spec/integration/uuid_spec.rb +4 -1
 - data/spec/support/fake_io_reactor.rb +5 -5
 - metadata +36 -7
 - data/lib/cql/client/connection_helper.rb +0 -181
 - data/spec/cql/client/connection_helper_spec.rb +0 -429
 
| 
         @@ -0,0 +1,255 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # encoding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Cql
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 5 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 6 
     | 
    
         
            +
                class ClusterConnector
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(sequence, logger)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @sequence = sequence
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @logger = logger
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def connect_all(hosts, connections_per_node)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    connections = hosts.flat_map do |host|
         
     | 
| 
      
 14 
     | 
    
         
            +
                      Array.new(connections_per_node) do
         
     | 
| 
      
 15 
     | 
    
         
            +
                        f = @sequence.connect(host)
         
     | 
| 
      
 16 
     | 
    
         
            +
                        f.on_value { |connection| register_logging(connection) }
         
     | 
| 
      
 17 
     | 
    
         
            +
                        f.recover do |error|
         
     | 
| 
      
 18 
     | 
    
         
            +
                          @logger.warn('Failed connecting to node at %s: %s' % [host, error.message])
         
     | 
| 
      
 19 
     | 
    
         
            +
                          FailedConnection.new(error, host)
         
     | 
| 
      
 20 
     | 
    
         
            +
                        end
         
     | 
| 
      
 21 
     | 
    
         
            +
                      end
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
                    Future.all(*connections).map do |connections|
         
     | 
| 
      
 24 
     | 
    
         
            +
                      connected_connections = connections.select(&:connected?)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      if connected_connections.empty?
         
     | 
| 
      
 26 
     | 
    
         
            +
                        e = connections.first.error
         
     | 
| 
      
 27 
     | 
    
         
            +
                        if e.is_a?(Cql::QueryError) && e.code == 0x100
         
     | 
| 
      
 28 
     | 
    
         
            +
                          e = AuthenticationError.new(e.message)
         
     | 
| 
      
 29 
     | 
    
         
            +
                        end
         
     | 
| 
      
 30 
     | 
    
         
            +
                        raise e
         
     | 
| 
      
 31 
     | 
    
         
            +
                      end
         
     | 
| 
      
 32 
     | 
    
         
            +
                      connected_connections
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  private
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def register_logging(connection)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    args = [connection[:host_id], connection.host, connection.port, connection[:data_center]]
         
     | 
| 
      
 40 
     | 
    
         
            +
                    @logger.info('Connected to node %s at %s:%d in data center %s' % args)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    connection.on_closed do |cause|
         
     | 
| 
      
 42 
     | 
    
         
            +
                      message = 'Connection to node %s at %s:%d in data center %s closed' % args
         
     | 
| 
      
 43 
     | 
    
         
            +
                      if cause
         
     | 
| 
      
 44 
     | 
    
         
            +
                        message << (' unexpectedly: %s' % cause.message)
         
     | 
| 
      
 45 
     | 
    
         
            +
                        @logger.warn(message)
         
     | 
| 
      
 46 
     | 
    
         
            +
                      else
         
     | 
| 
      
 47 
     | 
    
         
            +
                        @logger.info(message)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      end
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 54 
     | 
    
         
            +
                class Connector
         
     | 
| 
      
 55 
     | 
    
         
            +
                  def initialize(steps)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @steps = steps.dup
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  def connect(host)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    pending_connection = PendingConnection.new(host)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    seed = Future.resolved(pending_connection)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    f = @steps.reduce(seed) do |chain, step|
         
     | 
| 
      
 63 
     | 
    
         
            +
                      chain.flat_map do |pending_connection|
         
     | 
| 
      
 64 
     | 
    
         
            +
                        step.run(pending_connection)
         
     | 
| 
      
 65 
     | 
    
         
            +
                      end
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
                    f.map do |pending_connection|
         
     | 
| 
      
 68 
     | 
    
         
            +
                      pending_connection.connection
         
     | 
| 
      
 69 
     | 
    
         
            +
                    end
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 74 
     | 
    
         
            +
                class ConnectStep
         
     | 
| 
      
 75 
     | 
    
         
            +
                  def initialize(io_reactor, port, connection_timeout, logger)
         
     | 
| 
      
 76 
     | 
    
         
            +
                    @io_reactor = io_reactor
         
     | 
| 
      
 77 
     | 
    
         
            +
                    @port = port
         
     | 
| 
      
 78 
     | 
    
         
            +
                    @connection_timeout = connection_timeout
         
     | 
| 
      
 79 
     | 
    
         
            +
                    @logger = logger
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  def run(pending_connection)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    @logger.debug('Connecting to node at %s:%d' % [pending_connection.host, @port])
         
     | 
| 
      
 84 
     | 
    
         
            +
                    @io_reactor.connect(pending_connection.host, @port, @connection_timeout).map do |connection|
         
     | 
| 
      
 85 
     | 
    
         
            +
                      pending_connection.with_connection(connection)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    end
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 91 
     | 
    
         
            +
                class CacheOptionsStep
         
     | 
| 
      
 92 
     | 
    
         
            +
                  def run(pending_connection)
         
     | 
| 
      
 93 
     | 
    
         
            +
                    f = pending_connection.execute(Protocol::OptionsRequest.new)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    f.on_value do |supported_options|
         
     | 
| 
      
 95 
     | 
    
         
            +
                      pending_connection[:cql_version] = supported_options['CQL_VERSION']
         
     | 
| 
      
 96 
     | 
    
         
            +
                      pending_connection[:compression] = supported_options['COMPRESSION']
         
     | 
| 
      
 97 
     | 
    
         
            +
                    end
         
     | 
| 
      
 98 
     | 
    
         
            +
                    f.map(pending_connection)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  end
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 103 
     | 
    
         
            +
                class InitializeStep
         
     | 
| 
      
 104 
     | 
    
         
            +
                  def initialize(cql_version, compressor, logger)
         
     | 
| 
      
 105 
     | 
    
         
            +
                    @cql_version = cql_version
         
     | 
| 
      
 106 
     | 
    
         
            +
                    @compressor = compressor
         
     | 
| 
      
 107 
     | 
    
         
            +
                    @logger = logger
         
     | 
| 
      
 108 
     | 
    
         
            +
                  end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                  def run(pending_connection)
         
     | 
| 
      
 111 
     | 
    
         
            +
                    compression = @compressor && @compressor.algorithm
         
     | 
| 
      
 112 
     | 
    
         
            +
                    supported_algorithms = pending_connection[:compression]
         
     | 
| 
      
 113 
     | 
    
         
            +
                    if @compressor && !supported_algorithms.include?(@compressor.algorithm)
         
     | 
| 
      
 114 
     | 
    
         
            +
                      @logger.warn(%[Compression algorithm "#{@compressor.algorithm}" not supported (server supports "#{supported_algorithms.join('", "')}")])
         
     | 
| 
      
 115 
     | 
    
         
            +
                      compression = nil
         
     | 
| 
      
 116 
     | 
    
         
            +
                    elsif @compressor
         
     | 
| 
      
 117 
     | 
    
         
            +
                      @logger.debug('Using "%s" compression' % @compressor.algorithm)
         
     | 
| 
      
 118 
     | 
    
         
            +
                    end
         
     | 
| 
      
 119 
     | 
    
         
            +
                    f = pending_connection.execute(Protocol::StartupRequest.new(@cql_version, compression))
         
     | 
| 
      
 120 
     | 
    
         
            +
                    f.map do |startup_response|
         
     | 
| 
      
 121 
     | 
    
         
            +
                      if startup_response.is_a?(AuthenticationRequired)
         
     | 
| 
      
 122 
     | 
    
         
            +
                        pending_connection.with_authentication_class(startup_response.authentication_class)
         
     | 
| 
      
 123 
     | 
    
         
            +
                      else
         
     | 
| 
      
 124 
     | 
    
         
            +
                        pending_connection
         
     | 
| 
      
 125 
     | 
    
         
            +
                      end
         
     | 
| 
      
 126 
     | 
    
         
            +
                    end
         
     | 
| 
      
 127 
     | 
    
         
            +
                  end
         
     | 
| 
      
 128 
     | 
    
         
            +
                end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 131 
     | 
    
         
            +
                class SaslAuthenticationStep
         
     | 
| 
      
 132 
     | 
    
         
            +
                  def initialize(auth_provider)
         
     | 
| 
      
 133 
     | 
    
         
            +
                    @auth_provider = auth_provider
         
     | 
| 
      
 134 
     | 
    
         
            +
                  end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                  def run(pending_connection)
         
     | 
| 
      
 137 
     | 
    
         
            +
                    if pending_connection.authentication_class
         
     | 
| 
      
 138 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 139 
     | 
    
         
            +
                        authenticator = @auth_provider && @auth_provider.create_authenticator(pending_connection.authentication_class)
         
     | 
| 
      
 140 
     | 
    
         
            +
                        if authenticator
         
     | 
| 
      
 141 
     | 
    
         
            +
                          token = authenticator.initial_response
         
     | 
| 
      
 142 
     | 
    
         
            +
                          challenge_cycle(pending_connection, authenticator, token)
         
     | 
| 
      
 143 
     | 
    
         
            +
                        elsif @auth_provider
         
     | 
| 
      
 144 
     | 
    
         
            +
                          Future.failed(AuthenticationError.new('Auth provider does not support the required authentication class "%s" and/or protocol version %d' % [pending_connection.authentication_class, @protocol_version]))
         
     | 
| 
      
 145 
     | 
    
         
            +
                        else
         
     | 
| 
      
 146 
     | 
    
         
            +
                          Future.failed(AuthenticationError.new('Server requested authentication, but no auth provider found'))
         
     | 
| 
      
 147 
     | 
    
         
            +
                        end
         
     | 
| 
      
 148 
     | 
    
         
            +
                      rescue => e
         
     | 
| 
      
 149 
     | 
    
         
            +
                        Future.failed(AuthenticationError.new('Auth provider raised an error: %s' % e.message))
         
     | 
| 
      
 150 
     | 
    
         
            +
                      end
         
     | 
| 
      
 151 
     | 
    
         
            +
                    else
         
     | 
| 
      
 152 
     | 
    
         
            +
                      Future.resolved(pending_connection)
         
     | 
| 
      
 153 
     | 
    
         
            +
                    end
         
     | 
| 
      
 154 
     | 
    
         
            +
                  end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                  def challenge_cycle(pending_connection, authenticator, response_token)
         
     | 
| 
      
 157 
     | 
    
         
            +
                    request = Protocol::AuthResponseRequest.new(response_token)
         
     | 
| 
      
 158 
     | 
    
         
            +
                    f = pending_connection.execute(request) { |raw_response| raw_response }
         
     | 
| 
      
 159 
     | 
    
         
            +
                    f.flat_map do |response|
         
     | 
| 
      
 160 
     | 
    
         
            +
                      case response
         
     | 
| 
      
 161 
     | 
    
         
            +
                      when Protocol::AuthChallengeResponse
         
     | 
| 
      
 162 
     | 
    
         
            +
                        token = authenticator.challenge_response(response.token)
         
     | 
| 
      
 163 
     | 
    
         
            +
                        challenge_cycle(pending_connection, authenticator, token)
         
     | 
| 
      
 164 
     | 
    
         
            +
                      when Protocol::AuthSuccessResponse
         
     | 
| 
      
 165 
     | 
    
         
            +
                        authenticator.authentication_successful(response.token)
         
     | 
| 
      
 166 
     | 
    
         
            +
                        Future.resolved(pending_connection)
         
     | 
| 
      
 167 
     | 
    
         
            +
                      else
         
     | 
| 
      
 168 
     | 
    
         
            +
                        Future.resolved(pending_connection)
         
     | 
| 
      
 169 
     | 
    
         
            +
                      end
         
     | 
| 
      
 170 
     | 
    
         
            +
                    end
         
     | 
| 
      
 171 
     | 
    
         
            +
                  end
         
     | 
| 
      
 172 
     | 
    
         
            +
                end
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 175 
     | 
    
         
            +
                class CredentialsAuthenticationStep
         
     | 
| 
      
 176 
     | 
    
         
            +
                  def initialize(credentials)
         
     | 
| 
      
 177 
     | 
    
         
            +
                    @credentials = credentials
         
     | 
| 
      
 178 
     | 
    
         
            +
                  end
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
                  def run(pending_connection)
         
     | 
| 
      
 181 
     | 
    
         
            +
                    if pending_connection.authentication_class
         
     | 
| 
      
 182 
     | 
    
         
            +
                      if @credentials
         
     | 
| 
      
 183 
     | 
    
         
            +
                        request = Protocol::CredentialsRequest.new(@credentials)
         
     | 
| 
      
 184 
     | 
    
         
            +
                        pending_connection.execute(request).map(pending_connection)
         
     | 
| 
      
 185 
     | 
    
         
            +
                      else
         
     | 
| 
      
 186 
     | 
    
         
            +
                        Future.failed(AuthenticationError.new('Server requested authentication, but no credentials provided'))
         
     | 
| 
      
 187 
     | 
    
         
            +
                      end
         
     | 
| 
      
 188 
     | 
    
         
            +
                    else
         
     | 
| 
      
 189 
     | 
    
         
            +
                      Future.resolved(pending_connection)
         
     | 
| 
      
 190 
     | 
    
         
            +
                    end
         
     | 
| 
      
 191 
     | 
    
         
            +
                  end
         
     | 
| 
      
 192 
     | 
    
         
            +
                end
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 195 
     | 
    
         
            +
                class CachePropertiesStep
         
     | 
| 
      
 196 
     | 
    
         
            +
                  def run(pending_connection)
         
     | 
| 
      
 197 
     | 
    
         
            +
                    request = Protocol::QueryRequest.new('SELECT data_center, host_id FROM system.local', nil, nil, :one)
         
     | 
| 
      
 198 
     | 
    
         
            +
                    f = pending_connection.execute(request)
         
     | 
| 
      
 199 
     | 
    
         
            +
                    f.on_value do |result|
         
     | 
| 
      
 200 
     | 
    
         
            +
                      unless result.empty?
         
     | 
| 
      
 201 
     | 
    
         
            +
                        pending_connection[:host_id] = result.first['host_id']
         
     | 
| 
      
 202 
     | 
    
         
            +
                        pending_connection[:data_center] = result.first['data_center']
         
     | 
| 
      
 203 
     | 
    
         
            +
                      end
         
     | 
| 
      
 204 
     | 
    
         
            +
                    end
         
     | 
| 
      
 205 
     | 
    
         
            +
                    f.map(pending_connection)
         
     | 
| 
      
 206 
     | 
    
         
            +
                  end
         
     | 
| 
      
 207 
     | 
    
         
            +
                end
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 210 
     | 
    
         
            +
                class PendingConnection
         
     | 
| 
      
 211 
     | 
    
         
            +
                  attr_reader :host, :connection, :authentication_class
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                  def initialize(host, connection=nil, authentication_class=nil)
         
     | 
| 
      
 214 
     | 
    
         
            +
                    @host = host
         
     | 
| 
      
 215 
     | 
    
         
            +
                    @connection = connection
         
     | 
| 
      
 216 
     | 
    
         
            +
                    @authentication_class = authentication_class
         
     | 
| 
      
 217 
     | 
    
         
            +
                    @request_runner = RequestRunner.new
         
     | 
| 
      
 218 
     | 
    
         
            +
                  end
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                  def with_connection(connection)
         
     | 
| 
      
 221 
     | 
    
         
            +
                    self.class.new(host, connection, @authentication_class)
         
     | 
| 
      
 222 
     | 
    
         
            +
                  end
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
                  def with_authentication_class(authentication_class)
         
     | 
| 
      
 225 
     | 
    
         
            +
                    self.class.new(host, @connection, authentication_class)
         
     | 
| 
      
 226 
     | 
    
         
            +
                  end
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
                  def [](key)
         
     | 
| 
      
 229 
     | 
    
         
            +
                    @connection[key]
         
     | 
| 
      
 230 
     | 
    
         
            +
                  end
         
     | 
| 
      
 231 
     | 
    
         
            +
             
     | 
| 
      
 232 
     | 
    
         
            +
                  def []=(key, value)
         
     | 
| 
      
 233 
     | 
    
         
            +
                    @connection[key] = value
         
     | 
| 
      
 234 
     | 
    
         
            +
                  end
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                  def execute(request, &block)
         
     | 
| 
      
 237 
     | 
    
         
            +
                    @request_runner.execute(@connection, request, nil, nil, &block)
         
     | 
| 
      
 238 
     | 
    
         
            +
                  end
         
     | 
| 
      
 239 
     | 
    
         
            +
                end
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 242 
     | 
    
         
            +
                class FailedConnection
         
     | 
| 
      
 243 
     | 
    
         
            +
                  attr_reader :error, :host
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
      
 245 
     | 
    
         
            +
                  def initialize(error, host)
         
     | 
| 
      
 246 
     | 
    
         
            +
                    @error = error
         
     | 
| 
      
 247 
     | 
    
         
            +
                    @host = host
         
     | 
| 
      
 248 
     | 
    
         
            +
                  end
         
     | 
| 
      
 249 
     | 
    
         
            +
             
     | 
| 
      
 250 
     | 
    
         
            +
                  def connected?
         
     | 
| 
      
 251 
     | 
    
         
            +
                    false
         
     | 
| 
      
 252 
     | 
    
         
            +
                  end
         
     | 
| 
      
 253 
     | 
    
         
            +
                end
         
     | 
| 
      
 254 
     | 
    
         
            +
              end
         
     | 
| 
      
 255 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -6,21 +6,37 @@ module Cql 
     | 
|
| 
       6 
6 
     | 
    
         
             
                class ExecuteOptionsDecoder
         
     | 
| 
       7 
7 
     | 
    
         
             
                  def initialize(default_consistency)
         
     | 
| 
       8 
8 
     | 
    
         
             
                    @default_consistency = default_consistency
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @default_options = {:consistency => @default_consistency}.freeze
         
     | 
| 
       9 
10 
     | 
    
         
             
                  end
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
                  def decode_options( 
     | 
| 
       12 
     | 
    
         
            -
                     
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
                     
     | 
| 
      
 12 
     | 
    
         
            +
                  def decode_options(*args)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    if args.empty?
         
     | 
| 
      
 14 
     | 
    
         
            +
                      @default_options
         
     | 
| 
      
 15 
     | 
    
         
            +
                    elsif args.size == 1
         
     | 
| 
      
 16 
     | 
    
         
            +
                      decode_one(args.first)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    else
         
     | 
| 
      
 18 
     | 
    
         
            +
                      args.each_with_object({}) do |options_or_consistency, result|
         
     | 
| 
      
 19 
     | 
    
         
            +
                        result.merge!(decode_one(options_or_consistency))
         
     | 
| 
      
 20 
     | 
    
         
            +
                      end
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  private
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  def decode_one(options_or_consistency)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    return @default_options unless options_or_consistency
         
     | 
| 
       15 
28 
     | 
    
         
             
                    case options_or_consistency
         
     | 
| 
       16 
29 
     | 
    
         
             
                    when Symbol
         
     | 
| 
       17 
     | 
    
         
            -
                      consistency  
     | 
| 
      
 30 
     | 
    
         
            +
                      {:consistency => options_or_consistency}
         
     | 
| 
       18 
31 
     | 
    
         
             
                    when Hash
         
     | 
| 
       19 
     | 
    
         
            -
                       
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
                       
     | 
| 
      
 32 
     | 
    
         
            +
                      if options_or_consistency.include?(:consistency)
         
     | 
| 
      
 33 
     | 
    
         
            +
                        options_or_consistency
         
     | 
| 
      
 34 
     | 
    
         
            +
                      else
         
     | 
| 
      
 35 
     | 
    
         
            +
                        options = options_or_consistency.dup
         
     | 
| 
      
 36 
     | 
    
         
            +
                        options[:consistency] = @default_consistency
         
     | 
| 
      
 37 
     | 
    
         
            +
                        options
         
     | 
| 
      
 38 
     | 
    
         
            +
                      end
         
     | 
| 
       22 
39 
     | 
    
         
             
                    end
         
     | 
| 
       23 
     | 
    
         
            -
                    return consistency, timeout, trace
         
     | 
| 
       24 
40 
     | 
    
         
             
                  end
         
     | 
| 
       25 
41 
     | 
    
         
             
                end
         
     | 
| 
       26 
42 
     | 
    
         
             
              end
         
     | 
| 
         @@ -6,15 +6,15 @@ module Cql 
     | 
|
| 
       6 
6 
     | 
    
         
             
                class KeyspaceChanger
         
     | 
| 
       7 
7 
     | 
    
         
             
                  KEYSPACE_NAME_PATTERN = /^\w[\w\d_]*$|^"\w[\w\d_]*"$/
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                  def initialize
         
     | 
| 
       10 
     | 
    
         
            -
                    @request_runner =  
     | 
| 
      
 9 
     | 
    
         
            +
                  def initialize(request_runner=RequestRunner.new)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @request_runner = request_runner
         
     | 
| 
       11 
11 
     | 
    
         
             
                  end
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                  def use_keyspace( 
     | 
| 
      
 13 
     | 
    
         
            +
                  def use_keyspace(connection, keyspace)
         
     | 
| 
       14 
14 
     | 
    
         
             
                    return Future.resolved(connection) unless keyspace
         
     | 
| 
       15 
15 
     | 
    
         
             
                    return Future.failed(InvalidKeyspaceNameError.new(%("#{keyspace}" is not a valid keyspace name))) unless valid_keyspace_name?(keyspace)
         
     | 
| 
       16 
     | 
    
         
            -
                    request = Protocol::QueryRequest.new("USE #{keyspace}", :one)
         
     | 
| 
       17 
     | 
    
         
            -
                    @request_runner.execute(connection, request).map 
     | 
| 
      
 16 
     | 
    
         
            +
                    request = Protocol::QueryRequest.new("USE #{keyspace}", nil, nil, :one)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @request_runner.execute(connection, request).map(connection)
         
     | 
| 
       18 
18 
     | 
    
         
             
                  end
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
                  private
         
     | 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # encoding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Cql
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 5 
     | 
    
         
            +
                class PeerDiscovery
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(seed_connections)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @seed_connections = seed_connections
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @connection = seed_connections.sample
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @request_runner = RequestRunner.new
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def new_hosts
         
     | 
| 
      
 13 
     | 
    
         
            +
                    request = Protocol::QueryRequest.new('SELECT peer, data_center, host_id, rpc_address FROM system.peers', nil, nil, :one)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    response = @request_runner.execute(@connection, request)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    response.map do |result|
         
     | 
| 
      
 16 
     | 
    
         
            +
                      result.each_with_object([]) do |row, new_peers|
         
     | 
| 
      
 17 
     | 
    
         
            +
                        if include?(row['host_id'], row['data_center'])
         
     | 
| 
      
 18 
     | 
    
         
            +
                          rpc_address = row['rpc_address'].to_s
         
     | 
| 
      
 19 
     | 
    
         
            +
                          rpc_address = row['peer'].to_s if rpc_address == '0.0.0.0'
         
     | 
| 
      
 20 
     | 
    
         
            +
                          new_peers << rpc_address
         
     | 
| 
      
 21 
     | 
    
         
            +
                        end
         
     | 
| 
      
 22 
     | 
    
         
            +
                      end
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  private
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def include?(host_id, dc)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @seed_connections.any? { |c| c[:data_center] == dc } && @seed_connections.none? { |c| c[:host_id] == host_id }
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -14,10 +14,14 @@ module Cql 
     | 
|
| 
       14 
14 
     | 
    
         
             
                  attr_reader :trace_id
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
                  # @private
         
     | 
| 
       17 
     | 
    
         
            -
                   
     | 
| 
      
 17 
     | 
    
         
            +
                  attr_reader :paging_state
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  # @private
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def initialize(metadata, rows, trace_id, paging_state)
         
     | 
| 
       18 
21 
     | 
    
         
             
                    @metadata = ResultMetadata.new(metadata)
         
     | 
| 
       19 
22 
     | 
    
         
             
                    @rows = rows
         
     | 
| 
       20 
23 
     | 
    
         
             
                    @trace_id = trace_id
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @paging_state = paging_state
         
     | 
| 
       21 
25 
     | 
    
         
             
                  end
         
     | 
| 
       22 
26 
     | 
    
         | 
| 
       23 
27 
     | 
    
         
             
                  # Returns whether or not there are any rows in this result set
         
     | 
| 
         @@ -34,5 +38,124 @@ module Cql 
     | 
|
| 
       34 
38 
     | 
    
         
             
                  end
         
     | 
| 
       35 
39 
     | 
    
         
             
                  alias_method :each_row, :each
         
     | 
| 
       36 
40 
     | 
    
         
             
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                class PagedQueryResult < QueryResult
         
     | 
| 
      
 43 
     | 
    
         
            +
                  def metadata
         
     | 
| 
      
 44 
     | 
    
         
            +
                    @result.metadata
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  def trace_id
         
     | 
| 
      
 48 
     | 
    
         
            +
                    @result.trace_id
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def paging_state
         
     | 
| 
      
 52 
     | 
    
         
            +
                    @result.paging_state
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def empty?
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @result.empty?
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  def each(&block)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    @result.each(&block)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  alias_method :each_row, :each
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  def last_page?
         
     | 
| 
      
 65 
     | 
    
         
            +
                    @last_page
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  def next_page
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 73 
     | 
    
         
            +
                class AsynchronousPagedQueryResult < PagedQueryResult
         
     | 
| 
      
 74 
     | 
    
         
            +
                  def initialize(request, result, options)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    @request = request
         
     | 
| 
      
 76 
     | 
    
         
            +
                    @result = result
         
     | 
| 
      
 77 
     | 
    
         
            +
                    @result = result
         
     | 
| 
      
 78 
     | 
    
         
            +
                    @options = options.merge(paging_state: result.paging_state)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    @last_page = !result.paging_state
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 84 
     | 
    
         
            +
                class AsynchronousQueryPagedQueryResult < AsynchronousPagedQueryResult
         
     | 
| 
      
 85 
     | 
    
         
            +
                  def initialize(client, request, result, options)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    super(request, result, options)
         
     | 
| 
      
 87 
     | 
    
         
            +
                    @client = client
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  def next_page
         
     | 
| 
      
 91 
     | 
    
         
            +
                    @client.execute(@request.cql, *@request.values, @options)
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 96 
     | 
    
         
            +
                class AsynchronousPreparedPagedQueryResult < AsynchronousPagedQueryResult
         
     | 
| 
      
 97 
     | 
    
         
            +
                  def initialize(prepared_statement, request, result, options)
         
     | 
| 
      
 98 
     | 
    
         
            +
                    super(request, result, options)
         
     | 
| 
      
 99 
     | 
    
         
            +
                    @prepared_statement = prepared_statement
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                  def next_page
         
     | 
| 
      
 103 
     | 
    
         
            +
                    @prepared_statement.execute(*@request.values, @options)
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 108 
     | 
    
         
            +
                class SynchronousPagedQueryResult < PagedQueryResult
         
     | 
| 
      
 109 
     | 
    
         
            +
                  include SynchronousBacktrace
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                  def initialize(asynchronous_result)
         
     | 
| 
      
 112 
     | 
    
         
            +
                    @result = asynchronous_result
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                  def async
         
     | 
| 
      
 116 
     | 
    
         
            +
                    @result
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                  def last_page?
         
     | 
| 
      
 120 
     | 
    
         
            +
                    @result.last_page?
         
     | 
| 
      
 121 
     | 
    
         
            +
                  end
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                  def next_page
         
     | 
| 
      
 124 
     | 
    
         
            +
                    synchronous_backtrace { self.class.new(@result.next_page.value) }
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
                end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 129 
     | 
    
         
            +
                class LazyQueryResult < QueryResult
         
     | 
| 
      
 130 
     | 
    
         
            +
                  def initialize(metadata, lazy_rows, trace_id, paging_state)
         
     | 
| 
      
 131 
     | 
    
         
            +
                    super(metadata, nil, trace_id, paging_state)
         
     | 
| 
      
 132 
     | 
    
         
            +
                    @raw_metadata = metadata
         
     | 
| 
      
 133 
     | 
    
         
            +
                    @lazy_rows = lazy_rows
         
     | 
| 
      
 134 
     | 
    
         
            +
                    @lock = Mutex.new
         
     | 
| 
      
 135 
     | 
    
         
            +
                  end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                  def empty?
         
     | 
| 
      
 138 
     | 
    
         
            +
                    ensure_materialized
         
     | 
| 
      
 139 
     | 
    
         
            +
                    super
         
     | 
| 
      
 140 
     | 
    
         
            +
                  end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                  def each(&block)
         
     | 
| 
      
 143 
     | 
    
         
            +
                    ensure_materialized
         
     | 
| 
      
 144 
     | 
    
         
            +
                    super
         
     | 
| 
      
 145 
     | 
    
         
            +
                  end
         
     | 
| 
      
 146 
     | 
    
         
            +
                  alias_method :each_row, :each
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                  private
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                  def ensure_materialized
         
     | 
| 
      
 151 
     | 
    
         
            +
                    unless @rows
         
     | 
| 
      
 152 
     | 
    
         
            +
                      @lock.synchronize do
         
     | 
| 
      
 153 
     | 
    
         
            +
                        unless @rows
         
     | 
| 
      
 154 
     | 
    
         
            +
                          @rows = @lazy_rows.materialize(@raw_metadata)
         
     | 
| 
      
 155 
     | 
    
         
            +
                        end
         
     | 
| 
      
 156 
     | 
    
         
            +
                      end
         
     | 
| 
      
 157 
     | 
    
         
            +
                    end
         
     | 
| 
      
 158 
     | 
    
         
            +
                  end
         
     | 
| 
      
 159 
     | 
    
         
            +
                end
         
     | 
| 
       37 
160 
     | 
    
         
             
              end
         
     | 
| 
       38 
161 
     | 
    
         
             
            end
         
     |