class OccamsRecord::RawQuery
Represents a raw SQL query to be run and eager associations to be loaded. Use OccamsRecord.sql
to create your queries instead of instantiating objects directly.
Attributes
@return [Hash]
@return [String]
Public Class Methods
Initialize a new query.
@param sql [String] The SELECT statement to run. Binds should use Ruby's named string substitution. @param binds [Hash] Bind values (Symbol keys) @param use [Array<Module>] optional Module to include in the result class (single or array) @param eager_loaders [OccamsRecord::EagerLoaders::Context] @param query_logger [Array] (optional) an array into which all queries will be inserted for logging/debug purposes @param measurements [Array]
# File lib/occams-record/raw_query.rb, line 79 def initialize(sql, binds, use: nil, eager_loaders: nil, query_logger: nil, measurements: nil) @sql = sql @binds = binds @use = use @eager_loaders = eager_loaders || EagerLoaders::Context.new @query_logger, @measurements = query_logger, measurements end
Public Instance Methods
If you pass a block, each result row will be yielded to it. If you don't, an Enumerable will be returned.
@yield [OccansR::Results::Row] @return [Enumerable]
# File lib/occams-record/raw_query.rb, line 134 def each if block_given? to_a.each { |row| yield row } else to_a.each end end
Specify the model to be used to load eager associations. Normally this would be the main table you're SELECTing from.
NOTE Some database adapters, notably SQLite's, require that the model always be specified, even if you aren't doing eager loading.
@param klass [ActiveRecord::Base] @return [OccamsRecord::RawQuery] self
# File lib/occams-record/raw_query.rb, line 97 def model(klass) @eager_loaders.model = klass self end
Run the query and return the results.
@return [Array<OccamsRecord::Results::Row>]
# File lib/occams-record/raw_query.rb, line 107 def run _escaped_sql = escaped_sql @query_logger << _escaped_sql if @query_logger result = if measure? record_start_time! measure!(table_name, _escaped_sql) { conn.exec_query _escaped_sql } else conn.exec_query _escaped_sql end row_class = OccamsRecord::Results.klass(result.columns, result.column_types, @eager_loaders.names, model: @eager_loaders.model, modules: @use) rows = result.rows.map { |row| row_class.new row } @eager_loaders.run!(rows, query_logger: @query_logger, measurements: @measurements) yield_measurements! rows end
Private Instance Methods
Returns an Enumerator that yields batches of records, of size “of”. The SQL string must include 'LIMIT %{batch_limit} OFFSET %{batch_offset}'. The bind values will be provided by OccamsRecord
.
@param of [Integer] batch size @param use_transaction [Boolean] Ensure it runs inside of a database transaction @return [Enumerator] yields batches
# File lib/occams-record/raw_query.rb, line 170 def batches(of:, use_transaction: true, append_order_by: nil) unless @sql =~ /LIMIT\s+%\{batch_limit\}/i and @sql =~ /OFFSET\s+%\{batch_offset\}/i raise ArgumentError, "When using find_each/find_in_batches you must specify 'LIMIT %{batch_limit} OFFSET %{batch_offset}'. SQL statement: #{@sql}" end Enumerator.new do |y| if use_transaction and conn.open_transactions == 0 conn.transaction { run_batches y, of } else run_batches y, of end end end
# File lib/occams-record/raw_query.rb, line 200 def conn @conn ||= @eager_loaders.model&.connection || ActiveRecord::Base.connection end
Returns the SQL as a String with all variables escaped
# File lib/occams-record/raw_query.rb, line 145 def escaped_sql return sql if binds.empty? sql % binds.reduce({}) { |a, (col, val)| a[col.to_sym] = if val.is_a? Array val.map { |x| conn.quote x }.join(', ') else conn.quote val end a } end
# File lib/occams-record/raw_query.rb, line 186 def run_batches(y, of) offset = 0 loop do results = RawQuery.new(@sql, @binds.merge({ batch_limit: of, batch_offset: offset, }), use: @use, query_logger: @query_logger, eager_loaders: @eager_loaders).run y.yield results if results.any? break if results.size < of offset += results.size end end
# File lib/occams-record/raw_query.rb, line 157 def table_name @sql.match(/\s+FROM\s+"?(\w+)"?/i)&.captures&.first end