Condividi tramite


XQuery e tipizzazione statica

In SQL Server, XQuery è un linguaggio tipizzato in modo statico, ovvero genera errori di tipo durante la compilazione della query quando un'espressione restituisce un valore con una cardinalità o un tipo non accettato da una funzione o da un operatore specifico. Il controllo dei tipi statici può inoltre rilevare se un'espressione di percorso in un documento XML tipizzato è stata tipizzata in modo non corretto. Il compilatore XQuery applica innanzitutto la fase di normalizzazione che aggiunge le operazioni implicite, ad esempio l'atomizzazione, e quindi esegue l'inferenza e il controllo dei tipi statici.

Inferenza dei tipi statici

L'inferenza dei tipi statici determina il tipo restituito di un'espressione considerando i tipi statici dei parametri di input e la semantica statica dell'operazione e derivando il tipo statico del risultato. Ad esempio, il tipo statico dell'espressione 1 + 2,3 viene determinato nel modo seguente:

  • Il tipo statico di 1 è xs:integer e il tipo statico di 2,3 è xs:decimal. In base alla semantica dinamica, la semantica statica dell'operazione + converte il valore integer in valore decimale e quindi restituisce un valore decimale. Il tipo statico derivato è xs:decimal.

Per le istanze XML non tipizzate, sono disponibili tipi speciali per indicare che i dati non sono tipizzati. Tali informazioni vengono utilizzate durante il controllo dei tipi statici e per l'esecuzione di determinati cast impliciti.

Per i dati tipizzati, il tipo di input viene derivato dall'insieme di schemi XML che vincola l'istanza con tipo di dati XML. Se ad esempio lo schema consente solo elementi di tipo xs:integer, i risultati di un'espressione di percorso che utilizza tale elemento saranno zero o più elementi di tipo xs:integer. Tale risultato viene indicato tramite un'espressione, ad esempio element(age,xs:integer)*, in cui l'asterisco (*) indica la cardinalità del tipo risultante. In questo esempio, l'espressione può restituire zero o più elementi con nome "age" e tipo xs:integer. Le altre cardinalità corrispondono esattamente a: uno, espresso esclusivamente con il nome del tipo; zero o uno, espresso con un punto interrogativo (?); 1 o più, espresso con un segno più +).

Talvolta, mediante l'inferenza dei tipi statici è possibile dedurre che un'espressione restituirà sempre una sequenza vuota. Se ad esempio un'espressione di percorso su un'istanza tipizzata con tipo di dati XML cerca un elemento <name> all'interno di un elemento <customer> (/customer/name) ma lo schema non consente un elemento <name> in un elemento <customer>, l'inferenza dei tipi statici consentirà di dedurre che il risultato è una sequenza vuota. Tali informazioni verranno utilizzate per rilevare query non corrette e verranno indicate come errore statico, a meno che l'espressione sia () o data( () ).

Le regole di inferenza dettagliate sono indicate nella semantica formale delle specifiche XQuery. Microsoft ha modificato solo in misura minima tali regole per utilizzare istanze tipizzate con tipo di dati XML. La modifica più importante rispetto allo standard consiste nel fatto che il nodo di documento implicito conosce il tipo dell'istanza con tipo di dati XML. Di conseguenza, un'espressione di percorso con formato /age verrà tipizzata esattamente in base a tali informazioni.

Utilizzando Utilizzo di SQL Server Profiler, è possibile visualizzare i tipi statici restituiti come parte delle compilazioni di query. A tal fine, la traccia deve includere l'evento XQuery Static Type nella categoria di eventi TSQL.

Controllo dei tipi statici

Il controllo dei tipi statici verifica che l'esecuzione di run-time riceva solo valori di tipo appropriato per l'operazione. Poiché il controllo dei tipi non è necessario in fase di esecuzione, è possibile rilevare eventuali errori nelle fasi iniziali della compilazione, migliorando in tal modo le prestazioni. Per la tipizzazione statica, invece, è necessario che l'autore della query presti maggiore attenzione durante la formulazione.

I tipi appropriati che è possibile utilizzare sono i seguenti:

  • Tipi consentiti in modo esplicito da una funzione o da un'operazione

  • Un sottotipo di un tipo consentito in modo esplicito

I sottotipi vengono definiti in base alle regole di sottotipizzazione per l'utilizzo della derivazione per restrizione o per estensione dello schema XML. Ad esempio, un tipo S è un sottotipo del tipo T se tutti i valori di tipo S sono anche istanze del tipo T.

Inoltre, tutti i valori integer sono anche valori decimali, basati sulla gerarchia dei tipi dello schema XML. Tuttavia, non tutti i valori decimali sono valori integer. Pertanto, un valore integer è un sottotipo di un valore decimale ma non viceversa. Ad esempio, l'operazione + consente solo valori di determinati tipi, quali i tipi numerici xs:integer, xs:decimal, xs:float e xs:double. Se vengono passati valori di altri tipi, ad esempio xs:string, l'operazione restituisce un errore di tipo. Questo processo viene chiamato tipizzazione forte. È possibile convertire in modo implicito valori di altri tipi, ad esempio il tipo atomico utilizzato per indicare istanze XML non tipizzate, in un valore di un tipo accettato dall'operazione. Questo processo viene chiamato tipizzazione debole.

Se è necessario dopo una conversione implicita, il controllo dei tipi statici garantisce che solo i valori dei tipi consentiti con la cardinalità corretta vengano passati a un'operazione. Per "string" + 1, viene riconosciuto che il tipo statico di "string" è xs:string. Poiché questo tipo non è consentito per l'operazione +, viene restituito un errore di tipo.

In caso di addizione del risultato di un'espressione arbitraria E1 a un'espressione arbitraria E2 (E1 + E2), l'inferenza dei tipi statici determina innanzitutto i tipi statici di E1 e di E2 e quindi confronta tali tipi statici con quelli consentiti per l'operazione. Se ad esempio il tipo statico di E1 può essere xs:string o xs:integer, il controllo dei tipi statici restituisce un errore di tipo, anche se alcuni valori in fase di esecuzione potrebbero essere integer. Un errore analogo viene restituito nel caso in cui il tipo statico di E1 sia xs:integer*. Poiché l'operazione + accetta un solo valore integer corrispondente esattamente a uno e E1 può restituire zero o un valore maggiore di 1, il controllo dei tipi statici restituisce un errore.

Come menzionato in precedenza, l'inferenza deduce spesso un tipo più complesso rispetto alle informazioni in possesso dell'utente riguardo ai tipi di dati da passare. In tali casi, l'utente deve riformulare la query. In genere, tra i casi tipici rientrano i seguenti:

  • Il tipo deriva un tipo più generale, ad esempio un supertipo o un'unione di tipi. Nel caso di un tipo atomico, è consigliabile utilizzare l'espressione cast o la funzione costruttore per indicare il tipo statico effettivo. Se ad esempio il tipo derivato dell'espressione E1 è una scelta tra xs:string o xs:integer e l'addizione richiede xs:integer, è consigliabile scrivere xs:integer(E1) + E2 anziché E1+E2. Questa espressione può avere esito negativo in fase di esecuzione se viene rilevato un valore stringa di cui non è possibile eseguire il cast a xs:integer. Tuttavia, l'espressione passerà il controllo dei tipi statici. A partire da SQL Server 2005, viene eseguito il mapping tra questa espressione e la sequenza vuota.

  • Il tipo deriva una cardinalità più elevata rispetto a quella effettiva dei dati. Questo errore si verifica spesso, in quanto il tipo di dati xml può contenere più elementi di livello principale e l'insieme di schemi XML non può vincolarlo. Per limitare il tipo statico e garantire che venga passato effettivamente un unico valore, è consigliabile utilizzare il predicato basato sulla posizione [1]. Ad esempio, per aggiungere 1 al valore dell'attributo c dell'elemento b di livello inferiore rispetto all'elemento a di livello principale, è necessario scrivere (/a/b/@c)[1]+1. È inoltre possibile utilizzare la parola chiave DOCUMENT con un insieme di schemi XML.

  • Per alcune operazioni si verifica la perdita delle informazioni sul tipo durante l'inferenza. Ad esempio, ai nodi di cui non è possibile determinare il tipo viene attribuito il tipo anyType di cui non è possibile eseguire il cast implicito ad altri tipi. Queste conversioni si verificano con più frequenza durante l'esplorazione tramite asse parent. Se l'espressione crea un errore di tipo statico, è consigliabile evitare l'utilizzo di tali operazioni e riformulare la query.

Verifica dei tipi unione

I tipi unione devono essere gestiti con particolare attenzione, perché possono causare problemi durante la verifica dei tipi. Due di questi problemi sono illustrati negli esempi seguenti.

Esempio: funzione su tipo unione

Si consideri la seguente definizione per l'elemento <r> di tipo unione:

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

Nel contesto di XQuery, la funzione per il calcolo della media fn:avg (//r) restituisce un errore statico perché il compilatore XQuery non è in grado di sommare valori di tipi diversi (xs:int, xs:float o xs:double) per gli elementi <r> nell'argomento fn:avg(). Per risolvere il problema, è necessario riscrivere la chiamata alla funzione nel modo seguente: fn:avg(for $r in //r return $r cast as xs:double ?).

Esempio: operatore su tipo unione

L'operazione di addizione ('+') richiede tipi di operandi specifici. Di conseguenza, per l'elemento <r> l'espressione (//r)[1] + 1 restituisce un errore statico con la definizione di tipo illustrata in precedenza. È possibile risolvere il problema riscrivendo l'espressione nel modo seguente: (//r)[1] cast as xs:int? +1, dove "?" indica zero o una occorrenza. A partire da SQL Server 2005, SQL Server richiede "cast as" con "?", perché ogni operazione di cast può generare una sequenza vuota in caso di errore di run-time.