activerecord 6.1.4.1 → 7.0.1
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 +1132 -936
- data/MIT-LICENSE +1 -1
- 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 +34 -27
- data/lib/active_record/associations/collection_proxy.rb +8 -3
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- 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 +6 -2
- data/lib/active_record/associations/preloader/association.rb +187 -55
- 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 +118 -90
- 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 +66 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +13 -14
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +6 -21
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -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 +34 -9
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +78 -22
- 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 +97 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +37 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +5 -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 +21 -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 +50 -50
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -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 +29 -18
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +205 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +27 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +16 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +89 -30
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +47 -53
- data/lib/active_record/core.rb +122 -132
- 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 +16 -32
- data/lib/active_record/delegated_type.rb +52 -11
- 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 +61 -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 +208 -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 +49 -42
- 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 +17 -20
- 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 +3 -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 +9 -3
- data/lib/active_record/log_subscriber.rb +14 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +8 -3
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +107 -3
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +109 -79
- data/lib/active_record/model_schema.rb +45 -58
- 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 +219 -52
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +127 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +66 -129
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +67 -50
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +43 -38
- data/lib/active_record/relation/delegation.rb +6 -6
- 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 +243 -61
- 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 +184 -84
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +10 -3
- data/lib/active_record/schema_migration.rb +4 -4
- data/lib/active_record/scoping/default.rb +61 -12
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +120 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +4 -4
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +2 -2
- 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 +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -1
- data/lib/active_record.rb +204 -28
- 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 +59 -14
@@ -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_formatted_s(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
432
|
def _exec_scope(*args, &block) # :nodoc:
|
410
433
|
@delegate_to_klass = true
|
411
|
-
|
434
|
+
registry = klass.scope_registry
|
435
|
+
_scoping(nil, registry) { instance_exec(*args, &block) || 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 fallback 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,13 @@ 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
|
-
@records =
|
715
|
+
@records = nil
|
650
716
|
self
|
651
717
|
end
|
652
718
|
|
@@ -655,16 +721,14 @@ module ActiveRecord
|
|
655
721
|
# User.where(name: 'Oscar').to_sql
|
656
722
|
# # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
|
657
723
|
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) }
|
724
|
+
@to_sql ||= if eager_loading?
|
725
|
+
apply_join_dependency do |relation, join_dependency|
|
726
|
+
relation = join_dependency.apply_column_aliases(relation)
|
727
|
+
relation.to_sql
|
667
728
|
end
|
729
|
+
else
|
730
|
+
conn = klass.connection
|
731
|
+
conn.unprepared_statement { conn.to_sql(arel) }
|
668
732
|
end
|
669
733
|
end
|
670
734
|
|
@@ -722,6 +786,10 @@ module ActiveRecord
|
|
722
786
|
@values.dup
|
723
787
|
end
|
724
788
|
|
789
|
+
def values_for_queries # :nodoc:
|
790
|
+
@values.except(:extending, :skip_query_cache, :strict_loading)
|
791
|
+
end
|
792
|
+
|
725
793
|
def inspect
|
726
794
|
subject = loaded? ? records : annotate("loading for inspect")
|
727
795
|
entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
|
@@ -756,11 +824,9 @@ module ActiveRecord
|
|
756
824
|
def preload_associations(records) # :nodoc:
|
757
825
|
preload = preload_values
|
758
826
|
preload += includes_values unless eager_loading?
|
759
|
-
preloader = nil
|
760
827
|
scope = strict_loading_value ? StrictLoadingScope : nil
|
761
828
|
preload.each do |associations|
|
762
|
-
|
763
|
-
preloader.preload records, associations, scope
|
829
|
+
ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
|
764
830
|
end
|
765
831
|
end
|
766
832
|
|
@@ -775,8 +841,12 @@ module ActiveRecord
|
|
775
841
|
end
|
776
842
|
|
777
843
|
private
|
778
|
-
def already_in_scope?
|
779
|
-
@delegate_to_klass &&
|
844
|
+
def already_in_scope?(registry)
|
845
|
+
@delegate_to_klass && registry.current_scope(klass, true)
|
846
|
+
end
|
847
|
+
|
848
|
+
def global_scope?(registry)
|
849
|
+
registry.global_current_scope(klass, true)
|
780
850
|
end
|
781
851
|
|
782
852
|
def current_scope_restoring_block(&block)
|
@@ -799,11 +869,20 @@ module ActiveRecord
|
|
799
869
|
klass.create!(attributes, &block)
|
800
870
|
end
|
801
871
|
|
802
|
-
def _scoping(scope)
|
803
|
-
previous
|
872
|
+
def _scoping(scope, registry, all_queries = false)
|
873
|
+
previous = registry.current_scope(klass, true)
|
874
|
+
registry.set_current_scope(klass, scope)
|
875
|
+
|
876
|
+
if all_queries
|
877
|
+
previous_global = registry.global_current_scope(klass, true)
|
878
|
+
registry.set_global_current_scope(klass, scope)
|
879
|
+
end
|
804
880
|
yield
|
805
881
|
ensure
|
806
|
-
klass
|
882
|
+
registry.set_current_scope(klass, previous)
|
883
|
+
if all_queries
|
884
|
+
registry.set_global_current_scope(klass, previous_global)
|
885
|
+
end
|
807
886
|
end
|
808
887
|
|
809
888
|
def _substitute_values(values)
|
@@ -826,23 +905,15 @@ module ActiveRecord
|
|
826
905
|
|
827
906
|
def exec_queries(&block)
|
828
907
|
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
|
908
|
+
rows = if scheduled?
|
909
|
+
future = @future_result
|
910
|
+
@future_result = nil
|
911
|
+
future.result
|
912
|
+
else
|
913
|
+
exec_main_query
|
914
|
+
end
|
845
915
|
|
916
|
+
records = instantiate_records(rows, &block)
|
846
917
|
preload_associations(records) unless skip_preloading_value
|
847
918
|
|
848
919
|
records.each(&:readonly!) if readonly_value
|
@@ -852,18 +923,43 @@ module ActiveRecord
|
|
852
923
|
end
|
853
924
|
end
|
854
925
|
|
855
|
-
def
|
856
|
-
|
857
|
-
|
858
|
-
|
926
|
+
def exec_main_query(async: false)
|
927
|
+
skip_query_cache_if_necessary do
|
928
|
+
if where_clause.contradiction?
|
929
|
+
[].freeze
|
930
|
+
elsif eager_loading?
|
931
|
+
apply_join_dependency do |relation, join_dependency|
|
932
|
+
if relation.null_relation?
|
933
|
+
[].freeze
|
934
|
+
else
|
935
|
+
relation = join_dependency.apply_column_aliases(relation)
|
936
|
+
@_join_dependency = join_dependency
|
937
|
+
connection.select_all(relation.arel, "SQL", async: async)
|
938
|
+
end
|
939
|
+
end
|
940
|
+
else
|
941
|
+
klass._query_by_sql(arel, async: async)
|
859
942
|
end
|
943
|
+
end
|
944
|
+
end
|
945
|
+
|
946
|
+
def instantiate_records(rows, &block)
|
947
|
+
return [].freeze if rows.empty?
|
948
|
+
if eager_loading?
|
949
|
+
records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
|
950
|
+
@_join_dependency = nil
|
951
|
+
records
|
860
952
|
else
|
861
|
-
|
953
|
+
klass._load_from_sql(rows, &block).freeze
|
862
954
|
end
|
863
955
|
end
|
864
956
|
|
865
|
-
def
|
866
|
-
|
957
|
+
def skip_query_cache_if_necessary(&block)
|
958
|
+
if skip_query_cache_value
|
959
|
+
uncached(&block)
|
960
|
+
else
|
961
|
+
yield
|
962
|
+
end
|
867
963
|
end
|
868
964
|
|
869
965
|
def references_eager_loaded_tables?
|
@@ -889,5 +985,9 @@ module ActiveRecord
|
|
889
985
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
890
986
|
string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
|
891
987
|
end
|
988
|
+
|
989
|
+
def limited_count
|
990
|
+
limit_value ? count : limit(2).count
|
991
|
+
end
|
892
992
|
end
|
893
993
|
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
|
@@ -54,7 +54,7 @@ module ActiveRecord
|
|
54
54
|
# Accepts an array, or string of SQL conditions and sanitizes
|
55
55
|
# them into a valid SQL fragment for an ORDER clause.
|
56
56
|
#
|
57
|
-
# sanitize_sql_for_order(["field(id, ?)", [1,3,2]])
|
57
|
+
# sanitize_sql_for_order([Arel.sql("field(id, ?)"), [1,3,2]])
|
58
58
|
# # => "field(id, 1,3,2)"
|
59
59
|
#
|
60
60
|
# sanitize_sql_for_order("id ASC")
|
@@ -137,14 +137,18 @@ module ActiveRecord
|
|
137
137
|
def disallow_raw_sql!(args, permit: connection.column_name_matcher) # :nodoc:
|
138
138
|
unexpected = nil
|
139
139
|
args.each do |arg|
|
140
|
-
next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s)
|
140
|
+
next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s.strip)
|
141
141
|
(unexpected ||= []) << arg
|
142
142
|
end
|
143
143
|
|
144
144
|
if unexpected
|
145
145
|
raise(ActiveRecord::UnknownAttributeReference,
|
146
|
-
"
|
147
|
-
|
146
|
+
"Dangerous query method (method whose arguments are used as raw " \
|
147
|
+
"SQL) called with non-attribute argument(s): " \
|
148
|
+
"#{unexpected.map(&:inspect).join(", ")}." \
|
149
|
+
"This method should not be called with user-provided values, such as request " \
|
150
|
+
"parameters or model attributes. Known-safe values can be passed " \
|
151
|
+
"by wrapping them in Arel.sql()."
|
148
152
|
)
|
149
153
|
end
|
150
154
|
end
|
@@ -183,13 +187,13 @@ module ActiveRecord
|
|
183
187
|
if value.respond_to?(:map) && !value.acts_like?(:string)
|
184
188
|
values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
|
185
189
|
if values.empty?
|
186
|
-
c.
|
190
|
+
c.quote_bound_value(nil)
|
187
191
|
else
|
188
|
-
values.map! { |v| c.
|
192
|
+
values.map! { |v| c.quote_bound_value(v) }.join(",")
|
189
193
|
end
|
190
194
|
else
|
191
195
|
value = value.id_for_database if value.respond_to?(:id_for_database)
|
192
|
-
c.
|
196
|
+
c.quote_bound_value(value)
|
193
197
|
end
|
194
198
|
end
|
195
199
|
|
@@ -7,14 +7,14 @@ module ActiveRecord
|
|
7
7
|
#
|
8
8
|
# This class is used to dump the database schema for some connection to some
|
9
9
|
# output format (i.e., ActiveRecord::Schema).
|
10
|
-
class SchemaDumper
|
10
|
+
class SchemaDumper # :nodoc:
|
11
11
|
private_class_method :new
|
12
12
|
|
13
13
|
##
|
14
14
|
# :singleton-method:
|
15
15
|
# A list of tables which should not be dumped to the schema.
|
16
|
-
# Acceptable values are strings as well as regexp if ActiveRecord
|
17
|
-
# Only strings are accepted if ActiveRecord
|
16
|
+
# Acceptable values are strings as well as regexp if ActiveRecord.schema_format == :ruby.
|
17
|
+
# Only strings are accepted if ActiveRecord.schema_format == :sql.
|
18
18
|
cattr_accessor :ignore_tables, default: []
|
19
19
|
|
20
20
|
##
|
@@ -47,6 +47,7 @@ module ActiveRecord
|
|
47
47
|
def dump(stream)
|
48
48
|
header(stream)
|
49
49
|
extensions(stream)
|
50
|
+
types(stream)
|
50
51
|
tables(stream)
|
51
52
|
trailer(stream)
|
52
53
|
stream
|
@@ -99,6 +100,10 @@ HEADER
|
|
99
100
|
def extensions(stream)
|
100
101
|
end
|
101
102
|
|
103
|
+
# (enum) types are only supported by PostgreSQL
|
104
|
+
def types(stream)
|
105
|
+
end
|
106
|
+
|
102
107
|
def tables(stream)
|
103
108
|
sorted_tables = @connection.tables.sort
|
104
109
|
|
@@ -154,6 +159,7 @@ HEADER
|
|
154
159
|
columns.each do |column|
|
155
160
|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
|
156
161
|
next if column.name == pk
|
162
|
+
|
157
163
|
type, colspec = column_spec(column)
|
158
164
|
if type.is_a?(Symbol)
|
159
165
|
tbl.print " t.#{type} #{column.name.inspect}"
|
@@ -259,6 +265,7 @@ HEADER
|
|
259
265
|
|
260
266
|
parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
|
261
267
|
parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
|
268
|
+
parts << "deferrable: #{foreign_key.deferrable.inspect}" if foreign_key.deferrable
|
262
269
|
|
263
270
|
" #{parts.join(', ')}"
|
264
271
|
end
|