activerecord 5.2.3 → 6.0.1
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 +691 -542
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +20 -15
- data/lib/active_record/associations/association.rb +61 -20
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- 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 -21
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +2 -10
- data/lib/active_record/associations/has_many_through_association.rb +18 -25
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +28 -28
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +39 -31
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +111 -40
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +22 -8
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +5 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +126 -19
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +99 -123
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +21 -11
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +133 -54
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +187 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +138 -195
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +53 -43
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +164 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -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 +42 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
- data/lib/active_record/connection_handling.rb +155 -26
- data/lib/active_record/core.rb +103 -59
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +37 -7
- data/lib/active_record/errors.rb +15 -7
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +145 -472
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +76 -49
- data/lib/active_record/model_schema.rb +33 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +228 -24
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +32 -20
- data/lib/active_record/railtie.rb +80 -43
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +196 -46
- data/lib/active_record/reflection.rb +42 -44
- data/lib/active_record/relation.rb +311 -80
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +58 -51
- data/lib/active_record/relation/delegation.rb +26 -43
- data/lib/active_record/relation/finder_methods.rb +24 -28
- data/lib/active_record/relation/merger.rb +11 -20
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +219 -79
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +19 -15
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +194 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +57 -66
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +58 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- 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 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +111 -26
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -117,18 +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
|
-
ActiveRecord::Associations::JoinDependency.new(
|
124
|
-
other.klass, other.table, join
|
125
|
-
)
|
126
|
-
else
|
127
|
-
join
|
122
|
+
when Hash, Symbol, Array; true
|
128
123
|
end
|
129
124
|
end
|
130
125
|
|
131
|
-
|
126
|
+
join_dependency = other.construct_join_dependency(
|
127
|
+
associations, Arel::Nodes::InnerJoin
|
128
|
+
)
|
129
|
+
relation.joins!(join_dependency, *others)
|
132
130
|
end
|
133
131
|
end
|
134
132
|
|
@@ -138,18 +136,11 @@ module ActiveRecord
|
|
138
136
|
if other.klass == relation.klass
|
139
137
|
relation.left_outer_joins!(*other.left_outer_joins_values)
|
140
138
|
else
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
)
|
147
|
-
else
|
148
|
-
join
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
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)
|
153
144
|
end
|
154
145
|
end
|
155
146
|
|
@@ -27,7 +27,7 @@ module ActiveRecord
|
|
27
27
|
key
|
28
28
|
else
|
29
29
|
key = key.to_s
|
30
|
-
key.split("."
|
30
|
+
key.split(".").first if key.include?(".")
|
31
31
|
end
|
32
32
|
end.compact
|
33
33
|
end
|
@@ -62,9 +62,6 @@ module ActiveRecord
|
|
62
62
|
end
|
63
63
|
|
64
64
|
protected
|
65
|
-
|
66
|
-
attr_reader :table
|
67
|
-
|
68
65
|
def expand_from_hash(attributes)
|
69
66
|
return ["1=0"] if attributes.empty?
|
70
67
|
|
@@ -115,6 +112,7 @@ module ActiveRecord
|
|
115
112
|
end
|
116
113
|
|
117
114
|
private
|
115
|
+
attr_reader :table
|
118
116
|
|
119
117
|
def associated_predicate_builder(association_name)
|
120
118
|
self.class.new(table.associated_table(association_name))
|
@@ -122,11 +120,11 @@ module ActiveRecord
|
|
122
120
|
|
123
121
|
def convert_dot_notation_to_hash(attributes)
|
124
122
|
dot_notation = attributes.select do |k, v|
|
125
|
-
k.include?("."
|
123
|
+
k.include?(".") && !v.is_a?(Hash)
|
126
124
|
end
|
127
125
|
|
128
126
|
dot_notation.each_key do |key|
|
129
|
-
table_name, column_name = key.split("."
|
127
|
+
table_name, column_name = key.split(".")
|
130
128
|
value = attributes.delete(key)
|
131
129
|
attributes[table_name] ||= {}
|
132
130
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/array/extract"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
class PredicateBuilder
|
5
7
|
class ArrayHandler # :nodoc:
|
@@ -11,8 +13,8 @@ module ActiveRecord
|
|
11
13
|
return attribute.in([]) if value.empty?
|
12
14
|
|
13
15
|
values = value.map { |x| x.is_a?(Base) ? x.id : x }
|
14
|
-
nils
|
15
|
-
ranges
|
16
|
+
nils = values.extract!(&:nil?)
|
17
|
+
ranges = values.extract! { |v| v.is_a?(Range) }
|
16
18
|
|
17
19
|
values_predicate =
|
18
20
|
case values.length
|
@@ -34,8 +36,7 @@ module ActiveRecord
|
|
34
36
|
array_predicates.inject(&:or)
|
35
37
|
end
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
+
private
|
39
40
|
attr_reader :predicate_builder
|
40
41
|
|
41
42
|
module NullPredicate # :nodoc:
|
@@ -12,12 +12,9 @@ module ActiveRecord
|
|
12
12
|
[associated_table.association_join_foreign_key.to_s => ids]
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
17
|
-
protected
|
15
|
+
private
|
18
16
|
attr_reader :associated_table, :value
|
19
17
|
|
20
|
-
private
|
21
18
|
def ids
|
22
19
|
case value
|
23
20
|
when Relation
|
@@ -17,12 +17,9 @@ module ActiveRecord
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
22
|
-
protected
|
20
|
+
private
|
23
21
|
attr_reader :associated_table, :values
|
24
22
|
|
25
|
-
private
|
26
23
|
def type_to_ids_mapping
|
27
24
|
default_hash = Hash.new { |hsh, key| hsh[key] = [] }
|
28
25
|
values.each_with_object(default_hash) do |value, hash|
|
@@ -3,11 +3,7 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
class PredicateBuilder
|
5
5
|
class RangeHandler # :nodoc:
|
6
|
-
|
7
|
-
def exclude_end?
|
8
|
-
false
|
9
|
-
end
|
10
|
-
end
|
6
|
+
RangeWithBinds = Struct.new(:begin, :end, :exclude_end?)
|
11
7
|
|
12
8
|
def initialize(predicate_builder)
|
13
9
|
@predicate_builder = predicate_builder
|
@@ -16,26 +12,10 @@ module ActiveRecord
|
|
16
12
|
def call(attribute, value)
|
17
13
|
begin_bind = predicate_builder.build_bind_attribute(attribute.name, value.begin)
|
18
14
|
end_bind = predicate_builder.build_bind_attribute(attribute.name, value.end)
|
19
|
-
|
20
|
-
if begin_bind.value.infinity?
|
21
|
-
if end_bind.value.infinity?
|
22
|
-
attribute.not_in([])
|
23
|
-
elsif value.exclude_end?
|
24
|
-
attribute.lt(end_bind)
|
25
|
-
else
|
26
|
-
attribute.lteq(end_bind)
|
27
|
-
end
|
28
|
-
elsif end_bind.value.infinity?
|
29
|
-
attribute.gteq(begin_bind)
|
30
|
-
elsif value.exclude_end?
|
31
|
-
attribute.gteq(begin_bind).and(attribute.lt(end_bind))
|
32
|
-
else
|
33
|
-
attribute.between(RangeWithBinds.new(begin_bind, end_bind))
|
34
|
-
end
|
15
|
+
attribute.between(RangeWithBinds.new(begin_bind, end_bind, value.exclude_end?))
|
35
16
|
end
|
36
17
|
|
37
|
-
|
38
|
-
|
18
|
+
private
|
39
19
|
attr_reader :predicate_builder
|
40
20
|
end
|
41
21
|
end
|
@@ -22,22 +22,27 @@ module ActiveRecord
|
|
22
22
|
value_before_type_cast.nil? ||
|
23
23
|
type.respond_to?(:subtype, true) && value_for_database.nil?
|
24
24
|
end
|
25
|
+
rescue ::RangeError
|
25
26
|
end
|
26
27
|
|
27
|
-
def
|
28
|
-
|
29
|
-
value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
30
|
-
@_boundable = true
|
28
|
+
def infinite?
|
29
|
+
infinity?(value_before_type_cast) || infinity?(value_for_database)
|
31
30
|
rescue ::RangeError
|
32
|
-
@_boundable = false
|
33
31
|
end
|
34
32
|
|
35
|
-
def
|
36
|
-
|
33
|
+
def unboundable?
|
34
|
+
if defined?(@_unboundable)
|
35
|
+
@_unboundable
|
36
|
+
else
|
37
|
+
value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
38
|
+
@_unboundable = nil
|
39
|
+
end
|
40
|
+
rescue ::RangeError
|
41
|
+
@_unboundable = type.cast(value_before_type_cast) <=> 0
|
37
42
|
end
|
38
43
|
|
39
44
|
private
|
40
|
-
def
|
45
|
+
def infinity?(value)
|
41
46
|
value.respond_to?(:infinite?) && value.infinite?
|
42
47
|
end
|
43
48
|
end
|
@@ -41,18 +41,31 @@ 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
|
+
(`#{ opts.keys.map { |key| ".where.not(#{key.inspect} => ...)" }.join }`).
|
56
|
+
MSG
|
57
|
+
@scope.where_clause += where_clause.invert(:nor)
|
58
|
+
else
|
59
|
+
@scope.where_clause += where_clause.invert
|
60
|
+
end
|
61
|
+
|
54
62
|
@scope
|
55
63
|
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def not_behaves_as_nor?(opts)
|
67
|
+
opts.is_a?(Hash) && opts.size > 1
|
68
|
+
end
|
56
69
|
end
|
57
70
|
|
58
71
|
FROZEN_EMPTY_ARRAY = [].freeze
|
@@ -67,11 +80,13 @@ module ActiveRecord
|
|
67
80
|
end
|
68
81
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
69
82
|
def #{method_name} # def includes_values
|
70
|
-
|
83
|
+
default = DEFAULT_VALUES[:#{name}] # default = DEFAULT_VALUES[:includes]
|
84
|
+
@values.fetch(:#{name}, default) # @values.fetch(:includes, default)
|
71
85
|
end # end
|
72
86
|
|
73
87
|
def #{method_name}=(value) # def includes_values=(value)
|
74
|
-
|
88
|
+
assert_mutability! # assert_mutability!
|
89
|
+
@values[:#{name}] = value # @values[:includes] = value
|
75
90
|
end # end
|
76
91
|
CODE
|
77
92
|
end
|
@@ -100,7 +115,7 @@ module ActiveRecord
|
|
100
115
|
#
|
101
116
|
# === conditions
|
102
117
|
#
|
103
|
-
# If you want to add conditions to your included models you'll have
|
118
|
+
# If you want to add string conditions to your included models, you'll have
|
104
119
|
# to explicitly reference them. For example:
|
105
120
|
#
|
106
121
|
# User.includes(:posts).where('posts.name = ?', 'example')
|
@@ -111,6 +126,12 @@ module ActiveRecord
|
|
111
126
|
#
|
112
127
|
# Note that #includes works with association names while #references needs
|
113
128
|
# the actual table name.
|
129
|
+
#
|
130
|
+
# If you pass the conditions via hash, you don't need to call #references
|
131
|
+
# explicitly, as #where references the tables for you. For example, this
|
132
|
+
# will work correctly:
|
133
|
+
#
|
134
|
+
# User.includes(:posts).where(posts: { name: 'example' })
|
114
135
|
def includes(*args)
|
115
136
|
check_if_method_has_arguments!(:includes, args)
|
116
137
|
spawn.includes!(*args)
|
@@ -154,6 +175,19 @@ module ActiveRecord
|
|
154
175
|
self
|
155
176
|
end
|
156
177
|
|
178
|
+
# Extracts a named +association+ from the relation. The named association is first preloaded,
|
179
|
+
# then the individual association records are collected from the relation. Like so:
|
180
|
+
#
|
181
|
+
# account.memberships.extract_associated(:user)
|
182
|
+
# # => Returns collection of User records
|
183
|
+
#
|
184
|
+
# This is short-hand for:
|
185
|
+
#
|
186
|
+
# account.memberships.preload(:user).collect(&:user)
|
187
|
+
def extract_associated(association)
|
188
|
+
preload(association).collect(&association)
|
189
|
+
end
|
190
|
+
|
157
191
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
158
192
|
# and should therefore be JOINed in any query rather than loaded separately.
|
159
193
|
# This method only works in conjunction with #includes.
|
@@ -231,14 +265,33 @@ module ActiveRecord
|
|
231
265
|
end
|
232
266
|
|
233
267
|
def _select!(*fields) # :nodoc:
|
268
|
+
fields.reject!(&:blank?)
|
234
269
|
fields.flatten!
|
235
|
-
fields.map! do |field|
|
236
|
-
klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
|
237
|
-
end
|
238
270
|
self.select_values += fields
|
239
271
|
self
|
240
272
|
end
|
241
273
|
|
274
|
+
# Allows you to change a previously set select statement.
|
275
|
+
#
|
276
|
+
# Post.select(:title, :body)
|
277
|
+
# # SELECT `posts`.`title`, `posts`.`body` FROM `posts`
|
278
|
+
#
|
279
|
+
# Post.select(:title, :body).reselect(:created_at)
|
280
|
+
# # SELECT `posts`.`created_at` FROM `posts`
|
281
|
+
#
|
282
|
+
# This is short-hand for <tt>unscope(:select).select(fields)</tt>.
|
283
|
+
# Note that we're unscoping the entire select statement.
|
284
|
+
def reselect(*args)
|
285
|
+
check_if_method_has_arguments!(:reselect, args)
|
286
|
+
spawn.reselect!(*args)
|
287
|
+
end
|
288
|
+
|
289
|
+
# Same as #reselect but operates on relation in-place instead of copying.
|
290
|
+
def reselect!(*args) # :nodoc:
|
291
|
+
self.select_values = args
|
292
|
+
self
|
293
|
+
end
|
294
|
+
|
242
295
|
# Allows to specify a group attribute:
|
243
296
|
#
|
244
297
|
# User.group(:name)
|
@@ -319,7 +372,7 @@ module ActiveRecord
|
|
319
372
|
|
320
373
|
# Same as #reorder but operates on relation in-place instead of copying.
|
321
374
|
def reorder!(*args) # :nodoc:
|
322
|
-
preprocess_order_args(args)
|
375
|
+
preprocess_order_args(args) unless args.all?(&:blank?)
|
323
376
|
|
324
377
|
self.reordering_value = true
|
325
378
|
self.order_values = args
|
@@ -327,8 +380,8 @@ module ActiveRecord
|
|
327
380
|
end
|
328
381
|
|
329
382
|
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
|
330
|
-
:limit, :offset, :joins, :left_outer_joins,
|
331
|
-
:includes, :from, :readonly, :having])
|
383
|
+
:limit, :offset, :joins, :left_outer_joins, :annotate,
|
384
|
+
:includes, :from, :readonly, :having, :optimizer_hints])
|
332
385
|
|
333
386
|
# Removes an unwanted relation that is already defined on a chain of relations.
|
334
387
|
# This is useful when passing around chains of relations and would like to
|
@@ -379,7 +432,8 @@ module ActiveRecord
|
|
379
432
|
if !VALID_UNSCOPING_VALUES.include?(scope)
|
380
433
|
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
381
434
|
end
|
382
|
-
|
435
|
+
assert_mutability!
|
436
|
+
@values[scope] = DEFAULT_VALUES[scope]
|
383
437
|
when Hash
|
384
438
|
scope.each do |key, target_value|
|
385
439
|
if key != :where
|
@@ -879,6 +933,29 @@ module ActiveRecord
|
|
879
933
|
self
|
880
934
|
end
|
881
935
|
|
936
|
+
# Specify optimizer hints to be used in the SELECT statement.
|
937
|
+
#
|
938
|
+
# Example (for MySQL):
|
939
|
+
#
|
940
|
+
# Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
|
941
|
+
# # SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
|
942
|
+
#
|
943
|
+
# Example (for PostgreSQL with pg_hint_plan):
|
944
|
+
#
|
945
|
+
# Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
|
946
|
+
# # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
|
947
|
+
def optimizer_hints(*args)
|
948
|
+
check_if_method_has_arguments!(:optimizer_hints, args)
|
949
|
+
spawn.optimizer_hints!(*args)
|
950
|
+
end
|
951
|
+
|
952
|
+
def optimizer_hints!(*args) # :nodoc:
|
953
|
+
args.flatten!
|
954
|
+
|
955
|
+
self.optimizer_hints_values |= args
|
956
|
+
self
|
957
|
+
end
|
958
|
+
|
882
959
|
# Reverse the existing order clause on the relation.
|
883
960
|
#
|
884
961
|
# User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
|
@@ -898,26 +975,52 @@ module ActiveRecord
|
|
898
975
|
self
|
899
976
|
end
|
900
977
|
|
978
|
+
def skip_preloading! # :nodoc:
|
979
|
+
self.skip_preloading_value = true
|
980
|
+
self
|
981
|
+
end
|
982
|
+
|
983
|
+
# Adds an SQL comment to queries generated from this relation. For example:
|
984
|
+
#
|
985
|
+
# User.annotate("selecting user names").select(:name)
|
986
|
+
# # SELECT "users"."name" FROM "users" /* selecting user names */
|
987
|
+
#
|
988
|
+
# User.annotate("selecting", "user", "names").select(:name)
|
989
|
+
# # SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */
|
990
|
+
#
|
991
|
+
# The SQL block comment delimiters, "/*" and "*/", will be added automatically.
|
992
|
+
def annotate(*args)
|
993
|
+
check_if_method_has_arguments!(:annotate, args)
|
994
|
+
spawn.annotate!(*args)
|
995
|
+
end
|
996
|
+
|
997
|
+
# Like #annotate, but modifies relation in place.
|
998
|
+
def annotate!(*args) # :nodoc:
|
999
|
+
self.annotate_values += args
|
1000
|
+
self
|
1001
|
+
end
|
1002
|
+
|
901
1003
|
# Returns the Arel object associated with the relation.
|
902
1004
|
def arel(aliases = nil) # :nodoc:
|
903
1005
|
@arel ||= build_arel(aliases)
|
904
1006
|
end
|
905
1007
|
|
906
|
-
|
907
|
-
|
908
|
-
|
1008
|
+
def construct_join_dependency(associations, join_type) # :nodoc:
|
1009
|
+
ActiveRecord::Associations::JoinDependency.new(
|
1010
|
+
klass, table, associations, join_type
|
1011
|
+
)
|
909
1012
|
end
|
910
1013
|
|
911
1014
|
protected
|
1015
|
+
def build_subquery(subquery_alias, select_value) # :nodoc:
|
1016
|
+
subquery = except(:optimizer_hints).arel.as(subquery_alias)
|
912
1017
|
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
@values[name] = value
|
1018
|
+
Arel::SelectManager.new(subquery).project(select_value).tap do |arel|
|
1019
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
1020
|
+
end
|
917
1021
|
end
|
918
1022
|
|
919
1023
|
private
|
920
|
-
|
921
1024
|
def assert_mutability!
|
922
1025
|
raise ImmutableRelation if @loaded
|
923
1026
|
raise ImmutableRelation if defined?(@arel) && @arel
|
@@ -926,14 +1029,17 @@ module ActiveRecord
|
|
926
1029
|
def build_arel(aliases)
|
927
1030
|
arel = Arel::SelectManager.new(table)
|
928
1031
|
|
929
|
-
|
930
|
-
|
1032
|
+
if !joins_values.empty?
|
1033
|
+
build_joins(arel, joins_values.flatten, aliases)
|
1034
|
+
elsif !left_outer_joins_values.empty?
|
1035
|
+
build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
|
1036
|
+
end
|
931
1037
|
|
932
1038
|
arel.where(where_clause.ast) unless where_clause.empty?
|
933
1039
|
arel.having(having_clause.ast) unless having_clause.empty?
|
934
1040
|
if limit_value
|
935
1041
|
limit_attribute = ActiveModel::Attribute.with_cast_value(
|
936
|
-
"LIMIT"
|
1042
|
+
"LIMIT",
|
937
1043
|
connection.sanitize_limit(limit_value),
|
938
1044
|
Type.default_value,
|
939
1045
|
)
|
@@ -941,7 +1047,7 @@ module ActiveRecord
|
|
941
1047
|
end
|
942
1048
|
if offset_value
|
943
1049
|
offset_attribute = ActiveModel::Attribute.with_cast_value(
|
944
|
-
"OFFSET"
|
1050
|
+
"OFFSET",
|
945
1051
|
offset_value.to_i,
|
946
1052
|
Type.default_value,
|
947
1053
|
)
|
@@ -953,9 +1059,11 @@ module ActiveRecord
|
|
953
1059
|
|
954
1060
|
build_select(arel)
|
955
1061
|
|
1062
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
956
1063
|
arel.distinct(distinct_value)
|
957
1064
|
arel.from(build_from) unless from_clause.empty?
|
958
1065
|
arel.lock(lock_value) if lock_value
|
1066
|
+
arel.comment(*annotate_values) unless annotate_values.empty?
|
959
1067
|
|
960
1068
|
arel
|
961
1069
|
end
|
@@ -975,32 +1083,68 @@ module ActiveRecord
|
|
975
1083
|
end
|
976
1084
|
end
|
977
1085
|
|
978
|
-
def
|
979
|
-
|
980
|
-
|
1086
|
+
def select_association_list(associations)
|
1087
|
+
result = []
|
1088
|
+
associations.each do |association|
|
1089
|
+
case association
|
981
1090
|
when Hash, Symbol, Array
|
982
|
-
|
983
|
-
when ActiveRecord::Associations::JoinDependency
|
984
|
-
:stashed_join
|
1091
|
+
result << association
|
985
1092
|
else
|
986
|
-
|
1093
|
+
yield if block_given?
|
987
1094
|
end
|
988
1095
|
end
|
1096
|
+
result
|
1097
|
+
end
|
989
1098
|
|
1099
|
+
def valid_association_list(associations)
|
1100
|
+
select_association_list(associations) do
|
1101
|
+
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1102
|
+
end
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
def build_left_outer_joins(manager, outer_joins, aliases)
|
1106
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1107
|
+
buckets[:association_join] = valid_association_list(outer_joins)
|
990
1108
|
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
991
1109
|
end
|
992
1110
|
|
993
1111
|
def build_joins(manager, joins, aliases)
|
994
|
-
buckets =
|
1112
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1113
|
+
|
1114
|
+
unless left_outer_joins_values.empty?
|
1115
|
+
left_joins = valid_association_list(left_outer_joins_values.flatten)
|
1116
|
+
buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
|
1120
|
+
buckets[:stashed_join] << joins.pop if joins.last.base_klass == klass
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
joins.map! do |join|
|
1124
|
+
if join.is_a?(String)
|
1125
|
+
table.create_string_join(Arel.sql(join.strip)) unless join.blank?
|
1126
|
+
else
|
1127
|
+
join
|
1128
|
+
end
|
1129
|
+
end.delete_if(&:blank?).uniq!
|
1130
|
+
|
1131
|
+
while joins.first.is_a?(Arel::Nodes::Join)
|
1132
|
+
join_node = joins.shift
|
1133
|
+
if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
|
1134
|
+
buckets[:join_node] << join_node
|
1135
|
+
else
|
1136
|
+
buckets[:leading_join] << join_node
|
1137
|
+
end
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
joins.each do |join|
|
995
1141
|
case join
|
996
|
-
when String
|
997
|
-
:string_join
|
998
1142
|
when Hash, Symbol, Array
|
999
|
-
:association_join
|
1143
|
+
buckets[:association_join] << join
|
1000
1144
|
when ActiveRecord::Associations::JoinDependency
|
1001
|
-
:stashed_join
|
1145
|
+
buckets[:stashed_join] << join
|
1002
1146
|
when Arel::Nodes::Join
|
1003
|
-
:join_node
|
1147
|
+
buckets[:join_node] << join
|
1004
1148
|
else
|
1005
1149
|
raise "unknown class: %s" % join.class.name
|
1006
1150
|
end
|
@@ -1010,33 +1154,21 @@ module ActiveRecord
|
|
1010
1154
|
end
|
1011
1155
|
|
1012
1156
|
def build_join_query(manager, buckets, join_type, aliases)
|
1013
|
-
buckets.default = []
|
1014
|
-
|
1015
1157
|
association_joins = buckets[:association_join]
|
1016
1158
|
stashed_joins = buckets[:stashed_join]
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
join_list = join_nodes + convert_join_strings_to_ast(string_joins)
|
1021
|
-
alias_tracker = alias_tracker(join_list, aliases)
|
1022
|
-
|
1023
|
-
join_dependency = ActiveRecord::Associations::JoinDependency.new(
|
1024
|
-
klass, table, association_joins
|
1025
|
-
)
|
1026
|
-
|
1027
|
-
joins = join_dependency.join_constraints(stashed_joins, join_type, alias_tracker)
|
1028
|
-
joins.each { |join| manager.from(join) }
|
1159
|
+
leading_joins = buckets[:leading_join]
|
1160
|
+
join_nodes = buckets[:join_node]
|
1029
1161
|
|
1030
|
-
manager.join_sources
|
1162
|
+
join_sources = manager.join_sources
|
1163
|
+
join_sources.concat(leading_joins) unless leading_joins.empty?
|
1031
1164
|
|
1032
|
-
|
1033
|
-
|
1165
|
+
unless association_joins.empty? && stashed_joins.empty?
|
1166
|
+
alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
|
1167
|
+
join_dependency = construct_join_dependency(association_joins, join_type)
|
1168
|
+
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
|
1169
|
+
end
|
1034
1170
|
|
1035
|
-
|
1036
|
-
joins
|
1037
|
-
.flatten
|
1038
|
-
.reject(&:blank?)
|
1039
|
-
.map { |join| table.create_string_join(Arel.sql(join)) }
|
1171
|
+
join_sources.concat(join_nodes) unless join_nodes.empty?
|
1040
1172
|
end
|
1041
1173
|
|
1042
1174
|
def build_select(arel)
|
@@ -1053,10 +1185,11 @@ module ActiveRecord
|
|
1053
1185
|
columns.flat_map do |field|
|
1054
1186
|
case field
|
1055
1187
|
when Symbol
|
1056
|
-
field
|
1057
|
-
|
1188
|
+
arel_column(field.to_s) do |attr_name|
|
1189
|
+
connection.quote_table_name(attr_name)
|
1190
|
+
end
|
1058
1191
|
when String
|
1059
|
-
arel_column(field)
|
1192
|
+
arel_column(field, &:itself)
|
1060
1193
|
when Proc
|
1061
1194
|
field.call
|
1062
1195
|
else
|
@@ -1066,13 +1199,13 @@ module ActiveRecord
|
|
1066
1199
|
end
|
1067
1200
|
|
1068
1201
|
def arel_column(field)
|
1069
|
-
field = klass.
|
1202
|
+
field = klass.attribute_aliases[field] || field
|
1070
1203
|
from = from_clause.name || from_clause.value
|
1071
1204
|
|
1072
1205
|
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
1073
1206
|
arel_attribute(field)
|
1074
1207
|
else
|
1075
|
-
yield
|
1208
|
+
yield field
|
1076
1209
|
end
|
1077
1210
|
end
|
1078
1211
|
|
@@ -1095,7 +1228,7 @@ module ActiveRecord
|
|
1095
1228
|
o.reverse
|
1096
1229
|
when String
|
1097
1230
|
if does_not_support_reverse?(o)
|
1098
|
-
raise IrreversibleOrderError, "Order #{o.inspect}
|
1231
|
+
raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
|
1099
1232
|
end
|
1100
1233
|
o.split(",").map! do |s|
|
1101
1234
|
s.strip!
|
@@ -1115,7 +1248,7 @@ module ActiveRecord
|
|
1115
1248
|
# Uses SQL function with multiple arguments.
|
1116
1249
|
(order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
|
1117
1250
|
# Uses "nulls first" like construction.
|
1118
|
-
|
1251
|
+
/\bnulls\s+(?:first|last)\b/i.match?(order)
|
1119
1252
|
end
|
1120
1253
|
|
1121
1254
|
def build_order(arel)
|
@@ -1141,14 +1274,15 @@ module ActiveRecord
|
|
1141
1274
|
end
|
1142
1275
|
|
1143
1276
|
def preprocess_order_args(order_args)
|
1277
|
+
order_args.reject!(&:blank?)
|
1144
1278
|
order_args.map! do |arg|
|
1145
1279
|
klass.sanitize_sql_for_order(arg)
|
1146
1280
|
end
|
1147
1281
|
order_args.flatten!
|
1148
1282
|
|
1149
|
-
@klass.
|
1283
|
+
@klass.disallow_raw_sql!(
|
1150
1284
|
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
|
1151
|
-
|
1285
|
+
permit: connection.column_name_with_order_matcher
|
1152
1286
|
)
|
1153
1287
|
|
1154
1288
|
validate_order_args(order_args)
|
@@ -1161,20 +1295,14 @@ module ActiveRecord
|
|
1161
1295
|
order_args.map! do |arg|
|
1162
1296
|
case arg
|
1163
1297
|
when Symbol
|
1164
|
-
arg
|
1165
|
-
arel_column(arg) {
|
1166
|
-
Arel.sql(connection.quote_table_name(arg))
|
1167
|
-
}.asc
|
1298
|
+
order_column(arg.to_s).asc
|
1168
1299
|
when Hash
|
1169
1300
|
arg.map { |field, dir|
|
1170
1301
|
case field
|
1171
1302
|
when Arel::Nodes::SqlLiteral
|
1172
1303
|
field.send(dir.downcase)
|
1173
1304
|
else
|
1174
|
-
field
|
1175
|
-
arel_column(field) {
|
1176
|
-
Arel.sql(connection.quote_table_name(field))
|
1177
|
-
}.send(dir.downcase)
|
1305
|
+
order_column(field.to_s).send(dir.downcase)
|
1178
1306
|
end
|
1179
1307
|
}
|
1180
1308
|
else
|
@@ -1183,6 +1311,16 @@ module ActiveRecord
|
|
1183
1311
|
end.flatten!
|
1184
1312
|
end
|
1185
1313
|
|
1314
|
+
def order_column(field)
|
1315
|
+
arel_column(field) do |attr_name|
|
1316
|
+
if attr_name == "count" && !group_values.empty?
|
1317
|
+
arel_attribute(attr_name)
|
1318
|
+
else
|
1319
|
+
Arel.sql(connection.quote_table_name(attr_name))
|
1320
|
+
end
|
1321
|
+
end
|
1322
|
+
end
|
1323
|
+
|
1186
1324
|
# Checks to make sure that the arguments are not blank. Note that if some
|
1187
1325
|
# blank-like object were initially passed into the query method, then this
|
1188
1326
|
# method will not raise an error.
|
@@ -1207,8 +1345,10 @@ module ActiveRecord
|
|
1207
1345
|
|
1208
1346
|
STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
|
1209
1347
|
def structurally_incompatible_values_for_or(other)
|
1348
|
+
values = other.values
|
1210
1349
|
STRUCTURAL_OR_METHODS.reject do |method|
|
1211
|
-
|
1350
|
+
default = DEFAULT_VALUES[method]
|
1351
|
+
@values.fetch(method, default) == values.fetch(method, default)
|
1212
1352
|
end
|
1213
1353
|
end
|
1214
1354
|
|