您可以尝试删除该行并回滚效果。您不希望在触发器函数中这样做,因为任何异常都会取消对数据库的所有持久更改。手册:
当一个EXCEPTION
子句捕捉到错误时,PL/pgSQL 函数的局部变量保持与错误发生时一样,但对块内持久数据库状态的所有更改都将回滚。
大胆强调我的。
但是您可以将其包装到单独的块或单独的plpgsql 函数中并在那里捕获异常以防止对主(触发器)函数的影响。
CREATE OR REPLACE FUNCTION f_can_del(_id int)
RETURNS boolean AS
$func$
BEGIN
DELETE FROM master WHERE master_id = _id; -- DELETE is always rolled back
IF NOT FOUND THEN
RETURN NULL; -- ID not found, return NULL
END IF;
RAISE SQLSTATE 'MYERR'; -- If DELETE, raise custom exception
EXCEPTION
WHEN FOREIGN_KEY_VIOLATION THEN
RETURN FALSE;
WHEN SQLSTATE 'MYERR' THEN
RETURN TRUE;
-- other exceptions are propagated as usual
END
$func$ LANGUAGE plpgsql;
这返回TRUE
//表示该行可以删除FALSE
/NULL
不能删除/不存在。
db<>fiddle here
旧sqlfiddle
可以轻松地使该函数动态化以测试任何表/列/值。
从PostgreSQL 9.2开始,您还可以报告哪个表被阻塞。
PostgreSQL 9.3或更高版本提供了更详细的信息。
任意表、列和类型的通用函数
为什么您在评论中发布的动态功能尝试失败?手册中的这句话应该提供线索:
特别注意,EXECUTE
改变 的输出GET DIAGNOSTICS
,但不改变FOUND
。
它适用于GET DIAGNOSTICS
:
CREATE OR REPLACE FUNCTION f_can_del(_tbl regclass, _col text, _id int)
RETURNS boolean AS
$func$
DECLARE
_ct int; -- to receive count of deleted rows
BEGIN
EXECUTE format('DELETE FROM %s WHERE %I = $1', _tbl, _col)
USING _id; -- exception if other rows depend
GET DIAGNOSTICS _ct = ROW_COUNT;
IF _ct > 0 THEN
RAISE SQLSTATE 'MYERR'; -- If DELETE, raise custom exception
ELSE
RETURN NULL; -- ID not found, return NULL
END IF;
EXCEPTION
WHEN FOREIGN_KEY_VIOLATION THEN
RETURN FALSE;
WHEN SQLSTATE 'MYERR' THEN
RETURN TRUE;
-- other exceptions are propagated as usual
END
$func$ LANGUAGE plpgsql;
db<>fiddle here
旧sqlfiddle
在此过程中,我将其设置为完全动态的,包括列的数据类型(当然,它必须与给定的列匹配)。我为此目的使用多态类型。anyelement
看:
我还使用format()
和 类型的参数regclass
来防范 SQLi。看: