activerecord
Advanced tools
+23
-3
@@ -1,3 +0,25 @@ | ||
| ## Rails 8.1.0.rc1 (October 15, 2025) ## | ||
| ## Rails 8.1.0 (October 22, 2025) ## | ||
| * Fix SQLite3 data loss during table alterations with CASCADE foreign keys. | ||
| When altering a table in SQLite3 that is referenced by child tables with | ||
| `ON DELETE CASCADE` foreign keys, ActiveRecord would silently delete all | ||
| data from the child tables. This occurred because SQLite requires table | ||
| recreation for schema changes, and during this process the original table | ||
| is temporarily dropped, triggering CASCADE deletes on child tables. | ||
| The root cause was incorrect ordering of operations. The original code | ||
| wrapped `disable_referential_integrity` inside a transaction, but | ||
| `PRAGMA foreign_keys` cannot be modified inside a transaction in SQLite - | ||
| attempting to do so simply has no effect. This meant foreign keys remained | ||
| enabled during table recreation, causing CASCADE deletes to fire. | ||
| The fix reverses the order to follow the official SQLite 12-step ALTER TABLE | ||
| procedure: `disable_referential_integrity` now wraps the transaction instead | ||
| of being wrapped by it. This ensures foreign keys are properly disabled | ||
| before the transaction starts and re-enabled after it commits, preventing | ||
| CASCADE deletes while maintaining data integrity through atomic transactions. | ||
| *Ruy Rocha* | ||
| * Add replicas to test database parallelization setup. | ||
@@ -106,4 +128,2 @@ | ||
| ## Rails 8.1.0.beta1 (September 04, 2025) ## | ||
| * Remove deprecated `:unsigned_float` and `:unsigned_decimal` column methods for MySQL. | ||
@@ -110,0 +130,0 @@ |
@@ -45,2 +45,3 @@ # frozen_string_literal: true | ||
| end | ||
| stream.puts | ||
| end | ||
@@ -47,0 +48,0 @@ end |
@@ -616,4 +616,4 @@ # frozen_string_literal: true | ||
| transaction do | ||
| disable_referential_integrity do | ||
| disable_referential_integrity do | ||
| transaction do | ||
| move_table(table_name, altered_table_name, options.merge(temporary: true)) | ||
@@ -620,0 +620,0 @@ move_table(altered_table_name, table_name, &caller) |
@@ -176,5 +176,7 @@ # frozen_string_literal: true | ||
| append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes) | ||
| yield | ||
| ensure | ||
| connected_to_stack.pop | ||
| begin | ||
| yield | ||
| ensure | ||
| connected_to_stack.pop | ||
| end | ||
| end | ||
@@ -400,7 +402,9 @@ | ||
| append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self]) | ||
| return_value = yield | ||
| return_value.load if return_value.is_a? ActiveRecord::Relation | ||
| return_value | ||
| ensure | ||
| self.connected_to_stack.pop | ||
| begin | ||
| return_value = yield | ||
| return_value.load if return_value.is_a? ActiveRecord::Relation | ||
| return_value | ||
| ensure | ||
| self.connected_to_stack.pop | ||
| end | ||
| end | ||
@@ -407,0 +411,0 @@ |
@@ -13,3 +13,3 @@ # frozen_string_literal: true | ||
| TINY = 0 | ||
| PRE = "rc1" | ||
| PRE = nil | ||
@@ -16,0 +16,0 @@ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") |
@@ -428,10 +428,6 @@ # frozen_string_literal: true | ||
| if active_record.name.demodulize == class_name | ||
| begin | ||
| compute_class("::#{class_name}") | ||
| rescue NameError | ||
| compute_class(class_name) | ||
| end | ||
| else | ||
| compute_class(class_name) | ||
| return compute_class("::#{class_name}") rescue NameError | ||
| end | ||
| compute_class(class_name) | ||
| end | ||
@@ -438,0 +434,0 @@ |
@@ -15,3 +15,3 @@ # frozen_string_literal: true | ||
| owner: owner.name, | ||
| class: reflection.klass.name, | ||
| class: reflection.polymorphic? ? nil : reflection.klass.name, | ||
| name: reflection.name, | ||
@@ -18,0 +18,0 @@ ) |
Sorry, the diff of this file is too big to display