1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
# frozen_string_literal: true
module ActiveRecord
###
# = Active Record \Result
#
# This class encapsulates a result returned from calling
# {#exec_query}[rdoc-ref:ConnectionAdapters::DatabaseStatements#exec_query]
# on any database connection adapter. For example:
#
# result = ActiveRecord::Base.lease_connection.exec_query('SELECT id, title, body FROM posts')
# result # => #<ActiveRecord::Result:0xdeadbeef>
#
# # Get the column names of the result:
# result.columns
# # => ["id", "title", "body"]
#
# # Get the record values of the result:
# result.rows
# # => [[1, "title_1", "body_1"],
# [2, "title_2", "body_2"],
# ...
# ]
#
# # Get an array of hashes representing the result (column => value):
# result.to_a
# # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
# {"id" => 2, "title" => "title_2", "body" => "body_2"},
# ...
# ]
#
# # ActiveRecord::Result also includes Enumerable.
# result.each do |row|
# puts row['title'] + " " + row['body']
# end
class Result
include Enumerable
attr_reader :columns, :rows, :column_types
def self.empty(async: false) # :nodoc:
if async
EMPTY_ASYNC
else
EMPTY
end
end
def initialize(columns, rows, column_types = nil)
# We freeze the strings to prevent them getting duped when
# used as keys in ActiveRecord::Base's @attributes hash
@columns = columns.each(&:-@).freeze
@rows = rows
@hash_rows = nil
@column_types = column_types || EMPTY_HASH
@column_indexes = nil
end
# Returns true if this result set includes the column named +name+
def includes_column?(name)
@columns.include? name
end
# Returns the number of elements in the rows array.
def length
@rows.length
end
# Calls the given block once for each element in row collection, passing
# row as parameter.
#
# Returns an +Enumerator+ if no block is given.
def each(&block)
if block_given?
hash_rows.each(&block)
else
hash_rows.to_enum { @rows.size }
end
end
# Returns true if there are no records, otherwise false.
def empty?
rows.empty?
end
# Returns an array of hashes representing each row record.
def to_ary
hash_rows
end
alias :to_a :to_ary
def [](idx)
hash_rows[idx]
end
# Returns the last record from the rows collection.
def last(n = nil)
n ? hash_rows.last(n) : hash_rows.last
end
def result # :nodoc:
self
end
def cancel # :nodoc:
self
end
def cast_values(type_overrides = {}) # :nodoc:
if columns.one?
# Separated to avoid allocating an array per row
type = if type_overrides.is_a?(Array)
type_overrides.first
else
column_type(columns.first, 0, type_overrides)
end
rows.map do |(value)|
type.deserialize(value)
end
else
types = if type_overrides.is_a?(Array)
type_overrides
else
columns.map.with_index { |name, i| column_type(name, i, type_overrides) }
end
rows.map do |values|
Array.new(values.size) { |i| types[i].deserialize(values[i]) }
end
end
end
def initialize_copy(other)
@columns = columns
@rows = rows.dup
@column_types = column_types.dup
@hash_rows = nil
end
def freeze # :nodoc:
hash_rows.freeze
super
end
def column_indexes # :nodoc:
@column_indexes ||= begin
index = 0
hash = {}
length = columns.length
while index < length
hash[columns[index]] = index
index += 1
end
hash
end
end
private
def column_type(name, index, type_overrides)
type_overrides.fetch(name) do
column_types.fetch(index) do
column_types.fetch(name, Type.default_value)
end
end
end
def hash_rows
# We use transform_values to rows.
# This is faster because we avoid any reallocs and avoid hashing entirely.
@hash_rows ||= @rows.map do |row|
column_indexes.transform_values { |index| row[index] }
end
end
empty_array = [].freeze
EMPTY_HASH = {}.freeze
private_constant :EMPTY_HASH
EMPTY = new(empty_array, empty_array, EMPTY_HASH).freeze
private_constant :EMPTY
EMPTY_ASYNC = FutureResult.wrap(EMPTY).freeze
private_constant :EMPTY_ASYNC
end
end
|