XQuery y el establecimiento de tipos estáticos

XQuery en SQL Server es un lenguaje con establecimiento de tipos en modo estático. Es decir, provoca errores de tipo durante la compilación de consultas cuando una expresión devuelve un valor con un tipo o una cardinalidad no aceptado por una función o un operador determinados. Además, la comprobación de tipos estáticos también puede detectar si hay un error de asignación de tipo en una expresión de ruta de acceso de un documento XML con tipo. El compilador XQuery aplica en primer lugar la fase de normalización que agrega las operaciones implícitas, como la atomización y, a continuación, realiza la inferencia de tipos estáticos y la comprobación de tipos estáticos.

Inferencia de tipos estáticos

La inferencia de tipos estáticos determina el tipo de devolución de una expresión. Para determinarlo, obtiene los tipos estáticos de los parámetros de entrada y la semántica estática de la operación y deduce el tipo estático del resultado. Por ejemplo, el tipo estático de la expresión 1 + 2,3 se determina del modo siguiente:

  • El tipo estático de 1 es xs:integer y el de 2,3 es xs:decimal. Basándose en la semántica dinámica, la semántica estática de la operación + convierte el entero en un decimal y, a continuación, devuelve un decimal. En este caso, el tipo estático inferido será xs:decimal.

En el caso de las instancias XML sin tipo, existen tipos especiales que indican que no se ha asignado un tipo a los datos. Esta información se utiliza durante la comprobación de tipos estáticos y para realizar determinadas conversiones implícitas.

En el caso de los datos con tipo, se deduce el tipo de entrada a partir de la colección de esquemas XML que restringe la instancia de tipo de datos XML. Por ejemplo, si el esquema sólo permite elementos de tipo xs:integer, los resultados de una expresión de ruta de acceso que utilice ese elemento serán cero o más elementos de tipo xs:integer. Esto se suele mostrar con una expresión de tipo element(age,xs:integer)* en la que el asterisco (*) indica la cardinalidad del tipo resultante. En este ejemplo, la expresión puede dar como resultado cero o más elementos con el nombre "age" y el tipo xs:integer. Las demás cardinalidades son exactamente uno y se expresan únicamente mediante el nombre de tipo, cero o uno con un signo de interrogación (?), o bien 1 o más con un signo más (+).

En ocasiones, la inferencia de tipos estáticos puede deducir que una expresión siempre devolverá la secuencia vacía. Por ejemplo, si una expresión de ruta de acceso de un tipo de datos XML con tipo busca un elemento <name> en un elemento <customer> (/customer/name), pero el esquema no permite un elemento <name> en un elemento <customer>, la inferencia de tipos estáticos deducirá que el resultado está vacío. Esto se utilizará para detectar consultas incorrectas y se indicará como un error estático, a menos que la expresión sea () o data( () ).

Las reglas de inferencia detalladas se proporcionan en la semántica formal de la especificación XQuery. Microsoft la ha modificado ligeramente para trabajar con instancias de tipos de datos XML con tipo. El cambio más importante del estándar es que el nodo de documento implícito conoce el tipo de la instancia de tipo de datos XML. Como resultado, el tipo de una expresión de ruta de acceso con la forma /age se asignará de forma precisa basándose en esa información.

Si se utiliza Usar el Analizador de SQL Server, puede ver los tipos estáticos devueltos como parte de las compilaciones de la consulta. Si desea verlos, su traza debería incluir el evento XQuery Static Type en la categoría de eventos TSQL.

Comprobación de tipos estáticos

La comprobación de tipos estáticos garantiza que la ejecución en tiempo de ejecución sólo recibirá valores de tipo apropiado para la operación. Puesto que no es necesario comprobar los tipos en tiempo de ejecución, se pueden detectar posibles errores en una fase temprana de la compilación. Esto ayuda a mejorar el rendimiento. No obstante, el establecimiento de tipos estáticos requiere que el creador de la consulta sea más cuidadoso en la formulación.

A continuación se indican los tipos apropiados que se pueden utilizar:

  • Tipos permitidos explícitamente por una función u operación.

  • Un subtipo de un tipo permitido explícitamente.

Los subtipos se definen en función de las reglas de subtipo para utilizar la derivación mediante restricción o ampliación del esquema XML. Por ejemplo, un tipo S es un subtipo del tipo T si todos los valores del tipo S también son instancias del tipo T.

Además, todos los valores enteros también son valores decimales, en función de la jerarquía de tipos de esquema XML. No obstante, no todos los valores decimales son enteros. Por tanto, un entero es un subtipo de un decimal, pero no viceversa. Por ejemplo, la operación + sólo permite valores de determinados tipos, como los tipos numéricos xs:integer, xs:decimal, xs:float y xs:double. Si se pasan valores de otros tipos, como xs:string, la operación provocará un error de tipo. Esto se denomina establecimiento estricto de tipos. Los valores de otros tipos, como el tipo atómico utilizado para indicar XML sin tipo, se pueden convertir implícitamente en valores de un tipo aceptado por la operación. Esto se denomina establecimiento flexible de tipos.

Si es necesaria tras una conversión implícita, la comprobación de tipos estáticos garantiza que sólo se pasarán a una operación los valores de los tipos permitidos con la cardinalidad correcta. En el caso "string" + 1, reconocerá que el tipo estático de "string" es xs:string. Puesto que se trata de un tipo no permitido para la operación +, aparecerá un error de tipo.

Si se agrega el resultado de una expresión arbitraria E1 a una expresión arbitraria E2 (E1 + E2), la inferencia de tipos estáticos determinará en primer lugar los tipos estáticos de E1 y E2 y, a continuación, comprobará sus tipos estáticos con los tipos permitidos para la operación. Por ejemplo, si el tipo estático de E1 puede ser xs:string o xs:integer, la comprobación de tipos estáticos provocará un error de tipo, aunque puede que algunos de los valores de tiempo de ejecución sean enteros. Lo mismo ocurrirá si el tipo estático de E1 es xs:integer*. Puesto que la operación + sólo acepta exactamente un valor entero y E1 puede devolver cero o más de 1, la comprobación de tipos estáticos provocará un error.

Como se ha mencionado anteriormente, la inferencia de tipos suele inferir un tipo más general que el tipo que el usuario conoce de los datos que se pasan. En estos casos, el usuario debe volver a escribir la consulta. A continuación se exponen algunos casos habituales:

  • El tipo deduce un tipo más general, como un supertipo o una unión de tipos. Si el tipo es atómico, deberá utilizar la expresión de conversión o la función constructora para indicar el tipo estático real. Por ejemplo, si el tipo inferido de la expresión E1 es xs:string o xs:integer y la suma requiere xs:integer, deberá escribir xs:integer(E1) + E2 en lugar de E1+E2. Puede que esta expresión provoque un error de tiempo de ejecución si se detecta un valor de cadena que no se puede convertir a xs:integer. Sin embargo, ahora la expresión pasará la comprobación de tipos estáticos. A partir de SQL Server 2005, esta expresión se asigna a la secuencia vacía.

  • El tipo deduce una cardinalidad superior a la que contienen los datos realmente. Esto ocurre a menudo, pues el tipo de datos xml puede contener más de un elemento de nivel superior y una colección de esquemas XML no puede restringirlo. Para reducir el tipo estático y garantizar que efectivamente se pasa como máximo un valor, se debe utilizar el predicado de posición [1]. Por ejemplo, para sumar 1 al valor del atributo c del elemento b bajo el elemento a de nivel superior, debe usar write (/a/b/@c)[1]+1. Por otro lado, la palabra clave DOCUMENT se puede utilizar junto con una colección de esquemas XML.

  • Algunas operaciones pierden información de tipo durante la inferencia. Por ejemplo, si no se puede determinar el tipo de un nodo, éste adopta el tipo anyType. Este tipo no se convierte implícitamente en ningún otro tipo. Estas conversiones se producen sobre todo durante la exploración, utilizando el eje primario. Si la expresión provoca un error de tipo estático, debe evitar utilizar dichas operaciones y volver a escribir la consulta.

Comprobación de tipos para tipos de unión

Los tipos de unión requieren un tratamiento especial debido a la comprobación de tipos. Dos de los problemas se muestran en los ejemplos siguientes.

Ejemplo: función sobre tipo de unión

Considere la definición de un elemento para <r> de un tipo de unión:

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

En el contexto de XQuery, la función "promedio" fn:avg (//r) devuelve un error estático, ya que el compilador de XQuery no puede agregar valores de distintos tipos (xs:int, xs:float o xs:double) para los elementos <r> del argumento de fn:avg(). Para resolver esto, rescriba la invocación de función como fn:avg(for $r in //r return $r cast as xs:double ?).

Ejemplo: operador sobre tipo de unión

La operación de suma ("+") requiere tipos precisos para los operandos. Como resultado, la expresión (//r)[1] + 1 devuelve un error estático que tiene la definición de tipo descrita previamente para el elemento <r>. Una posible solución es rescribirlo como (//r)[1] cast as xs:int? +1, donde "?" indica 0 o 1 repeticiones. A partir de SQL Server 2005, SQL Server requiere "cast as" con "?", porque cualquier conversión puede resultar en una secuencia vacía como consecuencia de los errores en tiempo de ejecución.