【问题标题】:Show SQL generated by pending migrations in rails without updating the database在不更新数据库的情况下显示由 Rails 中的挂起迁移生成的 SQL
【发布时间】:2013-04-06 15:53:08
【问题描述】:

我希望有一种方法可以生成由 rake db:migrate 生成的实际 sql(即:如果我粘贴到 mysql 控制台,它会起作用),而无需实际更新目标数据库。

rake db:migrate:status 可以很好地显示给定数据库的待处理迁移,但我还没有找到一种方法来生成实际的 SQL。

有什么想法吗?

【问题讨论】:

标签: ruby-on-rails database rake


【解决方案1】:

非常有趣的问题!我是这样找到的:

  1. 假设您的迁移放置在文件db/migrate/20160102210050_create_items.rb 中并称为CreateItems
  2. 转到 Rails 控制台并加载迁移文件:

    rails c
    require './db/migrate/20160102210050_create_items'
    
  3. 在提交之前打开事务,运行迁移和回滚事务:)

    ActiveRecord::Base.connection.transaction do
      CreateItems.new.migrate :up
      raise ActiveRecord::Rollback
    end
    

如果您想在回滚时检查 SQL,只需在第 3 步调用 CreateItems.new.migrate :down。SQL 将在数据库上执行和测试,但不会提交 - 因此您可以验证您的迁移而不会受到影响。

【讨论】:

  • 你也可以使用rails c --sandbox,这样你就不需要任何事务也不需要任何回滚:只需使用CreateItems.new.migrate :up,Rails 会在关闭控制台时自动回滚
  • 这仅适用于支持事务 DDL 的 RDMBS。
【解决方案2】:

这可以通过对数据库适配器进行猴子修补来完成。此示例适用于 MySQL。

为“fake db:migrate”创建一个 rake 任务:

desc "Prints all SQL to be executed during pending migrations"
task :fake_db_migrate => :environment do

  module ActiveRecord
    module ConnectionAdapters
      class AbstractMysqlAdapter < AbstractAdapter

        alias_method :real_execute, :execute

        def execute(sql, name = nil)
          if sql =~ /^SHOW/ || sql =~ /^SELECT.*FROM.*schema_migrations/ || sql =~ /^SELECT.*information_schema/m
            real_execute(sql, name)
          else
            puts sql
          end
        end

      end
    end
  end

  Rake::Task["db:migrate"].invoke
end

rake 任务猴子修补连接适配器中的execute 方法,以便在实际运行迁移之前打印而不是执行 SQL。但是,我们仍然需要执行db:migrate 任务使用的一些内部 SQL 来获取数据库模式并找出哪些迁移正在等待。这就是real_execute 调用的作用。

测试

现在假设我们在db/migrate/20160211212415_create_some_table.rb 中有一个待处理的迁移:

class CreateSomeTable < ActiveRecord::Migration
  def change
    create_table :some_table do |t|
      t.string :string_column, null: false, default: 'ok'
      t.timestamps
    end
  end
end

$ rake db:migrate:status
...
down    20160211212415  Create some table

现在,让我们运行我们的假迁移任务:

$ rake fake_db_migrate
== 20160211212415 CreateSomeTable: migrating ==================================
-- create_table(:some_table)
CREATE TABLE `some_table` (`id` int(11) auto_increment PRIMARY KEY, `string_column` varchar(255) DEFAULT 'ok' NOT NULL, `created_at` datetime, `updated_at` datetime) ENGINE=InnoDB
   -> 0.0009s
== 20160211212415 CreateSomeTable: migrated (0.0010s) =========================

BEGIN
INSERT INTO `schema_migrations` (`version`) VALUES ('20160211212415')
COMMIT

迁移状态没有改变,即迁移仍在等待中:

$ rake db:migrate:status
...
down    20160211212415  Create some table

使用 mysql2 gem 在 rails 4.2.3 上测试。

【讨论】:

    【解决方案3】:

    一个稍微低级的函数,可以用于你的目的:

    # Get SQL query of a migration expression without executing it.
    #
    # @example
    #   schema_statement_to_sql { create_table(:tomatoes) }
    #   # => "CREATE TABLE \"tomatoes\" (\"id\" serial primary key) "
    def schema_statement_to_sql(&block)
      raise ArgumentError, 'No block given' unless block_given?
    
      connection = ActiveRecord::Base.connection
      original_execute = connection.method(:execute)
      sql_to_return = ''
      capturing_execute = proc { |sql| sql_to_return = sql }
      connection.define_singleton_method(:execute, &capturing_execute)
    
      begin
        connection.instance_eval(&block)
      ensure
        connection.define_singleton_method(:execute, &original_execute)
      end
    
      sql_to_return
    end
    

    【讨论】:

      【解决方案4】:

      你可以的

      rake db:migrate --dry-run --trace
      

      并且 rake 将测试该任务。然后使用列出的方法之一来获取将要运行的 SQL。

      【讨论】:

      • 我不清楚这是如何被接受的答案。你在说什么方法? --dry-run--trace 中没有任何内容表明要运行另一个打印 SQL 的任务。
      猜你喜欢
      • 2012-08-08
      • 2021-07-15
      • 1970-01-01
      • 2010-11-23
      • 2013-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多