在我组织的一个数据库中,有一张表
customerReview
。 简化后,它看起来像:
CREATE TABLE customerReview(
customerReviewId SERIAL PRIMARY KEY,
dateTimeCreated TIMESTAMP WITHOUT TIME ZONE,
dateTimeUpdated TIMESTAMP WITHOUT TIME ZONE,
businessID INTEGER,
reviewText TEXT,
reviewerIP TEXT,
isIPv6 BOOLEAN
)
我没有创建表,但我添加了
isIPv6
列和触发器函数:
CREATE OR REPLACE FUNCTION review_ip_version()
RETURNS TRIGGER AS
$BDY$
IF NEW.reviewerip ~ '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$' THEN
NEW.isIPv6 := FALSE;
ELSIF NEW.reviewerip ~ '^[0-9a-f]{0,4}(:[0-9a-f]{0,4}){2,7}$'
AND NEW.reviewerip !~ '::.*::|:::|[0-9a-f]{5,}|(:[0-9a-f]*){7}:' THEN
NEW.isIPv6 := TRUE
END IF;
RETURN NEW;
END;
$BDY$
LANGUAGE 'plpgsql';
目的是当
isIPv6
匹配有效的 IPv6 模式时 reviewerip
为 true,当它匹配有效的 IPv4 模式时为 false,否则为 null。 经过测试,它可以在包括 NULL 在内的各种模式下准确执行。
调用该函数的触发器是
BEFORE INSERT OR UPDATE
。 在添加触发器之前,在同一事务中,我使用相同的正则表达式更新了所有现有行以具有正确的 isIPv6
值。 这只是说我确信 isIPv6
列值在创建触发器后看起来与应有的样子完全一样。第二天测试也成功了。
触发器工作了大约一个月,没有任何问题,但就在今天,有些东西停止工作了。 具体来说,我们有 7 行失败。 所有 7 行都有
reviewerip = NULL
。 6 行有 isIPv6 = FALSE
,而 1 行有 isIPv6 = TRUE
(值应为 NULL)。 我知道它今天停止工作了,因为其他地方依赖于isIPv6
正确的流程刚刚中断。 触发器仍然存在并且适用于绝大多数行。
我的工作假设是某些进程正在更新值,但没有启动触发器。 不幸的是,我的组织没有保留日志表,并且
dateTimeUpdated
并不可靠,因为它不是在数据库级别强制执行的,而是通过某些 Web 进程强制执行的。 所以我没有什么可做的了。 但是是否存在一些模糊的情况,更新可以绕过表上的 BEFORE INSERT OR UPDATE
触发器?
您的触发函数存在语法错误:缺少首字母
BEGIN
和分号。
修复这些错误后,情况对我来说似乎很清楚:如果我用
reviewerip
的值更新一行,而该行与任一模式都不匹配(如 NULL),则 NEW.isIPv6
不会更改,因此它只保留它所具有的值更新之前。
您必须改进触发器功能,例如通过添加将列设置为 NULL 的
ELSE
分支。