PostgreSQL带嵌套IF ELSE的事件触发器函数语法错误排查
报错根因
- 嵌套IF语句未正确闭合:代码中一共写了两层IF判断,但仅配置了1个
END IF标记,PL/pgSQL语法解析时无法匹配块结构,直接触发42601语法错误,提示的「分号位置错误」「SQL表达式末尾缺少THEN」都是块结构不匹配引发的连锁报错。 - 角色校验逻辑不符合需求:原有写法
EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'test' or rolname = 'testa')的判定规则是「两个角色存在任意一个即校验通过」,和预期的「所有指定角色全部存在才放行,缺任意一个就抛异常」逻辑完全相反。 - 冗余变量:声明的
audit_query变量全程未使用,虽然不会直接触发报错,但属于无效代码。
修正后完整代码
-- 提前创建审计schema和表,可根据实际业务调整字段 -- CREATE SCHEMA IF NOT EXISTS user_monitor; -- CREATE TABLE IF NOT EXISTS user_monitor.ddl_history ( -- ddl_date timestamptz, -- ddl_tag text, -- object_name text -- ); CREATE OR REPLACE FUNCTION public.fn_ddl_audit() RETURNS event_trigger AS $$ DECLARE r RECORD; v_valid_role_cnt INT; BEGIN -- 校验指定角色是否全部存在 SELECT count(1) INTO v_valid_role_cnt FROM pg_catalog.pg_roles WHERE rolname IN ('test', 'testa'); IF v_valid_role_cnt < 2 THEN RAISE EXCEPTION 'Not all required operation roles exist, DDL blocked'; END IF; -- 判断触发事件是否为目标建表操作 IF tg_tag IN ('CREATE TABLE', 'CREATE TABLE AS') THEN -- 筛选指定schema下的操作写入审计记录 FOR r IN SELECT * FROM pg_event_trigger_ddl_commands() ddl WHERE ddl.schema_name IN ('testb', 'testc', 'testd') LOOP INSERT INTO user_monitor.ddl_history (ddl_date, ddl_tag, object_name) VALUES (statement_timestamp(), tg_tag, r.object_identity); END LOOP; END IF; END; $$ LANGUAGE plpgsql SECURITY DEFINER;
后续配置说明
- 函数创建完成后需要绑定到DDL事件触发器才能生效,绑定语句参考:
CREATE EVENT TRIGGER trg_ddl_audit ON ddl_command_end WHEN TAG IN ('CREATE TABLE', 'CREATE TABLE AS') EXECUTE FUNCTION public.fn_ddl_audit();
- 如果需要新增校验的角色,同步修改
rolname IN (...)的角色列表,同时把v_valid_role_cnt < 2中的数字调整为实际要校验的角色总数即可。 - 建议给函数加
SECURITY DEFINER属性并设置属主为超级用户,避免普通用户因为没有审计表写入权限、系统表查询权限导致正常DDL被阻断。
内容的提问来源于stack exchange,提问作者nick
相关产品推荐
相关产品推荐

