【问题标题】:How to migrate stored procedures to testing db?如何将存储过程迁移到测试数据库?
【发布时间】:2010-11-05 17:42:27
【问题描述】:

我对 Rails 3.0.7 中的存储过程和测试数据库有疑问。运行时

rake db:test:prepare

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

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

config.active_record.schema_format = :sql

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

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

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

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

MySQL 5.5、Rails 3.0.7、Ruby 1.8.7。

提前致谢!

【问题讨论】:

  • 我也想知道!!!
  • 不知道这是不是你需要的,但是你可以使用表information_schema.routines来转储你的函数和过程。
  • 你可以使用 mysql.proc 和 information_schema.triggers 我的回答指出。

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


【解决方案1】:

没有其他的 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>

【讨论】:

  • 嗯,昨天晚上我有以下想法:我要覆盖 rake 任务 db:schema:dump 和 db:schema:load。新的 rake 任务正在调用 mysqldump 和 mysql 命令行工具。然而,这并不漂亮——我希望这个解决方案在未来的 Rails 升级方面非常脆弱(3.1 版即将推出!!!)。必须有更好的解决方案。
  • 实际上我认为您的解决方案远没有猴子修补适配器那么脆弱。由于我查看了 rake 源并没有找到任何过程转储,因此它看起来也是唯一的方法。如果您不喜欢更改 rake 任务的想法,您可以随时制作自己的,例如db:test:clone_via_mysql.
【解决方案2】:

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

严格来说,就 MySQL 而言,您基本上有两种方法可以提取存储过程 (SP) 和存储函数 (SF)。

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

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

示例我的测试数据库中有 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

试试这些!!!

【讨论】:

    【解决方案3】:

    看起来(我还没有测试过)rake db:structure:dump 在 Rails 5 中添加了对存储函数和过程的支持。请参阅this commit in the rails GitHub project。 --routines 标志到mysqldump 被描述为here。注意方法structure_dump 看起来与六年前 Jan Minárik 回答时的方法大不相同。

    【讨论】:

      【解决方案4】:

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

      引用:

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

      即:

      config.active_record.schema_format = :sql

      虽然我自己还没有尝试过,所以我稍后会发布后续内容。

      【讨论】:

        【解决方案5】:

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

        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
        

        【讨论】:

          【解决方案6】:

          如果您想要 Ruby 转储(而不是 SQL 转储),您可以试试这个 gem:

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

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

          【讨论】:

            【解决方案7】:

            在 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 有点难看,因此您也可以将迁移代码提取到一个库中,然后从上面的迁移和 Rake 任务中调用它来清理它。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-06-01
              • 1970-01-01
              • 1970-01-01
              • 2017-05-01
              • 2018-05-04
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多