有条件地中止MySQL脚本

时间:2010-01-06 09:48:14

标签: mysql error-handling sql-scripts

鉴于:MySQL数据库。有时会推出数据库模式更改和更新(以sql脚本的形式)。为了保证应用更新的正确顺序(没有重复更新,没有更新丢失等),我计划部署以下解决方案:

  • CREATE TABLE元(名称TEXT PRIMARY KEY,val TEXT);
  • INSERT INTO meta VALUES('version','0');

每个更新脚本都带有一个顺序分配的版本N.在执行更新之前,脚本会检查meta.version是否与先前的脚本版本N-1匹配。执行更新后,meta.version更新为N.我不需要防止多个并行运行的脚本。

问题:如果不匹配,如何检查版本并中止脚本?我想出了执行

CALL `raise error`

会破坏脚本,但是如何根据meta.version有条件地执行它?不允许存储过程。有意义的错误消息是一个加号。 This没有提供合适的解决方案。

2 个答案:

答案 0 :(得分:1)

我认为最好在shell脚本或安装程序应用中解决。你无法在SQL脚本中真正拥有任何控制结构。

一个非常简单的解决方案是创建一个脚本,根据元表上的选择生成实际脚本的命令行。

因此,假设您有此脚本gen_upgrade_command.sql

select concat(
       'mysql -u<user> -p<password> -h<host> -e"SOURCE upgrade'
   ,    val + 1
   ,   '.sql"'
   )
from meta;

你这样运行

mysql -u<user> -p<password> -h<host> -Nrs < gen_upgrade_command.sql > do_upgrade.bat

现在,do_upgrade.bat包含此生成的命令行:

mysql -u<usere> -p<password> -h<host> -e"SOURCE upgrade1.sql"

并且正在运行do_upgrade.bat将运行upgrade1.sql

当然,您可以修改原始脚本,也不要选择任何行,这取决于您。

答案 1 :(得分:1)

好的,新答案。并不是说赏金让我更具创造性,而是我觉得建议解决方案不那么克制不直接考虑:)

(我仍然假设您要不惜一切代价避免存储例程)

解决方案#1:使用准备好的陈述

假设你的uprades脚本很简单:

ALTER TABLE t ADD COLUMN c INT DEFAULT 1

那怎么样:

SET @version := '1';

SELECT CASE val
           WHEN @version THEN 
               'ALTER TABLE t ADD COLUMN c INT DEFAULT 1'
           ELSE 'SELECT ''Wrong version. Nothing to upgrade.'''
       END
INTO   @stmt
FROM   meta 
WHERE  name = 'Version';

PREPARE stmt FROM @stmt;
EXECUTE stmt;

明显的缺点:

  • 严重混乱和开销,因为你必须为每个单独的语句写这个(PREPARE不能批处理多个语句)
  • 额外的烦恼是因为您必须将升级脚本中的每个语句都写成字符串... yuck
  • 并非所有陈述都可以使用PREPARE
  • 编写和执行

解决方案#2:美国一个准备好的声明来杀死连接

您已经指出您不喜欢您链接的解决方案......是因为KILL语句,还是因为存储的函数?如果是因为存储的功能,你可以解决这个问题:

SET @version := '1';

SELECT CASE val 
           WHEN @version THEN 'SELECT ''Performing upgrade...'''
           ELSE CONCAT('KILL CONNECTION', connection_id())
       END
INTO   @stmt
FROM   meta
WHERE  name = 'Version';

PREPARE stmt FROM @stmt;
EXECUTE stmt;  -- connection is killed if the version is not correct.

-- remainder of the upgrade script goes here...
  • mysql必须以--skip-reconnect选项启动,以确保在连接被终止时不能执行任何语句

我试图解决这个问题,并将PREPARE用于阻止当前连接的其他事情,例如删除用户并撤消其权限。不幸的是,这不能按预期工作(不,也不是在FLUSH PRIVILEGES之后)

解决方案#3:使用mysql代理拦截

另一个解决方案:使用代理。使用mysql代理(请参阅:http://forge.mysql.com/wiki/MySQL_Proxy),您可以在lua中编写一个自定义拦截脚本来解释您的自定义命令。使用它你可以添加必要的控制结构。缺点:你现在必须设计自己的迷你语言,并学习lua来解释它:)