activerecord 8.0.0 → 8.1.4

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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1381 -241
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/association_relation.rb +1 -1
  5. data/lib/active_record/associations/alias_tracker.rb +6 -4
  6. data/lib/active_record/associations/association.rb +35 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -1
  8. data/lib/active_record/associations/belongs_to_association.rb +20 -4
  9. data/lib/active_record/associations/builder/association.rb +16 -5
  10. data/lib/active_record/associations/builder/belongs_to.rb +36 -8
  11. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  12. data/lib/active_record/associations/builder/has_one.rb +1 -1
  13. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  14. data/lib/active_record/associations/collection_association.rb +9 -5
  15. data/lib/active_record/associations/collection_proxy.rb +26 -4
  16. data/lib/active_record/associations/deprecation.rb +88 -0
  17. data/lib/active_record/associations/errors.rb +3 -0
  18. data/lib/active_record/associations/foreign_association.rb +3 -1
  19. data/lib/active_record/associations/has_one_association.rb +1 -0
  20. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  21. data/lib/active_record/associations/join_dependency.rb +5 -4
  22. data/lib/active_record/associations/preloader/association.rb +1 -1
  23. data/lib/active_record/associations/preloader/batch.rb +7 -1
  24. data/lib/active_record/associations/preloader/branch.rb +1 -0
  25. data/lib/active_record/associations.rb +159 -21
  26. data/lib/active_record/attribute_methods/primary_key.rb +2 -1
  27. data/lib/active_record/attribute_methods/query.rb +34 -0
  28. data/lib/active_record/attribute_methods/serialization.rb +19 -6
  29. data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
  30. data/lib/active_record/attribute_methods.rb +24 -17
  31. data/lib/active_record/attributes.rb +40 -26
  32. data/lib/active_record/autosave_association.rb +22 -12
  33. data/lib/active_record/base.rb +3 -4
  34. data/lib/active_record/callbacks.rb +1 -2
  35. data/lib/active_record/coders/column_serializer.rb +1 -1
  36. data/lib/active_record/coders/json.rb +16 -6
  37. data/lib/active_record/coders/yaml_column.rb +2 -2
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +19 -18
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
  40. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -12
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +474 -112
  42. data/lib/active_record/connection_adapters/abstract/database_statements.rb +68 -41
  43. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -9
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +31 -35
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +94 -28
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -64
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +133 -40
  52. data/lib/active_record/connection_adapters/column.rb +14 -4
  53. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  54. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
  55. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  56. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
  57. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +40 -25
  58. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +66 -15
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -3
  60. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  61. data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
  62. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +29 -33
  63. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
  64. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
  65. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  67. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
  69. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -33
  70. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +70 -32
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +99 -54
  72. data/lib/active_record/connection_adapters/postgresql_adapter.rb +40 -12
  73. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  74. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -23
  75. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
  76. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +15 -3
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +7 -14
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +68 -37
  79. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  80. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +8 -3
  81. data/lib/active_record/connection_adapters/trilogy_adapter.rb +16 -2
  82. data/lib/active_record/connection_adapters.rb +1 -0
  83. data/lib/active_record/connection_handling.rb +15 -10
  84. data/lib/active_record/core.rb +44 -12
  85. data/lib/active_record/counter_cache.rb +36 -9
  86. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  87. data/lib/active_record/database_configurations/database_config.rb +5 -1
  88. data/lib/active_record/database_configurations/hash_config.rb +66 -9
  89. data/lib/active_record/database_configurations/url_config.rb +15 -3
  90. data/lib/active_record/database_configurations.rb +7 -3
  91. data/lib/active_record/delegated_type.rb +19 -19
  92. data/lib/active_record/disable_joins_association_relation.rb +5 -1
  93. data/lib/active_record/dynamic_matchers.rb +54 -69
  94. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  95. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  96. data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
  97. data/lib/active_record/encryption/encryptor.rb +39 -25
  98. data/lib/active_record/encryption/scheme.rb +1 -1
  99. data/lib/active_record/enum.rb +37 -20
  100. data/lib/active_record/errors.rb +23 -7
  101. data/lib/active_record/explain.rb +1 -1
  102. data/lib/active_record/explain_registry.rb +51 -2
  103. data/lib/active_record/filter_attribute_handler.rb +76 -0
  104. data/lib/active_record/fixture_set/table_row.rb +19 -2
  105. data/lib/active_record/fixtures.rb +2 -2
  106. data/lib/active_record/future_result.rb +5 -3
  107. data/lib/active_record/gem_version.rb +2 -2
  108. data/lib/active_record/inheritance.rb +9 -4
  109. data/lib/active_record/insert_all.rb +14 -9
  110. data/lib/active_record/locking/optimistic.rb +7 -0
  111. data/lib/active_record/locking/pessimistic.rb +5 -0
  112. data/lib/active_record/log_subscriber.rb +30 -22
  113. data/lib/active_record/message_pack.rb +13 -0
  114. data/lib/active_record/middleware/shard_selector.rb +34 -17
  115. data/lib/active_record/migration/command_recorder.rb +32 -6
  116. data/lib/active_record/migration/compatibility.rb +34 -24
  117. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  118. data/lib/active_record/migration.rb +31 -21
  119. data/lib/active_record/model_schema.rb +38 -12
  120. data/lib/active_record/nested_attributes.rb +10 -6
  121. data/lib/active_record/persistence.rb +35 -3
  122. data/lib/active_record/query_cache.rb +22 -15
  123. data/lib/active_record/query_logs.rb +7 -7
  124. data/lib/active_record/querying.rb +17 -5
  125. data/lib/active_record/railtie.rb +35 -6
  126. data/lib/active_record/railties/controller_runtime.rb +11 -6
  127. data/lib/active_record/railties/databases.rake +24 -20
  128. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  129. data/lib/active_record/railties/job_runtime.rb +10 -11
  130. data/lib/active_record/reflection.rb +36 -1
  131. data/lib/active_record/relation/batches.rb +25 -11
  132. data/lib/active_record/relation/calculations.rb +62 -41
  133. data/lib/active_record/relation/delegation.rb +0 -1
  134. data/lib/active_record/relation/finder_methods.rb +76 -45
  135. data/lib/active_record/relation/merger.rb +4 -2
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  137. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
  138. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
  139. data/lib/active_record/relation/predicate_builder.rb +10 -8
  140. data/lib/active_record/relation/query_attribute.rb +4 -2
  141. data/lib/active_record/relation/query_methods.rb +106 -45
  142. data/lib/active_record/relation/spawn_methods.rb +6 -6
  143. data/lib/active_record/relation/where_clause.rb +10 -11
  144. data/lib/active_record/relation.rb +63 -31
  145. data/lib/active_record/result.rb +44 -21
  146. data/lib/active_record/runtime_registry.rb +42 -58
  147. data/lib/active_record/sanitization.rb +2 -0
  148. data/lib/active_record/schema.rb +1 -1
  149. data/lib/active_record/schema_dumper.rb +41 -21
  150. data/lib/active_record/scoping.rb +0 -1
  151. data/lib/active_record/secure_token.rb +3 -3
  152. data/lib/active_record/signed_id.rb +48 -19
  153. data/lib/active_record/statement_cache.rb +16 -12
  154. data/lib/active_record/store.rb +46 -19
  155. data/lib/active_record/structured_event_subscriber.rb +85 -0
  156. data/lib/active_record/table_metadata.rb +5 -20
  157. data/lib/active_record/tasks/abstract_tasks.rb +83 -0
  158. data/lib/active_record/tasks/database_tasks.rb +44 -45
  159. data/lib/active_record/tasks/mysql_database_tasks.rb +4 -56
  160. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  161. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
  162. data/lib/active_record/test_databases.rb +18 -4
  163. data/lib/active_record/test_fixtures.rb +40 -6
  164. data/lib/active_record/testing/query_assertions.rb +8 -2
  165. data/lib/active_record/timestamp.rb +4 -2
  166. data/lib/active_record/transaction.rb +2 -5
  167. data/lib/active_record/transactions.rb +58 -32
  168. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  169. data/lib/active_record/type/internal/timezone.rb +7 -0
  170. data/lib/active_record/type/json.rb +13 -2
  171. data/lib/active_record/type/serialized.rb +16 -4
  172. data/lib/active_record/type/type_map.rb +1 -1
  173. data/lib/active_record/type_caster/connection.rb +2 -1
  174. data/lib/active_record/validations/associated.rb +1 -1
  175. data/lib/active_record.rb +72 -7
  176. data/lib/arel/alias_predication.rb +2 -0
  177. data/lib/arel/collectors/bind.rb +1 -1
  178. data/lib/arel/collectors/sql_string.rb +1 -1
  179. data/lib/arel/collectors/substitute_binds.rb +2 -2
  180. data/lib/arel/crud.rb +8 -11
  181. data/lib/arel/delete_manager.rb +5 -0
  182. data/lib/arel/nodes/binary.rb +1 -1
  183. data/lib/arel/nodes/count.rb +2 -2
  184. data/lib/arel/nodes/delete_statement.rb +4 -2
  185. data/lib/arel/nodes/function.rb +4 -10
  186. data/lib/arel/nodes/named_function.rb +2 -2
  187. data/lib/arel/nodes/node.rb +2 -2
  188. data/lib/arel/nodes/sql_literal.rb +1 -1
  189. data/lib/arel/nodes/update_statement.rb +4 -2
  190. data/lib/arel/nodes.rb +0 -2
  191. data/lib/arel/predications.rb +1 -3
  192. data/lib/arel/select_manager.rb +13 -4
  193. data/lib/arel/update_manager.rb +5 -0
  194. data/lib/arel/visitors/dot.rb +2 -3
  195. data/lib/arel/visitors/postgresql.rb +55 -0
  196. data/lib/arel/visitors/sqlite.rb +55 -8
  197. data/lib/arel/visitors/to_sql.rb +7 -23
  198. data/lib/arel.rb +3 -1
  199. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  200. metadata +16 -15
  201. data/lib/active_record/explain_subscriber.rb +0 -34
  202. data/lib/active_record/normalization.rb +0 -163
data/CHANGELOG.md CHANGED
@@ -1,454 +1,1594 @@
1
- ## Rails 8.0.0 (November 07, 2024) ##
1
+ ## Rails 8.1.4 (September 24, 2026) ##
2
2
 
3
- * Fix support for `query_cache: false` in `database.yml`.
3
+ * Avoid deadlocks when concurrent `find_or_create_by` calls read back the same
4
+ record within MySQL transactions.
4
5
 
5
- `query_cache: false` would no longer entirely disable the Active Record query cache.
6
+ Use a shared lock for the read after a duplicate insert, preserving visibility
7
+ under REPEATABLE READ without upgrading competing shared locks to exclusive locks.
8
+ This also applies to `create_or_find_by` and the bang variants of both methods.
6
9
 
7
- *zzak*
10
+ Fixes #54281.
8
11
 
9
- ## Rails 8.0.0.rc2 (October 30, 2024) ##
12
+ *Kirsten Westeinde*
10
13
 
11
- * NULLS NOT DISTINCT works with UNIQUE CONSTRAINT as well as UNIQUE INDEX.
14
+ * Filter the database password out of failed `db:` task command error messages.
12
15
 
13
- *Ryuta Kamizono*
16
+ *Ngan Pham*
14
17
 
15
- * The `db:prepare` task no longer loads seeds when a non-primary database is created.
18
+ * Fix `ActiveRecord::TypeCaster::Connection` sometimes leaking a checked-out
19
+ connection.
16
20
 
17
- Previously, the `db:prepare` task would load seeds whenever a new database
18
- is created, leading to potential loss of data if a database is added to an
19
- existing environment.
21
+ *Hartley McGuire*
20
22
 
21
- Introduces a new database config property `seeds` to control whether seeds
22
- are loaded during `db:prepare` which defaults to `true` for primary database
23
- configs and `false` otherwise.
23
+ * Fix PostgreSQL exclusion constraints with multiline expressions being parsed
24
+ incorrectly during schema introspection.
24
25
 
25
- Fixes #53348.
26
+ *Jake McAllister*
26
27
 
27
- *Mike Dalessio*
28
+ * Fix async `ActiveRecord::StatementCache#execute` raising an error for
29
+ out-of-range bind values instead of returning an empty result.
28
30
 
29
- * `PG::UnableToSend: no connection to the server` is now retryable as a connection-related exception
31
+ *viralpraxis*
30
32
 
31
- *Kazuma Watanabe*
33
+ * Fix `where` clauses with column-tuple syntax not resolving references to
34
+ other tables.
32
35
 
33
- * Fix strict loading propagation even if statement cache is not used.
36
+ *Chris Gunther*
34
37
 
35
- *Ryuta Kamizono*
38
+ * Fix `distinct: true` being ignored by `average`.
36
39
 
37
- * Allow `rename_enum` accepts two from/to name arguments as `rename_table` does so.
40
+ *Kenta Ishizaki*
38
41
 
39
- *Ryuta Kamizono*
42
+ * Fix `belongs_to` change tracking for composite foreign keys.
40
43
 
44
+ Only the first foreign key column was checked for changes; now all foreign
45
+ key columns are checked.
41
46
 
42
- ## Rails 8.0.0.rc1 (October 19, 2024) ##
47
+ *Anas Khan*
43
48
 
44
- * Remove deprecated support to setting `ENV["SCHEMA_CACHE"]`.
49
+ * Make `add_column(if_not_exists: true)` reversible.
45
50
 
46
- *Rafael Mendonça França*
51
+ *Kenta Ishizaki*
47
52
 
48
- * Remove deprecated support to passing a database name to `cache_dump_filename`.
53
+ * Quote the index name in MySQL `enable_index` and `disable_index`.
49
54
 
50
- *Rafael Mendonça França*
55
+ Unquoted index names containing special characters could cause SQL syntax
56
+ errors.
51
57
 
52
- * Remove deprecated `ActiveRecord::ConnectionAdapters::ConnectionPool#connection`.
58
+ *Kenta Ishizaki*
53
59
 
54
- *Rafael Mendonça França*
60
+ * Make `remove_foreign_key(if_exists: true)` reversible.
55
61
 
56
- * Remove deprecated `config.active_record.sqlite3_deprecated_warning`.
62
+ *Kenta Ishizaki*
57
63
 
58
- *Rafael Mendonça França*
64
+ * Fix `distinct: true` being ignored by grouped `sum`.
59
65
 
60
- * Remove deprecated `config.active_record.warn_on_records_fetched_greater_than`.
66
+ *Kenta Ishizaki*
61
67
 
62
- *Rafael Mendonça França*
68
+ * Return an `ActiveRecord::Promise` from `async_ids` on a contradictory
69
+ relation, instead of a plain `ActiveRecord::Result`.
63
70
 
64
- * Remove deprecated support for defining `enum` with keyword arguments.
71
+ *Kenta Ishizaki*
65
72
 
66
- *Rafael Mendonça França*
73
+ * Fix reversibility check for `drop_virtual_table`.
67
74
 
68
- * Remove deprecated support to finding database adapters that aren't registered to Active Record.
75
+ The wrong argument was extracted, so the check for whether options were
76
+ given could pass or fail incorrectly.
69
77
 
70
- *Rafael Mendonça França*
78
+ *Kenta Ishizaki*
71
79
 
72
- * Remove deprecated `config.active_record.allow_deprecated_singular_associations_name`.
80
+ * Fix `remove_check_constraint(if_exists: true)` incorrectly raising when
81
+ removing by expression.
73
82
 
74
- *Rafael Mendonça França*
83
+ The `if_exists` check did not pass the expression to
84
+ `check_constraint_exists?`, so it could not find the constraint.
75
85
 
76
- * Remove deprecated `config.active_record.commit_transaction_on_non_local_return`.
86
+ *Kenta Ishizaki*
77
87
 
78
- *Rafael Mendonça França*
88
+ * Clear PostgreSQL notices as they are handled, preventing warnings from
89
+ leaking to subsequent queries.
79
90
 
80
- * Fix incorrect SQL query when passing an empty hash to `ActiveRecord::Base.insert`.
91
+ *Matthew Draper*
81
92
 
82
- *David Stosik*
93
+ * Fix `WeakThreadKeyMap` raising "can't add a new key into hash during
94
+ iteration" when connections are checked in from multiple threads.
83
95
 
84
- * Allow to save records with polymorphic join tables that have `inverse_of`
85
- specified.
96
+ *Matthew Draper*
86
97
 
87
- *Markus Doits*
98
+ * Continue pool disconnect after a connection's cleanup or disconnect raises.
88
99
 
89
- * Fix association scopes applying on the incorrect join when using a polymorphic `has_many through:`.
100
+ Previously, a failure on one connection aborted the teardown, stranding
101
+ remaining connections and their resources.
90
102
 
91
- *Joshua Young*
103
+ *Matthew Draper*
104
+
105
+ * Fix race condition between `preconnect` and the connection pool reaper.
106
+
107
+ *Yasuo Honda*
108
+
109
+ * Fix `in_order_of` raising when the column is a CTE reference.
110
+
111
+ *viralpraxis*
112
+
113
+ * Fix `content_columns` including composite primary key components.
114
+
115
+ Only the first primary key column was excluded; now all primary key columns
116
+ are excluded.
117
+
118
+ *Kenta Ishizaki*
119
+
120
+ * Treat `remove_column` with options but no type as irreversible.
121
+
122
+ Previously, `remove_column(:table, :column, default: nil)` would not raise
123
+ `IrreversibleMigration` because the options hash was mistaken for a column
124
+ type.
125
+
126
+ *Kenta Ishizaki*
127
+
128
+ * Fix `counter_cache_column` crashing when `:counter_cache` is given as a
129
+ Hash without a `:column` key.
130
+
131
+ *Kenta Ishizaki*
132
+
133
+ * Coerce `seeds` and `use_metadata_table` to booleans in `UrlConfig`.
134
+
135
+ When configured via `DATABASE_URL`, these options were left as strings
136
+ instead of being coerced to booleans like `replica` and `database_tasks`.
137
+
138
+ *Kenta Ishizaki*
139
+
140
+ * Fix `excluding` (and its `without` alias) to work with composite primary key
141
+ models. Records passed to `excluding` are now matched by their full composite id
142
+ instead of being looked up by a single primary key column.
143
+
144
+ *Kenta Ishizaki*
145
+
146
+ * Fix `Relation#delete` raising on a single composite primary key id.
147
+
148
+ `Model.delete(id)` now wraps a single composite id in an array before building
149
+ the `WHERE` clause, so a single record can be deleted by its composite id.
150
+
151
+ *Kenta Ishizaki*
152
+
153
+ * Fix reading a composite primary key after a partial `select` returning the
154
+ raw value instead of `nil` for unselected key columns.
155
+
156
+ The attribute defaults builder excluded only the single primary key column from
157
+ defaults; for composite primary keys it left the unselected key columns in the
158
+ defaults, so `record.id` returned an array of raw column values instead of
159
+ `[nil, nil]`. All primary key columns are now excluded.
160
+
161
+ *Kenta Ishizaki*
162
+
163
+ * Fix `find` with an empty array on a composite primary key model returning the
164
+ argument array instead of `[]`.
165
+
166
+ `Model.find([])` now returns a fresh empty array for composite primary key
167
+ models, matching single-key models.
168
+
169
+ *Kenta Ishizaki*
170
+
171
+ * Fix `find` with an order clause and an offset past the end of the ids raising
172
+ `RecordNotFound` instead of returning an empty result.
173
+
174
+ *Kenta Ishizaki*
175
+
176
+ * Fix `in_order_of` dropping values that cannot be serialized for the column
177
+ instead of turning them into an `IS NULL` match.
178
+
179
+ Out-of-range integers and unknown enum keys are now ignored rather than
180
+ silently matching rows with `NULL` in that column. When all requested values
181
+ are unrepresentable, the relation returns no rows instead of building an empty
182
+ `CASE` expression.
183
+
184
+ *Kenta Ishizaki*
185
+
186
+ * Fix stale `finder_needs_type_condition?` memo after the schema cache is reloaded.
187
+
188
+ When an STI model's `type` column was removed and re-added (e.g. via a
189
+ migration) and `reset_column_information` was called, the cached
190
+ `finder_needs_type_condition` value was not cleared, so queries could omit the
191
+ STI type condition. The memo is now reset on `reload_schema_from_cache`.
192
+
193
+ *Kenta Ishizaki*
194
+
195
+ * Fix PostgreSQL schema dump to qualify `enum_type` when the same enum name exists
196
+ in different schemas.
197
+
198
+ Previously the dumped `enum_type` used the bare type name, which could resolve to
199
+ the wrong enum in another schema. It now uses the schema-qualified name.
200
+
201
+ *fatkodima*
202
+
203
+ * Fix PostgreSQL index introspection to parse opclasses from another schema.
204
+
205
+ Index expressions using a schema-qualified opclass (e.g.
206
+ `USING gin(position test_schema.gin_trgm_ops)`) are now parsed correctly so the
207
+ opclass is captured without the schema prefix.
208
+
209
+ *Nicolas Vandenboegaerde*
210
+
211
+ * Fix `belongs_to` `:touch` with composite foreign keys.
212
+
213
+ Touching the parent record when a `belongs_to` with a composite foreign key
214
+ changes or is destroyed now correctly resolves the old and new parent by all
215
+ foreign key columns.
216
+
217
+ *Romulo Storel*
218
+
219
+ * Fix `add_index` `comment:` option on a schema-qualified PostgreSQL table.
92
220
 
93
- * Allow `ActiveRecord::Base#pluck` to accept hash arguments with symbol and string values.
221
+ The `COMMENT ON INDEX` statement now schema-qualifies the index name, so the
222
+ comment is applied to the index in the correct schema instead of raising.
223
+
224
+ *Tobias Egli*
225
+
226
+ * Fix `check_all_foreign_keys_valid!` on PostgreSQL partitioned tables.
227
+
228
+ The validation query previously matched constraints by name across all tables in
229
+ the schema, marking unrelated constraints (including partition children) as not
230
+ validated. It now scopes the update to the specific table.
231
+
232
+ *Alexis Andrieu*
233
+
234
+ * Revert stable sorting of columns and indexes in serialized schema caches.
235
+
236
+ `SchemaCache#encode_with` no longer sorts a table's columns and indexes by name
237
+ when writing the cache, restoring the pre-8.1 behavior.
238
+
239
+ *Jean Boussier*
240
+
241
+ * Fix `only_columns` to enumerate columns in the `SELECT` clause.
242
+
243
+ Models using `only_columns` were generating `SELECT *` instead of an explicit
244
+ column list, which could select columns that were meant to be excluded.
245
+
246
+ *sarub0b0*
247
+
248
+ * Fix `eager_load` with a composite primary key and an explicit `select`.
249
+
250
+ The join dependency used the single primary key value to build aliases; for
251
+ composite primary keys it now uses the full array of primary key columns, so
252
+ eager loading with a custom `select` no longer drops the association records.
253
+
254
+ *fatkodima*
255
+
256
+ * Fix `find_signed` with a composite primary key.
257
+
258
+ `find_signed` now wraps the decoded id in an array so `find_by` matches against
259
+ all primary key columns, instead of silently returning `nil`.
260
+
261
+ *Kenta Ishizaki*
262
+
263
+ * Fix `with_connection(prevent_permanent_checkout: true)` clearing a sticky lease
264
+ that was already set by `lease_connection`.
265
+
266
+ A `with_connection` call with `prevent_permanent_checkout: true` no longer resets
267
+ `lease.sticky` to its previous value when the connection was already sticky, so
268
+ the connection remains checked out after the block.
269
+
270
+ *Ben Sheldon*
271
+
272
+ * Fix `reset_counters` to cast string ids to the primary key type.
273
+
274
+ `reset_counters` called with a string id (the form ids take from request params)
275
+ failed to find the record for both single and composite primary keys. Each id is
276
+ now cast against its own column type.
277
+
278
+ *fatkodima*
279
+
280
+ * Fix `belongs_to` presence validation for composite foreign keys.
281
+
282
+ The required-association validation only checked the first foreign key column.
283
+ For composite foreign keys it now considers the association present only when
284
+ none of the foreign key columns are `nil`, and changed when any of them changed.
285
+
286
+ *Felix Descoteaux*
287
+
288
+ * Fix preloading a `belongs_to` with a composite foreign key when some key columns
289
+ are `nil`.
290
+
291
+ The preloader now skips owners whose composite key has any `nil` component
292
+ instead of issuing a query that matches unrelated rows.
293
+
294
+ *Rose Wiegley*
295
+
296
+ * Fix `belongs_to` inverse matching with composite foreign keys.
297
+
298
+ `inversable?` compared a single foreign key column against a single primary key,
299
+ so `belongs_to` associations with composite foreign keys never matched their
300
+ inverse. Foreign key and primary key values are now compared component by
301
+ component.
302
+
303
+ *Martin Samson*
304
+
305
+ * Fix `validate_table_length!` to ignore the schema prefix on PostgreSQL.
306
+
307
+ Schema-qualified table names (e.g. `"schema.very_long_table_name"`) were
308
+ incorrectly rejected as exceeding the table name length limit because the schema
309
+ prefix was counted. The schema is now stripped before checking the length.
310
+
311
+ *fatkodima*
312
+
313
+ * Fix passing Trilogy SSL options to `mysql` and `mysqldump` commands.
314
+
315
+ *Ngan Pham*
316
+
317
+ * Fix `keepalive: false` in `database.yml` falling back to the
318
+ 600 second default.
319
+
320
+ *Joe Sak*
321
+
322
+ * Fix connection checkout blocking past the configured `checkout_timeout` while
323
+ waiting behind background connection maintenance.
324
+
325
+ `ConnectionPool#try_to_queue_for_background_connection` waited up to a
326
+ hardcoded 100 seconds for a connection tied up in maintenance (e.g. a keepalive
327
+ ping), regardless of the pool's configured `checkout_timeout`. If maintenance
328
+ stalled (a slow or unresponsive database server), checkout could block for far
329
+ longer than the caller asked for.
330
+
331
+ *Joe Sak*
332
+
333
+ * Fix encrypted fixtures for JSON columns.
334
+
335
+ Encrypted fixture values were serialized twice: once by the encrypted
336
+ attribute type and again by the column type when the rows were inserted.
337
+ For `json`/`jsonb` columns this stored the encrypted payload as a JSON
338
+ string, which then failed to decrypt. The payload is now handed back in
339
+ the column's cast form so it round-trips through the column type.
340
+
341
+ *Carlos Daniel Pohlod*
342
+
343
+ * Fix performance regression in `method_missing` for virtual SELECT alias
344
+ attributes.
345
+
346
+ Fixes #57183.
347
+
348
+ *Hammad Khan*
349
+
350
+ * Column defaults are no longer eagerly deserialized.
351
+
352
+ Restore the Active Record 8.0 and older behavior.
353
+ Column defaults are now whatever is returned by the database, generally a string.
354
+
355
+ This was changed in 8.1.0 to make the attribute API more consistent,
356
+ however it caused problems when the attribute type is later changed in the model.
357
+
358
+ Typically, some databases treat boolean columns as small integers, ance once the
359
+ default integer is casted to a boolean, it's not longer possible to change the
360
+ attribute back into an integer in the model.
361
+
362
+ Before:
94
363
 
95
364
  ```ruby
96
- Post.joins(:comments).pluck(:id, comments: :id)
97
- Post.joins(:comments).pluck("id", "comments" => "id")
365
+ Person.columns_hash["age"].default # => 0
98
366
  ```
99
367
 
368
+ After:
369
+
370
+ ```ruby
371
+ Person.columns_hash["age"].default # => "0"
372
+ ```
373
+
374
+ See https://github.com/rails/rails/issues/58292.
375
+
376
+ *Jean Boussier*
377
+
378
+ * Preserve the declared parent order when dumping PostgreSQL `INHERITS` table options.
379
+
380
+ The schema dumper read a table's inherited parents without an `ORDER BY`, so the
381
+ `INHERITS (...)` clause in `schema.rb` could be emitted in a nondeterministic order.
382
+ Parents are now ordered by `pg_inherits.inhseqno`.
383
+
384
+ *viralpraxis*
385
+
386
+ * Preserve SQLite partial and expression indexes when their stored SQL contains newlines.
387
+
388
+ *Javi Ramírez*
389
+
390
+ * Only use multi statement for `SET TRANSACTION ISOLATION LEVEL; BEGIN` if
391
+ MySQL connection is configured to use multi statement.
392
+
393
+ *Eliseu Daroit*, *Hartley McGuire*, *Matthew Draper*
394
+
395
+ * Mark a connection unverified when a non-`StandardError` interrupts a query.
396
+
397
+ A query interrupted by a non-`StandardError`, such as a fiber scheduler's
398
+ cancel or a `Timeout`, leaves the connection desynced. Active Record
399
+ previously returned it to the pool still marked verified, so the next
400
+ checkout reused it and failed on a dead socket. The connection is now
401
+ marked unverified, so it re-verifies and reconnects before its next use.
402
+
403
+ *Eliseu Daroit*
404
+
405
+ * Fix `has_many` and `has_one` associations on a new record returning an empty
406
+ result when the owner has a composite primary key, even when every primary
407
+ key column is populated.
408
+
409
+ *Jean-Samuel Aubry-Guzzi*
410
+
411
+ * Fix inverse association matching for `has_many` and `has_one` associations
412
+ with association-specific composite primary keys.
413
+
414
+ *Hugo Vacher*
415
+
416
+ * Fix `ActiveRecord::Base.with` to call `Object#with` when given a block.
417
+
418
+ The class-level `with` delegates to the relation's CTE query method, which
419
+ does not accept a block. When a block is passed, it now delegates to
420
+ `Object#with` instead, so it can be used to temporarily set attributes on
421
+ the class for the duration of the block.
422
+
423
+ *Janko Marohnić*
424
+
425
+ * Fix `serialize` with `coder: ActiveRecord::Coders::JSON` silently double-encoding
426
+ a native `json`/`jsonb` column.
427
+
428
+ *Kenta Ishizaki*
429
+
430
+ * Fix `update_all` / `delete_all` ignoring `group` and `having`, updating or
431
+ deleting every row in the table instead of only the rows that satisfy the
432
+ `HAVING` clause.
433
+
434
+ *Kenta Ishizaki*
435
+
436
+ * Fix `ActiveRecord::Relation#cache_key` / `cache_version` for a loaded collection
437
+ containing a record without a timestamp.
438
+
439
+ *Kenta Ishizaki*
440
+
441
+ * Fix model `filter_attributes` to not add useless filters into `config.filter_parameters`.
442
+
443
+ Proc and Regexp filters would be improperly turned into string matchers.
444
+ Now they are skipped.
445
+
446
+ *Jonathan Calvert*
447
+
448
+ * Fix `ActiveRecord::MessagePack` serialization raising `NoMethodError`
449
+ for any record with a populated `time` column, which made such records
450
+ uncacheable through the MessagePack cache serializer.
451
+
452
+ *Kenta Ishizaki*
453
+
454
+ * Fix replacing or clearing a polymorphic `has_one` leaving a stale type column
455
+ on the removed record.
456
+
457
+ *Kenta Ishizaki*
458
+
459
+ * Fix `accepts_nested_attributes_for` `:limit` miscounting a single-record hash.
460
+
461
+ *Kenta Ishizaki*
462
+
463
+ * Fix `rename_index` to preserve a partial index's `WHERE` for SQLite and older MySQL/MariaDB versions.
464
+
465
+ *Kenta Ishizaki*
466
+
467
+ * Fix PostgreSQL `foreign_keys` returning a corrupted `to_table` for a foreign key
468
+ that references a table in a quoted schema.
469
+
470
+ *Kenta Ishizaki*
471
+
472
+ * Fix grouped calculations (e.g. `count`) grouped by a `belongs_to` association
473
+ that points to a composite primary key model.
474
+
475
+ `Book.group(:order).count`, where `Book belongs_to :order` and `Order` has a
476
+ composite primary key, raised `ArgumentError: Expected corresponding value
477
+ for ["shop_id", "id"] to be an Array`. The grouped result is now keyed by the
478
+ associated records as it already is for single-column keys.
479
+
480
+ *Kenta Ishizaki*
481
+
482
+ * Fix `has_many`/`has_one :through` associations with `disable_joins: true`
483
+ silently returning an empty result when the source points to a composite
484
+ primary key model.
485
+
486
+ *Kenta Ishizaki*
487
+
488
+ * Fix collection association `ids=` writers (e.g. `author.book_ids=`) raising
489
+ `ActiveRecord::RecordNotFound` for existing records when a composite primary
490
+ key model is assigned string ids (the shape ids take from request params).
491
+
492
+ Each id component is now cast against its own column type, instead of casting
493
+ the whole tuple against an array of column names, which `type_for_attribute`
494
+ can't resolve to a real type (so the cast was a no-op and the string ids
495
+ never matched the loaded records).
496
+
497
+ *Kenta Ishizaki*
498
+
499
+ * Fix `find` with multiple composite primary key ids passed as strings
500
+ silently returning `[]`.
501
+
502
+ `Model.find([["1", "10"], ["1", "20"]])` (the shape ids take when they come
503
+ from request parameters) returned an empty array instead of the records and
504
+ without raising `RecordNotFound`. The ids were cast against the array of key
505
+ column names as a whole — which is a no-op — so the string tuples never
506
+ compared equal to the records' integer ids when ordering the result. Each
507
+ component is now cast against its own column type, matching the documented
508
+ coercion already performed for single-column keys.
509
+
510
+ *Kenta Ishizaki*
511
+
512
+ * Fix PostgreSQL `daterange` / `tsrange` / `tstzrange` schema dump producing
513
+ invalid Ruby.
514
+
515
+ The schema dumper used to render range defaults via `Range#inspect`, which
516
+ falls back to `Date#inspect` / `Time#inspect` for the bounds. The resulting
517
+ `schema.rb` literal (for example `default: Mon, 01 Jan 2024..Wed, 01 Jan 2025`)
518
+ raised a `SyntaxError` on `db:schema:load`. The bounds are now rendered via
519
+ the subtype's `type_cast_for_schema`, so date and timestamp range defaults
520
+ round-trip through `schema.rb` like any other column.
521
+
522
+ *Kenta Ishizaki*
523
+
524
+ * Fix deadlock when pool-less connection materializes while fetching database
525
+ server version.
526
+
527
+ *Hartley McGuire*
528
+
529
+ * Fix PostgreSQL range columns corrupting bounds that contain a comma.
530
+
531
+ `PostgreSQL::OID::Range#extract_bounds` split a range's textual
532
+ representation on the first comma, but PostgreSQL double-quotes any bound
533
+ that itself contains a comma (e.g. a range over a text subtype). A value
534
+ such as `["a,b","c,d")` was therefore split inside the first quoted bound
535
+ and read back with both endpoints corrupted. The split now skips over
536
+ double-quoted segments when locating the separating comma.
537
+
538
+ *Kenta Ishizaki*
539
+
540
+ * Fix store accessor `*_change` and `saved_change_to_*` reporting a change
541
+ for unchanged keys.
542
+
543
+ When one key of a `store` column changed, the generated `<key>_change` and
544
+ `saved_change_to_<key>` methods returned a `[value, value]` pair for the
545
+ column's *other* keys too, even though their matching `<key>_changed?` /
546
+ `saved_change_to_<key>?` correctly returned `false` and standard attribute
547
+ `_change` / `saved_change_to_*` methods return `nil` when unchanged. The
548
+ methods now return `nil` unless that specific key actually changed,
549
+ matching their predicate counterparts.
550
+
551
+ *Kenta Ishizaki*
552
+
553
+ * Reduce number of queries when introspecting MySQL/MariaDB tables.
554
+
555
+ A fix to support MariaDB function defaults caused the MySQL adapters to
556
+ issue one query per column when introspecting tables.
557
+
558
+ Now it only issue a single extra query per table, and only when using MariaDB.
559
+
560
+ *Iliana Hadzhiatanasova*
561
+
562
+ * Fully qualify query criterias in `has_and_belongs_to_many` associations.
563
+
564
+ Ensure that scopes with the same column name but in different tables aren't lost
565
+ when following an HABTM association.
566
+
567
+ *Philip Maina*
568
+
569
+ * Fix Active Record Pool Reaper thread leak after `Parallelization#shutdown`.
570
+
571
+ After parallelized test runs, the parent process leaked the Active Record Pool
572
+ Reaper thread, holding open connection pools and file descriptors for up to
573
+ `reaping_frequency` seconds (or indefinitely if never reaped).
574
+
575
+ Three fixes: `Parallelization#shutdown` now calls `run_cleanup_hooks`;
576
+ Active Record registers a cleanup hook that discards all connection pools; and
577
+ `ConnectionPool#discard!` now calls `Reaper.discard_pool`, which immediately
578
+ kills and joins the reaper thread when no pools remain at that frequency.
579
+
580
+ *Ruy Rocha*
581
+
582
+ * Fix duplicate `WHERE` conditions in `create_or_find_by`.
583
+
584
+ When `create_or_find_by` catches a `RecordNotUnique` error and retries the query, it now uses `rewhere` and `take!` to prevent duplicating existing scope conditions.
585
+
586
+ *Yavor Dashev*
587
+
588
+ * Fix `ActiveRecord::QueryMethods#in_order_of` when passing an out-of-range Integer
589
+
590
+ To match the behavior of the `Enumerable` version, `in_order_of` now ignores an out-of-range Integer.
591
+
592
+ *tejanium*
593
+
594
+ * Revert alphabetical sorting of table columns inside `schema.rb`.
595
+
596
+ Alphabetical sorting of table columns inside the schema creates improper production tables when using `db:prepare`.
597
+
598
+ *Bert McCutchen*
599
+
600
+ * Fix `strict_loading` violations ignored when using `pluck`
601
+
602
+ *Johnson Chan*
603
+
604
+ * Fix incorrect callback execution order when `config.active_record.run_after_transaction_callbacks_in_order_defined = true`
605
+ and using `after_commit` and `after_rollback` callbacks with `prepend: true`.
606
+
100
607
  *Joshua Young*
101
608
 
102
- * Make Float distinguish between `float4` and `float8` in PostgreSQL.
609
+ * Fix `becomes` to preserve `marked_for_destruction?`.
103
610
 
104
- Fixes #52742
611
+ *Vincent Robert*
105
612
 
106
- *Ryota Kitazawa*, *Takayuki Nagatomi*
613
+ * MySQL error 1046 (`ER_NO_DB_ERROR: No database selected`) is now retryable as a `ConnectionFailed` exception
107
614
 
615
+ *Clay Harmon*
108
616
 
109
- ## Rails 8.0.0.beta1 (September 26, 2024) ##
110
617
 
111
- * Allow `drop_table` to accept an array of table names.
618
+ ## Rails 8.1.3.1 (July 29, 2026) ##
112
619
 
113
- This will let you to drop multiple tables in a single call.
620
+ * No changes.
114
621
 
115
- ```ruby
116
- ActiveRecord::Base.lease_connection.drop_table(:users, :posts)
117
- ```
622
+
623
+ ## Rails 8.1.3 (March 24, 2026) ##
624
+
625
+ * Fix `insert_all` and `upsert_all` log message when called on anonymous classes.
118
626
 
119
627
  *Gabriel Sobrinho*
120
628
 
121
- * Add support for PostgreSQL `IF NOT EXISTS` via the `:if_not_exists` option
122
- on the `add_enum_value` method.
629
+ * Respect `ActiveRecord::SchemaDumper.ignore_tables` when dumping SQLite virtual tables.
123
630
 
124
- *Ariel Rzezak*
631
+ *Hans Schnedlitz*
125
632
 
126
- * When running `db:migrate` on a fresh database, load the databases schemas before running migrations.
633
+ * Restore previous instrumenter after `execute_or_skip`
127
634
 
128
- *Andrew Novoselac*, *Marek Kasztelnik*
635
+ `FutureResult#execute_or_skip` replaces the thread's instrumenter with an
636
+ `EventBuffer` to collect events published during async query execution.
637
+ If the global async executor is saturated and the `caller_runs` fallback
638
+ executes the task on the calling thread, we need to make sure the previous
639
+ instrumenter is restored or the stale `EventBuffer` would stay in place and
640
+ permanently swallow all subsequent `sql.active_record` notifications on
641
+ that thread.
129
642
 
130
- * Fix an issue where `.left_outer_joins` used with multiple associations that have
131
- the same child association but different parents does not join all parents.
643
+ *Rosa Gutierrez*
132
644
 
133
- Previously, using `.left_outer_joins` with the same child association would only join one of the parents.
645
+ * Bump the minimum PostgreSQL version to 9.5, due to usage of `array_position` function.
134
646
 
135
- Now it will correctly join both parents.
647
+ *Ivan Kuchin*
136
648
 
137
- Fixes #41498.
649
+ * Fix Ruby 4.0 delegator warning when calling inspect on ActiveRecord::Type::Serialized.
138
650
 
139
- *Garrett Blehm*
651
+ *Hammad Khan*
140
652
 
141
- * Deprecate `unsigned_float` and `unsigned_decimal` short-hand column methods.
653
+ * Fix support for table names containing hyphens.
142
654
 
143
- As of MySQL 8.0.17, the UNSIGNED attribute is deprecated for columns of type FLOAT, DOUBLE,
144
- and DECIMAL. Consider using a simple CHECK constraint instead for such columns.
655
+ *Evgeniy Demin*
145
656
 
146
- https://dev.mysql.com/doc/refman/8.0/en/numeric-type-syntax.html
657
+ * Fix column deduplication for SQLite3 and PostgreSQL virtual (generated) columns.
147
658
 
148
- *Ryuta Kamizono*
659
+ `Column#==` and `Column#hash` now account for `virtual?` so that the
660
+ `Deduplicable` registry does not treat a generated column and a regular
661
+ column with the same name and type as identical. Previously, if a
662
+ generated column was registered first, a regular column on a different
663
+ table could be deduplicated to the generated instance, silently
664
+ excluding it from INSERT/UPDATE statements.
149
665
 
150
- * Drop MySQL 5.5 support.
666
+ *Jay Huber*
151
667
 
152
- MySQL 5.5 is the only version that does not support datetime with precision,
153
- which we have supported in the core. Now we support MySQL 5.6.4 or later, which
154
- is the first version to support datetime with precision.
668
+ * Fix PostgreSQL schema dumping to handle schema-qualified table names in foreign_key references that span different schemas.
155
669
 
156
- *Ryuta Kamizono*
670
+ # before
671
+ add_foreign_key "hst.event_log_attributes", "hst.event_logs" # emits correctly because they're in the same schema (hst)
672
+ add_foreign_key "hst.event_log_attributes", "hst.usr.user_profiles", column: "created_by_id" # emits hst.user.* when user.* is expected
673
+
674
+ # after
675
+ add_foreign_key "hst.event_log_attributes", "hst.event_logs"
676
+ add_foreign_key "hst.event_log_attributes", "usr.user_profiles", column: "created_by_id"
677
+
678
+ *Chiperific*
679
+
680
+
681
+ ## Rails 8.1.2.1 (March 23, 2026) ##
682
+
683
+ * No changes.
684
+
685
+
686
+ ## Rails 8.1.2 (January 08, 2026) ##
687
+
688
+ * Fix counting cached queries in `ActiveRecord::RuntimeRegistry`.
157
689
 
158
- * Make Active Record asynchronous queries compatible with transactional fixtures.
690
+ *fatkodima*
691
+
692
+ * Fix merging relations with arel equality predicates with null relations.
693
+
694
+ *fatkodima*
695
+
696
+ * Fix SQLite3 schema dump for non-autoincrement integer primary keys.
697
+
698
+ Previously, `schema.rb` should incorrectly restore that table with an auto incrementing
699
+ primary key.
700
+
701
+ *Chris Hasiński*
702
+
703
+ * Fix PostgreSQL `schema_search_path` not being reapplied after `reset!` or `reconnect!`.
704
+
705
+ The `schema_search_path` configured in `database.yml` is now correctly
706
+ reapplied instead of falling back to PostgreSQL defaults.
707
+
708
+ *Tobias Egli*
709
+
710
+ * Restore the ability of enum to be foats.
711
+
712
+ ```ruby
713
+ enum :rating, { low: 0.0, medium: 0.5, high: 1.0 },
714
+ ```
715
+
716
+ In Rails 8.1.0, enum values are eagerly validated, and floats weren't expected.
717
+
718
+ *Said Kaldybaev*
159
719
 
160
- Previously transactional fixtures would disable asynchronous queries, because transactional
161
- fixtures impose all queries use the same connection.
720
+ * Ensure batched preloaded associations accounts for klass when grouping to avoid issues with STI.
162
721
 
163
- Now asynchronous queries will use the connection pinned by transactional fixtures, and behave
164
- much closer to production.
722
+ *zzak*, *Stjepan Hadjic*
723
+
724
+ * Fix `ActiveRecord::SoleRecordExceeded#record` to return the relation.
725
+
726
+ This was the case until Rails 7.2, but starting from 8.0 it
727
+ started mistakenly returning the model class.
728
+
729
+ *Jean Boussier*
730
+
731
+ * Improve PostgreSQLAdapter resilience to Timeout.timeout.
732
+
733
+ Better handle asynchronous exceptions being thrown inside
734
+ the `reconnect!` method.
735
+
736
+ This may fixes some deep errors such as:
737
+
738
+ ```
739
+ undefined method `key?' for nil:NilClass (NoMethodError)
740
+ if !type_map.key?(oid)
741
+ ```
165
742
 
166
743
  *Jean Boussier*
167
744
 
168
- * Deserialize binary data before decrypting
745
+ * Fix structured events for Active Record was not being emitted.
746
+
747
+ *Yuji Yaginuma*
748
+
749
+ * Fix `eager_load` when loading `has_many` assocations with composite primary keys.
169
750
 
170
- This ensures that we call `PG::Connection.unescape_bytea` on PostgreSQL before decryption.
751
+ This would result in some records being loaded multiple times.
171
752
 
172
- *Donal McBreen*
753
+ *Martin-Alexander*
173
754
 
174
- * Ensure `ActiveRecord::Encryption.config` is always ready before access.
175
755
 
176
- Previously, `ActiveRecord::Encryption` configuration was deferred until `ActiveRecord::Base`
177
- was loaded. Therefore, accessing `ActiveRecord::Encryption.config` properties before
178
- `ActiveRecord::Base` was loaded would give incorrect results.
756
+ ## Rails 8.1.1 (October 28, 2025) ##
179
757
 
180
- `ActiveRecord::Encryption` now has its own loading hook so that its configuration is set as
181
- soon as needed.
758
+ * No changes.
182
759
 
183
- When `ActiveRecord::Base` is loaded, even lazily, it in turn triggers the loading of
184
- `ActiveRecord::Encryption`, thus preserving the original behavior of having its config ready
185
- before any use of `ActiveRecord::Base`.
186
760
 
187
- *Maxime Réty*
761
+ ## Rails 8.1.0 (October 22, 2025) ##
188
762
 
189
- * Add `TimeZoneConverter#==` method, so objects will be properly compared by
190
- their type, scale, limit & precision.
763
+ * Fix SQLite3 data loss during table alterations with CASCADE foreign keys.
191
764
 
192
- Address #52699.
765
+ When altering a table in SQLite3 that is referenced by child tables with
766
+ `ON DELETE CASCADE` foreign keys, ActiveRecord would silently delete all
767
+ data from the child tables. This occurred because SQLite requires table
768
+ recreation for schema changes, and during this process the original table
769
+ is temporarily dropped, triggering CASCADE deletes on child tables.
770
+
771
+ The root cause was incorrect ordering of operations. The original code
772
+ wrapped `disable_referential_integrity` inside a transaction, but
773
+ `PRAGMA foreign_keys` cannot be modified inside a transaction in SQLite -
774
+ attempting to do so simply has no effect. This meant foreign keys remained
775
+ enabled during table recreation, causing CASCADE deletes to fire.
776
+
777
+ The fix reverses the order to follow the official SQLite 12-step ALTER TABLE
778
+ procedure: `disable_referential_integrity` now wraps the transaction instead
779
+ of being wrapped by it. This ensures foreign keys are properly disabled
780
+ before the transaction starts and re-enabled after it commits, preventing
781
+ CASCADE deletes while maintaining data integrity through atomic transactions.
193
782
 
194
783
  *Ruy Rocha*
195
784
 
196
- * Add support for SQLite3 full-text-search and other virtual tables.
785
+ * Add replicas to test database parallelization setup.
197
786
 
198
- Previously, adding sqlite3 virtual tables messed up `schema.rb`.
787
+ Setup and configuration of databases for parallel testing now includes replicas.
199
788
 
200
- Now, virtual tables can safely be added using `create_virtual_table`.
789
+ This fixes an issue when using a replica database, database selector middleware,
790
+ and non-transactional tests, where integration tests running in parallel would select
791
+ the base test database, i.e. `db_test`, instead of the numbered parallel worker database,
792
+ i.e. `db_test_{n}`.
201
793
 
202
- *Zacharias Knudsen*
794
+ *Adam Maas*
203
795
 
204
- * Support use of alternative database interfaces via the `database_cli` ActiveRecord configuration option.
796
+ * Support virtual (not persisted) generated columns on PostgreSQL 18+
797
+
798
+ PostgreSQL 18 introduces virtual (not persisted) generated columns,
799
+ which are now the default unless the `stored: true` option is explicitly specified on PostgreSQL 18+.
205
800
 
206
801
  ```ruby
207
- Rails.application.configure do
208
- config.active_record.database_cli = { postgresql: "pgcli" }
802
+ create_table :users do |t|
803
+ t.string :name
804
+ t.virtual :lower_name, type: :string, as: "LOWER(name)", stored: false
805
+ t.virtual :name_length, type: :integer, as: "LENGTH(name)"
209
806
  end
210
807
  ```
211
808
 
212
- *T S Vallender*
809
+ *Yasuo Honda*
213
810
 
214
- * Add support for dumping table inheritance and native partitioning table definitions for PostgeSQL adapter
811
+ * Optimize schema dumping to prevent duplicate file generation.
215
812
 
216
- *Justin Talbott*
813
+ `ActiveRecord::Tasks::DatabaseTasks.dump_all` now tracks which schema files
814
+ have already been dumped and skips dumping the same file multiple times.
815
+ This improves performance when multiple database configurations share the
816
+ same schema dump path.
217
817
 
218
- * Add support for `ActiveRecord::Point` type casts using `Hash` values
818
+ *Mikey Gough*, *Hartley McGuire*
219
819
 
220
- This allows `ActiveRecord::Point` to be cast or serialized from a hash
221
- with `:x` and `:y` keys of numeric values, mirroring the functionality of
222
- existing casts for string and array values. Both string and symbol keys are
223
- supported.
820
+ * Add structured events for Active Record:
821
+ - `active_record.strict_loading_violation`
822
+ - `active_record.sql`
224
823
 
824
+ *Gannon McGibbon*
825
+
826
+ * Add support for integer shard keys.
225
827
  ```ruby
226
- class PostgresqlPoint < ActiveRecord::Base
227
- attribute :x, :point
228
- attribute :y, :point
229
- attribute :z, :point
828
+ # Now accepts symbols as shard keys.
829
+ ActiveRecord::Base.connects_to(shards: {
830
+ 1: { writing: :primary_shard_one, reading: :primary_shard_one },
831
+ 2: { writing: :primary_shard_two, reading: :primary_shard_two},
832
+ })
833
+
834
+ ActiveRecord::Base.connected_to(shard: 1) do
835
+ # ..
230
836
  end
837
+ ```
231
838
 
232
- val = PostgresqlPoint.new({
233
- x: '(12.34, -43.21)',
234
- y: [12.34, '-43.21'],
235
- z: {x: '12.34', y: -43.21}
236
- })
237
- ActiveRecord::Point.new(12.32, -43.21) == val.x == val.y == val.z
839
+ *Nony Dutton*
840
+
841
+ * Add `ActiveRecord::Base.only_columns`
842
+
843
+ Similar in use case to `ignored_columns` but listing columns to consider rather than the ones
844
+ to ignore.
845
+
846
+ Can be useful when working with a legacy or shared database schema, or to make safe schema change
847
+ in two deploys rather than three.
848
+
849
+ *Anton Kandratski*
850
+
851
+ * Use `PG::Connection#close_prepared` (protocol level Close) to deallocate
852
+ prepared statements when available.
853
+
854
+ To enable its use, you must have pg >= 1.6.0, libpq >= 17, and a PostgreSQL
855
+ database version >= 17.
856
+
857
+ *Hartley McGuire*, *Andrew Jackson*
858
+
859
+ * Fix query cache for pinned connections in multi threaded transactional tests
860
+
861
+ When a pinned connection is used across separate threads, they now use a separate cache store
862
+ for each thread.
863
+
864
+ This improve accuracy of system tests, and any test using multiple threads.
865
+
866
+ *Heinrich Lee Yu*, *Jean Boussier*
867
+
868
+ * Fix time attribute dirty tracking with timezone conversions.
869
+
870
+ Time-only attributes now maintain a fixed date of 2000-01-01 during timezone conversions,
871
+ preventing them from being incorrectly marked as changed due to date shifts.
872
+
873
+ This fixes an issue where time attributes would be marked as changed when setting the same time value
874
+ due to timezone conversion causing internal date shifts.
875
+
876
+ *Prateek Choudhary*
877
+
878
+ * Skip calling `PG::Connection#cancel` in `cancel_any_running_query`
879
+ when using libpq >= 18 with pg < 1.6.0, due to incompatibility.
880
+ Rollback still runs, but may take longer.
881
+
882
+ *Yasuo Honda*, *Lars Kanis*
883
+
884
+ * Don't add `id_value` attribute alias when attribute/column with that name already exists.
885
+
886
+ *Rob Lewis*
887
+
888
+ * Remove deprecated `:unsigned_float` and `:unsigned_decimal` column methods for MySQL.
889
+
890
+ *Rafael Mendonça França*
891
+
892
+ * Remove deprecated `:retries` option for the SQLite3 adapter.
893
+
894
+ *Rafael Mendonça França*
895
+
896
+ * Introduce new database configuration options `keepalive`, `max_age`, and
897
+ `min_connections` -- and rename `pool` to `max_connections` to match.
898
+
899
+ There are no changes to default behavior, but these allow for more specific
900
+ control over pool behavior.
901
+
902
+ *Matthew Draper*, *Chris AtLee*, *Rachael Wright-Munn*
903
+
904
+ * Move `LIMIT` validation from query generation to when `limit()` is called.
905
+
906
+ *Hartley McGuire*, *Shuyang*
907
+
908
+ * Add `ActiveRecord::CheckViolation` error class for check constraint violations.
909
+
910
+ *Ryuta Kamizono*
911
+
912
+ * Add `ActiveRecord::ExclusionViolation` error class for exclusion constraint violations.
913
+
914
+ When an exclusion constraint is violated in PostgreSQL, the error will now be raised
915
+ as `ActiveRecord::ExclusionViolation` instead of the generic `ActiveRecord::StatementInvalid`,
916
+ making it easier to handle these specific constraint violations in application code.
917
+
918
+ This follows the same pattern as other constraint violation error classes like
919
+ `RecordNotUnique` for unique constraint violations and `InvalidForeignKey` for
920
+ foreign key constraint violations.
921
+
922
+ *Ryuta Kamizono*
923
+
924
+ * Attributes filtered by `filter_attributes` will now also be filtered by `filter_parameters`
925
+ so sensitive information is not leaked.
926
+
927
+ *Jill Klang*
928
+
929
+ * Add `connection.current_transaction.isolation` API to check current transaction's isolation level.
930
+
931
+ Returns the isolation level if it was explicitly set via the `isolation:` parameter
932
+ or through `ActiveRecord.with_transaction_isolation_level`, otherwise returns `nil`.
933
+ Nested transactions return the parent transaction's isolation level.
934
+
935
+ ```ruby
936
+ # Returns nil when no transaction
937
+ User.connection.current_transaction.isolation # => nil
938
+
939
+ # Returns explicitly set isolation level
940
+ User.transaction(isolation: :serializable) do
941
+ User.connection.current_transaction.isolation # => :serializable
942
+ end
943
+
944
+ # Returns nil when isolation not explicitly set
945
+ User.transaction do
946
+ User.connection.current_transaction.isolation # => nil
947
+ end
948
+
949
+ # Nested transactions inherit parent's isolation
950
+ User.transaction(isolation: :read_committed) do
951
+ User.transaction do
952
+ User.connection.current_transaction.isolation # => :read_committed
953
+ end
954
+ end
238
955
  ```
239
956
 
240
- *Stephen Drew*
957
+ *Kir Shatrov*
241
958
 
242
- * Replace `SQLite3::Database#busy_timeout` with `#busy_handler_timeout=`.
959
+ * Fix `#merge` with `#or` or `#and` and a mixture of attributes and SQL strings resulting in an incorrect query.
243
960
 
244
- Provides a non-GVL-blocking, fair retry interval busy handler implementation.
961
+ ```ruby
962
+ base = Comment.joins(:post).where(user_id: 1).where("recent = 1")
963
+ puts base.merge(base.where(draft: true).or(Post.where(archived: true))).to_sql
964
+ ```
245
965
 
246
- *Stephen Margheim*
966
+ Before:
967
+
968
+ ```SQL
969
+ SELECT "comments".* FROM "comments"
970
+ INNER JOIN "posts" ON "posts"."id" = "comments"."post_id"
971
+ WHERE (recent = 1)
972
+ AND (
973
+ "comments"."user_id" = 1
974
+ AND (recent = 1)
975
+ AND "comments"."draft" = 1
976
+ OR "posts"."archived" = 1
977
+ )
978
+ ```
247
979
 
248
- * SQLite3Adapter: Translate `SQLite3::BusyException` into `ActiveRecord::StatementTimeout`.
980
+ After:
981
+
982
+ ```SQL
983
+ SELECT "comments".* FROM "comments"
984
+ INNER JOIN "posts" ON "posts"."id" = "comments"."post_id"
985
+ WHERE "comments"."user_id" = 1
986
+ AND (recent = 1)
987
+ AND (
988
+ "comments"."user_id" = 1
989
+ AND (recent = 1)
990
+ AND "comments"."draft" = 1
991
+ OR "posts"."archived" = 1
992
+ )
993
+ ```
249
994
 
250
- *Matthew Nguyen*
995
+ *Joshua Young*
251
996
 
252
- * Include schema name in `enable_extension` statements in `db/schema.rb`.
997
+ * Make schema dumper to account for `ActiveRecord.dump_schemas` when dumping in `:ruby` format.
253
998
 
254
- The schema dumper will now include the schema name in generated
255
- `enable_extension` statements if they differ from the current schema.
999
+ *fatkodima*
256
1000
 
257
- For example, if you have a migration:
1001
+ * Add `:touch` option to `update_column`/`update_columns` methods.
258
1002
 
259
1003
  ```ruby
260
- enable_extension "heroku_ext.pgcrypto"
261
- enable_extension "pg_stat_statements"
1004
+ # Will update :updated_at/:updated_on alongside :nice column.
1005
+ user.update_column(:nice, true, touch: true)
1006
+
1007
+ # Will update :updated_at/:updated_on alongside :last_ip column
1008
+ user.update_columns(last_ip: request.remote_ip, touch: true)
262
1009
  ```
263
1010
 
264
- then the generated schema dump will also contain:
1011
+ *Dmitrii Ivliev*
1012
+
1013
+ * Optimize Active Record batching further when using ranges.
1014
+
1015
+ Tested on a PostgreSQL table with 10M records and batches of 10k records, the generation
1016
+ of relations for the 1000 batches was `4.8x` faster (`6.8s` vs. `1.4s`), used `900x`
1017
+ less bandwidth (`180MB` vs. `0.2MB`) and allocated `45x` less memory (`490MB` vs. `11MB`).
1018
+
1019
+ *Maxime Réty*, *fatkodima*
1020
+
1021
+ * Include current character length in error messages for index and table name length validations.
1022
+
1023
+ *Joshua Young*
1024
+
1025
+ * Add `rename_schema` method for PostgreSQL.
1026
+
1027
+ *T S Vallender*
1028
+
1029
+ * Implement support for deprecating associations:
265
1030
 
266
1031
  ```ruby
267
- enable_extension "heroku_ext.pgcrypto"
268
- enable_extension "pg_stat_statements"
1032
+ has_many :posts, deprecated: true
269
1033
  ```
270
1034
 
271
- *Tony Novak*
1035
+ With that, Active Record will report any usage of the `posts` association.
272
1036
 
273
- * Fix `ActiveRecord::Encryption::EncryptedAttributeType#type` to return
274
- actual cast type.
1037
+ Three reporting modes are supported (`:warn`, `:raise`, and `:notify`), and
1038
+ backtraces can be enabled or disabled. Defaults are `:warn` mode and
1039
+ disabled backtraces.
275
1040
 
276
- *Vasiliy Ermolovich*
1041
+ Please, check the docs for further details.
1042
+
1043
+ *Xavier Noria*
277
1044
 
278
- * SQLite3Adapter: Bulk insert fixtures.
1045
+ * PostgreSQL adapter create DB now supports `locale_provider` and `locale`.
279
1046
 
280
- Previously one insert command was executed for each fixture, now they are
281
- aggregated in a single bulk insert command.
1047
+ *Bengt-Ove Hollaender*
282
1048
 
283
- *Lázaro Nixon*
1049
+ * Use ntuples to populate row_count instead of count for Postgres
284
1050
 
285
- * PostgreSQLAdapter: Allow `disable_extension` to be called with schema-qualified name.
1051
+ *Jonathan Calvert*
286
1052
 
287
- For parity with `enable_extension`, the `disable_extension` method can be called with a schema-qualified
288
- name (e.g. `disable_extension "myschema.pgcrypto"`). Note that PostgreSQL's `DROP EXTENSION` does not
289
- actually take a schema name (unlike `CREATE EXTENSION`), so the resulting SQL statement will only name
290
- the extension, e.g. `DROP EXTENSION IF EXISTS "pgcrypto"`.
1053
+ * Fix checking whether an unpersisted record is `include?`d in a strictly
1054
+ loaded `has_and_belongs_to_many` association.
291
1055
 
292
- *Tony Novak*
1056
+ *Hartley McGuire*
293
1057
 
294
- * Make `create_schema` / `drop_schema` reversible in migrations.
1058
+ * Add ability to change transaction isolation for all pools within a block.
295
1059
 
296
- Previously, `create_schema` and `drop_schema` were irreversible migration operations.
1060
+ This functionality is useful if your application needs to change the database
1061
+ transaction isolation for a request or action.
297
1062
 
298
- *Tony Novak*
1063
+ Calling `ActiveRecord.with_transaction_isolation_level(level) {}` in an around filter or
1064
+ middleware will set the transaction isolation for all pools accessed within the block,
1065
+ but not for the pools that aren't.
299
1066
 
300
- * Support batching using custom columns.
1067
+ This works with explicit and implicit transactions:
301
1068
 
302
1069
  ```ruby
303
- Product.in_batches(cursor: [:shop_id, :id]) do |relation|
304
- # do something with relation
1070
+ ActiveRecord.with_transaction_isolation_level(:read_committed) do
1071
+ Tag.transaction do # opens a transaction explicitly
1072
+ Tag.create!
1073
+ end
305
1074
  end
306
1075
  ```
307
1076
 
308
- *fatkodima*
1077
+ ```ruby
1078
+ ActiveRecord.with_transaction_isolation_level(:read_committed) do
1079
+ Tag.create! # opens a transaction implicitly
1080
+ end
1081
+ ```
1082
+
1083
+ *Eileen M. Uchitelle*
309
1084
 
310
- * Use SQLite `IMMEDIATE` transactions when possible.
1085
+ * Raise `ActiveRecord::MissingRequiredOrderError` when order dependent finder methods (e.g. `#first`, `#last`) are
1086
+ called without `order` values on the relation, and the model does not have any order columns (`implicit_order_column`,
1087
+ `query_constraints`, or `primary_key`) to fall back on.
311
1088
 
312
- Transactions run against the SQLite3 adapter default to IMMEDIATE mode to improve concurrency support and avoid busy exceptions.
1089
+ This change will be introduced with a new framework default for Rails 8.1, and the current behavior of not raising
1090
+ an error has been deprecated with the aim of removing the configuration option in Rails 8.2.
313
1091
 
314
- *Stephen Margheim*
1092
+ ```ruby
1093
+ config.active_record.raise_on_missing_required_finder_order_columns = true
1094
+ ```
315
1095
 
316
- * Raise specific exception when a connection is not defined.
1096
+ *Joshua Young*
317
1097
 
318
- The new `ConnectionNotDefined` exception provides connection name, shard and role accessors indicating the details of the connection that was requested.
1098
+ * `:class_name` is now invalid in polymorphic `belongs_to` associations.
319
1099
 
320
- *Hana Harencarova*, *Matthew Draper*
1100
+ Reason is `:class_name` does not make sense in those associations because
1101
+ the class name of target records is dynamic and stored in the type column.
321
1102
 
322
- * Delete the deprecated constant `ActiveRecord::ImmutableRelation`.
1103
+ Existing polymorphic associations setting this option can just delete it.
1104
+ While it did not raise, it had no effect anyway.
323
1105
 
324
1106
  *Xavier Noria*
325
1107
 
326
- * Fix duplicate callback execution when child autosaves parent with `has_one` and `belongs_to`.
1108
+ * Add support for multiple databases to `db:migrate:reset`.
327
1109
 
328
- Before, persisting a new child record with a new associated parent record would run `before_validation`,
329
- `after_validation`, `before_save` and `after_save` callbacks twice.
1110
+ *Joé Dupuis*
330
1111
 
331
- Now, these callbacks are only executed once as expected.
1112
+ * Add `affected_rows` to `ActiveRecord::Result`.
332
1113
 
333
- *Joshua Young*
1114
+ *Jenny Shen*
334
1115
 
335
- * `ActiveRecord::Encryption::Encryptor` now supports a `:compressor` option to customize the compression algorithm used.
1116
+ * Enable passing retryable SqlLiterals to `#where`.
336
1117
 
337
- ```ruby
338
- module ZstdCompressor
339
- def self.deflate(data)
340
- Zstd.compress(data)
341
- end
1118
+ *Hartley McGuire*
342
1119
 
343
- def self.inflate(data)
344
- Zstd.decompress(data)
345
- end
1120
+ * Set default for primary keys in `insert_all`/`upsert_all`.
1121
+
1122
+ Previously in Postgres, updating and inserting new records in one upsert wasn't possible
1123
+ due to null primary key values. `nil` primary key values passed into `insert_all`/`upsert_all`
1124
+ are now implicitly set to the default insert value specified by adapter.
1125
+
1126
+ *Jenny Shen*
1127
+
1128
+ * Add a load hook `active_record_database_configurations` for `ActiveRecord::DatabaseConfigurations`
1129
+
1130
+ *Mike Dalessio*
1131
+
1132
+ * Use `TRUE` and `FALSE` for SQLite queries with boolean columns.
1133
+
1134
+ *Hartley McGuire*
1135
+
1136
+ * Bump minimum supported SQLite to 3.23.0.
1137
+
1138
+ *Hartley McGuire*
1139
+
1140
+ * Allow allocated Active Records to lookup associations.
1141
+
1142
+ Previously, the association cache isn't setup on allocated record objects, so association
1143
+ lookups will crash. Test frameworks like mocha use allocate to check for stubbable instance
1144
+ methods, which can trigger an association lookup.
1145
+
1146
+ *Gannon McGibbon*
1147
+
1148
+ * Encryption now supports `support_unencrypted_data: true` being set per-attribute.
1149
+
1150
+ Previously this only worked if `ActiveRecord::Encryption.config.support_unencrypted_data == true`.
1151
+ Now, if the global config is turned off, you can still opt in for a specific attribute.
1152
+
1153
+ ```ruby
1154
+ # ActiveRecord::Encryption.config.support_unencrypted_data = true
1155
+ class User < ActiveRecord::Base
1156
+ encrypts :name, support_unencrypted_data: false # only supports encrypted data
1157
+ encrypts :email # supports encrypted or unencrypted data
346
1158
  end
1159
+ ```
347
1160
 
348
- class User
349
- encrypts :name, compressor: ZstdCompressor
1161
+ ```ruby
1162
+ # ActiveRecord::Encryption.config.support_unencrypted_data = false
1163
+ class User < ActiveRecord::Base
1164
+ encrypts :name, support_unencrypted_data: true # supports encrypted or unencrypted data
1165
+ encrypts :email # only supports encrypted data
350
1166
  end
351
1167
  ```
352
1168
 
353
- You disable compression by passing `compress: false`.
1169
+ *Alex Ghiculescu*
1170
+
1171
+ * Model generator no longer needs a database connection to validate column types.
1172
+
1173
+ *Mike Dalessio*
1174
+
1175
+ * Allow signed ID verifiers to be configurable via `Rails.application.message_verifiers`
1176
+
1177
+ Prior to this change, the primary way to configure signed ID verifiers was
1178
+ to set `signed_id_verifier` on each model class:
1179
+
1180
+ ```ruby
1181
+ Post.signed_id_verifier = ActiveSupport::MessageVerifier.new(...)
1182
+ Comment.signed_id_verifier = ActiveSupport::MessageVerifier.new(...)
1183
+ ```
1184
+
1185
+ And if the developer did not set `signed_id_verifier`, a verifier would be
1186
+ instantiated with a secret derived from `secret_key_base` and the following
1187
+ options:
1188
+
1189
+ ```ruby
1190
+ { digest: "SHA256", serializer: JSON, url_safe: true }
1191
+ ```
1192
+
1193
+ Thus it was cumbersome to rotate configuration for all verifiers.
1194
+
1195
+ This change defines a new Rails config: [`config.active_record.use_legacy_signed_id_verifier`][].
1196
+ The default value is `:generate_and_verify`, which preserves the previous
1197
+ behavior. However, when set to `:verify`, signed ID verifiers will use
1198
+ configuration from `Rails.application.message_verifiers` (specifically,
1199
+ `Rails.application.message_verifiers["active_record/signed_id"]`) to
1200
+ generate and verify signed IDs, but will also verify signed IDs using the
1201
+ older configuration.
1202
+
1203
+ To avoid complication, the new behavior only applies when `signed_id_verifier_secret`
1204
+ is not set on a model class or any of its ancestors. Additionally,
1205
+ `signed_id_verifier_secret` is now deprecated. If you are currently setting
1206
+ `signed_id_verifier_secret` on a model class, you can set `signed_id_verifier`
1207
+ instead:
1208
+
1209
+ ```ruby
1210
+ # BEFORE
1211
+ Post.signed_id_verifier_secret = "my secret"
1212
+
1213
+ # AFTER
1214
+ Post.signed_id_verifier = ActiveSupport::MessageVerifier.new("my secret", digest: "SHA256", serializer: JSON, url_safe: true)
1215
+ ```
1216
+
1217
+ To ease migration, `signed_id_verifier` has also been changed to behave as a
1218
+ `class_attribute` (i.e. inheritable), but _only when `signed_id_verifier_secret`
1219
+ is not set_:
1220
+
1221
+ ```ruby
1222
+ # BEFORE
1223
+ ActiveRecord::Base.signed_id_verifier = ActiveSupport::MessageVerifier.new(...)
1224
+ Post.signed_id_verifier == ActiveRecord::Base.signed_id_verifier # => false
1225
+
1226
+ # AFTER
1227
+ ActiveRecord::Base.signed_id_verifier = ActiveSupport::MessageVerifier.new(...)
1228
+ Post.signed_id_verifier == ActiveRecord::Base.signed_id_verifier # => true
1229
+
1230
+ Post.signed_id_verifier_secret = "my secret" # => deprecation warning
1231
+ Post.signed_id_verifier == ActiveRecord::Base.signed_id_verifier # => false
1232
+ ```
1233
+
1234
+ Note, however, that it is recommended to eventually migrate from
1235
+ model-specific verifiers to a unified configuration managed by
1236
+ `Rails.application.message_verifiers`. `ActiveSupport::MessageVerifier#rotate`
1237
+ can facilitate that transition. For example:
1238
+
1239
+ ```ruby
1240
+ # BEFORE
1241
+ # Generate and verify signed Post IDs using Post-specific configuration
1242
+ Post.signed_id_verifier = ActiveSupport::MessageVerifier.new("post secret", ...)
1243
+
1244
+ # AFTER
1245
+ # Generate and verify signed Post IDs using the unified configuration
1246
+ Post.signed_id_verifier = Post.signed_id_verifier.dup
1247
+ # Fall back to Post-specific configuration when verifying signed IDs
1248
+ Post.signed_id_verifier.rotate("post secret", ...)
1249
+ ```
1250
+
1251
+ [`config.active_record.use_legacy_signed_id_verifier`]: https://guides.rubyonrails.org/v8.1/configuring.html#config-active-record-use-legacy-signed-id-verifier
1252
+
1253
+ *Ali Sepehri*, *Jonathan Hefner*
1254
+
1255
+ * Prepend `extra_flags` in postgres' `structure_load`
1256
+
1257
+ When specifying `structure_load_flags` with a postgres adapter, the flags
1258
+ were appended to the default flags, instead of prepended.
1259
+ This caused issues with flags not being taken into account by postgres.
1260
+
1261
+ *Alice Loeser*
1262
+
1263
+ * Allow bypassing primary key/constraint addition in `implicit_order_column`
1264
+
1265
+ When specifying multiple columns in an array for `implicit_order_column`, adding
1266
+ `nil` as the last element will prevent appending the primary key to order
1267
+ conditions. This allows more precise control of indexes used by
1268
+ generated queries. It should be noted that this feature does introduce the risk
1269
+ of API misbehavior if the specified columns are not fully unique.
1270
+
1271
+ *Issy Long*
1272
+
1273
+ * Allow setting the `schema_format` via database configuration.
1274
+
1275
+ ```
1276
+ primary:
1277
+ schema_format: ruby
1278
+ ```
1279
+
1280
+ Useful for multi-database setups when apps require different formats per-database.
1281
+
1282
+ *T S Vallender*
1283
+
1284
+ * Support disabling indexes for MySQL v8.0.0+ and MariaDB v10.6.0+
1285
+
1286
+ MySQL 8.0.0 added an option to disable indexes from being used by the query
1287
+ optimizer by making them "invisible". This allows the index to still be maintained
1288
+ and updated but no queries will be permitted to use it. This can be useful for adding
1289
+ new invisible indexes or making existing indexes invisible before dropping them
1290
+ to ensure queries are not negatively affected.
1291
+ See https://dev.mysql.com/blog-archive/mysql-8-0-invisible-indexes/ for more details.
1292
+
1293
+ MariaDB 10.6.0 also added support for this feature by allowing indexes to be "ignored"
1294
+ in queries. See https://mariadb.com/kb/en/ignored-indexes/ for more details.
1295
+
1296
+ Active Record now supports this option for MySQL 8.0.0+ and MariaDB 10.6.0+ for
1297
+ index creation and alteration where the new index option `enabled: true/false` can be
1298
+ passed to column and index methods as below:
354
1299
 
355
1300
  ```ruby
356
- class User
357
- encrypts :name, compress: false
1301
+ add_index :users, :email, enabled: false
1302
+ enable_index :users, :email
1303
+ add_column :users, :dob, :string, index: { enabled: false }
1304
+
1305
+ change_table :users do |t|
1306
+ t.index :name, enabled: false
1307
+ t.index :dob
1308
+ t.disable_index :dob
1309
+ t.column :username, :string, index: { enabled: false }
1310
+ t.references :account, index: { enabled: false }
1311
+ end
1312
+
1313
+ create_table :users do |t|
1314
+ t.string :name, index: { enabled: false }
1315
+ t.string :email
1316
+ t.index :email, enabled: false
358
1317
  end
359
1318
  ```
360
1319
 
361
- *heka1024*
1320
+ *Merve Taner*
1321
+
1322
+ * Respect `implicit_order_column` in `ActiveRecord::Relation#reverse_order`.
1323
+
1324
+ *Joshua Young*
1325
+
1326
+ * Add column types to `ActiveRecord::Result` for SQLite3.
1327
+
1328
+ *Andrew Kane*
1329
+
1330
+ * Raise `ActiveRecord::ReadOnlyError` when pessimistically locking with a readonly role.
1331
+
1332
+ *Joshua Young*
1333
+
1334
+ * Fix using the `SQLite3Adapter`'s `dbconsole` method outside of a Rails application.
1335
+
1336
+ *Hartley McGuire*
1337
+
1338
+ * Fix migrating multiple databases with `ActiveRecord::PendingMigration` action.
1339
+
1340
+ *Gannon McGibbon*
1341
+
1342
+ * Enable automatically retrying idempotent association queries on connection
1343
+ errors.
1344
+
1345
+ *Hartley McGuire*
1346
+
1347
+ * Add `allow_retry` to `sql.active_record` instrumentation.
362
1348
 
363
- * Add condensed `#inspect` for `ConnectionPool`, `AbstractAdapter`, and
364
- `DatabaseConfig`.
1349
+ This enables identifying queries which queries are automatically retryable on connection errors.
365
1350
 
366
1351
  *Hartley McGuire*
367
1352
 
368
- * Add `.shard_keys`, `.sharded?`, & `.connected_to_all_shards` methods.
1353
+ * Better support UPDATE with JOIN for Postgresql and SQLite3
1354
+
1355
+ Previously when generating update queries with one or more JOIN clauses,
1356
+ Active Record would use a sub query which would prevent to reference the joined
1357
+ tables in the `SET` clause, for instance:
369
1358
 
370
1359
  ```ruby
371
- class ShardedBase < ActiveRecord::Base
372
- self.abstract_class = true
1360
+ Comment.joins(:post).update_all("title = posts.title")
1361
+ ```
373
1362
 
374
- connects_to shards: {
375
- shard_one: { writing: :shard_one },
376
- shard_two: { writing: :shard_two }
377
- }
378
- end
1363
+ This is now supported as long as the relation doesn't also use a `LIMIT`, `ORDER` or
1364
+ `GROUP BY` clause. This was supported by the MySQL adapter for a long time.
1365
+
1366
+ *Jean Boussier*
1367
+
1368
+ * Introduce a before-fork hook in `ActiveSupport::Testing::Parallelization` to clear existing
1369
+ connections, to avoid fork-safety issues with the mysql2 adapter.
379
1370
 
380
- class ShardedModel < ShardedBase
1371
+ Fixes #41776
1372
+
1373
+ *Mike Dalessio*, *Donal McBreen*
1374
+
1375
+ * PoolConfig no longer keeps a reference to the connection class.
1376
+
1377
+ Keeping a reference to the class caused subtle issues when combined with reloading in
1378
+ development. Fixes #54343.
1379
+
1380
+ *Mike Dalessio*
1381
+
1382
+ * Fix SQL notifications sometimes not sent when using async queries.
1383
+
1384
+ ```ruby
1385
+ Post.async_count
1386
+ ActiveSupport::Notifications.subscribed(->(*) { "Will never reach here" }) do
1387
+ Post.count
381
1388
  end
1389
+ ```
1390
+
1391
+ In rare circumstances and under the right race condition, Active Support notifications
1392
+ would no longer be dispatched after using an asynchronous query.
1393
+ This is now fixed.
1394
+
1395
+ *Edouard Chin*
1396
+
1397
+ * Eliminate queries loading dumped schema cache on Postgres
382
1398
 
383
- ShardedModel.shard_keys => [:shard_one, :shard_two]
384
- ShardedModel.sharded? => true
385
- ShardedBase.connected_to_all_shards { ShardedModel.current_shard } => [:shard_one, :shard_two]
1399
+ Improve resiliency by avoiding needing to open a database connection to load the
1400
+ type map while defining attribute methods at boot when a schema cache file is
1401
+ configured on PostgreSQL databases.
1402
+
1403
+ *James Coleman*
1404
+
1405
+ * `ActiveRecord::Coder::JSON` can be instantiated
1406
+
1407
+ Options can now be passed to `ActiveRecord::Coder::JSON` when instantiating the coder. This allows:
1408
+ ```ruby
1409
+ serialize :config, coder: ActiveRecord::Coder::JSON.new(symbolize_names: true)
386
1410
  ```
1411
+ *matthaigh27*
387
1412
 
388
- *Nony Dutton*
1413
+ * Deprecate using `insert_all`/`upsert_all` with unpersisted records in associations.
1414
+
1415
+ Using these methods on associations containing unpersisted records will now
1416
+ show a deprecation warning, as the unpersisted records will be lost after
1417
+ the operation.
389
1418
 
390
- * Add a `filter` option to `in_order_of` to prioritize certain values in the sorting without filtering the results
391
- by these values.
1419
+ *Nick Schwaderer*
392
1420
 
393
- *Igor Depolli*
1421
+ * Make column name optional for `index_exists?`.
394
1422
 
395
- * Fix an issue where the IDs reader method did not return expected results
396
- for preloaded associations in models using composite primary keys.
1423
+ This aligns well with `remove_index` signature as well, where
1424
+ index name doesn't need to be derived from the column names.
397
1425
 
398
- *Jay Ang*
1426
+ *Ali Ismayiliov*
399
1427
 
400
- * Allow to configure `strict_loading_mode` globally or within a model.
1428
+ * Change the payload name of `sql.active_record` notification for eager
1429
+ loading from "SQL" to "#{model.name} Eager Load".
401
1430
 
402
- Defaults to `:all`, can be changed to `:n_plus_one_only`.
1431
+ *zzak*
403
1432
 
404
- *Garen Torikian*
1433
+ * Enable automatically retrying idempotent `#exists?` queries on connection
1434
+ errors.
405
1435
 
406
- * Add `ActiveRecord::Relation#readonly?`.
1436
+ *Hartley McGuire*, *classidied*
407
1437
 
408
- Reflects if the relation has been marked as readonly.
1438
+ * Deprecate usage of unsupported methods in conjunction with `update_all`:
409
1439
 
410
- *Theodor Tonum*
1440
+ `update_all` will now print a deprecation message if a query includes either `WITH`,
1441
+ `WITH RECURSIVE` or `DISTINCT` statements. Those were never supported and were ignored
1442
+ when generating the SQL query.
411
1443
 
412
- * Improve `ActiveRecord::Store` to raise a descriptive exception if the column is not either
413
- structured (e.g., PostgreSQL +hstore+/+json+, or MySQL +json+) or declared serializable via
414
- `ActiveRecord.store`.
1444
+ An error will be raised in a future Rails release. This behavior will be consistent
1445
+ with `delete_all` which currently raises an error for unsupported statements.
415
1446
 
416
- Previously, a `NoMethodError` would be raised when the accessor was read or written:
1447
+ *Edouard Chin*
417
1448
 
418
- NoMethodError: undefined method `accessor' for an instance of ActiveRecord::Type::Text
1449
+ * The table columns inside `schema.rb` are now sorted alphabetically.
419
1450
 
420
- Now, a descriptive `ConfigurationError` is raised:
1451
+ Previously they'd be sorted by creation order, which can cause merge conflicts when two
1452
+ branches modify the same table concurrently.
421
1453
 
422
- ActiveRecord::ConfigurationError: the column 'metadata' has not been configured as a store.
423
- Please make sure the column is declared serializable via 'ActiveRecord.store' or, if your
424
- database supports it, use a structured column type like hstore or json.
1454
+ *John Duff*
425
1455
 
426
- *Mike Dalessio*
1456
+ * Introduce versions formatter for the schema dumper.
1457
+
1458
+ It is now possible to override how schema dumper formats versions information inside the
1459
+ `structure.sql` file. Currently, the versions are simply sorted in the decreasing order.
1460
+ Within large teams, this can potentially cause many merge conflicts near the top of the list.
1461
+
1462
+ Now, the custom formatter can be provided with a custom sorting logic (e.g. by hash values
1463
+ of the versions), which can greatly reduce the number of conflicts.
1464
+
1465
+ *fatkodima*
427
1466
 
428
- * Fix inference of association model on nested models with the same demodularized name.
1467
+ * Serialized attributes can now be marked as comparable.
429
1468
 
430
- E.g. with the following setup:
1469
+ A not rare issue when working with serialized attributes is that the serialized representation of an object
1470
+ can change over time. Either because you are migrating from one serializer to the other (e.g. YAML to JSON or to msgpack),
1471
+ or because the serializer used subtly changed its output.
1472
+
1473
+ One example is libyaml that used to have some extra trailing whitespaces, and recently fixed that.
1474
+ When this sorts of thing happen, you end up with lots of records that report being changed even though
1475
+ they aren't, which in the best case leads to a lot more writes to the database and in the worst case lead to nasty bugs.
1476
+
1477
+ The solution is to instead compare the deserialized representation of the object, however Active Record
1478
+ can't assume the deserialized object has a working `==` method. Hence why this new functionality is opt-in.
431
1479
 
432
1480
  ```ruby
433
- class Nested::Post < ApplicationRecord
434
- has_one :post, through: :other
435
- end
1481
+ serialize :config, type: Hash, coder: JSON, comparable: true
436
1482
  ```
437
1483
 
438
- Before, `#post` would infer the model as `Nested::Post`, but now it correctly infers `Post`.
1484
+ *Jean Boussier*
1485
+
1486
+ * Fix MySQL default functions getting dropped when changing a column's nullability.
439
1487
 
440
- *Joshua Young*
1488
+ *Bastian Bartmann*
1489
+
1490
+ * SQLite extensions can be configured in `config/database.yml`.
1491
+
1492
+ The database configuration option `extensions:` allows an application to load SQLite extensions
1493
+ when using `sqlite3` >= v2.4.0. The array members may be filesystem paths or the names of
1494
+ modules that respond to `.to_path`:
1495
+
1496
+ ``` yaml
1497
+ development:
1498
+ adapter: sqlite3
1499
+ extensions:
1500
+ - SQLean::UUID # module name responding to `.to_path`
1501
+ - .sqlpkg/nalgeon/crypto/crypto.so # or a filesystem path
1502
+ - <%= AppExtensions.location %> # or ruby code returning a path
1503
+ ```
1504
+
1505
+ *Mike Dalessio*
1506
+
1507
+ * `ActiveRecord::Middleware::ShardSelector` supports granular database connection switching.
1508
+
1509
+ A new configuration option, `class_name:`, is introduced to
1510
+ `config.active_record.shard_selector` to allow an application to specify the abstract connection
1511
+ class to be switched by the shard selection middleware. The default class is
1512
+ `ActiveRecord::Base`.
1513
+
1514
+ For example, this configuration tells `ShardSelector` to switch shards using
1515
+ `AnimalsRecord.connected_to`:
1516
+
1517
+ ```
1518
+ config.active_record.shard_selector = { class_name: "AnimalsRecord" }
1519
+ ```
1520
+
1521
+ *Mike Dalessio*
1522
+
1523
+ * Reset relations after `insert_all`/`upsert_all`.
1524
+
1525
+ Bulk insert/upsert methods will now call `reset` if used on a relation, matching the behavior of `update_all`.
1526
+
1527
+ *Milo Winningham*
1528
+
1529
+ * Use `_N` as a parallel tests databases suffixes
441
1530
 
442
- * Add public method for checking if a table is ignored by the schema cache.
1531
+ Peviously, `-N` was used as a suffix. This can cause problems for RDBMSes
1532
+ which do not support dashes in database names.
443
1533
 
444
- Previously, an application would need to reimplement `ignored_table?` from the schema cache class to check if a table was set to be ignored. This adds a public method to support this and updates the schema cache to use that directly.
1534
+ *fatkodima*
1535
+
1536
+ * Remember when a database connection has recently been verified (for
1537
+ two seconds, by default), to avoid repeated reverifications during a
1538
+ single request.
1539
+
1540
+ This should recreate a similar rate of verification as in Rails 7.1,
1541
+ where connections are leased for the duration of a request, and thus
1542
+ only verified once.
1543
+
1544
+ *Matthew Draper*
1545
+
1546
+ * Allow to reset cache counters for multiple records.
1547
+
1548
+ ```
1549
+ Aircraft.reset_counters([1, 2, 3], :wheels_count)
1550
+ ```
1551
+
1552
+ It produces much fewer queries compared to the custom implementation using looping over ids.
1553
+ Previously: `O(ids.size * counters.size)` queries, now: `O(ids.size + counters.size)` queries.
1554
+
1555
+ *fatkodima*
1556
+
1557
+ * Add `affected_rows` to `sql.active_record` Notification.
1558
+
1559
+ *Hartley McGuire*
1560
+
1561
+ * Fix `sum` when performing a grouped calculation.
1562
+
1563
+ `User.group(:friendly).sum` no longer worked. This is fixed.
1564
+
1565
+ *Edouard Chin*
1566
+
1567
+ * Add support for enabling or disabling transactional tests per database.
1568
+
1569
+ A test class can now override the default `use_transactional_tests` setting
1570
+ for individual databases, which can be useful if some databases need their
1571
+ current state to be accessible to an external process while tests are running.
445
1572
 
446
1573
  ```ruby
447
- ActiveRecord.schema_cache_ignored_tables = ["developers"]
448
- ActiveRecord.schema_cache_ignored_table?("developers")
449
- => true
1574
+ class MostlyTransactionalTest < ActiveSupport::TestCase
1575
+ self.use_transactional_tests = true
1576
+ skip_transactional_tests_for_database :shared
1577
+ end
450
1578
  ```
451
1579
 
452
- *Eileen M. Uchitelle*
1580
+ *Matthew Cheetham*, *Morgan Mareve*
1581
+
1582
+ * Cast `query_cache` value when using URL configuration.
1583
+
1584
+ *zzak*
1585
+
1586
+ * NULLS NOT DISTINCT works with UNIQUE CONSTRAINT as well as UNIQUE INDEX.
1587
+
1588
+ *Ryuta Kamizono*
1589
+
1590
+ * `PG::UnableToSend: no connection to the server` is now retryable as a connection-related exception
1591
+
1592
+ *Kazuma Watanabe*
453
1593
 
454
- Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/activerecord/CHANGELOG.md) for previous changes.
1594
+ Please check [8-0-stable](https://github.com/rails/rails/blob/8-0-stable/activerecord/CHANGELOG.md) for previous changes.