activerecord 8.1.1 → 8.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7b18af13d6104f184766545f02331ab69f8adf68e94d279c1c5322e6dcae354
4
- data.tar.gz: a68c959652dc4a661c9d86d26dcacfca0fb1c723d23f09ba5c67c744911a05b6
3
+ metadata.gz: 74f90753b3b77d22a985336d06901c0876965ae29376b3a6f0aa0ab2cff9a0e0
4
+ data.tar.gz: 8fd109977d7e47cd685e2b236b7bb4acd891e74e1a878e55b51cbabd436dda45
5
5
  SHA512:
6
- metadata.gz: c86cf51a07c0a5da92cf811eba3743aa7bba1727bc2509348468614323d0ab6ff3200d5718e0b132f93b765c2961d050e068dd5d6e972a55510212cf7087ccd1
7
- data.tar.gz: e7ee18efff038f1011d88c4ab137512618c73c2bc9e33132c101439fa921f05051f3dbe91ece48a7aa5e1daae081ec9af7af3536d57f4d862ccd5130de86d479
6
+ metadata.gz: 35a0e941325f4dea1f08370e583190628484f2e364285af6fd68179094cfab455e08e0bd3e34f94f102778a19377fcdc368a86c365b8e1cf7c0cbd1e303241d9
7
+ data.tar.gz: '0493ea886af6d96eaeadc88fa06d682597a3019b71fe9d3f13549761959c2f8e5a78921d4ef0ace1daf2b3005e14f95fd99dbd821ed67bac43aa634a99cb7540'
data/CHANGELOG.md CHANGED
@@ -1,3 +1,73 @@
1
+ ## Rails 8.1.2 (January 08, 2026) ##
2
+
3
+ * Fix counting cached queries in `ActiveRecord::RuntimeRegistry`.
4
+
5
+ *fatkodima*
6
+
7
+ * Fix merging relations with arel equality predicates with null relations.
8
+
9
+ *fatkodima*
10
+
11
+ * Fix SQLite3 schema dump for non-autoincrement integer primary keys.
12
+
13
+ Previously, `schema.rb` should incorrectly restore that table with an auto incrementing
14
+ primary key.
15
+
16
+ *Chris Hasiński*
17
+
18
+ * Fix PostgreSQL `schema_search_path` not being reapplied after `reset!` or `reconnect!`.
19
+
20
+ The `schema_search_path` configured in `database.yml` is now correctly
21
+ reapplied instead of falling back to PostgreSQL defaults.
22
+
23
+ *Tobias Egli*
24
+
25
+ * Restore the ability of enum to be foats.
26
+
27
+ ```ruby
28
+ enum :rating, { low: 0.0, medium: 0.5, high: 1.0 },
29
+ ```
30
+
31
+ In Rails 8.1.0, enum values are eagerly validated, and floats weren't expected.
32
+
33
+ *Said Kaldybaev*
34
+
35
+ * Ensure batched preloaded associations accounts for klass when grouping to avoid issues with STI.
36
+
37
+ *zzak*, *Stjepan Hadjic*
38
+
39
+ * Fix `ActiveRecord::SoleRecordExceeded#record` to return the relation.
40
+
41
+ This was the case until Rails 7.2, but starting from 8.0 it
42
+ started mistakenly returning the model class.
43
+
44
+ *Jean Boussier*
45
+
46
+ * Improve PostgreSQLAdapter resilience to Timeout.timeout.
47
+
48
+ Better handle asynchronous exceptions being thrown inside
49
+ the `reconnect!` method.
50
+
51
+ This may fixes some deep errors such as:
52
+
53
+ ```
54
+ undefined method `key?' for nil:NilClass (NoMethodError)
55
+ if !type_map.key?(oid)
56
+ ```
57
+
58
+ *Jean Boussier*
59
+
60
+ * Fix structured events for Active Record was not being emitted.
61
+
62
+ *Yuji Yaginuma*
63
+
64
+ * Fix `eager_load` when loading `has_many` assocations with composite primary keys.
65
+
66
+ This would result in some records being loaded multiple times.
67
+
68
+ *Martin-Alexander*
69
+
70
+
1
71
  ## Rails 8.1.1 (October 28, 2025) ##
2
72
 
3
73
  * No changes.
@@ -103,7 +103,7 @@ module ActiveRecord
103
103
  end
104
104
 
105
105
  def instantiate(result_set, strict_loading_value, &block)
106
- primary_key = aliases.column_alias(join_root, join_root.primary_key)
106
+ primary_key = Array(join_root.primary_key).map { |column| aliases.column_alias(join_root, column) }
107
107
 
108
108
  seen = Hash.new { |i, parent|
109
109
  i[parent] = Hash.new { |j, child_class|
@@ -141,7 +141,7 @@ module ActiveRecord
141
141
 
142
142
  message_bus.instrument("instantiation.active_record", payload) do
143
143
  result_set.each { |row_hash|
144
- parent_key = primary_key ? row_hash[primary_key] : row_hash
144
+ parent_key = primary_key.empty? ? row_hash : row_hash.values_at(*primary_key)
145
145
  parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, column_types, &block)
146
146
  construct(parent, join_root, row_hash, seen, model_cache, strict_loading_value)
147
147
  }
@@ -38,7 +38,13 @@ module ActiveRecord
38
38
  attr_reader :loaders
39
39
 
40
40
  def group_and_load_similar(loaders)
41
- loaders.grep_v(ThroughAssociation).group_by(&:loader_query).each_pair do |query, similar_loaders|
41
+ non_through = loaders.grep_v(ThroughAssociation)
42
+
43
+ grouped = non_through.group_by do |loader|
44
+ [loader.loader_query, loader.klass]
45
+ end
46
+
47
+ grouped.each do |(query, _klass), similar_loaders|
42
48
  query.load_records_in_batch(similar_loaders)
43
49
  end
44
50
  end
@@ -1030,9 +1030,9 @@ module ActiveRecord
1030
1030
  # associated records themselves, you can always do something along the lines of
1031
1031
  # <tt>person.tasks.each(&:destroy)</tt>.
1032
1032
  #
1033
- # == Deprecated Associations
1033
+ # == Deprecated \Associations
1034
1034
  #
1035
- # Associations can be marked as deprecated by passing <tt>deprecated: true</tt>:
1035
+ # \Associations can be marked as deprecated by passing <tt>deprecated: true</tt>:
1036
1036
  #
1037
1037
  # has_many :posts, deprecated: true
1038
1038
  #
@@ -6,6 +6,7 @@ require "active_support/descendants_tracker"
6
6
  require "active_support/time"
7
7
  require "active_support/core_ext/class/subclasses"
8
8
  require "active_record/log_subscriber"
9
+ require "active_record/structured_event_subscriber"
9
10
  require "active_record/relation/delegation"
10
11
  require "active_record/attributes"
11
12
  require "active_record/type_caster"
@@ -713,37 +713,36 @@ module ActiveRecord
713
713
  deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
714
714
 
715
715
  @lock.synchronize do
716
- @allow_preconnect = false
716
+ attempt_configure_connection do
717
+ @allow_preconnect = false
717
718
 
718
- reconnect
719
+ reconnect
719
720
 
720
- enable_lazy_transactions!
721
- @raw_connection_dirty = false
722
- @last_activity = @connected_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
723
- @verified = true
724
- @allow_preconnect = true
725
-
726
- reset_transaction(restore: restore_transactions) do
727
- clear_cache!(new_connection: true)
728
- attempt_configure_connection
729
- end
730
- rescue => original_exception
731
- translated_exception = translate_exception_class(original_exception, nil, nil)
732
- retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
721
+ enable_lazy_transactions!
722
+ @raw_connection_dirty = false
723
+ @last_activity = @connected_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
724
+ @verified = true
725
+ @allow_preconnect = true
726
+
727
+ reset_transaction(restore: restore_transactions) do
728
+ clear_cache!(new_connection: true)
729
+ configure_connection
730
+ end
731
+ rescue => original_exception
732
+ translated_exception = translate_exception_class(original_exception, nil, nil)
733
+ retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
733
734
 
734
- if !retry_deadline_exceeded && retries_available > 0
735
- retries_available -= 1
735
+ if !retry_deadline_exceeded && retries_available > 0
736
+ retries_available -= 1
736
737
 
737
- if retryable_connection_error?(translated_exception)
738
- backoff(connection_retries - retries_available)
739
- retry
738
+ if retryable_connection_error?(translated_exception)
739
+ backoff(connection_retries - retries_available)
740
+ retry
741
+ end
740
742
  end
741
- end
742
-
743
- @last_activity = nil
744
- @verified = false
745
743
 
746
- raise translated_exception
744
+ raise translated_exception
745
+ end
747
746
  end
748
747
  end
749
748
 
@@ -755,6 +754,8 @@ module ActiveRecord
755
754
  reset_transaction
756
755
  @raw_connection_dirty = false
757
756
  @connected_since = nil
757
+ @last_activity = nil
758
+ @verified = false
758
759
  end
759
760
  end
760
761
 
@@ -777,9 +778,11 @@ module ActiveRecord
777
778
  # should call super immediately after resetting the connection (and while
778
779
  # still holding @lock).
779
780
  def reset!
780
- clear_cache!(new_connection: true)
781
- reset_transaction
782
- attempt_configure_connection
781
+ attempt_configure_connection do
782
+ clear_cache!(new_connection: true)
783
+ reset_transaction
784
+ configure_connection
785
+ end
783
786
  end
784
787
 
785
788
  # Removes the connection from the pool and disconnect it.
@@ -813,12 +816,14 @@ module ActiveRecord
813
816
  unless active?
814
817
  @lock.synchronize do
815
818
  if @unconfigured_connection
816
- @raw_connection = @unconfigured_connection
817
- @unconfigured_connection = nil
818
- attempt_configure_connection
819
- @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
820
- @verified = true
821
- @allow_preconnect = true
819
+ attempt_configure_connection do
820
+ @raw_connection = @unconfigured_connection
821
+ @unconfigured_connection = nil
822
+ configure_connection
823
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
824
+ @verified = true
825
+ @allow_preconnect = true
826
+ end
822
827
  return
823
828
  end
824
829
 
@@ -1282,7 +1287,7 @@ module ActiveRecord
1282
1287
  end
1283
1288
 
1284
1289
  def attempt_configure_connection
1285
- configure_connection
1290
+ yield
1286
1291
  rescue Exception # Need to handle things such as Timeout::ExitException
1287
1292
  disconnect!
1288
1293
  raise
@@ -395,6 +395,11 @@ module ActiveRecord
395
395
  end
396
396
  end
397
397
 
398
+ def clear_cache!(new_connection: false)
399
+ super
400
+ @schema_search_path = nil if new_connection
401
+ end
402
+
398
403
  # Disconnects from the database if already connected. Otherwise, this
399
404
  # method does nothing.
400
405
  def disconnect!
@@ -1009,6 +1014,8 @@ module ActiveRecord
1009
1014
  add_pg_encoders
1010
1015
  add_pg_decoders
1011
1016
 
1017
+ schema_search_path # populate cache
1018
+
1012
1019
  reload_type_map
1013
1020
  end
1014
1021
 
@@ -19,11 +19,23 @@ module ActiveRecord
19
19
  end
20
20
 
21
21
  def default_primary_key?(column)
22
- schema_type(column) == :integer
22
+ schema_type(column) == :integer && primary_key_has_autoincrement?
23
23
  end
24
24
 
25
25
  def explicit_primary_key_default?(column)
26
- column.bigint?
26
+ column.bigint? || (column.type == :integer && !primary_key_has_autoincrement?)
27
+ end
28
+
29
+ def primary_key_has_autoincrement?
30
+ return false unless table_name
31
+
32
+ table_sql = @connection.query_value(<<~SQL, "SCHEMA")
33
+ SELECT sql FROM sqlite_master WHERE name = #{@connection.quote(table_name)} AND type = 'table'
34
+ UNION ALL
35
+ SELECT sql FROM sqlite_temp_master WHERE name = #{@connection.quote(table_name)} AND type = 'table'
36
+ SQL
37
+
38
+ table_sql.to_s.match?(/\bAUTOINCREMENT\b/i)
27
39
  end
28
40
 
29
41
  def prepare_column_options(column)
@@ -349,10 +349,10 @@ module ActiveRecord
349
349
 
350
350
  values.each_value do |value|
351
351
  case value
352
- when String, Integer, true, false, nil
352
+ when String, Integer, Float, true, false, nil
353
353
  # noop
354
354
  else
355
- raise ArgumentError, "Enum values #{values} must be only booleans, integers, symbols or strings, got: #{value.class}"
355
+ raise ArgumentError, "Enum values #{values} must be only booleans, integers, floats, symbols or strings, got: #{value.class}"
356
356
  end
357
357
  end
358
358
 
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 8
11
11
  MINOR = 1
12
- TINY = 1
12
+ TINY = 2
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -148,7 +148,7 @@ module ActiveRecord
148
148
  elsif undesired.nil?
149
149
  found
150
150
  else
151
- raise ActiveRecord::SoleRecordExceeded.new(model)
151
+ raise ActiveRecord::SoleRecordExceeded.new(self)
152
152
  end
153
153
  end
154
154
 
@@ -182,7 +182,7 @@ module ActiveRecord
182
182
  non_attrs = columns.extract! { |node| node.is_a?(Arel::Predications) }
183
183
 
184
184
  predicates.reject do |node|
185
- if !non_attrs.empty? && node.equality? && node.left.is_a?(Arel::Predications)
185
+ if !non_attrs.empty? && equality_node?(node) && node.left.is_a?(Arel::Predications)
186
186
  non_attrs.include?(node.left)
187
187
  end || Arel.fetch_attribute(node) do |attr|
188
188
  attrs.include?(attr) || columns.include?(attr.name.to_s)
@@ -1022,7 +1022,7 @@ module ActiveRecord
1022
1022
  #
1023
1023
  # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
1024
1024
  #
1025
- # Both calls delete the affected posts all at once with a single DELETE statement.
1025
+ # This call deletes the affected posts all at once with a single DELETE statement.
1026
1026
  # If you need to destroy dependent associations or call your <tt>before_*</tt> or
1027
1027
  # +after_destroy+ callbacks, use the #destroy_all method instead.
1028
1028
  #
@@ -33,6 +33,7 @@ module ActiveRecord
33
33
  record(
34
34
  payload[:name],
35
35
  (finish - start) * 1_000.0,
36
+ cached: payload[:cached],
36
37
  async: payload[:async],
37
38
  lock_wait: payload[:lock_wait],
38
39
  )
@@ -232,8 +232,8 @@ module ActiveRecord
232
232
  def indexes(table, stream)
233
233
  if (indexes = @connection.indexes(table)).any?
234
234
  add_index_statements = indexes.map do |index|
235
- table_name = remove_prefix_and_suffix(index.table).inspect
236
- " add_index #{([relation_name(table_name)] + index_parts(index)).join(', ')}"
235
+ table_name = remove_prefix_and_suffix(index.table)
236
+ " add_index #{([relation_name(table_name).inspect] + index_parts(index)).join(', ')}"
237
237
  end
238
238
 
239
239
  stream.puts add_index_statements.sort.join("\n")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.1
4
+ version: 8.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
@@ -15,28 +15,28 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 8.1.1
18
+ version: 8.1.2
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 8.1.1
25
+ version: 8.1.2
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: activemodel
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - '='
31
31
  - !ruby/object:Gem::Version
32
- version: 8.1.1
32
+ version: 8.1.2
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - '='
38
38
  - !ruby/object:Gem::Version
39
- version: 8.1.1
39
+ version: 8.1.2
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: timeout
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -478,10 +478,10 @@ licenses:
478
478
  - MIT
479
479
  metadata:
480
480
  bug_tracker_uri: https://github.com/rails/rails/issues
481
- changelog_uri: https://github.com/rails/rails/blob/v8.1.1/activerecord/CHANGELOG.md
482
- documentation_uri: https://api.rubyonrails.org/v8.1.1/
481
+ changelog_uri: https://github.com/rails/rails/blob/v8.1.2/activerecord/CHANGELOG.md
482
+ documentation_uri: https://api.rubyonrails.org/v8.1.2/
483
483
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
484
- source_code_uri: https://github.com/rails/rails/tree/v8.1.1/activerecord
484
+ source_code_uri: https://github.com/rails/rails/tree/v8.1.2/activerecord
485
485
  rubygems_mfa_required: 'true'
486
486
  rdoc_options:
487
487
  - "--main"
@@ -499,7 +499,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
499
499
  - !ruby/object:Gem::Version
500
500
  version: '0'
501
501
  requirements: []
502
- rubygems_version: 3.6.9
502
+ rubygems_version: 4.0.3
503
503
  specification_version: 4
504
504
  summary: Object-relational mapper framework (part of Rails).
505
505
  test_files: []