public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
Made migrations transactional for PostgreSQL [#834 state:resolved]

Patch originally from http://dev.rubyonrails.org/ticket/5470
Tarmo Tänav (author)
Fri Aug 22 13:53:31 -0700 2008
jeremy (committer)
Fri Aug 22 14:46:25 -0700 2008
commit  707ee0e2695e85186d59aa407f09691ebfcc3125
tree    4357234d7cdb191d267e400ea217642124e06cae
parent  9dac5547ad65e82a6fbb6a6243ab5c95d9c44db0
...
1
2
 
 
3
4
5
...
1
2
3
4
5
6
7
0
@@ -1,5 +1,7 @@
0
 *Edge*
0
 
0
+* Transactional migrations for databases which support them.  #834 [divoxx, Adam Wiggins, Tarmo Tänav]
0
+
0
 * Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin]
0
 
0
 * change_column_default preserves the not-null constraint.  #617 [Tarmo Tänav]
...
51
52
53
 
 
 
 
 
 
 
54
55
56
...
51
52
53
54
55
56
57
58
59
60
61
62
63
0
@@ -51,6 +51,13 @@ module ActiveRecord
0
         true
0
       end
0
 
0
+      # Does this adapter support DDL rollbacks in transactions?  That is, would
0
+      # CREATE TABLE or ALTER TABLE get rolled back by a transaction?  PostgreSQL,
0
+      # SQL Server, and others support this.  MySQL and others do not.
0
+      def supports_ddl_transactions?
0
+        false
0
+      end
0
+
0
       # Should primary key values be selected from their corresponding
0
       # sequence before the insert statement?  If true, next_sequence_value
0
       # is called before each insert to set the record's primary key.
...
335
336
337
 
 
 
 
338
339
340
...
335
336
337
338
339
340
341
342
343
344
0
@@ -335,6 +335,10 @@ module ActiveRecord
0
         postgresql_version >= 80200
0
       end
0
 
0
+      def supports_ddl_transactions?
0
+        true
0
+      end
0
+
0
       # Returns the configured supported identifier length supported by PostgreSQL,
0
       # or report the default of 63 on PostgreSQL 7.x.
0
       def table_alias_length
...
461
462
463
464
465
466
 
467
468
469
470
471
 
 
 
 
 
 
 
 
 
 
 
472
473
474
...
531
532
533
 
 
 
 
 
 
 
 
 
534
535
...
461
462
463
 
464
465
466
467
468
 
 
 
469
470
471
472
473
474
475
476
477
478
479
480
481
482
...
539
540
541
542
543
544
545
546
547
548
549
550
551
552
0
@@ -461,14 +461,22 @@ module ActiveRecord
0
         Base.logger.info "Migrating to #{migration.name} (#{migration.version})"
0
 
0
         # On our way up, we skip migrating the ones we've already migrated
0
-        # On our way down, we skip reverting the ones we've never migrated
0
         next if up? && migrated.include?(migration.version.to_i)
0
 
0
+        # On our way down, we skip reverting the ones we've never migrated
0
         if down? && !migrated.include?(migration.version.to_i)
0
           migration.announce 'never migrated, skipping'; migration.write
0
-        else
0
-          migration.migrate(@direction)
0
-          record_version_state_after_migrating(migration.version)
0
+          next
0
+        end
0
+
0
+        begin
0
+          ddl_transaction do
0
+            migration.migrate(@direction)
0
+            record_version_state_after_migrating(migration.version)
0
+          end
0
+        rescue => e
0
+          canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
0
+          raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
0
         end
0
       end
0
     end
0
@@ -531,5 +539,14 @@ module ActiveRecord
0
       def down?
0
         @direction == :down
0
       end
0
+
0
+      # Wrap the migration in a transaction only if supported by the adapter.
0
+      def ddl_transaction(&block)
0
+        if Base.connection.supports_ddl_transactions?
0
+          Base.transaction { block.call }
0
+        else
0
+          block.call
0
+        end
0
+      end
0
   end
0
 end
...
937
938
939
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
940
941
942
...
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
0
@@ -937,6 +937,21 @@ if ActiveRecord::Base.connection.supports_migrations?
0
       assert_equal(0, ActiveRecord::Migrator.current_version)
0
     end
0
 
0
+    if current_adapter?(:PostgreSQLAdapter)
0
+      def test_migrator_one_up_with_exception_and_rollback
0
+        assert !Person.column_methods_hash.include?(:last_name)
0
+
0
+        e = assert_raises(StandardError) do
0
+          ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/broken", 100)
0
+        end
0
+
0
+        assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
0
+
0
+        Person.reset_column_information
0
+        assert !Person.column_methods_hash.include?(:last_name)
0
+      end
0
+    end
0
+
0
     def test_finds_migrations
0
       migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations
0
       [['1', 'people_have_last_names'],

Comments