activerecord 3.0.1 → 3.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +33 -0
- data/examples/performance.rb +18 -1
- data/lib/active_record.rb +3 -3
- data/lib/active_record/aggregations.rb +2 -2
- data/lib/active_record/association_preload.rb +1 -1
- data/lib/active_record/associations.rb +59 -26
- data/lib/active_record/associations/association_collection.rb +28 -18
- data/lib/active_record/associations/association_proxy.rb +4 -4
- data/lib/active_record/associations/belongs_to_association.rb +3 -3
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +10 -13
- data/lib/active_record/associations/has_many_through_association.rb +2 -3
- data/lib/active_record/associations/has_one_association.rb +6 -6
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -4
- data/lib/active_record/attribute_methods/primary_key.rb +4 -3
- data/lib/active_record/autosave_association.rb +7 -7
- data/lib/active_record/base.rb +62 -46
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +6 -8
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +5 -9
- data/lib/active_record/connection_adapters/mysql_adapter.rb +7 -7
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +2 -2
- data/lib/active_record/dynamic_finder_match.rb +20 -17
- data/lib/active_record/dynamic_scope_match.rb +6 -15
- data/lib/active_record/fixtures.rb +3 -5
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/nested_attributes.rb +9 -4
- data/lib/active_record/persistence.rb +7 -8
- data/lib/active_record/railties/databases.rake +7 -7
- data/lib/active_record/relation.rb +16 -18
- data/lib/active_record/relation/batches.rb +1 -1
- data/lib/active_record/relation/calculations.rb +37 -28
- data/lib/active_record/relation/finder_methods.rb +19 -19
- data/lib/active_record/relation/predicate_builder.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +100 -75
- data/lib/active_record/relation/spawn_methods.rb +50 -39
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/session_store.rb +4 -4
- data/lib/active_record/transactions.rb +6 -6
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +6 -1
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record.rb +2 -10
- data/lib/rails/generators/active_record/migration.rb +15 -0
- metadata +25 -11
@@ -50,7 +50,7 @@ module ActiveRecord
|
|
50
50
|
def find_in_batches(options = {})
|
51
51
|
relation = self
|
52
52
|
|
53
|
-
|
53
|
+
unless arel.orders.blank? && arel.taken.blank?
|
54
54
|
ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
|
55
55
|
end
|
56
56
|
|
@@ -14,9 +14,9 @@ module ActiveRecord
|
|
14
14
|
#
|
15
15
|
# * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
|
16
16
|
# See conditions in the intro to ActiveRecord::Base.
|
17
|
-
# * <tt>:joins</tt>: Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id"
|
17
|
+
# * <tt>:joins</tt>: Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id"
|
18
18
|
# (rarely needed) or named associations in the same form used for the <tt>:include</tt> option, which will
|
19
|
-
# perform an INNER JOIN on the associated table(s). If the value is a string, then the records
|
19
|
+
# perform an INNER JOIN on the associated table(s). If the value is a string, then the records
|
20
20
|
# will be returned read-only since they will have attributes that do not correspond to the table's columns.
|
21
21
|
# Pass <tt>:readonly => false</tt> to override.
|
22
22
|
# * <tt>:include</tt>: Named associations that should be loaded alongside using LEFT OUTER JOINs.
|
@@ -175,28 +175,39 @@ module ActiveRecord
|
|
175
175
|
end
|
176
176
|
|
177
177
|
distinct = options[:distinct] || distinct
|
178
|
-
column_name = :all if column_name.blank? && operation == "count"
|
179
178
|
|
180
179
|
if @group_values.any?
|
181
|
-
|
180
|
+
execute_grouped_calculation(operation, column_name, distinct)
|
182
181
|
else
|
183
|
-
|
182
|
+
execute_simple_calculation(operation, column_name, distinct)
|
184
183
|
end
|
185
184
|
end
|
186
185
|
|
187
|
-
def
|
188
|
-
|
189
|
-
Arel::Attribute.new(@klass.unscoped, column_name)
|
186
|
+
def aggregate_column(column_name)
|
187
|
+
if @klass.column_names.include?(column_name.to_s)
|
188
|
+
Arel::Attribute.new(@klass.unscoped.table, column_name)
|
190
189
|
else
|
191
|
-
Arel
|
190
|
+
Arel.sql(column_name == :all ? "*" : column_name.to_s)
|
192
191
|
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def operation_over_aggregate_column(column, operation, distinct)
|
195
|
+
operation == 'count' ? column.count(distinct) : column.send(operation)
|
196
|
+
end
|
197
|
+
|
198
|
+
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
|
199
|
+
column = aggregate_column(column_name)
|
193
200
|
|
194
201
|
# Postgresql doesn't like ORDER BY when there are no GROUP BY
|
195
|
-
relation = except(:order)
|
202
|
+
relation = except(:order)
|
203
|
+
select_value = operation_over_aggregate_column(column, operation, distinct)
|
204
|
+
|
205
|
+
relation.select_values = [select_value]
|
206
|
+
|
196
207
|
type_cast_calculated_value(@klass.connection.select_value(relation.to_sql), column_for(column_name), operation)
|
197
208
|
end
|
198
209
|
|
199
|
-
def execute_grouped_calculation(operation, column_name) #:nodoc:
|
210
|
+
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
200
211
|
group_attr = @group_values.first
|
201
212
|
association = @klass.reflect_on_association(group_attr.to_sym)
|
202
213
|
associated = association && association.macro == :belongs_to # only count belongs_to associations
|
@@ -206,33 +217,31 @@ module ActiveRecord
|
|
206
217
|
|
207
218
|
group = @klass.connection.adapter_name == 'FrontBase' ? group_alias : group_field
|
208
219
|
|
209
|
-
|
210
|
-
|
211
|
-
select_statement = if operation == 'count' && column_name == :all
|
212
|
-
"COUNT(*) AS count_all"
|
220
|
+
if operation == 'count' && column_name == :all
|
221
|
+
aggregate_alias = 'count_all'
|
213
222
|
else
|
214
|
-
|
223
|
+
aggregate_alias = column_alias_for(operation, column_name)
|
215
224
|
end
|
216
225
|
|
217
|
-
|
218
|
-
|
219
|
-
|
226
|
+
relation = except(:group).group(group)
|
227
|
+
relation.select_values = [
|
228
|
+
operation_over_aggregate_column(aggregate_column(column_name), operation, distinct).as(aggregate_alias),
|
229
|
+
"#{group_field} AS #{group_alias}"
|
230
|
+
]
|
220
231
|
|
221
232
|
calculated_data = @klass.connection.select_all(relation.to_sql)
|
222
233
|
|
223
234
|
if association
|
224
235
|
key_ids = calculated_data.collect { |row| row[group_alias] }
|
225
236
|
key_records = association.klass.base_class.find(key_ids)
|
226
|
-
key_records = key_records.
|
237
|
+
key_records = Hash[key_records.map { |r| [r.id, r] }]
|
227
238
|
end
|
228
239
|
|
229
|
-
|
230
|
-
key
|
231
|
-
key
|
232
|
-
|
233
|
-
|
234
|
-
all
|
235
|
-
end
|
240
|
+
ActiveSupport::OrderedHash[calculated_data.map do |row|
|
241
|
+
key = type_cast_calculated_value(row[group_alias], group_column)
|
242
|
+
key = key_records[key] if associated
|
243
|
+
[key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
|
244
|
+
end]
|
236
245
|
end
|
237
246
|
|
238
247
|
# Converts the given keys to the value that the database adapter returns as
|
@@ -268,7 +277,7 @@ module ActiveRecord
|
|
268
277
|
else type_cast_using_column(value, column)
|
269
278
|
end
|
270
279
|
else
|
271
|
-
value
|
280
|
+
type_cast_using_column(value, column)
|
272
281
|
end
|
273
282
|
end
|
274
283
|
|
@@ -21,7 +21,7 @@ module ActiveRecord
|
|
21
21
|
#
|
22
22
|
# ==== Parameters
|
23
23
|
#
|
24
|
-
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>[
|
24
|
+
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>["user_name = ?", username]</tt>,
|
25
25
|
# or <tt>["user_name = :user_name", { :user_name => user_name }]</tt>. See conditions in the intro.
|
26
26
|
# * <tt>:order</tt> - An SQL fragment like "created_at DESC, name".
|
27
27
|
# * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
|
@@ -54,7 +54,7 @@ module ActiveRecord
|
|
54
54
|
# Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
|
55
55
|
# Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
|
56
56
|
# Person.find([1]) # returns an array for the object with ID = 1
|
57
|
-
# Person.
|
57
|
+
# Person.where("administrator = 1").order("created_on DESC").find(1)
|
58
58
|
#
|
59
59
|
# Note that returned records may not be in the same order as the ids you
|
60
60
|
# provide since database rows are unordered. Give an explicit <tt>:order</tt>
|
@@ -63,23 +63,23 @@ module ActiveRecord
|
|
63
63
|
# ==== Examples
|
64
64
|
#
|
65
65
|
# # find first
|
66
|
-
# Person.
|
67
|
-
# Person.
|
68
|
-
# Person.
|
69
|
-
# Person.
|
66
|
+
# Person.first # returns the first object fetched by SELECT * FROM people
|
67
|
+
# Person.where(["user_name = ?", user_name]).first
|
68
|
+
# Person.where(["user_name = :u", { :u => user_name }]).first
|
69
|
+
# Person.order("created_on DESC").offset(5).first
|
70
70
|
#
|
71
71
|
# # find last
|
72
|
-
# Person.
|
73
|
-
# Person.
|
74
|
-
# Person.
|
72
|
+
# Person.last # returns the last object fetched by SELECT * FROM people
|
73
|
+
# Person.where(["user_name = ?", user_name]).last
|
74
|
+
# Person.order("created_on DESC").offset(5).last
|
75
75
|
#
|
76
76
|
# # find all
|
77
|
-
# Person.
|
78
|
-
# Person.
|
79
|
-
# Person.
|
80
|
-
# Person.
|
81
|
-
# Person.
|
82
|
-
# Person.
|
77
|
+
# Person.all # returns an array of objects for all the rows fetched by SELECT * FROM people
|
78
|
+
# Person.where(["category IN (?)", categories]).limit(50).all
|
79
|
+
# Person.where({ :friends => ["Bob", "Steve", "Fred"] }).all
|
80
|
+
# Person.offset(10).limit(10).all
|
81
|
+
# Person.includes([:account, :friends]).all
|
82
|
+
# Person.group("category").all
|
83
83
|
#
|
84
84
|
# Example for find with a lock: Imagine two concurrent transactions:
|
85
85
|
# each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
|
@@ -88,7 +88,7 @@ module ActiveRecord
|
|
88
88
|
# expected <tt>person.visits == 4</tt>.
|
89
89
|
#
|
90
90
|
# Person.transaction do
|
91
|
-
# person = Person.find(1
|
91
|
+
# person = Person.lock(true).find(1)
|
92
92
|
# person.visits += 1
|
93
93
|
# person.save!
|
94
94
|
# end
|
@@ -230,7 +230,7 @@ module ActiveRecord
|
|
230
230
|
end
|
231
231
|
|
232
232
|
def find_by_attributes(match, attributes, *args)
|
233
|
-
conditions = attributes.
|
233
|
+
conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
|
234
234
|
result = where(conditions).send(match.finder)
|
235
235
|
|
236
236
|
if match.bang? && result.blank?
|
@@ -291,8 +291,8 @@ module ActiveRecord
|
|
291
291
|
record = where(primary_key.eq(id)).first
|
292
292
|
|
293
293
|
unless record
|
294
|
-
conditions = arel.
|
295
|
-
conditions = " [
|
294
|
+
conditions = arel.where_sql
|
295
|
+
conditions = " [#{conditions}]" if conditions
|
296
296
|
raise RecordNotFound, "Couldn't find #{@klass.name} with ID=#{id}#{conditions}"
|
297
297
|
end
|
298
298
|
|
@@ -24,7 +24,9 @@ module ActiveRecord
|
|
24
24
|
|
25
25
|
case value
|
26
26
|
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Relation
|
27
|
-
values = value.to_a
|
27
|
+
values = value.to_a.map { |x|
|
28
|
+
x.respond_to?(:quoted_id) ? x.quoted_id : x
|
29
|
+
}
|
28
30
|
attribute.in(values)
|
29
31
|
when Range, Arel::Relation
|
30
32
|
attribute.in(value)
|
@@ -6,93 +6,133 @@ module ActiveRecord
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
attr_accessor :includes_values, :eager_load_values, :preload_values,
|
9
|
-
:select_values, :group_values, :order_values, :joins_values, :where_values, :having_values,
|
9
|
+
:select_values, :group_values, :order_values, :reorder_flag, :joins_values, :where_values, :having_values,
|
10
10
|
:limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, :from_value
|
11
11
|
|
12
12
|
def includes(*args)
|
13
|
-
args.reject! {
|
14
|
-
|
13
|
+
args.reject! {|a| a.blank? }
|
14
|
+
|
15
|
+
return clone if args.empty?
|
16
|
+
|
17
|
+
relation = clone
|
18
|
+
relation.includes_values = (relation.includes_values + args).flatten.uniq
|
19
|
+
relation
|
15
20
|
end
|
16
21
|
|
17
22
|
def eager_load(*args)
|
18
|
-
|
23
|
+
relation = clone
|
24
|
+
relation.eager_load_values += args unless args.blank?
|
25
|
+
relation
|
19
26
|
end
|
20
27
|
|
21
28
|
def preload(*args)
|
22
|
-
|
29
|
+
relation = clone
|
30
|
+
relation.preload_values += args unless args.blank?
|
31
|
+
relation
|
23
32
|
end
|
24
33
|
|
25
|
-
def select(
|
34
|
+
def select(value = Proc.new)
|
26
35
|
if block_given?
|
27
|
-
to_a.select {|*block_args|
|
36
|
+
to_a.select {|*block_args| value.call(*block_args) }
|
28
37
|
else
|
29
|
-
|
38
|
+
relation = clone
|
39
|
+
relation.select_values += Array.wrap(value)
|
40
|
+
relation
|
30
41
|
end
|
31
42
|
end
|
32
43
|
|
33
44
|
def group(*args)
|
34
|
-
|
45
|
+
relation = clone
|
46
|
+
relation.group_values += args.flatten unless args.blank?
|
47
|
+
relation
|
35
48
|
end
|
36
49
|
|
37
50
|
def order(*args)
|
38
|
-
|
51
|
+
relation = clone
|
52
|
+
relation.order_values += args.flatten unless args.blank?
|
53
|
+
relation
|
39
54
|
end
|
40
55
|
|
41
56
|
def reorder(*args)
|
42
|
-
|
57
|
+
ActiveSupport::Deprecation.warn "reorder is deprecated. Please use except(:order).order(...) instead", caller
|
58
|
+
relation = clone
|
59
|
+
unless args.blank?
|
60
|
+
relation.order_values = args
|
61
|
+
relation.reorder_flag = true
|
62
|
+
end
|
63
|
+
relation
|
43
64
|
end
|
44
65
|
|
45
66
|
def joins(*args)
|
67
|
+
relation = clone
|
68
|
+
|
46
69
|
args.flatten!
|
47
|
-
|
70
|
+
relation.joins_values += args unless args.blank?
|
71
|
+
|
72
|
+
relation
|
48
73
|
end
|
49
74
|
|
50
75
|
def where(opts, *rest)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
copy
|
76
|
+
relation = clone
|
77
|
+
relation.where_values += build_where(opts, rest) unless opts.blank?
|
78
|
+
relation
|
55
79
|
end
|
56
80
|
|
57
81
|
def having(*args)
|
58
|
-
|
59
|
-
|
82
|
+
relation = clone
|
83
|
+
relation.having_values += build_where(*args) unless args.blank?
|
84
|
+
relation
|
60
85
|
end
|
61
86
|
|
62
|
-
def limit(value
|
63
|
-
|
64
|
-
|
65
|
-
|
87
|
+
def limit(value)
|
88
|
+
relation = clone
|
89
|
+
relation.limit_value = value
|
90
|
+
relation
|
66
91
|
end
|
67
92
|
|
68
|
-
def offset(value
|
69
|
-
|
93
|
+
def offset(value)
|
94
|
+
relation = clone
|
95
|
+
relation.offset_value = value
|
96
|
+
relation
|
70
97
|
end
|
71
98
|
|
72
99
|
def lock(locks = true)
|
100
|
+
relation = clone
|
101
|
+
|
73
102
|
case locks
|
74
103
|
when String, TrueClass, NilClass
|
75
|
-
|
104
|
+
relation.lock_value = locks || true
|
76
105
|
else
|
77
|
-
|
106
|
+
relation.lock_value = false
|
78
107
|
end
|
108
|
+
|
109
|
+
relation
|
79
110
|
end
|
80
111
|
|
81
112
|
def readonly(value = true)
|
82
|
-
|
113
|
+
relation = clone
|
114
|
+
relation.readonly_value = value
|
115
|
+
relation
|
83
116
|
end
|
84
117
|
|
85
|
-
def create_with(value
|
86
|
-
|
118
|
+
def create_with(value)
|
119
|
+
relation = clone
|
120
|
+
relation.create_with_value = value
|
121
|
+
relation
|
87
122
|
end
|
88
123
|
|
89
|
-
def from(value
|
90
|
-
|
124
|
+
def from(value)
|
125
|
+
relation = clone
|
126
|
+
relation.from_value = value
|
127
|
+
relation
|
91
128
|
end
|
92
129
|
|
93
130
|
def extending(*modules, &block)
|
94
131
|
modules << Module.new(&block) if block_given?
|
95
|
-
|
132
|
+
|
133
|
+
relation = clone
|
134
|
+
relation.send(:apply_modules, modules.flatten)
|
135
|
+
relation
|
96
136
|
end
|
97
137
|
|
98
138
|
def reverse_order
|
@@ -103,7 +143,7 @@ module ActiveRecord
|
|
103
143
|
"#{@klass.table_name}.#{@klass.primary_key} DESC" :
|
104
144
|
reverse_sql_order(order_clause)
|
105
145
|
|
106
|
-
relation.order
|
146
|
+
relation.order(Arel::SqlLiteral.new(order))
|
107
147
|
end
|
108
148
|
|
109
149
|
def arel
|
@@ -111,24 +151,23 @@ module ActiveRecord
|
|
111
151
|
end
|
112
152
|
|
113
153
|
def custom_join_sql(*joins)
|
114
|
-
arel = table
|
154
|
+
arel = table.select_manager
|
155
|
+
|
115
156
|
joins.each do |join|
|
116
157
|
next if join.blank?
|
117
158
|
|
118
159
|
@implicit_readonly = true
|
119
160
|
|
120
161
|
case join
|
121
|
-
when
|
122
|
-
if array_of_strings?(join)
|
123
|
-
join_string = join.join(' ')
|
124
|
-
arel = arel.join(Arel::SqlLiteral.new(join_string))
|
125
|
-
end
|
162
|
+
when Array
|
163
|
+
join = Arel.sql(join.join(' ')) if array_of_strings?(join)
|
126
164
|
when String
|
127
|
-
|
128
|
-
else
|
129
|
-
arel = arel.join(join)
|
165
|
+
join = Arel.sql(join)
|
130
166
|
end
|
167
|
+
|
168
|
+
arel.join(join)
|
131
169
|
end
|
170
|
+
|
132
171
|
arel.joins(arel)
|
133
172
|
end
|
134
173
|
|
@@ -138,23 +177,18 @@ module ActiveRecord
|
|
138
177
|
arel = build_joins(arel, @joins_values) unless @joins_values.empty?
|
139
178
|
|
140
179
|
(@where_values - ['']).uniq.each do |where|
|
141
|
-
|
142
|
-
|
143
|
-
arel = arel.where(where)
|
144
|
-
else
|
145
|
-
sql = where.is_a?(String) ? where : where.to_sql
|
146
|
-
arel = arel.where(Arel::SqlLiteral.new("(#{sql})"))
|
147
|
-
end
|
180
|
+
where = Arel.sql(where) if String === where
|
181
|
+
arel = arel.where(Arel::Nodes::Grouping.new(where))
|
148
182
|
end
|
149
183
|
|
150
|
-
arel = arel.having(*@having_values.uniq.
|
184
|
+
arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
|
151
185
|
|
152
186
|
arel = arel.take(@limit_value) if @limit_value
|
153
187
|
arel = arel.skip(@offset_value) if @offset_value
|
154
188
|
|
155
|
-
arel = arel.group(*@group_values.uniq.
|
189
|
+
arel = arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
|
156
190
|
|
157
|
-
arel = arel.order(*@order_values.uniq.
|
191
|
+
arel = arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty?
|
158
192
|
|
159
193
|
arel = build_select(arel, @select_values.uniq)
|
160
194
|
|
@@ -164,22 +198,21 @@ module ActiveRecord
|
|
164
198
|
arel
|
165
199
|
end
|
166
200
|
|
201
|
+
private
|
202
|
+
|
167
203
|
def build_where(opts, other = [])
|
168
204
|
case opts
|
169
205
|
when String, Array
|
170
|
-
@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))
|
206
|
+
[@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
|
171
207
|
when Hash
|
172
208
|
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
|
173
209
|
PredicateBuilder.new(table.engine).build_from_hash(attributes, table)
|
174
210
|
else
|
175
|
-
opts
|
211
|
+
[opts]
|
176
212
|
end
|
177
213
|
end
|
178
214
|
|
179
|
-
private
|
180
|
-
|
181
215
|
def build_joins(relation, joins)
|
182
|
-
joined_associations = []
|
183
216
|
association_joins = []
|
184
217
|
|
185
218
|
joins = @joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq
|
@@ -203,18 +236,15 @@ module ActiveRecord
|
|
203
236
|
|
204
237
|
join_dependency.join_associations.each do |association|
|
205
238
|
if (association_relation = association.relation).is_a?(Array)
|
206
|
-
to_join << [association_relation.first, association.
|
207
|
-
to_join << [association_relation.last, association.
|
239
|
+
to_join << [association_relation.first, association.join_type, association.association_join.first]
|
240
|
+
to_join << [association_relation.last, association.join_type, association.association_join.last]
|
208
241
|
else
|
209
|
-
to_join << [association_relation, association.
|
242
|
+
to_join << [association_relation, association.join_type, association.association_join]
|
210
243
|
end
|
211
244
|
end
|
212
245
|
|
213
|
-
to_join.each do |
|
214
|
-
|
215
|
-
joined_associations << tj
|
216
|
-
relation = relation.join(tj[0], tj[1]).on(*tj[2])
|
217
|
-
end
|
246
|
+
to_join.uniq.each do |left, join_type, right|
|
247
|
+
relation = relation.join(left, join_type).on(*right)
|
218
248
|
end
|
219
249
|
|
220
250
|
relation.join(custom_joins)
|
@@ -223,22 +253,17 @@ module ActiveRecord
|
|
223
253
|
def build_select(arel, selects)
|
224
254
|
unless selects.empty?
|
225
255
|
@implicit_readonly = false
|
226
|
-
|
227
|
-
# Before this change we were passing to ARel the last element only, and ARel is capable of handling an array
|
228
|
-
if selects.all? {|s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/)
|
229
|
-
arel.project(*selects)
|
230
|
-
else
|
231
|
-
arel.project(selects.last)
|
232
|
-
end
|
256
|
+
arel.project(*selects)
|
233
257
|
else
|
234
258
|
arel.project(Arel::SqlLiteral.new(@klass.quoted_table_name + '.*'))
|
235
259
|
end
|
236
260
|
end
|
237
261
|
|
238
262
|
def apply_modules(modules)
|
239
|
-
|
240
|
-
|
241
|
-
|
263
|
+
unless modules.empty?
|
264
|
+
@extensions += modules
|
265
|
+
modules.each {|extension| extend(extension) }
|
266
|
+
end
|
242
267
|
end
|
243
268
|
|
244
269
|
def reverse_sql_order(order_query)
|