数据库触发器对于跨表完整性约束是不是安全?

Posted

技术标签:

【中文标题】数据库触发器对于跨表完整性约束是不是安全?【英文标题】:Are database triggers safe for cross table integrity constraints?数据库触发器对于跨表完整性约束是否安全? 【发布时间】:2013-06-02 08:23:33 【问题描述】:

我建议使用触发器来检查回答this question 的交叉表完整性约束。在 cmets 中已经提示它可能会导致问题:

执行跨行检查的触发器很少在大多数数据库上工作......因为它们无法从其他事务中读取未提交的行

不过,我还没有找到任何支持该声明的来源。 Official documentation 没有提及任何内容。我发现的其他问题已涵盖here on SO - 它主要批评潜在的隐藏复杂性,因为第一眼看不到触发器。但即使是highest rated answer 也承认它们用于完整性问题。

所以我的问题是:数据库触发器对于跨表完整性约束是否安全?具体来说,下面的解决方案是否可行?


总结原始问题。我们有桌子

玩家 - 玩家 ID、玩家名称 投注 - BetID、BetName plays_in - BetID、PlayerID

约束是 BetName 和 PlayerID 的组合应该是唯一的。建议触发器的定义:

CREATE TRIGGER check_bet_name BEFORE INSERT ON plays_in 
  FOR EACH ROW BEGIN
      DECLARE bet_exists INT DEFAULT 0;
      DECLARE msg VARCHAR(255);

      SELECT 1 INTO bet_exists 
        FROM Bet AS b1
        WHERE b1.BetID = NEW.BetID
          AND EXISTS (SELECT * 
            FROM plays_in AS p JOIN Bet AS b2 USING (BetID)
            WHERE p.PlayerID = NEW.PlayerID AND b2.BetName = b1.BetName
          )
        LIMIT 1;

      IF bet_exists THEN
        SET msg = "Bet name already exists...";
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg;
      END IF;
  END//

【问题讨论】:

你能否运行一个简单的测试来检查关于无法看到未提交更改的声明(听起来应该是真的)? @Thilo 哦,天哪,你说得对,它真的失败了 【参考方案1】:

答案是触发器不安全

事实证明,触发器确实没有看到其他事务中未提交的更改,并且没有错误地通过。可以这样演示

交易1:

START TRANSACTION;
INSERT INTO plays_in (BetID, PlayerID) VALUES (1,1); -- query A

事务 2:

START TRANSACTION;
INSERT INTO plays_in (BetID, PlayerID) VALUES (1,2); -- query B; in conflict with A, but passses

两笔交易:

COMMIT;

现在plays_in 将包含两个插入的记录,即使 A 和 B 在单个事务中执行,触发器也会抛出错误。

全部示例源码可以在here获取

【讨论】:

【参考方案2】:

这可能取决于哪个数据库以及您编写逻辑的程度。

早期版本的 infomodeler/visiomodeler 支持一些相当神秘和复杂的参照完整性形式,并提供了在多个数据库上实现它们的代码。 Sybase / sql server 的早期版本不支持声明性引用完整性,因此所有逻辑都在触发器中实现 - 成功。

我不会将反例的一个实现的失败视为权威。

【讨论】:

以上是关于数据库触发器对于跨表完整性约束是不是安全?的主要内容,如果未能解决你的问题,请参考以下文章

在SQLServer使用触发器实现数据完整性

图书管理系统总结——数据库操纵:数据库安全性

数据库完整性

数据库原理实验(openGauss)完整性控制

数据库完整性-第四六七节:约束命名子句断言和触发器

MySQL系列 MySQL的约束