PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

D.3. XML 限制与 SQL/XML 符合度 #

D.3.1. 查询被限制为 XPath 1.0
D.3.2. 实现的附带限制

ISO/IEC 9075-14(SQL/XML)中与 XML 相关的规范在 SQL:2006 中经历了重大修订。 PostgreSQL 对 XML 数据类型及相关函数的实现大体遵循较早的 2003 版,并从后续版本借鉴了一些内容。具体而言:

本节介绍你可能遇到的一些由此产生的差异。

D.3.1. 查询被限制为 XPath 1.0 #

PostgreSQL 专有的函数 xpath() 与 xpath_exists() 使用 XPath 语言查询 XML 文档。 PostgreSQL 还为官方使用 XQuery 语言的标准函数 XMLEXISTS 与 XMLTABLE 提供了仅支持 XPath 的变体。对所有这些函数, PostgreSQL 依赖 libxml2 库,而该库只提供 XPath 1.0。

XQuery 语言与 XPath 2.0 及更高版本之间有很强的关联:任何在两者中都语法有效且执行成功的表达式会产生相同的结果(一个次要例外是包含数字字符引用或预定义实体引用的表达式,XQuery 会用对应字符替换它们,而 XPath 保持原样)。但这些语言与 XPath 1.0 之间没有这种关联;它是一门更早的语言,在许多方面都有差异。

有两类限制需要记住:SQL 标准规定的函数从 XQuery 到 XPath 的限制,以及标准函数与 PostgreSQL 专有函数共同面临的 XPath 限制到 1.0 版。

D.3.1.1. 从 XQuery 到 XPath 的限制 #

XQuery 超出 XPath 的特性包括:

  • XQuery 表达式可以构造并返回新的 XML 节点,此外还能返回所有可能的 XPath 值。XPath 可以创建并返回原子类型的值(数字、字符串等),但只能返回已经出现在作为表达式输入的文档中的 XML 节点。

  • XQuery 具有用于迭代、排序与分组的控制结构。

  • XQuery 允许声明并使用局部函数。

较新的 XPath 版本开始提供与这些能力重叠的功能(例如函数式风格的 for-each 与 sort、匿名函数,以及从字符串创建节点的 parse-xml),但这些特性在 XPath 3.0 之前并不可用。

D.3.1.2. 把 XPath 限制到 1.0 #

对于熟悉 XQuery 与 XPath 2.0 及更高版本的开发者来说,XPath 1.0 带来了许多需要应对的差异:

  • XQuery/XPath 表达式的基本类型 序列(它可以包含 XML 节点、原子值,或两者兼有)在 XPath 1.0 中并不存在。1.0 表达式只能产生节点集(包含零个或多个 XML 节点)或单个原子值。

  • 与可以按任意期望顺序包含任意期望条目的 XQuery/XPath 序列不同,XPath 1.0 的节点集没有任何顺序保证,而且与任何集合一样,不允许同一条目多次出现。

    Note

    libxml2 库看起来确实总是以成员在输入文档中的相对顺序把节点集返回给 PostgreSQL。它的文档并未承诺这一行为,XPath 1.0 表达式也无法控制它。

  • XQuery/XPath 提供了 XML Schema 定义的所有类型以及作用于这些类型的众多操作符与函数,而 XPath 1.0 只有节点集以及三个原子类型 boolean、double 与 string。

  • XPath 1.0 没有条件操作符。诸如 if ( hat ) then hat/@size else "no hat" 这样的 XQuery/XPath 表达式在 XPath 1.0 中没有对应写法。

  • XPath 1.0 没有针对字符串的排序比较操作符。"cat" < "dog" 与 "cat" > "dog" 都为假,因为每个都是两个 NaN 之间的数值比较。相比之下, = 与 != 确实按字符串方式比较字符串。

  • XPath 1.0 混淆了 XQuery/XPath 所定义的 值比较 与 一般比较 的区别。sale/@hatsize = 7 与 sale/@customer = "alice" 都是存在量词比较,只要存在任何 sale 在该属性上具有给定值即为真;而 sale/@taxable = false() 是与整个节点集的 有效布尔值 进行的值比较,只有当完全没有 sale 拥有 taxable 属性时才为真。

  • 在 XQuery/XPath 数据模型中,文档节点 可以处于文档形式(即恰好一个顶层元素,其外只有注释与处理指令)或内容形式(放宽上述约束)。它在 XPath 1.0 中的对应物 根节点 只能处于文档形式。这也是作为上下文项传递给任何 PostgreSQL 基于 XPath 的函数的 xml 值必须处于文档形式的部分原因。

这里强调的差异并非全部。在 XQuery 与 XPath 2.0 及更高版本中存在一种 XPath 1.0 兼容模式,W3C 列出的该模式下的 函数库变更 与 语言变更 对这些差异给出了更完整(但仍不穷尽)的说明。兼容模式无法使后续语言与 XPath 1.0 完全等效。

D.3.1.3. SQL 与 XML 数据类型及值之间的映射 #

在 SQL:2006 及更高版本中,标准 SQL 数据类型与 XML Schema 类型之间双向转换都被精确规定。不过,这些规则使用 XQuery/XPath 的类型与语义表述,对 XPath 1.0 不同的数据模型没有直接适用性。

当 PostgreSQL 把 SQL 数据值映射为 XML(如 xmlelement),或把 XML 映射为 SQL(如 xmltable 的输出列)时,除少数被特殊处理的情况外, PostgreSQL 只是简单地假定 XML 数据类型的 XPath 1.0 字符串形式作为 SQL 数据类型的文本输入形式有效,反之亦然。这条规则的好处是简单,同时对于许多数据类型能产生与标准规定的映射相似的结果。

在需要与其他系统互操作时,对某些数据类型可能有必要显式使用数据类型格式化函数(例如 Section 9.8 中的那些)来产生标准的映射。

D.3.2. 实现的附带限制 #

本节关注的限制并非 libxml2 库所固有,而是适用于 PostgreSQL 中的当前实现。

D.3.2.1. 只支持 BY VALUE 传参机制 #

SQL 标准定义了两种 传参机制,适用于把 XML 参数从 SQL 传给 XML 函数或接收结果的场合: BY REF 保持特定 XML 值的节点标识, BY VALUE 只传递 XML 的内容而保留节点标识。机制可以在参数列表之前指定,作为所有参数的默认机制;也可以放在任何参数之后,以覆盖默认值。

为说明差异,如果 x 是一个 XML 值,在 SQL:2006 环境中以下两条查询会分别产生真与假:

SELECT XMLQUERY('$a is $b' PASSING BY REF x AS a, x AS b NULL ON EMPTY);
SELECT XMLQUERY('$a is $b' PASSING BY VALUE x AS a, x AS b NULL ON EMPTY);

PostgreSQL 会在 XMLEXISTS 或 XMLTABLE 结构中接受 BY VALUE 或 BY REF,但会忽略它们。xml 数据类型保存的是字符序列化的表示,因此没有节点标识可保留,传参实际上总是 BY VALUE。

D.3.2.2. 无法向查询传递命名参数 #

基于 XPath 的函数支持传递一个参数作为 XPath 表达式的上下文项,但不支持传递额外的值供表达式作为命名参数使用。

D.3.2.3. 没有 XML(SEQUENCE) 类型 #

PostgreSQL 的 xml 数据类型只能以 DOCUMENT 或 CONTENT 形式保存值。XQuery/XPath 表达式上下文项必须是单个 XML 节点或原子值,但 XPath 1.0 进一步把它限制为只能是 XML 节点,并且没有允许 CONTENT 的节点类型。结论是:格式良好的 DOCUMENT 是 PostgreSQL 能作为 XPath 上下文项提供的唯一 XML 值形式。