XQuery и статическая типизация

В SQL Server XQuery является языком со статической типизацией. Это означает, что при компиляции запросов, если выражение возвращает значение, тип или количество элементов которого неприемлемы для указанной функции или оператора, будет выдана ошибка преобразования типов. В дополнение к этому статическая проверка типов может также обнаружить несоответствие типа выражения пути в типизированном документе XML. Компилятор XQuery сначала применяет фазу нормализации, во время которой добавляются неявные операции (например атомизация), а затем производит статический вывод и статическую проверку типов.

Статический вывод типов

Во время операции вывода статического типа определяется тип возвращаемого значения выражения. Для этого берутся статические типы входных параметров и статическая семантика операции и выводится статический тип результата. Например статический тип выражения «1 + 2.3» определяется следующим образом.

  • Статический тип «1» равен xs:integer, а статический тип «2.3» — xs:decimal. На основе динамической семантики, статическая семантика операции + преобразует целочисленное значение в десятичное и затем возвращает десятичное значение. Таким образом выведенным статическим типом будет xs:decimal.

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

Для типизированных данных входной тип выводится из коллекции XML-схем, ограничивающей экземпляр типа данных XML. Например, если схема допускает только наличие элементов типа xs:integer, то результатом выражения пути, использующего этот элемент, будет нуль и более элементов типа xs:integer. Это часто обозначается выражениями вроде element(age,xs:integer)*, где символ звездочки (*) указывает количество элементов результирующего типа. В приведенном примере результатом выражения может стать нуль и более элементов с именем «age» и типом xs:integer. Остальные количества элементов: ровно 1, что выражается указанием только имени типа; 0 или 1, что выражается указанием вопросительного знака (?); 1 и более, что выражается указанием плюса (+).

Иногда вывод статического типа может определить, что выражение всегда возвращает пустую последовательность. Например, если выражение пути типизированного типа данных XML ищет элемент <name> внутри элемента <customer> (/customer/name), а схема не допускает содержание элементов <name> внутри элементов <customer>, статический вывод типа приведет к пустому результату. Этот эффект применяется для выявления неправильных запросов, то есть будет выдана статическая ошибка, если только этим выражением не было () или data( () ).

Подробное описание правил вывода содержится в формальной семантике спецификации XQuery. Корпорация Майкрософт только незначительно изменила их для работы с типизированными экземплярами типа данных XML. Наиболее важное отличие от стандарта заключается в том, что неявный узел документа знает тип данных экземпляра XML. Поэтому выражение пути в форме «/age» на основе этих сведений будет точно типизировано.

С помощью приложения Шаблоны и разрешения приложения SQL Server Profiler можно просмотреть статические типы, возвращаемые при компиляции запросов. Чтобы просмотреть их, трассировка должна включать событие XQuery Static Type в категории событий TSQL.

Статическая проверка типов

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

Ниже приведены все применимые типы:

  • типы, явно допустимые для функции или операции;

  • подтипы явно допустимых типов.

Подтипы определяются на основе правил создания подтипов XML-схемы при наследовании по ограничению или по расширению. Например, тип S является подтипом типа T, если все значения, которые имеет тип S, являются также экземплярами типа T.

Кроме того, в соответствии с иерархией типов XML-схемы, все целочисленные значения являются также и десятичными. Но при этом не все десятичные значения являются целочисленными. Иными словами, целочисленное является подтипом десятичного, но не наоборот. Например, операция + принимает только значения определенных типов, например числовых типов xs:integer, xs:decimal, xs:float и xs:double. Если переданные значения относятся к другим типам (например к xs:string), оператор выдает ошибку типа данных. Это называется строгой типизацией. Значения других типов, например атомарного типа, применяемого для обозначения нетипизированного XML, могут быть неявно преобразованы в значение типа, поддерживаемого для данной операции. Это называется слабой типизацией.

Если требуется неявное преобразование, статическая проверка типов гарантирует, что операции будут переданы только значения допустимых типов с верным количеством элементов. Для значения «"строка" + 1» будет определено, что статическим типом значения «строка» является xs:string. Поскольку этот тип недопустим для операции +, будет выдана ошибка типа данных.

При сложении результатов произвольного выражения E1 и произвольного выражения E2 (E1 + E2) статический вывод типов сначала определяет статические типы E1 и E2, а затем сверяет их с типами, допустимыми для данной операции. Например, если статический тип E1 может быть либо xs:string, либо xs:integer, проверка статического типа выдает ошибку типа данных, даже если некоторые значения на стадии выполнения могут оказаться целочисленными. То же самое может произойти, если статический тип E1 будет равен xs:integer*. Поскольку операция + принимает одно и только одно целочисленное значение, а выражение E1 может вернуть 0 или более 1, статическая проверка типов выдаст ошибку.

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

  • Тип выводит более общий тип: супертип или объединение типов. Если тип является атомарным, для обозначения действительного статического типа необходимо применить выражение явного приведения или функцию конструктора. Например, если выведенный тип выражения E1 может быть xs:string или xs:integer, а операции сложения требуется xs:integer, следует указывать xs:integer(E1) + E2, а не E1+E2. Это выражение на стадии выполнения может завершиться ошибкой, если символьное значение не сможет быть приведено к xs:integer, но зато теперь выражение будет проходить статическую проверку типов. Такое выражение сопоставляется с пустой последовательностью.

  • Тип выводит количество элементов большее, чем в действительности содержат данные. Это часто происходит из-за того, что тип данных xml может содержать более одного элемента верхнего уровня, и это не может быть ограничено коллекцией XML-схем. Чтобы ограничить статический тип и гарантировать, что передается не более одного значения, следует применять позиционный предикат [1]. Например, чтобы добавить 1 к значению атрибута c элемента b под элементом верхнего уровня, необходимо выполнить write (/a/b/@c)[1]+1. Также ключевое слово DOCUMENT может использоваться с коллекцией XML-схем.

  • Некоторые операции приводят к потере типа данных во время вывода. Если тип узла не может быть определен, ему назначается тип anyType. Это не неявное приведение типа к любому другому типу. Эти преобразования наиболее заметно появляются во время перемещения с помощью родительской оси. Если выражение приводит к ошибке статической типизации, избегайте таких операций и перепишите запрос.

Контроль типов объединенных типов

Работать с объединенными типами нужно особенно внимательно из-за проверки типов. Следующие примеры поясняют две из возможных проблем.

Пример: использование функции с объединенным типом

Взгляните на следующее определение элемента <r> объединенного типа:

<xs:element name="r">
<xs:simpleType>
   <xs:union memberTypes="xs:int xs:float xs:double"/>
</xs:simpleType>
</xs:element>

В контексте XQuery, функция «среднее значение» fn:avg (//r) возвращает статическую ошибку, потому что компилятор XQuery не может сложить значения аргументов различных типов (xs:int, xs:float или xs:double) для элементов <r> в аргументах функции fn:avg(). Чтобы решить эту проблему, перепишите вызов функции как fn:avg(for $r in //r return $r cast as xs:double ?).

Пример: использование оператора с объединенным типом

Оператор сложения ('+') требует использования точных типов операндов. Таким образом, выражение (//r)[1] + 1 возвращает статическую ошибку, которая имеет вышеуказанное определение типа элемента <r>. Единственное решение — переписать его как (//r)[1] cast as xs:int? +1, где «?» обозначает появление 0 или 1. В SQL Server требуется указывать «cast as» с «?», так как любое приведение может вызвать пустую последовательность в результате ошибок во время выполнения.

См. также

Другие ресурсы

Справочник по языку XQuery (SQL Server)