activerecord 5.2.3 → 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 +898 -532
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +5 -4
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +95 -42
- data/lib/active_record/associations/association_scope.rb +21 -21
- data/lib/active_record/associations/belongs_to_association.rb +50 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -5
- data/lib/active_record/associations/builder/association.rb +23 -21
- data/lib/active_record/associations/builder/belongs_to.rb +29 -59
- data/lib/active_record/associations/builder/collection_association.rb +10 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -2
- data/lib/active_record/associations/builder/has_one.rb +33 -2
- data/lib/active_record/associations/builder/singular_association.rb +3 -1
- data/lib/active_record/associations/collection_association.rb +31 -29
- data/lib/active_record/associations/collection_proxy.rb +25 -21
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +26 -13
- data/lib/active_record/associations/has_many_through_association.rb +27 -28
- data/lib/active_record/associations/has_one_association.rb +43 -31
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +91 -60
- data/lib/active_record/associations/preloader/association.rb +71 -43
- data/lib/active_record/associations/preloader/through_association.rb +49 -40
- data/lib/active_record/associations/preloader.rb +48 -35
- data/lib/active_record/associations/singular_association.rb +3 -17
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +133 -25
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -40
- data/lib/active_record/attribute_methods/primary_key.rb +20 -25
- data/lib/active_record/attribute_methods/query.rb +4 -8
- data/lib/active_record/attribute_methods/read.rb +14 -56
- data/lib/active_record/attribute_methods/serialization.rb +12 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +18 -34
- data/lib/active_record/attribute_methods.rb +81 -143
- data/lib/active_record/attributes.rb +45 -8
- data/lib/active_record/autosave_association.rb +76 -47
- data/lib/active_record/base.rb +4 -17
- data/lib/active_record/callbacks.rb +158 -43
- data/lib/active_record/coders/yaml_column.rb +1 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +293 -132
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +21 -17
- data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +203 -90
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +381 -146
- data/lib/active_record/connection_adapters/abstract/transaction.rb +155 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
- data/lib/active_record/connection_adapters/column.rb +30 -12
- 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/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
- 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 +37 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +222 -112
- data/lib/active_record/connection_adapters/schema_cache.rb +127 -21
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +19 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +175 -187
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_handling.rb +285 -33
- data/lib/active_record/core.rb +308 -100
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +3 -4
- data/lib/active_record/enum.rb +71 -17
- data/lib/active_record/errors.rb +62 -19
- data/lib/active_record/explain.rb +10 -6
- 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 +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +197 -481
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +53 -24
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +67 -17
- data/lib/active_record/internal_metadata.rb +26 -9
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +26 -22
- data/lib/active_record/locking/pessimistic.rb +9 -5
- data/lib/active_record/log_subscriber.rb +34 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +141 -64
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +205 -156
- data/lib/active_record/model_schema.rb +148 -22
- data/lib/active_record/nested_attributes.rb +4 -7
- data/lib/active_record/no_touching.rb +8 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +267 -59
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/querying.rb +40 -23
- data/lib/active_record/railtie.rb +115 -58
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +402 -78
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +113 -101
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +44 -35
- data/lib/active_record/relation/calculations.rb +157 -93
- data/lib/active_record/relation/delegation.rb +35 -50
- data/lib/active_record/relation/finder_methods.rb +65 -40
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +32 -40
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -7
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +58 -40
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +487 -199
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +9 -9
- data/lib/active_record/relation/where_clause.rb +108 -58
- data/lib/active_record/relation.rb +375 -104
- data/lib/active_record/result.rb +64 -38
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +22 -41
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +54 -9
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +6 -8
- data/lib/active_record/scoping/named.rb +17 -24
- data/lib/active_record/scoping.rb +8 -9
- 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 +51 -8
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -43
- data/lib/active_record/tasks/database_tasks.rb +276 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +43 -32
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +59 -117
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +3 -13
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +10 -5
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +38 -30
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +13 -12
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +70 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/migration.rb +19 -2
- 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
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +117 -32
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/string/filters"
|
4
|
-
require "concurrent/map"
|
5
4
|
|
6
5
|
module ActiveRecord
|
7
6
|
# = Active Record Reflection
|
@@ -13,33 +12,37 @@ module ActiveRecord
|
|
13
12
|
class_attribute :aggregate_reflections, instance_writer: false, default: {}
|
14
13
|
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
when :has_many
|
22
|
-
HasManyReflection
|
23
|
-
when :has_one
|
24
|
-
HasOneReflection
|
25
|
-
when :belongs_to
|
26
|
-
BelongsToReflection
|
27
|
-
else
|
28
|
-
raise "Unsupported Macro: #{macro}"
|
29
|
-
end
|
15
|
+
class << self
|
16
|
+
def create(macro, name, scope, options, ar)
|
17
|
+
reflection = reflection_class_for(macro).new(name, scope, options, ar)
|
18
|
+
options[:through] ? ThroughReflection.new(reflection) : reflection
|
19
|
+
end
|
30
20
|
|
31
|
-
|
32
|
-
|
33
|
-
|
21
|
+
def add_reflection(ar, name, reflection)
|
22
|
+
ar.clear_reflections_cache
|
23
|
+
name = -name.to_s
|
24
|
+
ar._reflections = ar._reflections.except(name).merge!(name => reflection)
|
25
|
+
end
|
34
26
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
ar._reflections = ar._reflections.except(name).merge!(name => reflection)
|
39
|
-
end
|
27
|
+
def add_aggregate_reflection(ar, name, reflection)
|
28
|
+
ar.aggregate_reflections = ar.aggregate_reflections.merge(-name.to_s => reflection)
|
29
|
+
end
|
40
30
|
|
41
|
-
|
42
|
-
|
31
|
+
private
|
32
|
+
def reflection_class_for(macro)
|
33
|
+
case macro
|
34
|
+
when :composed_of
|
35
|
+
AggregateReflection
|
36
|
+
when :has_many
|
37
|
+
HasManyReflection
|
38
|
+
when :has_one
|
39
|
+
HasOneReflection
|
40
|
+
when :belongs_to
|
41
|
+
BelongsToReflection
|
42
|
+
else
|
43
|
+
raise "Unsupported Macro: #{macro}"
|
44
|
+
end
|
45
|
+
end
|
43
46
|
end
|
44
47
|
|
45
48
|
# \Reflection enables the ability to examine the associations and aggregations of
|
@@ -159,13 +162,7 @@ module ActiveRecord
|
|
159
162
|
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
160
163
|
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
161
164
|
def class_name
|
162
|
-
@class_name ||= (options[:class_name] || derive_class_name)
|
163
|
-
end
|
164
|
-
|
165
|
-
JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
|
166
|
-
|
167
|
-
def join_keys
|
168
|
-
@join_keys ||= get_join_keys(klass)
|
165
|
+
@class_name ||= -(options[:class_name]&.to_s || derive_class_name)
|
169
166
|
end
|
170
167
|
|
171
168
|
# Returns a list of scopes that should be applied for this Reflection
|
@@ -174,20 +171,7 @@ module ActiveRecord
|
|
174
171
|
scope ? [scope] : []
|
175
172
|
end
|
176
173
|
|
177
|
-
def
|
178
|
-
key = join_keys.key
|
179
|
-
foreign_key = join_keys.foreign_key
|
180
|
-
|
181
|
-
constraint = table[key].eq(foreign_table[foreign_key])
|
182
|
-
|
183
|
-
if klass.finder_needs_type_condition?
|
184
|
-
table.create_and([constraint, klass.send(:type_condition, table)])
|
185
|
-
else
|
186
|
-
constraint
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
def join_scope(table, foreign_klass)
|
174
|
+
def join_scope(table, foreign_table, foreign_klass)
|
191
175
|
predicate_builder = predicate_builder(table)
|
192
176
|
scope_chain_items = join_scopes(table, predicate_builder)
|
193
177
|
klass_scope = klass_join_scope(table, predicate_builder)
|
@@ -197,11 +181,22 @@ module ActiveRecord
|
|
197
181
|
end
|
198
182
|
|
199
183
|
scope_chain_items.inject(klass_scope, &:merge!)
|
184
|
+
|
185
|
+
primary_key = join_primary_key
|
186
|
+
foreign_key = join_foreign_key
|
187
|
+
|
188
|
+
klass_scope.where!(table[primary_key].eq(foreign_table[foreign_key]))
|
189
|
+
|
190
|
+
if klass.finder_needs_type_condition?
|
191
|
+
klass_scope.where!(klass.send(:type_condition, table))
|
192
|
+
end
|
193
|
+
|
194
|
+
klass_scope
|
200
195
|
end
|
201
196
|
|
202
|
-
def join_scopes(table, predicate_builder) # :nodoc:
|
197
|
+
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
203
198
|
if scope
|
204
|
-
[scope_for(build_scope(table, predicate_builder))]
|
199
|
+
[scope_for(build_scope(table, predicate_builder, klass))]
|
205
200
|
else
|
206
201
|
[]
|
207
202
|
end
|
@@ -217,14 +212,14 @@ module ActiveRecord
|
|
217
212
|
end
|
218
213
|
|
219
214
|
def counter_cache_column
|
220
|
-
if belongs_to?
|
215
|
+
@counter_cache_column ||= if belongs_to?
|
221
216
|
if options[:counter_cache] == true
|
222
|
-
"#{active_record.name.demodulize.underscore.pluralize}_count"
|
217
|
+
-"#{active_record.name.demodulize.underscore.pluralize}_count"
|
223
218
|
elsif options[:counter_cache]
|
224
|
-
options[:counter_cache].to_s
|
219
|
+
-options[:counter_cache].to_s
|
225
220
|
end
|
226
221
|
else
|
227
|
-
options[:counter_cache]
|
222
|
+
-(options[:counter_cache]&.to_s || "#{name}_count")
|
228
223
|
end
|
229
224
|
end
|
230
225
|
|
@@ -271,7 +266,7 @@ module ActiveRecord
|
|
271
266
|
def has_cached_counter?
|
272
267
|
options[:counter_cache] ||
|
273
268
|
inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
|
274
|
-
|
269
|
+
active_record.has_attribute?(counter_cache_column)
|
275
270
|
end
|
276
271
|
|
277
272
|
def counter_must_be_updated_by_has_many?
|
@@ -286,11 +281,7 @@ module ActiveRecord
|
|
286
281
|
collect_join_chain
|
287
282
|
end
|
288
283
|
|
289
|
-
def
|
290
|
-
JoinKeys.new(join_primary_key(association_klass), join_foreign_key)
|
291
|
-
end
|
292
|
-
|
293
|
-
def build_scope(table, predicate_builder = predicate_builder(table))
|
284
|
+
def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
|
294
285
|
Relation.create(
|
295
286
|
klass,
|
296
287
|
table: table,
|
@@ -298,12 +289,8 @@ module ActiveRecord
|
|
298
289
|
)
|
299
290
|
end
|
300
291
|
|
301
|
-
def
|
302
|
-
|
303
|
-
end
|
304
|
-
|
305
|
-
def join_foreign_key
|
306
|
-
active_record_primary_key
|
292
|
+
def strict_loading?
|
293
|
+
options[:strict_loading]
|
307
294
|
end
|
308
295
|
|
309
296
|
protected
|
@@ -417,7 +404,7 @@ module ActiveRecord
|
|
417
404
|
class AssociationReflection < MacroReflection #:nodoc:
|
418
405
|
def compute_class(name)
|
419
406
|
if polymorphic?
|
420
|
-
raise ArgumentError, "Polymorphic
|
407
|
+
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
421
408
|
end
|
422
409
|
active_record.send(:compute_type, name)
|
423
410
|
end
|
@@ -427,22 +414,21 @@ module ActiveRecord
|
|
427
414
|
|
428
415
|
def initialize(name, scope, options, active_record)
|
429
416
|
super
|
430
|
-
@type
|
431
|
-
@foreign_type =
|
417
|
+
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
418
|
+
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
432
419
|
@constructable = calculate_constructable(macro, options)
|
433
|
-
@association_scope_cache = Concurrent::Map.new
|
434
420
|
|
435
421
|
if options[:class_name] && options[:class_name].class == Class
|
436
422
|
raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
|
437
423
|
end
|
438
424
|
end
|
439
425
|
|
440
|
-
def association_scope_cache(
|
441
|
-
key =
|
426
|
+
def association_scope_cache(klass, owner, &block)
|
427
|
+
key = self
|
442
428
|
if polymorphic?
|
443
429
|
key = [key, owner._read_attribute(@foreign_type)]
|
444
430
|
end
|
445
|
-
|
431
|
+
klass.cached_find_by_statement(key, &block)
|
446
432
|
end
|
447
433
|
|
448
434
|
def constructable? # :nodoc:
|
@@ -450,24 +436,31 @@ module ActiveRecord
|
|
450
436
|
end
|
451
437
|
|
452
438
|
def join_table
|
453
|
-
@join_table ||= options[:join_table] || derive_join_table
|
439
|
+
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
454
440
|
end
|
455
441
|
|
456
442
|
def foreign_key
|
457
|
-
@foreign_key ||= options[:foreign_key] || derive_foreign_key
|
443
|
+
@foreign_key ||= -(options[:foreign_key]&.to_s || derive_foreign_key)
|
458
444
|
end
|
459
445
|
|
460
446
|
def association_foreign_key
|
461
|
-
@association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
|
447
|
+
@association_foreign_key ||= -(options[:association_foreign_key]&.to_s || class_name.foreign_key)
|
462
448
|
end
|
463
449
|
|
464
|
-
# klass option is necessary to support loading polymorphic associations
|
465
450
|
def association_primary_key(klass = nil)
|
466
|
-
|
451
|
+
primary_key(klass || self.klass)
|
467
452
|
end
|
468
453
|
|
469
454
|
def active_record_primary_key
|
470
|
-
@active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
|
455
|
+
@active_record_primary_key ||= -(options[:primary_key]&.to_s || primary_key(active_record))
|
456
|
+
end
|
457
|
+
|
458
|
+
def join_primary_key(klass = nil)
|
459
|
+
foreign_key
|
460
|
+
end
|
461
|
+
|
462
|
+
def join_foreign_key
|
463
|
+
active_record_primary_key
|
471
464
|
end
|
472
465
|
|
473
466
|
def check_validity!
|
@@ -477,7 +470,7 @@ module ActiveRecord
|
|
477
470
|
def check_preloadable!
|
478
471
|
return unless scope
|
479
472
|
|
480
|
-
|
473
|
+
unless scope.arity == 0
|
481
474
|
raise ArgumentError, <<-MSG.squish
|
482
475
|
The association scope '#{name}' is instance dependent (the scope
|
483
476
|
block takes an argument). Preloading instance dependent scopes is
|
@@ -508,7 +501,7 @@ module ActiveRecord
|
|
508
501
|
# This is for clearing cache on the reflection. Useful for tests that need to compare
|
509
502
|
# SQL queries on associations.
|
510
503
|
def clear_association_scope_cache # :nodoc:
|
511
|
-
|
504
|
+
klass.initialize_find_by_cache
|
512
505
|
end
|
513
506
|
|
514
507
|
def nested?
|
@@ -590,7 +583,6 @@ module ActiveRecord
|
|
590
583
|
end
|
591
584
|
|
592
585
|
private
|
593
|
-
|
594
586
|
def calculate_constructable(macro, options)
|
595
587
|
true
|
596
588
|
end
|
@@ -620,7 +612,7 @@ module ActiveRecord
|
|
620
612
|
end
|
621
613
|
|
622
614
|
if valid_inverse_reflection?(reflection)
|
623
|
-
|
615
|
+
inverse_name
|
624
616
|
end
|
625
617
|
end
|
626
618
|
end
|
@@ -631,6 +623,7 @@ module ActiveRecord
|
|
631
623
|
# with the current reflection's klass name.
|
632
624
|
def valid_inverse_reflection?(reflection)
|
633
625
|
reflection &&
|
626
|
+
foreign_key == reflection.foreign_key &&
|
634
627
|
klass <= reflection.active_record &&
|
635
628
|
can_find_inverse_of_automatically?(reflection)
|
636
629
|
end
|
@@ -684,10 +677,6 @@ module ActiveRecord
|
|
684
677
|
Associations::HasManyAssociation
|
685
678
|
end
|
686
679
|
end
|
687
|
-
|
688
|
-
def association_primary_key(klass = nil)
|
689
|
-
primary_key(klass || self.klass)
|
690
|
-
end
|
691
680
|
end
|
692
681
|
|
693
682
|
class HasOneReflection < AssociationReflection # :nodoc:
|
@@ -704,7 +693,6 @@ module ActiveRecord
|
|
704
693
|
end
|
705
694
|
|
706
695
|
private
|
707
|
-
|
708
696
|
def calculate_constructable(macro, options)
|
709
697
|
!options[:through]
|
710
698
|
end
|
@@ -723,6 +711,15 @@ module ActiveRecord
|
|
723
711
|
end
|
724
712
|
end
|
725
713
|
|
714
|
+
# klass option is necessary to support loading polymorphic associations
|
715
|
+
def association_primary_key(klass = nil)
|
716
|
+
if primary_key = options[:primary_key]
|
717
|
+
@association_primary_key ||= -primary_key.to_s
|
718
|
+
else
|
719
|
+
primary_key(klass || self.klass)
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
726
723
|
def join_primary_key(klass = nil)
|
727
724
|
polymorphic? ? association_primary_key(klass) : association_primary_key
|
728
725
|
end
|
@@ -731,6 +728,10 @@ module ActiveRecord
|
|
731
728
|
foreign_key
|
732
729
|
end
|
733
730
|
|
731
|
+
def join_foreign_type
|
732
|
+
foreign_type
|
733
|
+
end
|
734
|
+
|
734
735
|
private
|
735
736
|
def can_find_inverse_of_automatically?(_)
|
736
737
|
!polymorphic? && super
|
@@ -752,8 +753,8 @@ module ActiveRecord
|
|
752
753
|
# Holds all the metadata about a :through association as it was specified
|
753
754
|
# in the Active Record class.
|
754
755
|
class ThroughReflection < AbstractReflection #:nodoc:
|
755
|
-
delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for,
|
756
|
-
:active_record_primary_key, :
|
756
|
+
delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
|
757
|
+
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
757
758
|
|
758
759
|
def initialize(delegate_reflection)
|
759
760
|
@delegate_reflection = delegate_reflection
|
@@ -839,8 +840,8 @@ module ActiveRecord
|
|
839
840
|
source_reflection.scopes + super
|
840
841
|
end
|
841
842
|
|
842
|
-
def join_scopes(table, predicate_builder) # :nodoc:
|
843
|
-
source_reflection.join_scopes(table, predicate_builder) + super
|
843
|
+
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
844
|
+
source_reflection.join_scopes(table, predicate_builder, klass) + super
|
844
845
|
end
|
845
846
|
|
846
847
|
def has_scope?
|
@@ -860,7 +861,15 @@ module ActiveRecord
|
|
860
861
|
def association_primary_key(klass = nil)
|
861
862
|
# Get the "actual" source reflection if the immediate source reflection has a
|
862
863
|
# source reflection itself
|
863
|
-
actual_source_reflection.options[:primary_key]
|
864
|
+
if primary_key = actual_source_reflection.options[:primary_key]
|
865
|
+
@association_primary_key ||= -primary_key.to_s
|
866
|
+
else
|
867
|
+
primary_key(klass || self.klass)
|
868
|
+
end
|
869
|
+
end
|
870
|
+
|
871
|
+
def join_primary_key(klass = self.klass)
|
872
|
+
source_reflection.join_primary_key(klass)
|
864
873
|
end
|
865
874
|
|
866
875
|
# Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
|
@@ -909,7 +918,7 @@ module ActiveRecord
|
|
909
918
|
|
910
919
|
def check_validity!
|
911
920
|
if through_reflection.nil?
|
912
|
-
raise HasManyThroughAssociationNotFoundError.new(active_record
|
921
|
+
raise HasManyThroughAssociationNotFoundError.new(active_record, self)
|
913
922
|
end
|
914
923
|
|
915
924
|
if through_reflection.polymorphic?
|
@@ -965,16 +974,14 @@ module ActiveRecord
|
|
965
974
|
collect_join_reflections(seed + [self])
|
966
975
|
end
|
967
976
|
|
968
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
969
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
970
977
|
protected
|
971
|
-
attr_reader :delegate_reflection
|
972
|
-
|
973
978
|
def actual_source_reflection # FIXME: this is a horrible name
|
974
979
|
source_reflection.actual_source_reflection
|
975
980
|
end
|
976
981
|
|
977
982
|
private
|
983
|
+
attr_reader :delegate_reflection
|
984
|
+
|
978
985
|
def collect_join_reflections(seed)
|
979
986
|
a = source_reflection.add_as_source seed
|
980
987
|
if options[:source_type]
|
@@ -998,16 +1005,17 @@ module ActiveRecord
|
|
998
1005
|
end
|
999
1006
|
|
1000
1007
|
class PolymorphicReflection < AbstractReflection # :nodoc:
|
1001
|
-
delegate :klass, :scope, :plural_name, :type, :
|
1008
|
+
delegate :klass, :scope, :plural_name, :type, :join_primary_key, :join_foreign_key,
|
1009
|
+
:name, :scope_for, to: :@reflection
|
1002
1010
|
|
1003
1011
|
def initialize(reflection, previous_reflection)
|
1004
1012
|
@reflection = reflection
|
1005
1013
|
@previous_reflection = previous_reflection
|
1006
1014
|
end
|
1007
1015
|
|
1008
|
-
def join_scopes(table, predicate_builder) # :nodoc:
|
1016
|
+
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
1009
1017
|
scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
|
1010
|
-
scopes << build_scope(table, predicate_builder).instance_exec(nil, &source_type_scope)
|
1018
|
+
scopes << build_scope(table, predicate_builder, klass).instance_exec(nil, &source_type_scope)
|
1011
1019
|
end
|
1012
1020
|
|
1013
1021
|
def constraints
|
@@ -1023,7 +1031,7 @@ module ActiveRecord
|
|
1023
1031
|
end
|
1024
1032
|
|
1025
1033
|
class RuntimeReflection < AbstractReflection # :nodoc:
|
1026
|
-
delegate :scope, :type, :constraints, :
|
1034
|
+
delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
|
1027
1035
|
|
1028
1036
|
def initialize(reflection, association)
|
1029
1037
|
@reflection = reflection
|
@@ -1035,7 +1043,11 @@ module ActiveRecord
|
|
1035
1043
|
end
|
1036
1044
|
|
1037
1045
|
def aliased_table
|
1038
|
-
|
1046
|
+
klass.arel_table
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
def join_primary_key(klass = self.klass)
|
1050
|
+
@reflection.join_primary_key(klass)
|
1039
1051
|
end
|
1040
1052
|
|
1041
1053
|
def all_includes; yield; end
|
@@ -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|
|
@@ -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
|
|
@@ -251,27 +258,29 @@ module ActiveRecord
|
|
251
258
|
end
|
252
259
|
end
|
253
260
|
|
254
|
-
|
255
|
-
|
261
|
+
batch_relation = relation.where(
|
262
|
+
predicate_builder[primary_key, primary_key_offset, order == :desc ? :lt : :gt]
|
263
|
+
)
|
256
264
|
end
|
257
265
|
end
|
258
266
|
|
259
267
|
private
|
260
|
-
|
261
|
-
|
262
|
-
if
|
263
|
-
attr = Relation::QueryAttribute.new(primary_key, start, klass.type_for_attribute(primary_key))
|
264
|
-
relation = relation.where(arel_attribute(primary_key).gteq(Arel::Nodes::BindParam.new(attr)))
|
265
|
-
end
|
266
|
-
if finish
|
267
|
-
attr = Relation::QueryAttribute.new(primary_key, finish, klass.type_for_attribute(primary_key))
|
268
|
-
relation = relation.where(arel_attribute(primary_key).lteq(Arel::Nodes::BindParam.new(attr)))
|
269
|
-
end
|
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
|
270
271
|
relation
|
271
272
|
end
|
272
273
|
|
273
|
-
def
|
274
|
-
|
274
|
+
def apply_start_limit(relation, start, order)
|
275
|
+
relation.where(predicate_builder[primary_key, start, order == :desc ? :lteq : :gteq])
|
276
|
+
end
|
277
|
+
|
278
|
+
def apply_finish_limit(relation, finish, order)
|
279
|
+
relation.where(predicate_builder[primary_key, finish, order == :desc ? :gteq : :lteq])
|
280
|
+
end
|
281
|
+
|
282
|
+
def batch_order(order)
|
283
|
+
table[primary_key].public_send(order)
|
275
284
|
end
|
276
285
|
|
277
286
|
def act_on_ignored_order(error_on_ignore)
|