Speicherung und Verarbeitung von XML-Dokumenten in relationalen DBMS Studienarbeit 16. November 2000 Martin Oesterle Studiengang: Mathematik mit Studienrichtung Informatik Betreuer: Prof. Dr. Lipeck UNIVERSITÄT HANNOVER Institut für Datenbanken und Informationssysteme 1 Zusammenfassung Im Zusammenhang mit dem World Wide Web hat XML 1 in den letzten Jahren immer mehr an Bedeutung gewonnen. Einige Webseiten im Internet werden heutzutage schon dynamisch aus XML-Dokumenten aufgebaut. Ebenso werden auch große Mengen an Daten im XML-Format verwaltet. Daher ist es nicht verwunderlich, daß es Bestrebungen gibt, eine Anfragesprache für XML zu definieren, wie es sie in Form von SQL und OQL schon für relationale und objektorientierte Daten gibt. Da relationale Datenbanksysteme sehr ausgereift sind und sich in der Praxis bewährt haben, stellt sich nun die Frage, inwieweit es möglich ist, eine Anfragesprache für XML auf ein solches System aufzusetzen. In dieser Arbeit wird ein Verfahren zur Abbildung von Dokumenten im XML-Format auf ein relationales Datenbanksystem beschrieben. Im gleichen Zuge wird eine Implementierung einer vereinfachten Version der Anfragesprache XML-QL vorgestellt. 1 Extensible Markup Language 2 Inhaltsverzeichnis 1 Einleitung 5 2 Begriffsdefinition 2.1 Extensible Markup Language (XML) . 2.2 XML-QL . . . . . . . . . . . . . . . . 2.2.1 Einführung und Beispiele . . . 2.2.2 XML-QL Syntax und Semantik 2.2.3 XML-QL Datenmodell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 6 6 8 10 3 Abbildung eines XML-Dokumentes auf Relationen 3.1 ER-Modell des XML-QL Datenmodells . . . . . . . . 3.2 Transformation des ER-Modells auf Relationen . . . 3.2.1 Relation Kante” . . . . . . . . . . . . . . . . 3.2.2 Relation Blatt’ . . . . . . . . . . . . . . . . . 3.2.3 Relation Attribut . . . . . . . . . . . . . . . . 3.2.4 Relation Dokument . . . . . . . . . . . . . . . 3.3 Importalgorithmus für XML-Dokumente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 11 12 12 13 13 13 4 Transformation von XML-QL auf SQL 4.1 Objektstruktur für XML-QL Anfragen . 4.2 Verarbeitung des Where-Abschnittes . . 4.3 Verarbeitung des Construct-Abschnittes 4.4 Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 14 16 18 20 5 Modulbeschreibung 5.1 Benutzeroberfläche . . . 5.2 Datenbankdesign . . . . 5.2.1 Datenbankmodell 5.2.2 Tabelle tblEdge . 5.2.3 Tabelle tblLeafs . 5.2.4 Tabelle tblAttrs 5.2.5 Tabelle tblDocs . 5.3 Anfrageprozessor . . . . 5.4 Anfrageübersetzer . . . 5.5 Dokumenterzeuger . . . 5.6 Dokument Import . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 24 24 24 24 26 26 26 26 26 26 27 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Installation 7 Benutzerhandbuch 7.1 Start des Programmes . . . . . . . . 7.2 Importieren eines XML-Dokumentes 7.3 Laden einer Anfrage . . . . . . . . . 7.4 Speichern einer Anfrage . . . . . . . 7.5 Ausführen einer Anfrage . . . . . . . 7.6 Auswahl der Ausgabeart . . . . . . . 27 . . . . . . . . . . . . . . . . . . 8 Erfahrungen und Ausblicke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 28 28 28 28 28 28 28 3 9 Anhang A ,SQL-Scripts 9.1 Tabellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.2 Sequenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.3 Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 29 29 30 31 1 Einleitung Diese Arbeit beschreibt die Abbildung mehrerer XML-Dokumente auf eine relationale Datenbank, insbesondere auf das DBMS Oracle 8i. Die Aufgabe besteht darin, ein Programm zu entwickeln, welches eine Anfrage in XML-QL entgegen nimmt und daraus ein XML-Dokument mit dem Anfrageergebnis erstellt. Dabei soll die Anfrage von XML-QL in SQL transformiert werden. Die Ergebnistupel der SQL-Anfrage werden dann wieder in ein XML-Dokument übersetzt. 2 Begriffsdefinition Im folgenden werden grundlegende Begriffe und Technologien, die in dieser Arbeit benutzt werden, kurz beschrieben. 2.1 Extensible Markup Language (XML) XML ist eine vom W3C-Konsortium definierte vereinfachte Untermenge von SGML. Das Ziel von XML ist es, eine selbstbeschreibende Sprache zum Austausch von Daten und Informationen zu erhalten. Dafür gibt es in XML die Möglichkeit, eigene Elemente und Strukturen zu beschreiben. Jedes XML-Dokument besteht aus einer Menge von Elementen, die in Start- und Endetags eingerahmt sind. Jedes Element hat einen Typ, der durch seinen Namen definiert wird und kann ebenfalls Attribute enthalten, die Namen und Werte enthalten. Weiterhin kann jedes Element auch noch eine beliebige Anzahl von Unterelementen enthalten. XML-Dokumente können also beliebig tief geschachtelt und strukturiert werden. Im weiteren Verlauf werden sich fast alle Beispielanfragen auf Dokumente mit folgender DTD 2 beziehen. Eine DTD beschreibt die Grammatik für ein XML-Dokument. <!ELEMENT tree (person*)> <!ELEMENT person (name,address,hobby*,child*)> <!ATTLIST person id ID #REQUIRED age CDATA #REQUIRED child IDREF #REQUIRED> <!ELEMENT child (person)> <!ELEMENT address (#PCDATA)> <!ELEMENT hobby (#PCDATA)> Diese DTD stellt einen Familienstammbaum dar, bei dem einer Person ein eindeutiger Schlüssel id und ein Alter age als Attribute zugewiesen werden muß. Der Name und die Adresse sind als Unterelemente definiert, die jeweils genau einmal vorkommen müssen. Die Unterelemente Kind und Hobby können gar nicht oder beliebig oft im Dokument als Unterelement von Person vorhanden sein. Ferner gibt es noch die Möglichkeit, das Kind einer Person nicht durch ein Unterelement zu beschreiben, sondern als Referenz innerhalb der Attributliste der Person. Im weiteren Verlauf wird folgendes XML-Dokument als Datenbasis für XML-QL Anfragen benutzt. <tree> <person id=1 age=55> <name>Peter</name> <address>4711 Fruitdale Ave.</address> <child> <person id=3 age=22> <name>John</name> 2 Document Type Definition 5 <address>5361 Columbia Ave.</address> <hobby>swimming</hobby> <hobby>cycling</hobby> </person> </child> <child> <person id=4 age=7> <name>David</name> <address>4711 Fruitdale Ave.</address> </person> </child> </person> <person id=2 age=38 child=4> <name>Mary</name> <address>4711 Fruitdale Ave.</address> <hobby>painting</hobby> </person> </tree> 2.2 2.2.1 XML-QL Einführung und Beispiele XML-QL 3 [1] ist eine Anfragesprache für XML-Dokumente . Sie erlaubt das Selektieren einzelner Element der XML-Datei und deren Rekonstruktion zu einem Ergebnisdokument. Eine XML-QL Anfrage besteht im wesentlichen aus einem Where-Teil und einem Construct-Teil. Selektieren von Dokumententeilen in der Where-Klausel Der Where-Teil der Anfrage beschreibt ein Muster, das die zu selektierenden Elemente beschreibt. Angenommen man möchte die Adressen aller Personen, die David heißen, anfragen. Eine Anfrage in XML-QL hat dann folgende Form: CONTRUCT <result> { WHERE <person> <name>David</name> <address>$a</address> </address> CONSTRUCT $a } </result> Dieses Anfrage selektiert alle Personen, die mindesten ein Unterelement vom Typ name und ein Unterelement vom Typ address besitzen und deren Name David ist. Für jede selektierte Person wird die Variable $a an die jeweilige Adresse gebunden. Eine Variable wird von Textelementen dadurch unterschieden, daß sie als Präfix ein $ enthält. Konstruktion des Ergebnisdokumentes in der Construct-Klausel Im obigen Beispiel wurden alle gefunden Adressen in ein Element mit dem Typ result geschrieben. Will man nun für jeden Wert der gebundenen Variable ein eigenes Element vom Typ address inklusive dem Namen David erzeugen, so sieht dies in XML-QL wie folgt aus. 3 Es gibt zur Zeit noch keine offizielle Spezifikation für XML-QL, daher beziehen sich alle Verweise auf eine Spezifikation immer auf [1] 6 CONTRUCT <result> { WHERE <person> <name>David</name> <address>$a</address> </address> CONSTRUCT <person> <name>David</name> <address>$a</address> </person> } </result> Als Ergebnisdokument erhält man nun das Wurzelelement result mit dem Unterelement person und den Unterelementen address und name, die die gebundenen Werte der Variable $a und $n enthalten, wobei die Anzahl der Elemente name und address der Anzahl der Bindungen entsprechen. Bedingungen für gebundene Variablen Möchte man zum Beispiel die Namen aller Personen, die älter als 24 Jahre sind, wissen, so kommt man mit den bisherigen Mittel nicht zum Ziel. Es gibt daher die Möglichkeit in XML-QL, Bedingungen für Variablen in Form eines oder mehrerer Prädikate anzugeben. Eine Verknüpfung der Attribute mit AND oder OR ist in der vorgestellten Implementierung möglich, ist allerdings nicht Bestandteil der in [1] beschriebenen Spezifikation, wobei in [2] eine Verknüpfung der Prädikate auch vorgesehen ist. Eine Anfrage aller Namen von Personen, die älter als 24 ,aber jünger als 50 sind, sieht dann so aus. CONTRUCT <result> { WHERE <person age=$a> <name>$n</name> </address>, ($a>24) AND ($a<50) CONSTRUCT <name>$n</name> } </result> Kombination von Elementen über Joins Mit Hilfe eines Joins können Anfragen über verschiedene Dokumente realisiert werden. Hat man nun z.B. ein zweites Dokument in dem es kein Element person, sondern stattdessen die Elemente man und woman gibt, und man gerne wissen möchte, welche Person ein Mann ist, so sieht eine Anfrage in XML-QL folgendermaßen aus. CONSTRUCT <result> { WHERE <person> <name>$n</name> </person> IN ’Persons.xml’, <man> <name>$n</name> </man> IN ’womanorman.xml’ CONSTRUCT <person> 7 <name>$n</name> </person> } </result> Als Ergebnis erhält man alle Namen der Person die sowohl in Persons.xml als auch in womanorman.xml als Element vom Typ man auftreten. Es können allerdings nur Textelemente über einen Join verbunden werden und keine Teilbaumreferenzen. Optionale Elemente Optionale Elemente werden dann gebraucht, wenn man Elemente sucht, die ein Unterelement haben können, aber nicht haben müssen. Sucht man zum Beispiel alle Namen von Personen, die entweder kein Hobby oder als Hobby schwimmen haben, kommt man auf folgende Anfrage. Der optionale Teil wird in eckige Klammern gesetzt. CONSTRUCT <result> { WHERE <person> <name>$n</name> [<hobby>swimming</hobby>] </person>, CONSTRUCT <person> <name>$n</name> </person> } </result> In dieser Arbeit darf innerhalb eines optionalen Elementes kein Unterelement als optional definiert werden, ebenfalls darf das Wurzelelement eines Anfragemusters nicht optional sein. 2.2.2 XML-QL Syntax und Semantik In diesem Abschnitt wird zuerst die Syntax der implementierten XML-QL Anfragesprache beschrieben. Danach wird die Semantik von XML-QL beschrieben. XML-QL Syntax In dieser Arbeit wird folgende XML-QL Syntax verwendet, die eine Untermenge der in der XML-QL Spezifikation definierten Syntax ist. 4 Der Syntax ist in EBNF5 beschrieben. <Query> ::= ’CONSTRUCT’ <Starttag><QueryBlock><Endtag> <QueryBlock> ::= <Starttag> ’{’<WherePart><ConstPart>’}’ <Endtag> <Starttag> ::= ’<’<Word>{’ ’<Attribute>}’>’ <Endtag> ::= ’</’<Word>’>’ 4 Bei der Produktion Predicate handelt es sich nicht um eine Untermenge der Spezifikation, sondern um eine Erweiterung, da eine Schachtelung der Prädikate zugelassen wird. 5 Erweiterte Backus-Naur Form 8 <Attribute> ::= <Word>’=’(<Quotedchars>|<Variable>) <Quotedchars>::= ’’’<Characters>’’’ <Characters> ::= {<Word>|<Whitespace>} <Word> ::= ’a’-’z’ | ’A’-’Z’ | ’0’-’9’ | ’.’ | ’,’ | ’_’ | ’/’ | ’:’ <WherePart> ::= ’WHERE’ <WherePat> {’,’ (<WherePat>|<Predicate>)} <ConstPart> ::= ’CONSTRUCT’ <Pattern>{<Pattern>} <Pattern> ::= <Starttag> (<Characters>|<Variable>|<Pattern>) <Endtag> <WherePat> ::= <Starttag> (<Characters>|<Variable>| <WherePat>| (’[’<Pattern>’])) <Endtag> <Variable> ::= ’$’<Word> <Predicate> ::= <Expression> | ’(’ <Predicate> ’)’ ’AND’ ’(’ <Predicate> ’)’ | ’(’ <Predicate> ’)’ ’OR’ ’(’ <Predicate> ’)’ <Expression> ::= <Variable> <RelOp> (<Quotedchars>|<Variable>) <RelOp> ::= ’=’ | ’!=’ | ’>=’ | ’<=’ | ’>’ | ’<’ XML-QL Semantik Angenommen man hat eine XML-QL Anfrage der Form WHERE P CONSTRUCT C, wobei P für ein beliebiges Pattern mit seinen Prädikaten steht, und C für ein beliebiges Constructpattern. P besteht also aus mehreren Bedingungen, die Tupel von Variablen x1 , . . . , xk binden. Die Semantik besteht nun aus zwei Teilen. Zum einen der WHERE-Teil der Anfrage. Hier wird eine Relation R(x1 , . . . , xk ) mit jeweils einer Spalte pro gebundener Variablen erzeugt. Jedes Tupel der Relation besteht nun aus denjenigen Werten der gebundenen Variablen, die den Bedingungen in P genügen. Jede dieser Variablen wird an eine Teilbaumreferenz oder an einen atomaren Wert gebunden. Nimmt man nun an, man hat n Ergebnistupel erhalten, so geht man nun zum zweiten Teil über, nämlich dem Constructpattern C. C beschreibt eine Schablone C(x1 , . . . , xk ), die an einige der Variablen gebunden ist. Man bezeichnet die einzelnen Tupel von R nun als xi1 , . . . , xik mit i = [1, n]. Nun wird für jedes Tupel aus R ein XML-Fragment Ci := C(xi1 , . . . , xik ) erzeugt. Das Anfrageergebnis ist dann die Vereinigung aus allen Ci . 9 [1] tree [2] person person [3] [age=55] [4] [age=38] name name Peter Mary address Fruitdale Av . Abbildung 1: XML-QL Datenmodell 2.2.3 XML-QL Datenmodell Wie jede Anfragesprache besitzt auch XML-QL ein Datenmodell, auf dem es aufbaut. Dieses Datenmodell besteht im wesentlichen aus einem Graphen. Dieser Graph setzt sich aus einer Menge von Knoten zusammen, wobei jedem Knoten genau eine eindeutige OID 6 zugewiesen wird. Die Kanten des Graphen werden mit dem Namen des XML-Tags beschriftet. Die Attribute des Tags werden als Namen/WertePaare an den Knoten gebunden. Sämtliche Blätter des Graphen enthalten Textkonstanten. Weiterhin gilt, daß es nur ein Wurzelelement geben darf. Betrachtet man nun folgendes Beispiel: <tree> <person age=’55’> <name>Peter</name> </person> <person age=’38’> <name>Mary</name> <address>Fruitdale Ave.</address> </person> </tree> Die zum Beispiel gehörende Instanz eines XML-QL Datenmodells besteht aus einen Wurzelknoten, von dem ein Kante für das Element tree ausgeht. Von diesem Element gehen wiederum zwei Kanten mit der Beschriftung Person aus. Die einzelnen Knoten haben jeweils das Attribut age mit den zugehörigen Werten. Von den beiden Knoten gehen dann ebenfalls Kanten aus ,die mit Name beschriftet sind. Da 6 Object Identifier 10 es keine Hierarchistufe unterhalb von Name gibt, handelt es sich bei den Knoten um Blätter und ihnen sind die Namen der Personen zugeordnet (Abbildung auf Seite 10). 3 Abbildung eines XML-Dokumentes auf Relationen In diesem Abschnitt wird zuerst die Abbildung des XML-QL Datenmodells auf ein ER-Schema und dann die Transformation des ER-Schemas auf Relationen beschrieben. Danach wird auf den Algorithmus zum Import eines XML-Dokumentes eingegangen. 3.1 ER-Modell des XML-QL Datenmodells Das XML-QL Datenmodell beschreibt einen Graphen. Es liegt nun nahe diesen Graphen in einem ERModell zu beschreiben, siehe Abbildung auf Seite 12. Da es möglich sein soll, mehrere Dokumente in einer Datenbank zu halten, gibt es noch eine Beziehung zwischen jedem Knoten und dem Dokument, zu dem er gehört. Das ER-Schema besteht im wesentlich aus den drei Entitäten Knoten, Blatt und Dokument. Im folgenden werden die einzelnen Entitaten und Beziehungen beschrieben. Entität Dokument: Die Entität Dokument enthält die eingearbeiteten Dokumente. Als Attribute besitzt sie eine eindeutige DokumentId , welches auch zugleich der Primärschlüssel ist, und ferner den Namen des Dokumentes, der ebenfalls eindeutig sein muß. Entität Blatt: Die Entität Blatt beschreibt die Blätter des Datenmodells. Zur Vereinfachung des ERSchemas werden Attribute ebenfalls als Blätter betrachtet. Um die Information, ob es sich um ein Blatt oder ein Attribut handelt, nicht zu verlieren, wird ein Attribut Typ eingeführt. Ein weiteres Attribut Wert enthält den Text, der an das Blatt bzw. Attribut gebunden ist. Handelt es sich bei dem Attribut um eine Referenz, so wird die Referenz im Attribut Wert eingetragen. Zu guter letzt besitzt jedes Blatt/Attribut noch eine eindeutige BlattId, welches zugleich der Primärschlüssel ist. Entität Knoten: Jeder Knoten des Datenmodells besitzt eine eindeutige KnotenId als Attribut und zugleich als Primärschlüssel. Weiterhin wird noch ein Attribut Tiefe eingeführt, welches die Tiefe des Teilbaumes unterhalb des Knotens enthält. Beziehung gehört zu: Jedem Knoten ist genau ein Dokument zugeordnet. Beziehung ist ein: Einige Knoten des Datenmodells sind Blätter oder Attribute im vereinfachten Modell. Beziehung Kante: Die Beziehung Kante modelliert die Graphstruktur des Datenmodells, und besitzt daher einen Quell- und einen Zielknoten. Eine weitere Vereinfachung des Datenmodells besteht darin, Referenzen von einem Knoten auf einen anderen nicht als Kante zu modellieren sondern als Typ eines Attributs. Deshalb kann man von einem Baum sprechen und fordern, daß jeder Knoten, der ein Quellknoten ist, maximal einmal als Zielknoten auftreten darf. 3.2 Transformation des ER-Modells auf Relationen Die Entitäten und Beziehungen werden nun auf einzelne Relationen verteilt. Im ersten Schritt wird für jede Entität und jede Beziehung eine eigene Relation erzeugt. Nun werden die 1:1 und 1:N Beziehungen auf die Entitätsrelationen aufgeteilt. Man erhält als Zwischenschritt dann folgende Relationen: Knoten(Id, BlattId, DokumentId, T ief e) Kante(KnotenId, KnotenId, N ame | {z } | {z } von nach Blatt(Id, W ert, T yp) Dokument(Id, N ame) 11 Abbildung 2: ER-Modell Da es sich bei der Entität Blatt um einen Knoten handelt, von dem keine Kante mehr ausgeht, kann man die Relation Knoten in die Relation Kante einsetzen. Da jeder innere Knoten außer der Wurzel immer im von-Teil der Kantenrelation auftritt, reicht es aus die BalttId und die DokumentId nur einmal zu speichern. Man erhält dann folgende Relation Kante’. Kante0 (QuellId, ZielId, BlattId, DokumentId, T ief e, N ame) {z } | {z } | nach von Nun wird noch die Relation Blatt anhand des Attributes Typ horizontal partitioniert. Dadurch erhält man die zwei neuen Relationen. Blatt0 (Id, W ert) Attribut(Id, W ert) Um die Information über den Typ des Blattes nicht zu verlieren, wird das Attribut Typ in die Relation Kante’ übernommen. Weiterhin wird BlattId in die Attribute BlattId’ und AttributId aufgeteilt. Als endgültiges Relationenmodell erhält man dann folgende Relationen. Kante00 (QuellId, ZielId, BlattId0 , AttributId, DokumentId, T yp, N ame, T ief e) Blatt0 (Id, W ert) Attribut(Id, W ert) Dokument(Id, N ame) 3.2.1 Relation Kante” Attribut QuellId ZielId BlattId’ AttributId DokumentId 3.2.2 Null nein nein ja ja nein eindeutig nein nein ja ja ja PK ja ja nein nein nein FK nein nein auf Blatt.Id auf Attribut.Id auf Dokument.Id Relation Blatt’ Attribut Id Wert Null nein nein eindeutig ja nein PK ja nein FK nein nein 12 3.2.3 Relation Attribut Attribut Id Wert 3.2.4 eindeutig ja nein PK ja nein FK nein nein PK ja nein FK nein nein Relation Dokument Attribut Id Wert 3.3 Null nein nein Null nein nein eindeutig ja ja Importalgorithmus für XML-Dokumente Für die Einarbeitung eines XML-Dokumentes muß die Eingabedatei geparst werden. Bei dem vorgestellten Algorithmus wird davon ausgegangen, daß es sich um einen eventgesteuerten Parser wie zum Beispiel die SAX-Parser7 handelt. Um die Arbeitsweise des Algorithmus übersichtlicher erklären zu können, wird auf ein vereinfachtes Datenmodell zurückgegriffen. Es besteht im wesentlich aus einer Relation. Kante(QuellId, ZielId, T yp, N ame, Zielwert, T ief e) Das Attribut Zielwert enthält den Wert des Zielknotens, falls es sich dabei um ein Blatt oder ein Attribut handelt. Definition: Sei S ein Stack. Die Funktion depth(Objekt) gibt die Anzahl der über dem Objekt liegenden Elemente von S zurück. S habe folgende Eigenschaften: 1. S enthält nur Objekt vom Typ Kante 2. Nach jedem push() gilt: ∀ki ∈ S : ki .tief e = max{ki .tief e, depth(ki .tief e} Im folgendes werden die Arbeitsschritte innerhalb der Ereignisse beschrieben. Dokumentanfang: Dieses Ereignis wird am Anfang eines Dokumentes ausgelöst. Eingabe:keine Aktion:keine Ausgabe:keine Starttag: Dieses Ereignis wird ausgelöst, wenn der Parser auf ein Starttag trifft. Eingabe:Name tag des Tags, Eine Liste der Attribute AttrList Aktion: Kante k = S.top() Erzeuge neue Kante k 0 k 0 .QuellId = k.ZielId k 0 .ZielId = neue KantenId k 0 .N ame = tag k 0 .T yp = ref erenz S.push(k 0 ) Für jedes attr in AttrList 7 SAX (Simple Api for XML) 13 Erzeuge neue Kante k 00 k 00 .QuellId = k.ZielId k 00 .ZielId = leer k 00 .N ame = attr.name k 00 .Zielwert = attr.wert Falls wert eine Referenz ist, dann k 00 .T yp = attrref , sonst k 00 .T yp = attribut gebe attr aus Ausgabe: Für jedes Attribut eine Kante k 00 Endetag: Dieses Ereignis wird ausgelöst, wenn der Parser auf ein Endetag trifft. Eingabe:keine Aktion: Kante k = S.pop() Ausgabe:Kante k Zeichenkette Diese Ereignis wird ausgelöst, wenn der Parser auf Text innerhalb eines Elementes trifft. Eingabe:Gefundener Text txt Aktion: Kante k = S.pop() k.T yp = Blatt k.W ert = txt S.push(k) Ausgabe:keine Dokumentende: Dieses Ereignis wird am Ende eines XML-Dokumentes vom Parser ausgelöst. Eingabe:keine Aktion: Der Algorithmus wird beendet Ausgabe:keine 4 Transformation von XML-QL auf SQL Im folgenden Abschnitt werden die Algorithmen vorgestellt, die zum einen eine XML-QL Anfrage in SQL übersetzen und zum anderen das Ergebnis der Anfrage wieder in ein XML-Dokument wandeln. Der Datenfluß der Anfrageverarbeitung ist auf Seite 15 beschrieben. Die XML-QL Anfrage wird vom Parser ausgewertet. Der Parser erzeugt dabei eine Objektstruktur der Anfrage. Aus dem Where-Teil der Objektstruktur wird die SQL-Anfrage generiert. Nachdem die SQL-Anfrage ausgeführt wurde, wird mit Hilfe der Ergebnistupel und des Construct-Teils der Objektstruktur das Ergebnisdokument im XML-Format erzeugt. 4.1 Objektstruktur für XML-QL Anfragen Um die XML-QL Anfrage verarbeiten zu können, muß diese in eine für den Algorithmus verwendbare Struktur gebracht werden. In dieser Arbeit wird dafür das Interpreterentwurfsmuster in Zusammenhang mit dem Besucherentwurfsmuster verwendet. Eine sehr gute Abhandlung ist in [7] zu finden. Eine 14 XML-QL Anfrage Parser Objektstruktur Generierung des SQL-Statement SQLStatement DBMS Ausführung des SQL-Statements Konstruktion des Ergebnisdokumentes Tupelmenge XMLDokument Abbildung 3: Datenfluss allgemeine Erläuterung dieser Entwurfsmuster würde den Rahmen dieser Arbeit sprengen. Das Interpreterentwurfsmuster ist eine objektorientierte Implementierung des AST8 einer XML-QL Anfrage, wie in [5] und [6] vorgeschlagen. Im folgenden werden alle Klassen des Objektmodells beschrieben. Klasse ASTQuery Die Klasse ASTQuery kapselt die gesamte XML-QL Anfrage und hat als Attribut lediglich den Namen des Tags des äußeren Construct-Blockes. Klasse name queryBlock ASTQuery Name des Wurzelelementes der Anfrage Referenz auf eine Objekt der Klasse ASTQueryBlock Klasse ASTQueryBlock Die Klasse ASTQueryBlock kapselt einen Anfrageblock in XML-QL und enthält daher Verweise auf die Wherepatterns, das Constructpattern und die Prädikate. Klasse wherePatterns predicate contstructPat ASTQueryBlock Eine Menge von Verweisen auf ASTElement-Objekte Ein Verweis auf das Wurzelobjekt der ASTPredicateStruktur Verweis auf ein ASTElement-Objekt Klasse ASTElement Die Klasse ASTElement kapselt eine XML-Element, mit dem Unterschied, daß es als Unterelement auch Variablen enthalten kann. Klasse ASTElement isRoot Wurzelement (ja oder nein) isOptional Flag, ob Element optional ist name Name des Elements subElement Verweis auf Objekt der Klasse ASTElement,ASTString oder ASTVariable 8 Abstrakter Syntax Baum 15 Klasse ASTString Die Klasse ASTString kapselt eine Zeichenfolge. Klasse text ASTString Zeichenkette Klasse ASTVariable Die Klasse ASTVariable kapselt eine Variable. Klasse varName ASTVariable Variablenbezeichner Klasse ASTPredicate Die Klasse ASTPredicate kapselt die geschachtelten Bedingungen. Klasse operator left right ASTPredicate Verknüpfungsoperator and oder or Verweis auf ASTPredicate, ASTString oder ASTVariable Verweis auf ASTPredicate, ASTString oder ASTVariable Klasse Context Die Klasse Context enthält alle global verfügbaren Informationen für den Algorithmus. Klasse Context select Container für die Elemente des Selectteils where Container für die Elemente des Whereteils from Container für die Elemente des Fromteils stack Stack für die Tabellenaliase hashtable Dictionary für die Zurordnung der Variablen zu den Tabellenaliasen. newAlias() Funktion zum Erzeugen neuer Tabellenaliase 4.2 Verarbeitung des Where-Abschnittes Dieser Abschnitt beschäftigt sich mit dem Algorithmus zur Erzeugung des SQL-Statements. Der gesamte Algorithmus zur Verarbeitung des Where-Abschnittes besteht aus zwei Teilen. Der erste Teil ist Algorithmus A und arbeitet die Wherepatterns ab und startet in der Objektstruktur bei jedem Element aus ASTQueryBlock.wherePatterns . Der zweite Teil ist recht einfach. Als Einstiegspunkt in die Objektstruktur wird das Wurzelobjekt der Klasse ASTPredicate benutzt. Es wird ein Inorderdurchlauf gestartet, bei dem die Bedingungen an den Whereteil der SQL-Anfrage angehängt werden. Die Auflösung der Variablen erfolgt über die Hashtabelle des Contextobjekts. Bemerkung: Bei der Implementierung des optionalen Elementes wird im Gegensatz zu [3] keine UNION verwendet, da bei einer Vielzahl von optionalen Elementen jede Kombination der Elemente einzeln angefragt wird. In dieser Arbeit wird das optionale Element über einen temporären View der Kantenrelation und einem anschließenden outer join realisiert. Definition: Im folgenden bezeichnet e immer die aktuelle Instanz von ASTElement. Der Pseudocode lehnt sich an die Programmiersprache Java an. Methode ASTElement.algorithmusA(Context c) 1.Hole aktuellen Alias vom Stack oldAlias=c.stack.top() 2.Erzeuge neuen Alias und speichere ihn auf den Stack aktAlias=c.newAlias() 16 c.stack.push(aktAlias) 3.Erweitere From-Klausel um neuen Alias, falls Element nicht Optional If not e.isOptional then c.from.add(aktAlias ) 4.Ist e Wurzelelement ? Wenn ja bilde joins If e.isRoot then 4.1 Unterscheide zwischen optionalen und nicht optionalen Element if e.isOptional then OptView=SELECT * FROM Kante WHERE Name=e.name c.from.add(OptView aktAlias ) c.where.add(oldAlias.ZielId=aktAlias.QuellId(+) ) else c.where.add(oldAlias.ZielId=aktAlias.QuellId ) 5.Erzeuge Bedingung für den Kantennamen c.where.add(aktAlias=e.name ) 6.Für jedes Subelement e’ von e führe folgende Schritte aus for each e’ in subElements do 6.1.Falls es eine Element ist, tue ... if e’ instanceof ASTElement then 6.1.1.Führe Algorithmus fuer Subelement aus e’.algorithmusA(c) 6.2.Falls es ein Wert ist, tue ... if e’ instanceof ASTString then 6.2.1. Erzeuge Bedingung c.where.add(aktAlias.wert=e’.text ) 6.3.Falls es eine Variable ist, tue ... if e’ instanceof ASTVariable then 6.3.1.Erweitere Select-Teil um Bindungen c.select.add(aktAlias.Typ,aktAlias.ZielId aktAlias.Zielwert,aktAlias.Tiefe ) 6.3.2.Ist Variablenname schon in der Hashtabelle, dann erzeuge join-Bedingung, sonst speichere sie in die Hashtabelle if e’.varname in c.hashtable then c.where.add(c.hastable.get(e’.varname)=aktAlias.Wert ) else c.hashtable.add(e’.varname.aktAlias.Wert) 7.Räume Stack auf c.stack.pop() 17 Als Beispiel dient folgende ,bereits aus der Einführung bekannte Anfrage mit optionalem Element. CONSTRUCT <result> { WHERE <person> <name>$n</name> [<hobby>swimming</hobby>] </person>, CONSTRUCT <person> <name>$n</name> </person> } </result> Beim Durchlaufen des Where-Abschnittes trifft der Algorithmus zuerst auf das Element Person. Als Zwischenergebnis erhält man: SELECT FROM KanteA WHERE A.N ame =0 person0 Als nächstes tritt das Element Name als Variable auf, daraus folgt dann. SELECT B.T yp, B.ZielId, B.ZielW ert, B.T ief e FROM KanteA, KanteB WHERE A.N ame =0 person0 AND A.ZielId = B.QuellId AND B.N ame =0 name0 Das darauf folgende Element ist optional, es wird also ein View als Unterabfrage gebildet. Das endgültige SQL-Statement sieht nun wie folgt aus. SELECT B.T yp, B.ZielId, B.ZielW ert, B.T ief e FROM KanteA, KanteB, (SELECT * FROM Kante WHERE Name=’hobby’) C WHERE A.N ame =0 person0 AND A.ZielId = B.QuellId AND B.N ame =0 name0 AND A.ZielId = C.QuellId AND C.N ame =0 hobby 0 AND C.ZielW ert =0 swimming 0 4.3 Verarbeitung des Construct-Abschnittes Wie im Abschnitt über die Semantik von XML-QL schon beschrieben, erhält man für das Contructpattern C eine Menge von Tupeln Ci := C(xi1 , . . . , xik ) mit i = [1, n] bei n Anfrageergebnissen aus dem 18 Where-Abschnitt. Bei der Rekonstruktion dient das Objekt ASTQueryBlock.constructPat als Einstiegspunkt. Der Algorithmus durchläuft das Constructpattern für jedes Tupel Ci des Anfrageergebnisses. Bei jeder Iteration sind zwei Fälle zu unterscheiden. Zum einem der Fall, daß ein xj , j = [1, k] an keine Teilbaumreferenz gebunden ist, dann wird einfach der an xj gebundene Wert eingesetzt. Zum anderen der Fall, daës sich um eine Teilbaumreferenz handelt, wobei es dabei verschiedene Möglichkeiten zur Rekonstruktion gibt. Das naive Verfahren besteht darin, den Baum rekursiv Stufe für Stufe mit Hilfe von vorbereiteten SQL-Anweisungen zu erstellen. Die Anzahl der benötigten SQL-Anfragen ist dabei proportional zur Tiefe des Teilbaumes. Eine anderes Vorgehen macht sich zu Nutze, daß beim Einlesen des XMLDokumentes für jeden Knoten die Tiefe des darunterliegenden Teilbaumes gespeichert wurde. Es ist mit dieser Information möglich, den Teilbaum mit Hilfe nur eines SQL-Statements zu rekonstruieren. Sei nun d die Tiefe des Teilbaumes und s die Id der Teilbaumwurzel, dann hat das SQL-Statement in Oracle-SQL folgende Form: SELECT k1 .N ame, k1 .Zielwert, . . . , kd .N ame, kd .Zielwert FROM Kante k1 , . . . , Kante kd WHERE (k1 .QuellId = s) AND (k1 .ZielId = k2 .QuellId(+)) AND ... (kd−1 .ZielId = kd .QuellId(+)) Um das Verfahren an einem praktischen Beispiel zu erläutern, soll dieser Teilbaum aus der Datenbank rekonstruiert werden. <person id=3 age=22> <name>John</name> <address>5361 Columbia Ave.</address> <child> <person> <name>Dave</name> </person> </child> </person> Das SQL-Statement sieht dann bei einer Teilbaumtiefe von 3 folgendermaßen aus: SELECT k1 .N ame, k1 .Zielwert, k2 .N ame, k2 .Zielwert, k3 .N ame, k3 .Zielwert FROM Kante k1 , Kante k2 , Kante k3 WHERE k1 .QuellId = 3 AND k1 .ZielId = k2 .QuellId(+) AND k2 .ZielId = k3 .QuellId(+) Das Ergebnis der Anfrage ist auf Seite 20 in Tabellenform dargestellt. Man kann daraus nun den Teilbaum wieder rekonstruieren. 19 k1 .N ame id age name address child k1 .Zielwert 3 22 John 5361 Col . . . (null) k2 .N ame (null) (null) (null) (null) person k2 .Zielwert (null) (null) (null) (null) (null) k3 .N ame (null) (null) (null) (null) name k3 .Zielwert (null) (null) (null) (null) Dave Tabelle 1: Anfrageergebnis der Teilbaumrekonstruktion 4.4 Beispiel In diesem Beispiel wird der gesamte Datenfluß durchgespielt. Als XML-Dokument wird das schon bekannte Beispiel aus Kapitel 2.1 benutzt. <tree> <person id=1 age=55> <name>Peter</name> <address>4711 Fruitdale Ave.</address> <child> <person id=3 age=22> <name>John</name> <address>5361 Columbia Ave.</address> <hobby>swimming</hobby> <hobby>cycling</hobby> </person> </child> <child> <person id=4 age=7> <name>David</name> <address>4711 Fruitdale Ave.</address> </person> </child> </person> <person id=2 age=38 child=4> <name>Mary</name> <address>4711 Fruitdale Ave.</address> <hobby>painting</hobby> </person> </tree> Möchte man nun zum Beispiel die Namen und die Kinder aller Personen, die in der 4711 Fruitdale Ave. wohnen, ausgegeben haben, sieht die Beispielanfrage wie folgt aus. CONSTRUCT <result> { WHERE <person> <name>$n</name> <address>4711 Fruitdale Ave.</address> <child>$c</child> </person>, CONSTRUCT <person> <name>$n</name> 20 QuellId 0 1 2 2 2 2 2 7 8 8 8 8 8 8 2 15 16 16 16 16 1 21 21 21 21 21 21 ZielId 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Typ referenz referenz attribut attribut blatt blatt referenz referenz attribut attribut blatt blatt blatt blatt referenz referenz attribut attribut blatt blatt referenz attribut attribut attrref blatt blatt blatt Name tree person id age name address child person id age name address hobby hobby child person id age name address person id age child name address hobby Zielwert 1 55 Peter 4711 Fruitdale Ave. 3 22 John 5461 Columbia Ave. swimming cycling 4 7 David 4711 Fruitdale Ave. 2 38 4 Mary 4711 Fruitdale Ave. painting Tiefe 4 3 0 0 0 0 2 1 0 0 0 0 0 0 2 1 0 0 0 0 1 0 0 0 0 0 0 Tabelle 2: Beispielkantentabelle <hobby>babysitting</hobby> <child>$c</child> </person> } </result> Im ersten Schritt wird das XML-Dokument in die Datenbank importiert. Dazu wird das XMLDokument durch den Importalgorithmus auf die Tabelle auf Seite 21 abgebildet. Im nächsten Schritt wird durch einen Parser die Objektstruktur aus Seite 22 erzeugt. Danach wird die SQL-Anfrage anhand des beschriebenen Algorithmus generiert. Als Ergebnis erhält man dann folgendes SQL-Statement. SELECT B.T yp AS n Typ, B.ZielId AS n ZId, B.Zielwert AS n ZWert, B.T ief e AS n Tiefe, D.T yp AS c Typ, D.ZielId AS c ZId, D.Zielwert AS c ZWert, D.T ief e AS c Tiefe FROM KanteA, KanteB, 21 Abbildung 4: Objektstruktur der Beispielanfrage KanteC, KanteD WHERE A.N ame =0 person0 AND A.ZielId = B.QuellId AND B.N ame =0 N ame0 A.ZielId = C.QuellId AND C.N ame =0 address0 AND C.Zielwert =0 4711F ruitdaleAve.0 AND A.ZielId = D.QuellId AND D.N ame =0 child0 Die Anfrage liefert als Ergebnis alle Bindungen der Variablen $n und $c. n Typ blatt blatt blatt n ZId 5 5 25 n ZWert Peter Peter Mary n Tiefe 0 0 0 c Typ referenz referenz attrref c ZId 7 15 24 c ZWert 4 c Tiefe 2 2 0 Nachdem nun die Bindungen der Variablen gefunden wurde, wird nun der Construct-Abschnitt betrachtet. Dabei wird für jedes Tupel der Ergebnismenge das Constructpattern durchlaufen. Die Variable $n ist immer als atomarer Wert an einen Text gebunden und wird einfach eingesetzt. Die Variable $c ist zweimal an Teilbaumreferenzen gebunden und einmal an eine Attributreferenz. Die Attributreferenz wird ebenfalls einfach eingesetzt. Für die erste Teilbaumreferenz auf den Zielknoten 7 wird folgenden SQL-Statement erzeugt. SELECT A.N ame, A.Zielwert, A.T yp, B.N ame, B.Zielwert B.T yp 22 FROM KanteA,KanteB WHERE A.QuellId = 7 AND A.ZielId = B.QuellId(+) AND Als Anfragergebnis erhält man folgende Tabelle. A.Name person person person person person person A.Zielwert A.Typ referenz referenz referenz referenz referenz referenz B.Name id age name address hobby hobby B.Zielwert 3 2 John 5361 Columbia Ave. swimming cycling B.Typ attribut attribut blatt blatt blatt blatt Beim zweiten Teilbaum wird ebenso verfahren. Aus den Anfragergebnissen der Teilbaumanfragen und den Einsetzungen wird das XML-Dokument erzeugt. <?xml version=’1.0’?> <result> <person> <name>Peter</name> <hobby>babysitting</hobby> <child> <person id=3 age=22> <name>John</name> <address>5361 Columbia Ave.</address> <hobby>swimming</hobby> <hobby>cycling</hobby> </person> </child> </person> <person> <name>Peter</name> <hobby>babysitting</hobby> <child> <person id=4 age=7> <name>David</name> <address>4711 Fruitdale Ave.</address> </person> </child> </person> <person child=4> <hobby>babysitting</hobby> </person> </result> 5 Modulbeschreibung Ein Gesamtmodulübersicht ist auf Seite 24 zu sehen. In den folgenden Abschnitten wird auf die einzelnen Module und ihre Funktionen kurz eingegangen. Die genaue Dokumentation zu den einzelnen Packages und Klassen ist als javadoc vorhanden. 23 !! # $ # $%%& & - - ."."''/./02.1 021 3344 !!"" 5 5 ''6!6!77 % '% ( ))% %*+%,*+ , '% ( Abbildung 5: Modulübersicht 5.1 Benutzeroberfläche Die Benutzeroberfläche besteht aus einem horizontal geteilten Fenster. Im oberen Fenster werden Anfragen entgegengenommen. Im unteren Fenster werden Fehlermeldungen und Anfragergebnisse ausgegeben. Die Unterscheidung zwischen Fehlermeldungen und Anfrageergebnissen wird über einen Reitersteuerelement realisiert. Im Auswahlmenü stehen Funktionen zum Importieren von XML-Dokumenten, Starten von Anfragen, Speichern von Anfragen, Laden von Anfragen und zum Beenden des Programmes bereit. Außerdem kann ausgewählt werden, ob die Ausgabe im Fenster oder in eine Datei erfolgen soll. 5.2 5.2.1 Datenbankdesign Datenbankmodell In diesem Abschnitt wird auf die Implementierung des Datenbankentwurfs auf Oracle 8i eingegangen und beschrieben. Die einzelnen Tabellen werden mit ihren Datentypen, Indexen und Intergritätsbedingungen beschrieben. Die SQL- Erstellungsskripte sind im Anhang enthalten. 5.2.2 Tabelle tblEdge Die Tabelle tblEdge ist die Implementierung der Relation Kante00 . 24 Abbildung 6: Benutzeroberfläche Feldname SourceId Feldtyp integer Null nein TargetId integer ja LeafId integer ja AttrId integer ja DocId integer nein EdgeName Type varchar(30) char(4) nein nein Depth smallint nein Inhalt Id des Startknoten der Kante, wird automatisch über eine Sequenz generiert Id des Zielknoten der Kante, Fremdschlüssel auf tblEdge(SourceId) Id des Elementwertes, falls es sich um ein Blatt handelt, Fremdschlüssel auf tblLeafs(LeafId) Id des Attributwertes, falls es sich um ein Attribut handelt, Fremdschlüssel auf tblAttrs(AttrId) Id des Dokumentes, in aus dem die Kante generiert wurde, Fremdschlüssel auf tblDocs(DocId) Name der Kante Typ des Zielknotens (Referenz,Blatt,Attribut,Attributreferenz) Tiefe des Teilbaumes unterhalb des Zielknotens Primärschlüssel (SourceId,TargetId,LeafId,AttrId,DocId,EdgeName) Indexe Es wird jeweils ein Index über die Felder SourceId,(EdgeName und TargetId), Leafid, AttrId und DocId gebildet. 25 5.2.3 Tabelle tblLeafs Die Tabelle tblLeafs ist die Implementierung der Relation Blatt0 Feldname LeafId Feldtyp integer Null nein Value varchar(255) ja Inhalt Id des Blattes bzw. Elementwertes, wird automatisch über eine Sequenz generiert Wert des Blattes (Zeichenkette) Primärschlüssel (LeafId) Indexe Es wird jeweils ein Index über die Felder LeafId und Value gebildet. 5.2.4 Tabelle tblAttrs Die Tabelle tblAttrs ist die Implementierung der Relation Attribut Feldname AttrId Feldtyp integer Null nein Value varchar(128) ja Inhalt Id des Attributes, wird automatisch über eine Sequenz generiert Wert des Attributes (Zeichenkette) Primärschlüssel (AttrId) Indexe Es wird jeweils ein Index über die Felder AttrId und Value gebildet. 5.2.5 Tabelle tblDocs Die Tabelle tblDocs ist die Implementierung der Relation Dokument Feldname DocId Feldtyp integer Null nein Value varchar(128) ja Inhalt Id des Dokumentes, wird automatisch über die Sequenz SEQ DOCID generiert Dokumentenname Primärschlüssel (DocId) Indexe Es wird jeweils ein Index über die Felder DocId und Value gebildet. 5.3 Anfrageprozessor Der Anfrageprozessor koordiniert den Ablauf der Anfrage. Er nimmt die Anfrage von der Oberfläche entgegen und läßt vom Anfrageübersetzer eine SQL-Anfrage generieren. Daraufhin führt er die Anfrage an die Datenbank aus und übergibt dem Dokumentenerzeuger die Ergebnistupel der SQL-Anfrage. 5.4 Anfrageübersetzer Der Anfrageübersetzer generiert mit Hilfe des vorgestellten Algorithmus aus der XML-QL Anfrage ein SQL-Statement. Der Algorithmus ist in der Implementierung an die erweiterten Tabellenstrukturn angepaßt. 5.5 Dokumenterzeuger Der Dokumenterzeuger erstellt mit Hilfe des vorgestellten Algorithmus und der Ergebnistupel der SQLAnfrage das Ergebnisdokument der XML-QL Anfrage. 26 5.6 Dokument Import Der Dokumentimport importiert ein XML-Dokument mit Hilfe des beschriebenen Algorithmus in die Datenbank. Der Import wird über die JDBC-Schnittstelle realisiert. 6 Installation Die Installationsdatein sind in vier Verzeichnisse aufgeteilt. install/jaxp/ In diesem Verzeichnis befindet sich das JAXP-Paket von SUN. Dieses Paket beinhaltet den benutzen SAX-Parser. install/src/ In diesem Verzeichnis befinden sich Programmquellen und Installationsskripts in einer Zip-Datei. install/sql/ In diesem Verzeichnis befindet sich das SQL-Skript zum Erstellen der Datenbanktabellen und Indexe. install/doc/ In diesem Verzeichnis befindet sich die TEX-Quellen und Bilder dieses Dokumentes. Nun werden die einzelnen Schritte der Installation beschrieben. 1.Vorrausetzungen: Der Benutzer muß einen Account für den Oracleserver des Institutes B der Universität Hannover und die Rechte besitzen, Tabellen anlegen, verändern und löschen zu können. Weiterhin muß das JAXP-Packet korrekt laut Installationsanleitung von SUN installiert sein, ebenso wie die Klassenbibliothek des Institutes. 2.Anlegen der Tabelle: Nachdem alle Vorraussetzungen erfüllt sind, muß das SQL-Script xmlqldb.sql ausgeführt werden. 3.XML-QL Engine Aus dem Verzeichnis install/src muß die Datei xmlql.zip in das gewünschte Zielverzeichnis entpackt werden. Es ist darauf zu achten ,daß sich das Zielverzeichnis im CLASSPATH befindet. Nun muß das Script buildall.sh 9 ausgeführt werden. Das Programm kann mit Hilfe des Skriptes xmlql.sh aus dem Zielverzeichnis gestartet werden. 9 Das Skript wird in das angegebene Zielverzeichnis entpackt 27 7 7.1 Benutzerhandbuch Start des Programmes Nach erfolgreicher Installation kann das Programm durch Aufrufen des Skripts xmlql.sh gestartet werden. Nachdem das Programm geladen wurde, wird der Benutzer aufgefordert den Username, die Datenbank und sein Password einzugeben, um sich an der Datenbank anzumelden. 7.2 Importieren eines XML-Dokumentes Unter dem Menüpunkt File-Import kann der der Import ausgewählt werden. Der Benutzer wird daraufhin aufgefordert, über eine Dialogauswahlbox die zu importierende Datei anzugeben. Danach wird der Import gestartet. 7.3 Laden einer Anfrage Unter dem Menüpunkt Query-load kann eine Anfrage über eine Dialogauswahlbox geladen werden. Die Dateien haben im Regelfall die Endung .xmlql. Wenn das Laden der Anfrage erfolgreich war, wird diese im Anfragefenster vorgeblendet. 7.4 Speichern einer Anfrage Unter dem Menüpunkt Query-save kann eine Anfrage über eine Dialogauswahlbox gespeichert werden. Der Benutzer wird dabei aufgefordert, einen Dateinamen anzugeben. 7.5 Ausführen einer Anfrage Unter dem Menüpunkt Query-execute kann eine Anfrage ausgeführt werden. Fehlermeldungen werden dem Benutzer im Meldungsfenster angezeigt. Falls das Ergebnis der Anfrage in eine Datei geschrieben werden soll, wird der Benutzer über eine Dialogauswahlbox zur Eingabe eines Dateinamens aufgefordert. 7.6 Auswahl der Ausgabeart Unter dem Menpunkt Option-send to kann das Ziel der Ausgabe über Radiobuttons eingestellt werden. Entweder wird das Ergebnis in eine Datei geschrieben oder im Ergebnisfenster angezeigt. 8 Erfahrungen und Ausblicke Verarbeitung großer XML-Dokumente Bei Probeläufen mit einer großen Datenbank (Tabelle mit mehr als 1.000.000 Zeilen) konnten keine nennenswerten Verschlechterungen der Geschwindigkeit festgestellt werden. Ein Großteil der Anfragezeit wurde für das Schreiben des Ergebnisdokumentes benötigt. Dabei wurden Indexe über die Felder tblEdge(Name,TargetId), tblEdge(SourceId), tblEdge(LeafId), tblEdge(AttrID) und tblEdge(DocId) verwendet. Erweiterungen der Implementierung Das Verfolgen von Referenzen im Where-Abschnitt kann durch die Einführung einer Hashtabelle beim Einlesen der Daten implementiert werden. Die Hashtabelle verwaltet die Beziehung zwischen der internen, von der Datenbank vergebene Id, und den externen, als Attribut Id vorhandenen, Id eines Elementes. Trifft der Importalgorithmus auf ein Attribut des Typs IDREF oder IDREFS wird anhand der Hashtabelle die externe Id in die interne Id übersetzt und als ZielId in die Kantenrelation geschrieben. Dieser Algorithmus funktioniert allerdings nur ,falls eine DTD vorhanden ist, und eine Referenz sich auf eine ID bezieht, die schon vorher im Dokument definiert wurde. 28 Falls eine Schachtelung der Anfragen möglich sein soll, muß für jedes Teilanfrageergenis eine Art Datenbankview erstellt werden. Die jetzige Implementierung erzeugt direkt aus den Ergebnistupeln der SQL-Anfrage das Ergebnisdokument, man müßte also einen Zwischenschritt einbauen, der aus den Ergebnistupeln der SQL-Anfrage eine temporäre Kantentabelle erzeugt und dann aus der Kantentabelle erst das XML-Ergebnisdokument. Mit einer solchen Tabelle kann eine Schachtelung leicht realisiert werden. Es gibt in XML-QL ein Konstrukt, das einen Outer Join in relationalen Datenbanken entspricht. Dabei werden zwei Anfrageblöcke mit einer Joinvariable erzeugt. CONSTRUCT <result> { WHERE ... <join>$a</join> ... CONSTRUCT ... } { WHERE ... <join>$a</join> ... CONSTRUCT ... } </result> Dieses Feature ist ebenfalls leicht über temporäre Kantenrelationen zu implementieren. 9 9.1 Anhang A ,SQL-Scripts Tabellen Tabelle tblEdge create table tblEdge ( SourceId int not null, TargetId int null, LeafId int null, AttrId int null, DocId int not null, EdgeName char(30) not null, Type char(4) not null, Depth smallint not null ); alter table tbledge add constraint pk_tbledge primary key (SourceId,TargetId); create index idx_SourceId on tblEdge(SourceId); create index idx_EdgeName_TargetId on tblEdge(EdgeName,TargetId); create index idx_LeafId on tblEdge(LeafId); 29 create index idx_AttrId on tblEdge(AttrId); create index idx_DocId on tblEdge(DocId); Tabelle tblDocs create table tblDocs ( DocId int not null, url varchar(255) null ); alter table tblDocs add constraint pk_tbldocs primary key (DocId); Tabelle tblAttrs create table tblAttrs ( AttrId int not null, Value varchar(255) null ); alter table tblAttrs add constraint pk_tblattrs primary key (AttrId); create index idx_Attrs_Value on tblAttrs(Value); Tabelle tblLeafs create table tblLeafs ( LeafId int not null, Value varchar(255) null ); alter table tblleafs add constraint pk_tblleafs primary key (leafid); create index idx_Leafs_Value on tblLeafs(Value); 9.2 Sequenzen Sequenz SEQ SOURCEID CREATE SEQUENCE seq_SourceId INCREMENT BY 1 START WITH 1 NOMAXVALUE NOCYCLE CACHE 10; Sequenz SEQ LEAFID CREATE SEQUENCE seq_LeafId INCREMENT BY 1 START WITH 1 NOMAXVALUE NOCYCLE CACHE 10; 30 Sequenz SEQ ATTRID CREATE SEQUENCE seq_AttrId INCREMENT BY 1 START WITH 1 NOMAXVALUE NOCYCLE CACHE 10; Sequenz SEQ DOCID CREATE SEQUENCE seq_DocId INCREMENT BY 1 START WITH 1 NOMAXVALUE NOCYCLE CACHE 10; 9.3 Funktionen Funktion getNextSourceId CREATE OR REPLACE FUNCTION getNextSourceId RETURN int AS New_SourceId int; BEGIN SELECT seq_SourceId.nextval INTO New_SourceId FROM dual; RETURN (New_SourceId); END; Funktion getNextLeafId CREATE OR REPLACE FUNCTION getNextLeafId RETURN int AS New_LeafId int; BEGIN SELECT seq_LeafId.nextval INTO New_LeafId FROM dual; RETURN (New_LeafId); END; Funktion getNextAttrId CREATE OR REPLACE FUNCTION getNextAttrId RETURN int AS New_AttrId int; BEGIN SELECT seq_AttrId.nextval INTO New_AttrId FROM dual; RETURN (New_AttrId); END; Funktion getNextDocId CREATE OR REPLACE FUNCTION getNextDocId RETURN int AS New_DocId int; BEGIN SELECT seq_DocId.nextval INTO New_DocId FROM dual; RETURN (New_DocId); END; 31 Literatur [1] Alin Deutsch, Mary Fernandez, Daniela Florescu, Alon Levy and Dan Suciu XML-QL: a query Language for XML. In Proc of the Int. WWW Conf., 1999 [2] Mary Fernandez Beispielimplementierung der XML-QL Anfragesprache, http://www.research.att.com/˜mff/xmlql [3] Daniela Florescu and Donals Kossmann Storing and Querying XML Data Using an RDBMS, Bulletin of the Technical Commitee on Data Engineering, September 1999. [4] Daniela Florescu and Donald Kossmann A Performance Evaluation of Alternative Mapping Schemes for Storing XML Data in a relational Database, INRIA Rapport de recherche, Mai 1999. [5] Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullmann Compilerbau, Addison Wesley Deutschland [6] N. Wirth Compilerbau, Teubner Studienbucher Informatik [7] Erich Gamma et al. Design Patterns, Addison Wesley Professional Computing Series 32