activerecord 7.1.0 → 7.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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'
|