FO C U S Le soluzioni di Oracle La principale innovazione presente nella release 2 di Oracle9i è il database XML chiamato XDB. Analizziamo le principali caratteristiche di questo importante strumento Oracle 9.2 XML Database [email protected] di Massimo Ruocchio È laureato in matematica ed è certificato Oracle Application Developer. Si occupa di analisi, progettazione e sviluppo di applicazioni software I n un articolo pubblicato nel numero di Gennaio 2002 di Computer Programming [3], è stato introdotto il tema dei database XML analizzando, a titolo d’esempio, tre prodotti di tipo diverso. Tra i prodotti presentati Oracle8i, che non usciva benissimo dal confronto con strumenti “specializzati” nella gestione di documenti XML. Con la versione 9.2 del database, Oracle si propone di porre rimedio a questa situazione fornendo XDB. XDB non è un prodotto a sé; è costituito da una serie di funzionalità, incluse nell’istallazione base di Oracle9i, per la gestione dei documenti XML. I due elementi principali di XDB sono il datatype XMLType, già presente nella release precedente ma decisamente migliorato nell’ultima, ed il repository XML. Nei prossimi paragrafi descriveremo questi due strumenti. XMLType Un documento XML può essere archiviato in un database relazionale in due modi: Monolitico, in un campo di tipo CLOB Segmentato, dato-per-dato in tabelle relazionali (archiviazione strutturata) Fino all’ultima release di Oracle9i bisognava decidere quale tipo di archiviazione utilizzare e poi progettare sia il database sia l’applicazione in maniera coerente con la scelta effettuata. Il datatype XMLType consente di gestire i documenti XML presenti nel database a prescindere dalla tecnica di archiviazione scelta. In più, XMLType fornisce una serie di metodi per il supporto dei principali standard XML: XML Schema, Xpath, Indici gerarchici, ecc. Il datatype XMLType può essere utilizzato sia per le colonne delle tabelle sia come parametro di procedure e funzioni. Quando è estratto dal database mediante una select restituisce un frammento XML ben formato. Internamente al database, invece, è conservato con una rappresentazione ad albero, tipo DOM per intenderci. Per creare una tabella che contenga dati di tipo XMLType si hanno due scelte: OraXmlDb Creare una tabella basata solo sul tipo XMLType (una sorta di array di XMLType) Creare una normale tabella relazionale in cui una colonna sia del tipo XMLType In entrambi i casi bisogna specificare il tipo di archiviazione che s’intende utilizzare e, opzionalmente, lo schema XML che i documenti archiviati devono rispettare. Ecco la prima grande innovazione: il supporto di XML Schema. Per maggiori informazioni su XML schema si può consultare [4]. Uno schema, prima di essere utilizzato, deve essere registrato nel database mediante il package dbms_xmlschema. Una volta registrato, lo schema è memorizzato nel repository XML, di cui parleremo più avanti, a fronte di un URL. Questo URL deve essere utilizzato nell’istruzione CREATE TABLE per assegnare lo schema al campo XMLType. Nel momento della registrazione dello schema, il DBMS definisce automaticamente le tabelle, i datatype e gli oggetti necessari per conservare i dati dei documenti che faranno riferimento a quello schema. Per gestire l’associazione tra gli elementi dello schema ed i tipi creati in fase di registrazione, allo schema vengono aggiunti alcuni attributi, ad esempio oraxdb:SQLType, oraxdb:SQLName e oraxdb: SQLSchema. Se si intendono utilizzare tipi e tabelle definiti dall’utente basta modificare, dopo averli creati, questi attributi dello schema. Vediamo le due sintassi possibili per creare una tabella con una colonna di tipo XMLType: CREATE TABLE [schema.] table_name OF XMLTYPE [XMLTYPE XMLType_storage] [XMLSCHEMA “XMLSchema URL”]; LISTATO 1 Una funzione che legge dal file system create or replace function getdocument(dir varchar2, filename varchar2) return clob authid current_user is xbfile bfile; xclob clob; begin xbfile := bfilename(dir, filename); dbms_lob.open(xbfile); dbms_lob.createtemporary(xclob, TRUE, dbms_lob.session); dbms_lob.loadfromfile(xclob, xbfile, dbms_lob.getlength(xbfile)); dbms_lob.close(xbfile); return xclob; end; / 35 CP 119 FO C U S oppure FIGURA 1 Creazione di una risorsa mediante Oracle Enterprise Manager CREATE TABLE [schema.] table_name (column_name XMLTYPE [NOT NULL]) [XMLTYPE COLUMN “column_name” XMLType_storage] [XMLSCHEMA “XMLSchema URL”]; In sostanza l’oggetto creato non cambia; nel primo caso Oracle attribuisce un nome di sistema alla colonna XMLType. Alla seconda istruzione possono essere aggiunte altre colonne a piacere. Torniamo a parlare del tipo d’archiviazione, indicato, come si è visto, mediante la clausola XMLType_storage. Se si assegna uno schema non c’è bisogno di indicare il tipo d’archiviazione. I dati vengono automaticamente spacchettati e suddivisi nelle tabelle create dal DBMS al momento della registrazione dello schema. Se si desidera archiviare il documento per intero in un campo CLOB basta specificare la clausola STORE AS CLOB dove nella sintassi appare XMLTYPE_storage. Se non si assegna uno schema, i dati sono archiviati in un campo di tipo CLOB. Poiché il campo XMLType è gestito internamente con una rappresentazione ad albero proprietaria di Oracle, in fase di inserimento o modifica dei dati bisogna convertire il documento XML che si intende inserire in un oggetto che abbia quella struttura. Ciò può essere fatto mediante il metodo costruttore del tipo XMLType. La sintassi per l’inserimento è semplicemente: insert into nome_tabella values (xmltype(documento_xml,URL_dello_schema)); dove documento_xml può essere una stringa, una variabile PL/SQL oppure un campo di database contenente il documento. E se si intende inserire un documento presente nel file system? Bisogna costruirsi una funzione che, da Oracle, legge il file e ne scarica il contenuto in una variabile CLOB o VARCHAR2. Nel Listato1 è riportata una funzione siffatta idonea per leggere un file contenuto nello stesso file system in cui si trova il database. La funzione è riportata dal forum della Oracle dedicato a XDB [7]. Per inserire dati in XDB è possibile anche utilizzare SQL*Loader. XDB è dedicato alla gestione dei documenti XML La modifica dei dati richiede l’utilizzo della funzione UPDATEXML. Questa funzione prende in input tre valori: il documento XML (in formato XMLType) da modificare, un percorso XPath che indica il valore da sostituire, il nuovo valore in formato alfanumerico. La sintassi è la seguente: update nome_tabella set nome_campo = UPDATEXML( documento_xml, xpath_da_sostituire, nuovo_valore); Bisogna notare che l’istruzione UPDATE sostituisce sempre tutto il contenuto del campo XMLType: non è pos- CP 119 36 sibile modificare un solo dato del documento (a meno di modificare direttamente la tabella relazionale che contiene il singolo dato). Abbiamo già introdotto Xpath. Xpath è un linguaggio standard del W3C con cui è possibile indicare la posizione di frammenti (o singoli nodi) all’interno di un documento XML; per far ciò si utilizzano dei percorsi tipo quelli adoperati nei file system. Xpath può essere utilizzato anche come linguaggio di query, perché dà la possibilità di indicare anche delle condizioni di estrazione oltre alla posizione dell’oggetto da estrarre. Ed è proprio come linguaggio di query che Xpath è utilizzato in XDB, congiuntamente a SQL, per estrarre dati da campi di tipo XMLType. XDB supporta Xpath tramite alcune nuove funzioni, ad esempio extractValue, updatexml, xmltype.extract, existsNode. La differenza tra xmltype.extract ed extractValue è che la prima ritorna un frammento XML ben formato mentre la seconda ritorna sempre un singolo valore. La funzione existsNode restituisce 1 se in una variabile XMLType assegnata esiste un certo percorso Xpath. XML Repository Il Repository di Oracle XML DB è una collezione di oggetti di database organizzati in forma gerarchica. Si può vedere il Repository come un file system, interno al database, in cui è possibile creare directory e conservare file di ogni genere. Tutti gli oggetti contenuti nel repository sono chiamati risorse. L’accesso alle risorse può essere realizzato mediante gli strumenti solitamente utilizzati per accedere ai più diffusi file system: FTP, HTTP, WebDAV. In particolare, la compatibilità con WebDAV consente di navigare il Repository da Internet Explorer e di aprire e modificare i documenti in esso contenuti con tutti gli editor che supportano questo standard (ad esempio Microsoft Word o XML Spy di Altova). Le risorse si dividono in due tipi: contenitori e file. In ogni caso una risorsa può essere creata mediante l’interfaccia grafica di Oracle Enterprise Manager (OEM) – un esempio è riportato in Figura 1 –, oppure utilizzando da PL/SQL le funzioni CREATEFOLDER e CREATERESOURCE del package DBMS_XDB. L’interfaccia grafica è sicuramente più comoda e veloce. Per avere informazioni sulle risorse presenti nel Repository possono essere interrogate le viste RESOURCE_VIEW e PATH_VIEW. Per agevolare la consultazione di queste viste, Oracle mette a disposizione le funzioni: UNDER_PATH che ritorna 1 quando una certa risorsa si trova EQUALS_PATH che torna 1 quando la risorsa si trova esatta- PATH che restituisce il percorso di una risorsa DEPTH che restituisce il numero di livelli gerarchici presenti tra il path indicato nella UNDER_PATH e la risorsa estratta. tra i “discendenti” di un percorso specificato mente al path specificato Le view PATH_VIEW e RESOURCE_VIEW sono modificabili; mediante esse, quindi, è possibile anche aggiornare direttamente le informazioni sulle risorse contenute nel repository. Il Repository può essere utilizzato anche per conservare le diverse versioni di una risorsa. Infatti, quando una risorsa è modificata, il vecchio contenuto può essere conservato, creando una nuova versione della risorsa. La gestione delle risorse è effettuata mediante il package DBMS_XDB_VERSION ed è opzionale. Per abilitarla bisogna eseguire lo script PL/SQL che segue: declare resid DBMS_XDB_VERSION.RESID_TYPE; begin resid := DBMS_XDB_VERSION.MakeVersioned(Path_della_ risorsa); end; / Non per tutte le risorse possono essere gestite le versioni. Fanno eccezione: Contenitori (le cartelle), i file ACL (di cui parleremo dopo) ed i documenti XML archiviati in maniera strutturata. L’ultima limitazione è molto grave e riduce fortemente l’utilità dello strumento. Come si è già detto, si può accedere al Repository anche mediante HTTP/WebDAV. In Figura 2 è mostrata una videata di Internet Explorer in cui è visualizzato il contenuto del Repository sotto la cartella di rete denominata “ruocchiolapt”, che è il nome del server Oracle. Per aggiungere il Repository alle risorse di rete basta accedervi una volta, ad esempio digitando http://nome_computer:8080 nella barra degli indirizzi e dando invio. La porta 8080 è quella di default. Tutti gli accessi al Repository, anche mediante FTP, HTTP e WebDAV, sono protetti da un meccanismo di sicurezza basato su ACL (Access Control List). Per una data risorsa, una ACL è una lista in cui vengono elencati i privilegi che i vari utenti/ruoli definiti nel database hanno sulla risorsa stessa. Le ACL possono a loro volta essere conservate nel Repository per essere utilizzate da più risorse. L’ACL è controllata ad ogni accesso alla risorsa. Anche le ACL possono essere gestite graficamente mediante OEM oppure via PL/SQL utilizzando il packge DBMS_XDB. Qualche esempio Ok, dopo tante chiacchiere vediamo qualche esempio concreto. Tutti i file menzionati in questo paragrafo sono disponibili sul sito FTP di Infomedia (ftp://ftp.infomedia.it/ pub/ComputerProgramming/Listati/). Per prima cosa è stato definito uno schema XML (file anag.xsd e Listato 2). A fronte dello schema sono stati creati sei documenti XML contenenti anagrafiche di persone fisiche o giuridiche (file anagN.xml). Tutte le istruzioni SQL indicate in questo paragrafo sono contenute nel file test.sql; nel Listato 3 è visualizzato uno spool con tutti i risultati delle istruzioni eseguite. Il primo passo da compiere per archiviare i dati nel database è la registrazione dello schema. Quest’attività è stata compiuta mediante OEM (Figura 3). Allo schema è stato assegnato l’URL ‘http: //ruocchio-lapt:8080/max/schemi/anag.xsd’. L’URL non indica necessariamente la collocazione fisica dello schema, è solo un nome logico con cui lo schema sarà referenziato nel database. LISTATO 2 Lo schema XML Anag.xsd <?xml version=”1.0” encoding=”UTF-8”?> <xs:schema xmlns:xs=”http://www.w3.org/2001/XMLSchema”> <xs:element name=”anagrafica”> <xs:complexType> <xs:sequence> <xs:element ref=”denominazione”/> <xs:element ref=”indirizzo”/> </xs:sequence> <xs:attribute name=”flag_fis_giu” use=”required”> <xs:simpleType> <xs:restriction base=”xs:NMTOKEN”> <xs:enumeration value=”F”/> <xs:enumeration value=”G”/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> </xs:element> <xs:element name=”cap” type=”xs:int”/> <xs:element name=”civico” type=”xs:string”/> <xs:element name=”cognome” type=”xs:string”/> <xs:element name=”comune” type=”xs:string”/> <xs:element name=”denominazione”> <xs:complexType> <xs:sequence> <xs:element ref=”titolo” minOccurs=”0”/> <xs:element ref=”nome” minOccurs=”0”/> <xs:element ref=”cognome” minOccurs=”0”/> <xs:element ref=”ragsoc” minOccurs=”0”/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name=”indirizzo”> <xs:complexType> <xs:sequence> <xs:element ref=”via”/> <xs:element ref=”civico”/> <xs:element ref=”cap”/> <xs:element ref=”comune”/> <xs:element ref=”provincia”/> <xs:element ref=”telefono”/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name=”nome” type=”xs:string”/> <xs:element name=”provincia” type=”xs:string”/> <xs:element name=”ragsoc” type=”xs:string”/> <xs:element name=”telefono” type=”xs:string”/> <xs:element name=”titolo”> <xs:simpleType> <xs:restriction base=”xs:string”> <xs:enumeration value=”Dott.”/> <xs:enumeration value=”Sig.”/> <xs:enumeration value=”Cav.”/> <xs:enumeration value=”Avv.”/> <xs:enumeration value=”Ing.”/> <xs:enumeration value=”Gr. Uff.”/> <xs:enumeration value=”Comm.”/> <xs:enumeration value=”On.”/> <xs:enumeration value=”Sen.”/> </xs:restriction> </xs:simpleType> </xs:element> <xs:element name=”via” type=”xs:string”/> </xs:schema> 37 CP 119 FO C U S Una volta registrato lo schema, XDB crea autonomamente i datatype e le tabelle da utilizzare per caricare gli elementi dei documenti XML che lo referenziano. Nel file allegato AnagORA.xsd c’è lo schema XML con le modifiche apportate da XDB per realizzare l’archiviazione strutturata. Per vedere ciò che è stato creato, eseguiamo le query: select type_name, typecode from user_types; insert into anagrafiche select 6, xmltype(getdocument(‘CARTELLA’,’anag6.xml’), ‘http://ruocchio-lapt:8080/max/schemi/anag.xsd’) from dual; Veniamo alle query (i risultati sono in Listato 3). Per prima cosa ci interessa ottenere un elenco dei soggetti inseriti in tabella con i relativi numeri di telefono. Bisogna distinguere tra persone fisiche e giuridiche: select table_name from user_xml_tables; Passiamo alla creazione della tabella deputata a contenere le nostre anagrafiche. Se intendiamo archiviare solo i documenti XML ci basta un array di XMLType, possiamo utilizzare l’istruzione: CREATE TABLE ANAGRAFICHE OF SYS.XMLTYPE XMLSCHEMA “http://ruocchio-lapt:8080/max/schemi/anag.xsd” ELEMENT “anagrafica”; select decode(extractvalue(anagrafica,’./anagrafica/ @flag_fis_giu’), ‘F’, extractvalue(anagrafica,’./anagrafica/ denominazione/cognome’), ‘G’, extractvalue(anagrafica,’./anagrafica/ denominazione/ragsoc’)) Denominazione, extractvalue(anagrafica,’./anagrafica/ indirizzo/telefono’) Telefono from anagrafiche; Noi vogliamo che nella tabella ANAGRAFICHE ci sia anche un progressivo numerico e, dunque, utilizziamo l’istruzione: Il datatype XMLType consente di CREATE TABLE ANAGRAFICHE (PROGR NUMBER NOT NULL, ANAGRAFICA XMLTYPE NOT NULL) XMLTYPE COLUMN “ANAGRAFICA” XMLSCHEMA “http://ruocchio-lapt:8080/max/schemi/anag.xsd” ELEMENT “anagrafica”; gestire i documenti XML presenti nel database a prescindere dalla tecnica di archiviazione scelta Per caricare i documenti utilizziamo due tecniche. Cinque dei sei file sono stati conservati in un campo CLOB della tabella TEMP_CLOB. Per caricarli in ANAGRAFICHE utilizziamo l’istruzione: insert into anagrafiche select rownum, xmltype(dati, ’http://ruocchio-lapt:8080/max/schemi/anag.xsd’) from temp_clob; Il sesto documento si trova nel file system. Per caricarlo vogliamo utilizzare la funzione getDocument (Listato 1), che abbiamo preventivamente creato nel database. Siccome la funzione accetta in input un identificativo di directory, possiamo crearne una con l’istruzione: create directory CARTELLA ‘c:\massimo\articoli\oraxmldb’; Ora possiamo caricare il documento nella tabella con l’istruzione: FIGURA 2 Navigazione del Repository mediante Internet Explorer La funzione extractvalue restituisce valori singoli mentre la funzione extract restituisce frammenti XML. Invece del telefono, visualizziamo l’intero indirizzo: select decode(extractvalue(anagrafica,’/anagrafica/ @flag_fis_giu’), ‘F’, extractvalue(anagrafica,’/anagrafica/ denominazione/cognome’), ‘G’, extractvalue(anagrafica,’/anagrafica/ denominazione/ragsoc’)) Denominazione, xmltype.extract(anagrafica,’/anagrafica/indirizzo’) Indirizzo from anagrafiche; Passiamo alla modifica dei dati. Il primo concetto da afferrare è il seguente: un campo XMLType viene sempre modificato completamente. Anche se si deve modificare un solo attributo in un documento di mille linee, viene sostituito l’intero documento. Per effettuare la modifica dei dati è stata introdotta la funzione UPDATEXML. Un esempio è il seguente: update anagrafiche set anagrafica = UPDATEXML(anagrafica, ‘/anagrafica/indirizzo/civico/text()’, ‘333’) where extractvalue(anagrafica,’/anagrafica/ indirizzo/civico’)=’133’; Sfruttando le potenzialità di Xpath, in particolare la possibilità di indicare una condizione d’estrazione tra parentesi quadre, si può anche scrivere: update anagrafiche set anagrafica = UPDATEXML(anagrafica, ‘/anagrafica/indirizzo/civico[text()=”133”]/ text()’, ‘333’); CP 119 38 LISTATO 3 I risultati delle istruzioni indicate nell’articolo SQL*Plus: Release 9.2.0.1.0 - Production on Mar Ago 27 11:14:43 2002 Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved. Connesso a: Personal Oracle9i Release 9.2.0.1.0 - Production With the Partitioning, OLAP and Oracle Data Mining options JServer Release 9.2.0.1.0 - Production SQL> select type_name, typecode from user_types; TYPE_NAME ---------------------------anagrafica196_T denominazione193_T indirizzo188_T TYPECODE ---------------------------OBJECT OBJECT OBJECT SQL> select table_name from user_xml_tables; TABLE_NAME -----------------------------anagrafica197_TAB denominazione195_TAB cognome194_TAB indirizzo192_TAB comune191_TAB cap190_TAB civico189_TAB nome187_TAB provincia186_TAB ragsoc185_TAB telefono184_TAB titolo183_TAB via182_TAB SQL> 2 3 4 5 6 CREATE TABLE ANAGRAFICHE (PROGR NUMBER NOT NULL, ANAGRAFICA XMLTYPE NOT NULL) XMLTYPE COLUMN “ANAGRAFICA” XMLSCHEMA “http://ruocchio-lapt:8080/max/ schemi/anag.xsd” ELEMENT “anagrafica”; SQL> select decode(extractvalue(anagrafica,’./ anagrafica/@flag_fis_giu’), 2 ‘F’, extractvalue(anagrafica,’./anagrafica/ denominazione/cognome’), 3 ‘G’, extractvalue(anagrafica,’./anagrafica/ denominazione/ragsoc’)) Denominazione, 4 extractvalue(anagrafica,’./anagrafica/ indirizzo/telefono’) Telefono 5 from anagrafiche; DENOMINAZIONE -----------------------------Landi Bernardi Pizza e Fichi S.p.A. Banda del Torchio S.r.l. Banda Bassotti S.a.s. Dei Paperoni SQL> col Indirizzo for a50 SQL> select decode(extractvalue(anagrafica,’./anagrafica/ @flag_fis_giu’), 2 ‘F’, extractvalue(anagrafica,’./anagrafica/ denominazione/cognome’), 3 ‘G’, extractvalue(anagrafica,’./anagrafica/ denominazione/ragsoc’)) Denominazione, 4 xmltype.extract(anagrafica,’./anagrafica/ indirizzo’) Indirizzo 5 from anagrafiche; DENOMINAZIONE INDIRIZZO ------------------------ ------------------------------Landi <indirizzo> <via>delle rose</via> <civico>92</civico> <cap>82343</cap> <comune>Corzano</comune> <provincia>TC</provincia> <telefono>0934434343</telefono> </indirizzo> Bernardi <indirizzo> <via>Vittorio Emanelele II</via> <civico>37</civico> <cap>132</cap> <comune>Roma</comune> <provincia>RM</provincia> <telefono>0655667788</telefono> </indirizzo> Pizza e Fichi S.p.A. <indirizzo> <via>della libertà</via> <civico>194B</civico> <cap>30153</cap> <comune>Frittole</comune> <provincia>FT</provincia> <telefono>042565758798</telefono> </indirizzo> Creata tabella. SQL> desc anagrafiche Nome Nullo? ---------------- --------PROGR NOT NULL ANAGRAFICA NOT NULL Tipe ---------------------------NUMBER SYS.XMLTYPE(XMLSchema “httpp://ruocchio-lapt:8080/max/ schemi/anag.xsd” Element “anagrafica”) STORAGE Object-relational TYPE “anagrafica196_T” SQL> insert into anagrafiche 2 select rownum, xmltype(dati,’http://ruocchio-lapt: 8080/max/schemi/anag.xsd’) 3 from temp_clob; Create 5 righe. SQL> insert into anagrafiche 2 select 6, xmltype(getdocument(‘CARTELLA’,’anag6.xml’), 3 ‘http://ruocchio-lapt:8080/max/schemi/anag.xsd’) 4 from dual; Creata 1 riga. SQL> col Denominazione for a30 SQL> col Telefono for a14 TELEFONO -------------0934434343 0655667788 042565758798 09889898787 022323232322 024234343566 Banda del Torchio S.r.l. <indirizzo> <via>dei banditi</via> <civico>13</civico> <cap>98212</cap> <comune>Santa Ortensia</comune> <provincia>XV</provincia> <telefono>09889898787</telefono> </indirizzo> Banda Bassotti S.a.s. <indirizzo> <via>dei paperi</via> <civico>77</civico> <cap>54554</cap> <comune>Paperopoli</comune> <provincia>PP</provincia> (segue a pagina seguente) 39 CP 119 FO C U S segue LISTATO 3 I risultati delle istruzioni indicate nell’articolo <telefono>022323232322</telefono> </indirizzo> Dei Paperoni <indirizzo> <via>del deposito di monete</via> <civico>133</civico> <cap>55445</cap> <comune>Paperopoli</comune> <provincia>PP</provincia> <telefono>024234343566</telefono> </indirizzo> SQL> update anagrafiche 2 set anagrafica = UPDATEXML(anagrafica, 3 ‘/anagrafica/indirizzo/civico [text()=”133”]/text()’, 4 ‘333’); Aggiornate 6 righe. SQL> select decode(extractvalue(anagrafica,’/anagrafica/ @flag_fis_giu’), 2 ‘F’, extractvalue(anagrafica,’/anagrafica/ denominazione/cognome’), 3 ‘G’, extractvalue(anagrafica,’/anagrafica/ denominazione/ragsoc’)) Denominazione, 4 xmltype.extract(anagrafica,’/anagrafica/ indirizzo’) Indirizzo 5 from anagrafiche 6 where progr=6; DENOMINAZIONE INDIRIZZO --------------------- -----------------------------------Dei Paperoni <indirizzo> <via>del deposito di monete</via> <civico>333</civico> <cap>55445</cap> <comune>Paperopoli</comune> <provincia>PP</provincia> <telefono>024234343566</telefono> </indirizzo> SQL> roll; Completato rollback. SQL> update anagrafiche 2 set anagrafica = UPDATEXML(anagrafica, 3 ‘/anagrafica/indirizzo/civico/text()’, 4 ‘333’) 5 where extractvalue(anagrafica,’/anagrafica/ indirizzo/civico’)=’133’; SQL> update anagrafiche 2 set anagrafica = UPDATEXML(anagrafica, 3 ‘/anagrafica/@flag_fis_giu’,’S’) 4 where extractvalue(anagrafica,’/anagrafica/ denominazione/nome’)=’Paperone’; update anagrafiche * ERRORE alla riga 1: ORA-31038: Valore enumeration S non valido SQL> select decode(extractvalue(anagrafica,’/anagrafica/ @flag_fis_giu’), 2 ‘F’, extractvalue(anagrafica,’/anagrafica/ denominazione/cognome’), 3 ‘G’, extractvalue(anagrafica,’/anagrafica/ denominazione/ragsoc’)) Denominazione, 4 xmltype.extract(anagrafica,’/anagrafica/ indirizzo’) Indirizzo 5 from anagrafiche 6 where progr=6; DENOMINAZIONE INDIRIZZO --------------------- -----------------------------------Dei Paperoni <indirizzo> <via>del deposito di monete</via> <civico>333</civico> <cap>55445</cap> <comune>Paperopoli</comune> <provincia>PP</provincia> <telefono>024234343566</telefono> </indirizzo> SQL> select path(0) percorso, depth(0) livello 2 from resource_view 3 where under_path(res,3,’/max’,0) = 1; PERCORSO LIVELLO ------------------------------------ ---------/anagrafiche/anag5.xml 2 /squadre/italia.xml 2 /anagrafiche/anag6.xml 2 /ord.xml 1 /anagrafiche 1 /anagrafiche/anag3.xml 2 /anagrafiche/anag1.xml 2 /anagrafiche/anag2.xml 2 /anagrafiche/anag4.xml 2 /schemi 1 /schemi/team.xsd 2 /squadre 1 Aggiornata 1 riga. La prima versione modifica solo la riga che ha civico 133, mentre la seconda effettua l’update di tutte le righe della tabella, lasciando invariato il contenuto di quelle che hanno il civico diverso da 133. Il risultato finale è identico, ma se la tabella ha molte righe la seconda istruzione è sicuramente da scartare. Per completare il discorso sulla modifica dei dati, vediamo cosa accade se cerchiamo di porre a ‘S’ il flag pers_ fis_giu che, da schema, può valere solo ‘F’ o ‘G’: update anagrafiche set anagrafica = UPDATEXML(anagrafica, ‘/anagrafica/ @flag_fis_giu’,’S’) where extractvalue(anagrafica,’/anagrafica/ denominazione/nome’)=’Paperone’; update anagrafiche * ERRORE alla riga 1: ORA-31038: Valore enumeration S non valido CP 119 40 Ci è stato correttamente segnalato che il valore ‘S’ non è tra quelli ammessi. Concludiamo questo paragrafo con un esempio di lettura delle informazioni sulle risorse contenute nel Repository. Dopo aver caricato i documenti XML nel Repository mediante OEM (Figura 1), eseguiamo la query: select path(0) percorso, depth(0) livello from resource_view where under_path(res,3,’/max’,0) = 1; Per ogni risorsa (campo RES della vista RESOURCE_VIEW) presente nel Repository tra i discendenti della cartella ‘max’, chiediamo il percorso ed il livello di profondità. Le funzioni PATH e DEPTH danno risultati relativi alla cartella che si è indicata nella funzione UNDER_PATH. Il livello ed il percorso vanno intesi a partire dalla cartella ‘max’, non sono valori assoluti nel Repository. FIGURA 3 Registrazione di uno schema mediante Oracle Enterprise Manager ricerche effettuate mediante la funzione existsNode. La sintassi è analoga alla precedente con la differenza che indextype deve valere ctxsys.ctxxpath invece di ctxsys.content. Un indice “function-based” è legato all’utilizzo di una particolare funzione su un particolare frammento di documento XML. Se, ad esempio, si intende velocizzare l’istruzione: Select extractvalue(anagrafica, ‘/anagrafica/indirizzo/comune’) From anagrafiche; è possibile creare l’indice “function-based” che segue: CREATE INDEX COMUNE_I ON ANAGRAFICHE (extractvalue(anagrafica, ‘/anagrafica/indirizzo/comune’)); L’intero 0 è il legame tra la UNDER_PATH e le funzioni PATH e DEPTH che ad essa fanno riferimento. Avremmo potuto utilizzare qualunque altro numero intero. Il problema delle performance In un database relazionale, in genere, la forma gerarchica dei dati XML causa problemi molto seri alle prestazioni delle query. Per limitare questi problemi, Oracle ha introdotto nuovi tipi di indici e migliorato quelli che già esistevano. In questo paragrafo faremo una panoramica sui tipi di indice che velocizzano l’accesso ai dati XMLType ed al Repository. Sulle colonne XMLType possono essere costruiti quattro tipi di indice: “B*Tree” e “bitmap” (che non sono stati modificati in questa versione) “Oracle Text” (il vecchio InterMedia, che è stato migliorato) “function-based” (che è stato creato apposta per i dati XMLType). Analizziamo più in dettaglio gli ultimi due tipi. Gli indici di tipo “Oracle Text” funzionano sulle colonne di tipo VARCHAR e CLOB. In Oracle9i sono stati migliorati per gestire anche i campi di tipo XMLType. Ad esempio, sulla tabella definita da: CREATE TABLE documenti_xml ( Doc_id number(10), Documento XMLType); è possibile creare l’indice: CREATE INDEX documenti_xml_i ON documenti_xml (Documento) indextype is ctxsys.context; Ovviamente il campo “Documento” ha un’archiviazione di tipo CLOB: dunque ha senso utilizzare un indice di tipo “Oracle text”. L’indice documenti_xml_i mette a disposizione gli strumenti dei classici indici di tipo “Oracle Text”, in più le nuove funzioni INPATH ed HASPATH sono state aggiunte proprio per i campi XMLType. La funzione INPATH controlla se una data parola appare nel documento XML ad un percorso specificato, la funzione HASPATH controlla se un dato percorso esiste nel documento. Tra gli indici di tipo “Oracle Text” è stata aggiunta una nuova categoria, chiamata CTXXPATH, che ha lo scopo di migliorare le che fa esplicito riferimento alla funzione da eseguire. In sostanza la sintassi è quella solita, con la variante di indicare la funzione al posto delle colonne da indicizzare. Con questi indici possono essere velocizzate anche le funzioni extract() ed existsNode(). Per quanto riguarda il Repository, non c’è bisogno di creare esplicitamente indici per velocizzare la navigazione. Un indice gerarchico del Repository, infatti, viene sempre aggiornato ed utilizzato automaticamente. Quello che l’utente può fare è ricostruire l’indice gerarchico mediante la funzione DBMS_XDB. REBUILTHIERARCHICALINDEX. Conclusioni XML è uno standard che sta avendo grande successo e diffusione. Il mercato dei database XML è ancora molto aperto e quindi in continua evoluzione [3]. Fino alla prima release di Oracle9i, Oracle era sensibilmente indietro rispetto ai prodotti specializzati nella gestione di documenti XML, i cosiddetti ‘database XML nativi’. Con XDB Oracle si propone di ribaltare questa situazione ed assumere la leadership nel settore. Si dice addirittura che XDB è, a tutti gli effetti, un database XML nativo. Prima di dare giudizi definitivi bisogna aspettare un po’ per vedere i risultati dei test significativi del prodotto nonché le reazioni del mercato. Per ora ci limitiamo a sottolineare i grandi passi avanti fatti con questa nuova release. Il miglior pregio ed il peggior difetto: la completa trasparenza, per lo sviluppatore, nella gestione dei diversi tipi di archiviazione dei dati (CLOB o strutturato) e la carenza di flessibilità in caso di modifiche degli schemi in corso d’opera. Chiudo con un interrogativo: quanti dei vostri clienti sono disposti a migrare ad Oracle 9.2 per utilizzare XDB? BIBLIOGRAFIA [1] Oracle - “Oracle 9.2 XML Database Developer’s Guide Oracle XML DB”, Oracle Corp., 2002 [2] Oracle - “Oracle XML DB Technical White Paper”, Oracle Corp., 2002 [3] M. Ruocchio - “Archiviare dati XML nel Database”, Computer Programming 109, 2002 [4] M. Ruocchio - “XML Schema ai mondiali di calcio”, Computer Programming 109, 2002 RIFERIMENTI [5] http://www.oracle.com [6] http://technet.oracle.com [7] http://www.oracle.com/forums/forum.jsp?id=1015144 41 CP 119