2009-09-08 87 views
14

,我從OpenCongress一些奇怪的Postgres遷移代碼和我得到這個錯誤:如何在Rails中啓動事務而不啓動事務?

RuntimeError: ERROR  C25001 MVACUUM cannot run inside a transaction block 
Fxact.c L2649 RPreventTransactionChain: VACUUM FULL ANALYZE; 

所以我想嘗試沒有通過交易得到裹運行它。

+0

請告訴更多的關於你正在運行的遷移,你的數據庫如果它不是默認的mysql/sqlite的話,使用哪個適配器。這樣我認爲一個更有用的答案會跟隨你的問題。 – Ariejan 2009-09-09 14:36:56

+0

對不起,只是看到你在使用Postgres。 – Ariejan 2009-09-09 14:37:27

+0

在這個特定遷移的情況下,我發現了'VACUUM'命令是不是真的有必要(它只做垃圾收集),以便消除這一呼籲的工作,但我還是想知道如何指導Rails的運行沒有交易的遷移。 – hsribei 2009-09-12 15:19:02

回答

14

ActiveRecord::Migration具有運行的遷移時調用下面的私有方法:

def ddl_transaction(&block) 
    if Base.connection.supports_ddl_transactions? 
    Base.transaction { block.call } 
    else 
    block.call 
    end 
end 

正如你所看到的,這將包裝在一個事務中的遷移,如果連接支持它。

ActiveRecord::ConnectionAdapters::PostgreSQLAdapter您有:

def supports_ddl_transactions? 
    true 
end 

SQLite的2.0版及以後也支持遷移交易。 在ActiveRecord::ConnectionAdapters::SQLiteAdapter您有:

def supports_ddl_transactions? 
    sqlite_version >= '2.0.0' 
end 

那麼,跳過交易,你需​​要以某種方式規避這一點。 這樣的事情可能會奏效,雖然我沒有測試它:

class ActiveRecord::Migration 
    class << self 
    def no_transaction 
     @no_transaction = true 
    end 

    def no_transaction? 
     @no_transaction == true 
    end 
    end 

    private 

    def ddl_transaction(&block) 
     if Base.connection.supports_ddl_transactions? && !self.class.no_transaction? 
     Base.transaction { block.call } 
     else 
     block.call 
     end 
    end 
end 

然後,您可以設置您的遷移如下:

class SomeMigration < ActiveRecord::Migration 
    no_transaction 

    def self.up 
    # Do something 
    end 

    def self.down 
    # Do something 
    end 
end 
+0

無法得到這個工作......但聰明的想法! – Crisfole 2012-11-29 17:07:06

+0

鑑於我的原始帖子已超過三年,我不一定會期望這個工作了。 – 2013-01-29 21:00:41

+1

我已經嘗試了此頁面上的大部分解決方案,但都沒有成功。這[要點](https://gist.github.com/olivierlacan/ba81d56d3c9e2a506216)沒有爲Rails 3.2的工作。基本上通過'ddl_transaction'補丁結束/重新啓動一個事務。 – 2015-02-16 22:07:03

4

以上回答是打破了Rails 3的作爲ddl_transaction是進入ActiveRecord :: Migrator。我無法想出一個辦法,以猴子打補丁類,所以這裏是一個替代的解決方案:

我加下的lib文件/

module NoMigrationTransactions 
    def self.included(base)                             
    base.class_eval do 
     alias_method :old_migrate, :migrate 

     say "Disabling transactions" 

     @@no_transaction = true 
     # Force no transactions 
     ActiveRecord::Base.connection.instance_eval do 
     alias :old_ddl :supports_ddl_transactions? 

     def supports_ddl_transactions? 
      false 
     end 
     end 

     def migrate(*args) 
     old_migrate(*args) 

     # Restore 
     if @@no_transaction 
      say "Restoring transactions" 
      ActiveRecord::Base.connection.instance_eval do 
      alias :supports_ddl_transactions? :old_ddl 
      end 
     end 
     end 
    end 
    end 
end 

然後你在你的遷移做的是:

class PopulateTrees < ActiveRecord::Migration 
    include NoMigrationTransactions 
end 

這樣做是禁止交易時遷移類被加載(以前的不同加載希望後任何將來的加載之前),那麼遷移之後,恢復舊的任何交易能力有。

+0

任何人都可以確認這適用於rails〜> 3.2.6嗎?我試過了,但沒有效果。 – 2012-10-28 12:40:20

2

我並不是說這是做到這一點的「正確方法」,但對我而言,唯一的辦法就是單獨運行一次遷移。

rake db:migrate:up VERSION=20120801151807 

其中20120801151807是遷移的時間戳。

顯然,它在運行單個遷移時不使用事務。

+0

哇,上述解決方案都沒有工作,但這真的很有幫助。謝謝! – hurshagrawal 2012-12-21 16:42:37

1

由於這是hacky,因此增加了'commit;'到我的sql的開始爲我工作,但這是爲SQL Server,不知道這是否適用於postgres ...

例如:CREATE FULLTEXT INDEX ...是sql-server用戶事務內部是非法的。

so ...:

execute <<-SQL 
    commit; 
    create fulltext index --...yada yada yada 
SQL 

工作正常......我們稍後會看到我是否後悔。

+0

這實際上是最hackish的所有我身邊發現無法使用新的'disable_ddl_transaction方式:) – Flevour 2013-09-25 13:25:30

10

一個非常簡單的,獨立於Rails版本(2.3,3.2,4.0,無所謂)的方法是在遷移開始時簡單地添加execute("commit;"),然後編寫SQL。

這立即關閉Rails的開始交易,並允許你寫原始SQL,它可以創建自己的事務。在下面的例子中,我使用.update_all和子查詢LIMIT處理更新一個龐大的數據庫表。

舉個例子,

class ChangeDefaultTabIdOfZeroToNilOnUsers < ActiveRecord::Migration 
    def self.up 
    execute("commit;") 
    while User.find_by_default_tab_id(0).present? do 
     User.update_all %{default_tab_id = NULL}, %{id IN (
     SELECT id FROM users WHERE default_tab_id = 0 LIMIT 1000 
    )}.squish! 
    end 
    end 

    def self.down 
    raise ActiveRecord::IrreversibleMigration 
    end 
end 
+0

最乾淨的變通舊的Rails應用程序!' – Pyrce 2015-01-26 19:09:52

+2

注意你可能要添加'執行( 「START TRANSACTION」)'while while循環後 – Pyrce 2015-01-26 19:41:13

56

現在有一種方法disable_ddl_transaction!,允許這一點,如:

class AddIndexesToTablesBasedOnUsage < ActiveRecord::Migration 
    disable_ddl_transaction! 
    def up 
    execute %{ 
     CREATE INDEX CONCURRENTLY index_reservations_subscription_id ON reservations (subscription_id); 
    } 
    end 
    def down 
    execute %{DROP INDEX index_reservations_subscription_id} 
    end 
end 
+3

這應該是公認的答案! – 2015-06-19 13:22:45

相關問題