XML 类型
PostgreSQL 的 xml 类型保存格式良好的 XML 文档或内容片段,并提供 XPath 查询函数。它适合兼容已有 XML 协议、配置和交换格式;新应用若没有 XML 互操作需求,不应为了“结构化”而默认选择它。
文档与内容片段
xml 可以配置为只接受完整文档,也可以接受 XML 内容片段:
CREATE TABLE messages (
message_id bigint PRIMARY KEY,
document xml NOT NULL
);
INSERT INTO messages VALUES
(1, '<order id="A-1"><total>12.50</total></order>');
输入会检查 XML 是否格式良好,但不会自动验证 XSD、业务字段、签名或外部实体策略。格式正确不等于业务有效。
XPath 查询
SELECT xpath('/order/@id', document)
FROM messages;
SELECT xpath('/order/total/text()', document)
FROM messages;
xpath 返回 xml[],即使只有一个结果也可能是数组;应用或 SQL 需要明确取元素并转换为目标类型。XPath 表达式和 XML 内容都应参数化或经过可信构造,不要通过字符串拼接接受任意查询表达式。
命名空间
带命名空间的 XML 查询需要提供前缀映射:
SELECT xpath(
'/p:order/p:total/text()',
'<p:order xmlns:p="urn:example"><p:total>12.50</p:total></p:order>',
ARRAY[ARRAY['p', 'urn:example']]
);
XPath 中的前缀只是查询映射,不要求与文档中实际使用的前缀文字相同;URI 才是命名空间身份。
编码、实体和安全
XML 可能包含实体、外部资源和大型嵌套结构。解析来自不可信来源的 XML 时,应在应用和数据库驱动层限制实体解析、大小和深度,防范 XXE、实体扩展和资源耗尽。PostgreSQL 的类型检查不能替代完整的安全策略。
XML 文本的字符编码在进入数据库前通常已转换为服务器编码。需要保留原始字节、签名或精确序列化格式时,应额外保存原始 bytea 或哈希。
索引与选型
xml 没有像 JSONB 那样通用的 GIN 索引。频繁查询的字段应抽取为普通列、生成列或辅助表并建立合适索引:
ALTER TABLE messages
ADD COLUMN order_id text
GENERATED ALWAYS AS ((xpath('/order/@id', document))[1]::text) STORED;
复杂 XML 搜索可考虑应用层解析或专门 XML 数据库。不要每次查询都扫描并解析大量文档后再判断。
常见误区
- 把
xml的格式校验当成 XSD 或业务校验。 - 忽略命名空间,使用看似正确但匹配不到节点的 XPath。
- 把 XML 当任意文本存储,无法利用结构查询,却承受了解析成本。
- 直接展示未经转义的 XML 内容,造成 HTML/XML 注入风险。
- 没有为大文档限制大小、深度和解析时间。