Static Typing Consequences of the Parent Axis
SQL Server 2005 has an XQuery implementation that provides full support for the parent axis, as defined in the XPath specification. Since our XQuery implementation is statically typed, we also support the static typing of the parent axis, as defined in the XQuery Formal Semantics document. The definition of static typing of the parent axis has a number of nuances that may not be entirely obvious without a detailed examination of it's definition. In essence, the main side effect of using this axis is that typing information is lost! This is best illustrated with an example.
Firstly, we create a simple schema with some typed elements that we will later query:
create xml schema collection small_type as N'
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="https://www.w3.org/2001/XMLSchema">
<xs:element name="small">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="item">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="embed-string" type="xs:string" nillable="true"/>
<xs:element name="embed-int" type="xs:int" />
</xs:sequence>
<xs:attribute name="index" type="xs:unsignedByte" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>'
Then we create a table and insert some instance data:
create table small_typed( id int IDENTITY PRIMARY KEY, xml_data xml(small_type), empty int)
go
insert into small_typed( xml_data ) values ('
<small>
<item index="1">
Item 1
<embed-string>String Item 1</embed-string>
<embed-int>1</embed-int>
</item>
</small>
')
The next step is to set up an XQuery static type SQL profiler trace so we can examine the effects of various path expressions on the static type of the query. Once that is done, we execute the following path expression:
select xml_data.exist('
/small/item/embed-string
') from small_typed
From the SQL profiler trace, we see that the static type of this expression is:
element(embed-string,xs:string) *
Which is exactly what we expected, based on our XSD schema. Lets try another query, this time targeting the index attribute of the item element:
select xml_data.exist('
/small/item/@index
') from small_typed
In this case, the static type returned is the following:
attribute(index,xs:unsignedByte) *
Again, precisely what we expected based on our XSD schema. Now, throwing the parent axis into the mix, we execute the following query:
select xml_data.exist('
/small/item/embed-string/../@index
') from small_typed
If we take a look at the static type that results from this query, we now get the following:
attribute(index,xdt:anyAtomicType) *
Notice that the static type of the expression is now the generic xdt:anyAtomicType type, instead of xs:unsignedByte (which is what we would expect based on our XSD schema!). This loss of typing has consequences when the result of these expressions are used for further processing e.g. if they are passed through to functions.
Consider the following query which uses the XQuery number() function to extract and parse the value of the index attribute:
select xml_data.query('
number( (/small/item)[1]/@index )
') from small_typed
This query executes fine. We now alter the path expression to use the parent axis as follows:
select xml_data.query('
number( (/small/item/embed-string/..)[1]/@index )
') from small_typed
We now get the following error message:
Msg 2364, Level 16, State 1, Line 2
XQuery [small_typed.xml_data.query()]: Cannot implicitly convert from 'xdt:anyAtomicType ?' to 'xs:double'
Essentially, the use of the parent axis (and it's associated loss of typing) can mean that the resultant static type precludes the use of the expression with many of the XQuery functions and other expressions that are type dependent. As a further example of this, consider the following query where we use the + operator on the value of the index attribute:
select xml_data.query('
(/small/item)[1]/@index + 1
') from small_typed
This query executes and returns the expected results. Once again, if we alter the path expression to include the parent operator as follows:
select xml_data.query('
(/small/item/embed-string/..)[1]/@index + 1
') from small_typed
The query now returns the following error message:
Msg 9308, Level 16, State 1, Line 2
XQuery [small_typed.xml_data.query()]: The argument of '+' must be of a single numeric primitive type or 'https://www.w3.org/2004/07/xpath-datatypes#untypedAtomic'. Found argument of type 'xdt:anyAtomicType ?'.
The moral of the story - use the parent axis with caution :)