Static typing and substitution groups

Since I’ve recently talked about both static typing and substitution groups maybe it’s time to see what happens when they meet.

If you’ve already read my posts about the effects of the type substitution mechanism on static typing (part I and part II) you’ll be in familiar territory. Also, if you don't know or don't remember how to obtain the static type of an expression, please see this post by Mike Rorke.

When inferring the static type of an expression the server will always take into account all possible element substitutions.

Let’s look at a simple example

CREATE XML SCHEMA COLLECTION mySC AS
'<schema xmlns="https://www.w3.org/2001/XMLSchema"
targetNamespace="myNS"
xmlns:ns="myNS">

 <element name="a" type="integer"/>

 <element name="b" substitutionGroup="ns:a"/>

 <element name="root">
<complexType>
<sequence>
<element ref="ns:a"/>
</sequence>
</complexType>
</element>

</schema>
'
go

Given a variable @x of type XML(mySC) if we run a T-SQL statement like
SELECT @var.query('declare namespace ns="myNS"; (/ns:root/*)[1]')
the static type of expression (/ns:root/*)[1] will be
(element(ns{myNS}:b,xs:integer) | element(ns{myNS}:a,xs:integer)) ?

As you can see, the server took into account the fact that element ‘b’ can be substituted for element ‘a’. Therefore the child of a ‘root’ element can be either ‘a’ or ‘b’.

Now, let’s see where this could cause a problem and examine the following schema

CREATE XML SCHEMA COLLECTION myCollection AS N'
<schema xmlns="https://www.w3.org/2001/XMLSchema"
targetNamespace = "myNS"
xmlns:ns="myNS">

<element name="head" type="decimal"/>

<element name="root">
<complexType>
<sequence>
<element ref="ns:head"/>
</sequence>
</complexType>
</element>

<element name="member1" substitutionGroup="ns:head"/>

<element name="member2" type="byte" substitutionGroup="ns:head"/>

</schema>'
go

Now let’s attempt to run a very simple query

DECLARE @var XML(myCollection)
SET @var = '<root xmlns="myNS"><head>0</head></root>'
SET @var.modify('declare namespace ns="myNS"; replace value of (/ns:root[1]/*)[1] with 15.62')
go

As you’ve probably already guessed this query isn’t going to compile.
We’ll get an error like this one:
The target of 'replace value of' cannot be a union type, found '(element(ns{myNS}:member2,xs:byte) | element(ns{myNS}:head,xs:decimal) | element(ns{myNS}:member1,xs:decimal)) ?'.

As you can see the use of wildcards in XPath expressions isn’t always a good idea. It would be particularly risky if we allowed the expansion of substitution groups through ALTER XML SCHEMA COLLECTION statements. One could’ve written a perfectly valid query only to see compilation fail after components were added to the schema collection. Since we now disallow the expansion of substitution groups when altering xml schema collections (see my last post) this isn’t really an issue anymore.

 

-
Disclaimer:
This posting is provided “AS IS” with no warranties, and confers no rights.
Use of included script samples are subject to the terms specified at https://www.microsoft.com/info/cpyright.htm.