activerecord 5.2.2.1 → 6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +734 -508
- 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 +16 -28
- data/lib/active_record/associations/collection_proxy.rb +19 -48
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +3 -10
- data/lib/active_record/associations/has_many_through_association.rb +20 -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 +40 -32
- 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 +137 -26
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +114 -130
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +26 -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 +135 -56
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +189 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +151 -198
- 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 +7 -11
- 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 +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -77
- 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 +45 -5
- 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 +104 -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 +30 -16
- 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 +91 -64
- 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 +231 -25
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +33 -22
- 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 +199 -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 +67 -57
- data/lib/active_record/relation/delegation.rb +26 -43
- data/lib/active_record/relation/finder_methods.rb +28 -28
- data/lib/active_record/relation/merger.rb +17 -23
- data/lib/active_record/relation/predicate_builder.rb +18 -15
- 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 +17 -10
- data/lib/active_record/relation/query_methods.rb +247 -73
- 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 +20 -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 +225 -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
|
|
@@ -175,9 +166,7 @@ module ActiveRecord
|
|
175
166
|
end
|
176
167
|
|
177
168
|
def merge_clauses
|
178
|
-
|
179
|
-
relation.from_clause = other.from_clause
|
180
|
-
end
|
169
|
+
relation.from_clause = other.from_clause if replace_from_clause?
|
181
170
|
|
182
171
|
where_clause = relation.where_clause.merge(other.where_clause)
|
183
172
|
relation.where_clause = where_clause unless where_clause.empty?
|
@@ -185,6 +174,11 @@ module ActiveRecord
|
|
185
174
|
having_clause = relation.having_clause.merge(other.having_clause)
|
186
175
|
relation.having_clause = having_clause unless having_clause.empty?
|
187
176
|
end
|
177
|
+
|
178
|
+
def replace_from_clause?
|
179
|
+
relation.from_clause.empty? && !other.from_clause.empty? &&
|
180
|
+
relation.klass.base_class == other.klass.base_class
|
181
|
+
end
|
188
182
|
end
|
189
183
|
end
|
190
184
|
end
|
@@ -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
|
|
@@ -93,16 +90,21 @@ module ActiveRecord
|
|
93
90
|
queries.reduce(&:or)
|
94
91
|
elsif table.aggregated_with?(key)
|
95
92
|
mapping = table.reflect_on_aggregation(key).mapping
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
93
|
+
values = value.nil? ? [nil] : Array.wrap(value)
|
94
|
+
if mapping.length == 1 || values.empty?
|
95
|
+
column_name, aggr_attr = mapping.first
|
96
|
+
values = values.map do |object|
|
97
|
+
object.respond_to?(aggr_attr) ? object.public_send(aggr_attr) : object
|
98
|
+
end
|
99
|
+
build(table.arel_attribute(column_name), values)
|
100
|
+
else
|
101
|
+
queries = values.map do |object|
|
102
|
+
mapping.map do |field_attr, aggregate_attr|
|
103
|
+
build(table.arel_attribute(field_attr), object.try!(aggregate_attr))
|
104
|
+
end.reduce(&:and)
|
105
|
+
end
|
106
|
+
queries.reduce(&:or)
|
104
107
|
end
|
105
|
-
queries.reduce(&:or)
|
106
108
|
else
|
107
109
|
build(table.arel_attribute(key), value)
|
108
110
|
end
|
@@ -110,6 +112,7 @@ module ActiveRecord
|
|
110
112
|
end
|
111
113
|
|
112
114
|
private
|
115
|
+
attr_reader :table
|
113
116
|
|
114
117
|
def associated_predicate_builder(association_name)
|
115
118
|
self.class.new(table.associated_table(association_name))
|
@@ -117,11 +120,11 @@ module ActiveRecord
|
|
117
120
|
|
118
121
|
def convert_dot_notation_to_hash(attributes)
|
119
122
|
dot_notation = attributes.select do |k, v|
|
120
|
-
k.include?("."
|
123
|
+
k.include?(".") && !v.is_a?(Hash)
|
121
124
|
end
|
122
125
|
|
123
126
|
dot_notation.each_key do |key|
|
124
|
-
table_name, column_name = key.split("."
|
127
|
+
table_name, column_name = key.split(".")
|
125
128
|
value = attributes.delete(key)
|
126
129
|
attributes[table_name] ||= {}
|
127
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
|
@@ -18,24 +18,31 @@ module ActiveRecord
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def nil?
|
21
|
-
|
22
|
-
|
21
|
+
unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
22
|
+
value_before_type_cast.nil? ||
|
23
|
+
type.respond_to?(:subtype, true) && value_for_database.nil?
|
24
|
+
end
|
25
|
+
rescue ::RangeError
|
23
26
|
end
|
24
27
|
|
25
|
-
def
|
26
|
-
|
27
|
-
nil?
|
28
|
-
@_boundable = true
|
28
|
+
def infinite?
|
29
|
+
infinity?(value_before_type_cast) || infinity?(value_for_database)
|
29
30
|
rescue ::RangeError
|
30
|
-
@_boundable = false
|
31
31
|
end
|
32
32
|
|
33
|
-
def
|
34
|
-
|
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
|
35
42
|
end
|
36
43
|
|
37
44
|
private
|
38
|
-
def
|
45
|
+
def infinity?(value)
|
39
46
|
value.respond_to?(:infinite?) && value.infinite?
|
40
47
|
end
|
41
48
|
end
|
@@ -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.
|
@@ -231,14 +276,33 @@ module ActiveRecord
|
|
231
276
|
end
|
232
277
|
|
233
278
|
def _select!(*fields) # :nodoc:
|
279
|
+
fields.reject!(&:blank?)
|
234
280
|
fields.flatten!
|
235
|
-
fields.map! do |field|
|
236
|
-
klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
|
237
|
-
end
|
238
281
|
self.select_values += fields
|
239
282
|
self
|
240
283
|
end
|
241
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
|
+
|
242
306
|
# Allows to specify a group attribute:
|
243
307
|
#
|
244
308
|
# User.group(:name)
|
@@ -319,7 +383,7 @@ module ActiveRecord
|
|
319
383
|
|
320
384
|
# Same as #reorder but operates on relation in-place instead of copying.
|
321
385
|
def reorder!(*args) # :nodoc:
|
322
|
-
preprocess_order_args(args)
|
386
|
+
preprocess_order_args(args) unless args.all?(&:blank?)
|
323
387
|
|
324
388
|
self.reordering_value = true
|
325
389
|
self.order_values = args
|
@@ -327,8 +391,8 @@ module ActiveRecord
|
|
327
391
|
end
|
328
392
|
|
329
393
|
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
|
330
|
-
:limit, :offset, :joins, :left_outer_joins,
|
331
|
-
:includes, :from, :readonly, :having])
|
394
|
+
:limit, :offset, :joins, :left_outer_joins, :annotate,
|
395
|
+
:includes, :from, :readonly, :having, :optimizer_hints])
|
332
396
|
|
333
397
|
# Removes an unwanted relation that is already defined on a chain of relations.
|
334
398
|
# This is useful when passing around chains of relations and would like to
|
@@ -379,7 +443,8 @@ module ActiveRecord
|
|
379
443
|
if !VALID_UNSCOPING_VALUES.include?(scope)
|
380
444
|
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
381
445
|
end
|
382
|
-
|
446
|
+
assert_mutability!
|
447
|
+
@values[scope] = DEFAULT_VALUES[scope]
|
383
448
|
when Hash
|
384
449
|
scope.each do |key, target_value|
|
385
450
|
if key != :where
|
@@ -879,6 +944,29 @@ module ActiveRecord
|
|
879
944
|
self
|
880
945
|
end
|
881
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
|
+
|
882
970
|
# Reverse the existing order clause on the relation.
|
883
971
|
#
|
884
972
|
# User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
|
@@ -898,26 +986,52 @@ module ActiveRecord
|
|
898
986
|
self
|
899
987
|
end
|
900
988
|
|
989
|
+
def skip_preloading! # :nodoc:
|
990
|
+
self.skip_preloading_value = true
|
991
|
+
self
|
992
|
+
end
|
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
|
+
|
901
1014
|
# Returns the Arel object associated with the relation.
|
902
1015
|
def arel(aliases = nil) # :nodoc:
|
903
1016
|
@arel ||= build_arel(aliases)
|
904
1017
|
end
|
905
1018
|
|
906
|
-
|
907
|
-
|
908
|
-
|
1019
|
+
def construct_join_dependency(associations, join_type) # :nodoc:
|
1020
|
+
ActiveRecord::Associations::JoinDependency.new(
|
1021
|
+
klass, table, associations, join_type
|
1022
|
+
)
|
909
1023
|
end
|
910
1024
|
|
911
1025
|
protected
|
1026
|
+
def build_subquery(subquery_alias, select_value) # :nodoc:
|
1027
|
+
subquery = except(:optimizer_hints).arel.as(subquery_alias)
|
912
1028
|
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
@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
|
917
1032
|
end
|
918
1033
|
|
919
1034
|
private
|
920
|
-
|
921
1035
|
def assert_mutability!
|
922
1036
|
raise ImmutableRelation if @loaded
|
923
1037
|
raise ImmutableRelation if defined?(@arel) && @arel
|
@@ -926,14 +1040,17 @@ module ActiveRecord
|
|
926
1040
|
def build_arel(aliases)
|
927
1041
|
arel = Arel::SelectManager.new(table)
|
928
1042
|
|
929
|
-
|
930
|
-
|
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
|
931
1048
|
|
932
1049
|
arel.where(where_clause.ast) unless where_clause.empty?
|
933
1050
|
arel.having(having_clause.ast) unless having_clause.empty?
|
934
1051
|
if limit_value
|
935
1052
|
limit_attribute = ActiveModel::Attribute.with_cast_value(
|
936
|
-
"LIMIT"
|
1053
|
+
"LIMIT",
|
937
1054
|
connection.sanitize_limit(limit_value),
|
938
1055
|
Type.default_value,
|
939
1056
|
)
|
@@ -941,7 +1058,7 @@ module ActiveRecord
|
|
941
1058
|
end
|
942
1059
|
if offset_value
|
943
1060
|
offset_attribute = ActiveModel::Attribute.with_cast_value(
|
944
|
-
"OFFSET"
|
1061
|
+
"OFFSET",
|
945
1062
|
offset_value.to_i,
|
946
1063
|
Type.default_value,
|
947
1064
|
)
|
@@ -953,9 +1070,11 @@ module ActiveRecord
|
|
953
1070
|
|
954
1071
|
build_select(arel)
|
955
1072
|
|
1073
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
956
1074
|
arel.distinct(distinct_value)
|
957
1075
|
arel.from(build_from) unless from_clause.empty?
|
958
1076
|
arel.lock(lock_value) if lock_value
|
1077
|
+
arel.comment(*annotate_values) unless annotate_values.empty?
|
959
1078
|
|
960
1079
|
arel
|
961
1080
|
end
|
@@ -975,32 +1094,68 @@ module ActiveRecord
|
|
975
1094
|
end
|
976
1095
|
end
|
977
1096
|
|
978
|
-
def
|
979
|
-
|
980
|
-
|
1097
|
+
def select_association_list(associations)
|
1098
|
+
result = []
|
1099
|
+
associations.each do |association|
|
1100
|
+
case association
|
981
1101
|
when Hash, Symbol, Array
|
982
|
-
|
983
|
-
when ActiveRecord::Associations::JoinDependency
|
984
|
-
:stashed_join
|
1102
|
+
result << association
|
985
1103
|
else
|
986
|
-
|
1104
|
+
yield if block_given?
|
987
1105
|
end
|
988
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
|
989
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)
|
990
1119
|
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
991
1120
|
end
|
992
1121
|
|
993
1122
|
def build_joins(manager, joins, aliases)
|
994
|
-
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|
|
995
1152
|
case join
|
996
|
-
when String
|
997
|
-
:string_join
|
998
1153
|
when Hash, Symbol, Array
|
999
|
-
:association_join
|
1154
|
+
buckets[:association_join] << join
|
1000
1155
|
when ActiveRecord::Associations::JoinDependency
|
1001
|
-
:stashed_join
|
1156
|
+
buckets[:stashed_join] << join
|
1002
1157
|
when Arel::Nodes::Join
|
1003
|
-
:join_node
|
1158
|
+
buckets[:join_node] << join
|
1004
1159
|
else
|
1005
1160
|
raise "unknown class: %s" % join.class.name
|
1006
1161
|
end
|
@@ -1010,33 +1165,21 @@ module ActiveRecord
|
|
1010
1165
|
end
|
1011
1166
|
|
1012
1167
|
def build_join_query(manager, buckets, join_type, aliases)
|
1013
|
-
buckets.default = []
|
1014
|
-
|
1015
1168
|
association_joins = buckets[:association_join]
|
1016
1169
|
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) }
|
1170
|
+
leading_joins = buckets[:leading_join]
|
1171
|
+
join_nodes = buckets[:join_node]
|
1029
1172
|
|
1030
|
-
manager.join_sources
|
1173
|
+
join_sources = manager.join_sources
|
1174
|
+
join_sources.concat(leading_joins) unless leading_joins.empty?
|
1031
1175
|
|
1032
|
-
|
1033
|
-
|
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
|
1034
1181
|
|
1035
|
-
|
1036
|
-
joins
|
1037
|
-
.flatten
|
1038
|
-
.reject(&:blank?)
|
1039
|
-
.map { |join| table.create_string_join(Arel.sql(join)) }
|
1182
|
+
join_sources.concat(join_nodes) unless join_nodes.empty?
|
1040
1183
|
end
|
1041
1184
|
|
1042
1185
|
def build_select(arel)
|
@@ -1051,11 +1194,14 @@ module ActiveRecord
|
|
1051
1194
|
|
1052
1195
|
def arel_columns(columns)
|
1053
1196
|
columns.flat_map do |field|
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1197
|
+
case field
|
1198
|
+
when Symbol
|
1199
|
+
arel_column(field.to_s) do |attr_name|
|
1200
|
+
connection.quote_table_name(attr_name)
|
1201
|
+
end
|
1202
|
+
when String
|
1203
|
+
arel_column(field, &:itself)
|
1204
|
+
when Proc
|
1059
1205
|
field.call
|
1060
1206
|
else
|
1061
1207
|
field
|
@@ -1063,6 +1209,21 @@ module ActiveRecord
|
|
1063
1209
|
end
|
1064
1210
|
end
|
1065
1211
|
|
1212
|
+
def arel_column(field)
|
1213
|
+
field = klass.attribute_aliases[field] || field
|
1214
|
+
from = from_clause.name || from_clause.value
|
1215
|
+
|
1216
|
+
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
1217
|
+
arel_attribute(field)
|
1218
|
+
else
|
1219
|
+
yield field
|
1220
|
+
end
|
1221
|
+
end
|
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
|
+
|
1066
1227
|
def reverse_sql_order(order_query)
|
1067
1228
|
if order_query.empty?
|
1068
1229
|
return [arel_attribute(primary_key).desc] if primary_key
|
@@ -1078,7 +1239,7 @@ module ActiveRecord
|
|
1078
1239
|
o.reverse
|
1079
1240
|
when String
|
1080
1241
|
if does_not_support_reverse?(o)
|
1081
|
-
raise IrreversibleOrderError, "Order #{o.inspect}
|
1242
|
+
raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
|
1082
1243
|
end
|
1083
1244
|
o.split(",").map! do |s|
|
1084
1245
|
s.strip!
|
@@ -1098,7 +1259,7 @@ module ActiveRecord
|
|
1098
1259
|
# Uses SQL function with multiple arguments.
|
1099
1260
|
(order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
|
1100
1261
|
# Uses "nulls first" like construction.
|
1101
|
-
|
1262
|
+
/\bnulls\s+(?:first|last)\b/i.match?(order)
|
1102
1263
|
end
|
1103
1264
|
|
1104
1265
|
def build_order(arel)
|
@@ -1124,14 +1285,15 @@ module ActiveRecord
|
|
1124
1285
|
end
|
1125
1286
|
|
1126
1287
|
def preprocess_order_args(order_args)
|
1288
|
+
order_args.reject!(&:blank?)
|
1127
1289
|
order_args.map! do |arg|
|
1128
1290
|
klass.sanitize_sql_for_order(arg)
|
1129
1291
|
end
|
1130
1292
|
order_args.flatten!
|
1131
1293
|
|
1132
|
-
@klass.
|
1294
|
+
@klass.disallow_raw_sql!(
|
1133
1295
|
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
|
1134
|
-
|
1296
|
+
permit: connection.column_name_with_order_matcher
|
1135
1297
|
)
|
1136
1298
|
|
1137
1299
|
validate_order_args(order_args)
|
@@ -1144,14 +1306,14 @@ module ActiveRecord
|
|
1144
1306
|
order_args.map! do |arg|
|
1145
1307
|
case arg
|
1146
1308
|
when Symbol
|
1147
|
-
|
1309
|
+
order_column(arg.to_s).asc
|
1148
1310
|
when Hash
|
1149
1311
|
arg.map { |field, dir|
|
1150
1312
|
case field
|
1151
1313
|
when Arel::Nodes::SqlLiteral
|
1152
1314
|
field.send(dir.downcase)
|
1153
1315
|
else
|
1154
|
-
|
1316
|
+
order_column(field.to_s).send(dir.downcase)
|
1155
1317
|
end
|
1156
1318
|
}
|
1157
1319
|
else
|
@@ -1160,6 +1322,16 @@ module ActiveRecord
|
|
1160
1322
|
end.flatten!
|
1161
1323
|
end
|
1162
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
|
+
|
1163
1335
|
# Checks to make sure that the arguments are not blank. Note that if some
|
1164
1336
|
# blank-like object were initially passed into the query method, then this
|
1165
1337
|
# method will not raise an error.
|
@@ -1184,8 +1356,10 @@ module ActiveRecord
|
|
1184
1356
|
|
1185
1357
|
STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
|
1186
1358
|
def structurally_incompatible_values_for_or(other)
|
1359
|
+
values = other.values
|
1187
1360
|
STRUCTURAL_OR_METHODS.reject do |method|
|
1188
|
-
|
1361
|
+
default = DEFAULT_VALUES[method]
|
1362
|
+
@values.fetch(method, default) == values.fetch(method, default)
|
1189
1363
|
end
|
1190
1364
|
end
|
1191
1365
|
|