activerecord 6.1.6 → 7.0.4
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 +1314 -975
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +33 -17
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +19 -21
- data/lib/active_record/associations/collection_proxy.rb +10 -5
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +8 -5
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/preloader/association.rb +186 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +49 -13
- data/lib/active_record/associations/preloader.rb +39 -113
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +124 -95
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +49 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +57 -19
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +14 -15
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +8 -23
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +10 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +105 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
- data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +51 -51
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +208 -107
- data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +49 -55
- data/lib/active_record/core.rb +124 -134
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -9
- data/lib/active_record/database_configurations/hash_config.rb +63 -5
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +15 -32
- data/lib/active_record/delegated_type.rb +53 -12
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +67 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +206 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +50 -43
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +41 -6
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +20 -23
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +80 -14
- data/lib/active_record/integration.rb +4 -3
- data/lib/active_record/internal_metadata.rb +1 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/locking/pessimistic.rb +10 -4
- data/lib/active_record/log_subscriber.rb +23 -7
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +18 -6
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +7 -7
- data/lib/active_record/migration/compatibility.rb +84 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +114 -83
- data/lib/active_record/model_schema.rb +58 -59
- data/lib/active_record/nested_attributes.rb +13 -12
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +228 -60
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +16 -6
- data/lib/active_record/railtie.rb +136 -22
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +78 -136
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +73 -50
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +6 -6
- data/lib/active_record/relation/calculations.rb +43 -38
- data/lib/active_record/relation/delegation.rb +7 -7
- data/lib/active_record/relation/finder_methods.rb +31 -35
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +276 -67
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +189 -88
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +17 -12
- data/lib/active_record/schema.rb +38 -23
- data/lib/active_record/schema_dumper.rb +25 -19
- data/lib/active_record/schema_migration.rb +4 -4
- data/lib/active_record/scoping/default.rb +60 -13
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +3 -3
- data/lib/active_record/store.rb +7 -2
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +127 -60
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +16 -9
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +4 -4
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +217 -27
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +11 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +8 -2
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +58 -2
- data/lib/arel.rb +2 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +55 -11
@@ -76,6 +76,11 @@ module ActiveRecord
|
|
76
76
|
other.is_a?(WhereClause) &&
|
77
77
|
predicates == other.predicates
|
78
78
|
end
|
79
|
+
alias :eql? :==
|
80
|
+
|
81
|
+
def hash
|
82
|
+
[self.class, predicates].hash
|
83
|
+
end
|
79
84
|
|
80
85
|
def invert
|
81
86
|
if predicates.size == 1
|
@@ -158,21 +163,8 @@ module ActiveRecord
|
|
158
163
|
attr = extract_attribute(node) || begin
|
159
164
|
node.left if equality_node?(node) && node.left.is_a?(Arel::Predications)
|
160
165
|
end
|
161
|
-
|
162
|
-
|
163
|
-
ref = referenced_columns[attr]
|
164
|
-
next false unless ref
|
165
|
-
|
166
|
-
if equality_node?(node) && equality_node?(ref) || node == ref
|
167
|
-
true
|
168
|
-
else
|
169
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
170
|
-
Merging (#{node.to_sql}) and (#{ref.to_sql}) no longer maintain
|
171
|
-
both conditions, and will be replaced by the latter in Rails 7.0.
|
172
|
-
To migrate to Rails 7.0's behavior, use `relation.merge(other, rewhere: true)`.
|
173
|
-
MSG
|
174
|
-
false
|
175
|
-
end
|
166
|
+
|
167
|
+
attr && referenced_columns[attr]
|
176
168
|
end
|
177
169
|
end
|
178
170
|
|
@@ -227,11 +219,10 @@ module ActiveRecord
|
|
227
219
|
end
|
228
220
|
|
229
221
|
def extract_node_value(node)
|
230
|
-
|
231
|
-
when Array
|
232
|
-
node.map { |v| extract_node_value(v) }
|
233
|
-
when Arel::Nodes::BindParam, Arel::Nodes::Casted, Arel::Nodes::Quoted
|
222
|
+
if node.respond_to?(:value_before_type_cast)
|
234
223
|
node.value_before_type_cast
|
224
|
+
elsif Array === node
|
225
|
+
node.map { |v| extract_node_value(v) }
|
235
226
|
end
|
236
227
|
end
|
237
228
|
end
|
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
:reverse_order, :distinct, :create_with, :skip_query_cache]
|
12
12
|
|
13
13
|
CLAUSE_METHODS = [:where, :having, :from]
|
14
|
-
INVALID_METHODS_FOR_DELETE_ALL = [:distinct
|
14
|
+
INVALID_METHODS_FOR_DELETE_ALL = [:distinct]
|
15
15
|
|
16
16
|
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
|
17
17
|
|
@@ -31,6 +31,8 @@ module ActiveRecord
|
|
31
31
|
@loaded = false
|
32
32
|
@predicate_builder = predicate_builder
|
33
33
|
@delegate_to_klass = false
|
34
|
+
@future_result = nil
|
35
|
+
@records = nil
|
34
36
|
end
|
35
37
|
|
36
38
|
def initialize_copy(other)
|
@@ -38,11 +40,6 @@ module ActiveRecord
|
|
38
40
|
reset
|
39
41
|
end
|
40
42
|
|
41
|
-
def arel_attribute(name) # :nodoc:
|
42
|
-
table[name]
|
43
|
-
end
|
44
|
-
deprecate :arel_attribute
|
45
|
-
|
46
43
|
def bind_attribute(name, value) # :nodoc:
|
47
44
|
if reflection = klass._reflect_on_association(name)
|
48
45
|
name = reflection.foreign_key
|
@@ -67,8 +64,12 @@ module ActiveRecord
|
|
67
64
|
# user = users.new { |user| user.name = 'Oscar' }
|
68
65
|
# user.name # => Oscar
|
69
66
|
def new(attributes = nil, &block)
|
70
|
-
|
71
|
-
|
67
|
+
if attributes.is_a?(Array)
|
68
|
+
attributes.collect { |attr| new(attr, &block) }
|
69
|
+
else
|
70
|
+
block = current_scope_restoring_block(&block)
|
71
|
+
scoping { _new(attributes, &block) }
|
72
|
+
end
|
72
73
|
end
|
73
74
|
alias build new
|
74
75
|
|
@@ -148,7 +149,7 @@ module ActiveRecord
|
|
148
149
|
# above can be alternatively written this way:
|
149
150
|
#
|
150
151
|
# # Find the first user named "Scarlett" or create a new one with a
|
151
|
-
# #
|
152
|
+
# # particular last name.
|
152
153
|
# User.find_or_create_by(first_name: 'Scarlett') do |user|
|
153
154
|
# user.last_name = 'Johansson'
|
154
155
|
# end
|
@@ -175,7 +176,7 @@ module ActiveRecord
|
|
175
176
|
find_by(attributes) || create!(attributes, &block)
|
176
177
|
end
|
177
178
|
|
178
|
-
# Attempts to create a record with the given attributes in a table that has a unique constraint
|
179
|
+
# Attempts to create a record with the given attributes in a table that has a unique database constraint
|
179
180
|
# on one or several of its columns. If a row already exists with one or several of these
|
180
181
|
# unique constraints, the exception such an insertion would normally raise is caught,
|
181
182
|
# and the existing record with those attributes is found using #find_by!.
|
@@ -186,7 +187,7 @@ module ActiveRecord
|
|
186
187
|
#
|
187
188
|
# There are several drawbacks to #create_or_find_by, though:
|
188
189
|
#
|
189
|
-
# * The underlying table must have the relevant columns defined with unique constraints.
|
190
|
+
# * The underlying table must have the relevant columns defined with unique database constraints.
|
190
191
|
# * A unique constraint violation may be triggered by only one, or at least less than all,
|
191
192
|
# of the given attributes. This means that the subsequent #find_by! may fail to find a
|
192
193
|
# matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
|
@@ -257,13 +258,20 @@ module ActiveRecord
|
|
257
258
|
|
258
259
|
# Returns size of the records.
|
259
260
|
def size
|
260
|
-
loaded?
|
261
|
+
if loaded?
|
262
|
+
records.length
|
263
|
+
else
|
264
|
+
count(:all)
|
265
|
+
end
|
261
266
|
end
|
262
267
|
|
263
268
|
# Returns true if there are no records.
|
264
269
|
def empty?
|
265
|
-
|
266
|
-
|
270
|
+
if loaded?
|
271
|
+
records.empty?
|
272
|
+
else
|
273
|
+
!exists?
|
274
|
+
end
|
267
275
|
end
|
268
276
|
|
269
277
|
# Returns true if there are no records.
|
@@ -281,13 +289,15 @@ module ActiveRecord
|
|
281
289
|
# Returns true if there is exactly one record.
|
282
290
|
def one?
|
283
291
|
return super if block_given?
|
284
|
-
|
292
|
+
return records.one? if loaded?
|
293
|
+
limited_count == 1
|
285
294
|
end
|
286
295
|
|
287
296
|
# Returns true if there is more than one record.
|
288
297
|
def many?
|
289
298
|
return super if block_given?
|
290
|
-
|
299
|
+
return records.many? if loaded?
|
300
|
+
limited_count > 1
|
291
301
|
end
|
292
302
|
|
293
303
|
# Returns a stable cache key that can be used to identify this query.
|
@@ -344,7 +354,7 @@ module ActiveRecord
|
|
344
354
|
def compute_cache_version(timestamp_column) # :nodoc:
|
345
355
|
timestamp_column = timestamp_column.to_s
|
346
356
|
|
347
|
-
if loaded?
|
357
|
+
if loaded?
|
348
358
|
size = records.size
|
349
359
|
if size > 0
|
350
360
|
timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
|
@@ -357,6 +367,7 @@ module ActiveRecord
|
|
357
367
|
|
358
368
|
if collection.has_limit_or_offset?
|
359
369
|
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
370
|
+
query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
|
360
371
|
subquery_alias = "subquery_for_cache_key"
|
361
372
|
subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
|
362
373
|
arel = query.build_subquery(subquery_alias, select_values % subquery_column)
|
@@ -377,7 +388,7 @@ module ActiveRecord
|
|
377
388
|
end
|
378
389
|
|
379
390
|
if timestamp
|
380
|
-
"#{size}-#{timestamp.utc.
|
391
|
+
"#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
|
381
392
|
else
|
382
393
|
"#{size}"
|
383
394
|
end
|
@@ -400,15 +411,28 @@ module ActiveRecord
|
|
400
411
|
# end
|
401
412
|
# # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
|
402
413
|
#
|
414
|
+
# If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
|
415
|
+
# for the relation including +update+ and +delete+ on instances.
|
416
|
+
# Once +all_queries+ is set to true it cannot be set to false in a
|
417
|
+
# nested block.
|
418
|
+
#
|
403
419
|
# Please check unscoped if you want to remove all previous scopes (including
|
404
420
|
# the default_scope) during the execution of a block.
|
405
|
-
def scoping
|
406
|
-
|
421
|
+
def scoping(all_queries: nil, &block)
|
422
|
+
registry = klass.scope_registry
|
423
|
+
if global_scope?(registry) && all_queries == false
|
424
|
+
raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
|
425
|
+
elsif already_in_scope?(registry)
|
426
|
+
yield
|
427
|
+
else
|
428
|
+
_scoping(self, registry, all_queries, &block)
|
429
|
+
end
|
407
430
|
end
|
408
431
|
|
409
|
-
def _exec_scope(
|
432
|
+
def _exec_scope(...) # :nodoc:
|
410
433
|
@delegate_to_klass = true
|
411
|
-
|
434
|
+
registry = klass.scope_registry
|
435
|
+
_scoping(nil, registry) { instance_exec(...) || self }
|
412
436
|
ensure
|
413
437
|
@delegate_to_klass = false
|
414
438
|
end
|
@@ -440,17 +464,6 @@ module ActiveRecord
|
|
440
464
|
def update_all(updates)
|
441
465
|
raise ArgumentError, "Empty list of attributes to change" if updates.blank?
|
442
466
|
|
443
|
-
arel = eager_loading? ? apply_join_dependency.arel : build_arel
|
444
|
-
arel.source.left = table
|
445
|
-
|
446
|
-
stmt = Arel::UpdateManager.new
|
447
|
-
stmt.table(arel.source)
|
448
|
-
stmt.key = table[primary_key]
|
449
|
-
stmt.take(arel.limit)
|
450
|
-
stmt.offset(arel.offset)
|
451
|
-
stmt.order(*arel.orders)
|
452
|
-
stmt.wheres = arel.constraints
|
453
|
-
|
454
467
|
if updates.is_a?(Hash)
|
455
468
|
if klass.locking_enabled? &&
|
456
469
|
!updates.key?(klass.locking_column) &&
|
@@ -458,11 +471,17 @@ module ActiveRecord
|
|
458
471
|
attr = table[klass.locking_column]
|
459
472
|
updates[attr.name] = _increment_attribute(attr)
|
460
473
|
end
|
461
|
-
|
474
|
+
values = _substitute_values(updates)
|
462
475
|
else
|
463
|
-
|
476
|
+
values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
|
464
477
|
end
|
465
478
|
|
479
|
+
arel = eager_loading? ? apply_join_dependency.arel : build_arel
|
480
|
+
arel.source.left = table
|
481
|
+
|
482
|
+
group_values_arel_columns = arel_columns(group_values.uniq)
|
483
|
+
having_clause_ast = having_clause.ast unless having_clause.empty?
|
484
|
+
stmt = arel.compile_update(values, table[primary_key], having_clause_ast, group_values_arel_columns)
|
466
485
|
klass.connection.update(stmt, "#{klass} Update All").tap { reset }
|
467
486
|
end
|
468
487
|
|
@@ -474,6 +493,14 @@ module ActiveRecord
|
|
474
493
|
end
|
475
494
|
end
|
476
495
|
|
496
|
+
def update!(id = :all, attributes) # :nodoc:
|
497
|
+
if id == :all
|
498
|
+
each { |record| record.update!(attributes) }
|
499
|
+
else
|
500
|
+
klass.update!(id, attributes)
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
477
504
|
# Updates the counters of the records in the current relation.
|
478
505
|
#
|
479
506
|
# ==== Parameters
|
@@ -583,15 +610,11 @@ module ActiveRecord
|
|
583
610
|
arel = eager_loading? ? apply_join_dependency.arel : build_arel
|
584
611
|
arel.source.left = table
|
585
612
|
|
586
|
-
|
587
|
-
|
588
|
-
stmt
|
589
|
-
stmt.take(arel.limit)
|
590
|
-
stmt.offset(arel.offset)
|
591
|
-
stmt.order(*arel.orders)
|
592
|
-
stmt.wheres = arel.constraints
|
613
|
+
group_values_arel_columns = arel_columns(group_values.uniq)
|
614
|
+
having_clause_ast = having_clause.ast unless having_clause.empty?
|
615
|
+
stmt = arel.compile_delete(table[primary_key], having_clause_ast, group_values_arel_columns)
|
593
616
|
|
594
|
-
klass.connection.delete(stmt, "#{klass}
|
617
|
+
klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
|
595
618
|
end
|
596
619
|
|
597
620
|
# Finds and destroys all records matching the specified conditions.
|
@@ -620,6 +643,47 @@ module ActiveRecord
|
|
620
643
|
where(*args).delete_all
|
621
644
|
end
|
622
645
|
|
646
|
+
# Schedule the query to be performed from a background thread pool.
|
647
|
+
#
|
648
|
+
# Post.where(published: true).load_async # => #<ActiveRecord::Relation>
|
649
|
+
#
|
650
|
+
# When the +Relation+ is iterated, if the background query wasn't executed yet,
|
651
|
+
# it will be performed by the foreground thread.
|
652
|
+
#
|
653
|
+
# Note that {config.active_record.async_query_executor}[https://guides.rubyonrails.org/configuring.html#config-active-record-async-query-executor] must be configured
|
654
|
+
# for queries to actually be executed concurrently. Otherwise it defaults to
|
655
|
+
# executing them in the foreground.
|
656
|
+
#
|
657
|
+
# +load_async+ will also fall back to executing in the foreground in the test environment when transactional
|
658
|
+
# fixtures are enabled.
|
659
|
+
#
|
660
|
+
# If the query was actually executed in the background, the Active Record logs will show
|
661
|
+
# it by prefixing the log line with <tt>ASYNC</tt>:
|
662
|
+
#
|
663
|
+
# ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
|
664
|
+
def load_async
|
665
|
+
return load if !connection.async_enabled?
|
666
|
+
|
667
|
+
unless loaded?
|
668
|
+
result = exec_main_query(async: connection.current_transaction.closed?)
|
669
|
+
|
670
|
+
if result.is_a?(Array)
|
671
|
+
@records = result
|
672
|
+
else
|
673
|
+
@future_result = result
|
674
|
+
end
|
675
|
+
@loaded = true
|
676
|
+
end
|
677
|
+
|
678
|
+
self
|
679
|
+
end
|
680
|
+
|
681
|
+
# Returns <tt>true</tt> if the relation was scheduled on the background
|
682
|
+
# thread pool.
|
683
|
+
def scheduled?
|
684
|
+
!!@future_result
|
685
|
+
end
|
686
|
+
|
623
687
|
# Causes the records to be loaded from the database if they have not
|
624
688
|
# been loaded already. You can use this if for some reason you need
|
625
689
|
# to explicitly load some records before actually using them. The
|
@@ -627,7 +691,7 @@ module ActiveRecord
|
|
627
691
|
#
|
628
692
|
# Post.where(published: true).load # => #<ActiveRecord::Relation>
|
629
693
|
def load(&block)
|
630
|
-
|
694
|
+
if !loaded? || scheduled?
|
631
695
|
@records = exec_queries(&block)
|
632
696
|
@loaded = true
|
633
697
|
end
|
@@ -642,11 +706,14 @@ module ActiveRecord
|
|
642
706
|
end
|
643
707
|
|
644
708
|
def reset
|
709
|
+
@future_result&.cancel
|
710
|
+
@future_result = nil
|
645
711
|
@delegate_to_klass = false
|
646
712
|
@to_sql = @arel = @loaded = @should_eager_load = nil
|
647
713
|
@offsets = @take = nil
|
648
714
|
@cache_keys = nil
|
649
|
-
@
|
715
|
+
@cache_versions = nil
|
716
|
+
@records = nil
|
650
717
|
self
|
651
718
|
end
|
652
719
|
|
@@ -655,16 +722,14 @@ module ActiveRecord
|
|
655
722
|
# User.where(name: 'Oscar').to_sql
|
656
723
|
# # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
|
657
724
|
def to_sql
|
658
|
-
@to_sql ||=
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
relation.to_sql
|
663
|
-
end
|
664
|
-
else
|
665
|
-
conn = klass.connection
|
666
|
-
conn.unprepared_statement { conn.to_sql(arel) }
|
725
|
+
@to_sql ||= if eager_loading?
|
726
|
+
apply_join_dependency do |relation, join_dependency|
|
727
|
+
relation = join_dependency.apply_column_aliases(relation)
|
728
|
+
relation.to_sql
|
667
729
|
end
|
730
|
+
else
|
731
|
+
conn = klass.connection
|
732
|
+
conn.unprepared_statement { conn.to_sql(arel) }
|
668
733
|
end
|
669
734
|
end
|
670
735
|
|
@@ -672,7 +737,7 @@ module ActiveRecord
|
|
672
737
|
#
|
673
738
|
# User.where(name: 'Oscar').where_values_hash
|
674
739
|
# # => {name: "Oscar"}
|
675
|
-
def where_values_hash(relation_table_name = klass.table_name)
|
740
|
+
def where_values_hash(relation_table_name = klass.table_name) # :nodoc:
|
676
741
|
where_clause.to_h(relation_table_name)
|
677
742
|
end
|
678
743
|
|
@@ -692,7 +757,7 @@ module ActiveRecord
|
|
692
757
|
# Joins that are also marked for preloading. In which case we should just eager load them.
|
693
758
|
# Note that this is a naive implementation because we could have strings and symbols which
|
694
759
|
# represent the same association, but that aren't matched by this. Also, we could have
|
695
|
-
# nested hashes which partially match, e.g. { a: :b } & { a: [:b, :c] }
|
760
|
+
# nested hashes which partially match, e.g. <tt>{ a: :b } & { a: [:b, :c] }</tt>
|
696
761
|
def joined_includes_values
|
697
762
|
includes_values & joins_values
|
698
763
|
end
|
@@ -722,6 +787,10 @@ module ActiveRecord
|
|
722
787
|
@values.dup
|
723
788
|
end
|
724
789
|
|
790
|
+
def values_for_queries # :nodoc:
|
791
|
+
@values.except(:extending, :skip_query_cache, :strict_loading)
|
792
|
+
end
|
793
|
+
|
725
794
|
def inspect
|
726
795
|
subject = loaded? ? records : annotate("loading for inspect")
|
727
796
|
entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
|
@@ -756,11 +825,9 @@ module ActiveRecord
|
|
756
825
|
def preload_associations(records) # :nodoc:
|
757
826
|
preload = preload_values
|
758
827
|
preload += includes_values unless eager_loading?
|
759
|
-
preloader = nil
|
760
828
|
scope = strict_loading_value ? StrictLoadingScope : nil
|
761
829
|
preload.each do |associations|
|
762
|
-
|
763
|
-
preloader.preload records, associations, scope
|
830
|
+
ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
|
764
831
|
end
|
765
832
|
end
|
766
833
|
|
@@ -775,8 +842,12 @@ module ActiveRecord
|
|
775
842
|
end
|
776
843
|
|
777
844
|
private
|
778
|
-
def already_in_scope?
|
779
|
-
@delegate_to_klass &&
|
845
|
+
def already_in_scope?(registry)
|
846
|
+
@delegate_to_klass && registry.current_scope(klass, true)
|
847
|
+
end
|
848
|
+
|
849
|
+
def global_scope?(registry)
|
850
|
+
registry.global_current_scope(klass, true)
|
780
851
|
end
|
781
852
|
|
782
853
|
def current_scope_restoring_block(&block)
|
@@ -799,11 +870,20 @@ module ActiveRecord
|
|
799
870
|
klass.create!(attributes, &block)
|
800
871
|
end
|
801
872
|
|
802
|
-
def _scoping(scope)
|
803
|
-
previous
|
873
|
+
def _scoping(scope, registry, all_queries = false)
|
874
|
+
previous = registry.current_scope(klass, true)
|
875
|
+
registry.set_current_scope(klass, scope)
|
876
|
+
|
877
|
+
if all_queries
|
878
|
+
previous_global = registry.global_current_scope(klass, true)
|
879
|
+
registry.set_global_current_scope(klass, scope)
|
880
|
+
end
|
804
881
|
yield
|
805
882
|
ensure
|
806
|
-
klass
|
883
|
+
registry.set_current_scope(klass, previous)
|
884
|
+
if all_queries
|
885
|
+
registry.set_global_current_scope(klass, previous_global)
|
886
|
+
end
|
807
887
|
end
|
808
888
|
|
809
889
|
def _substitute_values(values)
|
@@ -826,44 +906,61 @@ module ActiveRecord
|
|
826
906
|
|
827
907
|
def exec_queries(&block)
|
828
908
|
skip_query_cache_if_necessary do
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
else
|
837
|
-
relation = join_dependency.apply_column_aliases(relation)
|
838
|
-
rows = connection.select_all(relation.arel, "SQL")
|
839
|
-
join_dependency.instantiate(rows, strict_loading_value, &block)
|
840
|
-
end.freeze
|
841
|
-
end
|
842
|
-
else
|
843
|
-
klass.find_by_sql(arel, &block).freeze
|
844
|
-
end
|
909
|
+
rows = if scheduled?
|
910
|
+
future = @future_result
|
911
|
+
@future_result = nil
|
912
|
+
future.result
|
913
|
+
else
|
914
|
+
exec_main_query
|
915
|
+
end
|
845
916
|
|
917
|
+
records = instantiate_records(rows, &block)
|
846
918
|
preload_associations(records) unless skip_preloading_value
|
847
919
|
|
848
920
|
records.each(&:readonly!) if readonly_value
|
849
|
-
records.each
|
921
|
+
records.each { |record| record.strict_loading!(strict_loading_value) } unless strict_loading_value.nil?
|
850
922
|
|
851
923
|
records
|
852
924
|
end
|
853
925
|
end
|
854
926
|
|
855
|
-
def
|
856
|
-
|
857
|
-
|
858
|
-
|
927
|
+
def exec_main_query(async: false)
|
928
|
+
skip_query_cache_if_necessary do
|
929
|
+
if where_clause.contradiction?
|
930
|
+
[].freeze
|
931
|
+
elsif eager_loading?
|
932
|
+
apply_join_dependency do |relation, join_dependency|
|
933
|
+
if relation.null_relation?
|
934
|
+
[].freeze
|
935
|
+
else
|
936
|
+
relation = join_dependency.apply_column_aliases(relation)
|
937
|
+
@_join_dependency = join_dependency
|
938
|
+
connection.select_all(relation.arel, "SQL", async: async)
|
939
|
+
end
|
940
|
+
end
|
941
|
+
else
|
942
|
+
klass._query_by_sql(arel, async: async)
|
859
943
|
end
|
944
|
+
end
|
945
|
+
end
|
946
|
+
|
947
|
+
def instantiate_records(rows, &block)
|
948
|
+
return [].freeze if rows.empty?
|
949
|
+
if eager_loading?
|
950
|
+
records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
|
951
|
+
@_join_dependency = nil
|
952
|
+
records
|
860
953
|
else
|
861
|
-
|
954
|
+
klass._load_from_sql(rows, &block).freeze
|
862
955
|
end
|
863
956
|
end
|
864
957
|
|
865
|
-
def
|
866
|
-
|
958
|
+
def skip_query_cache_if_necessary(&block)
|
959
|
+
if skip_query_cache_value
|
960
|
+
uncached(&block)
|
961
|
+
else
|
962
|
+
yield
|
963
|
+
end
|
867
964
|
end
|
868
965
|
|
869
966
|
def references_eager_loaded_tables?
|
@@ -889,5 +986,9 @@ module ActiveRecord
|
|
889
986
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
890
987
|
string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
|
891
988
|
end
|
989
|
+
|
990
|
+
def limited_count
|
991
|
+
limit_value ? count : limit(2).count
|
992
|
+
end
|
892
993
|
end
|
893
994
|
end
|
data/lib/active_record/result.rb
CHANGED
@@ -36,6 +36,10 @@ module ActiveRecord
|
|
36
36
|
|
37
37
|
attr_reader :columns, :rows, :column_types
|
38
38
|
|
39
|
+
def self.empty # :nodoc:
|
40
|
+
EMPTY
|
41
|
+
end
|
42
|
+
|
39
43
|
def initialize(columns, rows, column_types = {})
|
40
44
|
@columns = columns
|
41
45
|
@rows = rows
|
@@ -43,6 +47,9 @@ module ActiveRecord
|
|
43
47
|
@column_types = column_types
|
44
48
|
end
|
45
49
|
|
50
|
+
EMPTY = new([].freeze, [].freeze, {}.freeze)
|
51
|
+
private_constant :EMPTY
|
52
|
+
|
46
53
|
# Returns true if this result set includes the column named +name+
|
47
54
|
def includes_column?(name)
|
48
55
|
@columns.include? name
|
@@ -57,19 +64,14 @@ module ActiveRecord
|
|
57
64
|
# row as parameter.
|
58
65
|
#
|
59
66
|
# Returns an +Enumerator+ if no block is given.
|
60
|
-
def each
|
67
|
+
def each(&block)
|
61
68
|
if block_given?
|
62
|
-
hash_rows.each
|
69
|
+
hash_rows.each(&block)
|
63
70
|
else
|
64
71
|
hash_rows.to_enum { @rows.size }
|
65
72
|
end
|
66
73
|
end
|
67
74
|
|
68
|
-
alias :map! :map
|
69
|
-
alias :collect! :map
|
70
|
-
deprecate "map!": :map
|
71
|
-
deprecate "collect!": :map
|
72
|
-
|
73
75
|
# Returns true if there are no records, otherwise false.
|
74
76
|
def empty?
|
75
77
|
rows.empty?
|
@@ -91,6 +93,14 @@ module ActiveRecord
|
|
91
93
|
n ? hash_rows.last(n) : hash_rows.last
|
92
94
|
end
|
93
95
|
|
96
|
+
def result # :nodoc:
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def cancel # :nodoc:
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
94
104
|
def cast_values(type_overrides = {}) # :nodoc:
|
95
105
|
if columns.one?
|
96
106
|
# Separated to avoid allocating an array per row
|
@@ -1,24 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/per_thread_registry"
|
4
|
-
|
5
3
|
module ActiveRecord
|
6
4
|
# This is a thread locals registry for Active Record. For example:
|
7
5
|
#
|
8
|
-
# ActiveRecord::RuntimeRegistry.
|
9
|
-
#
|
10
|
-
# returns the connection handler local to the current thread.
|
6
|
+
# ActiveRecord::RuntimeRegistry.sql_runtime
|
11
7
|
#
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
extend ActiveSupport::PerThreadRegistry
|
8
|
+
# returns the connection handler local to the current unit of execution (either thread of fiber).
|
9
|
+
module RuntimeRegistry # :nodoc:
|
10
|
+
extend self
|
16
11
|
|
17
|
-
|
12
|
+
def sql_runtime
|
13
|
+
ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime]
|
14
|
+
end
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
|
16
|
+
def sql_runtime=(runtime)
|
17
|
+
ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] = runtime
|
22
18
|
end
|
23
19
|
end
|
24
20
|
end
|