activerecord 6.0.0 → 6.1.4
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 +1178 -600
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/active_record/aggregations.rb +5 -6
- data/lib/active_record/association_relation.rb +30 -10
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +55 -29
- data/lib/active_record/associations/association_scope.rb +19 -15
- data/lib/active_record/associations/belongs_to_association.rb +23 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
- data/lib/active_record/associations/builder/association.rb +32 -5
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +25 -8
- data/lib/active_record/associations/collection_proxy.rb +14 -7
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -3
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +77 -42
- data/lib/active_record/associations/preloader/association.rb +51 -25
- data/lib/active_record/associations/preloader/through_association.rb +2 -2
- data/lib/active_record/associations/preloader.rb +13 -8
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +120 -13
- data/lib/active_record/attribute_assignment.rb +10 -9
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
- data/lib/active_record/attribute_methods/dirty.rb +3 -13
- data/lib/active_record/attribute_methods/primary_key.rb +6 -4
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -12
- data/lib/active_record/attribute_methods/serialization.rb +11 -6
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +12 -21
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +33 -9
- data/lib/active_record/autosave_association.rb +63 -44
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +153 -24
- data/lib/active_record/coders/yaml_column.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +141 -52
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
- data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
- data/lib/active_record/connection_adapters/abstract_adapter.rb +76 -79
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -65
- data/lib/active_record/connection_adapters/schema_cache.rb +106 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +219 -81
- data/lib/active_record/core.rb +268 -71
- data/lib/active_record/counter_cache.rb +4 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -41
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +2 -3
- data/lib/active_record/enum.rb +80 -38
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -5
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -3
- data/lib/active_record/fixture_set/table_rows.rb +0 -1
- data/lib/active_record/fixtures.rb +58 -12
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +40 -21
- data/lib/active_record/insert_all.rb +43 -10
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +16 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +33 -18
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +28 -9
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
- data/lib/active_record/middleware/database_selector.rb +4 -2
- data/lib/active_record/migration/command_recorder.rb +53 -45
- data/lib/active_record/migration/compatibility.rb +71 -20
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +115 -85
- data/lib/active_record/model_schema.rb +120 -15
- data/lib/active_record/nested_attributes.rb +2 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +50 -46
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +12 -7
- data/lib/active_record/railtie.rb +65 -45
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +280 -99
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +77 -63
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -32
- data/lib/active_record/relation/calculations.rb +106 -45
- data/lib/active_record/relation/delegation.rb +9 -7
- data/lib/active_record/relation/finder_methods.rb +55 -17
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +27 -26
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +59 -40
- data/lib/active_record/relation/query_methods.rb +344 -181
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -8
- data/lib/active_record/relation/where_clause.rb +111 -62
- data/lib/active_record/relation.rb +116 -82
- data/lib/active_record/result.rb +41 -34
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/default.rb +1 -4
- data/lib/active_record/scoping/named.rb +7 -18
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +3 -3
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -36
- data/lib/active_record/tasks/database_tasks.rb +140 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +79 -16
- data/lib/active_record/timestamp.rb +4 -7
- data/lib/active_record/touch_later.rb +20 -21
- data/lib/active_record/transactions.rb +26 -73
- data/lib/active_record/type/adapter_specific_registry.rb +2 -5
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +8 -2
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +3 -3
- data/lib/active_record.rb +7 -13
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/predications.rb +17 -24
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -3
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -5
- data/lib/arel/visitors/sqlite.rb +0 -1
- data/lib/arel/visitors/to_sql.rb +89 -79
- data/lib/arel/visitors/visitor.rb +0 -1
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +15 -12
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +6 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +27 -24
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Calculations
|
5
7
|
# Count the records.
|
@@ -134,7 +136,7 @@ module ActiveRecord
|
|
134
136
|
relation.select_values = [ klass.primary_key || table[Arel.star] ]
|
135
137
|
end
|
136
138
|
# PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
|
137
|
-
relation.order_values = []
|
139
|
+
relation.order_values = [] if group_values.empty?
|
138
140
|
end
|
139
141
|
|
140
142
|
relation.calculate(operation, column_name)
|
@@ -172,14 +174,14 @@ module ActiveRecord
|
|
172
174
|
# # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
|
173
175
|
# # => [2, 3]
|
174
176
|
#
|
175
|
-
# Person.pluck('DATEDIFF(updated_at, created_at)')
|
177
|
+
# Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
|
176
178
|
# # SELECT DATEDIFF(updated_at, created_at) FROM people
|
177
179
|
# # => ['0', '27761', '173']
|
178
180
|
#
|
179
181
|
# See also #ids.
|
180
182
|
#
|
181
183
|
def pluck(*column_names)
|
182
|
-
if loaded? && (column_names
|
184
|
+
if loaded? && all_attributes?(column_names)
|
183
185
|
return records.pluck(*column_names)
|
184
186
|
end
|
185
187
|
|
@@ -188,10 +190,17 @@ module ActiveRecord
|
|
188
190
|
relation.pluck(*column_names)
|
189
191
|
else
|
190
192
|
klass.disallow_raw_sql!(column_names)
|
193
|
+
columns = arel_columns(column_names)
|
191
194
|
relation = spawn
|
192
|
-
relation.select_values =
|
193
|
-
result = skip_query_cache_if_necessary
|
194
|
-
|
195
|
+
relation.select_values = columns
|
196
|
+
result = skip_query_cache_if_necessary do
|
197
|
+
if where_clause.contradiction?
|
198
|
+
ActiveRecord::Result.new([], [])
|
199
|
+
else
|
200
|
+
klass.connection.select_all(relation.arel, nil)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
type_cast_pluck_values(result, columns)
|
195
204
|
end
|
196
205
|
end
|
197
206
|
|
@@ -210,6 +219,10 @@ module ActiveRecord
|
|
210
219
|
# # SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1
|
211
220
|
# # => [ 'David', 'david@loudthinking.com' ]
|
212
221
|
def pick(*column_names)
|
222
|
+
if loaded? && all_attributes?(column_names)
|
223
|
+
return records.pick(*column_names)
|
224
|
+
end
|
225
|
+
|
213
226
|
limit(1).pluck(*column_names).first
|
214
227
|
end
|
215
228
|
|
@@ -222,6 +235,10 @@ module ActiveRecord
|
|
222
235
|
end
|
223
236
|
|
224
237
|
private
|
238
|
+
def all_attributes?(column_names)
|
239
|
+
(column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
|
240
|
+
end
|
241
|
+
|
225
242
|
def has_include?(column_name)
|
226
243
|
eager_loading? || (includes_values.present? && column_name && column_name != :all)
|
227
244
|
end
|
@@ -266,12 +283,10 @@ module ActiveRecord
|
|
266
283
|
end
|
267
284
|
|
268
285
|
def operation_over_aggregate_column(column, operation, distinct)
|
269
|
-
operation == "count" ? column.count(distinct) : column.
|
286
|
+
operation == "count" ? column.count(distinct) : column.public_send(operation)
|
270
287
|
end
|
271
288
|
|
272
289
|
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
|
273
|
-
column_alias = column_name
|
274
|
-
|
275
290
|
if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
|
276
291
|
# Shortcut when limit is zero.
|
277
292
|
return 0 if limit_value == 0
|
@@ -282,31 +297,36 @@ module ActiveRecord
|
|
282
297
|
relation = unscope(:order).distinct!(false)
|
283
298
|
|
284
299
|
column = aggregate_column(column_name)
|
285
|
-
|
286
300
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
287
|
-
if operation == "sum" && distinct
|
288
|
-
select_value.distinct = true
|
289
|
-
end
|
301
|
+
select_value.distinct = true if operation == "sum" && distinct
|
290
302
|
|
291
|
-
column_alias = select_value.alias
|
292
|
-
column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
|
293
303
|
relation.select_values = [select_value]
|
294
304
|
|
295
305
|
query_builder = relation.arel
|
296
306
|
end
|
297
307
|
|
298
|
-
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder
|
299
|
-
row = result.first
|
300
|
-
value = row && row.values.first
|
301
|
-
type = result.column_types.fetch(column_alias) do
|
302
|
-
type_for(column_name)
|
303
|
-
end
|
308
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
|
304
309
|
|
305
|
-
type_cast_calculated_value(
|
310
|
+
type_cast_calculated_value(result.cast_values.first, operation) do |value|
|
311
|
+
type = column.try(:type_caster) ||
|
312
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
313
|
+
type = type.subtype if Enum::EnumType === type
|
314
|
+
type.deserialize(value)
|
315
|
+
end
|
306
316
|
end
|
307
317
|
|
308
318
|
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
309
319
|
group_fields = group_values
|
320
|
+
group_fields = group_fields.uniq if group_fields.size > 1
|
321
|
+
|
322
|
+
unless group_fields == group_values
|
323
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
324
|
+
`#{operation}` with group by duplicated fields does no longer affect to result in Rails 6.2.
|
325
|
+
To migrate to Rails 6.2's behavior, use `uniq!(:group)` to deduplicate group fields
|
326
|
+
(`#{klass.name&.tableize || klass.table_name}.uniq!(:group).#{operation}(#{column_name.inspect})`).
|
327
|
+
MSG
|
328
|
+
group_fields = group_values
|
329
|
+
end
|
310
330
|
|
311
331
|
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
|
312
332
|
association = klass._reflect_on_association(group_fields.first)
|
@@ -321,14 +341,12 @@ module ActiveRecord
|
|
321
341
|
}
|
322
342
|
group_columns = group_aliases.zip(group_fields)
|
323
343
|
|
324
|
-
|
344
|
+
column = aggregate_column(column_name)
|
345
|
+
column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
|
346
|
+
select_value = operation_over_aggregate_column(column, operation, distinct)
|
347
|
+
select_value.as(column_alias)
|
325
348
|
|
326
|
-
select_values = [
|
327
|
-
operation_over_aggregate_column(
|
328
|
-
aggregate_column(column_name),
|
329
|
-
operation,
|
330
|
-
distinct).as(aggregate_alias)
|
331
|
-
]
|
349
|
+
select_values = [select_value]
|
332
350
|
select_values += self.select_values unless having_clause.empty?
|
333
351
|
|
334
352
|
select_values.concat group_columns.map { |aliaz, field|
|
@@ -348,22 +366,36 @@ module ActiveRecord
|
|
348
366
|
if association
|
349
367
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
350
368
|
key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
|
351
|
-
key_records =
|
369
|
+
key_records = key_records.index_by(&:id)
|
352
370
|
end
|
353
371
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
372
|
+
key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
|
373
|
+
types[aliaz] = type_for(col_name) do
|
374
|
+
calculated_data.column_types.fetch(aliaz, Type.default_value)
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
hash_rows = calculated_data.cast_values(key_types).map! do |row|
|
379
|
+
calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
|
380
|
+
hash[col_name] = row[i]
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
type = nil
|
385
|
+
hash_rows.each_with_object({}) do |row, result|
|
386
|
+
key = group_aliases.map { |aliaz| row[aliaz] }
|
361
387
|
key = key.first if key.size == 1
|
362
388
|
key = key_records[key] if associated
|
363
389
|
|
364
|
-
|
365
|
-
|
366
|
-
|
390
|
+
result[key] = type_cast_calculated_value(row[column_alias], operation) do |value|
|
391
|
+
unless type
|
392
|
+
type = column.try(:type_caster) ||
|
393
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
394
|
+
type = type.subtype if Enum::EnumType === type
|
395
|
+
end
|
396
|
+
type.deserialize(value)
|
397
|
+
end
|
398
|
+
end
|
367
399
|
end
|
368
400
|
|
369
401
|
# Converts the given field to the value that the database adapter returns as
|
@@ -388,12 +420,41 @@ module ActiveRecord
|
|
388
420
|
@klass.type_for_attribute(field_name, &block)
|
389
421
|
end
|
390
422
|
|
391
|
-
def
|
423
|
+
def lookup_cast_type_from_join_dependencies(name, join_dependencies = build_join_dependencies)
|
424
|
+
each_join_dependencies(join_dependencies) do |join|
|
425
|
+
type = join.base_klass.attribute_types.fetch(name, nil)
|
426
|
+
return type if type
|
427
|
+
end
|
428
|
+
nil
|
429
|
+
end
|
430
|
+
|
431
|
+
def type_cast_pluck_values(result, columns)
|
432
|
+
cast_types = if result.columns.size != columns.size
|
433
|
+
klass.attribute_types
|
434
|
+
else
|
435
|
+
join_dependencies = nil
|
436
|
+
columns.map.with_index do |column, i|
|
437
|
+
column.try(:type_caster) ||
|
438
|
+
klass.attribute_types.fetch(name = result.columns[i]) do
|
439
|
+
join_dependencies ||= build_join_dependencies
|
440
|
+
lookup_cast_type_from_join_dependencies(name, join_dependencies) ||
|
441
|
+
result.column_types[name] || Type.default_value
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
result.cast_values(cast_types)
|
446
|
+
end
|
447
|
+
|
448
|
+
def type_cast_calculated_value(value, operation)
|
392
449
|
case operation
|
393
|
-
when "count"
|
394
|
-
|
395
|
-
when "
|
396
|
-
|
450
|
+
when "count"
|
451
|
+
value.to_i
|
452
|
+
when "sum"
|
453
|
+
yield value || 0
|
454
|
+
when "average"
|
455
|
+
value&.respond_to?(:to_d) ? value.to_d : value
|
456
|
+
else # "minimum", "maximum"
|
457
|
+
yield value
|
397
458
|
end
|
398
459
|
end
|
399
460
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "mutex_m"
|
4
|
+
require "active_support/core_ext/module/delegation"
|
4
5
|
|
5
6
|
module ActiveRecord
|
6
7
|
module Delegation # :nodoc:
|
@@ -59,16 +60,18 @@ module ActiveRecord
|
|
59
60
|
synchronize do
|
60
61
|
return if method_defined?(method)
|
61
62
|
|
62
|
-
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
|
63
|
+
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method) && !DELEGATION_RESERVED_METHOD_NAMES.include?(method.to_s)
|
64
|
+
definition = RUBY_VERSION >= "2.7" ? "..." : "*args, &block"
|
63
65
|
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
64
|
-
def #{method}(
|
65
|
-
scoping { klass.#{method}(
|
66
|
+
def #{method}(#{definition})
|
67
|
+
scoping { klass.#{method}(#{definition}) }
|
66
68
|
end
|
67
69
|
RUBY
|
68
70
|
else
|
69
71
|
define_method(method) do |*args, &block|
|
70
72
|
scoping { klass.public_send(method, *args, &block) }
|
71
73
|
end
|
74
|
+
ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
|
72
75
|
end
|
73
76
|
end
|
74
77
|
end
|
@@ -99,7 +102,6 @@ module ActiveRecord
|
|
99
102
|
end
|
100
103
|
|
101
104
|
private
|
102
|
-
|
103
105
|
def method_missing(method, *args, &block)
|
104
106
|
if @klass.respond_to?(method)
|
105
107
|
@klass.generate_relation_method(method)
|
@@ -108,15 +110,15 @@ module ActiveRecord
|
|
108
110
|
super
|
109
111
|
end
|
110
112
|
end
|
113
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
111
114
|
end
|
112
115
|
|
113
116
|
module ClassMethods # :nodoc:
|
114
|
-
def create(klass, *args)
|
115
|
-
relation_class_for(klass).new(klass, *args)
|
117
|
+
def create(klass, *args, **kwargs)
|
118
|
+
relation_class_for(klass).new(klass, *args, **kwargs)
|
116
119
|
end
|
117
120
|
|
118
121
|
private
|
119
|
-
|
120
122
|
def relation_class_for(klass)
|
121
123
|
klass.relation_delegate_class(self)
|
122
124
|
end
|
@@ -114,6 +114,8 @@ module ActiveRecord
|
|
114
114
|
# Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
|
115
115
|
#
|
116
116
|
def first(limit = nil)
|
117
|
+
check_reorder_deprecation unless loaded?
|
118
|
+
|
117
119
|
if limit
|
118
120
|
find_nth_with_limit(0, limit)
|
119
121
|
else
|
@@ -275,9 +277,9 @@ module ActiveRecord
|
|
275
277
|
# * Integer - Finds the record with this primary key.
|
276
278
|
# * String - Finds the record with a primary key corresponding to this
|
277
279
|
# string (such as <tt>'5'</tt>).
|
278
|
-
# * Array - Finds the record that matches these +
|
280
|
+
# * Array - Finds the record that matches these +where+-style conditions
|
279
281
|
# (such as <tt>['name LIKE ?', "%#{query}%"]</tt>).
|
280
|
-
# * Hash - Finds the record that matches these +
|
282
|
+
# * Hash - Finds the record that matches these +where+-style conditions
|
281
283
|
# (such as <tt>{name: 'David'}</tt>).
|
282
284
|
# * +false+ - Returns always +false+.
|
283
285
|
# * No args - Returns +false+ if the relation is empty, +true+ otherwise.
|
@@ -313,10 +315,26 @@ module ActiveRecord
|
|
313
315
|
end
|
314
316
|
|
315
317
|
relation = construct_relation_for_exists(conditions)
|
318
|
+
return false if relation.where_clause.contradiction?
|
316
319
|
|
317
|
-
skip_query_cache_if_necessary { connection.
|
320
|
+
skip_query_cache_if_necessary { connection.select_rows(relation.arel, "#{name} Exists?").size == 1 }
|
318
321
|
end
|
319
322
|
|
323
|
+
# Returns true if the relation contains the given record or false otherwise.
|
324
|
+
#
|
325
|
+
# No query is performed if the relation is loaded; the given record is
|
326
|
+
# compared to the records in memory. If the relation is unloaded, an
|
327
|
+
# efficient existence query is performed, as in #exists?.
|
328
|
+
def include?(record)
|
329
|
+
if loaded? || offset_value || limit_value || having_clause.any?
|
330
|
+
records.include?(record)
|
331
|
+
else
|
332
|
+
record.is_a?(klass) && exists?(record.id)
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
alias :member? :include?
|
337
|
+
|
320
338
|
# This method is called whenever no records are found with either a single
|
321
339
|
# id or multiple ids and raises an ActiveRecord::RecordNotFound exception.
|
322
340
|
#
|
@@ -326,15 +344,15 @@ module ActiveRecord
|
|
326
344
|
# the expected number of results should be provided in the +expected_size+
|
327
345
|
# argument.
|
328
346
|
def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key, not_found_ids = nil) # :nodoc:
|
329
|
-
conditions = arel.where_sql(
|
330
|
-
|
347
|
+
conditions = " [#{arel.where_sql(klass)}]" unless where_clause.empty?
|
348
|
+
|
331
349
|
name = @klass.name
|
332
350
|
|
333
351
|
if ids.nil?
|
334
352
|
error = +"Couldn't find #{name}"
|
335
353
|
error << " with#{conditions}" if conditions
|
336
354
|
raise RecordNotFound.new(error, name, key)
|
337
|
-
elsif Array(ids).size == 1
|
355
|
+
elsif Array.wrap(ids).size == 1
|
338
356
|
error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
|
339
357
|
raise RecordNotFound.new(error, name, key, ids)
|
340
358
|
else
|
@@ -346,9 +364,15 @@ module ActiveRecord
|
|
346
364
|
end
|
347
365
|
|
348
366
|
private
|
349
|
-
|
350
|
-
|
351
|
-
|
367
|
+
def check_reorder_deprecation
|
368
|
+
if !order_values.empty? && order_values.all?(&:blank?)
|
369
|
+
blank_value = order_values.first
|
370
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
371
|
+
`.reorder(#{blank_value.inspect})` with `.first` / `.first!` no longer
|
372
|
+
takes non-deterministic result in Rails 6.2.
|
373
|
+
To continue taking non-deterministic result, use `.take` / `.take!` instead.
|
374
|
+
MSG
|
375
|
+
end
|
352
376
|
end
|
353
377
|
|
354
378
|
def construct_relation_for_exists(conditions)
|
@@ -372,11 +396,20 @@ module ActiveRecord
|
|
372
396
|
|
373
397
|
def apply_join_dependency(eager_loading: group_values.empty?)
|
374
398
|
join_dependency = construct_join_dependency(
|
375
|
-
eager_load_values
|
399
|
+
eager_load_values | includes_values, Arel::Nodes::OuterJoin
|
376
400
|
)
|
377
401
|
relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
|
378
402
|
|
379
|
-
if eager_loading && !
|
403
|
+
if eager_loading && !(
|
404
|
+
using_limitable_reflections?(join_dependency.reflections) &&
|
405
|
+
using_limitable_reflections?(
|
406
|
+
construct_join_dependency(
|
407
|
+
select_association_list(joins_values).concat(
|
408
|
+
select_association_list(left_outer_joins_values)
|
409
|
+
), nil
|
410
|
+
).reflections
|
411
|
+
)
|
412
|
+
)
|
380
413
|
if has_limit_or_offset?
|
381
414
|
limited_ids = limited_ids_for(relation)
|
382
415
|
limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
|
@@ -393,14 +426,14 @@ module ActiveRecord
|
|
393
426
|
|
394
427
|
def limited_ids_for(relation)
|
395
428
|
values = @klass.connection.columns_for_distinct(
|
396
|
-
connection.visitor.compile(
|
429
|
+
connection.visitor.compile(table[primary_key]),
|
397
430
|
relation.order_values
|
398
431
|
)
|
399
432
|
|
400
433
|
relation = relation.except(:select).select(values).distinct!
|
401
434
|
|
402
|
-
id_rows = skip_query_cache_if_necessary { @klass.connection.
|
403
|
-
id_rows.map
|
435
|
+
id_rows = skip_query_cache_if_necessary { @klass.connection.select_rows(relation.arel, "SQL") }
|
436
|
+
id_rows.map(&:last)
|
404
437
|
end
|
405
438
|
|
406
439
|
def using_limitable_reflections?(reflections)
|
@@ -501,7 +534,8 @@ module ActiveRecord
|
|
501
534
|
end
|
502
535
|
|
503
536
|
def find_nth(index)
|
504
|
-
@offsets
|
537
|
+
@offsets ||= {}
|
538
|
+
@offsets[index] ||= find_nth_with_limit(index, 1).first
|
505
539
|
end
|
506
540
|
|
507
541
|
def find_nth_with_limit(index, limit)
|
@@ -515,7 +549,7 @@ module ActiveRecord
|
|
515
549
|
end
|
516
550
|
|
517
551
|
if limit > 0
|
518
|
-
relation = relation.offset(
|
552
|
+
relation = relation.offset((offset_value || 0) + index) unless index.zero?
|
519
553
|
relation.limit(limit).to_a
|
520
554
|
else
|
521
555
|
[]
|
@@ -543,7 +577,11 @@ module ActiveRecord
|
|
543
577
|
|
544
578
|
def ordered_relation
|
545
579
|
if order_values.empty? && (implicit_order_column || primary_key)
|
546
|
-
|
580
|
+
if implicit_order_column && primary_key && implicit_order_column != primary_key
|
581
|
+
order(table[implicit_order_column].asc, table[primary_key].asc)
|
582
|
+
else
|
583
|
+
order(table[implicit_order_column || primary_key].asc)
|
584
|
+
end
|
547
585
|
else
|
548
586
|
self
|
549
587
|
end
|
@@ -7,15 +7,16 @@ module ActiveRecord
|
|
7
7
|
class HashMerger # :nodoc:
|
8
8
|
attr_reader :relation, :hash
|
9
9
|
|
10
|
-
def initialize(relation, hash)
|
10
|
+
def initialize(relation, hash, rewhere = nil)
|
11
11
|
hash.assert_valid_keys(*Relation::VALUE_METHODS)
|
12
12
|
|
13
13
|
@relation = relation
|
14
14
|
@hash = hash
|
15
|
+
@rewhere = rewhere
|
15
16
|
end
|
16
17
|
|
17
|
-
def merge
|
18
|
-
Merger.new(relation, other).merge
|
18
|
+
def merge
|
19
|
+
Merger.new(relation, other, @rewhere).merge
|
19
20
|
end
|
20
21
|
|
21
22
|
# Applying values to a relation has some side effects. E.g.
|
@@ -28,19 +29,14 @@ module ActiveRecord
|
|
28
29
|
table: relation.table,
|
29
30
|
predicate_builder: relation.predicate_builder
|
30
31
|
)
|
31
|
-
hash.each
|
32
|
-
if k == :
|
33
|
-
|
34
|
-
|
35
|
-
else
|
36
|
-
other.joins!(*v)
|
37
|
-
end
|
38
|
-
elsif k == :select
|
39
|
-
other._select!(v)
|
32
|
+
hash.each do |k, v|
|
33
|
+
k = :_select if k == :select
|
34
|
+
if Array === v
|
35
|
+
other.public_send("#{k}!", *v)
|
40
36
|
else
|
41
|
-
other.
|
37
|
+
other.public_send("#{k}!", v)
|
42
38
|
end
|
43
|
-
|
39
|
+
end
|
44
40
|
other
|
45
41
|
end
|
46
42
|
end
|
@@ -48,10 +44,11 @@ module ActiveRecord
|
|
48
44
|
class Merger # :nodoc:
|
49
45
|
attr_reader :relation, :values, :other
|
50
46
|
|
51
|
-
def initialize(relation, other)
|
47
|
+
def initialize(relation, other, rewhere = nil)
|
52
48
|
@relation = relation
|
53
49
|
@values = other.values
|
54
50
|
@other = other
|
51
|
+
@rewhere = rewhere
|
55
52
|
end
|
56
53
|
|
57
54
|
NORMAL_VALUES = Relation::VALUE_METHODS -
|
@@ -73,7 +70,7 @@ module ActiveRecord
|
|
73
70
|
if name == :select
|
74
71
|
relation._select!(*value)
|
75
72
|
else
|
76
|
-
relation.
|
73
|
+
relation.public_send("#{name}!", *value)
|
77
74
|
end
|
78
75
|
end
|
79
76
|
end
|
@@ -89,13 +86,12 @@ module ActiveRecord
|
|
89
86
|
end
|
90
87
|
|
91
88
|
private
|
92
|
-
|
93
89
|
def merge_preloads
|
94
90
|
return if other.preload_values.empty? && other.includes_values.empty?
|
95
91
|
|
96
92
|
if other.klass == relation.klass
|
97
|
-
relation.
|
98
|
-
relation.
|
93
|
+
relation.preload_values |= other.preload_values unless other.preload_values.empty?
|
94
|
+
relation.includes_values |= other.includes_values unless other.includes_values.empty?
|
99
95
|
else
|
100
96
|
reflection = relation.klass.reflect_on_all_associations.find do |r|
|
101
97
|
r.class_name == other.klass.name
|
@@ -112,10 +108,10 @@ module ActiveRecord
|
|
112
108
|
end
|
113
109
|
|
114
110
|
def merge_joins
|
115
|
-
return if other.joins_values.
|
111
|
+
return if other.joins_values.empty?
|
116
112
|
|
117
113
|
if other.klass == relation.klass
|
118
|
-
relation.
|
114
|
+
relation.joins_values |= other.joins_values
|
119
115
|
else
|
120
116
|
associations, others = other.joins_values.partition do |join|
|
121
117
|
case join
|
@@ -131,16 +127,21 @@ module ActiveRecord
|
|
131
127
|
end
|
132
128
|
|
133
129
|
def merge_outer_joins
|
134
|
-
return if other.left_outer_joins_values.
|
130
|
+
return if other.left_outer_joins_values.empty?
|
135
131
|
|
136
132
|
if other.klass == relation.klass
|
137
|
-
relation.
|
133
|
+
relation.left_outer_joins_values |= other.left_outer_joins_values
|
138
134
|
else
|
139
|
-
associations = other.left_outer_joins_values
|
135
|
+
associations, others = other.left_outer_joins_values.partition do |join|
|
136
|
+
case join
|
137
|
+
when Hash, Symbol, Array; true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
140
141
|
join_dependency = other.construct_join_dependency(
|
141
142
|
associations, Arel::Nodes::OuterJoin
|
142
143
|
)
|
143
|
-
relation.
|
144
|
+
relation.left_outer_joins!(join_dependency, *others)
|
144
145
|
end
|
145
146
|
end
|
146
147
|
|
@@ -168,7 +169,7 @@ module ActiveRecord
|
|
168
169
|
def merge_clauses
|
169
170
|
relation.from_clause = other.from_clause if replace_from_clause?
|
170
171
|
|
171
|
-
where_clause = relation.where_clause.merge(other.where_clause)
|
172
|
+
where_clause = relation.where_clause.merge(other.where_clause, @rewhere)
|
172
173
|
relation.where_clause = where_clause unless where_clause.empty?
|
173
174
|
|
174
175
|
having_clause = relation.having_clause.merge(other.having_clause)
|
@@ -20,20 +20,19 @@ module ActiveRecord
|
|
20
20
|
case values.length
|
21
21
|
when 0 then NullPredicate
|
22
22
|
when 1 then predicate_builder.build(attribute, values.first)
|
23
|
-
else
|
24
|
-
values.map! do |v|
|
25
|
-
predicate_builder.build_bind_attribute(attribute.name, v)
|
26
|
-
end
|
27
|
-
values.empty? ? NullPredicate : attribute.in(values)
|
23
|
+
else Arel::Nodes::HomogeneousIn.new(values, attribute, :in)
|
28
24
|
end
|
29
25
|
|
30
26
|
unless nils.empty?
|
31
|
-
values_predicate = values_predicate.or(
|
27
|
+
values_predicate = values_predicate.or(attribute.eq(nil))
|
32
28
|
end
|
33
29
|
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
if ranges.empty?
|
31
|
+
values_predicate
|
32
|
+
else
|
33
|
+
array_predicates = ranges.map! { |range| predicate_builder.build(attribute, range) }
|
34
|
+
array_predicates.inject(values_predicate, &:or)
|
35
|
+
end
|
37
36
|
end
|
38
37
|
|
39
38
|
private
|
@@ -9,7 +9,7 @@ module ActiveRecord
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def queries
|
12
|
-
[associated_table.
|
12
|
+
[ associated_table.join_foreign_key => ids ]
|
13
13
|
end
|
14
14
|
|
15
15
|
private
|
@@ -27,13 +27,12 @@ module ActiveRecord
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def primary_key
|
30
|
-
associated_table.
|
30
|
+
associated_table.join_primary_key
|
31
31
|
end
|
32
32
|
|
33
33
|
def convert_to_id(value)
|
34
|
-
|
35
|
-
|
36
|
-
value._read_attribute(primary_key)
|
34
|
+
if value.respond_to?(primary_key)
|
35
|
+
value.public_send(primary_key)
|
37
36
|
else
|
38
37
|
value
|
39
38
|
end
|