@@ -47,10 +47,13 @@ def self.empty(async: false) # :nodoc:
47
47
end
48
48
49
49
def initialize ( columns , rows , column_types = nil )
50
- @columns = columns
50
+ # We freeze the strings to prevent them getting duped when
51
+ # used as keys in ActiveRecord::Base's @attributes hash
52
+ @columns = columns . each ( &:-@ ) . freeze
51
53
@rows = rows
52
54
@hash_rows = nil
53
55
@column_types = column_types || EMPTY_HASH
56
+ @column_indexes = nil
54
57
end
55
58
56
59
# Returns true if this result set includes the column named +name+
@@ -131,7 +134,7 @@ def cast_values(type_overrides = {}) # :nodoc:
131
134
end
132
135
133
136
def initialize_copy ( other )
134
- @columns = columns . dup
137
+ @columns = columns
135
138
@rows = rows . dup
136
139
@column_types = column_types . dup
137
140
@hash_rows = nil
@@ -142,6 +145,19 @@ def freeze # :nodoc:
142
145
super
143
146
end
144
147
148
+ def column_indexes # :nodoc:
149
+ @column_indexes ||= begin
150
+ index = 0
151
+ hash = { }
152
+ length = columns . length
153
+ while index < length
154
+ hash [ columns [ index ] ] = index
155
+ index += 1
156
+ end
157
+ hash
158
+ end
159
+ end
160
+
145
161
private
146
162
def column_type ( name , index , type_overrides )
147
163
type_overrides . fetch ( name ) do
@@ -152,44 +168,11 @@ def column_type(name, index, type_overrides)
152
168
end
153
169
154
170
def hash_rows
155
- @hash_rows ||=
156
- begin
157
- # We freeze the strings to prevent them getting duped when
158
- # used as keys in ActiveRecord::Base's @attributes hash
159
- columns = @columns . map ( &:-@ )
160
- length = columns . length
161
- template = nil
162
-
163
- @rows . map { |row |
164
- if template
165
- # We use transform_values to build subsequent rows from the
166
- # hash of the first row. This is faster because we avoid any
167
- # reallocs and in Ruby 2.7+ avoid hashing entirely.
168
- index = -1
169
- template . transform_values do
170
- row [ index += 1 ]
171
- end
172
- else
173
- # In the past we used Hash[columns.zip(row)]
174
- # though elegant, the verbose way is much more efficient
175
- # both time and memory wise cause it avoids a big array allocation
176
- # this method is called a lot and needs to be micro optimised
177
- hash = { }
178
-
179
- index = 0
180
- while index < length
181
- hash [ columns [ index ] ] = row [ index ]
182
- index += 1
183
- end
184
-
185
- # It's possible to select the same column twice, in which case
186
- # we can't use a template
187
- template = hash if hash . length == length
188
-
189
- hash
190
- end
191
- }
192
- end
171
+ # We use transform_values to rows.
172
+ # This is faster because we avoid any reallocs and avoid hashing entirely.
173
+ @hash_rows ||= @rows . map do |row |
174
+ column_indexes . transform_values { |index | row [ index ] }
175
+ end
193
176
end
194
177
195
178
empty_array = [ ] . freeze
0 commit comments