activerecord 6.1.7 → 7.2.0
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 +520 -1385
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +404 -509
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +2 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +51 -49
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +18 -34
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- 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/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- 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/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +276 -251
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +3 -3
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -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 +170 -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 +157 -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 +53 -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_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +163 -63
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +374 -177
- data/lib/active_record/model_schema.rb +143 -159
- data/lib/active_record/nested_attributes.rb +48 -21
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +234 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +325 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +300 -111
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +842 -150
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +277 -149
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +118 -41
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +64 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- 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 +100 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -5,11 +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:, order: :asc, use_ranges: nil) # :nodoc:
|
9
9
|
@of = of
|
10
10
|
@relation = relation
|
11
11
|
@start = start
|
12
12
|
@finish = finish
|
13
|
+
@order = order
|
14
|
+
@use_ranges = use_ranges
|
15
|
+
end
|
16
|
+
|
17
|
+
# The primary key value from which the BatchEnumerator starts, inclusive of the value.
|
18
|
+
attr_reader :start
|
19
|
+
|
20
|
+
# The primary key value at which the BatchEnumerator ends, inclusive of the value.
|
21
|
+
attr_reader :finish
|
22
|
+
|
23
|
+
# The relation from which the BatchEnumerator yields batches.
|
24
|
+
attr_reader :relation
|
25
|
+
|
26
|
+
# The size of the batches yielded by the BatchEnumerator.
|
27
|
+
def batch_size
|
28
|
+
@of
|
13
29
|
end
|
14
30
|
|
15
31
|
# Looping through a collection of records from the database (using the
|
@@ -33,11 +49,11 @@ module ActiveRecord
|
|
33
49
|
# Person.in_batches.each_record.with_index do |person, index|
|
34
50
|
# person.award_trophy(index + 1)
|
35
51
|
# end
|
36
|
-
def each_record
|
52
|
+
def each_record(&block)
|
37
53
|
return to_enum(:each_record) unless block_given?
|
38
54
|
|
39
|
-
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true).each do |relation|
|
40
|
-
relation.records.each
|
55
|
+
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true, order: @order).each do |relation|
|
56
|
+
relation.records.each(&block)
|
41
57
|
end
|
42
58
|
end
|
43
59
|
|
@@ -61,13 +77,26 @@ module ActiveRecord
|
|
61
77
|
end
|
62
78
|
end
|
63
79
|
|
64
|
-
#
|
80
|
+
# Touches records in batches. Returns the total number of rows affected.
|
81
|
+
#
|
82
|
+
# Person.in_batches.touch_all
|
83
|
+
#
|
84
|
+
# See Relation#touch_all for details of how each batch is touched.
|
85
|
+
def touch_all(...)
|
86
|
+
sum do |relation|
|
87
|
+
relation.touch_all(...)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Destroys records in batches. Returns the total number of rows affected.
|
65
92
|
#
|
66
93
|
# Person.where("age < 10").in_batches.destroy_all
|
67
94
|
#
|
68
95
|
# See Relation#destroy_all for details of how each batch is destroyed.
|
69
96
|
def destroy_all
|
70
|
-
|
97
|
+
sum do |relation|
|
98
|
+
relation.destroy_all.count(&:destroyed?)
|
99
|
+
end
|
71
100
|
end
|
72
101
|
|
73
102
|
# Yields an ActiveRecord::Relation object for each batch of records.
|
@@ -75,9 +104,9 @@ module ActiveRecord
|
|
75
104
|
# Person.in_batches.each do |relation|
|
76
105
|
# relation.update_all(awesome: true)
|
77
106
|
# end
|
78
|
-
def each
|
79
|
-
enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false)
|
80
|
-
return enum.each
|
107
|
+
def each(&block)
|
108
|
+
enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false, order: @order, use_ranges: @use_ranges)
|
109
|
+
return enum.each(&block) if block_given?
|
81
110
|
enum
|
82
111
|
end
|
83
112
|
end
|
@@ -3,8 +3,10 @@
|
|
3
3
|
require "active_record/relation/batches/batch_enumerator"
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
|
+
# = Active Record \Batches
|
6
7
|
module Batches
|
7
8
|
ORDER_IGNORE_MESSAGE = "Scoped order is ignored, it's forced to be batch order."
|
9
|
+
DEFAULT_ORDER = :asc
|
8
10
|
|
9
11
|
# Looping through a collection of records from the database
|
10
12
|
# (using the Scoping::Named::ClassMethods.all method, for example)
|
@@ -37,7 +39,16 @@ module ActiveRecord
|
|
37
39
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
38
40
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
39
41
|
# an order is present in the relation.
|
40
|
-
# * <tt>:order</tt> - Specifies the primary key order (can be
|
42
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be +:asc+ or +:desc+ or an array consisting
|
43
|
+
# of :asc or :desc). Defaults to +:asc+.
|
44
|
+
#
|
45
|
+
# class Order < ActiveRecord::Base
|
46
|
+
# self.primary_key = [:id_1, :id_2]
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# Order.find_each(order: [:asc, :desc])
|
50
|
+
#
|
51
|
+
# In the above code, +id_1+ is sorted in ascending order and +id_2+ in descending order.
|
41
52
|
#
|
42
53
|
# Limits are honored, and if present there is no requirement for the batch
|
43
54
|
# size: it can be less than, equal to, or greater than the limit.
|
@@ -65,15 +76,15 @@ module ActiveRecord
|
|
65
76
|
#
|
66
77
|
# NOTE: By its nature, batch processing is subject to race conditions if
|
67
78
|
# other processes are modifying the database.
|
68
|
-
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order:
|
79
|
+
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: DEFAULT_ORDER, &block)
|
69
80
|
if block_given?
|
70
81
|
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do |records|
|
71
|
-
records.each
|
82
|
+
records.each(&block)
|
72
83
|
end
|
73
84
|
else
|
74
85
|
enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
|
75
86
|
relation = self
|
76
|
-
apply_limits(relation, start, finish, order).size
|
87
|
+
apply_limits(relation, start, finish, build_batch_orders(order)).size
|
77
88
|
end
|
78
89
|
end
|
79
90
|
end
|
@@ -102,7 +113,16 @@ module ActiveRecord
|
|
102
113
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
103
114
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
104
115
|
# an order is present in the relation.
|
105
|
-
# * <tt>:order</tt> - Specifies the primary key order (can be
|
116
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be +:asc+ or +:desc+ or an array consisting
|
117
|
+
# of :asc or :desc). Defaults to +:asc+.
|
118
|
+
#
|
119
|
+
# class Order < ActiveRecord::Base
|
120
|
+
# self.primary_key = [:id_1, :id_2]
|
121
|
+
# end
|
122
|
+
#
|
123
|
+
# Order.find_in_batches(order: [:asc, :desc])
|
124
|
+
#
|
125
|
+
# In the above code, +id_1+ is sorted in ascending order and +id_2+ in descending order.
|
106
126
|
#
|
107
127
|
# Limits are honored, and if present there is no requirement for the batch
|
108
128
|
# size: it can be less than, equal to, or greater than the limit.
|
@@ -125,11 +145,11 @@ module ActiveRecord
|
|
125
145
|
#
|
126
146
|
# NOTE: By its nature, batch processing is subject to race conditions if
|
127
147
|
# other processes are modifying the database.
|
128
|
-
def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order:
|
148
|
+
def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: DEFAULT_ORDER)
|
129
149
|
relation = self
|
130
150
|
unless block_given?
|
131
151
|
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
|
152
|
+
total = apply_limits(relation, start, finish, build_batch_orders(order)).size
|
133
153
|
(total - 1).div(batch_size) + 1
|
134
154
|
end
|
135
155
|
end
|
@@ -167,7 +187,22 @@ module ActiveRecord
|
|
167
187
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
168
188
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
169
189
|
# an order is present in the relation.
|
170
|
-
# * <tt>:order</tt> - Specifies the primary key order (can be
|
190
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be +:asc+ or +:desc+ or an array consisting
|
191
|
+
# of :asc or :desc). Defaults to +:asc+.
|
192
|
+
#
|
193
|
+
# class Order < ActiveRecord::Base
|
194
|
+
# self.primary_key = [:id_1, :id_2]
|
195
|
+
# end
|
196
|
+
#
|
197
|
+
# Order.in_batches(order: [:asc, :desc])
|
198
|
+
#
|
199
|
+
# In the above code, +id_1+ is sorted in ascending order and +id_2+ in descending order.
|
200
|
+
#
|
201
|
+
# * <tt>:use_ranges</tt> - Specifies whether to use range iteration (id >= x AND id <= y).
|
202
|
+
# It can make iterating over the whole or almost whole tables several times faster.
|
203
|
+
# Only whole table iterations use this style of iteration by default. You can disable this behavior by passing +false+.
|
204
|
+
# If you iterate over the table and the only condition is, e.g., <tt>archived_at: nil</tt> (and only a tiny fraction
|
205
|
+
# of the records are archived), it makes sense to opt in to this approach.
|
171
206
|
#
|
172
207
|
# Limits are honored, and if present there is no requirement for the batch
|
173
208
|
# size, it can be less than, equal, or greater than the limit.
|
@@ -201,14 +236,13 @@ module ActiveRecord
|
|
201
236
|
#
|
202
237
|
# NOTE: By its nature, batch processing is subject to race conditions if
|
203
238
|
# other processes are modifying the database.
|
204
|
-
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, order: :
|
205
|
-
|
206
|
-
|
207
|
-
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
|
239
|
+
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, order: DEFAULT_ORDER, use_ranges: nil, &block)
|
240
|
+
unless Array(order).all? { |ord| [:asc, :desc].include?(ord) }
|
241
|
+
raise ArgumentError, ":order must be :asc or :desc or an array consisting of :asc or :desc, got #{order.inspect}"
|
208
242
|
end
|
209
243
|
|
210
|
-
unless
|
211
|
-
|
244
|
+
unless block
|
245
|
+
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self, order: order, use_ranges: use_ranges)
|
212
246
|
end
|
213
247
|
|
214
248
|
if arel.orders.present?
|
@@ -216,75 +250,80 @@ module ActiveRecord
|
|
216
250
|
end
|
217
251
|
|
218
252
|
batch_limit = of
|
253
|
+
|
219
254
|
if limit_value
|
220
255
|
remaining = limit_value
|
221
256
|
batch_limit = remaining if remaining < batch_limit
|
222
257
|
end
|
223
258
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
yield yielded_relation
|
246
|
-
|
247
|
-
break if ids.length < batch_limit
|
248
|
-
|
249
|
-
if limit_value
|
250
|
-
remaining -= ids.length
|
251
|
-
|
252
|
-
if remaining == 0
|
253
|
-
# Saves a useless iteration when the limit is a multiple of the
|
254
|
-
# batch size.
|
255
|
-
break
|
256
|
-
elsif remaining < batch_limit
|
257
|
-
relation = relation.limit(remaining)
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
batch_relation = relation.where(
|
262
|
-
predicate_builder[primary_key, primary_key_offset, order == :desc ? :lt : :gt]
|
259
|
+
if self.loaded?
|
260
|
+
batch_on_loaded_relation(
|
261
|
+
relation: self,
|
262
|
+
start: start,
|
263
|
+
finish: finish,
|
264
|
+
order: order,
|
265
|
+
batch_limit: batch_limit,
|
266
|
+
&block
|
267
|
+
)
|
268
|
+
else
|
269
|
+
batch_on_unloaded_relation(
|
270
|
+
relation: self,
|
271
|
+
start: start,
|
272
|
+
finish: finish,
|
273
|
+
load: load,
|
274
|
+
order: order,
|
275
|
+
use_ranges: use_ranges,
|
276
|
+
remaining: remaining,
|
277
|
+
batch_limit: batch_limit,
|
278
|
+
&block
|
263
279
|
)
|
264
280
|
end
|
265
281
|
end
|
266
282
|
|
267
283
|
private
|
268
|
-
def apply_limits(relation, start, finish,
|
269
|
-
relation = apply_start_limit(relation, start,
|
270
|
-
relation = apply_finish_limit(relation, finish,
|
284
|
+
def apply_limits(relation, start, finish, batch_orders)
|
285
|
+
relation = apply_start_limit(relation, start, batch_orders) if start
|
286
|
+
relation = apply_finish_limit(relation, finish, batch_orders) if finish
|
271
287
|
relation
|
272
288
|
end
|
273
289
|
|
274
|
-
def apply_start_limit(relation, start,
|
275
|
-
|
290
|
+
def apply_start_limit(relation, start, batch_orders)
|
291
|
+
operators = batch_orders.map do |_column, order|
|
292
|
+
order == :desc ? :lteq : :gteq
|
293
|
+
end
|
294
|
+
batch_condition(relation, primary_key, start, operators)
|
276
295
|
end
|
277
296
|
|
278
|
-
def apply_finish_limit(relation, finish,
|
279
|
-
|
297
|
+
def apply_finish_limit(relation, finish, batch_orders)
|
298
|
+
operators = batch_orders.map do |_column, order|
|
299
|
+
order == :desc ? :gteq : :lteq
|
300
|
+
end
|
301
|
+
batch_condition(relation, primary_key, finish, operators)
|
280
302
|
end
|
281
303
|
|
282
|
-
def
|
283
|
-
|
304
|
+
def batch_condition(relation, columns, values, operators)
|
305
|
+
cursor_positions = Array(columns).zip(Array(values), operators)
|
306
|
+
|
307
|
+
first_clause_column, first_clause_value, operator = cursor_positions.pop
|
308
|
+
where_clause = predicate_builder[first_clause_column, first_clause_value, operator]
|
309
|
+
|
310
|
+
cursor_positions.reverse_each do |column_name, value, operator|
|
311
|
+
where_clause = predicate_builder[column_name, value, operator == :lteq ? :lt : :gt].or(
|
312
|
+
predicate_builder[column_name, value, :eq].and(where_clause)
|
313
|
+
)
|
314
|
+
end
|
315
|
+
|
316
|
+
relation.where(where_clause)
|
317
|
+
end
|
318
|
+
|
319
|
+
def build_batch_orders(order)
|
320
|
+
get_the_order_of_primary_key(order).map do |column, ord|
|
321
|
+
[column, ord || DEFAULT_ORDER]
|
322
|
+
end
|
284
323
|
end
|
285
324
|
|
286
325
|
def act_on_ignored_order(error_on_ignore)
|
287
|
-
raise_error = (error_on_ignore.nil? ?
|
326
|
+
raise_error = (error_on_ignore.nil? ? ActiveRecord.error_on_ignored_order : error_on_ignore)
|
288
327
|
|
289
328
|
if raise_error
|
290
329
|
raise ArgumentError.new(ORDER_IGNORE_MESSAGE)
|
@@ -292,5 +331,101 @@ module ActiveRecord
|
|
292
331
|
logger.warn(ORDER_IGNORE_MESSAGE)
|
293
332
|
end
|
294
333
|
end
|
334
|
+
|
335
|
+
def get_the_order_of_primary_key(order)
|
336
|
+
Array(primary_key).zip(Array(order))
|
337
|
+
end
|
338
|
+
|
339
|
+
def batch_on_loaded_relation(relation:, start:, finish:, order:, batch_limit:)
|
340
|
+
records = relation.to_a
|
341
|
+
|
342
|
+
if start || finish
|
343
|
+
records = records.filter do |record|
|
344
|
+
id = record.id
|
345
|
+
|
346
|
+
if order == :asc
|
347
|
+
(start.nil? || id >= start) && (finish.nil? || id <= finish)
|
348
|
+
else
|
349
|
+
(start.nil? || id <= start) && (finish.nil? || id >= finish)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
records.sort_by!(&:id)
|
355
|
+
|
356
|
+
if order == :desc
|
357
|
+
records.reverse!
|
358
|
+
end
|
359
|
+
|
360
|
+
records.each_slice(batch_limit) do |subrecords|
|
361
|
+
subrelation = relation.spawn
|
362
|
+
subrelation.load_records(subrecords)
|
363
|
+
|
364
|
+
yield subrelation
|
365
|
+
end
|
366
|
+
|
367
|
+
nil
|
368
|
+
end
|
369
|
+
|
370
|
+
def batch_on_unloaded_relation(relation:, start:, finish:, load:, order:, use_ranges:, remaining:, batch_limit:)
|
371
|
+
batch_orders = build_batch_orders(order)
|
372
|
+
relation = relation.reorder(batch_orders.to_h).limit(batch_limit)
|
373
|
+
relation = apply_limits(relation, start, finish, batch_orders)
|
374
|
+
relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
|
375
|
+
batch_relation = relation
|
376
|
+
empty_scope = to_sql == klass.unscoped.all.to_sql
|
377
|
+
|
378
|
+
loop do
|
379
|
+
if load
|
380
|
+
records = batch_relation.records
|
381
|
+
ids = records.map(&:id)
|
382
|
+
yielded_relation = where(primary_key => ids)
|
383
|
+
yielded_relation.load_records(records)
|
384
|
+
elsif (empty_scope && use_ranges != false) || use_ranges
|
385
|
+
ids = batch_relation.ids
|
386
|
+
finish = ids.last
|
387
|
+
if finish
|
388
|
+
yielded_relation = apply_finish_limit(batch_relation, finish, batch_orders)
|
389
|
+
yielded_relation = yielded_relation.except(:limit, :order)
|
390
|
+
yielded_relation.skip_query_cache!(false)
|
391
|
+
end
|
392
|
+
else
|
393
|
+
ids = batch_relation.ids
|
394
|
+
yielded_relation = where(primary_key => ids)
|
395
|
+
end
|
396
|
+
|
397
|
+
break if ids.empty?
|
398
|
+
|
399
|
+
primary_key_offset = ids.last
|
400
|
+
raise ArgumentError.new("Primary key not included in the custom select clause") unless primary_key_offset
|
401
|
+
|
402
|
+
yield yielded_relation
|
403
|
+
|
404
|
+
break if ids.length < batch_limit
|
405
|
+
|
406
|
+
if limit_value
|
407
|
+
remaining -= ids.length
|
408
|
+
|
409
|
+
if remaining == 0
|
410
|
+
# Saves a useless iteration when the limit is a multiple of the
|
411
|
+
# batch size.
|
412
|
+
break
|
413
|
+
elsif remaining < batch_limit
|
414
|
+
relation = relation.limit(remaining)
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
batch_orders_copy = batch_orders.dup
|
419
|
+
_last_column, last_order = batch_orders_copy.pop
|
420
|
+
operators = batch_orders_copy.map do |_column, order|
|
421
|
+
order == :desc ? :lteq : :gteq
|
422
|
+
end
|
423
|
+
operators << (last_order == :desc ? :lt : :gt)
|
424
|
+
|
425
|
+
batch_relation = batch_condition(relation, primary_key, primary_key_offset, operators)
|
426
|
+
end
|
427
|
+
|
428
|
+
nil
|
429
|
+
end
|
295
430
|
end
|
296
431
|
end
|