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
@@ -251,25 +251,28 @@ module ActiveRecord
|
|
251
251
|
end
|
252
252
|
end
|
253
253
|
|
254
|
-
|
255
|
-
|
254
|
+
batch_relation = relation.where(
|
255
|
+
bind_attribute(primary_key, primary_key_offset) { |attr, bind| attr.gt(bind) }
|
256
|
+
)
|
256
257
|
end
|
257
258
|
end
|
258
259
|
|
259
260
|
private
|
260
261
|
|
261
262
|
def apply_limits(relation, start, finish)
|
262
|
-
if start
|
263
|
-
|
264
|
-
relation = relation.where(arel_attribute(primary_key).gteq(Arel::Nodes::BindParam.new(attr)))
|
265
|
-
end
|
266
|
-
if finish
|
267
|
-
attr = Relation::QueryAttribute.new(primary_key, finish, klass.type_for_attribute(primary_key))
|
268
|
-
relation = relation.where(arel_attribute(primary_key).lteq(Arel::Nodes::BindParam.new(attr)))
|
269
|
-
end
|
263
|
+
relation = apply_start_limit(relation, start) if start
|
264
|
+
relation = apply_finish_limit(relation, finish) if finish
|
270
265
|
relation
|
271
266
|
end
|
272
267
|
|
268
|
+
def apply_start_limit(relation, start)
|
269
|
+
relation.where(bind_attribute(primary_key, start) { |attr, bind| attr.gteq(bind) })
|
270
|
+
end
|
271
|
+
|
272
|
+
def apply_finish_limit(relation, finish)
|
273
|
+
relation.where(bind_attribute(primary_key, finish) { |attr, bind| attr.lteq(bind) })
|
274
|
+
end
|
275
|
+
|
273
276
|
def batch_order
|
274
277
|
arel_attribute(primary_key).asc
|
275
278
|
end
|
@@ -41,15 +41,13 @@ module ActiveRecord
|
|
41
41
|
def count(column_name = nil)
|
42
42
|
if block_given?
|
43
43
|
unless column_name.nil?
|
44
|
-
|
45
|
-
"When `count' is called with a block, it ignores other arguments. " \
|
46
|
-
"This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
|
44
|
+
raise ArgumentError, "Column name argument is not supported when a block is passed."
|
47
45
|
end
|
48
46
|
|
49
|
-
|
47
|
+
super()
|
48
|
+
else
|
49
|
+
calculate(:count, column_name)
|
50
50
|
end
|
51
|
-
|
52
|
-
calculate(:count, column_name)
|
53
51
|
end
|
54
52
|
|
55
53
|
# Calculates the average value on a given column. Returns +nil+ if there's
|
@@ -86,15 +84,13 @@ module ActiveRecord
|
|
86
84
|
def sum(column_name = nil)
|
87
85
|
if block_given?
|
88
86
|
unless column_name.nil?
|
89
|
-
|
90
|
-
"When `sum' is called with a block, it ignores other arguments. " \
|
91
|
-
"This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
|
87
|
+
raise ArgumentError, "Column name argument is not supported when a block is passed."
|
92
88
|
end
|
93
89
|
|
94
|
-
|
90
|
+
super()
|
91
|
+
else
|
92
|
+
calculate(:sum, column_name)
|
95
93
|
end
|
96
|
-
|
97
|
-
calculate(:sum, column_name)
|
98
94
|
end
|
99
95
|
|
100
96
|
# This calculates aggregate values in the given column. Methods for #count, #sum, #average,
|
@@ -133,11 +129,12 @@ module ActiveRecord
|
|
133
129
|
relation = apply_join_dependency
|
134
130
|
|
135
131
|
if operation.to_s.downcase == "count"
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
relation.order_values = []
|
132
|
+
unless distinct_value || distinct_select?(column_name || select_for_count)
|
133
|
+
relation.distinct!
|
134
|
+
relation.select_values = [ klass.primary_key || table[Arel.star] ]
|
140
135
|
end
|
136
|
+
# PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
|
137
|
+
relation.order_values = []
|
141
138
|
end
|
142
139
|
|
143
140
|
relation.calculate(operation, column_name)
|
@@ -190,16 +187,32 @@ module ActiveRecord
|
|
190
187
|
relation = apply_join_dependency
|
191
188
|
relation.pluck(*column_names)
|
192
189
|
else
|
193
|
-
|
190
|
+
klass.disallow_raw_sql!(column_names)
|
194
191
|
relation = spawn
|
195
|
-
relation.select_values = column_names
|
196
|
-
@klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
|
197
|
-
}
|
192
|
+
relation.select_values = column_names
|
198
193
|
result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
|
199
194
|
result.cast_values(klass.attribute_types)
|
200
195
|
end
|
201
196
|
end
|
202
197
|
|
198
|
+
# Pick the value(s) from the named column(s) in the current relation.
|
199
|
+
# This is short-hand for <tt>relation.limit(1).pluck(*column_names).first</tt>, and is primarily useful
|
200
|
+
# when you have a relation that's already narrowed down to a single row.
|
201
|
+
#
|
202
|
+
# Just like #pluck, #pick will only load the actual value, not the entire record object, so it's also
|
203
|
+
# more efficient. The value is, again like with pluck, typecast by the column type.
|
204
|
+
#
|
205
|
+
# Person.where(id: 1).pick(:name)
|
206
|
+
# # SELECT people.name FROM people WHERE id = 1 LIMIT 1
|
207
|
+
# # => 'David'
|
208
|
+
#
|
209
|
+
# Person.where(id: 1).pick(:name, :email_address)
|
210
|
+
# # SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1
|
211
|
+
# # => [ 'David', 'david@loudthinking.com' ]
|
212
|
+
def pick(*column_names)
|
213
|
+
limit(1).pluck(*column_names).first
|
214
|
+
end
|
215
|
+
|
203
216
|
# Pluck all the ID's for the relation using the table's primary key
|
204
217
|
#
|
205
218
|
# Person.ids # SELECT people.id FROM people
|
@@ -209,7 +222,6 @@ module ActiveRecord
|
|
209
222
|
end
|
210
223
|
|
211
224
|
private
|
212
|
-
|
213
225
|
def has_include?(column_name)
|
214
226
|
eager_loading? || (includes_values.present? && column_name && column_name != :all)
|
215
227
|
end
|
@@ -224,10 +236,12 @@ module ActiveRecord
|
|
224
236
|
if operation == "count"
|
225
237
|
column_name ||= select_for_count
|
226
238
|
if column_name == :all
|
227
|
-
if distinct
|
239
|
+
if !distinct
|
240
|
+
distinct = distinct_select?(select_for_count) if group_values.empty?
|
241
|
+
elsif group_values.any? || select_values.empty? && order_values.empty?
|
228
242
|
column_name = primary_key
|
229
243
|
end
|
230
|
-
elsif column_name
|
244
|
+
elsif distinct_select?(column_name)
|
231
245
|
distinct = nil
|
232
246
|
end
|
233
247
|
end
|
@@ -239,13 +253,15 @@ module ActiveRecord
|
|
239
253
|
end
|
240
254
|
end
|
241
255
|
|
256
|
+
def distinct_select?(column_name)
|
257
|
+
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
|
258
|
+
end
|
259
|
+
|
242
260
|
def aggregate_column(column_name)
|
243
261
|
return column_name if Arel::Expressions === column_name
|
244
262
|
|
245
|
-
|
246
|
-
|
247
|
-
else
|
248
|
-
Arel.sql(column_name == :all ? "*" : column_name.to_s)
|
263
|
+
arel_column(column_name.to_s) do |name|
|
264
|
+
Arel.sql(column_name == :all ? "*" : name)
|
249
265
|
end
|
250
266
|
end
|
251
267
|
|
@@ -290,25 +306,22 @@ module ActiveRecord
|
|
290
306
|
end
|
291
307
|
|
292
308
|
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
293
|
-
|
309
|
+
group_fields = group_values
|
294
310
|
|
295
|
-
if
|
296
|
-
association =
|
297
|
-
associated =
|
298
|
-
group_fields = Array(
|
299
|
-
else
|
300
|
-
group_fields = group_attrs
|
311
|
+
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
|
312
|
+
association = klass._reflect_on_association(group_fields.first)
|
313
|
+
associated = association && association.belongs_to? # only count belongs_to associations
|
314
|
+
group_fields = Array(association.foreign_key) if associated
|
301
315
|
end
|
302
316
|
group_fields = arel_columns(group_fields)
|
303
317
|
|
304
|
-
group_aliases = group_fields.map { |field|
|
318
|
+
group_aliases = group_fields.map { |field|
|
319
|
+
field = connection.visitor.compile(field) if Arel.arel_node?(field)
|
320
|
+
column_alias_for(field.to_s.downcase)
|
321
|
+
}
|
305
322
|
group_columns = group_aliases.zip(group_fields)
|
306
323
|
|
307
|
-
|
308
|
-
aggregate_alias = "count_all"
|
309
|
-
else
|
310
|
-
aggregate_alias = column_alias_for([operation, column_name].join(" "))
|
311
|
-
end
|
324
|
+
aggregate_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
|
312
325
|
|
313
326
|
select_values = [
|
314
327
|
operation_over_aggregate_column(
|
@@ -353,25 +366,21 @@ module ActiveRecord
|
|
353
366
|
end]
|
354
367
|
end
|
355
368
|
|
356
|
-
# Converts the given
|
369
|
+
# Converts the given field to the value that the database adapter returns as
|
357
370
|
# a usable column name:
|
358
371
|
#
|
359
372
|
# column_alias_for("users.id") # => "users_id"
|
360
373
|
# column_alias_for("sum(id)") # => "sum_id"
|
361
374
|
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
362
375
|
# column_alias_for("count(*)") # => "count_all"
|
363
|
-
def column_alias_for(
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
table_name.strip!
|
372
|
-
table_name.gsub!(/ +/, "_")
|
373
|
-
|
374
|
-
@klass.connection.table_alias_for(table_name)
|
376
|
+
def column_alias_for(field)
|
377
|
+
column_alias = +field
|
378
|
+
column_alias.gsub!(/\*/, "all")
|
379
|
+
column_alias.gsub!(/\W+/, " ")
|
380
|
+
column_alias.strip!
|
381
|
+
column_alias.gsub!(/ +/, "_")
|
382
|
+
|
383
|
+
connection.table_alias_for(column_alias)
|
375
384
|
end
|
376
385
|
|
377
386
|
def type_for(field, &block)
|
@@ -383,7 +392,7 @@ module ActiveRecord
|
|
383
392
|
case operation
|
384
393
|
when "count" then value.to_i
|
385
394
|
when "sum" then type.deserialize(value || 0)
|
386
|
-
when "average" then value
|
395
|
+
when "average" then value&.respond_to?(:to_d) ? value.to_d : value
|
387
396
|
else type.deserialize(value)
|
388
397
|
end
|
389
398
|
end
|
@@ -399,16 +408,17 @@ module ActiveRecord
|
|
399
408
|
|
400
409
|
def build_count_subquery(relation, column_name, distinct)
|
401
410
|
if column_name == :all
|
411
|
+
column_alias = Arel.star
|
402
412
|
relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
|
403
413
|
else
|
404
414
|
column_alias = Arel.sql("count_column")
|
405
415
|
relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
|
406
416
|
end
|
407
417
|
|
408
|
-
|
409
|
-
select_value = operation_over_aggregate_column(column_alias
|
418
|
+
subquery_alias = Arel.sql("subquery_for_count")
|
419
|
+
select_value = operation_over_aggregate_column(column_alias, "count", false)
|
410
420
|
|
411
|
-
|
421
|
+
relation.build_subquery(subquery_alias, select_value)
|
412
422
|
end
|
413
423
|
end
|
414
424
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "mutex_m"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Delegation # :nodoc:
|
5
7
|
module DelegateCache # :nodoc:
|
@@ -17,7 +19,8 @@ module ActiveRecord
|
|
17
19
|
delegate = Class.new(klass) {
|
18
20
|
include ClassSpecificRelation
|
19
21
|
}
|
20
|
-
|
22
|
+
include_relation_methods(delegate)
|
23
|
+
mangled_name = klass.name.gsub("::", "_")
|
21
24
|
const_set mangled_name, delegate
|
22
25
|
private_constant mangled_name
|
23
26
|
|
@@ -29,7 +32,48 @@ module ActiveRecord
|
|
29
32
|
child_class.initialize_relation_delegate_cache
|
30
33
|
super
|
31
34
|
end
|
35
|
+
|
36
|
+
def generate_relation_method(method)
|
37
|
+
generated_relation_methods.generate_method(method)
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
def include_relation_methods(delegate)
|
42
|
+
superclass.include_relation_methods(delegate) unless base_class?
|
43
|
+
delegate.include generated_relation_methods
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def generated_relation_methods
|
48
|
+
@generated_relation_methods ||= GeneratedRelationMethods.new.tap do |mod|
|
49
|
+
const_set(:GeneratedRelationMethods, mod)
|
50
|
+
private_constant :GeneratedRelationMethods
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class GeneratedRelationMethods < Module # :nodoc:
|
56
|
+
include Mutex_m
|
57
|
+
|
58
|
+
def generate_method(method)
|
59
|
+
synchronize do
|
60
|
+
return if method_defined?(method)
|
61
|
+
|
62
|
+
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
|
63
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
64
|
+
def #{method}(*args, &block)
|
65
|
+
scoping { klass.#{method}(*args, &block) }
|
66
|
+
end
|
67
|
+
RUBY
|
68
|
+
else
|
69
|
+
define_method(method) do |*args, &block|
|
70
|
+
scoping { klass.public_send(method, *args, &block) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
32
75
|
end
|
76
|
+
private_constant :GeneratedRelationMethods
|
33
77
|
|
34
78
|
extend ActiveSupport::Concern
|
35
79
|
|
@@ -38,7 +82,7 @@ module ActiveRecord
|
|
38
82
|
# may vary depending on the klass of a relation, so we create a subclass of Relation
|
39
83
|
# for each different klass, and the delegations are compiled into that subclass only.
|
40
84
|
|
41
|
-
delegate :to_xml, :encode_with, :length, :each, :
|
85
|
+
delegate :to_xml, :encode_with, :length, :each, :join,
|
42
86
|
:[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
|
43
87
|
:to_sentence, :to_formatted_s, :as_json,
|
44
88
|
:shuffle, :split, :slice, :index, :rindex, to: :records
|
@@ -48,49 +92,18 @@ module ActiveRecord
|
|
48
92
|
module ClassSpecificRelation # :nodoc:
|
49
93
|
extend ActiveSupport::Concern
|
50
94
|
|
51
|
-
included do
|
52
|
-
@delegation_mutex = Mutex.new
|
53
|
-
end
|
54
|
-
|
55
95
|
module ClassMethods # :nodoc:
|
56
96
|
def name
|
57
97
|
superclass.name
|
58
98
|
end
|
59
|
-
|
60
|
-
def delegate_to_scoped_klass(method)
|
61
|
-
@delegation_mutex.synchronize do
|
62
|
-
return if method_defined?(method)
|
63
|
-
|
64
|
-
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
|
65
|
-
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
66
|
-
def #{method}(*args, &block)
|
67
|
-
scoping { @klass.#{method}(*args, &block) }
|
68
|
-
end
|
69
|
-
RUBY
|
70
|
-
else
|
71
|
-
define_method method do |*args, &block|
|
72
|
-
scoping { @klass.public_send(method, *args, &block) }
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
99
|
end
|
78
100
|
|
79
101
|
private
|
80
102
|
|
81
103
|
def method_missing(method, *args, &block)
|
82
104
|
if @klass.respond_to?(method)
|
83
|
-
|
105
|
+
@klass.generate_relation_method(method)
|
84
106
|
scoping { @klass.public_send(method, *args, &block) }
|
85
|
-
elsif @delegate_to_klass && @klass.respond_to?(method, true)
|
86
|
-
ActiveSupport::Deprecation.warn \
|
87
|
-
"Delegating missing #{method} method to #{@klass}. " \
|
88
|
-
"Accessibility of private/protected class methods in :scope is deprecated and will be removed in Rails 6.0."
|
89
|
-
@klass.send(method, *args, &block)
|
90
|
-
elsif arel.respond_to?(method)
|
91
|
-
ActiveSupport::Deprecation.warn \
|
92
|
-
"Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
|
93
|
-
arel.public_send(method, *args, &block)
|
94
107
|
else
|
95
108
|
super
|
96
109
|
end
|
@@ -111,7 +124,7 @@ module ActiveRecord
|
|
111
124
|
|
112
125
|
private
|
113
126
|
def respond_to_missing?(method, _)
|
114
|
-
super || @klass.respond_to?(method)
|
127
|
+
super || @klass.respond_to?(method)
|
115
128
|
end
|
116
129
|
end
|
117
130
|
end
|
@@ -7,8 +7,8 @@ module ActiveRecord
|
|
7
7
|
ONE_AS_ONE = "1 AS one"
|
8
8
|
|
9
9
|
# Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
|
10
|
-
# If one or more records
|
11
|
-
# is an integer, find by id coerces its arguments using +to_i+.
|
10
|
+
# If one or more records cannot be found for the requested ids, then ActiveRecord::RecordNotFound will be raised.
|
11
|
+
# If the primary key is an integer, find by id coerces its arguments by using +to_i+.
|
12
12
|
#
|
13
13
|
# Person.find(1) # returns the object for ID = 1
|
14
14
|
# Person.find("1") # returns the object for ID = 1
|
@@ -79,17 +79,12 @@ module ActiveRecord
|
|
79
79
|
# Post.find_by "published_at < ?", 2.weeks.ago
|
80
80
|
def find_by(arg, *args)
|
81
81
|
where(arg, *args).take
|
82
|
-
rescue ::RangeError
|
83
|
-
nil
|
84
82
|
end
|
85
83
|
|
86
84
|
# Like #find_by, except that if no record is found, raises
|
87
85
|
# an ActiveRecord::RecordNotFound error.
|
88
86
|
def find_by!(arg, *args)
|
89
87
|
where(arg, *args).take!
|
90
|
-
rescue ::RangeError
|
91
|
-
raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
|
92
|
-
@klass.name, @klass.primary_key)
|
93
88
|
end
|
94
89
|
|
95
90
|
# Gives a record (or N records if a parameter is supplied) without any implied
|
@@ -319,9 +314,7 @@ module ActiveRecord
|
|
319
314
|
|
320
315
|
relation = construct_relation_for_exists(conditions)
|
321
316
|
|
322
|
-
skip_query_cache_if_necessary { connection.
|
323
|
-
rescue ::RangeError
|
324
|
-
false
|
317
|
+
skip_query_cache_if_necessary { connection.select_one(relation.arel, "#{name} Exists?") } ? true : false
|
325
318
|
end
|
326
319
|
|
327
320
|
# This method is called whenever no records are found with either a single
|
@@ -338,14 +331,14 @@ module ActiveRecord
|
|
338
331
|
name = @klass.name
|
339
332
|
|
340
333
|
if ids.nil?
|
341
|
-
error = "Couldn't find #{name}"
|
334
|
+
error = +"Couldn't find #{name}"
|
342
335
|
error << " with#{conditions}" if conditions
|
343
336
|
raise RecordNotFound.new(error, name, key)
|
344
337
|
elsif Array(ids).size == 1
|
345
338
|
error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
|
346
339
|
raise RecordNotFound.new(error, name, key, ids)
|
347
340
|
else
|
348
|
-
error = "Couldn't find all #{name.pluralize} with '#{key}': "
|
341
|
+
error = +"Couldn't find all #{name.pluralize} with '#{key}': "
|
349
342
|
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})."
|
350
343
|
error << " Couldn't find #{name.pluralize(not_found_ids.size)} with #{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.join(', ')}." if not_found_ids
|
351
344
|
raise RecordNotFound.new(error, name, key, ids)
|
@@ -359,11 +352,17 @@ module ActiveRecord
|
|
359
352
|
end
|
360
353
|
|
361
354
|
def construct_relation_for_exists(conditions)
|
362
|
-
|
355
|
+
conditions = sanitize_forbidden_attributes(conditions)
|
356
|
+
|
357
|
+
if distinct_value && offset_value
|
358
|
+
relation = except(:order).limit!(1)
|
359
|
+
else
|
360
|
+
relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
|
361
|
+
end
|
363
362
|
|
364
363
|
case conditions
|
365
364
|
when Array, Hash
|
366
|
-
relation.where!(conditions)
|
365
|
+
relation.where!(conditions) unless conditions.empty?
|
367
366
|
else
|
368
367
|
relation.where!(primary_key => conditions) unless conditions == :none
|
369
368
|
end
|
@@ -371,18 +370,22 @@ module ActiveRecord
|
|
371
370
|
relation
|
372
371
|
end
|
373
372
|
|
374
|
-
def construct_join_dependency
|
375
|
-
including = eager_load_values + includes_values
|
376
|
-
ActiveRecord::Associations::JoinDependency.new(
|
377
|
-
klass, table, including
|
378
|
-
)
|
379
|
-
end
|
380
|
-
|
381
373
|
def apply_join_dependency(eager_loading: group_values.empty?)
|
382
|
-
join_dependency = construct_join_dependency
|
374
|
+
join_dependency = construct_join_dependency(
|
375
|
+
eager_load_values + includes_values, Arel::Nodes::OuterJoin
|
376
|
+
)
|
383
377
|
relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
|
384
378
|
|
385
|
-
if eager_loading && !
|
379
|
+
if eager_loading && !(
|
380
|
+
using_limitable_reflections?(join_dependency.reflections) &&
|
381
|
+
using_limitable_reflections?(
|
382
|
+
construct_join_dependency(
|
383
|
+
select_association_list(joins_values).concat(
|
384
|
+
select_association_list(left_outer_joins_values)
|
385
|
+
), nil
|
386
|
+
).reflections
|
387
|
+
)
|
388
|
+
)
|
386
389
|
if has_limit_or_offset?
|
387
390
|
limited_ids = limited_ids_for(relation)
|
388
391
|
limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
|
@@ -399,7 +402,7 @@ module ActiveRecord
|
|
399
402
|
|
400
403
|
def limited_ids_for(relation)
|
401
404
|
values = @klass.connection.columns_for_distinct(
|
402
|
-
connection.
|
405
|
+
connection.visitor.compile(arel_attribute(primary_key)),
|
403
406
|
relation.order_values
|
404
407
|
)
|
405
408
|
|
@@ -417,7 +420,7 @@ module ActiveRecord
|
|
417
420
|
raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
|
418
421
|
|
419
422
|
expects_array = ids.first.kind_of?(Array)
|
420
|
-
return
|
423
|
+
return [] if expects_array && ids.first.empty?
|
421
424
|
|
422
425
|
ids = ids.flatten.compact.uniq
|
423
426
|
|
@@ -433,9 +436,6 @@ module ActiveRecord
|
|
433
436
|
else
|
434
437
|
find_some(ids)
|
435
438
|
end
|
436
|
-
rescue ::RangeError
|
437
|
-
error_message = "Couldn't find #{model_name} with an out of range ID"
|
438
|
-
raise RecordNotFound.new(error_message, model_name, primary_key, ids)
|
439
439
|
end
|
440
440
|
|
441
441
|
def find_one(id)
|
@@ -551,8 +551,8 @@ module ActiveRecord
|
|
551
551
|
end
|
552
552
|
|
553
553
|
def ordered_relation
|
554
|
-
if order_values.empty? && primary_key
|
555
|
-
order(arel_attribute(primary_key).asc)
|
554
|
+
if order_values.empty? && (implicit_order_column || primary_key)
|
555
|
+
order(arel_attribute(implicit_order_column || primary_key).asc)
|
556
556
|
else
|
557
557
|
self
|
558
558
|
end
|