MySQL 创建“插入之前”触发器失败并在触发器中引发错误

发布于 2024-09-17 16:32:34 字数 227 浏览 4 评论 0原文

我已经使用 innodb 引擎成功地在表上创建了一个插入前触发器,该引擎通过在不存在的表上执行插入来“抛出”错误。问题是,当我尝试在生产服务器上运行数据库创建脚本时,它失败了,因为插入表不存在。然而,相同的脚本在我的工作站上运行良好,这让我认为有一个 MySQL 配置设置导致创建脚本失败。

我的问题提出的问题是生产服务器是否正在编译触发器而我的工作站是否没有(或在运行时编译)。在生产环境中,我更喜欢在创建时编译 SQL。

I have successfully created an insert before trigger on a table using the innodb engine which "throws" an error by performing an insert on a non-existent table. The problem is that as I try to run the database create script on the production server it fails because the insert table does not exist. However, the same script runs fine on my workstation which makes me think that there is a MySQL config setting which is causing the create script to fail.

The question my issue brings up is whether the production server is compiling the trigger and my workstation is not (or compiling at runtime). On a production environment I would prefer to have the SQL compiled when created.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

随遇而安 2024-09-24 16:32:34

我模拟在触发器中引发错误的方法是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');

请注意,我将局部变量命名为与我要测试的列 bar 相同的名称。这样变量的名称就会出现在错误消息中,它与列相同:

错误 1366 (HY000):整数值不正确:“您无法存储该值!”对于第 1 行的“bar”列

My method for simulating raising an error in a trigger is to DECLARE an integer variable and store a string in it. If you have SQL_MODE = STRICT_ALL_TABLES, this raises an error. Otherwise it raises a warning.

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');

Notice I named my local variable the same as the column bar that I want to test. That way the name of the variable appears in the error message, it's the same as the column:

ERROR 1366 (HY000): Incorrect integer value: 'You cannot store that value!' for column 'bar' at row 1

迷雾森÷林ヴ 2024-09-24 16:32:34

事实证明,表名的长度导致了问题。这两个服务器版本来自不同的版本,服务器是以前的版本(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);
    }
}

It turns out the length of the table name was causing the problem. The two server versions were from different releases with the server being a previous release (5.0 vs 5.1).

As for raising errors in triggers I have taken a different approach, however one that may be flawed depending on how MySQL handles the create trigger statement. If the trigger statements are verified on creation then this will fail.

Once an error is encountered (expected error) I set a session variable (variable name prefixed by an @) with the error message to display. Once the error message is set I do an insert into the non-existing table Die to raise an error in MySQL.

The application then catches the database error and performs a query on the error session variable. Based on the result I either throw an exception with the error message, the database error message or have the query quietly fail leaving the calling code to handle the failed query.

MySQL Trigger:

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$

Application side Code:

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);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文