activerecord 7.1.2 → 7.1.3

Sign up to get free protection for your applications and to get access to all the features.
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: