activerecord 5.2.1.1 → 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 +738 -445
- 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 +18 -9
- data/lib/active_record/associations.rb +20 -15
- data/lib/active_record/associations/association.rb +69 -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 +15 -29
- 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 +11 -10
- data/lib/active_record/associations/has_many_through_association.rb +42 -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 +114 -38
- 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 +27 -13
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +6 -20
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +140 -27
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +22 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +116 -127
- 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 +55 -45
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +9 -4
- 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 +8 -2
- 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 +65 -77
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql/utils.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +172 -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 +38 -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 +34 -10
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +233 -28
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +33 -21
- data/lib/active_record/railtie.rb +81 -46
- 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 +320 -70
- 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 +48 -35
- data/lib/active_record/relation/finder_methods.rb +30 -30
- data/lib/active_record/relation/merger.rb +19 -25
- data/lib/active_record/relation/predicate_builder.rb +18 -15
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -6
- 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 +236 -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 +21 -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 +195 -26
- 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 -27
- 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,28 +136,21 @@ 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
|
|
156
147
|
def merge_multi_values
|
157
148
|
if other.reordering_value
|
158
149
|
# override any order specified in the original relation
|
159
|
-
relation.reorder!
|
150
|
+
relation.reorder!(*other.order_values)
|
160
151
|
elsif other.order_values.any?
|
161
152
|
# merge in order_values from relation
|
162
|
-
relation.order!
|
153
|
+
relation.order!(*other.order_values)
|
163
154
|
end
|
164
155
|
|
165
156
|
extensions = other.extensions - relation.extensions
|
@@ -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,18 +13,18 @@ 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
|
19
21
|
when 0 then NullPredicate
|
20
22
|
when 1 then predicate_builder.build(attribute, values.first)
|
21
23
|
else
|
22
|
-
|
24
|
+
values.map! do |v|
|
23
25
|
predicate_builder.build_bind_attribute(attribute.name, v)
|
24
26
|
end
|
25
|
-
attribute.in(
|
27
|
+
values.empty? ? NullPredicate : attribute.in(values)
|
26
28
|
end
|
27
29
|
|
28
30
|
unless nils.empty?
|
@@ -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,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
|
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
|
989
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)
|
@@ -1051,11 +1183,14 @@ module ActiveRecord
|
|
1051
1183
|
|
1052
1184
|
def arel_columns(columns)
|
1053
1185
|
columns.flat_map do |field|
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1186
|
+
case field
|
1187
|
+
when Symbol
|
1188
|
+
arel_column(field.to_s) do |attr_name|
|
1189
|
+
connection.quote_table_name(attr_name)
|
1190
|
+
end
|
1191
|
+
when String
|
1192
|
+
arel_column(field, &:itself)
|
1193
|
+
when Proc
|
1059
1194
|
field.call
|
1060
1195
|
else
|
1061
1196
|
field
|
@@ -1063,6 +1198,21 @@ module ActiveRecord
|
|
1063
1198
|
end
|
1064
1199
|
end
|
1065
1200
|
|
1201
|
+
def arel_column(field)
|
1202
|
+
field = klass.attribute_aliases[field] || field
|
1203
|
+
from = from_clause.name || from_clause.value
|
1204
|
+
|
1205
|
+
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
1206
|
+
arel_attribute(field)
|
1207
|
+
else
|
1208
|
+
yield field
|
1209
|
+
end
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
def table_name_matches?(from)
|
1213
|
+
/(?:\A|(?<!FROM)\s)(?:\b#{table.name}\b|#{connection.quote_table_name(table.name)})(?!\.)/i.match?(from.to_s)
|
1214
|
+
end
|
1215
|
+
|
1066
1216
|
def reverse_sql_order(order_query)
|
1067
1217
|
if order_query.empty?
|
1068
1218
|
return [arel_attribute(primary_key).desc] if primary_key
|
@@ -1078,7 +1228,7 @@ module ActiveRecord
|
|
1078
1228
|
o.reverse
|
1079
1229
|
when String
|
1080
1230
|
if does_not_support_reverse?(o)
|
1081
|
-
raise IrreversibleOrderError, "Order #{o.inspect}
|
1231
|
+
raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
|
1082
1232
|
end
|
1083
1233
|
o.split(",").map! do |s|
|
1084
1234
|
s.strip!
|
@@ -1098,7 +1248,7 @@ module ActiveRecord
|
|
1098
1248
|
# Uses SQL function with multiple arguments.
|
1099
1249
|
(order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
|
1100
1250
|
# Uses "nulls first" like construction.
|
1101
|
-
|
1251
|
+
/\bnulls\s+(?:first|last)\b/i.match?(order)
|
1102
1252
|
end
|
1103
1253
|
|
1104
1254
|
def build_order(arel)
|
@@ -1124,14 +1274,15 @@ module ActiveRecord
|
|
1124
1274
|
end
|
1125
1275
|
|
1126
1276
|
def preprocess_order_args(order_args)
|
1277
|
+
order_args.reject!(&:blank?)
|
1127
1278
|
order_args.map! do |arg|
|
1128
1279
|
klass.sanitize_sql_for_order(arg)
|
1129
1280
|
end
|
1130
1281
|
order_args.flatten!
|
1131
1282
|
|
1132
|
-
@klass.
|
1283
|
+
@klass.disallow_raw_sql!(
|
1133
1284
|
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
|
1134
|
-
|
1285
|
+
permit: connection.column_name_with_order_matcher
|
1135
1286
|
)
|
1136
1287
|
|
1137
1288
|
validate_order_args(order_args)
|
@@ -1144,14 +1295,14 @@ module ActiveRecord
|
|
1144
1295
|
order_args.map! do |arg|
|
1145
1296
|
case arg
|
1146
1297
|
when Symbol
|
1147
|
-
|
1298
|
+
order_column(arg.to_s).asc
|
1148
1299
|
when Hash
|
1149
1300
|
arg.map { |field, dir|
|
1150
1301
|
case field
|
1151
1302
|
when Arel::Nodes::SqlLiteral
|
1152
1303
|
field.send(dir.downcase)
|
1153
1304
|
else
|
1154
|
-
|
1305
|
+
order_column(field.to_s).send(dir.downcase)
|
1155
1306
|
end
|
1156
1307
|
}
|
1157
1308
|
else
|
@@ -1160,6 +1311,16 @@ module ActiveRecord
|
|
1160
1311
|
end.flatten!
|
1161
1312
|
end
|
1162
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
|
+
|
1163
1324
|
# Checks to make sure that the arguments are not blank. Note that if some
|
1164
1325
|
# blank-like object were initially passed into the query method, then this
|
1165
1326
|
# method will not raise an error.
|
@@ -1184,8 +1345,10 @@ module ActiveRecord
|
|
1184
1345
|
|
1185
1346
|
STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
|
1186
1347
|
def structurally_incompatible_values_for_or(other)
|
1348
|
+
values = other.values
|
1187
1349
|
STRUCTURAL_OR_METHODS.reject do |method|
|
1188
|
-
|
1350
|
+
default = DEFAULT_VALUES[method]
|
1351
|
+
@values.fetch(method, default) == values.fetch(method, default)
|
1189
1352
|
end
|
1190
1353
|
end
|
1191
1354
|
|