Class: OccamsRecord::Cursor
- Inherits:
-
Object
- Object
- OccamsRecord::Cursor
- Defined in:
- lib/occams-record/cursor.rb
Overview
An interface to database cursors. Supported databases:
* PostgreSQL
Constant Summary collapse
- SCROLL =
{ true => "SCROLL", false => "NO SCROLL", nil => "", }.freeze
- HOLD =
{ true => "WITH HOLD", false => "WITHOUT HOLD", nil => "", }.freeze
- DIRECTIONS =
{ next: "NEXT", prior: "PRIOR", first: "FIRST", last: "LAST", absolute: "ABSOLUTE", relative: "RELATIVE", forward: "FORWARD", backward: "BACKWARD", }.freeze
Instance Attribute Summary collapse
- #conn ⇒ ActiveRecord::Connection readonly
-
#name ⇒ String
readonly
Name of the cursor.
-
#quoted_name ⇒ String
readonly
Name of the cursor (safely SQL-escaped).
Instance Method Summary collapse
-
#each(batch_size: 1000) ⇒ Object
Loads records in batches of N and yields each record to a block (if given).
-
#each_batch(batch_size: 1000) ⇒ Object
Loads records in batches of N and yields each batch to a block (if given).
-
#execute(sql, binds = {}) ⇒ Object
Run an arbitrary command on the cursor.
-
#fetch(direction, num = nil) ⇒ OccamsRecord::Results::Row
Fetch records in the given direction.
-
#initialize(conn, sql, name: nil, scroll: nil, hold: nil, use: nil, query_logger: nil, eager_loaders: nil) ⇒ Cursor
constructor
Initializes a new Cursor.
-
#move(direction, num = nil) ⇒ Object
Move the cursor the given direction.
-
#open(use_transaction: true) {|self| ... } ⇒ Object
Declares and opens the cursor, runs the given block (yielding self), and closes the cursor.
-
#query(sql, binds = {}) ⇒ Object
Run an arbitrary query on the cursor.
Constructor Details
#initialize(conn, sql, name: nil, scroll: nil, hold: nil, use: nil, query_logger: nil, eager_loaders: nil) ⇒ Cursor
Initializes a new Cursor. NOTE all operations must be performed within a block passed to #open.
While you CAN manually initialize a cursor, it’s more common to get one via OccamsRecord::Query#cursor or OccamsRecord::RawQuery#cursor.
61 62 63 64 65 66 67 68 |
# File 'lib/occams-record/cursor.rb', line 61 def initialize(conn, sql, name: nil, scroll: nil, hold: nil, use: nil, query_logger: nil, eager_loaders: nil) @conn, @sql = conn, sql @scroll = SCROLL.fetch(scroll) @hold = HOLD.fetch(hold) @use, @query_logger, @eager_loaders = use, query_logger, eager_loaders @name = name || "occams_cursor_#{SecureRandom.hex 4}" @quoted_name = conn.quote_table_name(@name) end |
Instance Attribute Details
#conn ⇒ ActiveRecord::Connection (readonly)
36 37 38 |
# File 'lib/occams-record/cursor.rb', line 36 def conn @conn end |
#name ⇒ String (readonly)
Name of the cursor
40 41 42 |
# File 'lib/occams-record/cursor.rb', line 40 def name @name end |
#quoted_name ⇒ String (readonly)
Name of the cursor (safely SQL-escaped)
44 45 46 |
# File 'lib/occams-record/cursor.rb', line 44 def quoted_name @quoted_name end |
Instance Method Details
#each(batch_size: 1000) ⇒ Object
Loads records in batches of N and yields each record to a block (if given). If no block is given, returns an Enumerator.
cursor.open do |c|
c.each do |record|
...
end
end
104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/occams-record/cursor.rb', line 104 def each(batch_size: 1000) enum = Enumerator.new { |y| each_batch(batch_size: batch_size).each { |batch| batch.each { |record| y.yield record } } } if block_given? enum.each { |record| yield record } else enum end end |
#each_batch(batch_size: 1000) ⇒ Object
Loads records in batches of N and yields each batch to a block (if given). If no block is given, returns an Enumerator.
cursor.open do |c|
c.each_batch do |batch|
...
end
end
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/occams-record/cursor.rb', line 129 def each_batch(batch_size: 1000) enum = Enumerator.new { |y| out_of_records = false until out_of_records results = fetch :forward, batch_size y.yield results if results.any? out_of_records = results.size < batch_size end } if block_given? enum.each { |batch| yield batch } else enum end end |
#execute(sql, binds = {}) ⇒ Object
Run an arbitrary command on the cursor. Use ‘binds’ to escape inputs.
cursor.open do |c|
c.execute("MOVE FORWARD %{num} FOR #{c.quoted_name}", {num: 100})
...
end
205 206 207 208 209 |
# File 'lib/occams-record/cursor.rb', line 205 def execute(sql, binds = {}) conn.execute(sql % binds.each_with_object({}) { |(key, val), acc| acc[key] = conn.quote(val) }) end |
#fetch(direction, num = nil) ⇒ OccamsRecord::Results::Row
Fetch records in the given direction.
cursor.open do |c|
c.fetch :forward, 100
...
end
157 158 159 160 161 162 163 |
# File 'lib/occams-record/cursor.rb', line 157 def fetch(direction, num = nil) query "FETCH %{dir} %{num} FROM %{name}".freeze % { dir: DIRECTIONS.fetch(direction), num: num&.to_i, name: @quoted_name, } end |
#move(direction, num = nil) ⇒ Object
Move the cursor the given direction.
cursor.open do |c|
...
c.move :backward, 100
...
end
177 178 179 180 181 182 183 |
# File 'lib/occams-record/cursor.rb', line 177 def move(direction, num = nil) query "MOVE %{dir} %{num} FROM %{name}".freeze % { dir: DIRECTIONS.fetch(direction), num: num&.to_i, name: @quoted_name, } end |
#open(use_transaction: true) {|self| ... } ⇒ Object
Declares and opens the cursor, runs the given block (yielding self), and closes the cursor.
cursor.open do |c|
c.fetch :forward, 100
end
81 82 83 84 85 86 87 88 89 90 |
# File 'lib/occams-record/cursor.rb', line 81 def open(use_transaction: true) raise ArgumentError, "A block is required" unless block_given? if use_transaction and conn.open_transactions == 0 conn.transaction { perform { yield self } } else perform { yield self } end end |
#query(sql, binds = {}) ⇒ Object
Run an arbitrary query on the cursor. Use ‘binds’ to escape inputs.
cursor.open do |c|
c.query("FETCH FORWARD %{num} FOR #{c.quoted_name}", {num: 100})
...
end
193 194 195 |
# File 'lib/occams-record/cursor.rb', line 193 def query(sql, binds = {}) ::OccamsRecord::RawQuery.new(sql, binds, use: @use, query_logger: @query_logger, eager_loaders: @eager_loaders, connection: conn).run end |