activerecord 6.1.7.10 → 7.0.0.alpha1
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 +726 -1404
- 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 +31 -9
- 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 +1 -1
- 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 +14 -23
- 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/preloader/association.rb +161 -47
- data/lib/active_record/associations/preloader/batch.rb +51 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +37 -11
- data/lib/active_record/associations/preloader.rb +46 -110
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +76 -81
- data/lib/active_record/asynchronous_queries_tracker.rb +57 -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 +41 -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 +6 -9
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +3 -18
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +2 -14
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -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 +31 -558
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +12 -14
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
- data/lib/active_record/connection_adapters/abstract/transaction.rb +3 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +112 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +1 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -14
- 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 +28 -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 +6 -32
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +159 -102
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -37
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -19
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +20 -38
- data/lib/active_record/core.rb +111 -125
- data/lib/active_record/database_configurations/connection_url_resolver.rb +0 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -0
- data/lib/active_record/database_configurations/hash_config.rb +27 -1
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +17 -9
- data/lib/active_record/delegated_type.rb +33 -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 +29 -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 +80 -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 +41 -41
- data/lib/active_record/errors.rb +66 -3
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +40 -5
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +16 -11
- 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 +34 -5
- data/lib/active_record/integration.rb +1 -1
- data/lib/active_record/internal_metadata.rb +1 -5
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/log_subscriber.rb +6 -2
- 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/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +89 -10
- 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 -31
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/no_touching.rb +2 -2
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +134 -45
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +203 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +117 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +72 -48
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +45 -44
- 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 +39 -26
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +31 -22
- 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 +230 -49
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +8 -4
- data/lib/active_record/relation.rb +166 -77
- data/lib/active_record/result.rb +17 -2
- data/lib/active_record/runtime_registry.rb +2 -4
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +3 -3
- data/lib/active_record/schema_migration.rb +0 -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 +40 -22
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/store.rb +1 -6
- data/lib/active_record/tasks/database_tasks.rb +106 -22
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +9 -13
- 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.rb +170 -2
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +18 -22
- data/lib/arel/delete_manager.rb +2 -4
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +8 -13
- 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 +3 -2
- data/lib/arel/predications.rb +1 -1
- 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 +2 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +6 -1
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +43 -2
- data/lib/arel.rb +1 -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
- metadata +52 -14
@@ -31,6 +31,9 @@ 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
|
36
|
+
@limited_count = nil
|
34
37
|
end
|
35
38
|
|
36
39
|
def initialize_copy(other)
|
@@ -67,8 +70,12 @@ module ActiveRecord
|
|
67
70
|
# user = users.new { |user| user.name = 'Oscar' }
|
68
71
|
# user.name # => Oscar
|
69
72
|
def new(attributes = nil, &block)
|
70
|
-
|
71
|
-
|
73
|
+
if attributes.is_a?(Array)
|
74
|
+
attributes.collect { |attr| new(attr, &block) }
|
75
|
+
else
|
76
|
+
block = current_scope_restoring_block(&block)
|
77
|
+
scoping { _new(attributes, &block) }
|
78
|
+
end
|
72
79
|
end
|
73
80
|
alias build new
|
74
81
|
|
@@ -148,7 +155,7 @@ module ActiveRecord
|
|
148
155
|
# above can be alternatively written this way:
|
149
156
|
#
|
150
157
|
# # Find the first user named "Scarlett" or create a new one with a
|
151
|
-
# #
|
158
|
+
# # particular last name.
|
152
159
|
# User.find_or_create_by(first_name: 'Scarlett') do |user|
|
153
160
|
# user.last_name = 'Johansson'
|
154
161
|
# end
|
@@ -175,7 +182,7 @@ module ActiveRecord
|
|
175
182
|
find_by(attributes) || create!(attributes, &block)
|
176
183
|
end
|
177
184
|
|
178
|
-
# Attempts to create a record with the given attributes in a table that has a unique constraint
|
185
|
+
# Attempts to create a record with the given attributes in a table that has a unique database constraint
|
179
186
|
# on one or several of its columns. If a row already exists with one or several of these
|
180
187
|
# unique constraints, the exception such an insertion would normally raise is caught,
|
181
188
|
# and the existing record with those attributes is found using #find_by!.
|
@@ -186,7 +193,7 @@ module ActiveRecord
|
|
186
193
|
#
|
187
194
|
# There are several drawbacks to #create_or_find_by, though:
|
188
195
|
#
|
189
|
-
# * The underlying table must have the relevant columns defined with unique constraints.
|
196
|
+
# * The underlying table must have the relevant columns defined with unique database constraints.
|
190
197
|
# * A unique constraint violation may be triggered by only one, or at least less than all,
|
191
198
|
# of the given attributes. This means that the subsequent #find_by! may fail to find a
|
192
199
|
# matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
|
@@ -257,13 +264,20 @@ module ActiveRecord
|
|
257
264
|
|
258
265
|
# Returns size of the records.
|
259
266
|
def size
|
260
|
-
loaded?
|
267
|
+
if loaded?
|
268
|
+
records.length
|
269
|
+
else
|
270
|
+
count(:all)
|
271
|
+
end
|
261
272
|
end
|
262
273
|
|
263
274
|
# Returns true if there are no records.
|
264
275
|
def empty?
|
265
|
-
|
266
|
-
|
276
|
+
if loaded?
|
277
|
+
records.empty?
|
278
|
+
else
|
279
|
+
!exists?
|
280
|
+
end
|
267
281
|
end
|
268
282
|
|
269
283
|
# Returns true if there are no records.
|
@@ -281,13 +295,15 @@ module ActiveRecord
|
|
281
295
|
# Returns true if there is exactly one record.
|
282
296
|
def one?
|
283
297
|
return super if block_given?
|
284
|
-
|
298
|
+
return records.one? if limit_value || loaded?
|
299
|
+
limited_count == 1
|
285
300
|
end
|
286
301
|
|
287
302
|
# Returns true if there is more than one record.
|
288
303
|
def many?
|
289
304
|
return super if block_given?
|
290
|
-
|
305
|
+
return records.many? if limit_value || loaded?
|
306
|
+
limited_count > 1
|
291
307
|
end
|
292
308
|
|
293
309
|
# Returns a stable cache key that can be used to identify this query.
|
@@ -344,7 +360,7 @@ module ActiveRecord
|
|
344
360
|
def compute_cache_version(timestamp_column) # :nodoc:
|
345
361
|
timestamp_column = timestamp_column.to_s
|
346
362
|
|
347
|
-
if loaded?
|
363
|
+
if loaded?
|
348
364
|
size = records.size
|
349
365
|
if size > 0
|
350
366
|
timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
|
@@ -357,6 +373,7 @@ module ActiveRecord
|
|
357
373
|
|
358
374
|
if collection.has_limit_or_offset?
|
359
375
|
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
376
|
+
query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
|
360
377
|
subquery_alias = "subquery_for_cache_key"
|
361
378
|
subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
|
362
379
|
arel = query.build_subquery(subquery_alias, select_values % subquery_column)
|
@@ -400,15 +417,28 @@ module ActiveRecord
|
|
400
417
|
# end
|
401
418
|
# # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
|
402
419
|
#
|
420
|
+
# If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
|
421
|
+
# for the relation including +update+ and +delete+ on instances.
|
422
|
+
# Once +all_queries+ is set to true it cannot be set to false in a
|
423
|
+
# nested block.
|
424
|
+
#
|
403
425
|
# Please check unscoped if you want to remove all previous scopes (including
|
404
426
|
# the default_scope) during the execution of a block.
|
405
|
-
def scoping
|
406
|
-
|
427
|
+
def scoping(all_queries: nil, &block)
|
428
|
+
registry = klass.scope_registry
|
429
|
+
if global_scope?(registry) && all_queries == false
|
430
|
+
raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
|
431
|
+
elsif already_in_scope?(registry)
|
432
|
+
yield
|
433
|
+
else
|
434
|
+
_scoping(self, registry, all_queries, &block)
|
435
|
+
end
|
407
436
|
end
|
408
437
|
|
409
438
|
def _exec_scope(*args, &block) # :nodoc:
|
410
439
|
@delegate_to_klass = true
|
411
|
-
|
440
|
+
registry = klass.scope_registry
|
441
|
+
_scoping(nil, registry) { instance_exec(*args, &block) || self }
|
412
442
|
ensure
|
413
443
|
@delegate_to_klass = false
|
414
444
|
end
|
@@ -440,17 +470,6 @@ module ActiveRecord
|
|
440
470
|
def update_all(updates)
|
441
471
|
raise ArgumentError, "Empty list of attributes to change" if updates.blank?
|
442
472
|
|
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
473
|
if updates.is_a?(Hash)
|
455
474
|
if klass.locking_enabled? &&
|
456
475
|
!updates.key?(klass.locking_column) &&
|
@@ -458,11 +477,16 @@ module ActiveRecord
|
|
458
477
|
attr = table[klass.locking_column]
|
459
478
|
updates[attr.name] = _increment_attribute(attr)
|
460
479
|
end
|
461
|
-
|
480
|
+
values = _substitute_values(updates)
|
462
481
|
else
|
463
|
-
|
482
|
+
values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
|
464
483
|
end
|
465
484
|
|
485
|
+
arel = eager_loading? ? apply_join_dependency.arel : build_arel
|
486
|
+
arel.source.left = table
|
487
|
+
|
488
|
+
stmt = arel.compile_update(values, table[primary_key])
|
489
|
+
|
466
490
|
klass.connection.update(stmt, "#{klass} Update All").tap { reset }
|
467
491
|
end
|
468
492
|
|
@@ -474,6 +498,14 @@ module ActiveRecord
|
|
474
498
|
end
|
475
499
|
end
|
476
500
|
|
501
|
+
def update!(id = :all, attributes) # :nodoc:
|
502
|
+
if id == :all
|
503
|
+
each { |record| record.update!(attributes) }
|
504
|
+
else
|
505
|
+
klass.update!(id, attributes)
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
477
509
|
# Updates the counters of the records in the current relation.
|
478
510
|
#
|
479
511
|
# ==== Parameters
|
@@ -583,15 +615,9 @@ module ActiveRecord
|
|
583
615
|
arel = eager_loading? ? apply_join_dependency.arel : build_arel
|
584
616
|
arel.source.left = table
|
585
617
|
|
586
|
-
stmt =
|
587
|
-
stmt.from(arel.source)
|
588
|
-
stmt.key = table[primary_key]
|
589
|
-
stmt.take(arel.limit)
|
590
|
-
stmt.offset(arel.offset)
|
591
|
-
stmt.order(*arel.orders)
|
592
|
-
stmt.wheres = arel.constraints
|
618
|
+
stmt = arel.compile_delete(table[primary_key])
|
593
619
|
|
594
|
-
klass.connection.delete(stmt, "#{klass}
|
620
|
+
klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
|
595
621
|
end
|
596
622
|
|
597
623
|
# Finds and destroys all records matching the specified conditions.
|
@@ -620,6 +646,32 @@ module ActiveRecord
|
|
620
646
|
where(*args).delete_all
|
621
647
|
end
|
622
648
|
|
649
|
+
# Schedule the query to be performed from a background thread pool.
|
650
|
+
#
|
651
|
+
# Post.where(published: true).load_async # => #<ActiveRecord::Relation>
|
652
|
+
def load_async
|
653
|
+
return load if !connection.async_enabled?
|
654
|
+
|
655
|
+
unless loaded?
|
656
|
+
result = exec_main_query(async: connection.current_transaction.closed?)
|
657
|
+
|
658
|
+
if result.is_a?(Array)
|
659
|
+
@records = result
|
660
|
+
else
|
661
|
+
@future_result = result
|
662
|
+
end
|
663
|
+
@loaded = true
|
664
|
+
end
|
665
|
+
|
666
|
+
self
|
667
|
+
end
|
668
|
+
|
669
|
+
# Returns <tt>true</tt> if the relation was scheduled on the background
|
670
|
+
# thread pool.
|
671
|
+
def scheduled?
|
672
|
+
!!@future_result
|
673
|
+
end
|
674
|
+
|
623
675
|
# Causes the records to be loaded from the database if they have not
|
624
676
|
# been loaded already. You can use this if for some reason you need
|
625
677
|
# to explicitly load some records before actually using them. The
|
@@ -627,7 +679,7 @@ module ActiveRecord
|
|
627
679
|
#
|
628
680
|
# Post.where(published: true).load # => #<ActiveRecord::Relation>
|
629
681
|
def load(&block)
|
630
|
-
|
682
|
+
if !loaded? || scheduled?
|
631
683
|
@records = exec_queries(&block)
|
632
684
|
@loaded = true
|
633
685
|
end
|
@@ -642,11 +694,14 @@ module ActiveRecord
|
|
642
694
|
end
|
643
695
|
|
644
696
|
def reset
|
697
|
+
@future_result&.cancel
|
698
|
+
@future_result = nil
|
645
699
|
@delegate_to_klass = false
|
646
700
|
@to_sql = @arel = @loaded = @should_eager_load = nil
|
647
701
|
@offsets = @take = nil
|
648
702
|
@cache_keys = nil
|
649
|
-
@records =
|
703
|
+
@records = nil
|
704
|
+
@limited_count = nil
|
650
705
|
self
|
651
706
|
end
|
652
707
|
|
@@ -655,16 +710,14 @@ module ActiveRecord
|
|
655
710
|
# User.where(name: 'Oscar').to_sql
|
656
711
|
# # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
|
657
712
|
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) }
|
713
|
+
@to_sql ||= if eager_loading?
|
714
|
+
apply_join_dependency do |relation, join_dependency|
|
715
|
+
relation = join_dependency.apply_column_aliases(relation)
|
716
|
+
relation.to_sql
|
667
717
|
end
|
718
|
+
else
|
719
|
+
conn = klass.connection
|
720
|
+
conn.unprepared_statement { conn.to_sql(arel) }
|
668
721
|
end
|
669
722
|
end
|
670
723
|
|
@@ -722,6 +775,10 @@ module ActiveRecord
|
|
722
775
|
@values.dup
|
723
776
|
end
|
724
777
|
|
778
|
+
def values_for_queries # :nodoc:
|
779
|
+
@values.except(:extending, :skip_query_cache, :strict_loading)
|
780
|
+
end
|
781
|
+
|
725
782
|
def inspect
|
726
783
|
subject = loaded? ? records : annotate("loading for inspect")
|
727
784
|
entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
|
@@ -756,11 +813,9 @@ module ActiveRecord
|
|
756
813
|
def preload_associations(records) # :nodoc:
|
757
814
|
preload = preload_values
|
758
815
|
preload += includes_values unless eager_loading?
|
759
|
-
preloader = nil
|
760
816
|
scope = strict_loading_value ? StrictLoadingScope : nil
|
761
817
|
preload.each do |associations|
|
762
|
-
|
763
|
-
preloader.preload records, associations, scope
|
818
|
+
ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
|
764
819
|
end
|
765
820
|
end
|
766
821
|
|
@@ -775,8 +830,12 @@ module ActiveRecord
|
|
775
830
|
end
|
776
831
|
|
777
832
|
private
|
778
|
-
def already_in_scope?
|
779
|
-
@delegate_to_klass &&
|
833
|
+
def already_in_scope?(registry)
|
834
|
+
@delegate_to_klass && registry.current_scope(klass, true)
|
835
|
+
end
|
836
|
+
|
837
|
+
def global_scope?(registry)
|
838
|
+
registry.global_current_scope(klass, true)
|
780
839
|
end
|
781
840
|
|
782
841
|
def current_scope_restoring_block(&block)
|
@@ -799,11 +858,20 @@ module ActiveRecord
|
|
799
858
|
klass.create!(attributes, &block)
|
800
859
|
end
|
801
860
|
|
802
|
-
def _scoping(scope)
|
803
|
-
previous
|
861
|
+
def _scoping(scope, registry, all_queries = false)
|
862
|
+
previous = registry.current_scope(klass, true)
|
863
|
+
registry.set_current_scope(klass, scope)
|
864
|
+
|
865
|
+
if all_queries
|
866
|
+
previous_global = registry.global_current_scope(klass, true)
|
867
|
+
registry.set_global_current_scope(klass, scope)
|
868
|
+
end
|
804
869
|
yield
|
805
870
|
ensure
|
806
|
-
klass
|
871
|
+
registry.set_current_scope(klass, previous)
|
872
|
+
if all_queries
|
873
|
+
registry.set_global_current_scope(klass, previous_global)
|
874
|
+
end
|
807
875
|
end
|
808
876
|
|
809
877
|
def _substitute_values(values)
|
@@ -826,23 +894,15 @@ module ActiveRecord
|
|
826
894
|
|
827
895
|
def exec_queries(&block)
|
828
896
|
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
|
897
|
+
rows = if scheduled?
|
898
|
+
future = @future_result
|
899
|
+
@future_result = nil
|
900
|
+
future.result
|
901
|
+
else
|
902
|
+
exec_main_query
|
903
|
+
end
|
845
904
|
|
905
|
+
records = instantiate_records(rows, &block)
|
846
906
|
preload_associations(records) unless skip_preloading_value
|
847
907
|
|
848
908
|
records.each(&:readonly!) if readonly_value
|
@@ -852,18 +912,43 @@ module ActiveRecord
|
|
852
912
|
end
|
853
913
|
end
|
854
914
|
|
855
|
-
def
|
856
|
-
|
857
|
-
|
858
|
-
|
915
|
+
def exec_main_query(async: false)
|
916
|
+
skip_query_cache_if_necessary do
|
917
|
+
if where_clause.contradiction?
|
918
|
+
[].freeze
|
919
|
+
elsif eager_loading?
|
920
|
+
apply_join_dependency do |relation, join_dependency|
|
921
|
+
if relation.null_relation?
|
922
|
+
[].freeze
|
923
|
+
else
|
924
|
+
relation = join_dependency.apply_column_aliases(relation)
|
925
|
+
@_join_dependency = join_dependency
|
926
|
+
connection.select_all(relation.arel, "SQL", async: async)
|
927
|
+
end
|
928
|
+
end
|
929
|
+
else
|
930
|
+
klass._query_by_sql(arel, async: async)
|
859
931
|
end
|
932
|
+
end
|
933
|
+
end
|
934
|
+
|
935
|
+
def instantiate_records(rows, &block)
|
936
|
+
return [].freeze if rows.empty?
|
937
|
+
if eager_loading?
|
938
|
+
records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
|
939
|
+
@_join_dependency = nil
|
940
|
+
records
|
860
941
|
else
|
861
|
-
|
942
|
+
klass._load_from_sql(rows, &block).freeze
|
862
943
|
end
|
863
944
|
end
|
864
945
|
|
865
|
-
def
|
866
|
-
|
946
|
+
def skip_query_cache_if_necessary(&block)
|
947
|
+
if skip_query_cache_value
|
948
|
+
uncached(&block)
|
949
|
+
else
|
950
|
+
yield
|
951
|
+
end
|
867
952
|
end
|
868
953
|
|
869
954
|
def references_eager_loaded_tables?
|
@@ -889,5 +974,9 @@ module ActiveRecord
|
|
889
974
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
890
975
|
string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
|
891
976
|
end
|
977
|
+
|
978
|
+
def limited_count
|
979
|
+
@limited_count ||= limit(2).count
|
980
|
+
end
|
892
981
|
end
|
893
982
|
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,9 +64,9 @@ 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
|
@@ -91,6 +98,14 @@ module ActiveRecord
|
|
91
98
|
n ? hash_rows.last(n) : hash_rows.last
|
92
99
|
end
|
93
100
|
|
101
|
+
def result # :nodoc:
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def cancel # :nodoc:
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
94
109
|
def cast_values(type_overrides = {}) # :nodoc:
|
95
110
|
if columns.one?
|
96
111
|
# Separated to avoid allocating an array per row
|
@@ -16,9 +16,7 @@ module ActiveRecord
|
|
16
16
|
|
17
17
|
attr_accessor :sql_runtime
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
|
22
|
-
end
|
19
|
+
def self.sql_runtime; instance.sql_runtime; end
|
20
|
+
def self.sql_runtime=(x); instance.sql_runtime = x; end
|
23
21
|
end
|
24
22
|
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
|
##
|