activerecord 5.1.0 → 5.2.3
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 +5 -5
- data/CHANGELOG.md +596 -450
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -5
- data/examples/performance.rb +2 -0
- data/examples/simple.rb +2 -0
- data/lib/active_record.rb +11 -4
- data/lib/active_record/aggregations.rb +6 -5
- data/lib/active_record/association_relation.rb +7 -5
- data/lib/active_record/associations.rb +77 -85
- data/lib/active_record/associations/alias_tracker.rb +23 -32
- data/lib/active_record/associations/association.rb +49 -35
- data/lib/active_record/associations/association_scope.rb +55 -55
- data/lib/active_record/associations/belongs_to_association.rb +30 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +4 -7
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +2 -0
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +66 -53
- data/lib/active_record/associations/collection_proxy.rb +30 -73
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +13 -2
- data/lib/active_record/associations/has_many_through_association.rb +37 -19
- data/lib/active_record/associations/has_one_association.rb +14 -1
- data/lib/active_record/associations/has_one_through_association.rb +13 -8
- data/lib/active_record/associations/join_dependency.rb +52 -96
- data/lib/active_record/associations/join_dependency/join_association.rb +22 -75
- data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
- data/lib/active_record/associations/preloader.rb +17 -37
- data/lib/active_record/associations/preloader/association.rb +53 -92
- data/lib/active_record/associations/preloader/through_association.rb +72 -73
- data/lib/active_record/associations/singular_association.rb +14 -16
- data/lib/active_record/associations/through_association.rb +27 -12
- data/lib/active_record/attribute_assignment.rb +2 -5
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods.rb +65 -24
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
- data/lib/active_record/attribute_methods/dirty.rb +33 -216
- data/lib/active_record/attribute_methods/primary_key.rb +10 -13
- data/lib/active_record/attribute_methods/query.rb +2 -0
- data/lib/active_record/attribute_methods/read.rb +9 -3
- data/lib/active_record/attribute_methods/serialization.rb +23 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
- data/lib/active_record/attribute_methods/write.rb +22 -19
- data/lib/active_record/attributes.rb +7 -6
- data/lib/active_record/autosave_association.rb +15 -13
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +12 -6
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +2 -0
- data/lib/active_record/collection_cache_key.rb +15 -11
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +120 -39
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +192 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -25
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +65 -7
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -87
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
- data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +126 -189
- data/lib/active_record/connection_adapters/column.rb +4 -2
- data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -15
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +258 -129
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -87
- data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +24 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +90 -96
- data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
- data/lib/active_record/connection_handling.rb +4 -2
- data/lib/active_record/core.rb +41 -61
- data/lib/active_record/counter_cache.rb +20 -15
- data/lib/active_record/define_callbacks.rb +5 -3
- data/lib/active_record/dynamic_matchers.rb +9 -9
- data/lib/active_record/enum.rb +18 -13
- data/lib/active_record/errors.rb +60 -15
- data/lib/active_record/explain.rb +3 -1
- data/lib/active_record/explain_registry.rb +2 -0
- data/lib/active_record/explain_subscriber.rb +2 -0
- data/lib/active_record/fixture_set/file.rb +2 -0
- data/lib/active_record/fixtures.rb +67 -60
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +49 -19
- data/lib/active_record/integration.rb +58 -19
- data/lib/active_record/internal_metadata.rb +2 -0
- data/lib/active_record/legacy_yaml_adapter.rb +3 -1
- data/lib/active_record/locking/optimistic.rb +30 -42
- data/lib/active_record/locking/pessimistic.rb +10 -7
- data/lib/active_record/log_subscriber.rb +46 -4
- data/lib/active_record/migration.rb +189 -139
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +81 -29
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/model_schema.rb +74 -58
- data/lib/active_record/nested_attributes.rb +18 -6
- data/lib/active_record/no_touching.rb +3 -1
- data/lib/active_record/null_relation.rb +2 -0
- data/lib/active_record/persistence.rb +199 -54
- data/lib/active_record/query_cache.rb +8 -10
- data/lib/active_record/querying.rb +5 -3
- data/lib/active_record/railtie.rb +62 -6
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +2 -0
- data/lib/active_record/railties/databases.rake +48 -38
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +137 -207
- data/lib/active_record/relation.rb +132 -207
- data/lib/active_record/relation/batches.rb +32 -17
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/calculations.rb +66 -25
- data/lib/active_record/relation/delegation.rb +45 -29
- data/lib/active_record/relation/finder_methods.rb +76 -85
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +53 -23
- data/lib/active_record/relation/predicate_builder.rb +60 -79
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/query_attribute.rb +28 -2
- data/lib/active_record/relation/query_methods.rb +135 -103
- data/lib/active_record/relation/record_fetch_warning.rb +2 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -2
- data/lib/active_record/relation/where_clause.rb +65 -67
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/result.rb +2 -0
- data/lib/active_record/runtime_registry.rb +2 -0
- data/lib/active_record/sanitization.rb +129 -121
- data/lib/active_record/schema.rb +4 -2
- data/lib/active_record/schema_dumper.rb +36 -26
- data/lib/active_record/schema_migration.rb +2 -0
- data/lib/active_record/scoping.rb +12 -10
- data/lib/active_record/scoping/default.rb +10 -7
- data/lib/active_record/scoping/named.rb +40 -12
- data/lib/active_record/secure_token.rb +2 -0
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +22 -12
- data/lib/active_record/store.rb +3 -1
- data/lib/active_record/suppressor.rb +2 -0
- data/lib/active_record/table_metadata.rb +12 -3
- data/lib/active_record/tasks/database_tasks.rb +38 -26
- data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +13 -6
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +32 -27
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type.rb +4 -1
- data/lib/active_record/type/adapter_specific_registry.rb +2 -0
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +2 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +6 -0
- data/lib/active_record/type/text.rb +2 -0
- data/lib/active_record/type/time.rb +2 -0
- data/lib/active_record/type/type_map.rb +2 -0
- data/lib/active_record/type/unsigned_integer.rb +2 -0
- data/lib/active_record/type_caster.rb +2 -0
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/validations.rb +2 -0
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +2 -0
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +2 -0
- data/lib/active_record/validations/uniqueness.rb +36 -6
- data/lib/active_record/version.rb +2 -0
- data/lib/rails/generators/active_record.rb +3 -1
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +2 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +24 -36
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -15
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -18
- data/lib/active_record/attribute.rb +0 -240
- data/lib/active_record/attribute/user_provided_default.rb +0 -30
- data/lib/active_record/attribute_mutation_tracker.rb +0 -113
- data/lib/active_record/attribute_set.rb +0 -113
- data/lib/active_record/attribute_set/builder.rb +0 -124
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_record/relation/batches/batch_enumerator"
|
2
4
|
|
3
5
|
module ActiveRecord
|
@@ -30,14 +32,14 @@ module ActiveRecord
|
|
30
32
|
# end
|
31
33
|
#
|
32
34
|
# ==== Options
|
33
|
-
# * <tt>:batch_size</tt> - Specifies the size of the batch.
|
35
|
+
# * <tt>:batch_size</tt> - Specifies the size of the batch. Defaults to 1000.
|
34
36
|
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
|
35
37
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
36
38
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
37
|
-
#
|
39
|
+
# an order is present in the relation.
|
38
40
|
#
|
39
41
|
# Limits are honored, and if present there is no requirement for the batch
|
40
|
-
# size
|
42
|
+
# size: it can be less than, equal to, or greater than the limit.
|
41
43
|
#
|
42
44
|
# The options +start+ and +finish+ are especially useful if you want
|
43
45
|
# multiple workers dealing with the same processing queue. You can make
|
@@ -45,7 +47,12 @@ module ActiveRecord
|
|
45
47
|
# handle from 10000 and beyond by setting the +:start+ and +:finish+
|
46
48
|
# option on each worker.
|
47
49
|
#
|
48
|
-
# #
|
50
|
+
# # In worker 1, let's process until 9999 records.
|
51
|
+
# Person.find_each(finish: 9_999) do |person|
|
52
|
+
# person.party_all_night!
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# # In worker 2, let's process from record 10_000 and onwards.
|
49
56
|
# Person.find_each(start: 10_000) do |person|
|
50
57
|
# person.party_all_night!
|
51
58
|
# end
|
@@ -89,14 +96,14 @@ module ActiveRecord
|
|
89
96
|
# To be yielded each record one by one, use #find_each instead.
|
90
97
|
#
|
91
98
|
# ==== Options
|
92
|
-
# * <tt>:batch_size</tt> - Specifies the size of the batch.
|
99
|
+
# * <tt>:batch_size</tt> - Specifies the size of the batch. Defaults to 1000.
|
93
100
|
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
|
94
101
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
95
102
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
96
|
-
#
|
103
|
+
# an order is present in the relation.
|
97
104
|
#
|
98
105
|
# Limits are honored, and if present there is no requirement for the batch
|
99
|
-
# size
|
106
|
+
# size: it can be less than, equal to, or greater than the limit.
|
100
107
|
#
|
101
108
|
# The options +start+ and +finish+ are especially useful if you want
|
102
109
|
# multiple workers dealing with the same processing queue. You can make
|
@@ -140,9 +147,9 @@ module ActiveRecord
|
|
140
147
|
# If you do not provide a block to #in_batches, it will return a
|
141
148
|
# BatchEnumerator which is enumerable.
|
142
149
|
#
|
143
|
-
# Person.in_batches.
|
150
|
+
# Person.in_batches.each_with_index do |relation, batch_index|
|
144
151
|
# puts "Processing relation ##{batch_index}"
|
145
|
-
# relation.
|
152
|
+
# relation.delete_all
|
146
153
|
# end
|
147
154
|
#
|
148
155
|
# Examples of calling methods on the returned BatchEnumerator object:
|
@@ -152,12 +159,12 @@ module ActiveRecord
|
|
152
159
|
# Person.in_batches.each_record(&:party_all_night!)
|
153
160
|
#
|
154
161
|
# ==== Options
|
155
|
-
# * <tt>:of</tt> - Specifies the size of the batch.
|
156
|
-
# * <tt>:load</tt> - Specifies if the relation should be loaded.
|
162
|
+
# * <tt>:of</tt> - Specifies the size of the batch. Defaults to 1000.
|
163
|
+
# * <tt>:load</tt> - Specifies if the relation should be loaded. Defaults to false.
|
157
164
|
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
|
158
165
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
159
166
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
160
|
-
#
|
167
|
+
# an order is present in the relation.
|
161
168
|
#
|
162
169
|
# Limits are honored, and if present there is no requirement for the batch
|
163
170
|
# size, it can be less than, equal, or greater than the limit.
|
@@ -186,7 +193,7 @@ module ActiveRecord
|
|
186
193
|
#
|
187
194
|
# NOTE: It's not possible to set the order. That is automatically set to
|
188
195
|
# ascending on the primary key ("id ASC") to make the batch ordering
|
189
|
-
# consistent. Therefore the primary key must be orderable, e.g an integer
|
196
|
+
# consistent. Therefore the primary key must be orderable, e.g. an integer
|
190
197
|
# or a string.
|
191
198
|
#
|
192
199
|
# NOTE: By its nature, batch processing is subject to race conditions if
|
@@ -209,6 +216,7 @@ module ActiveRecord
|
|
209
216
|
|
210
217
|
relation = relation.reorder(batch_order).limit(batch_limit)
|
211
218
|
relation = apply_limits(relation, start, finish)
|
219
|
+
relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
|
212
220
|
batch_relation = relation
|
213
221
|
|
214
222
|
loop do
|
@@ -243,20 +251,27 @@ module ActiveRecord
|
|
243
251
|
end
|
244
252
|
end
|
245
253
|
|
246
|
-
|
254
|
+
attr = Relation::QueryAttribute.new(primary_key, primary_key_offset, klass.type_for_attribute(primary_key))
|
255
|
+
batch_relation = relation.where(arel_attribute(primary_key).gt(Arel::Nodes::BindParam.new(attr)))
|
247
256
|
end
|
248
257
|
end
|
249
258
|
|
250
259
|
private
|
251
260
|
|
252
261
|
def apply_limits(relation, start, finish)
|
253
|
-
|
254
|
-
|
262
|
+
if start
|
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
|
255
270
|
relation
|
256
271
|
end
|
257
272
|
|
258
273
|
def batch_order
|
259
|
-
|
274
|
+
arel_attribute(primary_key).asc
|
260
275
|
end
|
261
276
|
|
262
277
|
def act_on_ignored_order(error_on_ignore)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Calculations
|
3
5
|
# Count the records.
|
@@ -37,7 +39,16 @@ module ActiveRecord
|
|
37
39
|
# Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
|
38
40
|
# between databases. In invalid cases, an error from the database is thrown.
|
39
41
|
def count(column_name = nil)
|
40
|
-
|
42
|
+
if block_given?
|
43
|
+
unless column_name.nil?
|
44
|
+
ActiveSupport::Deprecation.warn \
|
45
|
+
"When `count' is called with a block, it ignores other arguments. " \
|
46
|
+
"This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
|
47
|
+
end
|
48
|
+
|
49
|
+
return super()
|
50
|
+
end
|
51
|
+
|
41
52
|
calculate(:count, column_name)
|
42
53
|
end
|
43
54
|
|
@@ -73,7 +84,16 @@ module ActiveRecord
|
|
73
84
|
#
|
74
85
|
# Person.sum(:age) # => 4562
|
75
86
|
def sum(column_name = nil)
|
76
|
-
|
87
|
+
if block_given?
|
88
|
+
unless column_name.nil?
|
89
|
+
ActiveSupport::Deprecation.warn \
|
90
|
+
"When `sum' is called with a block, it ignores other arguments. " \
|
91
|
+
"This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
|
92
|
+
end
|
93
|
+
|
94
|
+
return super()
|
95
|
+
end
|
96
|
+
|
77
97
|
calculate(:sum, column_name)
|
78
98
|
end
|
79
99
|
|
@@ -110,8 +130,15 @@ module ActiveRecord
|
|
110
130
|
# end
|
111
131
|
def calculate(operation, column_name)
|
112
132
|
if has_include?(column_name)
|
113
|
-
relation =
|
114
|
-
|
133
|
+
relation = apply_join_dependency
|
134
|
+
|
135
|
+
if operation.to_s.downcase == "count"
|
136
|
+
relation.distinct!
|
137
|
+
# PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
|
138
|
+
if (column_name == :all || column_name.nil?) && select_values.empty?
|
139
|
+
relation.order_values = []
|
140
|
+
end
|
141
|
+
end
|
115
142
|
|
116
143
|
relation.calculate(operation, column_name)
|
117
144
|
else
|
@@ -160,13 +187,13 @@ module ActiveRecord
|
|
160
187
|
end
|
161
188
|
|
162
189
|
if has_include?(column_names.first)
|
163
|
-
|
190
|
+
relation = apply_join_dependency
|
191
|
+
relation.pluck(*column_names)
|
164
192
|
else
|
193
|
+
klass.enforce_raw_sql_whitelist(column_names)
|
165
194
|
relation = spawn
|
166
|
-
relation.select_values = column_names
|
167
|
-
|
168
|
-
}
|
169
|
-
result = klass.connection.select_all(relation.arel, nil, bound_attributes)
|
195
|
+
relation.select_values = column_names
|
196
|
+
result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
|
170
197
|
result.cast_values(klass.attribute_types)
|
171
198
|
end
|
172
199
|
end
|
@@ -180,7 +207,6 @@ module ActiveRecord
|
|
180
207
|
end
|
181
208
|
|
182
209
|
private
|
183
|
-
|
184
210
|
def has_include?(column_name)
|
185
211
|
eager_loading? || (includes_values.present? && column_name && column_name != :all)
|
186
212
|
end
|
@@ -194,8 +220,15 @@ module ActiveRecord
|
|
194
220
|
|
195
221
|
if operation == "count"
|
196
222
|
column_name ||= select_for_count
|
197
|
-
|
198
|
-
|
223
|
+
if column_name == :all
|
224
|
+
if !distinct
|
225
|
+
distinct = distinct_select?(select_for_count) if group_values.empty?
|
226
|
+
elsif group_values.any? || select_values.empty? && order_values.empty?
|
227
|
+
column_name = primary_key
|
228
|
+
end
|
229
|
+
elsif distinct_select?(column_name)
|
230
|
+
distinct = nil
|
231
|
+
end
|
199
232
|
end
|
200
233
|
|
201
234
|
if group_values.any?
|
@@ -205,10 +238,14 @@ module ActiveRecord
|
|
205
238
|
end
|
206
239
|
end
|
207
240
|
|
241
|
+
def distinct_select?(column_name)
|
242
|
+
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
|
243
|
+
end
|
244
|
+
|
208
245
|
def aggregate_column(column_name)
|
209
246
|
return column_name if Arel::Expressions === column_name
|
210
247
|
|
211
|
-
if @klass.has_attribute?(column_name
|
248
|
+
if @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
|
212
249
|
@klass.arel_attribute(column_name)
|
213
250
|
else
|
214
251
|
Arel.sql(column_name == :all ? "*" : column_name.to_s)
|
@@ -222,7 +259,7 @@ module ActiveRecord
|
|
222
259
|
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
|
223
260
|
column_alias = column_name
|
224
261
|
|
225
|
-
if operation == "count" && (
|
262
|
+
if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
|
226
263
|
# Shortcut when limit is zero.
|
227
264
|
return 0 if limit_value == 0
|
228
265
|
|
@@ -234,6 +271,9 @@ module ActiveRecord
|
|
234
271
|
column = aggregate_column(column_name)
|
235
272
|
|
236
273
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
274
|
+
if operation == "sum" && distinct
|
275
|
+
select_value.distinct = true
|
276
|
+
end
|
237
277
|
|
238
278
|
column_alias = select_value.alias
|
239
279
|
column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
|
@@ -242,7 +282,7 @@ module ActiveRecord
|
|
242
282
|
query_builder = relation.arel
|
243
283
|
end
|
244
284
|
|
245
|
-
result = @klass.connection.select_all(query_builder, nil
|
285
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
|
246
286
|
row = result.first
|
247
287
|
value = row && row.values.first
|
248
288
|
type = result.column_types.fetch(column_alias) do
|
@@ -293,7 +333,7 @@ module ActiveRecord
|
|
293
333
|
relation.group_values = group_fields
|
294
334
|
relation.select_values = select_values
|
295
335
|
|
296
|
-
calculated_data = @klass.connection.select_all(relation, nil
|
336
|
+
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
|
297
337
|
|
298
338
|
if association
|
299
339
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
@@ -346,7 +386,7 @@ module ActiveRecord
|
|
346
386
|
case operation
|
347
387
|
when "count" then value.to_i
|
348
388
|
when "sum" then type.deserialize(value || 0)
|
349
|
-
when "average" then value.respond_to?(:to_d) ? value.to_d : value
|
389
|
+
when "average" then value && value.respond_to?(:to_d) ? value.to_d : value
|
350
390
|
else type.deserialize(value)
|
351
391
|
end
|
352
392
|
end
|
@@ -361,16 +401,17 @@ module ActiveRecord
|
|
361
401
|
end
|
362
402
|
|
363
403
|
def build_count_subquery(relation, column_name, distinct)
|
364
|
-
|
365
|
-
|
404
|
+
if column_name == :all
|
405
|
+
relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
|
406
|
+
else
|
407
|
+
column_alias = Arel.sql("count_column")
|
408
|
+
relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
|
409
|
+
end
|
366
410
|
|
367
|
-
|
368
|
-
|
369
|
-
subquery = relation.arel.as(subquery_alias)
|
411
|
+
subquery = relation.arel.as(Arel.sql("subquery_for_count"))
|
412
|
+
select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false)
|
370
413
|
|
371
|
-
|
372
|
-
select_value = operation_over_aggregate_column(column_alias, "count", distinct)
|
373
|
-
sm.project(select_value).from(subquery)
|
414
|
+
Arel::SelectManager.new(subquery).project(select_value)
|
374
415
|
end
|
375
416
|
end
|
376
417
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Delegation # :nodoc:
|
3
5
|
module DelegateCache # :nodoc:
|
@@ -15,6 +17,7 @@ module ActiveRecord
|
|
15
17
|
delegate = Class.new(klass) {
|
16
18
|
include ClassSpecificRelation
|
17
19
|
}
|
20
|
+
include_relation_methods(delegate)
|
18
21
|
mangled_name = klass.name.gsub("::".freeze, "_".freeze)
|
19
22
|
const_set mangled_name, delegate
|
20
23
|
private_constant mangled_name
|
@@ -25,10 +28,37 @@ module ActiveRecord
|
|
25
28
|
|
26
29
|
def inherited(child_class)
|
27
30
|
child_class.initialize_relation_delegate_cache
|
28
|
-
delegate = child_class.relation_delegate_class(ActiveRecord::Associations::CollectionProxy)
|
29
|
-
delegate.include ActiveRecord::Associations::CollectionProxy::DelegateExtending
|
30
31
|
super
|
31
32
|
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
def include_relation_methods(delegate)
|
36
|
+
superclass.include_relation_methods(delegate) unless base_class == self
|
37
|
+
delegate.include generated_relation_methods
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def generated_relation_methods
|
42
|
+
@generated_relation_methods ||= Module.new.tap do |mod|
|
43
|
+
mod_name = "GeneratedRelationMethods"
|
44
|
+
const_set mod_name, mod
|
45
|
+
private_constant mod_name
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def generate_relation_method(method)
|
50
|
+
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
|
51
|
+
generated_relation_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
52
|
+
def #{method}(*args, &block)
|
53
|
+
scoping { klass.#{method}(*args, &block) }
|
54
|
+
end
|
55
|
+
RUBY
|
56
|
+
else
|
57
|
+
generated_relation_methods.send(:define_method, method) do |*args, &block|
|
58
|
+
scoping { klass.public_send(method, *args, &block) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
32
62
|
end
|
33
63
|
|
34
64
|
extend ActiveSupport::Concern
|
@@ -38,13 +68,12 @@ module ActiveRecord
|
|
38
68
|
# may vary depending on the klass of a relation, so we create a subclass of Relation
|
39
69
|
# for each different klass, and the delegations are compiled into that subclass only.
|
40
70
|
|
41
|
-
delegate :to_xml, :encode_with, :length, :each, :uniq, :
|
42
|
-
:[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
|
71
|
+
delegate :to_xml, :encode_with, :length, :each, :uniq, :join,
|
72
|
+
:[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
|
43
73
|
:to_sentence, :to_formatted_s, :as_json,
|
44
|
-
:shuffle, :split, :index, to: :records
|
74
|
+
:shuffle, :split, :slice, :index, :rindex, to: :records
|
45
75
|
|
46
|
-
delegate :
|
47
|
-
:connection, :columns_hash, to: :klass
|
76
|
+
delegate :primary_key, :connection, to: :klass
|
48
77
|
|
49
78
|
module ClassSpecificRelation # :nodoc:
|
50
79
|
extend ActiveSupport::Concern
|
@@ -75,13 +104,6 @@ module ActiveRecord
|
|
75
104
|
end
|
76
105
|
end
|
77
106
|
end
|
78
|
-
|
79
|
-
def delegate(method, opts = {})
|
80
|
-
@delegation_mutex.synchronize do
|
81
|
-
return if method_defined?(method)
|
82
|
-
super
|
83
|
-
end
|
84
|
-
end
|
85
107
|
end
|
86
108
|
|
87
109
|
private
|
@@ -90,8 +112,14 @@ module ActiveRecord
|
|
90
112
|
if @klass.respond_to?(method)
|
91
113
|
self.class.delegate_to_scoped_klass(method)
|
92
114
|
scoping { @klass.public_send(method, *args, &block) }
|
115
|
+
elsif @delegate_to_klass && @klass.respond_to?(method, true)
|
116
|
+
ActiveSupport::Deprecation.warn \
|
117
|
+
"Delegating missing #{method} method to #{@klass}. " \
|
118
|
+
"Accessibility of private/protected class methods in :scope is deprecated and will be removed in Rails 6.0."
|
119
|
+
@klass.send(method, *args, &block)
|
93
120
|
elsif arel.respond_to?(method)
|
94
|
-
|
121
|
+
ActiveSupport::Deprecation.warn \
|
122
|
+
"Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
|
95
123
|
arel.public_send(method, *args, &block)
|
96
124
|
else
|
97
125
|
super
|
@@ -111,21 +139,9 @@ module ActiveRecord
|
|
111
139
|
end
|
112
140
|
end
|
113
141
|
|
114
|
-
def respond_to_missing?(method, include_private = false)
|
115
|
-
super || @klass.respond_to?(method, include_private) ||
|
116
|
-
arel.respond_to?(method, include_private)
|
117
|
-
end
|
118
|
-
|
119
142
|
private
|
120
|
-
|
121
|
-
|
122
|
-
if @klass.respond_to?(method)
|
123
|
-
scoping { @klass.public_send(method, *args, &block) }
|
124
|
-
elsif arel.respond_to?(method)
|
125
|
-
arel.public_send(method, *args, &block)
|
126
|
-
else
|
127
|
-
super
|
128
|
-
end
|
143
|
+
def respond_to_missing?(method, _)
|
144
|
+
super || @klass.respond_to?(method) || arel.respond_to?(method)
|
129
145
|
end
|
130
146
|
end
|
131
147
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/string/filters"
|
2
4
|
|
3
5
|
module ActiveRecord
|
@@ -16,9 +18,10 @@ module ActiveRecord
|
|
16
18
|
# Person.find([1]) # returns an array for the object with ID = 1
|
17
19
|
# Person.where("administrator = 1").order("created_on DESC").find(1)
|
18
20
|
#
|
19
|
-
# NOTE: The returned records
|
20
|
-
#
|
21
|
-
#
|
21
|
+
# NOTE: The returned records are in the same order as the ids you provide.
|
22
|
+
# If you want the results to be sorted by database, you can use ActiveRecord::QueryMethods#where
|
23
|
+
# method and provide an explicit ActiveRecord::QueryMethods#order option.
|
24
|
+
# But ActiveRecord::QueryMethods#where method doesn't raise ActiveRecord::RecordNotFound.
|
22
25
|
#
|
23
26
|
# ==== Find with lock
|
24
27
|
#
|
@@ -86,7 +89,7 @@ module ActiveRecord
|
|
86
89
|
where(arg, *args).take!
|
87
90
|
rescue ::RangeError
|
88
91
|
raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
|
89
|
-
@klass.name)
|
92
|
+
@klass.name, @klass.primary_key)
|
90
93
|
end
|
91
94
|
|
92
95
|
# Gives a record (or N records if a parameter is supplied) without any implied
|
@@ -145,10 +148,9 @@ module ActiveRecord
|
|
145
148
|
#
|
146
149
|
# [#<Person id:4>, #<Person id:3>, #<Person id:2>]
|
147
150
|
def last(limit = nil)
|
148
|
-
return find_last(limit) if loaded? ||
|
151
|
+
return find_last(limit) if loaded? || has_limit_or_offset?
|
149
152
|
|
150
|
-
result = limit(limit)
|
151
|
-
result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
|
153
|
+
result = ordered_relation.limit(limit)
|
152
154
|
result = result.reverse_order!
|
153
155
|
|
154
156
|
limit ? result.reverse : result.first
|
@@ -283,7 +285,7 @@ module ActiveRecord
|
|
283
285
|
# * Hash - Finds the record that matches these +find+-style conditions
|
284
286
|
# (such as <tt>{name: 'David'}</tt>).
|
285
287
|
# * +false+ - Returns always +false+.
|
286
|
-
# * No args - Returns +false+ if the
|
288
|
+
# * No args - Returns +false+ if the relation is empty, +true+ otherwise.
|
287
289
|
#
|
288
290
|
# For more information about specifying conditions as a hash or array,
|
289
291
|
# see the Conditions section in the introduction to ActiveRecord::Base.
|
@@ -299,6 +301,7 @@ module ActiveRecord
|
|
299
301
|
# Person.exists?(name: 'David')
|
300
302
|
# Person.exists?(false)
|
301
303
|
# Person.exists?
|
304
|
+
# Person.where(name: 'Spartacus', rating: 4).exists?
|
302
305
|
def exists?(conditions = :none)
|
303
306
|
if Base === conditions
|
304
307
|
raise ArgumentError, <<-MSG.squish
|
@@ -307,14 +310,16 @@ module ActiveRecord
|
|
307
310
|
MSG
|
308
311
|
end
|
309
312
|
|
310
|
-
return false if !conditions
|
313
|
+
return false if !conditions || limit_value == 0
|
311
314
|
|
312
|
-
|
313
|
-
|
315
|
+
if eager_loading?
|
316
|
+
relation = apply_join_dependency(eager_loading: false)
|
317
|
+
return relation.exists?(conditions)
|
318
|
+
end
|
314
319
|
|
315
|
-
relation = construct_relation_for_exists(
|
320
|
+
relation = construct_relation_for_exists(conditions)
|
316
321
|
|
317
|
-
connection.
|
322
|
+
skip_query_cache_if_necessary { connection.select_one(relation.arel, "#{name} Exists") } ? true : false
|
318
323
|
rescue ::RangeError
|
319
324
|
false
|
320
325
|
end
|
@@ -327,23 +332,23 @@ module ActiveRecord
|
|
327
332
|
# of results obtained should be provided in the +result_size+ argument and
|
328
333
|
# the expected number of results should be provided in the +expected_size+
|
329
334
|
# argument.
|
330
|
-
def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key) # :nodoc:
|
331
|
-
conditions = arel.where_sql(@klass
|
335
|
+
def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key, not_found_ids = nil) # :nodoc:
|
336
|
+
conditions = arel.where_sql(@klass)
|
332
337
|
conditions = " [#{conditions}]" if conditions
|
333
338
|
name = @klass.name
|
334
339
|
|
335
340
|
if ids.nil?
|
336
|
-
error = "Couldn't find #{name}"
|
341
|
+
error = "Couldn't find #{name}".dup
|
337
342
|
error << " with#{conditions}" if conditions
|
338
|
-
raise RecordNotFound.new(error, name)
|
343
|
+
raise RecordNotFound.new(error, name, key)
|
339
344
|
elsif Array(ids).size == 1
|
340
345
|
error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
|
341
346
|
raise RecordNotFound.new(error, name, key, ids)
|
342
347
|
else
|
343
|
-
error = "Couldn't find all #{name.pluralize} with '#{key}': "
|
344
|
-
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
|
345
|
-
|
346
|
-
raise RecordNotFound.new(error, name,
|
348
|
+
error = "Couldn't find all #{name.pluralize} with '#{key}': ".dup
|
349
|
+
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})."
|
350
|
+
error << " Couldn't find #{name.pluralize(not_found_ids.size)} with #{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.join(', ')}." if not_found_ids
|
351
|
+
raise RecordNotFound.new(error, name, key, ids)
|
347
352
|
end
|
348
353
|
end
|
349
354
|
|
@@ -353,41 +358,16 @@ module ActiveRecord
|
|
353
358
|
offset_value || 0
|
354
359
|
end
|
355
360
|
|
356
|
-
def
|
357
|
-
|
358
|
-
|
359
|
-
#
|
360
|
-
# failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
|
361
|
-
# incorrect SQL is generated. In that case, the join dependency for
|
362
|
-
# SpecialCategorizations is constructed without knowledge of the
|
363
|
-
# preexisting join in joins_values to categorizations (by way of
|
364
|
-
# the `has_many :through` for categories).
|
365
|
-
#
|
366
|
-
join_dependency = construct_join_dependency(joins_values)
|
367
|
-
|
368
|
-
aliases = join_dependency.aliases
|
369
|
-
relation = select aliases.columns
|
370
|
-
relation = apply_join_dependency(relation, join_dependency)
|
371
|
-
|
372
|
-
if block_given?
|
373
|
-
yield relation
|
361
|
+
def construct_relation_for_exists(conditions)
|
362
|
+
if distinct_value && offset_value
|
363
|
+
relation = limit(1)
|
374
364
|
else
|
375
|
-
|
376
|
-
[]
|
377
|
-
else
|
378
|
-
arel = relation.arel
|
379
|
-
rows = connection.select_all(arel, "SQL", relation.bound_attributes)
|
380
|
-
join_dependency.instantiate(rows, aliases)
|
381
|
-
end
|
365
|
+
relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
|
382
366
|
end
|
383
|
-
end
|
384
|
-
|
385
|
-
def construct_relation_for_exists(relation, conditions)
|
386
|
-
relation = relation.except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
|
387
367
|
|
388
368
|
case conditions
|
389
369
|
when Array, Hash
|
390
|
-
relation.where!(conditions)
|
370
|
+
relation.where!(conditions) unless conditions.empty?
|
391
371
|
else
|
392
372
|
relation.where!(primary_key => conditions) unless conditions == :none
|
393
373
|
end
|
@@ -395,37 +375,41 @@ module ActiveRecord
|
|
395
375
|
relation
|
396
376
|
end
|
397
377
|
|
398
|
-
def construct_join_dependency
|
378
|
+
def construct_join_dependency
|
399
379
|
including = eager_load_values + includes_values
|
400
|
-
ActiveRecord::Associations::JoinDependency.new(
|
380
|
+
ActiveRecord::Associations::JoinDependency.new(
|
381
|
+
klass, table, including
|
382
|
+
)
|
401
383
|
end
|
402
384
|
|
403
|
-
def
|
404
|
-
|
405
|
-
|
385
|
+
def apply_join_dependency(eager_loading: group_values.empty?)
|
386
|
+
join_dependency = construct_join_dependency
|
387
|
+
relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
|
406
388
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
if using_limitable_reflections?(join_dependency.reflections)
|
411
|
-
relation
|
412
|
-
else
|
413
|
-
if relation.limit_value
|
389
|
+
if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
|
390
|
+
if has_limit_or_offset?
|
414
391
|
limited_ids = limited_ids_for(relation)
|
415
392
|
limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
|
416
393
|
end
|
417
|
-
relation.
|
394
|
+
relation.limit_value = relation.offset_value = nil
|
395
|
+
end
|
396
|
+
|
397
|
+
if block_given?
|
398
|
+
yield relation, join_dependency
|
399
|
+
else
|
400
|
+
relation
|
418
401
|
end
|
419
402
|
end
|
420
403
|
|
421
404
|
def limited_ids_for(relation)
|
422
405
|
values = @klass.connection.columns_for_distinct(
|
423
|
-
|
406
|
+
connection.column_name_from_arel_node(arel_attribute(primary_key)),
|
407
|
+
relation.order_values
|
408
|
+
)
|
424
409
|
|
425
410
|
relation = relation.except(:select).select(values).distinct!
|
426
|
-
arel = relation.arel
|
427
411
|
|
428
|
-
id_rows = @klass.connection.select_all(arel, "SQL"
|
412
|
+
id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
|
429
413
|
id_rows.map { |row| row[primary_key] }
|
430
414
|
end
|
431
415
|
|
@@ -437,13 +421,16 @@ module ActiveRecord
|
|
437
421
|
raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
|
438
422
|
|
439
423
|
expects_array = ids.first.kind_of?(Array)
|
440
|
-
return
|
424
|
+
return [] if expects_array && ids.first.empty?
|
441
425
|
|
442
426
|
ids = ids.flatten.compact.uniq
|
443
427
|
|
428
|
+
model_name = @klass.name
|
429
|
+
|
444
430
|
case ids.size
|
445
431
|
when 0
|
446
|
-
|
432
|
+
error_message = "Couldn't find #{model_name} without an ID"
|
433
|
+
raise RecordNotFound.new(error_message, model_name, primary_key)
|
447
434
|
when 1
|
448
435
|
result = find_one(ids.first)
|
449
436
|
expects_array ? [ result ] : result
|
@@ -451,7 +438,8 @@ module ActiveRecord
|
|
451
438
|
find_some(ids)
|
452
439
|
end
|
453
440
|
rescue ::RangeError
|
454
|
-
|
441
|
+
error_message = "Couldn't find #{model_name} with an out of range ID"
|
442
|
+
raise RecordNotFound.new(error_message, model_name, primary_key, ids)
|
455
443
|
end
|
456
444
|
|
457
445
|
def find_one(id)
|
@@ -533,13 +521,13 @@ module ActiveRecord
|
|
533
521
|
if loaded?
|
534
522
|
records[index, limit] || []
|
535
523
|
else
|
536
|
-
relation =
|
537
|
-
|
538
|
-
|
539
|
-
|
524
|
+
relation = ordered_relation
|
525
|
+
|
526
|
+
if limit_value
|
527
|
+
limit = [limit_value - index, limit].min
|
540
528
|
end
|
541
529
|
|
542
|
-
if
|
530
|
+
if limit > 0
|
543
531
|
relation = relation.offset(offset_index + index) unless index.zero?
|
544
532
|
relation.limit(limit).to_a
|
545
533
|
else
|
@@ -552,23 +540,26 @@ module ActiveRecord
|
|
552
540
|
if loaded?
|
553
541
|
records[-index]
|
554
542
|
else
|
555
|
-
relation =
|
556
|
-
|
543
|
+
relation = ordered_relation
|
544
|
+
|
545
|
+
if equal?(relation) || has_limit_or_offset?
|
546
|
+
relation.records[-index]
|
557
547
|
else
|
558
|
-
|
548
|
+
relation.last(index)[-index]
|
559
549
|
end
|
560
|
-
|
561
|
-
relation.to_a[-index]
|
562
|
-
# TODO: can be made more performant on large result sets by
|
563
|
-
# for instance, last(index)[-index] (which would require
|
564
|
-
# refactoring the last(n) finder method to make test suite pass),
|
565
|
-
# or by using a combination of reverse_order, limit, and offset,
|
566
|
-
# e.g., reverse_order.offset(index-1).first
|
567
550
|
end
|
568
551
|
end
|
569
552
|
|
570
553
|
def find_last(limit)
|
571
554
|
limit ? records.last(limit) : records.last
|
572
555
|
end
|
556
|
+
|
557
|
+
def ordered_relation
|
558
|
+
if order_values.empty? && primary_key
|
559
|
+
order(arel_attribute(primary_key).asc)
|
560
|
+
else
|
561
|
+
self
|
562
|
+
end
|
563
|
+
end
|
573
564
|
end
|
574
565
|
end
|