activerecord 7.1.2 → 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 +99 -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 +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +11 -8
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- 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 +37 -4
- 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/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/middleware/database_selector.rb +1 -1
- data/lib/active_record/migration/compatibility.rb +8 -0
- data/lib/active_record/migration.rb +3 -2
- data/lib/active_record/persistence.rb +4 -3
- data/lib/active_record/railties/controller_runtime.rb +2 -1
- data/lib/active_record/reflection.rb +1 -1
- data/lib/active_record/relation/calculations.rb +28 -1
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation.rb +18 -3
- data/lib/active_record/runtime_registry.rb +15 -1
- data/lib/active_record/secure_token.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +5 -5
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f7c51f040979b7650eb209b0dc856a0877bccc968dd99cde06e3cf1776dd638
|
4
|
+
data.tar.gz: b81eca56b185f087c95b6691b307ff1c9c63d0d03ff650b729e943888f053152
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f68dc9e6cdccf23f5467810a68083564760601aa3c82b4c85720cfb2c39500439a86bb63c2ce8fea37337de6fc4f157722300c691cfd239e92732569b5c20f6
|
7
|
+
data.tar.gz: 6f8382df74c2c16b873cab0effeda0a3ef3e578d5087e435d7d2f8c6890dcf0d85e2211f2bc890182af49e836b09e4e345e2b9bc37eb8472814e98bb6636b005
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,102 @@
|
|
1
|
+
## Rails 7.1.3 (January 16, 2024) ##
|
2
|
+
|
3
|
+
* Fix Migrations with versions older than 7.1 validating options given to
|
4
|
+
`add_reference`.
|
5
|
+
|
6
|
+
*Hartley McGuire*
|
7
|
+
|
8
|
+
* Ensure `reload` sets correct owner for each association.
|
9
|
+
|
10
|
+
*Dmytro Savochkin*
|
11
|
+
|
12
|
+
* Fix view runtime for controllers with async queries.
|
13
|
+
|
14
|
+
*fatkodima*
|
15
|
+
|
16
|
+
* Fix `load_async` to work with query cache.
|
17
|
+
|
18
|
+
*fatkodima*
|
19
|
+
|
20
|
+
* Fix polymorphic `belongs_to` to correctly use parent's `query_constraints`.
|
21
|
+
|
22
|
+
*fatkodima*
|
23
|
+
|
24
|
+
* Fix `Preloader` to not generate a query for already loaded association with `query_constraints`.
|
25
|
+
|
26
|
+
*fatkodima*
|
27
|
+
|
28
|
+
* Fix multi-database polymorphic preloading with equivalent table names.
|
29
|
+
|
30
|
+
When preloading polymorphic associations, if two models pointed to two
|
31
|
+
tables with the same name but located in different databases, the
|
32
|
+
preloader would only load one.
|
33
|
+
|
34
|
+
*Ari Summer*
|
35
|
+
|
36
|
+
* Fix `encrypted_attribute?` to take into account context properties passed to `encrypts`.
|
37
|
+
|
38
|
+
*Maxime Réty*
|
39
|
+
|
40
|
+
* Fix `find_by` to work correctly in presence of composite primary keys.
|
41
|
+
|
42
|
+
*fatkodima*
|
43
|
+
|
44
|
+
* Fix async queries sometimes returning a raw result if they hit the query cache.
|
45
|
+
|
46
|
+
`ShipPart.async_count` could return a raw integer rather than a Promise
|
47
|
+
if it found the result in the query cache.
|
48
|
+
|
49
|
+
*fatkodima*
|
50
|
+
|
51
|
+
* Fix `Relation#transaction` to not apply a default scope.
|
52
|
+
|
53
|
+
The method was incorrectly setting a default scope around its block:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
Post.where(published: true).transaction do
|
57
|
+
Post.count # SELECT COUNT(*) FROM posts WHERE published = FALSE;
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
*Jean Boussier*
|
62
|
+
|
63
|
+
* Fix calling `async_pluck` on a `none` relation.
|
64
|
+
|
65
|
+
`Model.none.async_pluck(:id)` was returning a naked value
|
66
|
+
instead of a promise.
|
67
|
+
|
68
|
+
*Jean Boussier*
|
69
|
+
|
70
|
+
* Fix calling `load_async` on a `none` relation.
|
71
|
+
|
72
|
+
`Model.none.load_async` was returning a broken result.
|
73
|
+
|
74
|
+
*Lucas Mazza*
|
75
|
+
|
76
|
+
* TrilogyAdapter: ignore `host` if `socket` parameter is set.
|
77
|
+
|
78
|
+
This allows to configure a connection on a UNIX socket via DATABASE_URL:
|
79
|
+
|
80
|
+
```
|
81
|
+
DATABASE_URL=trilogy://does-not-matter/my_db_production?socket=/var/run/mysql.sock
|
82
|
+
```
|
83
|
+
|
84
|
+
*Jean Boussier*
|
85
|
+
|
86
|
+
* Fix `has_secure_token` calls the setter method on initialize.
|
87
|
+
|
88
|
+
*Abeid Ahmed*
|
89
|
+
|
90
|
+
* Allow using `object_id` as a database column name.
|
91
|
+
It was available before rails 7.1 and may be used as a part of polymorphic relationship to `object` where `object` can be any other database record.
|
92
|
+
|
93
|
+
*Mikhail Doronin*
|
94
|
+
|
95
|
+
* Fix `rails db:create:all` to not touch databases before they are created.
|
96
|
+
|
97
|
+
*fatkodima*
|
98
|
+
|
99
|
+
|
1
100
|
## Rails 7.1.2 (November 10, 2023) ##
|
2
101
|
|
3
102
|
* Fix renaming primary key index when renaming a table with a UUID primary key
|
@@ -33,7 +33,8 @@ module ActiveRecord
|
|
33
33
|
# <tt>owner</tt>, the collection of its posts as <tt>target</tt>, and
|
34
34
|
# the <tt>reflection</tt> object represents a <tt>:has_many</tt> macro.
|
35
35
|
class Association # :nodoc:
|
36
|
-
|
36
|
+
attr_accessor :owner
|
37
|
+
attr_reader :target, :reflection, :disable_joins
|
37
38
|
|
38
39
|
delegate :options, to: :reflection
|
39
40
|
|
@@ -17,11 +17,12 @@ module ActiveRecord
|
|
17
17
|
def eql?(other)
|
18
18
|
association_key_name == other.association_key_name &&
|
19
19
|
scope.table_name == other.scope.table_name &&
|
20
|
+
scope.connection_specification_name == other.scope.connection_specification_name &&
|
20
21
|
scope.values_for_queries == other.scope.values_for_queries
|
21
22
|
end
|
22
23
|
|
23
24
|
def hash
|
24
|
-
[association_key_name, scope.table_name, scope.values_for_queries].hash
|
25
|
+
[association_key_name, scope.table_name, scope.connection_specification_name, scope.values_for_queries].hash
|
25
26
|
end
|
26
27
|
|
27
28
|
def records_for(loaders)
|
@@ -38,6 +39,8 @@ module ActiveRecord
|
|
38
39
|
end
|
39
40
|
|
40
41
|
def load_records_for_keys(keys, &block)
|
42
|
+
return [] if keys.empty?
|
43
|
+
|
41
44
|
if association_key_name.is_a?(Array)
|
42
45
|
query_constraints = Hash.new { |hsh, key| hsh[key] = Set.new }
|
43
46
|
|
@@ -1050,7 +1050,7 @@ module ActiveRecord
|
|
1050
1050
|
# query per addressable type.
|
1051
1051
|
# For example, if all the addressables are either of class Person or Company, then a total
|
1052
1052
|
# of 3 queries will be executed. The list of addressable types to load is determined on
|
1053
|
-
# the back of the addresses loaded. This is not supported if Active Record has to
|
1053
|
+
# the back of the addresses loaded. This is not supported if Active Record has to fall back
|
1054
1054
|
# to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError.
|
1055
1055
|
# The reason is that the parent model's type is a column value so its corresponding table
|
1056
1056
|
# name cannot be put in the +FROM+/+JOIN+ clauses of that query.
|
@@ -34,7 +34,7 @@ module ActiveRecord
|
|
34
34
|
Base.private_instance_methods -
|
35
35
|
Base.superclass.instance_methods -
|
36
36
|
Base.superclass.private_instance_methods +
|
37
|
-
%i[__id__ dup freeze frozen? hash
|
37
|
+
%i[__id__ dup freeze frozen? hash class clone]
|
38
38
|
).map { |m| -m.to_s }.to_set.freeze
|
39
39
|
end
|
40
40
|
end
|
@@ -58,12 +58,10 @@ module ActiveRecord
|
|
58
58
|
# Connections can be obtained and used from a connection pool in several
|
59
59
|
# ways:
|
60
60
|
#
|
61
|
-
# 1. Simply use {ActiveRecord::Base.connection}[rdoc-ref:ConnectionHandling.connection]
|
62
|
-
#
|
63
|
-
# earlier (pre-connection-pooling). Eventually, when you're done with
|
64
|
-
# the connection(s) and wish it to be returned to the pool, you call
|
61
|
+
# 1. Simply use {ActiveRecord::Base.connection}[rdoc-ref:ConnectionHandling.connection].
|
62
|
+
# When you're done with the connection(s) and wish it to be returned to the pool, you call
|
65
63
|
# {ActiveRecord::Base.connection_handler.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
|
66
|
-
# This
|
64
|
+
# This is the default behavior for Active Record when used in conjunction with
|
67
65
|
# Action Pack's request handling cycle.
|
68
66
|
# 2. Manually check out a connection from the pool with
|
69
67
|
# {ActiveRecord::Base.connection_pool.checkout}[rdoc-ref:#checkout]. You are responsible for
|
@@ -465,8 +463,7 @@ module ActiveRecord
|
|
465
463
|
@available.num_waiting
|
466
464
|
end
|
467
465
|
|
468
|
-
#
|
469
|
-
# Example:
|
466
|
+
# Returns the connection pool's usage statistic.
|
470
467
|
#
|
471
468
|
# ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
|
472
469
|
def stat
|
@@ -658,7 +655,13 @@ module ActiveRecord
|
|
658
655
|
conn
|
659
656
|
else
|
660
657
|
reap
|
661
|
-
|
658
|
+
# Retry after reaping, which may return an available connection,
|
659
|
+
# remove an inactive connection, or both
|
660
|
+
if conn = @available.poll || try_to_checkout_new_connection
|
661
|
+
conn
|
662
|
+
else
|
663
|
+
@available.poll(checkout_timeout)
|
664
|
+
end
|
662
665
|
end
|
663
666
|
end
|
664
667
|
|
@@ -106,7 +106,8 @@ module ActiveRecord
|
|
106
106
|
sql, binds, preparable = to_sql_and_binds(arel, binds, preparable)
|
107
107
|
|
108
108
|
if async
|
109
|
-
lookup_sql_cache(sql, name, binds) || super(sql, name, binds, preparable: preparable, async: async)
|
109
|
+
result = lookup_sql_cache(sql, name, binds) || super(sql, name, binds, preparable: preparable, async: async)
|
110
|
+
FutureResult::Complete.new(result)
|
110
111
|
else
|
111
112
|
cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable, async: async) }
|
112
113
|
end
|
@@ -57,7 +57,7 @@ module ActiveRecord
|
|
57
57
|
def new_client(config)
|
58
58
|
config[:ssl_mode] = parse_ssl_mode(config[:ssl_mode]) if config[:ssl_mode]
|
59
59
|
::Trilogy.new(config)
|
60
|
-
rescue ::Trilogy::
|
60
|
+
rescue ::Trilogy::Error => error
|
61
61
|
raise translate_connect_error(config, error)
|
62
62
|
end
|
63
63
|
|
@@ -99,6 +99,14 @@ module ActiveRecord
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
+
def initialize(...)
|
103
|
+
super
|
104
|
+
|
105
|
+
# Trilogy ignore `socket` if `host is set. We want the opposite to allow
|
106
|
+
# configuring UNIX domain sockets via `DATABASE_URL`.
|
107
|
+
@config.delete(:host) if @config[:socket]
|
108
|
+
end
|
109
|
+
|
102
110
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
103
111
|
|
104
112
|
def supports_json?
|
@@ -256,7 +256,7 @@ module ActiveRecord
|
|
256
256
|
|
257
257
|
attr_writer :connection_specification_name
|
258
258
|
|
259
|
-
#
|
259
|
+
# Returns the connection specification name from the current class or its parent.
|
260
260
|
def connection_specification_name
|
261
261
|
if !defined?(@connection_specification_name) || @connection_specification_name.nil?
|
262
262
|
return self == Base ? Base.name : superclass.connection_specification_name
|
data/lib/active_record/core.rb
CHANGED
@@ -272,10 +272,25 @@ module ActiveRecord
|
|
272
272
|
elsif reflection.belongs_to? && !reflection.polymorphic?
|
273
273
|
key = reflection.join_foreign_key
|
274
274
|
pkey = reflection.join_primary_key
|
275
|
-
|
275
|
+
|
276
|
+
if pkey.is_a?(Array)
|
277
|
+
if pkey.all? { |attribute| value.respond_to?(attribute) }
|
278
|
+
value = pkey.map do |attribute|
|
279
|
+
if attribute == "id"
|
280
|
+
value.id_value
|
281
|
+
else
|
282
|
+
value.public_send(attribute)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
composite_primary_key = true
|
286
|
+
end
|
287
|
+
else
|
288
|
+
value = value.public_send(pkey) if value.respond_to?(pkey)
|
289
|
+
end
|
276
290
|
end
|
277
291
|
|
278
|
-
if !
|
292
|
+
if !composite_primary_key &&
|
293
|
+
(!columns_hash.key?(key) || StatementCache.unsupported_value?(value))
|
279
294
|
return super
|
280
295
|
end
|
281
296
|
|
@@ -402,12 +417,18 @@ module ActiveRecord
|
|
402
417
|
|
403
418
|
def cached_find_by(keys, values)
|
404
419
|
statement = cached_find_by_statement(keys) { |params|
|
405
|
-
wheres = keys.index_with
|
420
|
+
wheres = keys.index_with do |key|
|
421
|
+
if key.is_a?(Array)
|
422
|
+
[key.map { params.bind }]
|
423
|
+
else
|
424
|
+
params.bind
|
425
|
+
end
|
426
|
+
end
|
406
427
|
where(wheres).limit(1)
|
407
428
|
}
|
408
429
|
|
409
430
|
begin
|
410
|
-
statement.execute(values, connection).first
|
431
|
+
statement.execute(values.flatten, connection).first
|
411
432
|
rescue TypeError
|
412
433
|
raise ActiveRecord::StatementInvalid
|
413
434
|
end
|
@@ -545,6 +566,10 @@ module ActiveRecord
|
|
545
566
|
# Returns a hash of the given methods with their names as keys and returned
|
546
567
|
# values as values.
|
547
568
|
#
|
569
|
+
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
570
|
+
# topic.slice(:title, :author_name)
|
571
|
+
# => { "title" => "Budget", "author_name" => "Jason" }
|
572
|
+
#
|
548
573
|
#--
|
549
574
|
# Implemented by ActiveModel::Access#slice.
|
550
575
|
|
@@ -555,6 +580,10 @@ module ActiveRecord
|
|
555
580
|
#
|
556
581
|
# Returns an array of the values returned by the given methods.
|
557
582
|
#
|
583
|
+
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
584
|
+
# topic.values_at(:title, :author_name)
|
585
|
+
# => ["Budget", "Jason"]
|
586
|
+
#
|
558
587
|
#--
|
559
588
|
# Implemented by ActiveModel::Access#values_at.
|
560
589
|
|
@@ -673,6 +702,10 @@ module ActiveRecord
|
|
673
702
|
end
|
674
703
|
|
675
704
|
# Marks this record as read only.
|
705
|
+
#
|
706
|
+
# customer = Customer.first
|
707
|
+
# customer.readonly!
|
708
|
+
# customer.save # Raises an ActiveRecord::ReadOnlyRecord
|
676
709
|
def readonly!
|
677
710
|
@readonly = true
|
678
711
|
end
|
@@ -138,7 +138,7 @@ module ActiveRecord
|
|
138
138
|
#
|
139
139
|
# Now you can list a bunch of entries, call <tt>Entry#title</tt>, and polymorphism will provide you with the answer.
|
140
140
|
#
|
141
|
-
# == Nested Attributes
|
141
|
+
# == Nested \Attributes
|
142
142
|
#
|
143
143
|
# Enabling nested attributes on a delegated_type association allows you to
|
144
144
|
# create the entry and message in one go:
|
@@ -144,7 +144,13 @@ module ActiveRecord
|
|
144
144
|
|
145
145
|
# Returns whether a given attribute is encrypted or not.
|
146
146
|
def encrypted_attribute?(attribute_name)
|
147
|
-
|
147
|
+
name = attribute_name.to_s
|
148
|
+
name = self.class.attribute_aliases[name] || name
|
149
|
+
|
150
|
+
return false unless self.class.encrypted_attributes&.include? name.to_sym
|
151
|
+
|
152
|
+
type = type_for_attribute(name)
|
153
|
+
type.encrypted? read_attribute_before_type_cast(name)
|
148
154
|
end
|
149
155
|
|
150
156
|
# Returns the ciphertext for +attribute_name+.
|
@@ -44,6 +44,10 @@ module ActiveRecord
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
def encrypted?(value)
|
48
|
+
with_context { encryptor.encrypted? value }
|
49
|
+
end
|
50
|
+
|
47
51
|
def changed_in_place?(raw_old_value, new_value)
|
48
52
|
old_value = raw_old_value.nil? ? nil : deserialize(raw_old_value)
|
49
53
|
old_value != new_value
|
data/lib/active_record/errors.rb
CHANGED
@@ -318,14 +318,15 @@ module ActiveRecord
|
|
318
318
|
class << self
|
319
319
|
def db_error(db_name)
|
320
320
|
NoDatabaseError.new(<<~MSG)
|
321
|
-
We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml
|
321
|
+
We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml.
|
322
322
|
|
323
323
|
To resolve this error:
|
324
324
|
|
325
|
-
- Did you create the database
|
326
|
-
- Has the database name changed? Check your database.yml config has the correct database name.
|
325
|
+
- Did you not create the database, or did you delete it? To create the database, run:
|
327
326
|
|
328
|
-
|
327
|
+
bin/rails db:create
|
328
|
+
|
329
|
+
- Has the database name changed? Verify that config/database.yml contains the correct database name.
|
329
330
|
MSG
|
330
331
|
end
|
331
332
|
end
|
@@ -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
|
@@ -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:
|
@@ -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
|
@@ -371,7 +371,8 @@ module ActiveRecord
|
|
371
371
|
# The \Rails package has several tools to help create and apply migrations.
|
372
372
|
#
|
373
373
|
# To generate a new migration, you can use
|
374
|
-
#
|
374
|
+
#
|
375
|
+
# $ bin/rails generate migration MyNewMigration
|
375
376
|
#
|
376
377
|
# where MyNewMigration is the name of your migration. The generator will
|
377
378
|
# create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
|
@@ -380,7 +381,7 @@ module ActiveRecord
|
|
380
381
|
#
|
381
382
|
# There is a special syntactic shortcut to generate migrations that add fields to a table.
|
382
383
|
#
|
383
|
-
# bin/rails generate migration add_fieldname_to_tablename fieldname:string
|
384
|
+
# $ bin/rails generate migration add_fieldname_to_tablename fieldname:string
|
384
385
|
#
|
385
386
|
# This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
|
386
387
|
# class AddFieldnameToTablename < ActiveRecord::Migration[7.1]
|
@@ -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
|
@@ -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
|
@@ -870,7 +870,7 @@ module ActiveRecord
|
|
870
870
|
def association_primary_key(klass = nil)
|
871
871
|
if primary_key = options[:primary_key]
|
872
872
|
@association_primary_key ||= -primary_key.to_s
|
873
|
-
elsif
|
873
|
+
elsif (klass || self.klass).has_query_constraints? || options[:query_constraints]
|
874
874
|
(klass || self.klass).composite_query_constraints_list
|
875
875
|
elsif (klass || self.klass).composite_primary_key?
|
876
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?
|
@@ -148,6 +158,17 @@ module ActiveRecord
|
|
148
158
|
# #calculate for examples with options.
|
149
159
|
#
|
150
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.
|
151
172
|
def sum(initial_value_or_column = 0, &block)
|
152
173
|
if block_given?
|
153
174
|
map(&block).sum(initial_value_or_column)
|
@@ -260,7 +281,13 @@ module ActiveRecord
|
|
260
281
|
#
|
261
282
|
# See also #ids.
|
262
283
|
def pluck(*column_names)
|
263
|
-
|
284
|
+
if @none
|
285
|
+
if @async
|
286
|
+
return Promise::Complete.new([])
|
287
|
+
else
|
288
|
+
return []
|
289
|
+
end
|
290
|
+
end
|
264
291
|
|
265
292
|
if loaded? && all_attributes?(column_names)
|
266
293
|
result = records.pluck(*column_names)
|
@@ -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
|
@@ -164,8 +164,8 @@ module ActiveRecord
|
|
164
164
|
#
|
165
165
|
# If creation failed because of a unique constraint, this method will
|
166
166
|
# assume it encountered a race condition and will try finding the record
|
167
|
-
# once more If somehow the second find still find
|
168
|
-
# concurrent DELETE happened, it will then raise an
|
167
|
+
# once more. If somehow the second find still does not find a record
|
168
|
+
# because a concurrent DELETE happened, it will then raise an
|
169
169
|
# ActiveRecord::RecordNotFound exception.
|
170
170
|
#
|
171
171
|
# Please note <b>this method is not atomic</b>, it runs first a SELECT,
|
@@ -291,6 +291,11 @@ module ActiveRecord
|
|
291
291
|
end
|
292
292
|
|
293
293
|
# Returns true if there are no records.
|
294
|
+
#
|
295
|
+
# When a pattern argument is given, this method checks whether elements in
|
296
|
+
# the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
|
297
|
+
#
|
298
|
+
# posts.none?(Comment) # => true or false
|
294
299
|
def none?(*args)
|
295
300
|
return true if @none
|
296
301
|
|
@@ -299,6 +304,11 @@ module ActiveRecord
|
|
299
304
|
end
|
300
305
|
|
301
306
|
# Returns true if there are any records.
|
307
|
+
#
|
308
|
+
# When a pattern argument is given, this method checks whether elements in
|
309
|
+
# the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
|
310
|
+
#
|
311
|
+
# posts.any?(Post) # => true or false
|
302
312
|
def any?(*args)
|
303
313
|
return false if @none
|
304
314
|
|
@@ -307,6 +317,11 @@ module ActiveRecord
|
|
307
317
|
end
|
308
318
|
|
309
319
|
# Returns true if there is exactly one record.
|
320
|
+
#
|
321
|
+
# When a pattern argument is given, this method checks whether elements in
|
322
|
+
# the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
|
323
|
+
#
|
324
|
+
# posts.one?(Post) # => true or false
|
310
325
|
def one?(*args)
|
311
326
|
return false if @none
|
312
327
|
|
@@ -960,7 +975,7 @@ module ActiveRecord
|
|
960
975
|
def exec_main_query(async: false)
|
961
976
|
if @none
|
962
977
|
if async
|
963
|
-
return
|
978
|
+
return FutureResult::Complete.new([])
|
964
979
|
else
|
965
980
|
return []
|
966
981
|
end
|
@@ -17,13 +17,27 @@ module ActiveRecord
|
|
17
17
|
ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] = runtime
|
18
18
|
end
|
19
19
|
|
20
|
+
def async_sql_runtime
|
21
|
+
ActiveSupport::IsolatedExecutionState[:active_record_async_sql_runtime] ||= 0.0
|
22
|
+
end
|
23
|
+
|
24
|
+
def async_sql_runtime=(runtime)
|
25
|
+
ActiveSupport::IsolatedExecutionState[:active_record_async_sql_runtime] = runtime
|
26
|
+
end
|
27
|
+
|
20
28
|
def reset
|
21
29
|
rt, self.sql_runtime = sql_runtime, 0.0
|
30
|
+
self.async_sql_runtime = 0.0
|
22
31
|
rt
|
23
32
|
end
|
24
33
|
end
|
25
34
|
end
|
26
35
|
|
27
36
|
ActiveSupport::Notifications.monotonic_subscribe("sql.active_record") do |name, start, finish, id, payload|
|
28
|
-
|
37
|
+
runtime = (finish - start) * 1_000.0
|
38
|
+
|
39
|
+
if payload[:async]
|
40
|
+
ActiveRecord::RuntimeRegistry.async_sql_runtime += (runtime - payload[:lock_wait])
|
41
|
+
end
|
42
|
+
ActiveRecord::RuntimeRegistry.sql_runtime += runtime
|
29
43
|
end
|
@@ -53,7 +53,7 @@ module ActiveRecord
|
|
53
53
|
define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token(length: length) }
|
54
54
|
set_callback on, on == :initialize ? :after : :before do
|
55
55
|
if new_record? && !query_attribute(attribute)
|
56
|
-
|
56
|
+
send("#{attribute}=", self.class.generate_unique_secure_token(length: length))
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
@@ -125,11 +125,11 @@ module ActiveRecord
|
|
125
125
|
end
|
126
126
|
|
127
127
|
def create_all
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
128
|
+
each_local_configuration do |db_config|
|
129
|
+
with_temporary_connection(db_config) do
|
130
|
+
create(db_config)
|
131
|
+
end
|
132
|
+
end
|
133
133
|
end
|
134
134
|
|
135
135
|
def setup_initial_database_yaml # :nodoc:
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.1.
|
4
|
+
version: 7.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 7.1.
|
19
|
+
version: 7.1.3
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 7.1.
|
26
|
+
version: 7.1.3
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activemodel
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 7.1.
|
33
|
+
version: 7.1.3
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 7.1.
|
40
|
+
version: 7.1.3
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: timeout
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -470,10 +470,10 @@ licenses:
|
|
470
470
|
- MIT
|
471
471
|
metadata:
|
472
472
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
473
|
-
changelog_uri: https://github.com/rails/rails/blob/v7.1.
|
474
|
-
documentation_uri: https://api.rubyonrails.org/v7.1.
|
473
|
+
changelog_uri: https://github.com/rails/rails/blob/v7.1.3/activerecord/CHANGELOG.md
|
474
|
+
documentation_uri: https://api.rubyonrails.org/v7.1.3/
|
475
475
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
476
|
-
source_code_uri: https://github.com/rails/rails/tree/v7.1.
|
476
|
+
source_code_uri: https://github.com/rails/rails/tree/v7.1.3/activerecord
|
477
477
|
rubygems_mfa_required: 'true'
|
478
478
|
post_install_message:
|
479
479
|
rdoc_options:
|