postgres insert triggers and/or 可以在不插入的情况下检查 运行

Can postgres insert triggers and/or check be ran without inserting

我希望能够使用数据库的现有约束(引发异常和检查的触发器)来验证代表 table 行的对象,而无需将它们实际插入到数据库中。

目前有没有一种方法可以在 postgres 中做到这一点?至少对于 BEFORE INSERT 触发器和 CHECK,我认为它对于 AFTER INSERT 触发器没有意义。

我能想到或现在最简单的方法是:

  1. 锁定table
  2. 插入一个新行
  3. 如果异常引发 API / 否则删除该行并将其称为有效
  4. 解锁

但我可以看到几个问题。

更简单的方法是在事务中插入而不提交:

BEGIN;
INSERT INTO tbl(...) VALUES (...);
-- see effects ...
ROLLBACK;

无需额外锁定。该行对于具有默认事务隔离级别 READ COMMITTED 的任何其他事务永远不可见。 (您可能会停止与测试行冲突的并发写入。)

Notable side-effect:即使 INSERT 从未提交,serialIDENTITY 列的序列也会被推进。但无论如何,序列号之间的差距是可以预料的,无需担心。

警惕 side-effect 的触发器。所有“事务性”SQL 效果都会回滚,甚至大多数 DDL 命令也是如此。但一些特殊操作(如推进序列)永远不会回滚。

此外,DEFERRED 约束不会生效。The manual:

DEFERRED constraints are not checked until transaction commit.

如果您经常需要它,请使用您的 table 甚至您的数据库的副本。

严格来说,虽然允许任何触发器/约束/并发事件,但除了将它们插入实际目标数据库中的实际目标 table 之外,没有其他方法可以“验证对象”时间点。触发器、约束,甚至默认值,都可以与整个数据库的当前状态交互。排除的可能性越多,要求越低,我们模拟测试的选项就越多。

CREATE FUNCTION validate_function ( )
 RETURNS trigger LANGUAGE plpgsql
 
AS $function$
DECLARE
    valid_flag boolean := 't';
BEGIN
    --Validation code
    if valid_flag = 'f' then
        RAISE EXCEPTION 'This record is not valid id  %', id
        USING HINT = 'Please enter valid record';    
        RETURN NULL;
    else
        RETURN NEW;
    end if;
END;
$function$

CREATE TRIGGER validate_rec BEFORE INSERT OR UPDATE ON some_tbl
    FOR EACH ROW EXECUTE FUNCTION validate_function();

使用此函数和触发器,您可以在触发器内部进行验证。如果新记录验证失败,则将 valid_flag 设置为 false,然后使用它来引发异常。 RETURN NULL; 可能是多余的,我不确定是否会到达,但如果是,它也会中止插入或更新。如果记录有效,那么您 RETURN NEW 并且 insert/update 完成。