activerecord 6.1.7.2 → 7.0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1295 -1007
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +33 -17
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +19 -21
- data/lib/active_record/associations/collection_proxy.rb +10 -5
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +8 -5
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/preloader/association.rb +186 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +49 -13
- data/lib/active_record/associations/preloader.rb +39 -113
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +124 -95
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +49 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +57 -19
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +14 -15
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +8 -23
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +4 -8
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +105 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
- data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +71 -71
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +206 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +49 -55
- data/lib/active_record/core.rb +123 -148
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -9
- data/lib/active_record/database_configurations/hash_config.rb +63 -5
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +15 -32
- data/lib/active_record/delegated_type.rb +53 -12
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +67 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +206 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +50 -43
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +41 -6
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +20 -23
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +80 -14
- data/lib/active_record/integration.rb +4 -3
- data/lib/active_record/internal_metadata.rb +1 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/locking/pessimistic.rb +10 -4
- data/lib/active_record/log_subscriber.rb +23 -7
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +18 -6
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +7 -7
- data/lib/active_record/migration/compatibility.rb +84 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +114 -83
- data/lib/active_record/model_schema.rb +58 -59
- data/lib/active_record/nested_attributes.rb +13 -12
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +228 -60
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +149 -0
- data/lib/active_record/querying.rb +16 -6
- data/lib/active_record/railtie.rb +136 -22
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +78 -136
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +73 -50
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +6 -6
- data/lib/active_record/relation/calculations.rb +43 -38
- data/lib/active_record/relation/delegation.rb +7 -7
- data/lib/active_record/relation/finder_methods.rb +31 -35
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +276 -67
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +189 -88
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +17 -12
- data/lib/active_record/schema.rb +38 -23
- data/lib/active_record/schema_dumper.rb +25 -19
- data/lib/active_record/schema_migration.rb +4 -4
- data/lib/active_record/scoping/default.rb +60 -13
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +3 -3
- data/lib/active_record/store.rb +1 -1
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +127 -60
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +9 -6
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +4 -4
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +225 -27
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +11 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +8 -2
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +58 -2
- data/lib/arel.rb +2 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +53 -9
@@ -5,13 +5,27 @@ module ActiveRecord
|
|
5
5
|
class BatchEnumerator
|
6
6
|
include Enumerable
|
7
7
|
|
8
|
-
def initialize(of: 1000, start: nil, finish: nil, relation:)
|
8
|
+
def initialize(of: 1000, start: nil, finish: nil, relation:) # :nodoc:
|
9
9
|
@of = of
|
10
10
|
@relation = relation
|
11
11
|
@start = start
|
12
12
|
@finish = finish
|
13
13
|
end
|
14
14
|
|
15
|
+
# The primary key value from which the BatchEnumerator starts, inclusive of the value.
|
16
|
+
attr_reader :start
|
17
|
+
|
18
|
+
# The primary key value at which the BatchEnumerator ends, inclusive of the value.
|
19
|
+
attr_reader :finish
|
20
|
+
|
21
|
+
# The relation from which the BatchEnumerator yields batches.
|
22
|
+
attr_reader :relation
|
23
|
+
|
24
|
+
# The size of the batches yielded by the BatchEnumerator.
|
25
|
+
def batch_size
|
26
|
+
@of
|
27
|
+
end
|
28
|
+
|
15
29
|
# Looping through a collection of records from the database (using the
|
16
30
|
# +all+ method, for example) is very inefficient since it will try to
|
17
31
|
# instantiate all the objects at once.
|
@@ -33,11 +47,11 @@ module ActiveRecord
|
|
33
47
|
# Person.in_batches.each_record.with_index do |person, index|
|
34
48
|
# person.award_trophy(index + 1)
|
35
49
|
# end
|
36
|
-
def each_record
|
50
|
+
def each_record(&block)
|
37
51
|
return to_enum(:each_record) unless block_given?
|
38
52
|
|
39
53
|
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true).each do |relation|
|
40
|
-
relation.records.each
|
54
|
+
relation.records.each(&block)
|
41
55
|
end
|
42
56
|
end
|
43
57
|
|
@@ -75,9 +89,9 @@ module ActiveRecord
|
|
75
89
|
# Person.in_batches.each do |relation|
|
76
90
|
# relation.update_all(awesome: true)
|
77
91
|
# end
|
78
|
-
def each
|
92
|
+
def each(&block)
|
79
93
|
enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false)
|
80
|
-
return enum.each
|
94
|
+
return enum.each(&block) if block_given?
|
81
95
|
enum
|
82
96
|
end
|
83
97
|
end
|
@@ -37,7 +37,7 @@ module ActiveRecord
|
|
37
37
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
38
38
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
39
39
|
# an order is present in the relation.
|
40
|
-
# * <tt>:order</tt> - Specifies the primary key order (can be
|
40
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be +:asc+ or +:desc+). Defaults to +:asc+.
|
41
41
|
#
|
42
42
|
# Limits are honored, and if present there is no requirement for the batch
|
43
43
|
# size: it can be less than, equal to, or greater than the limit.
|
@@ -65,10 +65,10 @@ module ActiveRecord
|
|
65
65
|
#
|
66
66
|
# NOTE: By its nature, batch processing is subject to race conditions if
|
67
67
|
# other processes are modifying the database.
|
68
|
-
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
|
68
|
+
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc, &block)
|
69
69
|
if block_given?
|
70
70
|
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do |records|
|
71
|
-
records.each
|
71
|
+
records.each(&block)
|
72
72
|
end
|
73
73
|
else
|
74
74
|
enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
|
@@ -102,7 +102,7 @@ module ActiveRecord
|
|
102
102
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
103
103
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
104
104
|
# an order is present in the relation.
|
105
|
-
# * <tt>:order</tt> - Specifies the primary key order (can be
|
105
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be +:asc+ or +:desc+). Defaults to +:asc+.
|
106
106
|
#
|
107
107
|
# Limits are honored, and if present there is no requirement for the batch
|
108
108
|
# size: it can be less than, equal to, or greater than the limit.
|
@@ -167,7 +167,7 @@ module ActiveRecord
|
|
167
167
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
168
168
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
169
169
|
# an order is present in the relation.
|
170
|
-
# * <tt>:order</tt> - Specifies the primary key order (can be
|
170
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be +:asc+ or +:desc+). Defaults to +:asc+.
|
171
171
|
#
|
172
172
|
# Limits are honored, and if present there is no requirement for the batch
|
173
173
|
# size, it can be less than, equal, or greater than the limit.
|
@@ -284,7 +284,7 @@ module ActiveRecord
|
|
284
284
|
end
|
285
285
|
|
286
286
|
def act_on_ignored_order(error_on_ignore)
|
287
|
-
raise_error = (error_on_ignore.nil? ?
|
287
|
+
raise_error = (error_on_ignore.nil? ? ActiveRecord.error_on_ignored_order : error_on_ignore)
|
288
288
|
|
289
289
|
if raise_error
|
290
290
|
raise ArgumentError.new(ORDER_IGNORE_MESSAGE)
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
#
|
32
32
|
# Article.group(:status, :category).count
|
33
33
|
# # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
|
34
|
-
#
|
34
|
+
# # ["published", "business"]=>0, ["published", "technology"]=>2}
|
35
35
|
#
|
36
36
|
# If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
|
37
37
|
#
|
@@ -83,15 +83,24 @@ module ActiveRecord
|
|
83
83
|
# #calculate for examples with options.
|
84
84
|
#
|
85
85
|
# Person.sum(:age) # => 4562
|
86
|
-
def sum(
|
86
|
+
def sum(identity_or_column = nil, &block)
|
87
87
|
if block_given?
|
88
|
-
|
89
|
-
|
88
|
+
values = map(&block)
|
89
|
+
if identity_or_column.nil? && (values.first.is_a?(Numeric) || values.first(1) == [])
|
90
|
+
identity_or_column = 0
|
90
91
|
end
|
91
92
|
|
92
|
-
|
93
|
+
if identity_or_column.nil?
|
94
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
95
|
+
Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
|
96
|
+
Sum of non-numeric elements requires an initial argument.
|
97
|
+
MSG
|
98
|
+
values.inject(:+) || 0
|
99
|
+
else
|
100
|
+
values.sum(identity_or_column)
|
101
|
+
end
|
93
102
|
else
|
94
|
-
calculate(:sum,
|
103
|
+
calculate(:sum, identity_or_column)
|
95
104
|
end
|
96
105
|
end
|
97
106
|
|
@@ -146,7 +155,7 @@ module ActiveRecord
|
|
146
155
|
end
|
147
156
|
|
148
157
|
# Use #pluck as a shortcut to select one or more attributes without
|
149
|
-
# loading
|
158
|
+
# loading an entire record object per row.
|
150
159
|
#
|
151
160
|
# Person.pluck(:name)
|
152
161
|
#
|
@@ -195,9 +204,9 @@ module ActiveRecord
|
|
195
204
|
relation.select_values = columns
|
196
205
|
result = skip_query_cache_if_necessary do
|
197
206
|
if where_clause.contradiction?
|
198
|
-
ActiveRecord::Result.
|
207
|
+
ActiveRecord::Result.empty
|
199
208
|
else
|
200
|
-
klass.connection.select_all(relation.arel,
|
209
|
+
klass.connection.select_all(relation.arel, "#{klass.name} Pluck")
|
201
210
|
end
|
202
211
|
end
|
203
212
|
type_cast_pluck_values(result, columns)
|
@@ -286,7 +295,7 @@ module ActiveRecord
|
|
286
295
|
operation == "count" ? column.count(distinct) : column.public_send(operation)
|
287
296
|
end
|
288
297
|
|
289
|
-
def execute_simple_calculation(operation, column_name, distinct)
|
298
|
+
def execute_simple_calculation(operation, column_name, distinct) # :nodoc:
|
290
299
|
if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
|
291
300
|
# Shortcut when limit is zero.
|
292
301
|
return 0 if limit_value == 0
|
@@ -305,29 +314,21 @@ module ActiveRecord
|
|
305
314
|
query_builder = relation.arel
|
306
315
|
end
|
307
316
|
|
308
|
-
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
|
317
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, "#{@klass.name} #{operation.capitalize}") }
|
309
318
|
|
310
|
-
|
319
|
+
if operation != "count"
|
311
320
|
type = column.try(:type_caster) ||
|
312
321
|
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
313
322
|
type = type.subtype if Enum::EnumType === type
|
314
|
-
type.deserialize(value)
|
315
323
|
end
|
324
|
+
|
325
|
+
type_cast_calculated_value(result.cast_values.first, operation, type)
|
316
326
|
end
|
317
327
|
|
318
|
-
def execute_grouped_calculation(operation, column_name, distinct)
|
328
|
+
def execute_grouped_calculation(operation, column_name, distinct) # :nodoc:
|
319
329
|
group_fields = group_values
|
320
330
|
group_fields = group_fields.uniq if group_fields.size > 1
|
321
331
|
|
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 7.0.
|
325
|
-
To migrate to Rails 7.0'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
|
330
|
-
|
331
332
|
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
|
332
333
|
association = klass._reflect_on_association(group_fields.first)
|
333
334
|
associated = association && association.belongs_to? # only count belongs_to associations
|
@@ -344,12 +345,13 @@ module ActiveRecord
|
|
344
345
|
column = aggregate_column(column_name)
|
345
346
|
column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
|
346
347
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
347
|
-
select_value.as(column_alias)
|
348
|
+
select_value.as(connection.quote_column_name(column_alias))
|
348
349
|
|
349
350
|
select_values = [select_value]
|
350
351
|
select_values += self.select_values unless having_clause.empty?
|
351
352
|
|
352
353
|
select_values.concat group_columns.map { |aliaz, field|
|
354
|
+
aliaz = connection.quote_column_name(aliaz)
|
353
355
|
if field.respond_to?(:as)
|
354
356
|
field.as(aliaz)
|
355
357
|
else
|
@@ -361,7 +363,7 @@ module ActiveRecord
|
|
361
363
|
relation.group_values = group_fields
|
362
364
|
relation.select_values = select_values
|
363
365
|
|
364
|
-
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel,
|
366
|
+
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "#{@klass.name} #{operation.capitalize}") }
|
365
367
|
|
366
368
|
if association
|
367
369
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
@@ -381,20 +383,18 @@ module ActiveRecord
|
|
381
383
|
end
|
382
384
|
end
|
383
385
|
|
384
|
-
|
386
|
+
if operation != "count"
|
387
|
+
type = column.try(:type_caster) ||
|
388
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
389
|
+
type = type.subtype if Enum::EnumType === type
|
390
|
+
end
|
391
|
+
|
385
392
|
hash_rows.each_with_object({}) do |row, result|
|
386
393
|
key = group_aliases.map { |aliaz| row[aliaz] }
|
387
394
|
key = key.first if key.size == 1
|
388
395
|
key = key_records[key] if associated
|
389
396
|
|
390
|
-
result[key] = type_cast_calculated_value(row[column_alias], operation)
|
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
|
397
|
+
result[key] = type_cast_calculated_value(row[column_alias], operation, type)
|
398
398
|
end
|
399
399
|
end
|
400
400
|
|
@@ -445,16 +445,21 @@ module ActiveRecord
|
|
445
445
|
result.cast_values(cast_types)
|
446
446
|
end
|
447
447
|
|
448
|
-
def type_cast_calculated_value(value, operation)
|
448
|
+
def type_cast_calculated_value(value, operation, type)
|
449
449
|
case operation
|
450
450
|
when "count"
|
451
451
|
value.to_i
|
452
452
|
when "sum"
|
453
|
-
|
453
|
+
type.deserialize(value || 0)
|
454
454
|
when "average"
|
455
|
-
|
455
|
+
case type.type
|
456
|
+
when :integer, :decimal
|
457
|
+
value&.to_d
|
458
|
+
else
|
459
|
+
type.deserialize(value)
|
460
|
+
end
|
456
461
|
else # "minimum", "maximum"
|
457
|
-
|
462
|
+
type.deserialize(value)
|
458
463
|
end
|
459
464
|
end
|
460
465
|
|
@@ -15,7 +15,8 @@ module ActiveRecord
|
|
15
15
|
[
|
16
16
|
ActiveRecord::Relation,
|
17
17
|
ActiveRecord::Associations::CollectionProxy,
|
18
|
-
ActiveRecord::AssociationRelation
|
18
|
+
ActiveRecord::AssociationRelation,
|
19
|
+
ActiveRecord::DisableJoinsAssociationRelation
|
19
20
|
].each do |klass|
|
20
21
|
delegate = Class.new(klass) {
|
21
22
|
include ClassSpecificRelation
|
@@ -61,17 +62,16 @@ module ActiveRecord
|
|
61
62
|
return if method_defined?(method)
|
62
63
|
|
63
64
|
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"
|
65
65
|
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
66
|
-
def #{method}(
|
67
|
-
scoping { klass.#{method}(
|
66
|
+
def #{method}(...)
|
67
|
+
scoping { klass.#{method}(...) }
|
68
68
|
end
|
69
69
|
RUBY
|
70
70
|
else
|
71
71
|
define_method(method) do |*args, &block|
|
72
72
|
scoping { klass.public_send(method, *args, &block) }
|
73
73
|
end
|
74
|
-
ruby2_keywords(method)
|
74
|
+
ruby2_keywords(method)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
@@ -87,7 +87,7 @@ module ActiveRecord
|
|
87
87
|
|
88
88
|
delegate :to_xml, :encode_with, :length, :each, :join,
|
89
89
|
:[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
|
90
|
-
:to_sentence, :to_formatted_s, :as_json,
|
90
|
+
:to_sentence, :to_fs, :to_formatted_s, :as_json,
|
91
91
|
:shuffle, :split, :slice, :index, :rindex, to: :records
|
92
92
|
|
93
93
|
delegate :primary_key, :connection, to: :klass
|
@@ -110,7 +110,7 @@ module ActiveRecord
|
|
110
110
|
super
|
111
111
|
end
|
112
112
|
end
|
113
|
-
ruby2_keywords(:method_missing)
|
113
|
+
ruby2_keywords(:method_missing)
|
114
114
|
end
|
115
115
|
|
116
116
|
module ClassMethods # :nodoc:
|
@@ -104,6 +104,32 @@ module ActiveRecord
|
|
104
104
|
take || raise_record_not_found_exception!
|
105
105
|
end
|
106
106
|
|
107
|
+
# Finds the sole matching record. Raises ActiveRecord::RecordNotFound if no
|
108
|
+
# record is found. Raises ActiveRecord::SoleRecordExceeded if more than one
|
109
|
+
# record is found.
|
110
|
+
#
|
111
|
+
# Product.where(["price = %?", price]).sole
|
112
|
+
def sole
|
113
|
+
found, undesired = first(2)
|
114
|
+
|
115
|
+
if found.nil?
|
116
|
+
raise_record_not_found_exception!
|
117
|
+
elsif undesired.present?
|
118
|
+
raise ActiveRecord::SoleRecordExceeded.new(self)
|
119
|
+
else
|
120
|
+
found
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Finds the sole matching record. Raises ActiveRecord::RecordNotFound if no
|
125
|
+
# record is found. Raises ActiveRecord::SoleRecordExceeded if more than one
|
126
|
+
# record is found.
|
127
|
+
#
|
128
|
+
# Product.find_sole_by(["price = %?", price])
|
129
|
+
def find_sole_by(arg, *args)
|
130
|
+
where(arg, *args).sole
|
131
|
+
end
|
132
|
+
|
107
133
|
# Find the first record (or first N records if a parameter is supplied).
|
108
134
|
# If no order is defined it will order by primary key.
|
109
135
|
#
|
@@ -114,8 +140,6 @@ module ActiveRecord
|
|
114
140
|
# Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
|
115
141
|
#
|
116
142
|
def first(limit = nil)
|
117
|
-
check_reorder_deprecation unless loaded?
|
118
|
-
|
119
143
|
if limit
|
120
144
|
find_nth_with_limit(0, limit)
|
121
145
|
else
|
@@ -364,17 +388,6 @@ module ActiveRecord
|
|
364
388
|
end
|
365
389
|
|
366
390
|
private
|
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 7.0.
|
373
|
-
To continue taking non-deterministic result, use `.take` / `.take!` instead.
|
374
|
-
MSG
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
391
|
def construct_relation_for_exists(conditions)
|
379
392
|
conditions = sanitize_forbidden_attributes(conditions)
|
380
393
|
|
@@ -400,7 +413,7 @@ module ActiveRecord
|
|
400
413
|
)
|
401
414
|
relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
|
402
415
|
|
403
|
-
if eager_loading && !(
|
416
|
+
if eager_loading && has_limit_or_offset? && !(
|
404
417
|
using_limitable_reflections?(join_dependency.reflections) &&
|
405
418
|
using_limitable_reflections?(
|
406
419
|
construct_join_dependency(
|
@@ -409,12 +422,10 @@ module ActiveRecord
|
|
409
422
|
), nil
|
410
423
|
).reflections
|
411
424
|
)
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
|
425
|
+
)
|
426
|
+
relation = skip_query_cache_if_necessary do
|
427
|
+
klass.connection.distinct_relation_for_primary_key(relation)
|
416
428
|
end
|
417
|
-
relation.limit_value = relation.offset_value = nil
|
418
429
|
end
|
419
430
|
|
420
431
|
if block_given?
|
@@ -424,18 +435,6 @@ module ActiveRecord
|
|
424
435
|
end
|
425
436
|
end
|
426
437
|
|
427
|
-
def limited_ids_for(relation)
|
428
|
-
values = @klass.connection.columns_for_distinct(
|
429
|
-
connection.visitor.compile(table[primary_key]),
|
430
|
-
relation.order_values
|
431
|
-
)
|
432
|
-
|
433
|
-
relation = relation.except(:select).select(values).distinct!
|
434
|
-
|
435
|
-
id_rows = skip_query_cache_if_necessary { @klass.connection.select_rows(relation.arel, "SQL") }
|
436
|
-
id_rows.map(&:last)
|
437
|
-
end
|
438
|
-
|
439
438
|
def using_limitable_reflections?(reflections)
|
440
439
|
reflections.none?(&:collection?)
|
441
440
|
end
|
@@ -508,10 +507,7 @@ module ActiveRecord
|
|
508
507
|
result = except(:limit, :offset).where(primary_key => ids).records
|
509
508
|
|
510
509
|
if result.size == ids.size
|
511
|
-
|
512
|
-
|
513
|
-
records_by_id = result.index_by(&:id)
|
514
|
-
ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
|
510
|
+
result.in_order_of(:id, ids.map { |id| @klass.type_for_attribute(primary_key).cast(id) })
|
515
511
|
else
|
516
512
|
raise_record_not_found_exception!(ids, result.size, ids.size)
|
517
513
|
end
|
@@ -51,30 +51,25 @@ module ActiveRecord
|
|
51
51
|
@rewhere = rewhere
|
52
52
|
end
|
53
53
|
|
54
|
-
NORMAL_VALUES = Relation::VALUE_METHODS -
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
NORMAL_VALUES
|
60
|
-
end
|
54
|
+
NORMAL_VALUES = Relation::VALUE_METHODS - Relation::CLAUSE_METHODS -
|
55
|
+
[
|
56
|
+
:select, :includes, :preload, :joins, :left_outer_joins,
|
57
|
+
:order, :reverse_order, :lock, :create_with, :reordering
|
58
|
+
]
|
61
59
|
|
62
60
|
def merge
|
63
|
-
|
61
|
+
NORMAL_VALUES.each do |name|
|
64
62
|
value = values[name]
|
65
63
|
# The unless clause is here mostly for performance reasons (since the `send` call might be moderately
|
66
64
|
# expensive), most of the time the value is going to be `nil` or `.blank?`, the only catch is that
|
67
65
|
# `false.blank?` returns `true`, so there needs to be an extra check so that explicit `false` values
|
68
66
|
# don't fall through the cracks.
|
69
67
|
unless value.nil? || (value.blank? && false != value)
|
70
|
-
|
71
|
-
relation._select!(*value)
|
72
|
-
else
|
73
|
-
relation.public_send("#{name}!", *value)
|
74
|
-
end
|
68
|
+
relation.public_send(:"#{name}!", *value)
|
75
69
|
end
|
76
70
|
end
|
77
71
|
|
72
|
+
merge_select_values
|
78
73
|
merge_multi_values
|
79
74
|
merge_single_values
|
80
75
|
merge_clauses
|
@@ -86,6 +81,18 @@ module ActiveRecord
|
|
86
81
|
end
|
87
82
|
|
88
83
|
private
|
84
|
+
def merge_select_values
|
85
|
+
return if other.select_values.empty?
|
86
|
+
|
87
|
+
if other.klass == relation.klass
|
88
|
+
relation.select_values |= other.select_values
|
89
|
+
else
|
90
|
+
relation.select_values |= other.instance_eval do
|
91
|
+
arel_columns(select_values)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
89
96
|
def merge_preloads
|
90
97
|
return if other.preload_values.empty? && other.includes_values.empty?
|
91
98
|
|
@@ -9,10 +9,6 @@ module ActiveRecord
|
|
9
9
|
require "active_record/relation/predicate_builder/association_query_value"
|
10
10
|
require "active_record/relation/predicate_builder/polymorphic_array_value"
|
11
11
|
|
12
|
-
# No-op BaseHandler to work Mashal.load(File.read("legacy_relation.dump")).
|
13
|
-
# TODO: Remove the constant alias once Rails 6.1 has released.
|
14
|
-
BaseHandler = BasicObjectHandler
|
15
|
-
|
16
12
|
def initialize(table)
|
17
13
|
@table = table
|
18
14
|
@handlers = []
|
@@ -69,8 +65,7 @@ module ActiveRecord
|
|
69
65
|
end
|
70
66
|
|
71
67
|
def build_bind_attribute(column_name, value)
|
72
|
-
|
73
|
-
Arel::Nodes::BindParam.new(attr)
|
68
|
+
Relation::QueryAttribute.new(column_name, value, table.type(column_name))
|
74
69
|
end
|
75
70
|
|
76
71
|
def resolve_arel_attribute(table_name, column_name, &block)
|
@@ -20,25 +20,19 @@ module ActiveRecord
|
|
20
20
|
def nil?
|
21
21
|
unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
22
22
|
value_before_type_cast.nil? ||
|
23
|
-
type.respond_to?(:subtype
|
23
|
+
type.respond_to?(:subtype) && serializable? && value_for_database.nil?
|
24
24
|
end
|
25
|
-
rescue ::RangeError
|
26
25
|
end
|
27
26
|
|
28
27
|
def infinite?
|
29
|
-
infinity?(value_before_type_cast) || infinity?(value_for_database)
|
30
|
-
rescue ::RangeError
|
28
|
+
infinity?(value_before_type_cast) || serializable? && infinity?(value_for_database)
|
31
29
|
end
|
32
30
|
|
33
31
|
def unboundable?
|
34
|
-
|
35
|
-
@_unboundable
|
36
|
-
else
|
37
|
-
value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
38
|
-
@_unboundable = nil
|
32
|
+
unless defined?(@_unboundable)
|
33
|
+
serializable? { |value| @_unboundable = value <=> 0 } && @_unboundable = nil
|
39
34
|
end
|
40
|
-
|
41
|
-
@_unboundable = type.cast(value_before_type_cast) <=> 0
|
35
|
+
@_unboundable
|
42
36
|
end
|
43
37
|
|
44
38
|
private
|