Class: OccamsRecord::RawQuery

Inherits:
Object
  • Object
show all
Includes:
Enumerable, Batches::CursorHelpers, EagerLoaders::Builder, Measureable, Pluck
Defined in:
lib/occams-record/raw_query.rb

Overview

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.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Measureable

#measure

Methods included from EagerLoaders::Builder

#eager_load, #eager_load_many, #eager_load_one, #nest

Methods included from Batches::CursorHelpers

#find_each_with_cursor, #find_in_batches_with_cursor

Constructor Details

#initialize(sql, binds, use: nil, eager_loaders: nil, query_logger: nil, measurements: nil, connection: nil) ⇒ RawQuery

Initialize a new query.

Parameters:

  • sql (String)

    The SELECT statement to run. Binds may be Rails-style (?, :foo) or Ruby-style (%s, %foo).

  • binds (Hash)

    Bind values as Hash (with Symbol keys) or an Array

  • use (Array<Module>) (defaults to: nil)

    optional Module to include in the result class (single or array)

  • eager_loaders (OccamsRecord::EagerLoaders::Context) (defaults to: nil)
  • query_logger (Array) (defaults to: nil)

    (optional) an array into which all queries will be inserted for logging/debug purposes

  • measurements (Array) (defaults to: nil)
  • connection (defaults to: nil)


81
82
83
84
85
86
87
88
# File 'lib/occams-record/raw_query.rb', line 81

def initialize(sql, binds, use: nil, eager_loaders: nil, query_logger: nil, measurements: nil, connection: nil)
  @sql = BindsConverter.convert(sql, binds)
  @binds = binds
  @use = use
  @eager_loaders = eager_loaders || EagerLoaders::Context.new
  @query_logger, @measurements = query_logger, measurements
  @conn = connection
end

Instance Attribute Details

#bindsHash|Array (readonly)

Returns:

  • (Hash|Array)


62
63
64
# File 'lib/occams-record/raw_query.rb', line 62

def binds
  @binds
end

#sqlString (readonly)

Returns:

  • (String)


60
61
62
# File 'lib/occams-record/raw_query.rb', line 60

def sql
  @sql
end

Instance Method Details

#cursor(name: nil, scroll: nil, hold: nil) ⇒ OccamsRecord::Cursor

Returns a cursor you can open and perform operations on. A lower-level alternative to find_each_with_cursor and find_in_batches_with_cursor.

NOTE Postgres only. See the docs for OccamsRecord::Cursor for more details.

Parameters:

  • name (String) (defaults to: nil)

    Specify a name for the cursor (defaults to a random name)

  • scroll (Boolean) (defaults to: nil)

    true = SCROLL, false = NO SCROLL, nil = default behavior of DB

  • hold (Boolean) (defaults to: nil)

    true = WITH HOLD, false = WITHOUT HOLD, nil = default behavior of DB

Returns:



207
208
209
210
211
212
# File 'lib/occams-record/raw_query.rb', line 207

def cursor(name: nil, scroll: nil, hold: nil)
  Cursor.new(conn, @sql,
    name: name, scroll: scroll, hold: hold,
    use: @use, query_logger: @query_logger, eager_loaders: @eager_loaders,
  )
end

#each {|OccansR::Results::Row| ... } ⇒ Enumerable

If you pass a block, each result row will be yielded to it. If you don’t, an Enumerable will be returned.

Yields:

  • (OccansR::Results::Row)

Returns:

  • (Enumerable)


138
139
140
141
142
143
144
# File 'lib/occams-record/raw_query.rb', line 138

def each
  if block_given?
    to_a.each { |row| yield row }
  else
    to_a.each
  end
end

#find_each(batch_size: 1000, use_transaction: true) {|OccamsRecord::Results::Row| ... } ⇒ Enumerator

Load records in batches of N and yield each record to a block if given. If no block is given, returns an Enumerator.

NOTE Unlike ActiveRecord’s find_each, ORDER BY is respected. The primary key will be appended to the ORDER BY clause to help ensure consistent batches. Additionally, it will be run inside of a transaction.

Parameters:

  • batch_size (Integer) (defaults to: 1000)
  • use_transaction (Boolean) (defaults to: true)

    Ensure it runs inside of a database transaction

Yields:

Returns:

  • (Enumerator)

    will yield each record



159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/occams-record/raw_query.rb', line 159

def find_each(batch_size: 1000, use_transaction: true)
  enum = Enumerator.new { |y|
    find_in_batches(batch_size: batch_size, use_transaction: use_transaction).each { |batch|
      batch.each { |record| y.yield record }
    }
  }
  if block_given?
    enum.each { |record| yield record }
  else
    enum
  end
end

#find_in_batches(batch_size: 1000, use_transaction: true) {|OccamsRecord::Results::Row| ... } ⇒ Enumerator

Load records in batches of N and yield each batch to a block if given. If no block is given, returns an Enumerator.

NOTE Unlike ActiveRecord’s find_each, ORDER BY is respected. The primary key will be appended to the ORDER BY clause to help ensure consistent batches. Additionally, it will be run inside of a transaction.

Parameters:

  • batch_size (Integer) (defaults to: 1000)
  • use_transaction (Boolean) (defaults to: true)

    Ensure it runs inside of a database transaction

Yields:

Returns:

  • (Enumerator)

    will yield each batch



185
186
187
188
189
190
191
192
193
194
# File 'lib/occams-record/raw_query.rb', line 185

def find_in_batches(batch_size: 1000, use_transaction: true)
  enum = Batches::OffsetLimit::RawQuery
    .new(conn, @sql, @binds, use: @use, query_logger: @query_logger, eager_loaders: @eager_loaders)
    .enum(batch_size: batch_size, use_transaction: use_transaction)
  if block_given?
    enum.each { |batch| yield batch }
  else
    enum
  end
end

#model(klass) ⇒ OccamsRecord::RawQuery

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.

Parameters:

  • klass (ActiveRecord::Base)

Returns:



100
101
102
103
# File 'lib/occams-record/raw_query.rb', line 100

def model(klass)
  @eager_loaders.model = klass
  self
end

#pluck(*args) ⇒ Array

Returns the column(s) you’ve SELECT as an array of values.

If you’re selecting multiple columns, you’ll get back an array of arrays. Otherwise you’ll get an array of the single column’s values.

Parameters:

  • *args

    DEPRECATED

Returns:

  • (Array)


223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/occams-record/raw_query.rb', line 223

def pluck(*args)
  $stderr.puts "OccamsRecord: passing arguments to OccamsRecord.sql(\"...\").pluck is deprecated and will be removed in a future version. Called from #{caller[0]}" if args.any?

  _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
  pluck_results(result, model: @eager_loaders.model)
end

#runArray<OccamsRecord::Results::Row> Also known as: to_a

Run the query and return the results.

Returns:



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/occams-record/raw_query.rb', line 110

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, tracer: @eager_loaders.tracer)
  rows = result.rows.map { |row| row_class.new row }
  @eager_loaders.run!(rows, query_logger: @query_logger, measurements: @measurements)
  yield_measurements!
  rows
end