在PostgreSQL触发器中安全读取JSON字段需用->>、?、@?等操作符,禁用NEW.data.field语法;JSONB支持可靠结构校验,JSON则不可靠且易报错。

触发器里怎么安全读取JSON字段并报错
PostgreSQL 的 JSON 和 JSONB 字段不能直接用 = 或 IS NULL 做结构化校验,必须用 ->、->>、@? 等操作符提取内容。触发器中若直接写 NEW.data.field 会报错 —— 因为 data 是 JSON 类型,不是记录类型。
常见错误现象:ERROR: operator does not exist: json -> unknown(漏写引号)、ERROR: cannot extract field from a scalar(误对字符串用 ->)。
- 用
NEW.json_column ->> 'key'提取文本值(自动转字符串,适合非空/格式校验) - 用
(NEW.json_column -> 'key')::text IS NOT NULL判断字段是否存在且非 null(注意括号和类型转换) - 校验数组长度:用
jsonb_array_length(NEW.json_column -> 'items'),但要先确认items是 JSONB 数组,否则抛错 - 避免在触发器里用
jsonb_path_exists()做复杂断言 —— 它在旧版 PG(
INSERT/UPDATE 触发器中如何阻止非法 JSON 写入
触发器函数返回 NULL 可中止当前行操作(对 BEFORE 触发器有效),这是阻断非法数据最直接的方式。别用 RAISE EXCEPTION 代替 —— 虽然也能报错,但会让事务中断,而你通常只想拒绝这一行。
典型使用场景:要求 config 字段必须包含 {"theme": "dark", "lang": "zh"} 这类固定结构,且 lang 只能是 'zh' 或 'en'。
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
- 先检查字段是否为合法 JSONB:
NEW.config IS NOT NULL AND jsonb_typeof(NEW.config) = 'object' - 再校验必填键:
(NEW.config ? 'theme') AND (NEW.config ? 'lang') - 最后约束值范围:
(NEW.config ->> 'lang') IN ('zh', 'en') - 全部通过才
RETURN NEW;任一失败就RETURN NULL
为什么推荐用 JSONB 而不是 JSON 做触发器校验
JSONB 支持索引、路径查询、存在性操作符(?、@?),而 JSON 类型只能做文本解析,几乎无法在触发器里做可靠结构判断。用 JSON 类型写触发器,等于把校验逻辑推给应用层。
性能与兼容性影响:
-
jsonb_typeof()在JSONB上是 O(1),在JSON上会隐式转换再计算,慢且不可靠 -
NEW.data ? 'field'(存在性判断)仅对JSONB有效,JSON会报operator does not exist - 如果表已用
JSON,迁移只需ALTER TABLE t ALTER COLUMN data TYPE JSONB USING data::jsonb(确保现有数据合法)
触发器函数里处理缺失键或 null 值的惯用写法
JSON 字段里 {"user": null} 和 {}、{"user": "abc"} 是三种不同状态,触发器必须区分。直接写 NEW.data ->> 'user' = 'abc' 会把前两种都判为 false,但你可能需要明确拒绝 null 值。
- 判断键存在且非 null:
(NEW.data ? 'user') AND (NEW.data -> 'user') IS NOT NULL - 判断键存在且值为字符串
'admin':(NEW.data ? 'role') AND (NEW.data ->> 'role') = 'admin' - 允许键缺失但不允许值为 null:
NOT (NEW.data ? 'timeout') OR (NEW.data -> 'timeout') IS NOT NULL - 避免
COALESCE(NEW.data ->> 'flag', 'false')::boolean这类写法 —— 若flag是数字0,转换会失败
最易被忽略的是键存在性与值 null 性的组合判断,少一层括号或漏掉 IS NOT NULL 就会导致校验绕过。

















