D.3. Ограничения XML и совместимость с SQL/XML

В SQL:2006 были внесены значительные изменения в посвящённой XML части ISO/IEC 9075-14 (SQL/XML). Реализация типа данных XML и связанных функций в Postgres Pro в большей степени соответствует более ранней редакции, SQL:2003, с некоторыми заимствованиями из последующих редакций. В частности:

  • Тогда как в текущем стандарте существует семейство типов данных XML, содержащих «документы» или «содержимое» в нетипизированном виде или с типами XML Schema, а также тип XML(SEQUENCE), содержащий произвольные части XML-документа, в Postgres Pro есть только один тип xml, который может содержать «документ» или «содержимое». Определённый в стандарте тип «последовательность» в Postgres Pro отсутствует.

  • Postgres Pro предоставляет две функции, появившиеся в SQL:2006, но вместо языка XML Query, как должно быть согласно стандарту, в них используется язык XPath 1.0.

В этом разделе описаны некоторые из образовавшихся в итоге различий, с которыми вы можете столкнуться.

D.3.1. Запросы ограничиваются XPath версии 1.0

Специфичные для Postgres Pro функции xpath() и xpath_exists() выполняют запросы к XML-документам на языке XPath. В Postgres Pro также имеются поддерживающие только XPath стандартные функции XMLEXISTS и XMLTABLE, хотя согласно стандарту они должны поддерживать XQuery. Все эти функции в Postgres Pro реализованы с использованием библиотеки libxml2, которая поддерживает только XPath 1.0.

Существует тесная связь между языком XQuery и XPath версии 2.0 и новее: любое выражение, синтаксически правильное и выполняющееся успешно, выдаёт в обоих языках одинаковые результаты (за незначительным исключением, связанным с числовым обозначением символов или использованием предопределённых сущностей — XQuery заменяет их соответствующими символами, а XPath оставляет в исходном виде). Но между XPath 1.0 и этими языками подобная связь отсутствует: он появился гораздо раньше и во многом отличается от них.

Заслуживают отдельного рассмотрения две категории ограничений: ограничение языка XQuery до XPath для функций, описанных в стандарте SQL, и ограничение XPath до версии 1.0 как для стандартизированных функций, так и для специфичных функций Postgres Pro.

D.3.1.1. Ограничение языка XQuery до XPath

В число отличий XQuery от XPath входят:

  • Выражения XQuery могут выдавать не только всевозможные значения XPath, но и конструировать новые XML-узлы. XPath может создавать и возвращать значения атомарных типов (числа, строки и так далее), но выдаваемые им XML-узлы должны уже присутствовать в документе, поступившем на вход выражения.

  • В XQuery есть управляющие конструкции для организации циклов, сортировки и группировки.

  • В XQuery поддерживается объявление и использование локальных функций.

В последних версиях XPath начинают появляться возможности, пересекающиеся с имеющимися в XQuery (например, конструкции 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, которые могут содержать произвольные элементы в любом требующемся порядке, во множестве узлов XPath 1.0 нет гарантированного порядка, и оно, как и любое другое множество, не может содержать несколько вхождений одного элемента.

    Примечание

    Библиотека libxml2 не всегда возвращает в Postgres Pro наборы узлов с внутренними членами в том порядке, в котором они идут во входном документе. В её документации не гарантируется корректное поведение, а выражение XPath 1.0 не может на это воздействовать.

  • Тогда как XQuery/XPath поддерживают все типы, определённые в стандарте XML Schema, а также множество операторов и функций, работающих с этими типами, XPath 1.0 поддерживает только множества узлов и три атомарных типа: boolean, double и string.

  • В XPath 1.0 отсутствует условный оператор. Выражение XQuery/XPath вида if ( hat ) then hat/@size else "no hat" не имеет эквивалента в 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, передаваемое в качестве элемента контекста любым функциям Postgres Pro на базе XPath, должно быть в форме документа.

Кроме отмеченных выше имеются и другие различия. В языках 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.

Когда Postgres Pro сопоставляет значения данных SQL с XML (как в функции xmlelement), или XML с SQL (как в выходных столбцах xmltable), за исключением нескольких отдельно обрабатываемых случаев, Postgres Pro просто полагает, что строка XPath 1.0, содержащая данные типа XML, будет допустимой для ввода в текстовом виде в тип данных SQL, и наоборот. Это правило добродетельно своей простотой, и при этом преобразования для многих типов данных в итоге оказываются такими, какими и должны быть согласно стандарту. В текущем выпуске требуется явное преобразование, если выражение столбца xmltable выдаёт логическое значение или число с плавающей точкой; см. Подраздел D.3.2.

Там же, где это нужно для взаимодействия с другими системами, для некоторых типов данных можно явно использовать функции форматирования типов данных (например, описанные в Разделе 9.8) для получения преобразований, в точности соответствующих стандарту.

D.3.2. Непреднамеренные ограничения реализации

В этом разделе описываются дополнительные ограничения, присущие текущей реализации в Postgres Pro, но не самой библиотеке libxml2.

D.3.2.1. Необходимость приведения столбцов xmltable, имеющих логический или числовой тип

Выражение столбца xmltable, результатом которого должно быть логическое или числовое значение XPath, будет выдавать ошибку «unexpected XPath object type» (неожиданный тип объекта XPath). Чтобы обойти эту ошибку, это выражение надо поместить внутрь вызова функции string языка XPath; в этом случае Postgres Pro сможет успешно присвоить строковое значение выходному столбцу SQL, имеющему логический или числовой тип с плавающей точкой.

D.3.2.2. Результат вычисления пути или выходной столбец SQL типа XML

В текущей версии выражение столбца xmltable, выдающее набор XML-узлов, может быть присвоено выходному SQL-столбцу типа XML. В этом случае результатом будет конкатенация следующих данных: для большинства типов узлов в наборе узлов берётся текстовый узел со строковым значением узла в определении XPath 1.0, но для узлов—элементов — копия этого узла. Такой набор узлов может быть присвоен SQL-столбцу, имеющему не XML-тип, только если этот набор содержит один узел. При этом строковое значение узлов большинства типов заменяется пустой строкой, строковое значение узла-элемента заменяется конкатенацией только его непосредственных потомков — текстовых узлов (другие потомки исключаются из рассмотрения), а строковое значение текстового узла или атрибута выдаётся согласно определению XPath 1.0. Строковое выражение XPath, присваиваемое выходному столбцу типа XML, должно проходить разбор XML.

При разработке кода не рекомендуется полагаться на описанное особое поведение: во-первых, оно не вполне соответствует стандарту, а во-вторых, в Postgres Pro 12 оно изменено.

D.3.2.3. Передача параметров только по значению (BY VALUE)

В стандарте SQL определены два механизма передачи параметров, осуществляющих передачу XML-аргумента из SQL в XML-функцию или получение результата: BY REF, в котором конкретное значение в XML остаётся привязанным к своему узлу, и BY VALUE, в котором передаётся содержимое XML, но связь с узлом теряется. Выбрать механизм можно перед списком параметров, в качестве механизма по умолчанию для всех параметров, или после каждого отдельного параметра, переопределив тем самым выбор по умолчанию.

В качестве иллюстрации различия взгляните на следующие два запроса, которые в окружении SQL:2006 выдают true и false, если x является XML-значением:

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);

В этом выпуске Postgres Pro принимает указание BY REF в конструкции XMLEXISTS или XMLTABLE, но игнорирует его. Тип xml содержит сериализованное представление данных в текстовом виде, поэтому сущность узла, которую нужно сохранять, отсутствует, и передача фактически производится по значению (BY VALUE).

D.3.2.4. Отсутствие именованных параметров запросов

Функции на базе XPath могут принимать один параметр, служащий контекстным элементом для выражения XPath, но не поддерживают передачу дополнительных значений, которые могли бы использоваться в выражении как именованные параметры.

D.3.2.5. Отсутствие типа XML(SEQUENCE)

Тип данных xml в Postgres Pro может содержать значение только в форме документа (DOCUMENT) или содержимого (CONTENT). Контекстный элемент выражения XQuery/XPath должен быть одиночным XML-узлом или атомарным значением, но в XPath 1.0 это может быть только XML-узел, и при этом нет типа узла, содержащего CONTENT. Как следствие, в Postgres Pro в качестве контекстного элемента XPath можно передать данные XML в единственном виде — в виде правильно оформленного документа (DOCUMENT).

D.3. XML Limits and Conformance to SQL/XML

Significant revisions to the XML-related specifications in ISO/IEC 9075-14 (SQL/XML) were introduced with SQL:2006. Postgres Pro's implementation of the XML data type and related functions largely follows the earlier 2003 edition, with some borrowing from later editions. In particular:

  • Where the current standard provides a family of XML data types to hold document or content in untyped or XML Schema-typed variants, and a type XML(SEQUENCE) to hold arbitrary pieces of XML content, Postgres Pro provides the single xml type, which can hold document or content. There is no equivalent of the standard's sequence type.

  • Postgres Pro provides two functions introduced in SQL:2006, but in variants that use the XPath 1.0 language, rather than XML Query as specified for them in the standard.

This section presents some of the resulting differences you may encounter.

D.3.1. Queries are restricted to XPath 1.0

The Postgres Pro-specific functions xpath() and xpath_exists() query XML documents using the XPath language. Postgres Pro also provides XPath-only variants of the standard functions XMLEXISTS and XMLTABLE, which officially use the XQuery language. For all of these functions, Postgres Pro relies on the libxml2 library, which provides only XPath 1.0.

There is a strong connection between the XQuery language and XPath versions 2.0 and later: any expression that is syntactically valid and executes successfully in both produces the same result (with a minor exception for expressions containing numeric character references or predefined entity references, which XQuery replaces with the corresponding character while XPath leaves them alone). But there is no such connection between these languages and XPath 1.0; it was an earlier language and differs in many respects.

There are two categories of limitation to keep in mind: the restriction from XQuery to XPath for the functions specified in the SQL standard, and the restriction of XPath to version 1.0 for both the standard and the Postgres Pro-specific functions.

D.3.1.1. Restriction of XQuery to XPath

Features of XQuery beyond those of XPath include:

  • XQuery expressions can construct and return new XML nodes, in addition to all possible XPath values. XPath can create and return values of the atomic types (numbers, strings, and so on) but can only return XML nodes that were already present in documents supplied as input to the expression.

  • XQuery has control constructs for iteration, sorting, and grouping.

  • XQuery allows declaration and use of local functions.

Recent XPath versions begin to offer capabilities overlapping with these (such as functional-style for-each and sort, anonymous functions, and parse-xml to create a node from a string), but such features were not available before XPath 3.0.

D.3.1.2. Restriction of XPath to 1.0

For developers familiar with XQuery and XPath 2.0 or later, XPath 1.0 presents a number of differences to contend with:

  • The fundamental type of an XQuery/XPath expression, the sequence, which can contain XML nodes, atomic values, or both, does not exist in XPath 1.0. A 1.0 expression can only produce a node-set (containing zero or more XML nodes), or a single atomic value.

  • Unlike an XQuery/XPath sequence, which can contain any desired items in any desired order, an XPath 1.0 node-set has no guaranteed order and, like any set, does not allow multiple appearances of the same item.

    Note

    The libxml2 library does seem to always return node-sets to Postgres Pro with their members in the same relative order they had in the input document. Its documentation does not commit to this behavior, and an XPath 1.0 expression cannot control it.

  • While XQuery/XPath provides all of the types defined in XML Schema and many operators and functions over those types, XPath 1.0 has only node-sets and the three atomic types boolean, double, and string.

  • XPath 1.0 has no conditional operator. An XQuery/XPath expression such as if ( hat ) then hat/@size else "no hat" has no XPath 1.0 equivalent.

  • XPath 1.0 has no ordering comparison operator for strings. Both "cat" < "dog" and "cat" > "dog" are false, because each is a numeric comparison of two NaNs. In contrast, = and != do compare the strings as strings.

  • XPath 1.0 blurs the distinction between value comparisons and general comparisons as XQuery/XPath define them. Both sale/@hatsize = 7 and sale/@customer = "alice" are existentially quantified comparisons, true if there is any sale with the given value for the attribute, but sale/@taxable = false() is a value comparison to the effective boolean value of a whole node-set. It is true only if no sale has a taxable attribute at all.

  • In the XQuery/XPath data model, a document node can have either document form (i.e., exactly one top-level element, with only comments and processing instructions outside of it) or content form (with those constraints relaxed). Its equivalent in XPath 1.0, the root node, can only be in document form. This is part of the reason an xml value passed as the context item to any Postgres Pro XPath-based function must be in document form.

The differences highlighted here are not all of them. In XQuery and the 2.0 and later versions of XPath, there is an XPath 1.0 compatibility mode, and the W3C lists of function library changes and language changes applied in that mode offer a more complete (but still not exhaustive) account of the differences. The compatibility mode cannot make the later languages exactly equivalent to XPath 1.0.

D.3.1.3. Mappings between SQL and XML data types and values

In SQL:2006 and later, both directions of conversion between standard SQL data types and the XML Schema types are specified precisely. However, the rules are expressed using the types and semantics of XQuery/XPath, and have no direct application to the different data model of XPath 1.0.

When Postgres Pro maps SQL data values to XML (as in xmlelement), or XML to SQL (as in the output columns of xmltable), except for a few cases treated specially, Postgres Pro simply assumes that the XML data type's XPath 1.0 string form will be valid as the text-input form of the SQL datatype, and conversely. This rule has the virtue of simplicity while producing, for many data types, results similar to the mappings specified in the standard. In this release, an explicit cast is needed if an xmltable column expression produces a boolean or double value; see Section D.3.2.

Where interoperability with other systems is a concern, for some data types, it may be necessary to use data type formatting functions (such as those in Section 9.8) explicitly to produce the standard mappings.

D.3.2.  Incidental limits of the implementation

This section concerns limits that are not inherent in the libxml2 library, but apply to the current implementation in Postgres Pro.

D.3.2.1.  Cast needed for xmltable column of boolean or double type

An xmltable column expression evaluating to an XPath boolean or number result will produce an unexpected XPath object type error. The workaround is to rewrite the column expression to be inside the XPath string function; Postgres Pro will then assign the string value successfully to an SQL output column of boolean or double type.

D.3.2.2.  Column path result or SQL result column of XML type

In this release, a xmltable column expression that evaluates to an XML node-set can be assigned to an SQL result column of XML type, producing a concatenation of: for most types of node in the node-set, a text node containing the XPath 1.0 string-value of the node, but for an element node, a copy of the node itself. Such a node-set may be assigned to an SQL column of non-XML type only if the node-set has a single node, with the string-value of most node types replaced with an empty string, the string-value of an element node replaced with a concatenation of only its direct text-node children (excluding those of descendants), and the string-value of a text or attribute node being as defined in XPath 1.0. An XPath string value assigned to a result column of XML type must be parsable as XML.

It is best not to develop code that relies on these behaviors, which have little resemblance to the spec, and are changed in Postgres Pro 12.

D.3.2.3. Only BY VALUE passing mechanism is supported

The SQL standard defines two passing mechanisms that apply when passing an XML argument from SQL to an XML function or receiving a result: BY REF, in which a particular XML value retains its node identity, and BY VALUE, in which the content of the XML is passed but node identity is not preserved. A mechanism can be specified before a list of parameters, as the default mechanism for all of them, or after any parameter, to override the default.

To illustrate the difference, if x is an XML value, these two queries in an SQL:2006 environment would produce true and false, respectively:

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);

In this release, Postgres Pro will accept BY REF in an XMLEXISTS or XMLTABLE construct, but will ignore it. The xml data type holds a character-string serialized representation, so there is no node identity to preserve, and passing is always effectively BY VALUE.

D.3.2.4. Cannot pass named parameters to queries

The XPath-based functions support passing one parameter to serve as the XPath expression's context item, but do not support passing additional values to be available to the expression as named parameters.

D.3.2.5. No XML(SEQUENCE) type

The Postgres Pro xml data type can only hold a value in DOCUMENT or CONTENT form. An XQuery/XPath expression context item must be a single XML node or atomic value, but XPath 1.0 further restricts it to be only an XML node, and has no node type allowing CONTENT. The upshot is that a well-formed DOCUMENT is the only form of XML value that Postgres Pro can supply as an XPath context item.

FAQ