activerecord 6.0.0.beta2 → 6.0.2.rc1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +471 -9
- data/README.rdoc +3 -1
- data/lib/active_record.rb +0 -1
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +6 -2
- data/lib/active_record/associations/collection_proxy.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency.rb +14 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +12 -3
- data/lib/active_record/associations/preloader.rb +13 -8
- data/lib/active_record/associations/preloader/association.rb +34 -30
- data/lib/active_record/associations/preloader/through_association.rb +48 -28
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +21 -7
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +109 -11
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +88 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +79 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -4
- data/lib/active_record/connection_adapters/abstract_adapter.rb +114 -34
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +78 -73
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +10 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +39 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +68 -27
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -114
- data/lib/active_record/connection_handling.rb +31 -13
- data/lib/active_record/core.rb +23 -24
- data/lib/active_record/database_configurations.rb +73 -44
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +12 -12
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +1 -1
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +3 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -6
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +10 -0
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +206 -13
- data/lib/active_record/querying.rb +17 -12
- data/lib/active_record/railtie.rb +0 -1
- data/lib/active_record/railties/databases.rake +127 -25
- data/lib/active_record/reflection.rb +3 -3
- data/lib/active_record/relation.rb +99 -20
- data/lib/active_record/relation/calculations.rb +38 -40
- data/lib/active_record/relation/delegation.rb +22 -30
- data/lib/active_record/relation/finder_methods.rb +17 -12
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_methods.rb +228 -76
- data/lib/active_record/relation/where_clause.rb +9 -5
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +1 -0
- data/lib/active_record/timestamp.rb +26 -16
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -46
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +3 -5
- data/lib/arel.rb +12 -5
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +16 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -129,11 +129,12 @@ module ActiveRecord
|
|
129
129
|
relation = apply_join_dependency
|
130
130
|
|
131
131
|
if operation.to_s.downcase == "count"
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
relation.order_values = []
|
132
|
+
unless distinct_value || distinct_select?(column_name || select_for_count)
|
133
|
+
relation.distinct!
|
134
|
+
relation.select_values = [ klass.primary_key || table[Arel.star] ]
|
136
135
|
end
|
136
|
+
# PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
|
137
|
+
relation.order_values = []
|
137
138
|
end
|
138
139
|
|
139
140
|
relation.calculate(operation, column_name)
|
@@ -221,7 +222,6 @@ module ActiveRecord
|
|
221
222
|
end
|
222
223
|
|
223
224
|
private
|
224
|
-
|
225
225
|
def has_include?(column_name)
|
226
226
|
eager_loading? || (includes_values.present? && column_name && column_name != :all)
|
227
227
|
end
|
@@ -236,10 +236,12 @@ module ActiveRecord
|
|
236
236
|
if operation == "count"
|
237
237
|
column_name ||= select_for_count
|
238
238
|
if column_name == :all
|
239
|
-
if distinct
|
239
|
+
if !distinct
|
240
|
+
distinct = distinct_select?(select_for_count) if group_values.empty?
|
241
|
+
elsif group_values.any? || select_values.empty? && order_values.empty?
|
240
242
|
column_name = primary_key
|
241
243
|
end
|
242
|
-
elsif
|
244
|
+
elsif distinct_select?(column_name)
|
243
245
|
distinct = nil
|
244
246
|
end
|
245
247
|
end
|
@@ -251,13 +253,15 @@ module ActiveRecord
|
|
251
253
|
end
|
252
254
|
end
|
253
255
|
|
256
|
+
def distinct_select?(column_name)
|
257
|
+
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
|
258
|
+
end
|
259
|
+
|
254
260
|
def aggregate_column(column_name)
|
255
261
|
return column_name if Arel::Expressions === column_name
|
256
262
|
|
257
|
-
|
258
|
-
|
259
|
-
else
|
260
|
-
Arel.sql(column_name == :all ? "*" : column_name.to_s)
|
263
|
+
arel_column(column_name.to_s) do |name|
|
264
|
+
Arel.sql(column_name == :all ? "*" : name)
|
261
265
|
end
|
262
266
|
end
|
263
267
|
|
@@ -302,25 +306,22 @@ module ActiveRecord
|
|
302
306
|
end
|
303
307
|
|
304
308
|
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
305
|
-
|
309
|
+
group_fields = group_values
|
306
310
|
|
307
|
-
if
|
308
|
-
association =
|
309
|
-
associated =
|
310
|
-
group_fields = Array(
|
311
|
-
else
|
312
|
-
group_fields = group_attrs
|
311
|
+
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
|
312
|
+
association = klass._reflect_on_association(group_fields.first)
|
313
|
+
associated = association && association.belongs_to? # only count belongs_to associations
|
314
|
+
group_fields = Array(association.foreign_key) if associated
|
313
315
|
end
|
314
316
|
group_fields = arel_columns(group_fields)
|
315
317
|
|
316
|
-
group_aliases = group_fields.map { |field|
|
318
|
+
group_aliases = group_fields.map { |field|
|
319
|
+
field = connection.visitor.compile(field) if Arel.arel_node?(field)
|
320
|
+
column_alias_for(field.to_s.downcase)
|
321
|
+
}
|
317
322
|
group_columns = group_aliases.zip(group_fields)
|
318
323
|
|
319
|
-
|
320
|
-
aggregate_alias = "count_all"
|
321
|
-
else
|
322
|
-
aggregate_alias = column_alias_for([operation, column_name].join(" "))
|
323
|
-
end
|
324
|
+
aggregate_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
|
324
325
|
|
325
326
|
select_values = [
|
326
327
|
operation_over_aggregate_column(
|
@@ -365,25 +366,21 @@ module ActiveRecord
|
|
365
366
|
end]
|
366
367
|
end
|
367
368
|
|
368
|
-
# Converts the given
|
369
|
+
# Converts the given field to the value that the database adapter returns as
|
369
370
|
# a usable column name:
|
370
371
|
#
|
371
372
|
# column_alias_for("users.id") # => "users_id"
|
372
373
|
# column_alias_for("sum(id)") # => "sum_id"
|
373
374
|
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
374
375
|
# column_alias_for("count(*)") # => "count_all"
|
375
|
-
def column_alias_for(
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
table_name.strip!
|
384
|
-
table_name.gsub!(/ +/, "_")
|
385
|
-
|
386
|
-
@klass.connection.table_alias_for(table_name)
|
376
|
+
def column_alias_for(field)
|
377
|
+
column_alias = +field
|
378
|
+
column_alias.gsub!(/\*/, "all")
|
379
|
+
column_alias.gsub!(/\W+/, " ")
|
380
|
+
column_alias.strip!
|
381
|
+
column_alias.gsub!(/ +/, "_")
|
382
|
+
|
383
|
+
connection.table_alias_for(column_alias)
|
387
384
|
end
|
388
385
|
|
389
386
|
def type_for(field, &block)
|
@@ -411,16 +408,17 @@ module ActiveRecord
|
|
411
408
|
|
412
409
|
def build_count_subquery(relation, column_name, distinct)
|
413
410
|
if column_name == :all
|
411
|
+
column_alias = Arel.star
|
414
412
|
relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
|
415
413
|
else
|
416
414
|
column_alias = Arel.sql("count_column")
|
417
415
|
relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
|
418
416
|
end
|
419
417
|
|
420
|
-
|
421
|
-
select_value = operation_over_aggregate_column(column_alias
|
418
|
+
subquery_alias = Arel.sql("subquery_for_count")
|
419
|
+
select_value = operation_over_aggregate_column(column_alias, "count", false)
|
422
420
|
|
423
|
-
|
421
|
+
relation.build_subquery(subquery_alias, select_value)
|
424
422
|
end
|
425
423
|
end
|
426
424
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "mutex_m"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Delegation # :nodoc:
|
5
7
|
module DelegateCache # :nodoc:
|
@@ -31,6 +33,10 @@ module ActiveRecord
|
|
31
33
|
super
|
32
34
|
end
|
33
35
|
|
36
|
+
def generate_relation_method(method)
|
37
|
+
generated_relation_methods.generate_method(method)
|
38
|
+
end
|
39
|
+
|
34
40
|
protected
|
35
41
|
def include_relation_methods(delegate)
|
36
42
|
superclass.include_relation_methods(delegate) unless base_class?
|
@@ -39,27 +45,35 @@ module ActiveRecord
|
|
39
45
|
|
40
46
|
private
|
41
47
|
def generated_relation_methods
|
42
|
-
@generated_relation_methods ||=
|
43
|
-
|
44
|
-
|
45
|
-
private_constant mod_name
|
48
|
+
@generated_relation_methods ||= GeneratedRelationMethods.new.tap do |mod|
|
49
|
+
const_set(:GeneratedRelationMethods, mod)
|
50
|
+
private_constant :GeneratedRelationMethods
|
46
51
|
end
|
47
52
|
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class GeneratedRelationMethods < Module # :nodoc:
|
56
|
+
include Mutex_m
|
57
|
+
|
58
|
+
def generate_method(method)
|
59
|
+
synchronize do
|
60
|
+
return if method_defined?(method)
|
48
61
|
|
49
|
-
def generate_relation_method(method)
|
50
62
|
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
|
51
|
-
|
63
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
52
64
|
def #{method}(*args, &block)
|
53
65
|
scoping { klass.#{method}(*args, &block) }
|
54
66
|
end
|
55
67
|
RUBY
|
56
68
|
else
|
57
|
-
|
69
|
+
define_method(method) do |*args, &block|
|
58
70
|
scoping { klass.public_send(method, *args, &block) }
|
59
71
|
end
|
60
72
|
end
|
61
73
|
end
|
74
|
+
end
|
62
75
|
end
|
76
|
+
private_constant :GeneratedRelationMethods
|
63
77
|
|
64
78
|
extend ActiveSupport::Concern
|
65
79
|
|
@@ -78,39 +92,17 @@ module ActiveRecord
|
|
78
92
|
module ClassSpecificRelation # :nodoc:
|
79
93
|
extend ActiveSupport::Concern
|
80
94
|
|
81
|
-
included do
|
82
|
-
@delegation_mutex = Mutex.new
|
83
|
-
end
|
84
|
-
|
85
95
|
module ClassMethods # :nodoc:
|
86
96
|
def name
|
87
97
|
superclass.name
|
88
98
|
end
|
89
|
-
|
90
|
-
def delegate_to_scoped_klass(method)
|
91
|
-
@delegation_mutex.synchronize do
|
92
|
-
return if method_defined?(method)
|
93
|
-
|
94
|
-
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
|
95
|
-
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
96
|
-
def #{method}(*args, &block)
|
97
|
-
scoping { @klass.#{method}(*args, &block) }
|
98
|
-
end
|
99
|
-
RUBY
|
100
|
-
else
|
101
|
-
define_method method do |*args, &block|
|
102
|
-
scoping { @klass.public_send(method, *args, &block) }
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
99
|
end
|
108
100
|
|
109
101
|
private
|
110
102
|
|
111
103
|
def method_missing(method, *args, &block)
|
112
104
|
if @klass.respond_to?(method)
|
113
|
-
|
105
|
+
@klass.generate_relation_method(method)
|
114
106
|
scoping { @klass.public_send(method, *args, &block) }
|
115
107
|
else
|
116
108
|
super
|
@@ -7,8 +7,8 @@ module ActiveRecord
|
|
7
7
|
ONE_AS_ONE = "1 AS one"
|
8
8
|
|
9
9
|
# Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
|
10
|
-
# If one or more records
|
11
|
-
# is an integer, find by id coerces its arguments using +to_i+.
|
10
|
+
# If one or more records cannot be found for the requested ids, then ActiveRecord::RecordNotFound will be raised.
|
11
|
+
# If the primary key is an integer, find by id coerces its arguments by using +to_i+.
|
12
12
|
#
|
13
13
|
# Person.find(1) # returns the object for ID = 1
|
14
14
|
# Person.find("1") # returns the object for ID = 1
|
@@ -314,7 +314,7 @@ module ActiveRecord
|
|
314
314
|
|
315
315
|
relation = construct_relation_for_exists(conditions)
|
316
316
|
|
317
|
-
skip_query_cache_if_necessary { connection.select_one(relation.arel, "#{name} Exists") } ? true : false
|
317
|
+
skip_query_cache_if_necessary { connection.select_one(relation.arel, "#{name} Exists?") } ? true : false
|
318
318
|
end
|
319
319
|
|
320
320
|
# This method is called whenever no records are found with either a single
|
@@ -355,7 +355,7 @@ module ActiveRecord
|
|
355
355
|
conditions = sanitize_forbidden_attributes(conditions)
|
356
356
|
|
357
357
|
if distinct_value && offset_value
|
358
|
-
relation = limit(1)
|
358
|
+
relation = except(:order).limit!(1)
|
359
359
|
else
|
360
360
|
relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
|
361
361
|
end
|
@@ -370,17 +370,22 @@ module ActiveRecord
|
|
370
370
|
relation
|
371
371
|
end
|
372
372
|
|
373
|
-
def construct_join_dependency(associations)
|
374
|
-
ActiveRecord::Associations::JoinDependency.new(
|
375
|
-
klass, table, associations
|
376
|
-
)
|
377
|
-
end
|
378
|
-
|
379
373
|
def apply_join_dependency(eager_loading: group_values.empty?)
|
380
|
-
join_dependency = construct_join_dependency(
|
374
|
+
join_dependency = construct_join_dependency(
|
375
|
+
eager_load_values + includes_values, Arel::Nodes::OuterJoin
|
376
|
+
)
|
381
377
|
relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
|
382
378
|
|
383
|
-
if eager_loading && !
|
379
|
+
if eager_loading && !(
|
380
|
+
using_limitable_reflections?(join_dependency.reflections) &&
|
381
|
+
using_limitable_reflections?(
|
382
|
+
construct_join_dependency(
|
383
|
+
select_association_list(joins_values).concat(
|
384
|
+
select_association_list(left_outer_joins_values)
|
385
|
+
), nil
|
386
|
+
).reflections
|
387
|
+
)
|
388
|
+
)
|
384
389
|
if has_limit_or_offset?
|
385
390
|
limited_ids = limited_ids_for(relation)
|
386
391
|
limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
|
@@ -117,16 +117,16 @@ module ActiveRecord
|
|
117
117
|
if other.klass == relation.klass
|
118
118
|
relation.joins!(*other.joins_values)
|
119
119
|
else
|
120
|
-
|
120
|
+
associations, others = other.joins_values.partition do |join|
|
121
121
|
case join
|
122
|
-
when Hash, Symbol, Array
|
123
|
-
other.send(:construct_join_dependency, join)
|
124
|
-
else
|
125
|
-
join
|
122
|
+
when Hash, Symbol, Array; true
|
126
123
|
end
|
127
124
|
end
|
128
125
|
|
129
|
-
|
126
|
+
join_dependency = other.construct_join_dependency(
|
127
|
+
associations, Arel::Nodes::InnerJoin
|
128
|
+
)
|
129
|
+
relation.joins!(join_dependency, *others)
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
@@ -136,16 +136,11 @@ module ActiveRecord
|
|
136
136
|
if other.klass == relation.klass
|
137
137
|
relation.left_outer_joins!(*other.left_outer_joins_values)
|
138
138
|
else
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
join
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
relation.left_outer_joins!(*joins_dependency)
|
139
|
+
associations = other.left_outer_joins_values
|
140
|
+
join_dependency = other.construct_join_dependency(
|
141
|
+
associations, Arel::Nodes::OuterJoin
|
142
|
+
)
|
143
|
+
relation.joins!(join_dependency)
|
149
144
|
end
|
150
145
|
end
|
151
146
|
|
@@ -41,18 +41,42 @@ module ActiveRecord
|
|
41
41
|
#
|
42
42
|
# User.where.not(name: %w(Ko1 Nobu))
|
43
43
|
# # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
|
44
|
-
#
|
45
|
-
# User.where.not(name: "Jon", role: "admin")
|
46
|
-
# # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
|
47
44
|
def not(opts, *rest)
|
48
45
|
opts = sanitize_forbidden_attributes(opts)
|
49
46
|
|
50
47
|
where_clause = @scope.send(:where_clause_factory).build(opts, rest)
|
51
48
|
|
52
49
|
@scope.references!(PredicateBuilder.references(opts)) if Hash === opts
|
53
|
-
|
50
|
+
|
51
|
+
if not_behaves_as_nor?(opts)
|
52
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
53
|
+
NOT conditions will no longer behave as NOR in Rails 6.1.
|
54
|
+
To continue using NOR conditions, NOT each conditions manually
|
55
|
+
(`#{
|
56
|
+
opts.flat_map { |key, value|
|
57
|
+
if value.is_a?(Hash) && value.size > 1
|
58
|
+
value.map { |k, v| ".where.not(#{key.inspect} => { #{k.inspect} => ... })" }
|
59
|
+
else
|
60
|
+
".where.not(#{key.inspect} => ...)"
|
61
|
+
end
|
62
|
+
}.join
|
63
|
+
}`).
|
64
|
+
MSG
|
65
|
+
@scope.where_clause += where_clause.invert(:nor)
|
66
|
+
else
|
67
|
+
@scope.where_clause += where_clause.invert
|
68
|
+
end
|
69
|
+
|
54
70
|
@scope
|
55
71
|
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def not_behaves_as_nor?(opts)
|
75
|
+
return false unless opts.is_a?(Hash)
|
76
|
+
|
77
|
+
opts.any? { |k, v| v.is_a?(Hash) && v.size > 1 } ||
|
78
|
+
opts.size > 1
|
79
|
+
end
|
56
80
|
end
|
57
81
|
|
58
82
|
FROZEN_EMPTY_ARRAY = [].freeze
|
@@ -67,11 +91,13 @@ module ActiveRecord
|
|
67
91
|
end
|
68
92
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
69
93
|
def #{method_name} # def includes_values
|
70
|
-
|
94
|
+
default = DEFAULT_VALUES[:#{name}] # default = DEFAULT_VALUES[:includes]
|
95
|
+
@values.fetch(:#{name}, default) # @values.fetch(:includes, default)
|
71
96
|
end # end
|
72
97
|
|
73
98
|
def #{method_name}=(value) # def includes_values=(value)
|
74
|
-
|
99
|
+
assert_mutability! # assert_mutability!
|
100
|
+
@values[:#{name}] = value # @values[:includes] = value
|
75
101
|
end # end
|
76
102
|
CODE
|
77
103
|
end
|
@@ -100,7 +126,7 @@ module ActiveRecord
|
|
100
126
|
#
|
101
127
|
# === conditions
|
102
128
|
#
|
103
|
-
# If you want to add conditions to your included models you'll have
|
129
|
+
# If you want to add string conditions to your included models, you'll have
|
104
130
|
# to explicitly reference them. For example:
|
105
131
|
#
|
106
132
|
# User.includes(:posts).where('posts.name = ?', 'example')
|
@@ -111,6 +137,12 @@ module ActiveRecord
|
|
111
137
|
#
|
112
138
|
# Note that #includes works with association names while #references needs
|
113
139
|
# the actual table name.
|
140
|
+
#
|
141
|
+
# If you pass the conditions via hash, you don't need to call #references
|
142
|
+
# explicitly, as #where references the tables for you. For example, this
|
143
|
+
# will work correctly:
|
144
|
+
#
|
145
|
+
# User.includes(:posts).where(posts: { name: 'example' })
|
114
146
|
def includes(*args)
|
115
147
|
check_if_method_has_arguments!(:includes, args)
|
116
148
|
spawn.includes!(*args)
|
@@ -154,6 +186,19 @@ module ActiveRecord
|
|
154
186
|
self
|
155
187
|
end
|
156
188
|
|
189
|
+
# Extracts a named +association+ from the relation. The named association is first preloaded,
|
190
|
+
# then the individual association records are collected from the relation. Like so:
|
191
|
+
#
|
192
|
+
# account.memberships.extract_associated(:user)
|
193
|
+
# # => Returns collection of User records
|
194
|
+
#
|
195
|
+
# This is short-hand for:
|
196
|
+
#
|
197
|
+
# account.memberships.preload(:user).collect(&:user)
|
198
|
+
def extract_associated(association)
|
199
|
+
preload(association).collect(&association)
|
200
|
+
end
|
201
|
+
|
157
202
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
158
203
|
# and should therefore be JOINed in any query rather than loaded separately.
|
159
204
|
# This method only works in conjunction with #includes.
|
@@ -233,13 +278,31 @@ module ActiveRecord
|
|
233
278
|
def _select!(*fields) # :nodoc:
|
234
279
|
fields.reject!(&:blank?)
|
235
280
|
fields.flatten!
|
236
|
-
fields.map! do |field|
|
237
|
-
klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
|
238
|
-
end
|
239
281
|
self.select_values += fields
|
240
282
|
self
|
241
283
|
end
|
242
284
|
|
285
|
+
# Allows you to change a previously set select statement.
|
286
|
+
#
|
287
|
+
# Post.select(:title, :body)
|
288
|
+
# # SELECT `posts`.`title`, `posts`.`body` FROM `posts`
|
289
|
+
#
|
290
|
+
# Post.select(:title, :body).reselect(:created_at)
|
291
|
+
# # SELECT `posts`.`created_at` FROM `posts`
|
292
|
+
#
|
293
|
+
# This is short-hand for <tt>unscope(:select).select(fields)</tt>.
|
294
|
+
# Note that we're unscoping the entire select statement.
|
295
|
+
def reselect(*args)
|
296
|
+
check_if_method_has_arguments!(:reselect, args)
|
297
|
+
spawn.reselect!(*args)
|
298
|
+
end
|
299
|
+
|
300
|
+
# Same as #reselect but operates on relation in-place instead of copying.
|
301
|
+
def reselect!(*args) # :nodoc:
|
302
|
+
self.select_values = args
|
303
|
+
self
|
304
|
+
end
|
305
|
+
|
243
306
|
# Allows to specify a group attribute:
|
244
307
|
#
|
245
308
|
# User.group(:name)
|
@@ -320,7 +383,7 @@ module ActiveRecord
|
|
320
383
|
|
321
384
|
# Same as #reorder but operates on relation in-place instead of copying.
|
322
385
|
def reorder!(*args) # :nodoc:
|
323
|
-
preprocess_order_args(args)
|
386
|
+
preprocess_order_args(args) unless args.all?(&:blank?)
|
324
387
|
|
325
388
|
self.reordering_value = true
|
326
389
|
self.order_values = args
|
@@ -328,8 +391,8 @@ module ActiveRecord
|
|
328
391
|
end
|
329
392
|
|
330
393
|
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
|
331
|
-
:limit, :offset, :joins, :left_outer_joins,
|
332
|
-
:includes, :from, :readonly, :having])
|
394
|
+
:limit, :offset, :joins, :left_outer_joins, :annotate,
|
395
|
+
:includes, :from, :readonly, :having, :optimizer_hints])
|
333
396
|
|
334
397
|
# Removes an unwanted relation that is already defined on a chain of relations.
|
335
398
|
# This is useful when passing around chains of relations and would like to
|
@@ -380,7 +443,8 @@ module ActiveRecord
|
|
380
443
|
if !VALID_UNSCOPING_VALUES.include?(scope)
|
381
444
|
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
382
445
|
end
|
383
|
-
|
446
|
+
assert_mutability!
|
447
|
+
@values[scope] = DEFAULT_VALUES[scope]
|
384
448
|
when Hash
|
385
449
|
scope.each do |key, target_value|
|
386
450
|
if key != :where
|
@@ -880,6 +944,29 @@ module ActiveRecord
|
|
880
944
|
self
|
881
945
|
end
|
882
946
|
|
947
|
+
# Specify optimizer hints to be used in the SELECT statement.
|
948
|
+
#
|
949
|
+
# Example (for MySQL):
|
950
|
+
#
|
951
|
+
# Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
|
952
|
+
# # SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
|
953
|
+
#
|
954
|
+
# Example (for PostgreSQL with pg_hint_plan):
|
955
|
+
#
|
956
|
+
# Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
|
957
|
+
# # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
|
958
|
+
def optimizer_hints(*args)
|
959
|
+
check_if_method_has_arguments!(:optimizer_hints, args)
|
960
|
+
spawn.optimizer_hints!(*args)
|
961
|
+
end
|
962
|
+
|
963
|
+
def optimizer_hints!(*args) # :nodoc:
|
964
|
+
args.flatten!
|
965
|
+
|
966
|
+
self.optimizer_hints_values |= args
|
967
|
+
self
|
968
|
+
end
|
969
|
+
|
883
970
|
# Reverse the existing order clause on the relation.
|
884
971
|
#
|
885
972
|
# User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
|
@@ -904,23 +991,47 @@ module ActiveRecord
|
|
904
991
|
self
|
905
992
|
end
|
906
993
|
|
994
|
+
# Adds an SQL comment to queries generated from this relation. For example:
|
995
|
+
#
|
996
|
+
# User.annotate("selecting user names").select(:name)
|
997
|
+
# # SELECT "users"."name" FROM "users" /* selecting user names */
|
998
|
+
#
|
999
|
+
# User.annotate("selecting", "user", "names").select(:name)
|
1000
|
+
# # SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */
|
1001
|
+
#
|
1002
|
+
# The SQL block comment delimiters, "/*" and "*/", will be added automatically.
|
1003
|
+
def annotate(*args)
|
1004
|
+
check_if_method_has_arguments!(:annotate, args)
|
1005
|
+
spawn.annotate!(*args)
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
# Like #annotate, but modifies relation in place.
|
1009
|
+
def annotate!(*args) # :nodoc:
|
1010
|
+
self.annotate_values += args
|
1011
|
+
self
|
1012
|
+
end
|
1013
|
+
|
907
1014
|
# Returns the Arel object associated with the relation.
|
908
1015
|
def arel(aliases = nil) # :nodoc:
|
909
1016
|
@arel ||= build_arel(aliases)
|
910
1017
|
end
|
911
1018
|
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
1019
|
+
def construct_join_dependency(associations, join_type) # :nodoc:
|
1020
|
+
ActiveRecord::Associations::JoinDependency.new(
|
1021
|
+
klass, table, associations, join_type
|
1022
|
+
)
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
protected
|
1026
|
+
def build_subquery(subquery_alias, select_value) # :nodoc:
|
1027
|
+
subquery = except(:optimizer_hints).arel.as(subquery_alias)
|
917
1028
|
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
@values[name] = value
|
1029
|
+
Arel::SelectManager.new(subquery).project(select_value).tap do |arel|
|
1030
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
1031
|
+
end
|
922
1032
|
end
|
923
1033
|
|
1034
|
+
private
|
924
1035
|
def assert_mutability!
|
925
1036
|
raise ImmutableRelation if @loaded
|
926
1037
|
raise ImmutableRelation if defined?(@arel) && @arel
|
@@ -929,8 +1040,11 @@ module ActiveRecord
|
|
929
1040
|
def build_arel(aliases)
|
930
1041
|
arel = Arel::SelectManager.new(table)
|
931
1042
|
|
932
|
-
|
933
|
-
|
1043
|
+
if !joins_values.empty?
|
1044
|
+
build_joins(arel, joins_values.flatten, aliases)
|
1045
|
+
elsif !left_outer_joins_values.empty?
|
1046
|
+
build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
|
1047
|
+
end
|
934
1048
|
|
935
1049
|
arel.where(where_clause.ast) unless where_clause.empty?
|
936
1050
|
arel.having(having_clause.ast) unless having_clause.empty?
|
@@ -956,9 +1070,11 @@ module ActiveRecord
|
|
956
1070
|
|
957
1071
|
build_select(arel)
|
958
1072
|
|
1073
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
959
1074
|
arel.distinct(distinct_value)
|
960
1075
|
arel.from(build_from) unless from_clause.empty?
|
961
1076
|
arel.lock(lock_value) if lock_value
|
1077
|
+
arel.comment(*annotate_values) unless annotate_values.empty?
|
962
1078
|
|
963
1079
|
arel
|
964
1080
|
end
|
@@ -978,32 +1094,68 @@ module ActiveRecord
|
|
978
1094
|
end
|
979
1095
|
end
|
980
1096
|
|
981
|
-
def
|
982
|
-
|
983
|
-
|
1097
|
+
def select_association_list(associations)
|
1098
|
+
result = []
|
1099
|
+
associations.each do |association|
|
1100
|
+
case association
|
984
1101
|
when Hash, Symbol, Array
|
985
|
-
|
986
|
-
when ActiveRecord::Associations::JoinDependency
|
987
|
-
:stashed_join
|
1102
|
+
result << association
|
988
1103
|
else
|
989
|
-
|
1104
|
+
yield if block_given?
|
990
1105
|
end
|
991
1106
|
end
|
1107
|
+
result
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
def valid_association_list(associations)
|
1111
|
+
select_association_list(associations) do
|
1112
|
+
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1113
|
+
end
|
1114
|
+
end
|
992
1115
|
|
1116
|
+
def build_left_outer_joins(manager, outer_joins, aliases)
|
1117
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1118
|
+
buckets[:association_join] = valid_association_list(outer_joins)
|
993
1119
|
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
994
1120
|
end
|
995
1121
|
|
996
1122
|
def build_joins(manager, joins, aliases)
|
997
|
-
buckets =
|
1123
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1124
|
+
|
1125
|
+
unless left_outer_joins_values.empty?
|
1126
|
+
left_joins = valid_association_list(left_outer_joins_values.flatten)
|
1127
|
+
buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
|
1131
|
+
buckets[:stashed_join] << joins.pop if joins.last.base_klass == klass
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
joins.map! do |join|
|
1135
|
+
if join.is_a?(String)
|
1136
|
+
table.create_string_join(Arel.sql(join.strip)) unless join.blank?
|
1137
|
+
else
|
1138
|
+
join
|
1139
|
+
end
|
1140
|
+
end.delete_if(&:blank?).uniq!
|
1141
|
+
|
1142
|
+
while joins.first.is_a?(Arel::Nodes::Join)
|
1143
|
+
join_node = joins.shift
|
1144
|
+
if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
|
1145
|
+
buckets[:join_node] << join_node
|
1146
|
+
else
|
1147
|
+
buckets[:leading_join] << join_node
|
1148
|
+
end
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
joins.each do |join|
|
998
1152
|
case join
|
999
|
-
when String
|
1000
|
-
:string_join
|
1001
1153
|
when Hash, Symbol, Array
|
1002
|
-
:association_join
|
1154
|
+
buckets[:association_join] << join
|
1003
1155
|
when ActiveRecord::Associations::JoinDependency
|
1004
|
-
:stashed_join
|
1156
|
+
buckets[:stashed_join] << join
|
1005
1157
|
when Arel::Nodes::Join
|
1006
|
-
:join_node
|
1158
|
+
buckets[:join_node] << join
|
1007
1159
|
else
|
1008
1160
|
raise "unknown class: %s" % join.class.name
|
1009
1161
|
end
|
@@ -1013,31 +1165,21 @@ module ActiveRecord
|
|
1013
1165
|
end
|
1014
1166
|
|
1015
1167
|
def build_join_query(manager, buckets, join_type, aliases)
|
1016
|
-
buckets.default = []
|
1017
|
-
|
1018
1168
|
association_joins = buckets[:association_join]
|
1019
1169
|
stashed_joins = buckets[:stashed_join]
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
join_list = join_nodes + convert_join_strings_to_ast(string_joins)
|
1024
|
-
alias_tracker = alias_tracker(join_list, aliases)
|
1025
|
-
|
1026
|
-
join_dependency = construct_join_dependency(association_joins)
|
1027
|
-
|
1028
|
-
joins = join_dependency.join_constraints(stashed_joins, join_type, alias_tracker)
|
1029
|
-
joins.each { |join| manager.from(join) }
|
1170
|
+
leading_joins = buckets[:leading_join]
|
1171
|
+
join_nodes = buckets[:join_node]
|
1030
1172
|
|
1031
|
-
manager.join_sources
|
1173
|
+
join_sources = manager.join_sources
|
1174
|
+
join_sources.concat(leading_joins) unless leading_joins.empty?
|
1032
1175
|
|
1033
|
-
|
1034
|
-
|
1176
|
+
unless association_joins.empty? && stashed_joins.empty?
|
1177
|
+
alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
|
1178
|
+
join_dependency = construct_join_dependency(association_joins, join_type)
|
1179
|
+
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
|
1180
|
+
end
|
1035
1181
|
|
1036
|
-
|
1037
|
-
joins
|
1038
|
-
.flatten
|
1039
|
-
.reject(&:blank?)
|
1040
|
-
.map { |join| table.create_string_join(Arel.sql(join)) }
|
1182
|
+
join_sources.concat(join_nodes) unless join_nodes.empty?
|
1041
1183
|
end
|
1042
1184
|
|
1043
1185
|
def build_select(arel)
|
@@ -1054,10 +1196,11 @@ module ActiveRecord
|
|
1054
1196
|
columns.flat_map do |field|
|
1055
1197
|
case field
|
1056
1198
|
when Symbol
|
1057
|
-
field
|
1058
|
-
|
1199
|
+
arel_column(field.to_s) do |attr_name|
|
1200
|
+
connection.quote_table_name(attr_name)
|
1201
|
+
end
|
1059
1202
|
when String
|
1060
|
-
arel_column(field)
|
1203
|
+
arel_column(field, &:itself)
|
1061
1204
|
when Proc
|
1062
1205
|
field.call
|
1063
1206
|
else
|
@@ -1067,17 +1210,20 @@ module ActiveRecord
|
|
1067
1210
|
end
|
1068
1211
|
|
1069
1212
|
def arel_column(field)
|
1070
|
-
field = klass.
|
1213
|
+
field = klass.attribute_aliases[field] || field
|
1071
1214
|
from = from_clause.name || from_clause.value
|
1072
1215
|
|
1073
|
-
if klass.columns_hash.key?(field) &&
|
1074
|
-
(!from || from == table.name || from == connection.quote_table_name(table.name))
|
1216
|
+
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
1075
1217
|
arel_attribute(field)
|
1076
1218
|
else
|
1077
|
-
yield
|
1219
|
+
yield field
|
1078
1220
|
end
|
1079
1221
|
end
|
1080
1222
|
|
1223
|
+
def table_name_matches?(from)
|
1224
|
+
/(?:\A|(?<!FROM)\s)(?:\b#{table.name}\b|#{connection.quote_table_name(table.name)})(?!\.)/i.match?(from.to_s)
|
1225
|
+
end
|
1226
|
+
|
1081
1227
|
def reverse_sql_order(order_query)
|
1082
1228
|
if order_query.empty?
|
1083
1229
|
return [arel_attribute(primary_key).desc] if primary_key
|
@@ -1093,7 +1239,7 @@ module ActiveRecord
|
|
1093
1239
|
o.reverse
|
1094
1240
|
when String
|
1095
1241
|
if does_not_support_reverse?(o)
|
1096
|
-
raise IrreversibleOrderError, "Order #{o.inspect}
|
1242
|
+
raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
|
1097
1243
|
end
|
1098
1244
|
o.split(",").map! do |s|
|
1099
1245
|
s.strip!
|
@@ -1139,6 +1285,7 @@ module ActiveRecord
|
|
1139
1285
|
end
|
1140
1286
|
|
1141
1287
|
def preprocess_order_args(order_args)
|
1288
|
+
order_args.reject!(&:blank?)
|
1142
1289
|
order_args.map! do |arg|
|
1143
1290
|
klass.sanitize_sql_for_order(arg)
|
1144
1291
|
end
|
@@ -1146,7 +1293,7 @@ module ActiveRecord
|
|
1146
1293
|
|
1147
1294
|
@klass.disallow_raw_sql!(
|
1148
1295
|
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
|
1149
|
-
permit:
|
1296
|
+
permit: connection.column_name_with_order_matcher
|
1150
1297
|
)
|
1151
1298
|
|
1152
1299
|
validate_order_args(order_args)
|
@@ -1159,20 +1306,14 @@ module ActiveRecord
|
|
1159
1306
|
order_args.map! do |arg|
|
1160
1307
|
case arg
|
1161
1308
|
when Symbol
|
1162
|
-
arg
|
1163
|
-
arel_column(arg) {
|
1164
|
-
Arel.sql(connection.quote_table_name(arg))
|
1165
|
-
}.asc
|
1309
|
+
order_column(arg.to_s).asc
|
1166
1310
|
when Hash
|
1167
1311
|
arg.map { |field, dir|
|
1168
1312
|
case field
|
1169
1313
|
when Arel::Nodes::SqlLiteral
|
1170
1314
|
field.send(dir.downcase)
|
1171
1315
|
else
|
1172
|
-
field
|
1173
|
-
arel_column(field) {
|
1174
|
-
Arel.sql(connection.quote_table_name(field))
|
1175
|
-
}.send(dir.downcase)
|
1316
|
+
order_column(field.to_s).send(dir.downcase)
|
1176
1317
|
end
|
1177
1318
|
}
|
1178
1319
|
else
|
@@ -1181,6 +1322,16 @@ module ActiveRecord
|
|
1181
1322
|
end.flatten!
|
1182
1323
|
end
|
1183
1324
|
|
1325
|
+
def order_column(field)
|
1326
|
+
arel_column(field) do |attr_name|
|
1327
|
+
if attr_name == "count" && !group_values.empty?
|
1328
|
+
arel_attribute(attr_name)
|
1329
|
+
else
|
1330
|
+
Arel.sql(connection.quote_table_name(attr_name))
|
1331
|
+
end
|
1332
|
+
end
|
1333
|
+
end
|
1334
|
+
|
1184
1335
|
# Checks to make sure that the arguments are not blank. Note that if some
|
1185
1336
|
# blank-like object were initially passed into the query method, then this
|
1186
1337
|
# method will not raise an error.
|
@@ -1207,7 +1358,8 @@ module ActiveRecord
|
|
1207
1358
|
def structurally_incompatible_values_for_or(other)
|
1208
1359
|
values = other.values
|
1209
1360
|
STRUCTURAL_OR_METHODS.reject do |method|
|
1210
|
-
|
1361
|
+
default = DEFAULT_VALUES[method]
|
1362
|
+
@values.fetch(method, default) == values.fetch(method, default)
|
1211
1363
|
end
|
1212
1364
|
end
|
1213
1365
|
|