XQuery e digitação estática

O XQuery no SQL Server é uma linguagem estaticamente digitada. Ou seja, ele gera erros de tipo durante a compilação de consulta quando uma expressão retornar um valor que tenha um tipo ou cardinalidade que não sejam aceitos por determinada função ou operador. Além disso, a verificação de tipo estático também poderá detectar se uma expressão de caminho em um documento XML digitado tiver sido digitada incorretamente. O compilador do XQuery primeiro aplica a fase de normalização que adiciona as operações implícitas, como atomização e, em seguida, executa a inferência e a verificação de tipo estático.

Inferência de tipo estático

A inferência de tipo estático determina o tipo de retorno de uma expressão. Ela determina isso considerando os tipos estáticos dos parâmetros de entrada e a semântica estática da operação e inferindo o tipo estático do resultado. Por exemplo, o tipo estático da expressão 1 + 2,3 é determinado da seguinte forma:

  • O tipo estático de 1 é xs:integer e o tipo estático de 2,3 é xs:decimal. Com base na semântica dinâmica, a semântica estática da operação + converte o inteiro em um decimal e, em seguida, retorna um decimal. O tipo estático inferido seria então xs:decimal.

Para instâncias XML não digitadas, há tipos especiais para indicar que os dados não foram digitados. Essas informações são usadas durante a verificação de tipo estático e para executar determinadas conversões implícitas.

Para dados digitados, o tipo de entrada é inferido da coleção de esquemas XML que restringe a instância do tipo de dados XML. Por exemplo, se o esquema só permitir elementos do tipo xs:integer, os resultados de uma expressão de caminho usando esse elemento serão zero ou mais elementos do tipo xs:integer. Isso é atualmente expresso usando uma expressão, como element(age,xs:integer)*, na qual o asterisco (*) indica a cardinalidade do tipo resultante. Neste exemplo, a expressão pode resultar em zero ou mais elementos de nome "age" e tipo xs:integer. Outras cardinalidades são exatamente um e são expressas usando o nome de tipo sozinho, zero ou um e expressas usando um ponto de interrogação (?) e 1 ou mais e expressas usando um sinal de mais (+).

Às vezes, a inferência de tipo estático pode deduzir que uma expressão sempre retornará a seqüência vazia. Por exemplo, se uma expressão de caminho em um tipo de dados XML procurar um elemento <name> no elemento <customer> (/cliente/nome), mas o esquema não permitir um <name> no <customer>, a inferência de tipo estático inferirá que o resultado será vazio. Isso será usado para detectar consultas incorretas e será relatado como um erro estático, a menos que a expressão seja () ou data( () ).

As regras de inferência detalhadas são fornecidas na semântica formal da especificação XQuery. A Microsoft somente as modificou levemente para que funcionem com instâncias do tipo de dados XML digitados. A mudança mais importante do padrão é que o nó de documento implícito sabe sobre o tipo da instância do tipo de dados XML. Conseqüentemente, uma expressão de caminho da forma /age será digitada precisamente com base nessas informações.

Ao usar Usando o SQL Server Profiler, é possível ver os tipos estáticos retornados como parte de compilações de consulta. Para vê-los, o seu traço deve incluir o evento de tipo estático do XQuery na categoria de eventos TSQL.

Verificação de tipo estático

A verificação de tipo estático assegura que a execução do tempo de execução só receberá valores que sejam o tipo apropriado para a operação. Como os tipos não precisam ser verificados no tempo de execução, os erros em potencial podem ser detectados erros precocemente na compilação. Isso ajuda a melhorar o desempenho. No entanto, a digitação estática requer que o autor da consulta tenha mais cuidado ao formular uma consulta.

A seguir, são apresentados os tipos apropriados que podem ser usados:

  • Tipos explicitamente permitidos por uma função ou operação.

  • Um subtipo de um tipo explicitamente permitido.

Os subtipos são definidos, com base nas regras de subdigitação por usar derivação por restrição ou extensão do esquema XML. Por exemplo, um tipo S é um subtipo do tipo T, se todos os valores que tiverem o tipo S também forem instâncias do tipo T.

Além disso, todos os valores inteiros também são valores decimais com base na hierarquia do tipo de esquemas XML. No entanto, nem todos os valores decimais são inteiros. Portanto, um inteiro é um subtipo de decimal, mas não vice-versa. Por exemplo, a operação + permite somente valores de determinados tipos, como os tipos numéricos xs:integer, xs:decimal, xs:floate xs:double. Se forem passados valores de outros tipos, como xs:string, a operação gerará um erro de tipo. Isso será referido como digitação forte. Os valores de outros tipos, como o tipo atômico usado para indicar XML não digitado, podem ser convertidos implicitamente em um valor de um tipo que a operação aceite. Isso é referido como digitação fraca.

Se for necessário depois de uma conversão implícita, a verificação de tipo estático garantirá que somente valores dos tipos permitidos com a cardinalidade correta serão passados em uma operação. Para a "cadeia de caracteres" + 1, é reconhecido que o tipo estático da "cadeia de caracteres" é xs:string. Como esse não é um tipo permitido para a operação +, um erro de digitação será gerado.

No caso de adicionar o resultado de uma expressão arbitrária E1 a uma expressão arbitrária E2 (E1 + E2), a inferência de tipo estático determina os tipos estáticos de E1 e E2 primeiro e, em seguida, verifica os tipos estáticos com os tipos permitidos para a operação. Por exemplo, se o tipo estático de E1 puder ser um xs:string ou um xs:integer, a verificação de tipo estático gerará um erro de digitação, mesmo que alguns valores no tempo de execução possam ser inteiros. O mesmo seria o caso se o tipo estático de E1 fosse xs:integer *. Como a operação + só aceita exatamente um valor inteiro e E1 poderia retornar zero ou mais de 1, a verificação de tipo estático gera um erro.

Como mencionado anteriormente, a inferência de tipo freqüentemente infere um tipo que é mais amplo do que aquilo que o usuário sabe sobre o tipo de dados sendo passados. Nesses casos, o usuário precisa reescrever a consulta. Alguns casos característicos incluem o seguinte:

  • O tipo infere um tipo mais geral, como um supertipo ou uma união de tipos. Se o tipo for um tipo atômico, você deve usar a expressão de conversão ou função de construtor para indicar o tipo estático real. Por exemplo, se o tipo inferido da expressão E1 for uma escolha entre xs:string ou xs:integer e a adição exigir xs:integer, você deve escrever xs:integer(E1) + E2 em vez de E1+E2. Essa expressão pode falhar no tempo de execução se um valor da cadeia de caracteres não puder ser convertido em xs:integer. No entanto, a expressão agora passará a verificação de tipo estático. A partir do SQL Server 2005, essa expressão é mapeada para a seqüência vazia.

  • O tipo infere uma cardinalidade superior àquela que os dados realmente contêm. Isso freqüentemente ocorre, pois o tipo de dados xml pode conter mais de um elemento de nível superior e uma coleção de esquemas XML não pode restringir isso. Para reduzir o tipo estático e garantir que realmente haja um valor máximo sendo passado, você deve usar o predicado posicional [1]. Por exemplo, para adicionar 1 ao valor do atributo c do elemento b no elemento de nível superior, é necessário write (/a/b/@c)[1]+1. Além disso, uma palavra-chave DOCUMENT pode ser usada junto com uma coleção de esquemas XML.

  • Algumas operações perdem informações de tipo durante a inferência. Por exemplo, se o tipo de um nó não puder ser determinado, ele se tornará anyType. Isso não é convertido implicitamente em qualquer outro tipo. Tais conversões ocorrem mais notavelmente durante a navegação usando o eixo pai. Você deve evitar o uso de tais operações e reescrever a consulta, se a expressão for criar um erro de tipo estático.

Verificação de tipo de tipos de união

Os tipos de união requerem o tratamento cuidadoso devido à verificação de tipo. São ilustrados dois dos problemas nos exemplos a seguir.

Exemplo: função em relação ao tipo de união

Considere uma definição de elemento para <r> de um tipo de união:

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

No contexto do XQuery, a função "average" fn:avg (//r) retorna um erro estático, pois o compilador do XQuery não pode adicionar valores de tipos diferentes (xs:int, xs:float ou xs:double) para os elementos <r> no argumento de fn:avg(). Para solucionar isso, reescreva a invocação de função como fn:avg(for $r in //r return $r cast as xs:double ?).

Exemplo: operador em relação ao tipo de união

A operação de adição ('+') requer tipos precisos dos operandos. Como resultado, a expressão (//r)[1] + 1 retorna um erro estático que tem a definição de tipo previamente descrita para o elemento <r>. Uma solução é reescrevê-la como (//r)[1] cast as xs:int? +1, onde o “?” indica 0 ou 1 ocorrência. A partir do SQL Server 2005, SQL Server requer-se "cast as" com "?", pois qualquer conversão pode causar a seqüência vazia como resultado de erros no tempo de execução.