我已经使用 innodb 引擎成功地在表上创建了一个插入前触发器,该引擎通过在不存在的表上执行插入来“抛出”错误。问题是当我尝试在生产服务器上运行数据库创建脚本时它失败了,因为插入表不存在。但是,相同的脚本在我的工作站上运行良好,这让我认为有一个 MySQL 配置设置导致创建脚本失败。
我的问题提出的问题是生产服务器是否正在编译触发器而我的工作站不是(或在运行时编译)。在生产环境中,我希望在创建时编译 SQL。
我已经使用 innodb 引擎成功地在表上创建了一个插入前触发器,该引擎通过在不存在的表上执行插入来“抛出”错误。问题是当我尝试在生产服务器上运行数据库创建脚本时它失败了,因为插入表不存在。但是,相同的脚本在我的工作站上运行良好,这让我认为有一个 MySQL 配置设置导致创建脚本失败。
我的问题提出的问题是生产服务器是否正在编译触发器而我的工作站不是(或在运行时编译)。在生产环境中,我希望在创建时编译 SQL。
事实证明,表名的长度导致了问题。这两个服务器版本来自不同的版本,服务器是以前的版本(5.0 与 5.1)。
至于在触发器中引发错误,我采用了一种不同的方法,但是根据 MySQL 处理创建触发器语句的方式,这种方法可能存在缺陷。如果触发器语句在创建时得到验证,那么这将失败。
一旦遇到错误(预期错误),我会设置一个会话变量(以@ 为前缀的变量名)并显示错误消息。设置错误消息后,我会插入不存在的表 Die 以在 MySQL 中引发错误。
然后应用程序捕获数据库错误并对错误会话变量执行查询。根据结果,我要么用错误消息、数据库错误消息抛出异常,要么让查询悄悄失败,让调用代码处理失败的查询。
MySQL触发器:
CREATE TRIGGER Error_Trigger
BEFORE INSERT ON Fubar
FOR EACH ROW
BEGIN
if DataIsBad then
set @Err = 'The data is fubared.';
insert into Die values (1);
end if;
END$$
应用端代码:
public function getDatabaseErrorMessage($AdminMessage = false){
if ($this->db->_error_number() != 0){
$AdminError = $this->db->_error_message();
$Query = $this->db->query("select @Err");
if ($Query){
$Error = $Query->row_array();
}
if (isset($Error["@Err"]) && $Error["@Err"] != ""){
$Error = $Error["@Err"];
$this->db->query("set @Err = ''");
} else {
if ($AdminMessage){
$Error = $AdminError;
} else {
return false;
}
}
throw new Exception($Error);
}
}
我在触发器中模拟引发错误的方法是使用DECLARE
一个整数变量并在其中存储一个字符串。如果您有 SQL_MODE = STRICT_ALL_TABLES
,则会引发错误。否则会发出警告。
use test;
drop table if exists foo;
create table foo (id serial primary key, bar varchar(10));
drop trigger if exists RaiseError;
delimiter //
create trigger RaiseError before insert on foo
for each row
begin
declare bar int;
if new.bar = 'boom' then
set bar = 'You cannot store that value!';
end if;
end //
delimiter ;
set sql_mode = strict_all_tables;
insert into foo (bar) values ('boom');
请注意,我将局部变量命名为与要测试的列栏相同。这样变量的名称就会出现在错误消息中,它与列相同:
错误 1366 (HY000):不正确的整数值:“您无法存储该值!” 对于第 1 行的“bar”列