activerecord 6.0.3.4 → 6.1.0
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 +799 -713
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/active_record.rb +7 -14
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +22 -14
- data/lib/active_record/associations.rb +114 -11
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +44 -28
- data/lib/active_record/associations/association_scope.rb +17 -15
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +9 -3
- 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 -1
- 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 +19 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- 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.rb +72 -50
- data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/preloader.rb +11 -5
- 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/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +11 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attributes.rb +32 -7
- data/lib/active_record/autosave_association.rb +57 -40
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +1 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +191 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
- 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 +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +112 -27
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
- data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
- data/lib/active_record/connection_adapters/abstract_adapter.rb +54 -71
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
- 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 +31 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -25
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- 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/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
- data/lib/active_record/connection_handling.rb +210 -71
- data/lib/active_record/core.rb +229 -63
- data/lib/active_record/database_configurations.rb +124 -85
- 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 -40
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +40 -16
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- 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 -2
- data/lib/active_record/fixtures.rb +54 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +35 -6
- 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 +22 -16
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +26 -8
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/migration.rb +113 -83
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +67 -17
- data/lib/active_record/model_schema.rb +117 -13
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/databases.rake +266 -95
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +71 -57
- data/lib/active_record/relation.rb +96 -67
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/calculations.rb +101 -44
- data/lib/active_record/relation/delegation.rb +2 -1
- data/lib/active_record/relation/finder_methods.rb +45 -15
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +27 -25
- data/lib/active_record/relation/predicate_builder.rb +57 -33
- 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 +3 -3
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +330 -195
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +104 -57
- data/lib/active_record/result.rb +41 -33
- 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/named.rb +6 -17
- 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 +2 -2
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -51
- data/lib/active_record/tasks/database_tasks.rb +139 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +37 -16
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +15 -64
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type/time.rb +10 -0
- 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.rb +1 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/arel.rb +5 -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.rb +3 -1
- 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 +72 -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/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/rails/generators/active_record/migration.rb +6 -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 +3 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +27 -28
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- 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 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
@@ -37,6 +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 :asc or :desc). Defaults to :asc.
|
40
41
|
#
|
41
42
|
# Limits are honored, and if present there is no requirement for the batch
|
42
43
|
# size: it can be less than, equal to, or greater than the limit.
|
@@ -57,22 +58,22 @@ module ActiveRecord
|
|
57
58
|
# person.party_all_night!
|
58
59
|
# end
|
59
60
|
#
|
60
|
-
# NOTE:
|
61
|
-
# ascending on the primary key ("id ASC")
|
62
|
-
#
|
61
|
+
# NOTE: Order can be ascending (:asc) or descending (:desc). It is automatically set to
|
62
|
+
# ascending on the primary key ("id ASC").
|
63
|
+
# This also means that this method only works when the primary key is
|
63
64
|
# orderable (e.g. an integer or string).
|
64
65
|
#
|
65
66
|
# NOTE: By its nature, batch processing is subject to race conditions if
|
66
67
|
# other processes are modifying the database.
|
67
|
-
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
|
68
|
+
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
|
68
69
|
if block_given?
|
69
|
-
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do |records|
|
70
|
+
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do |records|
|
70
71
|
records.each { |record| yield record }
|
71
72
|
end
|
72
73
|
else
|
73
|
-
enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
|
74
|
+
enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
|
74
75
|
relation = self
|
75
|
-
apply_limits(relation, start, finish).size
|
76
|
+
apply_limits(relation, start, finish, order).size
|
76
77
|
end
|
77
78
|
end
|
78
79
|
end
|
@@ -101,6 +102,7 @@ module ActiveRecord
|
|
101
102
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
102
103
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
103
104
|
# an order is present in the relation.
|
105
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
|
104
106
|
#
|
105
107
|
# Limits are honored, and if present there is no requirement for the batch
|
106
108
|
# size: it can be less than, equal to, or greater than the limit.
|
@@ -116,23 +118,23 @@ module ActiveRecord
|
|
116
118
|
# group.each { |person| person.party_all_night! }
|
117
119
|
# end
|
118
120
|
#
|
119
|
-
# NOTE:
|
120
|
-
# ascending on the primary key ("id ASC")
|
121
|
-
#
|
121
|
+
# NOTE: Order can be ascending (:asc) or descending (:desc). It is automatically set to
|
122
|
+
# ascending on the primary key ("id ASC").
|
123
|
+
# This also means that this method only works when the primary key is
|
122
124
|
# orderable (e.g. an integer or string).
|
123
125
|
#
|
124
126
|
# NOTE: By its nature, batch processing is subject to race conditions if
|
125
127
|
# other processes are modifying the database.
|
126
|
-
def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
|
128
|
+
def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
|
127
129
|
relation = self
|
128
130
|
unless block_given?
|
129
|
-
return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
|
130
|
-
total = apply_limits(relation, start, finish).size
|
131
|
+
return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
|
132
|
+
total = apply_limits(relation, start, finish, order).size
|
131
133
|
(total - 1).div(batch_size) + 1
|
132
134
|
end
|
133
135
|
end
|
134
136
|
|
135
|
-
in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore) do |batch|
|
137
|
+
in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore, order: order) do |batch|
|
136
138
|
yield batch.to_a
|
137
139
|
end
|
138
140
|
end
|
@@ -165,6 +167,7 @@ module ActiveRecord
|
|
165
167
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
166
168
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
167
169
|
# an order is present in the relation.
|
170
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
|
168
171
|
#
|
169
172
|
# Limits are honored, and if present there is no requirement for the batch
|
170
173
|
# size, it can be less than, equal, or greater than the limit.
|
@@ -191,19 +194,23 @@ module ActiveRecord
|
|
191
194
|
#
|
192
195
|
# Person.in_batches.each_record(&:party_all_night!)
|
193
196
|
#
|
194
|
-
# NOTE:
|
195
|
-
# ascending on the primary key ("id ASC")
|
196
|
-
#
|
197
|
-
# or
|
197
|
+
# NOTE: Order can be ascending (:asc) or descending (:desc). It is automatically set to
|
198
|
+
# ascending on the primary key ("id ASC").
|
199
|
+
# This also means that this method only works when the primary key is
|
200
|
+
# orderable (e.g. an integer or string).
|
198
201
|
#
|
199
202
|
# NOTE: By its nature, batch processing is subject to race conditions if
|
200
203
|
# other processes are modifying the database.
|
201
|
-
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil)
|
204
|
+
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, order: :asc)
|
202
205
|
relation = self
|
203
206
|
unless block_given?
|
204
207
|
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
|
205
208
|
end
|
206
209
|
|
210
|
+
unless [:asc, :desc].include?(order)
|
211
|
+
raise ArgumentError, ":order must be :asc or :desc, got #{order.inspect}"
|
212
|
+
end
|
213
|
+
|
207
214
|
if arel.orders.present?
|
208
215
|
act_on_ignored_order(error_on_ignore)
|
209
216
|
end
|
@@ -214,8 +221,8 @@ module ActiveRecord
|
|
214
221
|
batch_limit = remaining if remaining < batch_limit
|
215
222
|
end
|
216
223
|
|
217
|
-
relation = relation.reorder(batch_order).limit(batch_limit)
|
218
|
-
relation = apply_limits(relation, start, finish)
|
224
|
+
relation = relation.reorder(batch_order(order)).limit(batch_limit)
|
225
|
+
relation = apply_limits(relation, start, finish, order)
|
219
226
|
relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
|
220
227
|
batch_relation = relation
|
221
228
|
|
@@ -252,28 +259,28 @@ module ActiveRecord
|
|
252
259
|
end
|
253
260
|
|
254
261
|
batch_relation = relation.where(
|
255
|
-
|
262
|
+
predicate_builder[primary_key, primary_key_offset, order == :desc ? :lt : :gt]
|
256
263
|
)
|
257
264
|
end
|
258
265
|
end
|
259
266
|
|
260
267
|
private
|
261
|
-
def apply_limits(relation, start, finish)
|
262
|
-
relation = apply_start_limit(relation, start) if start
|
263
|
-
relation = apply_finish_limit(relation, finish) if finish
|
268
|
+
def apply_limits(relation, start, finish, order)
|
269
|
+
relation = apply_start_limit(relation, start, order) if start
|
270
|
+
relation = apply_finish_limit(relation, finish, order) if finish
|
264
271
|
relation
|
265
272
|
end
|
266
273
|
|
267
|
-
def apply_start_limit(relation, start)
|
268
|
-
relation.where(
|
274
|
+
def apply_start_limit(relation, start, order)
|
275
|
+
relation.where(predicate_builder[primary_key, start, order == :desc ? :lteq : :gteq])
|
269
276
|
end
|
270
277
|
|
271
|
-
def apply_finish_limit(relation, finish)
|
272
|
-
relation.where(
|
278
|
+
def apply_finish_limit(relation, finish, order)
|
279
|
+
relation.where(predicate_builder[primary_key, finish, order == :desc ? :gteq : :lteq])
|
273
280
|
end
|
274
281
|
|
275
|
-
def batch_order
|
276
|
-
|
282
|
+
def batch_order(order)
|
283
|
+
table[primary_key].public_send(order)
|
277
284
|
end
|
278
285
|
|
279
286
|
def act_on_ignored_order(error_on_ignore)
|
@@ -41,19 +41,35 @@ module ActiveRecord
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
#
|
44
|
+
# Deletes records in batches. Returns the total number of rows affected.
|
45
45
|
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
46
|
+
# Person.in_batches.delete_all
|
47
|
+
#
|
48
|
+
# See Relation#delete_all for details of how each batch is deleted.
|
49
|
+
def delete_all
|
50
|
+
sum(&:delete_all)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Updates records in batches. Returns the total number of rows affected.
|
54
|
+
#
|
55
|
+
# Person.in_batches.update_all("age = age + 1")
|
56
|
+
#
|
57
|
+
# See Relation#update_all for details of how each batch is updated.
|
58
|
+
def update_all(updates)
|
59
|
+
sum do |relation|
|
60
|
+
relation.update_all(updates)
|
54
61
|
end
|
55
62
|
end
|
56
63
|
|
64
|
+
# Destroys records in batches.
|
65
|
+
#
|
66
|
+
# Person.where("age < 10").in_batches.destroy_all
|
67
|
+
#
|
68
|
+
# See Relation#destroy_all for details of how each batch is destroyed.
|
69
|
+
def destroy_all
|
70
|
+
each(&:destroy_all)
|
71
|
+
end
|
72
|
+
|
57
73
|
# Yields an ActiveRecord::Relation object for each batch of records.
|
58
74
|
#
|
59
75
|
# Person.in_batches.each do |relation|
|
@@ -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)
|
@@ -179,7 +181,7 @@ module ActiveRecord
|
|
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,35 @@ 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.deserialize(value)
|
314
|
+
end
|
306
315
|
end
|
307
316
|
|
308
317
|
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
309
318
|
group_fields = group_values
|
319
|
+
group_fields = group_fields.uniq if group_fields.size > 1
|
320
|
+
|
321
|
+
unless group_fields == group_values
|
322
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
323
|
+
`#{operation}` with group by duplicated fields does no longer affect to result in Rails 6.2.
|
324
|
+
To migrate to Rails 6.2's behavior, use `uniq!(:group)` to deduplicate group fields
|
325
|
+
(`#{klass.name&.tableize || klass.table_name}.uniq!(:group).#{operation}(#{column_name.inspect})`).
|
326
|
+
MSG
|
327
|
+
group_fields = group_values
|
328
|
+
end
|
310
329
|
|
311
330
|
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
|
312
331
|
association = klass._reflect_on_association(group_fields.first)
|
@@ -321,14 +340,12 @@ module ActiveRecord
|
|
321
340
|
}
|
322
341
|
group_columns = group_aliases.zip(group_fields)
|
323
342
|
|
324
|
-
|
343
|
+
column = aggregate_column(column_name)
|
344
|
+
column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
|
345
|
+
select_value = operation_over_aggregate_column(column, operation, distinct)
|
346
|
+
select_value.as(column_alias)
|
325
347
|
|
326
|
-
select_values = [
|
327
|
-
operation_over_aggregate_column(
|
328
|
-
aggregate_column(column_name),
|
329
|
-
operation,
|
330
|
-
distinct).as(aggregate_alias)
|
331
|
-
]
|
348
|
+
select_values = [select_value]
|
332
349
|
select_values += self.select_values unless having_clause.empty?
|
333
350
|
|
334
351
|
select_values.concat group_columns.map { |aliaz, field|
|
@@ -348,22 +365,33 @@ module ActiveRecord
|
|
348
365
|
if association
|
349
366
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
350
367
|
key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
|
351
|
-
key_records =
|
368
|
+
key_records = key_records.index_by(&:id)
|
352
369
|
end
|
353
370
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
371
|
+
key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
|
372
|
+
types[aliaz] = type_for(col_name) do
|
373
|
+
calculated_data.column_types.fetch(aliaz, Type.default_value)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
hash_rows = calculated_data.cast_values(key_types).map! do |row|
|
378
|
+
calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
|
379
|
+
hash[col_name] = row[i]
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
type = nil
|
384
|
+
hash_rows.each_with_object({}) do |row, result|
|
385
|
+
key = group_aliases.map { |aliaz| row[aliaz] }
|
361
386
|
key = key.first if key.size == 1
|
362
387
|
key = key_records[key] if associated
|
363
388
|
|
364
|
-
|
365
|
-
|
366
|
-
|
389
|
+
result[key] = type_cast_calculated_value(row[column_alias], operation) do |value|
|
390
|
+
type ||= column.try(:type_caster) ||
|
391
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
392
|
+
type.deserialize(value)
|
393
|
+
end
|
394
|
+
end
|
367
395
|
end
|
368
396
|
|
369
397
|
# Converts the given field to the value that the database adapter returns as
|
@@ -388,12 +416,41 @@ module ActiveRecord
|
|
388
416
|
@klass.type_for_attribute(field_name, &block)
|
389
417
|
end
|
390
418
|
|
391
|
-
def
|
419
|
+
def lookup_cast_type_from_join_dependencies(name, join_dependencies = build_join_dependencies)
|
420
|
+
each_join_dependencies(join_dependencies) do |join|
|
421
|
+
type = join.base_klass.attribute_types.fetch(name, nil)
|
422
|
+
return type if type
|
423
|
+
end
|
424
|
+
nil
|
425
|
+
end
|
426
|
+
|
427
|
+
def type_cast_pluck_values(result, columns)
|
428
|
+
cast_types = if result.columns.size != columns.size
|
429
|
+
klass.attribute_types
|
430
|
+
else
|
431
|
+
join_dependencies = nil
|
432
|
+
columns.map.with_index do |column, i|
|
433
|
+
column.try(:type_caster) ||
|
434
|
+
klass.attribute_types.fetch(name = result.columns[i]) do
|
435
|
+
join_dependencies ||= build_join_dependencies
|
436
|
+
lookup_cast_type_from_join_dependencies(name, join_dependencies) ||
|
437
|
+
result.column_types[name] || Type.default_value
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
result.cast_values(cast_types)
|
442
|
+
end
|
443
|
+
|
444
|
+
def type_cast_calculated_value(value, operation)
|
392
445
|
case operation
|
393
|
-
when "count"
|
394
|
-
|
395
|
-
when "
|
396
|
-
|
446
|
+
when "count"
|
447
|
+
value.to_i
|
448
|
+
when "sum"
|
449
|
+
yield value || 0
|
450
|
+
when "average"
|
451
|
+
value&.respond_to?(:to_d) ? value.to_d : value
|
452
|
+
else # "minimum", "maximum"
|
453
|
+
yield value
|
397
454
|
end
|
398
455
|
end
|
399
456
|
|
@@ -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,7 +60,7 @@ 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)
|
63
64
|
definition = RUBY_VERSION >= "2.7" ? "..." : "*args, &block"
|
64
65
|
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
65
66
|
def #{method}(#{definition})
|