<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>activerecord/test/migrations/broken/100_migration_that_raises_exception.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,5 +1,7 @@
 *Edge*
 
+* Transactional migrations for databases which support them.  #834 [divoxx, Adam Wiggins, Tarmo T&#228;nav]
+
 * Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin]
 
 * change_column_default preserves the not-null constraint.  #617 [Tarmo T&#228;nav]</diff>
      <filename>activerecord/CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -51,6 +51,13 @@ module ActiveRecord
         true
       end
 
+      # Does this adapter support DDL rollbacks in transactions?  That is, would
+      # CREATE TABLE or ALTER TABLE get rolled back by a transaction?  PostgreSQL,
+      # SQL Server, and others support this.  MySQL and others do not.
+      def supports_ddl_transactions?
+        false
+      end
+
       # Should primary key values be selected from their corresponding
       # sequence before the insert statement?  If true, next_sequence_value
       # is called before each insert to set the record's primary key.</diff>
      <filename>activerecord/lib/active_record/connection_adapters/abstract_adapter.rb</filename>
    </modified>
    <modified>
      <diff>@@ -335,6 +335,10 @@ module ActiveRecord
         postgresql_version &gt;= 80200
       end
 
+      def supports_ddl_transactions?
+        true
+      end
+
       # Returns the configured supported identifier length supported by PostgreSQL,
       # or report the default of 63 on PostgreSQL 7.x.
       def table_alias_length</diff>
      <filename>activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb</filename>
    </modified>
    <modified>
      <diff>@@ -461,14 +461,22 @@ module ActiveRecord
         Base.logger.info &quot;Migrating to #{migration.name} (#{migration.version})&quot;
 
         # On our way up, we skip migrating the ones we've already migrated
-        # On our way down, we skip reverting the ones we've never migrated
         next if up? &amp;&amp; migrated.include?(migration.version.to_i)
 
+        # On our way down, we skip reverting the ones we've never migrated
         if down? &amp;&amp; !migrated.include?(migration.version.to_i)
           migration.announce 'never migrated, skipping'; migration.write
-        else
-          migration.migrate(@direction)
-          record_version_state_after_migrating(migration.version)
+          next
+        end
+
+        begin
+          ddl_transaction do
+            migration.migrate(@direction)
+            record_version_state_after_migrating(migration.version)
+          end
+        rescue =&gt; e
+          canceled_msg = Base.connection.supports_ddl_transactions? ? &quot;this and &quot; : &quot;&quot;
+          raise StandardError, &quot;An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}&quot;, e.backtrace
         end
       end
     end
@@ -531,5 +539,14 @@ module ActiveRecord
       def down?
         @direction == :down
       end
+
+      # Wrap the migration in a transaction only if supported by the adapter.
+      def ddl_transaction(&amp;block)
+        if Base.connection.supports_ddl_transactions?
+          Base.transaction { block.call }
+        else
+          block.call
+        end
+      end
   end
 end</diff>
      <filename>activerecord/lib/active_record/migration.rb</filename>
    </modified>
    <modified>
      <diff>@@ -937,6 +937,21 @@ if ActiveRecord::Base.connection.supports_migrations?
       assert_equal(0, ActiveRecord::Migrator.current_version)
     end
 
+    if current_adapter?(:PostgreSQLAdapter)
+      def test_migrator_one_up_with_exception_and_rollback
+        assert !Person.column_methods_hash.include?(:last_name)
+
+        e = assert_raises(StandardError) do
+          ActiveRecord::Migrator.up(MIGRATIONS_ROOT + &quot;/broken&quot;, 100)
+        end
+
+        assert_equal &quot;An error has occurred, this and all later migrations canceled:\n\nSomething broke&quot;, e.message
+
+        Person.reset_column_information
+        assert !Person.column_methods_hash.include?(:last_name)
+      end
+    end
+
     def test_finds_migrations
       migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + &quot;/valid&quot;).migrations
       [['1', 'people_have_last_names'],</diff>
      <filename>activerecord/test/cases/migration_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>9dac5547ad65e82a6fbb6a6243ab5c95d9c44db0</id>
    </parent>
  </parents>
  <author>
    <name>Tarmo T&#228;nav</name>
    <email>tarmo@itech.ee</email>
  </author>
  <url>http://github.com/rails/rails/commit/707ee0e2695e85186d59aa407f09691ebfcc3125</url>
  <id>707ee0e2695e85186d59aa407f09691ebfcc3125</id>
  <committed-date>2008-08-22T14:46:25-07:00</committed-date>
  <authored-date>2008-08-22T13:53:31-07:00</authored-date>
  <message>Made migrations transactional for PostgreSQL [#834 state:resolved]

Patch originally from http://dev.rubyonrails.org/ticket/5470</message>
  <tree>4357234d7cdb191d267e400ea217642124e06cae</tree>
  <committer>
    <name>Jeremy Kemper</name>
    <email>jeremy@bitsweat.net</email>
  </committer>
</commit>
