D.3. XML 的限制以及对 SQL/XML 的符合性 #
SQL:2006 对 ISO/IEC 9075-14(SQL/XML)中的 XML 相关规范做了重大修订。PostgreSQL 对 XML 数据类型及相关函数的实现,大体遵循较早的 2003 年版,同时也借鉴了后续版本的一些内容。特别是:
当现行标准提供一组 XML 数据类型,用于以无类型或 XML Schema 类型化的变体保存“文档”或“内容”,并提供
XML(SEQUENCE)类型来保存任意 XML 内容片段时,PostgreSQL 只提供单一的xml类型,该类型可以保存“文档”或“内容”。标准中的“sequence”类型在这里没有对应物。PostgreSQL 提供了 SQL:2006 引入的两个函数,但其变体使用的是 XPath 1.0,而不是标准为其规定的 XML Query 语言。
PostgreSQL 不支持
RETURNING CONTENT或RETURNING SEQUENCE子句;规范中被定义为带有这些子句的函数,在这里都隐式返回内容。
本节介绍由此产生的一些差异。
D.3.1. 查询受限于 XPath 1.0 #
PostgreSQL 特有的函数
xpath() 和 xpath_exists()
使用 XPath 语言查询 XML 文档。PostgreSQL
还提供了标准函数 XMLEXISTS 和 XMLTABLE 的仅限 XPath 版本,而这两个标准函数正式使用的是 XQuery 语言。对于所有这些函数,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 表达式除了能够产生 XPath 所有可能的值之外,还可以构造并返回新的 XML 节点。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 表达式的基础类型
sequence可以包含 XML 节点、原子值或两者兼有,但 XPath 1.0 中不存在这一类型。1.0 表达式只能产生一个节点集(包含零个或多个 XML 节点),或者一个单一的原子值。与可按任意顺序包含任意条目的 XQuery/XPath 序列(sequence)不同,XPath 1.0 的节点集没有保证顺序,并且像任何集合一样,不允许同一条目出现多次。
注意
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 中对应的根节点只能是文档形式。这也是为什么
xml值在作为上下文项传给任何基于 XPath 的 PostgreSQL 函数时,必须采用文档形式的部分原因。
这里强调的差异并不全面。在 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 数据类型的文本输入形式,反之亦然。这一规则的优点在于简单,同时对许多数据类型来说,其结果也与标准规定的映射大致相似。
如果需要与其他系统互操作,那么对于某些数据类型,可能需要显式使用数据类型格式化函数(例如第 9.8 节中的函数)来生成标准规定的映射。
D.3.2. 实现带来的附带限制 #
本节讨论的限制并非 libxml2 库本身固有,而是适用于 PostgreSQL 当前实现的限制。
D.3.2.1. 仅支持 BY VALUE 传递机制 #
SQL 标准定义了两种传递机制,适用于从 SQL 向 XML 函数传递 XML 参数,或在接收结果时使用:BY REF 表示某个特定 XML 值保留其节点身份,BY VALUE 表示传递 XML 的内容,但不保留节点身份。该机制既可以写在参数列表之前,作为所有参数的默认机制,也可以写在任一参数之后,以覆盖默认机制。
为了说明其中的区别,如果 x 是一个 XML 值,那么在 SQL:2006 环境中,下面两个查询将分别产生 true 和 false:
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 会接受 BY VALUE 或
BY REF 出现在 XMLEXISTS 或
XMLTABLE 结构中的用法,但会忽略它们。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 值形式。
报告文档问题
阅读 上游文档. 通过 PostgreSQL 文档反馈表单.