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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +99 -0
  3. data/lib/active_record/associations/association.rb +2 -1
  4. data/lib/active_record/associations/preloader/association.rb +4 -1
  5. data/lib/active_record/associations.rb +1 -1
  6. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  7. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  8. data/lib/active_record/attribute_methods.rb +1 -1
  9. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +11 -8
  10. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  11. data/lib/active_record/connection_adapters/trilogy_adapter.rb +9 -1
  12. data/lib/active_record/connection_handling.rb +1 -1
  13. data/lib/active_record/core.rb +37 -4
  14. data/lib/active_record/delegated_type.rb +1 -1
  15. data/lib/active_record/encryption/encryptable_record.rb +7 -1
  16. data/lib/active_record/encryption/encrypted_attribute_type.rb +4 -0
  17. data/lib/active_record/errors.rb +5 -4
  18. data/lib/active_record/fixtures.rb +16 -0
  19. data/lib/active_record/future_result.rb +1 -0
  20. data/lib/active_record/gem_version.rb +1 -1
  21. data/lib/active_record/middleware/database_selector.rb +1 -1
  22. data/lib/active_record/migration/compatibility.rb +8 -0
  23. data/lib/active_record/migration.rb +3 -2
  24. data/lib/active_record/persistence.rb +4 -3
  25. data/lib/active_record/railties/controller_runtime.rb +2 -1
  26. data/lib/active_record/reflection.rb +1 -1
  27. data/lib/active_record/relation/calculations.rb +28 -1
  28. data/lib/active_record/relation/delegation.rb +1 -1
  29. data/lib/active_record/relation.rb +18 -3
  30. data/lib/active_record/runtime_registry.rb +15 -1
  31. data/lib/active_record/secure_token.rb +1 -1
  32. data/lib/active_record/tasks/database_tasks.rb +5 -5
  33. metadata +9 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dba30f1c9ab82b5a3e78337bcdb364d613e04a20e815c4b1f6952e1360d8e602
4
- data.tar.gz: dc7470a1bc6823f31ec4c3b678df945ac4a32a2a1a642943909f47318af205f6
3
+ metadata.gz: 9f7c51f040979b7650eb209b0dc856a0877bccc968dd99cde06e3cf1776dd638
4
+ data.tar.gz: b81eca56b185f087c95b6691b307ff1c9c63d0d03ff650b729e943888f053152
5
5
  SHA512:
6
- metadata.gz: b85f866d527785cad076c222228e1b6ee39122b383663cc1fe81dcad6eb152c52dfb46ad86dee471f01525c5e074a382fbe5360fd99e78e7886d7169c2c60091
7
- data.tar.gz: 5291b47487b7bdb83885687b2c6ca90fe5295ed0c017317585bbb712c125c28f66c4b3d5c7cceb0e041bd25be069854c365222179bad6dc360b9156e5f3753ae
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
- attr_reader :owner, :target, :reflection, :disable_joins
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 fallback
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.
@@ -56,7 +56,7 @@ module ActiveRecord
56
56
  # serialization.
57
57
  #
58
58
  # class Book < ActiveRecord::Base
59
- # enum status: { draft: 1, published: 2 }
59
+ # enum :status, { draft: 1, published: 2 }
60
60
  # end
61
61
  #
62
62
  # book = Book.new(status: "published")
@@ -14,7 +14,7 @@ module ActiveRecord
14
14
  # class Person < ActiveRecord::Base
15
15
  # end
16
16
  #
17
- # person = Person.create(name: "Alisson")
17
+ # person = Person.create(name: "Allison")
18
18
  # person.changed? # => false
19
19
  #
20
20
  # Change the name:
@@ -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 object_id class clone]
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
- # as with Active Record 2.1 and
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 will be the default behavior for Active Record when used in conjunction with
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
- # Return connection pool's usage statistic
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
- @available.poll(checkout_timeout)
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::ConnectionError, ::Trilogy::ProtocolError => error
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
- # Return the connection specification name from the current class or its parent.
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
@@ -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
- value = value.public_send(pkey) if value.respond_to?(pkey)
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 !columns_hash.key?(key) || StatementCache.unsupported_value?(value)
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 { params.bind }
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
- ActiveRecord::Encryption.encryptor.encrypted? read_attribute_before_type_cast(attribute_name)
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
@@ -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 file.
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 for this app, or delete it? You may need to create your 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
- To create your database, run:\n\n bin/rails db:create
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
@@ -48,6 +48,7 @@ module ActiveRecord
48
48
  Canceled = Class.new(ActiveRecordError)
49
49
 
50
50
  delegate :empty?, :to_a, to: :result
51
+ delegate_missing_to :result
51
52
 
52
53
  attr_reader :lock_wait
53
54
 
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 7
11
11
  MINOR = 1
12
- TINY = 2
12
+ TINY = 3
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -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
- # bin/rails generate migration MyNewMigration
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 - db_rt_after_render
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 !polymorphic? && ((klass || self.klass).has_query_constraints? || options[:query_constraints])
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
- return [] if @none
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 no record because a
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 Promise::Complete.new([])
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
- ActiveRecord::RuntimeRegistry.sql_runtime += (finish - start) * 1_000.0
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
- write_attribute(attribute, self.class.generate_unique_secure_token(length: length))
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
- db_config = migration_connection.pool.db_config
129
-
130
- each_local_configuration { |db_config| create(db_config) }
131
-
132
- migration_class.establish_connection(db_config)
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.2
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: 2023-11-10 00:00:00.000000000 Z
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.2
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.2
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.2
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.2
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.2/activerecord/CHANGELOG.md
474
- documentation_uri: https://api.rubyonrails.org/v7.1.2/
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.2/activerecord
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: