activerecord 7.1.0 → 7.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +194 -0
- data/README.rdoc +1 -0
- data/lib/active_record/associations/association.rb +2 -1
- data/lib/active_record/associations/preloader/association.rb +4 -1
- data/lib/active_record/associations.rb +15 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +14 -10
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +11 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +13 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +3 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +14 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -32
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -3
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +1 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +9 -1
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +62 -28
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -1
- data/lib/active_record/encryption/encrypted_attribute_type.rb +4 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +0 -15
- data/lib/active_record/enum.rb +6 -9
- data/lib/active_record/errors.rb +5 -4
- data/lib/active_record/fixtures.rb +16 -0
- data/lib/active_record/future_result.rb +1 -0
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +3 -3
- data/lib/active_record/internal_metadata.rb +3 -1
- data/lib/active_record/middleware/database_selector.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +4 -1
- data/lib/active_record/migration/compatibility.rb +8 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +9 -5
- data/lib/active_record/model_schema.rb +5 -5
- data/lib/active_record/nested_attributes.rb +7 -9
- data/lib/active_record/normalization.rb +8 -0
- data/lib/active_record/persistence.rb +4 -3
- data/lib/active_record/promise.rb +1 -1
- data/lib/active_record/railtie.rb +1 -1
- data/lib/active_record/railties/controller_runtime.rb +2 -1
- data/lib/active_record/railties/databases.rake +5 -5
- data/lib/active_record/reflection.rb +13 -1
- data/lib/active_record/relation/calculations.rb +44 -9
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +1 -1
- data/lib/active_record/relation.rb +18 -3
- data/lib/active_record/runtime_registry.rb +15 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/secure_token.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +5 -5
- data/lib/active_record/timestamp.rb +1 -1
- data/lib/arel/nodes/homogeneous_in.rb +1 -1
- metadata +10 -9
@@ -268,6 +268,8 @@ module ActiveRecord
|
|
268
268
|
# name: Reginald the Pirate
|
269
269
|
# monkey_id: 1
|
270
270
|
#
|
271
|
+
# <code></code>
|
272
|
+
#
|
271
273
|
# ### in monkeys.yml
|
272
274
|
#
|
273
275
|
# george:
|
@@ -285,6 +287,8 @@ module ActiveRecord
|
|
285
287
|
# name: Reginald the Pirate
|
286
288
|
# monkey: george
|
287
289
|
#
|
290
|
+
# <code></code>
|
291
|
+
#
|
288
292
|
# ### in monkeys.yml
|
289
293
|
#
|
290
294
|
# george:
|
@@ -306,6 +310,8 @@ module ActiveRecord
|
|
306
310
|
#
|
307
311
|
# belongs_to :eater, polymorphic: true
|
308
312
|
#
|
313
|
+
# <code></code>
|
314
|
+
#
|
309
315
|
# ### in fruits.yml
|
310
316
|
#
|
311
317
|
# apple:
|
@@ -331,6 +337,8 @@ module ActiveRecord
|
|
331
337
|
# id: 1
|
332
338
|
# name: George the Monkey
|
333
339
|
#
|
340
|
+
# <code></code>
|
341
|
+
#
|
334
342
|
# ### in fruits.yml
|
335
343
|
#
|
336
344
|
# apple:
|
@@ -345,6 +353,8 @@ module ActiveRecord
|
|
345
353
|
# id: 3
|
346
354
|
# name: grape
|
347
355
|
#
|
356
|
+
# <code></code>
|
357
|
+
#
|
348
358
|
# ### in fruits_monkeys.yml
|
349
359
|
#
|
350
360
|
# apple_george:
|
@@ -368,6 +378,8 @@ module ActiveRecord
|
|
368
378
|
# name: George the Monkey
|
369
379
|
# fruits: apple, orange, grape
|
370
380
|
#
|
381
|
+
# <code></code>
|
382
|
+
#
|
371
383
|
# ### in fruits.yml
|
372
384
|
#
|
373
385
|
# apple:
|
@@ -467,6 +479,8 @@ module ActiveRecord
|
|
467
479
|
# belongs_to :author
|
468
480
|
# end
|
469
481
|
#
|
482
|
+
# <code></code>
|
483
|
+
#
|
470
484
|
# # books.yml
|
471
485
|
# alices_adventure_in_wonderland:
|
472
486
|
# author_id: <%= ActiveRecord::FixtureSet.identify(:lewis_carroll) %>
|
@@ -482,6 +496,8 @@ module ActiveRecord
|
|
482
496
|
# belongs_to :book, query_constraints: [:author_id, :book_id]
|
483
497
|
# end
|
484
498
|
#
|
499
|
+
# <code></code>
|
500
|
+
#
|
485
501
|
# # book_orders.yml
|
486
502
|
# alices_adventure_in_wonderland_in_books:
|
487
503
|
# author: lewis_carroll
|
@@ -23,8 +23,6 @@ module ActiveRecord
|
|
23
23
|
@keys = @inserts.first.keys
|
24
24
|
end
|
25
25
|
|
26
|
-
configure_on_duplicate_update_logic
|
27
|
-
|
28
26
|
if model.scope_attributes?
|
29
27
|
@scope_attributes = model.scope_attributes
|
30
28
|
@keys |= @scope_attributes.keys
|
@@ -35,8 +33,8 @@ module ActiveRecord
|
|
35
33
|
@returning = false if @returning == []
|
36
34
|
|
37
35
|
@unique_by = find_unique_index_for(@unique_by)
|
38
|
-
@on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
|
39
36
|
|
37
|
+
configure_on_duplicate_update_logic
|
40
38
|
ensure_valid_options_for_connection!
|
41
39
|
end
|
42
40
|
|
@@ -135,6 +133,8 @@ module ActiveRecord
|
|
135
133
|
elsif custom_update_sql_provided?
|
136
134
|
@update_sql = on_duplicate
|
137
135
|
@on_duplicate = :update
|
136
|
+
elsif @on_duplicate == :update && updatable_columns.empty?
|
137
|
+
@on_duplicate = :skip
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
@@ -10,7 +10,7 @@ module ActiveRecord
|
|
10
10
|
# This is enabled by default. To disable this functionality set
|
11
11
|
# `use_metadata_table` to false in your database configuration.
|
12
12
|
class InternalMetadata # :nodoc:
|
13
|
-
class NullInternalMetadata
|
13
|
+
class NullInternalMetadata # :nodoc:
|
14
14
|
end
|
15
15
|
|
16
16
|
attr_reader :connection, :arel_table
|
@@ -64,6 +64,8 @@ module ActiveRecord
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def create_table_and_set_flags(environment, schema_sha1 = nil)
|
67
|
+
return unless enabled?
|
68
|
+
|
67
69
|
create_table
|
68
70
|
update_or_create_entry(:environment, environment)
|
69
71
|
update_or_create_entry(:schema_sha1, schema_sha1) if schema_sha1
|
@@ -24,7 +24,7 @@ module ActiveRecord
|
|
24
24
|
# To use the DatabaseSelector in your application with default settings,
|
25
25
|
# run the provided generator.
|
26
26
|
#
|
27
|
-
# bin/rails g active_record:multi_db
|
27
|
+
# $ bin/rails g active_record:multi_db
|
28
28
|
#
|
29
29
|
# This will create a file named +config/initializers/multi_db.rb+ with the
|
30
30
|
# following contents:
|
@@ -206,7 +206,10 @@ module ActiveRecord
|
|
206
206
|
end
|
207
207
|
|
208
208
|
def invert_rename_table(args)
|
209
|
-
|
209
|
+
old_name, new_name, options = args
|
210
|
+
args = [new_name, old_name]
|
211
|
+
args << options if options
|
212
|
+
[:rename_table, args]
|
210
213
|
end
|
211
214
|
|
212
215
|
def invert_remove_column(args)
|
@@ -61,8 +61,10 @@ module ActiveRecord
|
|
61
61
|
column_name.is_a?(String) && /\W/.match?(column_name)
|
62
62
|
end
|
63
63
|
end
|
64
|
+
|
64
65
|
module TableDefinition
|
65
66
|
include LegacyIndexName
|
67
|
+
|
66
68
|
def column(name, type, **options)
|
67
69
|
options[:_skip_validate_options] = true
|
68
70
|
super
|
@@ -95,6 +97,12 @@ module ActiveRecord
|
|
95
97
|
super
|
96
98
|
end
|
97
99
|
|
100
|
+
def add_reference(table_name, ref_name, **options)
|
101
|
+
options[:_skip_validate_options] = true
|
102
|
+
super
|
103
|
+
end
|
104
|
+
alias :add_belongs_to :add_reference
|
105
|
+
|
98
106
|
def create_table(table_name, **options)
|
99
107
|
options[:_uses_legacy_table_name] = true
|
100
108
|
options[:_skip_validate_options] = true
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class PendingMigrationConnection # :nodoc:
|
5
|
+
def self.establish_temporary_connection(db_config, &block)
|
6
|
+
pool = ActiveRecord::Base.connection_handler.establish_connection(db_config, owner_name: self)
|
7
|
+
|
8
|
+
yield pool.connection
|
9
|
+
ensure
|
10
|
+
ActiveRecord::Base.connection_handler.remove_connection_pool(self.name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.primary_class?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.current_preventing_writes
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -7,6 +7,7 @@ require "active_support/core_ext/array/access"
|
|
7
7
|
require "active_support/core_ext/enumerable"
|
8
8
|
require "active_support/core_ext/module/attribute_accessors"
|
9
9
|
require "active_support/actionable_error"
|
10
|
+
require "active_record/migration/pending_migration_connection"
|
10
11
|
|
11
12
|
module ActiveRecord
|
12
13
|
class MigrationError < ActiveRecordError # :nodoc:
|
@@ -370,7 +371,8 @@ module ActiveRecord
|
|
370
371
|
# The \Rails package has several tools to help create and apply migrations.
|
371
372
|
#
|
372
373
|
# To generate a new migration, you can use
|
373
|
-
#
|
374
|
+
#
|
375
|
+
# $ bin/rails generate migration MyNewMigration
|
374
376
|
#
|
375
377
|
# where MyNewMigration is the name of your migration. The generator will
|
376
378
|
# create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
|
@@ -379,7 +381,7 @@ module ActiveRecord
|
|
379
381
|
#
|
380
382
|
# There is a special syntactic shortcut to generate migrations that add fields to a table.
|
381
383
|
#
|
382
|
-
# bin/rails generate migration add_fieldname_to_tablename fieldname:string
|
384
|
+
# $ bin/rails generate migration add_fieldname_to_tablename fieldname:string
|
383
385
|
#
|
384
386
|
# This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
|
385
387
|
# class AddFieldnameToTablename < ActiveRecord::Migration[7.1]
|
@@ -768,9 +770,11 @@ module ActiveRecord
|
|
768
770
|
def pending_migrations
|
769
771
|
pending_migrations = []
|
770
772
|
|
771
|
-
ActiveRecord::
|
772
|
-
|
773
|
-
|
773
|
+
ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
|
774
|
+
ActiveRecord::PendingMigrationConnection.establish_temporary_connection(db_config) do |conn|
|
775
|
+
if pending = conn.migration_context.open.pending_migrations
|
776
|
+
pending_migrations << pending
|
777
|
+
end
|
774
778
|
end
|
775
779
|
end
|
776
780
|
|
@@ -284,8 +284,10 @@ module ActiveRecord
|
|
284
284
|
|
285
285
|
# Computes the table name, (re)sets it internally, and returns it.
|
286
286
|
def reset_table_name # :nodoc:
|
287
|
-
self.table_name = if
|
288
|
-
|
287
|
+
self.table_name = if self == Base
|
288
|
+
nil
|
289
|
+
elsif abstract_class?
|
290
|
+
superclass.table_name
|
289
291
|
elsif superclass.abstract_class?
|
290
292
|
superclass.table_name || compute_table_name
|
291
293
|
else
|
@@ -467,7 +469,7 @@ module ActiveRecord
|
|
467
469
|
end
|
468
470
|
|
469
471
|
# Returns the column object for the named attribute.
|
470
|
-
# Returns an
|
472
|
+
# Returns an ActiveRecord::ConnectionAdapters::NullColumn if the
|
471
473
|
# named attribute does not exist.
|
472
474
|
#
|
473
475
|
# class Person < ActiveRecord::Base
|
@@ -627,8 +629,6 @@ module ActiveRecord
|
|
627
629
|
)
|
628
630
|
alias_attribute :id_value, :id if name == "id"
|
629
631
|
end
|
630
|
-
|
631
|
-
super
|
632
632
|
end
|
633
633
|
|
634
634
|
# Guesses the table name, but does not decorate it with prefix and suffix information.
|
@@ -283,13 +283,11 @@ module ActiveRecord
|
|
283
283
|
#
|
284
284
|
# === Creating forms with nested attributes
|
285
285
|
#
|
286
|
-
# Use ActionView::Helpers::FormHelper#fields_for to create form elements
|
287
|
-
#
|
286
|
+
# Use ActionView::Helpers::FormHelper#fields_for to create form elements for
|
287
|
+
# nested attributes.
|
288
288
|
#
|
289
|
-
#
|
290
|
-
#
|
291
|
-
# If you are using ActionView::Helpers::FormHelper#fields_for, your integration
|
292
|
-
# tests should replicate the HTML structure it provides. For example;
|
289
|
+
# Integration test params should reflect the structure of the form. For
|
290
|
+
# example:
|
293
291
|
#
|
294
292
|
# post members_path, params: {
|
295
293
|
# member: {
|
@@ -309,7 +307,7 @@ module ActiveRecord
|
|
309
307
|
# [:allow_destroy]
|
310
308
|
# If true, destroys any members from the attributes hash with a
|
311
309
|
# <tt>_destroy</tt> key and a value that evaluates to +true+
|
312
|
-
# (e.g. 1, '1', true, or 'true'). This option is
|
310
|
+
# (e.g. 1, '1', true, or 'true'). This option is false by default.
|
313
311
|
# [:reject_if]
|
314
312
|
# Allows you to specify a Proc or a Symbol pointing to a method
|
315
313
|
# that checks whether a record should be built for a certain attribute
|
@@ -334,11 +332,11 @@ module ActiveRecord
|
|
334
332
|
# nested attributes are going to be used when an associated record already
|
335
333
|
# exists. In general, an existing record may either be updated with the
|
336
334
|
# new set of attribute values or be replaced by a wholly new record
|
337
|
-
# containing those values. By default the +:update_only+ option is
|
335
|
+
# containing those values. By default the +:update_only+ option is false
|
338
336
|
# and the nested attributes are used to update the existing record only
|
339
337
|
# if they include the record's <tt>:id</tt> value. Otherwise a new
|
340
338
|
# record will be instantiated and used to replace the existing one.
|
341
|
-
# However if the +:update_only+ option is
|
339
|
+
# However if the +:update_only+ option is true, the nested attributes
|
342
340
|
# are used to update the record's attributes always, regardless of
|
343
341
|
# whether the <tt>:id</tt> is present. The option is ignored for collection
|
344
342
|
# associations.
|
@@ -49,6 +49,14 @@ module ActiveRecord # :nodoc:
|
|
49
49
|
# By default, the normalization will not be applied to +nil+ values. This
|
50
50
|
# behavior can be changed with the +:apply_to_nil+ option.
|
51
51
|
#
|
52
|
+
# Be aware that if your app was created before Rails 7.1, and your app
|
53
|
+
# marshals instances of the targeted model (for example, when caching),
|
54
|
+
# then you should set ActiveRecord.marshalling_format_version to +7.1+ or
|
55
|
+
# higher via either <tt>config.load_defaults 7.1</tt> or
|
56
|
+
# <tt>config.active_record.marshalling_format_version = 7.1</tt>.
|
57
|
+
# Otherwise, +Marshal+ may attempt to serialize the normalization +Proc+
|
58
|
+
# and raise +TypeError+.
|
59
|
+
#
|
52
60
|
# ==== Options
|
53
61
|
#
|
54
62
|
# * +:with+ - Any callable object that accepts the attribute's value as
|
@@ -115,7 +115,7 @@ module ActiveRecord
|
|
115
115
|
# ==== Options
|
116
116
|
#
|
117
117
|
# [:returning]
|
118
|
-
# (PostgreSQL only) An array of attributes to return for all successfully
|
118
|
+
# (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
|
119
119
|
# inserted records, which by default is the primary key.
|
120
120
|
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
121
121
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
@@ -205,7 +205,7 @@ module ActiveRecord
|
|
205
205
|
# ==== Options
|
206
206
|
#
|
207
207
|
# [:returning]
|
208
|
-
# (PostgreSQL only) An array of attributes to return for all successfully
|
208
|
+
# (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
|
209
209
|
# inserted records, which by default is the primary key.
|
210
210
|
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
211
211
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
@@ -271,7 +271,7 @@ module ActiveRecord
|
|
271
271
|
# ==== Options
|
272
272
|
#
|
273
273
|
# [:returning]
|
274
|
-
# (PostgreSQL only) An array of attributes to return for all successfully
|
274
|
+
# (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
|
275
275
|
# inserted records, which by default is the primary key.
|
276
276
|
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
277
277
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
@@ -1076,6 +1076,7 @@ module ActiveRecord
|
|
1076
1076
|
end
|
1077
1077
|
|
1078
1078
|
@association_cache = fresh_object.instance_variable_get(:@association_cache)
|
1079
|
+
@association_cache.each_value { |association| association.owner = self }
|
1079
1080
|
@attributes = fresh_object.instance_variable_get(:@attributes)
|
1080
1081
|
@new_record = false
|
1081
1082
|
@previously_new_record = false
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
# Returns a new +ActiveRecord::Promise+ that will apply the passed block
|
32
32
|
# when the value is accessed:
|
33
33
|
#
|
34
|
-
# Post.
|
34
|
+
# Post.async_pick(:title).then { |title| title.upcase }.value
|
35
35
|
# # => "POST TITLE"
|
36
36
|
def then(&block)
|
37
37
|
Promise.new(@future_result, @block ? @block >> block : block)
|
@@ -398,7 +398,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
398
398
|
end
|
399
399
|
|
400
400
|
ActiveSupport.on_load(:active_record_fixture_set) do
|
401
|
-
# Encrypt
|
401
|
+
# Encrypt Active Record fixtures
|
402
402
|
if ActiveRecord::Encryption.config.encrypt_fixtures
|
403
403
|
ActiveRecord::Fixture.prepend ActiveRecord::Encryption::EncryptedFixtures
|
404
404
|
end
|
@@ -37,9 +37,10 @@ module ActiveRecord
|
|
37
37
|
db_rt_before_render = ActiveRecord::RuntimeRegistry.reset
|
38
38
|
self.db_runtime = (db_runtime || 0) + db_rt_before_render
|
39
39
|
runtime = super
|
40
|
+
queries_rt = ActiveRecord::RuntimeRegistry.sql_runtime - ActiveRecord::RuntimeRegistry.async_sql_runtime
|
40
41
|
db_rt_after_render = ActiveRecord::RuntimeRegistry.reset
|
41
42
|
self.db_runtime += db_rt_after_render
|
42
|
-
runtime -
|
43
|
+
runtime - queries_rt
|
43
44
|
else
|
44
45
|
super
|
45
46
|
end
|
@@ -195,7 +195,7 @@ db_namespace = namespace :db do
|
|
195
195
|
|
196
196
|
namespace :up do
|
197
197
|
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
|
198
|
-
desc
|
198
|
+
desc "Run the \"up\" on #{name} database for a given migration VERSION."
|
199
199
|
task name => :load_config do
|
200
200
|
raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
|
201
201
|
|
@@ -204,7 +204,7 @@ db_namespace = namespace :db do
|
|
204
204
|
conn.migration_context.run(:up, ActiveRecord::Tasks::DatabaseTasks.target_version)
|
205
205
|
end
|
206
206
|
|
207
|
-
db_namespace["_dump"].invoke
|
207
|
+
db_namespace["_dump:#{name}"].invoke
|
208
208
|
end
|
209
209
|
end
|
210
210
|
end
|
@@ -226,7 +226,7 @@ db_namespace = namespace :db do
|
|
226
226
|
|
227
227
|
namespace :down do
|
228
228
|
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
|
229
|
-
desc
|
229
|
+
desc "Run the \"down\" on #{name} database for a given migration VERSION."
|
230
230
|
task name => :load_config do
|
231
231
|
raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
|
232
232
|
|
@@ -235,7 +235,7 @@ db_namespace = namespace :db do
|
|
235
235
|
conn.migration_context.run(:down, ActiveRecord::Tasks::DatabaseTasks.target_version)
|
236
236
|
end
|
237
237
|
|
238
|
-
db_namespace["_dump"].invoke
|
238
|
+
db_namespace["_dump:#{name}"].invoke
|
239
239
|
end
|
240
240
|
end
|
241
241
|
end
|
@@ -269,7 +269,7 @@ db_namespace = namespace :db do
|
|
269
269
|
conn.migration_context.rollback(step)
|
270
270
|
end
|
271
271
|
|
272
|
-
db_namespace["_dump"].invoke
|
272
|
+
db_namespace["_dump:#{name}"].invoke
|
273
273
|
end
|
274
274
|
end
|
275
275
|
end
|
@@ -382,6 +382,7 @@ module ActiveRecord
|
|
382
382
|
@klass = options[:anonymous_class]
|
383
383
|
@plural_name = active_record.pluralize_table_names ?
|
384
384
|
name.to_s.pluralize : name.to_s
|
385
|
+
validate_reflection!
|
385
386
|
end
|
386
387
|
|
387
388
|
def autosave=(autosave)
|
@@ -433,6 +434,17 @@ module ActiveRecord
|
|
433
434
|
def derive_class_name
|
434
435
|
name.to_s.camelize
|
435
436
|
end
|
437
|
+
|
438
|
+
def validate_reflection!
|
439
|
+
return unless options[:foreign_key].is_a?(Array)
|
440
|
+
|
441
|
+
message = <<~MSG.squish
|
442
|
+
Passing #{options[:foreign_key]} array to :foreign_key option
|
443
|
+
on the #{active_record}##{name} association is not supported.
|
444
|
+
Use the query_constraints: #{options[:foreign_key]} option instead to represent a composite foreign key.
|
445
|
+
MSG
|
446
|
+
raise ArgumentError, message
|
447
|
+
end
|
436
448
|
end
|
437
449
|
|
438
450
|
# Holds all the metadata about an aggregation as it was specified in the
|
@@ -858,7 +870,7 @@ module ActiveRecord
|
|
858
870
|
def association_primary_key(klass = nil)
|
859
871
|
if primary_key = options[:primary_key]
|
860
872
|
@association_primary_key ||= -primary_key.to_s
|
861
|
-
elsif
|
873
|
+
elsif (klass || self.klass).has_query_constraints? || options[:query_constraints]
|
862
874
|
(klass || self.klass).composite_query_constraints_list
|
863
875
|
elsif (klass || self.klass).composite_primary_key?
|
864
876
|
# If klass has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
@@ -81,6 +81,16 @@ module ActiveRecord
|
|
81
81
|
#
|
82
82
|
# Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
|
83
83
|
# between databases. In invalid cases, an error from the database is thrown.
|
84
|
+
#
|
85
|
+
# When given a block, loads all records in the relation, if the relation
|
86
|
+
# hasn't been loaded yet. Calls the block with each record in the relation.
|
87
|
+
# Returns the number of records for which the block returns a truthy value.
|
88
|
+
#
|
89
|
+
# Person.count { |person| person.age > 21 }
|
90
|
+
# # => counts the number of people older that 21
|
91
|
+
#
|
92
|
+
# Note: If there are a lot of records in the relation, loading all records
|
93
|
+
# could result in performance issues.
|
84
94
|
def count(column_name = nil)
|
85
95
|
if block_given?
|
86
96
|
unless column_name.nil?
|
@@ -93,7 +103,8 @@ module ActiveRecord
|
|
93
103
|
end
|
94
104
|
end
|
95
105
|
|
96
|
-
# Same as
|
106
|
+
# Same as #count, but performs the query asynchronously and returns an
|
107
|
+
# ActiveRecord::Promise.
|
97
108
|
def async_count(column_name = nil)
|
98
109
|
async.count(column_name)
|
99
110
|
end
|
@@ -106,7 +117,8 @@ module ActiveRecord
|
|
106
117
|
calculate(:average, column_name)
|
107
118
|
end
|
108
119
|
|
109
|
-
# Same as
|
120
|
+
# Same as #average, but performs the query asynchronously and returns an
|
121
|
+
# ActiveRecord::Promise.
|
110
122
|
def async_average(column_name)
|
111
123
|
async.average(column_name)
|
112
124
|
end
|
@@ -120,7 +132,8 @@ module ActiveRecord
|
|
120
132
|
calculate(:minimum, column_name)
|
121
133
|
end
|
122
134
|
|
123
|
-
# Same as
|
135
|
+
# Same as #minimum, but performs the query asynchronously and returns an
|
136
|
+
# ActiveRecord::Promise.
|
124
137
|
def async_minimum(column_name)
|
125
138
|
async.minimum(column_name)
|
126
139
|
end
|
@@ -134,7 +147,8 @@ module ActiveRecord
|
|
134
147
|
calculate(:maximum, column_name)
|
135
148
|
end
|
136
149
|
|
137
|
-
# Same as
|
150
|
+
# Same as #maximum, but performs the query asynchronously and returns an
|
151
|
+
# ActiveRecord::Promise.
|
138
152
|
def async_maximum(column_name)
|
139
153
|
async.maximum(column_name)
|
140
154
|
end
|
@@ -144,6 +158,17 @@ module ActiveRecord
|
|
144
158
|
# #calculate for examples with options.
|
145
159
|
#
|
146
160
|
# Person.sum(:age) # => 4562
|
161
|
+
#
|
162
|
+
# When given a block, loads all records in the relation, if the relation
|
163
|
+
# hasn't been loaded yet. Calls the block with each record in the relation.
|
164
|
+
# Returns the sum of +initial_value_or_column+ and the block return
|
165
|
+
# values:
|
166
|
+
#
|
167
|
+
# Person.sum { |person| person.age } # => 4562
|
168
|
+
# Person.sum(1000) { |person| person.age } # => 5562
|
169
|
+
#
|
170
|
+
# Note: If there are a lot of records in the relation, loading all records
|
171
|
+
# could result in performance issues.
|
147
172
|
def sum(initial_value_or_column = 0, &block)
|
148
173
|
if block_given?
|
149
174
|
map(&block).sum(initial_value_or_column)
|
@@ -152,7 +177,8 @@ module ActiveRecord
|
|
152
177
|
end
|
153
178
|
end
|
154
179
|
|
155
|
-
# Same as
|
180
|
+
# Same as #sum, but performs the query asynchronously and returns an
|
181
|
+
# ActiveRecord::Promise.
|
156
182
|
def async_sum(identity_or_column = nil)
|
157
183
|
async.sum(identity_or_column)
|
158
184
|
end
|
@@ -255,7 +281,13 @@ module ActiveRecord
|
|
255
281
|
#
|
256
282
|
# See also #ids.
|
257
283
|
def pluck(*column_names)
|
258
|
-
|
284
|
+
if @none
|
285
|
+
if @async
|
286
|
+
return Promise::Complete.new([])
|
287
|
+
else
|
288
|
+
return []
|
289
|
+
end
|
290
|
+
end
|
259
291
|
|
260
292
|
if loaded? && all_attributes?(column_names)
|
261
293
|
result = records.pluck(*column_names)
|
@@ -287,7 +319,8 @@ module ActiveRecord
|
|
287
319
|
end
|
288
320
|
end
|
289
321
|
|
290
|
-
# Same as
|
322
|
+
# Same as #pluck, but performs the query asynchronously and returns an
|
323
|
+
# ActiveRecord::Promise.
|
291
324
|
def async_pluck(*column_names)
|
292
325
|
async.pluck(*column_names)
|
293
326
|
end
|
@@ -315,7 +348,8 @@ module ActiveRecord
|
|
315
348
|
limit(1).pluck(*column_names).then(&:first)
|
316
349
|
end
|
317
350
|
|
318
|
-
# Same as
|
351
|
+
# Same as #pick, but performs the query asynchronously and returns an
|
352
|
+
# ActiveRecord::Promise.
|
319
353
|
def async_pick(*column_names)
|
320
354
|
async.pick(*column_names)
|
321
355
|
end
|
@@ -358,7 +392,8 @@ module ActiveRecord
|
|
358
392
|
result.then { |result| type_cast_pluck_values(result, columns) }
|
359
393
|
end
|
360
394
|
|
361
|
-
# Same as
|
395
|
+
# Same as #ids, but performs the query asynchronously and returns an
|
396
|
+
# ActiveRecord::Promise.
|
362
397
|
def async_ids
|
363
398
|
async.ids
|
364
399
|
end
|
@@ -102,7 +102,7 @@ module ActiveRecord
|
|
102
102
|
:to_sentence, :to_fs, :to_formatted_s, :as_json,
|
103
103
|
:shuffle, :split, :slice, :index, :rindex, to: :records
|
104
104
|
|
105
|
-
delegate :primary_key, :connection, to: :klass
|
105
|
+
delegate :primary_key, :connection, :transaction, to: :klass
|
106
106
|
|
107
107
|
module ClassSpecificRelation # :nodoc:
|
108
108
|
extend ActiveSupport::Concern
|
@@ -588,7 +588,7 @@ module ActiveRecord
|
|
588
588
|
# User.order(Arel.sql('end_date - start_date'))
|
589
589
|
# # SELECT "users".* FROM "users" ORDER BY end_date - start_date
|
590
590
|
#
|
591
|
-
# Custom query syntax, like JSON columns for
|
591
|
+
# Custom query syntax, like JSON columns for PostgreSQL, is supported in this way.
|
592
592
|
#
|
593
593
|
# User.order(Arel.sql("payload->>'kind'"))
|
594
594
|
# # SELECT "users".* FROM "users" ORDER BY payload->>'kind'
|