如何将存储过程迁移到测试db?

时间:2010-11-05 17:42:28

标签: mysql ruby-on-rails-3 testing stored-procedures

我在Rails 3.0.7中存储过程和测试数据库存在问题。运行时

  

rake db:test:prepare

它从schema.rb迁移db表,而不是直接从迁移迁移。通过调用execute方法并传入CREATE FUNCTION foo() ... BEGIN ... END;等SQL字符串,在迁移过程中创建过程。

经过研究,我发现你应该使用

  

config.active_record.schema_format =:sql

application.rb内。添加此行后,我执行了

  

rake db:structure:dump   rake db:test:clone_structure

第一个应该将结构转储到development.sql文件中,第二个应该从该文件创建测试数据库。但是我的存储过程和函数仍未出现在测试数据库中。如果有人知道这个问题。 将不胜感激。

我也尝试过运行rake db:test:再次准备,但仍然没有结果。

MySQL 5.5,Rails 3.0.7,Ruby 1.8.7。

提前致谢!

7 个答案:

答案 0 :(得分:9)

没有其他rake任务,而structure_dump的定义如下:

# File activerecord/lib/active_record/connection_adapters/mysql_adapter.rb, line 354
  def structure_dump #:nodoc:
    if supports_views?
      sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
    else
      sql = "SHOW TABLES"
    end

    select_all(sql).map do |table|
      table.delete('Table_type')
      select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
    end.join("")
  end

所以它显然只适用于表格,而不是程序,除非你将它单个化。

据我所知,唯一的解决方案是使用shell:

mysqldump -R -u <user> <development_database> | mysql -u <user> <test_database>

答案 1 :(得分:1)

免责声明:我不是Ruby-on-Rails程序员

严格地说,在MySQL方面,你基本上有两种方法来提取存储过程(SP)和存储函数(SF)。

请记住,mysql.proc和information_schema.routines为磁盘和内存中的SP提供了外壳。但是,有两个SQL语句可以检索它们:SHOW CREATE PROCEDURESHOW CREATE FUNCTION

第一种方法是使用mysql.proc收集所有SP和SF,并将它们形成为暴露它们的SQL语句。

示例I我的测试数据库中有6个SP和2个SF。以下是为所有8个例程生成SQL的方法:

mysql> SELECT CONCAT('SHOW CREATE ',type,' `',db,'`.`',name,'`\\G') SQLStatements FROM mysql.proc;
+-----------------------------------------------------+
| SQLStatements                                       |
+-----------------------------------------------------+
| SHOW CREATE PROCEDURE `test`.`CreateSampleTable`\G  |
| SHOW CREATE PROCEDURE `test`.`CreateSampleTables`\G |
| SHOW CREATE PROCEDURE `test`.`GetMissingIntegers`\G |
| SHOW CREATE FUNCTION `test`.`GetTestTableCounts`\G  |
| SHOW CREATE PROCEDURE `test`.`ImportWeeklyBatch`\G  |
| SHOW CREATE FUNCTION `test`.`InsertName`\G          |
| SHOW CREATE PROCEDURE `test`.`LoadSampleTables`\G   |
| SHOW CREATE PROCEDURE `test`.`MigrateColumn`\G      |
+-----------------------------------------------------+
8 rows in set (0.00 sec)

您可以循环访问并收集每个存储过程和函数所需的代码。

触发器必须单独收集。

在MySQL 5.x中,您可以使用此查询收集触发器:

mysql> SELECT CONCAT('SHOW CREATE TRIGGER `',trigger_schema,'`.`',trigger_name,'`\\G') SQLStatements FROM information_schema.triggers;
+--------------------------------------------------+
| SQLStatements                                    |
+--------------------------------------------------+
| SHOW CREATE TRIGGER `test`.`AddPermTempKey`\G    |
| SHOW CREATE TRIGGER `test`.`DeletePermTempKey`\G |
+--------------------------------------------------+

或节省时间UNION两个SQL语句

mysql> SELECT CONCAT('SHOW CREATE ',type,' `',db,'`.`',name,'`\\G') SQLStatements FROM mysql.proc UNION SELECT CONCAT('SHOW CREATE TRIGGER `',trigger_schema,'`.`',trigger_name,'`\\G') SQLStatements FROM information_schema.triggers;
+-----------------------------------------------------+
| SQLStatements                                       |
+-----------------------------------------------------+
| SHOW CREATE PROCEDURE `test`.`CreateSampleTable`\G  |
| SHOW CREATE PROCEDURE `test`.`CreateSampleTables`\G |
| SHOW CREATE PROCEDURE `test`.`GetMissingIntegers`\G |
| SHOW CREATE FUNCTION `test`.`GetTestTableCounts`\G  |
| SHOW CREATE PROCEDURE `test`.`ImportWeeklyBatch`\G  |
| SHOW CREATE FUNCTION `test`.`InsertName`\G          |
| SHOW CREATE PROCEDURE `test`.`LoadSampleTables`\G   |
| SHOW CREATE PROCEDURE `test`.`MigrateColumn`\G      |
| SHOW CREATE TRIGGER `test`.`AddPermTempKey`\G       |
| SHOW CREATE TRIGGER `test`.`DeletePermTempKey`\G    |
+-----------------------------------------------------+
10 rows in set (0.07 sec)

第二种方式是DBA的首选方式,使用mysqldump。

这将在单个文件中收集所有表结构,SP,SF和触发器。

mysqldump -h... -u... -p... --no-data --routines --triggers --all-databases > MySQLSchema.sql

这样做会没有CREATE TABLE的东西:

mysqldump -h... -u... -p... --no-data --no-create-info --routines --triggers --all-databases > MySQLSchema.sql

试试这些!!!

答案 2 :(得分:1)

在Rails 5中添加了(我尚未测试过)rake db:structure:dump对存储函数和过程的支持。请参阅this commit in the rails GitHub project--routines的{​​{1}}标记描述为here。注意方法mysqldump看起来与JanMinárik六年前回答时的情况截然不同。

答案 3 :(得分:0)

正在寻找如何做同样的事情然后看到了这个:http://guides.rubyonrails.org/migrations.html#types-of-schema-dumps

引用:

“db / schema.rb无法表达特定于数据库的项目,例如外键约束,触发器或存储过程。在迁移过程中,您可以执行自定义SQL语句,而模式转储器无法从数据库重新构建这些语句。如果您是使用这样的功能,你应该将架构格式设置为:sql。“

即:

config.active_record.schema_format =:sql

我自己还没有尝试过,所以我稍后会发布一个后续行动。

答案 4 :(得分:0)

我采用了Matthew Bass删除现有rake任务的方法,并使用带有RolandoMySQLDBA提供的选项的mysqldump重新定义了一个任务

http://matthewbass.com/2007/03/07/overriding-existing-rake-tasks/

Rake::TaskManager.class_eval do
  def remove_task(task_name)
    @tasks.delete(task_name.to_s)
  end
end

def remove_task(task_name)
  Rake.application.remove_task(task_name)
end

# Override existing test task to prevent integrations
# from being run unless specifically asked for
remove_task 'db:test:prepare'

namespace :db do
  namespace :test do
    desc "Create a db/schema.rb file"
    task :prepare => :environment do
      sh "mysqldump --routines --no-data -u root ni | mysql -u root ni_test"
    end
  end
end

答案 5 :(得分:0)

如果您想要Ruby转储(而不是SQL转储),您可以尝试使用此gem:

https://github.com/jovoto-team/trackless_triggers

它支持开箱即用的mysql转储触发器和函数,而不会引入新的rake任务。它基于tenderlove的trigger_happy插件。

答案 6 :(得分:0)

On Rails 4我把它作为db:test:load rake任务的后加载钩子挂钩,如下所示:

require File.expand_path('../config/application', __FILE__)

Rails.application.load_tasks

namespace :db do
  namespace :test do

    task :post_load_hook do
      re_create_sps
    end

    def re_create_sps
      [20170905123456, 20170905123457].each do |version|
        ActiveRecord::Migrator.run(
          :down, ActiveRecord::Migrator.migrations_paths, version)
        ActiveRecord::Migrator.run(
          :up, ActiveRecord::Migrator.migrations_paths, version)
      end
    end

    # stored procs must be restored each time.
  end
end

Rake::Task['db:test:load'].enhance(['db:test:post_load_hook'])

这种方法会自动运行,因此您不必在每次测试运行时手动重新加载数据库,它只会影响db:test:load任务,所以我认为它是相当孤立的。

恕我直言,在任务中有迁移ID有点难看,所以你可以选择将迁移代码提取到lib并从上面的迁移和Rake任务中调用它来清理它。