Department of Informatics, University of Zürich MSc Thesis MidPerm - Realisierung einer Provenance SQL-Erweiterung als Middleware Lösung Vagliardo Alessandro Matrikelnummer: 02-920-502 Email: [email protected] Mai 31, 2010 supervised by Prof. Dr. M. Böhlen and B. Glavic Department of Informatics 2 Zusammenfassung Ich habe eine Packung Milch gekauft und möchte wissen von welchem Bauernhof und welchen Kühen die Milch kommt. Unter anderen können solche Fragen durch Provenance Anwendungen beantwortet werden. Es gibt aber wenige generelle Lösungen, die auf allen Datenbankensystemen angewendet werden können. MidPerm ist ein erster Schritt eine allgemeine Provenance Applikation zu entwickeln. Die MidPerm Applikation entkoppelt das in Postgres integrierte Perm Modul, welches für die Provenance Berechnung zuständig ist. Über eine JDBC Schnittstelle wird ein Zugang zu MidPerm ermöglicht und so können alle Datenbanken, die über eine JDBC Schnittstelle verfügen, die Provenance Berechnung benutzen. Abstract I have bought a bottle of milk and now I want to know from which farm and cows the milk came from. Such questions can be answered by provenance applications. There are only a few general solutions which every database can use. MidPerm is a first try to generate such a solution. MidPerm separates the Perm module which is integrated into the Postgres database. Perm calculates the provenance data for a SQL query. A JDBC interface allows a communication with MidPerm. Through this interface MidPerm allows all databases with a JDBC interface to use the provenance calculation. Inhaltsverzeichnis 1 Einleitung 1.1 Motivation . . . . 1.2 Ziele der Arbeit . 1.3 Aufbau der Arbeit 1.4 Danksagung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 8 9 9 10 2 Related Work 2.1 Bedeutung von Provenance . . . 2.1.1 Konzepte . . . . . . . . 2.2 PostgreSQL . . . . . . . . . . . 2.2.1 Client . . . . . . . . . . 2.2.2 Server . . . . . . . . . . 2.2.3 Speicherverwaltung . . . 2.3 Perm . . . . . . . . . . . . . . . 2.3.1 Perm Umschreiberegeln 2.4 Java Native Interface . . . . . . 2.4.1 Diskussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 11 13 14 14 16 16 17 21 23 3 Genereller Ansatz 3.1 Ziele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Architektur des generellen Ansatzes . . . . . . . . . . . . . . . . . . . . . . 3.2.1 Diskussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 24 26 28 4 Implementierung 4.1 Einleitung . . . . . . . . . . . . . . . . . . . . . . . . 4.2 Implementierung des eigenen JDBC-Treiber . . . . . . 4.2.1 Diskussion . . . . . . . . . . . . . . . . . . . 4.3 JNI Schnittstelle . . . . . . . . . . . . . . . . . . . . . 4.3.1 JNINativeInterface . . . . . . . . . . . . . . . 4.3.2 Katalog LookUp Interface . . . . . . . . . . . 4.3.3 Log-System . . . . . . . . . . . . . . . . . . . 4.3.4 Diskussion . . . . . . . . . . . . . . . . . . . 4.4 Perm-Modul . . . . . . . . . . . . . . . . . . . . . . . 4.4.1 Parser . . . . . . . . . . . . . . . . . . . . . . 4.4.2 Analyse . . . . . . . . . . . . . . . . . . . . . 4.4.3 Umschreibung und Provenance Umschreibung 30 30 30 32 32 32 33 34 34 35 35 36 37 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.5 4.6 4.4.4 Query Transformer 4.4.5 Diskussion . . . . Cache und ID- Verwaltung 4.5.1 Diskussion . . . . Angebundene Datenbanken 4.6.1 Postgres . . . . . . 4.6.2 HSQLDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 40 40 40 41 41 43 5 Testen und Debugging 46 6 Zusammenfassung 48 7 Ausblick 50 6 Abbildungsverzeichnis 2.1 2.2 2.3 2.4 2.5 2.6 2.7 3.1 4.1 4.2 4.3 4.4 Grober Architekturentwurf eines Client- Serverprozess von PostgreSQL aus [8] Anfrage- Prozess Architektur von PostgreSQL aus [8] . . . . . . . . . . . . . Perm Architektur aus [10] . . . . . . . . . . . . . . . . . . . . . . . . . . . Umschreiberegeln von Perm aus [9] . . . . . . . . . . . . . . . . . . . . . . Mit Hilfe der beschriebenen Umschreiberegeln wird eine Anfrage in eine Provenance Anfrage umgewandelt . . . . . . . . . . . . . . . . . . . . . . . . . Rolle von JNI in einer Java Umgebung. [14] . . . . . . . . . . . . . . . . . . Beschreibung des JNIEnv Pointer aus [14] . . . . . . . . . . . . . . . . . . . Generelle Architektur der MidPerm Applikation mit Flussbeschreibung einer Provenance Anfrage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Perm Treiber Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . Interne Struktur einer Anfrage, die vom Parser generiert wird. . . . . . . . . Interne Struktur einer Anfrage, die nach der Analyse generiert wird. . . . . . Interne Struktur einer Anfrage, nachdem sie durch die Umschreiberegeln von Perm umgeschrieben wurde. . . . . . . . . . . . . . . . . . . . . . . . . . . 7 14 15 17 18 20 21 22 27 31 36 38 39 1 Einleitung Die Einleitung soll eine kurze Einführung in Perm (Provenance Extension of the Relational Model) geben und die aktuellen Probleme der Integration in das Datenbanksystem Postgres zeigen, falls man Perm mit einem anderen Datenbanksystem als Postgres verwenden möchte. Anschliessend wird die Aufgabenstellung und der Aufbau dieser Arbeit vorgestellt. 1.1 Motivation Viele Anwendungen brauchen für Analysen, Berechnungen und Qualitätsdefinition Quelldaten und Informationen wie Daten in eine Datenbank gelangt sind. Diese Informationen stellt die Provenance dar. Provenance wird in verschiedenen Gebieten genutzt. Die Wissenschaft, die Geschäftswelt und das Data Warehousing sind nur einige wenige Anwendungen, die die Provenance Berechnung verwenden. Viele dieser Gebiete verwenden Provenance aber auf eine andere Art und Weise. Daten aus der Anwendung und Daten für die Provenance Berechnung werden bei einigen Systemen auf unterschiedliche Art gespeichert, um eine Provenance Berechnung zu ermöglichen. Es gibt einige wenige Ansätze, wie Trio oder DBNotes, die das Speichern der Provenance Daten auf dem gleichen System erlauben, wie die Anwendungsdaten. Die Provenance wird auf verschiedene Arten definiert. Buneman at al [3] definiert die Provenance im Kontext eines Datenbanksystems als die Beschreibung der Herkunft der Daten und den Prozess, wie die Daten in die Datenbank gelangt sind. Lanter [17] beschreibt bei geographischen Informationssystemen die Abstammung von abgeleiteten Produkten als Provenance. Dabei spielen das Material und die Transformationen, die zu diesen abgeleiteten Daten führen eine wichtige Rolle für die Provenance Berechnung. Es gibt viele Ansätze, wie die Provenance Daten berechnet werden können. Einige der im Kapitel 2.1.1 vorgestellten Ansätze haben die Eigenschaft, dass sie nicht auf das darunterliegende Datenbank Management System (DBMS) aufbauen. Dies bedeutet, dass die Provenance Daten auf eine andere Art gespeichert werden als die benutzten Daten der Anwendung. Perm verwendet ein anderes Konzept für das Provenance Management System. Perm wurde als neues Modul in Postgres integriert. Postgres ist ein relationales Datenbanksystem. Um die Provenance Daten zu berechnen, benutzt Perm Umschreiberegeln, wie sie im Kapitel 2.3.1 beschrieben werden. Diese wandeln eine SQL Anfrage so um, dass die umgeschriebene Anfrage beim Ausführen, die Provenance Daten berechnet. Durch die direkte Integration in das Datenbanksystem werden die Daten der Anwendung und die Provenance Daten im gleichen Datenbanksystem gespeichert. Falls nun das Provenance System Perm aber für eine andere Datenbank genutzt werden soll, entstehen einige Probleme: 8 • Man muss eine Verbindungsschnittstelle implementieren, die die Anfragen an das Perm Modul senden und diese dann wieder an die angebundene Datenbank zurücksendet. • Bevor die SQL Anfrage an ein anderes Datenbanksystem als Postgres zurückgesendet werden kann, muss eine Transformation erfolgen, um den korrekten SQL Dialekt für die Anfrage zu verwenden. • Da für das Parsen und Analysieren einer Anfrage Katalogzugriffe erfolgen, ist eine Integration einer anderen Datenbank sehr schwierig, da zuerst die verschiedenen Katalogzugriffe in Postgres definiert und entkoppelt werden müssen. Es existiert also eine elegante Lösung für das Erstellen und Behandeln von Provenance Daten, aber da Perm fest in Postgres integriert wurde, können andere Datenbankensysteme nur schwer dieses Konzept benutzen. 1.2 Ziele der Arbeit Das Ziel dieser Arbeit ist es, den Perm Ansatz zur Berechnung von Provenance Daten aus Postgres zu entkoppeln und als datenbankunabhängige Middleware zu implementieren. Die so entstandene MidPerm Anwendung soll durch einen JDBC Treiber von einem DBMS angesprochen werden können. Das Perm Modul, welches im MidPerm integriert ist, behandelt die SQL Anfrage des Benutzers und sendet eine umgeschrieben Anfrage wieder zurück an das angebundene Datenbanksystem. Datenbanksysteme unterscheiden sich in den unterstützten SQL Dialekten, deshalb muss MidPerm umgeschriebene Anfragen in verschiedene Datenbankdialekte serialisieren können. Die Unterstützung von verschiedenen SQL Dialekten ist aber nur ein Beispiel der vielen Herausforderungen, die bei der Entwicklung von MidPerm entstehen. Die Hauptaufgabe bei der Entkopplung des Perm Moduls besteht darin die Katalogdatenzugriffe von Postgres zu definieren und diese dann auszulagern. Beim Parsen und beim Analysieren einer Anfrage müssen viele Katalogabfragen verwendet werden, um sicherzustellen, dass der Benutzer eine korrekte SQL Anfrage erzeugt hat. Diese Systemtabellendaten müssen im MidPerm System von dem angebundenen Datenbanksystem erhalten werden. Um die Informationen von Perm an die JDBC Schnittstelle zu senden, die dann die Anfrage an das Datenbanksystem sendet, und dann wieder an das Perm Modul zurückzuschicken, müssen native Methoden definiert werden. Diese ermöglichen eine Kommunikation zwischen Java und dem Perm Modul. 1.3 Aufbau der Arbeit Die Arbeit ist folgenderweise aufgebaut: Neben der Einleitung werden im Kapitel zwei die verschiedenen Provenance Ansätze vorgestellt und den Begriff Provenance näher erläutert. Nebst den verschiedenen Ansätzen von Provenance werden aber auch Perm und die Integration in Postgres beschrieben. Um eine Verständnis der benutzten Technologien zu erhalten, ist 9 eine kleine Einführung in Postgres und dem Java Native Interface (JNI) vorhanden. Im dritten Kapitel wird die generelle Architektur von MidPerm beschrieben und mittels einem kleinem Beispiel der Datenfluss einer Anfrage durch das MidPerm gezeigt. Der Hauptteil dieser Arbeit besteht in der Beschreibung der Implementierung von MidPerm. Im Kapitel fünf wird das Testen und Debugging der Anwendung kurz beschrieben. Der letzte Teil dieser Arbeit fasst die wichtigsten Ergebnisse zusammen und gibt einen Ausblick für die weitere Entwicklung von MidPerm. Kursiv geschriebene Wörter bedeuten Klassennamen oder Methodennamen in Java, Funktionsnamen oder Strukturen in C. 1.4 Danksagung Einen besonderen Dank geht an der Database Technology Research Group der Universität Zürich, insbesondere an meinem Assistenten Boris Glavic, der mich über die ganze Arbeit hinweg immer unterstützend beistand und mich immer wieder auf den richtigen Weg brachte. 10 2 Related Work In diesem Kapitel werden Perm, PostgreSQL und JNI näher erläutert, da diese die Hauptbestandteile des MidPerms darstellen. PostgreSQL wird durch Perm mit einem zusätzlichen Subsystem ergänzt. Dieses erweitert den Prozess von einer SQL Anfrage zu einem Resultat durch einen weiteren Schritt, der die Provenance Daten findet. MidPerm, wie auch PostgreSQL, benutzt für die Erzeugung der Provenance Daten das Subsystem Perm. Damit MidPerm mit dem Perm Subsystem kommunizieren kann, wird die JNI Technologie genutzt. Dies ermöglicht Daten an das Perm Modul zu schicken, aber auch Methoden in Java aus dem Perm Modul hinaus aufzurufen. 2.1 Bedeutung von Provenance Provenance Daten sind Information über die Entstehung eines Datenobjekts. Dabei spielen Quelldaten, Transformationen, wie ein Datenobjekt zu der aktuellen Repräsentation kam, und Metadaten eine sehr wichtige Rolle. Die Provenance Daten sind alle Informationen über den Entstehungsprozess und die Herkunft eines Datenobjekts von der Erschaffung bis zur jetzigen Repräsentation. [1] Ein Beispiel aus [1] ist, dass Wissenschaftler aus der Biologie, Chemie oder Physik Daten aus einer speziellen Datenbank benutzen. Die meisten Daten darin sind aus Transformationen und Ableitungen entstanden. Die Wissenschaftler sind aber auch an den Quelldaten und an den Transformationen dieser Daten interessiert, um die Datenqualität zu beurteilen oder um ein spezifisches Experiment zu wiederholen. Es gibt noch viele weitere Beispiele, die die Bedeutung von Provenance verdeutlichen. Das grösste Problem bei der Provenance Definition ist, dass es viele Unterschiede zwischen den einzelnen Applikationen und der Datenrepräsentation gibt. Es ist deshalb schwierig eine generelle Lösung zu implementieren. [1] Im nächsten Abschnitt werden verschiedene Ansätze beschrieben, wie die Provenance definiert werden kann. Es sind nur kurze Erklärungen zu den jeweiligen Konzepten beschrieben. Für eine ausführlichere Definition und Beschreibung sind die angefügten Literaturhinweise [1], [3], [4], [5] und [6] zu berücksichtigen. 2.1.1 Konzepte Es gibt zwei verschiedene grundlegende Arten von Provenance. Die Erste beschreibt den Prozess der Erzeugung eines Datenobjekts als Provenance. Die zweite Ansicht sieht die Quelldaten, aus denen das Datenobjekt abgeleitet wurde, als Provenance. [1] 11 Buneman et al. [3] unterscheiden zwischen Why- und Where-Provenance. Bei der WhyProvenance geht es darum alle Quelldaten zu erfassen, die bei der Erzeugung eines Datenobjektes Informationen zum Resultat beitragen. Bei der Where-Provenance gehören nur Eingabedaten eines Ausgabedatums, die kopiert wurden, zum Resultat. Folgendes veränderte Beispiel aus [3] soll die Why- und Where- Provenance näher beschreiben: Beispiel 1 Betrachtet man folgende SQL Anfrage: SELECT Name, Legi_Nummer FROM Student WHERE Note > SELECT min(Note) FROM Student Erhält man als Beispiel folgendes Resultat (’Peter’, 11-222-333), ist die Why-Provenance: Jedes Tupel, welches zum Resultat beiträgt und welches ein Tupel in der Studenten Tabelle beeinflusst, kann das Resultat verändern. Die Where-Provenance für dies Anfrage wäre, woher kommt die Legi-Nummer im Resultat (’Peter’, 11-222-333). Diese kommt aus dem Legi_N ummer Feld der Tupeleingabe ’Peter’. Voraussetzung dafür ist, dass der Name ein Schlüssel der Studententabelle ist, falls nicht muss man die Tupel der Quelldaten auf eine andere Weise identifizieren. Einige Anwendungen berechnen die Provenance zu dem Zeitpunkt, wenn neue Daten aus einer Transformation entstehen, der ’eager’- Ansatz. Der ’lazy’- Ansatz erzeugt Provenance Daten erst dann, wenn diese vom Benutzer explizit verlangt werden. [1] Einige andere Ansätze beschreiben Provenance als Service- und Arbeitsflussmanagement. Ein Beispiel für diesen Ansatz bietet PReServ. Dieser benutzt dabei ein zentrales Provenance Verwaltungssystem. In [4] wird das Verwaltungssystem als Web Service implementiert. Die Web Services, die für die Datentransformation zuständig sind, senden Nachrichten, die sogenannten p-assertions, an das zentrale Provenance System. Dieses speichert alle Provenance Daten in einem separaten Speichersystem. PReServ benutzt dabei eine allgemeine Schnittstelle für das Speichern, damit unterschiedliche Datenbankensysteme benutzt werden können. [1] Chimera [5], [6] bietet einen Virtuellen Datenkatalog (VDC) für die Provenance Informationen an. Das VDC ist als relationales Datenbanksystem implementiert und bietet eine virtuelle Datensprache (VDL) für das Chimera System. Transformationen, Daten und Ableitungen werden vom Benutzer in das Chimera VDC abgelegt. [1] Im GIS Forschungsbereich werden die Provenance Informationen für die Evaluation der Qualität der Daten benutzt. Für diesen Zweck wurden Metadaten Standards entwickelt, die Provenance Informationen beinhalten. Diese Standards sind aber meist auf die GIS-Domäne zugeschnitten und können nur sehr schwer generalisiert werden. Weiter ist der Prozess der Metadatenerzeugung nicht automatisiert und so muss der Benutzer diese Daten manuell eintragen. [1] Ein System, das wie Perm das relationale Model erweitert, ist Trio. Trio erweitert dieses aber nicht nur mit Provenance Daten, sondern auch noch mit einem Unsicherheitsfaktor. Trio berechnet die Provenance einer Anfrage während der Ausführung und speichert das Resultat in 12 einer so genannten Abstammungsrelation. Um die Provenance zu erhalten, wird iterativ der Beitrag eines Eingabetupels jeder Transformation mittels der Abstammungsrelation berechnet. Trio benutzt den ’eager’- Ansatz. [1] DBNotes erweitert das relationale Model mit Kommentaren. Jeder Attributwert kann mittels einem oder mehreren Textkommentare ergänzt werden. Diese Anmerkungen werden bei einer Transformation eines Datenobjekts propagiert. Die Methode, wie eine solche Weiterleitung erfolgen soll, basiert auf Provenance. Die Provenance eines Datenobjekts wird durch eine Sequenz von Transformationsschritten ermittelt, die die Annotationen untersuchen. Eine Anmerkung, die einem Datenobjekt d im Resultat einer Transformation assoziiert wurde, besteht aus den Anmerkungen jedes Quelldatenobjekts aus dem d kopiert wurde. Eine Erweiterung von SQL ist die pSQL Sprache, diese erlaubt zu definieren, wie eine Anmerkung durch eine SQL Anfrage propagiert werden soll. [15] Es gibt noch einige weitere Ansätze, die in [1] nachgelesen werden können. Bevor nun das Konzept, das in Perm verwendet wird, beschrieben wird, muss zuerst die Open Source Datenbank PostgreSQL beschrieben werden, um so die Integration besser verstehen zu können. 2.2 PostgreSQL PostgreSQL ist ein relationales Datenbanksystem, welches 1977 noch unter dem Name Ingres bekannt war. In den späten 90er Jahren wurde Postgres auf die Anfragesprache SQL umgestellt und der Name auf PostgreSQL geändert. PostgreSQL wurde in C implementiert. [8] Das klassische Client-Server Model wird zwischen dem PostgreSQL Clienten und dem Server benutzt. Der Datenzugriff ist strikt in Schichten unterteilt. Auf der Serverschicht wird die Anfrage von einem Teilmodul zu einem Anderen gesendet, um ein Resultat zu erhalten. Jedem Clienten wird ein separater Serverprozess zugewiesen. Die meisten Interaktionen sind vom Typ der Art Anfrage/ Antwort. Ein grober Architekturentwurf einer solchen Verbindung wird in der Figur 2.1 aus [8] beschrieben. Das Frontend ist als eine typische Client-Server Architektur und das Backend ist als geschichteter Architektur implementiert. [8] Eine wichtige Eigenschaft von PostgreSQL ist, dass es Datenbankkataloge in viel grösserem Ausmasse als andere Datenbanksysteme benutzt. Kataloge werden nicht nur verwendet um Tabellen zu beschreiben, sondern man definiert auch Operatoren, Funktionen, Datentypen und viele weitere Datenbankobjekte. [8] In den nachfolgenden Abschnitten werden die einzelnen Subsysteme von PostgreSQL näher beschrieben. 13 Abbildung 2.1: Grober Architekturentwurf eines Client- Serverprozess von PostgreSQL aus [8] 2.2.1 Client Dieses Subsystem besteht aus der Clientenapplikation und der Libpq Bibliothek. Die Clientenapplikation entspricht dabei der Benutzerschnittstelle, diese ermöglicht dem Benutzer verschiedenste SQL Anfragen einzugeben und ausführen zu lassen, um ein Resultat zu erhalten oder Änderungen an der Datenbank vorzunehmen. [8] Für die Kommunikation mit dem Serverprozess ist die Libpq Bibliothek zuständig. Diese Bibliothek hat folgende Aufgaben: • Die Bibliothek stellt eine Verbindung mit dem Postmaster auf. • Sie erhält einen Postgres Serverprozess für die aktuelle Session und sendet die Benutzeranfragen an das Backend weiter. Das Backend entspricht dabei dem neu erstellten Serverprozess. 2.2.2 Server Die Serverseite besteht aus dem Postmaster und dem Postgres Subsystem. Der Postmaster behandelt eingehende Verbindungsanfragen des Clienten. Er ist für Authentifizierungen, für die Erstellung der Client - Postgres Server Kommunikation und für die Zugriffskontrolle zuständig. Der Postgres Server hingegen behandelt alle Anfragen und Kommandos des Clienten. Da PostgreSQL für jeden Clienten einen eigenen Serverprozess erstellt und da dieser Prozess sich nicht um irgendwelche Netzwerkverbindungen kümmert, müssen der Postmaster und der Server Prozess auf der gleichen Maschine sein. Dieses Konzept skaliert nicht auf die Anzahl gleichzeitig aktiver Clientenverbindungen. Deshalb wird PostgreSQL nur für relativ kleine Datenbankapplikationen benutzt. [8] Der Client sendet eine Anfrage für einen Zugriff auf die Datenbank an den Postmaster. Dieser läuft die ganze Zeit und wartet auf Zugriffsanfragen eines Clienten. Findet eine solche Anfrage statt, erzeugt der Postmaster den Serverprozess, den sogenannten Postgres Prozess. Dieser 14 läuft nur solange der Client verbunden ist. Die Anfrage des Benutzers wird von der Clientseite unbearbeitet an den Server gesendet. Erst der Server parst, analysiert diese und erzeugt einen Ausführungsplan und sendet dann das Resultat an den Client zurück. [8] In der aus [8] beschriebene Architektur, Figur 2.2, wird der Kontrol- und der Datenfluss zwischen einem Clienten und dem Server dargestellt. Dabei haben die einzelnen Subsysteme folgende Eigenschaften: Abbildung 2.2: Anfrage- Prozess Architektur von PostgreSQL aus [8] • Parser: Der Parser kontrolliert die gesendete Anfrage auf syntaktische Korrektheit und wandelt sie in eine interne Darstellung um. Wie eine solche Darstellung aussieht, wird im Kapitel 4.4 näher erläutert. [8] • Traffic Cop: Traffic Cop überprüft den Typ einer Anfrage. Falls es sich um eine komplexe Anfrage vom Typ: SELECT, INSERT, UPDATE oder DELETE handelt, wird sie zum nächsten Subsystem gesendet. Falls es sich um eine andere Anfrage handelt wird sie zum Subsystem Utility Command gesendet. [8] • Utility Command: Dieses Subsystem behandelt Anfragen der Art: COPY, ALTER, CREATE TABLE, CREATE TYPE und viele weitere Anfragen, die keine komplexe Bearbeitung benötigen. [8] 15 • Query Rewriter: Der Rewriter schreibt eine Anfrage um. Für die Umschreibung werden Regeln angewendet. Wie eine solche Umschreibung aussieht, wird im Kapitel 4.4 beschrieben. [8] • Planner: Um den besten Plan für eine Anfrage zu wählen, werden die Kosten für Joins einer Relation berechnet. Weiter werden die Kosten für verschiedene Scans berechnet. Der billigste Plan wird dann für die weitere Ausführung ausgewählt. [8] • Executor: Der Executer führt den optimalen Plan aus und sendet die erhaltenen Tupel an den Clienten zurück. [8] 2.2.3 Speicherverwaltung Die Speicherverwaltung ist für die generelle Speicherverwaltung und Ressourcenkontrolle im Backend verantwortlich. Weiter sind verteilte Pufferstrategien, Dateiverwaltungen, Konsistenzkontrollen und Sperrverwaltungen vorhanden. 2.3 Perm Perm ist ein relationales Provenance Management System das Postgres um Sprachkonstrukte für die Berechnung der Provenance von SQL Anfragen erweitert. Zu den Sprachkonstrukten gehören zum Beispiel: PROVENANCE, BASISRELATION oder ON CONTRIBUTION. Diese können in einer SQL Anfrage verwendet werden, um verschiedene Provenance Daten zu erhalten. Wie schon erwähnt wurde, gehören Transformationen, Quell- und intermediale Daten zu den Provenance Informationen eines Datenobjekts. Perm wurde im Kontext der relationalen Datenbank entwickelt. In dieser entsprechen die Provenance Informationen Relationen, Tupeln und Attributen den Quell- und Zwischendaten und die SQL Anfrage und Funktionen entsprechen den Transformationen. Das Perm System wurde als eine Erweiterung des darunterliegenden Datenmodels entwickelt. Es hat also nicht den Nachteil, dass die Provenance Daten auf eine andere Art gespeichert werden als die aktuellen Daten. Wie in der Architekturbeschreibung in 4.1 aus [10] gezeigt wird, wird Perm als zusätzliches Subsystem in den Prozess von einer Anfrage zu einem Resultat integriert. Um in diesen Prozess integriert zu werden, muss das Subsystem die Fähigkeit haben, die interne Anfragestruktur von PostgreSQL benutzen und verarbeiten zu können. Perm benutzt diese und erweitert sie um einen weiteren Knoten, der die Provenance Informationen darstellt. Diese werden bei der Umschreibung angewendet. Diese interne Darstellung beschreibt eine SQL Anfrage mit allen benötigten Daten, die beim Parsen, Analysieren und Umschreiben benötigt werden. Perm erzeugt für eine Anfrage q eine einzelne Anfrage q + , welche das gleiche Resultat wie q erzeugt. Das Resultat wird aber durch zusätzliche Attribute erweitert, die die Provenance Informationen darstellen. Dies ermöglicht die vollständige Ausdrucksstärke von SQL zu benutzen. [9] 16 Abbildung 2.3: Perm Architektur aus [10] Um dies zu ermöglichen, wurden Umschreiberegeln definiert, die im nächsten Abschnitt näher erläutert werden. 2.3.1 Perm Umschreiberegeln Perm benutzt die im Abschnitt 2.1.1 beschriebene Why- Provenance. Wird zum Beispiel eine Anfrage für eine Summe eines Attributes für alle Tupel einer Eingaberelation erzeugt, bedeutet das für die Why- Provenance, das alle Eingabetupel als Provenance-Informationen benutzt werden müssen, weil jedes einzelne Tupel etwas zum Resultat der Summe beiträgt. [9] Die relationale Algebra im Perm baut auf die Multimengen-Semantik auf. Wie diese aussieht, kann im Abschnitt 3 von [9] nachgelesen werden. Wir konzentrieren uns hier auf die Umschreiberegeln, wie sie in der Figur 2.4 aufgelistet sind. Diese Regeln werden nun einzeln kurz beschrieben. Für jede Regel ist P die Liste der Provenance Attribute, die am ursprünglichen Resultat angehängt wird. [9] 17 Abbildung 2.4: Umschreiberegeln von Perm aus [9] • R1 : Für eine Relation R werden die Attribute dupliziert und mittels eines Provenance Namensschemas umbenannt. Gehört zum Beispiel ein Attribut N ame der Relation Person zu den Provenance Daten, dann wird das Attribut im Resultat zu prov_N ame verändert. • R2 : Eine Projektion wird so umgeschrieben, dass sie eine Liste von Provenance Attributen aus T + erhält. • R3 : Bei einem Selektionsoperator wird die unveränderte Selektion auf die umgeschriebene Eingabe angewendet. Eine Selektion filtert nur Tupel aus. Sie ändert keine Tupel und fügt auch keine hinzu. • R4 : Das Kreuzprodukt verknüpft Eingabetupel von T1 und T2 . Provenance Informationen werden nur an T1 hinzugefügt, da T2 nicht das ursprüngliche Resultat ändert. • R5 : Diese Regel schreibt Aggregat-Operatoren um. Es können aber keine weiteren Tupel oder Attribute hinzugefügt werden ohne dass der Wert der Aggregat-Funktion verändert wird. Die Aggregat-Funktion wird also auf die Eingabe T angewendet und das Resultat wird mittels eines Gruppen-Attributs mit der umgeschriebenen Version von T verbunden. 18 • R6 und R7 : R6 und R7 sind Umschreiberegeln für Operatoren der Vereinigungs- und der Schnittmenge. Da ein Tupel t aus T1 oder T2 zu einem Resultattupel t’ beiträgt, falls die Attributwerte gleich sind, werden Joins angewendet für die Regeln. Bei der Vereinigung wird ein Links Outer Join benutzt, da einige Tupel nur in T1 oder in T2 vorkommen können. • R8 und R9 : Für die Mengendifferenz besteht die Provenance eines Tupels t aus allen Tupeln aus T2 , die unterschiedlich sind wie t. Für R8 und R9 wird der Links Join Operator auf T1 6= T2 ausgeführt, um diese Tupel an das Resultat anzuhängen. Um eine Umschreibung mit diesen Regeln besser zu erläutern, werden sie an einem kleinem Beispiel angewendet, wie in [9] beschrieben wird. Für dieses Beispiel werden folgende drei Tabellen benutzt: shop name numEmpl Merdies 3 Joba 14 sales sName itemId Merdies 1 Merdies 2 Merdies 2 Joba 3 Joba 3 item id 1 2 3 price 100 10 25 Auf diesen Tabellen wird nun folgende Anfrage durchgeführt, wie sie auch in [9] verwendet wird: qex = ↵name,sum(price) ( name=sN ame^itemId=id (prod)) prod = shop ⇥ sales ⇥ items Dieser algebraische Ausdruck entspricht folgender SQL Anfrage: SELECT name, sum(price) FROM shop, sales, item WHERE name =sName AND itemId = id Wird diese Anfrage ohne Provenance ausgeführt, erhält man folgendes Resultat: name sum(price) Merdies 120 Joba 50 Werden nun die vorher erwähnten Umschreiberegeln angewendet, wird diese Anfrage folgendermassen umgewandelt: 19 Abbildung 2.5: Mit Hilfe der beschriebenen Umschreiberegeln wird eine Anfrage in eine Provenance Anfrage umgewandelt Die einzelnen Schritte haben folgende Bedeutung: • Step 1: Der erste Operator der Anfrage qex ist die Aggregationsfunktion. Diese wird mittels der + Regel R5 umgeschrieben. Diese Regel sagt aus, dass die P-Liste für qex gleich P(T + ) + sein muss. T wurde aber noch nicht berechnet, deshalb wird es als unbekannt stehen gelassen. Die Unteranfrage T ist eine Selektion, deshalb wird sie nicht umgeschrieben. • Step 2: Im zweiten Schritt wird das Kreuzprodukt shop ⇥ sales ⇥ items durch die Regel R4 umgeschrieben. Die P-Liste eines umgeschriebenen Kreuzproduktes ist die Verknüpfung aller P-Listen der Unteranfragen, die im Kreuzprodukt verwenden werden (sales, items und shop). • Step 3,4 und 5: In diesen drei Schritten wird immer die Regel R1 angewendet, um die umgeschriebenen Relationen shop+ , sales+ und items+ abzuleiten. Die umgeschrieben Anfrage liefert nun folgendes Resultat. Die Provenance Daten fangen mit einem kleinem p an: name sum(price) pName pNumEmpl pSName pItemId pId pPrice Merdies 120 Merdies 3 Merdies 1 1 100 Merdies 120 Merdies 3 Merdies 2 2 10 Merdies 120 Merdies 3 Merdies 2 2 10 Joba 50 Joba 14 Joba 3 3 25 Joba 50 Joba 14 Joba 3 3 25 20 2.4 Java Native Interface Das Java Native Interface, kurz JNI, wird für die Kommunikation zwischen Java und dem nativem Code benutzt. Der native Code kann zum Beispiel in C oder in C++ geschrieben sein. JNI erlaubt die Vorteile einer Java Applikation zu erhalten und gleichzeitig Code einer anderen Sprache zu benutzen. JNI wird als Zwei- Wege Schnittstelle angesehen. Dies erlaubt nativen Code aus Java aus aufzurufen und umgekehrt. Die Abbildung 2.6 aus [14] zeigt die Rolle von JNI in einer Java Umgebung. Die Host Umgebung entspricht dem Betriebssystem, einer Anzahl von nativen Bibliotheken und der CPU. Die Applikationen und Bibliotheken sind in einer nativen Sprache wie C oder C++ geschrieben und in einem hostspezifischen binären Code kompiliert. Die Java Virtual Machine (JVM) führt Java Klassen aus der Java Applikation und aus der Java Bibliothek aus. JNI unterstützt folgende zwei Arten von nativem Abbildung 2.6: Rolle von JNI in einer Java Umgebung. [14] Code: Native Bibliotheken und Native Anwendungen • Native Methoden: Java Anwendungen können Funktionen, die in einer nativen Bibliothek implementiert sind, in Java aufrufen, wie wenn es eigene Methoden wären. [14] • Schnittstellenaufruf: Diese Schnittstelle erlaubt eine JVM Implementierung in einer nativen Bibliothek einzubinden. Zum Beispiel kann ein Webbrowser, welches in C geschrieben wurde, Applets in einer eingebundenen JVM Implementierung aufrufen. [14] Wird zum Beispiel auf der Java Seite folgende Methode in der Klasse Test definiert: Class Test { //Native Methode die einen String an die native Bibliothek //sendet und den gleichen String wieder zurückgibt private native String printLine(String message); public static void main(String args[]){ Test t = new Test(); String message = t.printLine("Hallo"); 21 System.out.println(message); } } Die Methode printLine sendet eine Text Nachricht an die native Applikation und erhält von dieser die gleiche Nachricht wieder zurück. Um nun die richtige C Funktion zu erhalten, kann mittels des javah Tools, das richtige Header File für die native Methode im Beispiel generiert werden. Das javah Tool benutzt bei der Generierung der Header Datei, das Package der Java Klasse, in der die native Methode implementiert wurde. Liegt die Test Klasse im Package test1, dann wird eine Headerdatei mit dem Namen ”test1_Test.h” generiert und diese beinhaltet folgende Funktion: JNIEXPORT jstring JNICALL Java_test1_Test_getLine(JNIEnv *env, jobject this, jstring message); Liegt die Klasse aber im Packet test2, dann wird die ”test2_Test.h” Headerdatei generiert mit folgender Funktion: JNIEXPORT jstring JNICALL Java_test2_Test_getLine(JNIEnv *env, jobject this, jstring message); JNIEXPORT und JNICALL sind Makros, die in der ”jni.h” Headerdatei definiert sind. Diese stellen sicher, dass die Funktion von der nativen Bibliothek aus exportiert wird. Bei der Generierung der Methoden werden zwei weitere Parameter in der C Funktion hinzugefügt. Der erste Parameter ist der JNIEnv Pointer. Dieser zeigt auf den Speicherpunkt, der den Pointer zur Funktionstabelle beinhaltet. Jeder Eintrag in der Funktionstabelle zeigt auf eine JNI Funktion. Native Methoden greifen auf Datenelemente in der JVM durch einer dieser JNI Funktionen zu. Figur 2.7 aus [14] beschreibt den Zusammenhang zwischen den einzelnen Pointers. Das zwei- Abbildung 2.7: Beschreibung des JNIEnv Pointer aus [14] te Argument jobect ist eine Referenz auf das Objekt in welchem die Java Methode definiert ist, ähnlich dem this Pointer in C++ oder dem this in Java. [14] 22 2.4.1 Diskussion Java ist Typensicher, währenddessen native Sprachen wie C oder C++ nicht typensicher sind. Dies bedeutet, dass man zusätzliche Überprüfungen durchführen sollte, bevor eine native Funktion oder Methoden in Java von der nativen Bibliothek aus aufgerufen werden. [14] Falls ein Problem bei einer nativen Methode entsteht, kann dies die ganze Applikation zum Absturz bringen und so müssen viele Sicherheitschecks durchgeführt werden. [14] Da eine native Bibliotheken in einem hostspezifischen binären Code kompiliert werden muss. Entsteht das Problem, dass eine C Applikation, die auf einem Betriebssystem kompiliert wurde, meistens nicht auf einem anderen Betriebssystem läuft. [14] 23 3 Genereller Ansatz 3.1 Ziele Perm ist zurzeit, wie im Kapitel 2.3 schon beschrieben wurde, in Postgres integriert. Durch die Integration von Perm in Postgres, führt eine Anbindung einer neuen Datenbank zu vielen Problemen. • Perm verwendet die gleichen Datentypen wie Postgres, dies kann zu Problemen führen, wenn eine andere Datenbank diese nicht unterstützt. • Um eine Datenbank anzubinden, muss man sich zuerst in den Code von Postgres einarbeiten und ihn verstehen. Dies führt zu grossen Verzögerungen bei einer Anbindung und zu grossen Veränderungen in Postgres. • Für jede neue Datenbank muss eine eigene Implementierung von Postgres erzeugt werden. Um diese Probleme zu umgehen, ist nun das Ziel, Perm aus Postgres herauszulösen und als Middleware zu implementieren, als so genanntes MidPerm. Dadurch wird ein datenbankunabhängiges System entwickelt. Es sollen möglichst einfache Schnittstellen angeboten werden, die für jedes Datenbanksystem implementiert werden müssen, um so Perm anwenden zu können. Um dieses Ziel zu erreichen, gibt es zwei Hauptansätze. Der erste Ansatz wäre das Perm Modul neu in einer anderen Sprache zu implementieren und eine Schnittstelle wie zum Beispiel JDBC oder ODBC anzubieten, damit man andere Datenbanken verwenden kann. Dies hat zur folge, dass man die Umschreiberegeln, die im Kapitel 2.3.1 erklärt wurden, vollständig in einer anderen Sprache, z.B. Java, schreiben muss. Der zweite Ansatz entkapselt die vorhandene Anwendung so, dass sie als Middleware genutzt werden kann. Um Perm anzusprechen, wird die JNI Technologie benutzt. In dieser Variante wird der Prozess von der SQL Anfrage bis zur umgeschriebenen Anfrage vom Perm Modul übernommen. Um eine Anfrage auf Korrektheit zu überprüfen, werden Datenbankkataloginformationen benötigt. Zum Beispiel werden alle Attribute einer Relation benötigt, um ⇤ zu expandieren. Um mit dem Datenbanksystem kommunizieren zu können, bedarf es einer Datenbankschnittstelle, wie ODBC oder JDBC. Die Vorteile dieser Variante sind: • Der ganze Umschreibeprozess muss nicht neu implementiert werden. Man kann das Parsen, die Analyse und die Umschreibung vom Perm Modul übernehmen. • Durch den Einsatz einer Datenbankschnittstelle, ist es sehr leicht eine neue Datenbank anzubinden ohne grosse Veränderungen vorzunehmen. 24 • Es müssen nur die Katalogzugriffe im Perm Modul identifiziert und ausgelagert werden. Die negativen Aspekte für diese Variante sind: • Die Datenbankkatalogzugriffe müssen identifiziert und ausgelagert werden. • Da Perm in Postgres integriert wurde, sind selbstverständlich sehr viele Abhängigkeiten untereinander entstanden. Es kann sich also als sehr schwierig erweisen, diese Bezüge zu entkoppeln und eine eigenständige Anwendung zu entwickeln. • Wie löst man das Problem, wenn die angebundene Datenbank nicht alle Datentypen, die Postgres anbietet, unterstützt. Es muss eine Zuordnung zwischen den Postgres Datentypen und den angebundenen Datenbanktypen implementiert werden und dasselbe gilt für Operatoren, für Funktionen und für SQL-Dialekte. Der erste Ansatz - Perm neu zu implementieren - hat folgende Nachteile: • Eine vorhandene funktionierende Implementierung muss aus einer Programmiersprache in eine andere transformiert werden. Dabei gibt es einfachere Ansätze, um Perm in einer anderen Sprache wie z.B. Java zu benutzen. • Das Perm Modul verwendet für die Umschreibung einer Provenance Anfrage die in Postgres interne Darstellung für eine Anfrage. Nun muss entschieden werden, implementiert man die gleiche Baumstruktur in der ausgewählten Programmiersprache oder entwickelt man ein ganz neues Konzept. • Es müssen ein Parser und ein Analyser für SQL Anfragen implementiert werden. Es gibt aber nicht nur negative Aspekte für diese Variante: • Bei einer Neuimplementierung entsteht kein Overhead, wie er beim zweiten Ansatz durch JNI entstehen kann. • Man hat ein einheitliches System, das nur aus einer Programmiersprache besteht. • Postgres ist ein sehr komplexes System. Es kann sich als sehr schwierig erweisen, die benötigten Stellen herauszusuchen an denen man Perm herauskapseln kann. Dieses Problem entsteht bei dieser Variante nicht. Für diese Arbeit wurde der zweite Ansatz als Lösung gewählt. Eine Neuimplementierung würde eine nicht ausgereifte MidPerm Anwendung ergeben. Da die Implementierung eines Parsers und Analysers und die Erarbeitung eines neuen Konzeptes für die Umschreiberegeln viel mehr Aufwand bedeuten würde. Es könnte keine ausgereifte Version in den sechs Monaten implementiert werden. Ein weiterer Punkt, wieso der zweite Ansatz die bessere Lösung ist, ist: Perm wurde schon gut in Postgres getestet und man hat eine stabile und zuverlässige Anwendung. Falls es gelingt dieses Modul aus Postgres zu entkoppeln, hat man die viel ausgereiftere Version als in der ersten Variante. 25 3.2 Architektur des generellen Ansatzes In diesem Abschnitt wird der gewählte Ansatz auf einer generellen Ebene erläutert und mittels einer Grafik illustriert. Das Perm Modul wurde als dynamische Bibliothek umgesetzt. Diese kann auf der Java Seite eingebunden und mittels der JNI Schnittstelle angesprochen und verwendet werden. Um die Methoden dieser Bibliothek anwenden zu können, wird die JNI Technologie benutzt. Mittels JNI kann man nicht nur Methoden aus der nativen Sprache aufrufen, sondern es ist auch möglich Java Funktionen von C aus aufzurufen. Für die Benutzung der MidPerm Applikation wurde ein eigener JDBC Treiber implementiert. Dieser stellt eine Verbindung mit der angebundenen Datenbank her und sendet die Anfragen weiter. Für die Verbindung werden die gewohnten Parameter genutzt, wie für einen originalen JDBC Treiber. Falls eine Provenance Anfrage durch das System geschleust werden soll, wird diese erkannt und mittels der JNI Schnittstelle an das Perm Modul weitergeleitet. Schickt der Benutzer keine Provenance Anfrage an das MidPerm, so wird diese direkt an das Datenbanksystem gesendet, um ein Resultat zu erhalten. Das Perm Modul ist durch folgende Submodule aufgebaut: • Parser: Das Perm Modul erhält die Anfrage und startet als erstes den Parser. Dieser überprüft die Anfrage auf syntaktische Korrektheit. Falls keine Fehler bestehen, wird die Analyse gestartet. In diesem Schritt finden noch keine Katalogzugriffe statt. • Analyse: Nachdem die syntaktische Korrektheit überprüft wurde, findet nun die eigentliche Überprüfung der Anfrage statt. Es wird zum Beispiel, die in einer Anfrage benutzten Relationen und Attribute auf ihre Existenz überprüft. Funktionen werden auch auf ihre Existenz geprüft und viele weitere Überprüfungen finden in diesem Schritt statt. Dabei entstehen etliche Katalogzugriffe. Diese Daten müssen über die JNI Schnittstelle und dem JDBC Treiber geholt und wieder an das Perm Modul zurückgesendet werden. • Umschreibung: Als letzter Schritt findet eine Umschreibung statt. Bevor aber die Umschreiberegeln von Perm angewendet werden können, müssen zuerst alle Sichten expandiert werden und weitere Umschreibungen erfolgen. Nachdem keine Sichten mehr vorhanden sind, können alle Provenance Regeln angewendet werden und man erhält eine umgeschriebene Anfrage, die je nachdem, welche Datenbank angebunden wurde, anders ausschauen muss. Für diesen Zweck wurden verschiedene Query Transformer implementiert, die die interne Datenstruktur in den Dialekt des angebundenen DBMS übersetzt. Die umgeschriebene Anfrage wird mittels der JNI Schnittstelle an den Perm Treiber zurückgesendet und dieser leitet die Anfrage an die JDBC Schnittstelle weiter, um das Resultat der Anfrage zu erhalten. Für alle Katalogzugriffe wurde ein Katalog LookUp Interface definiert, das für jede angebundene Datenbank implementiert werden muss. Diese Schnittstelle beinhaltet alle Rückruf Methoden, die das Perm Modul braucht, um Informationen aus dem Datenbankkatalog zu erhalten. Die Abbildung 3.1 gibt einen Überblick über die verwendete Architektur, sowie eine Darstellung des Flusses von einer Anfrage bis zu der umgeschriebene Anfrage. 26 Abbildung 3.1: Generelle Architektur der MidPerm Applikation mit Flussbeschreibung einer Provenance Anfrage 27 Um eine SQL Anfrage durch das MidPerm System umzuschreiben, werden folgende Schritte ausgeführt: 1. Als erster Schritt sendet der User, nachdem die Verbindung mit dem JDBC Treiber hergestellt wurde, die SQL Anfrage an die JDBC Schnittstelle von MidPerm. 2. Die JDBC Schnittstelle sendet die Anfrage an den PermDriver. 3. Der PermDriver überprüft zuerst, ob es sich um eine Provenance Anfrage handelt. Falls nicht wird die Anfrage direkt an das Datenbanksystem über die JDBC Schnittstelle gesendet, um ein Resultat zurückzubekommen. Falls es sich um eine Provenance Anfrage handelt, wird zuerst ermittelt, welche Datenbank verbunden ist und danach wird die SQL Anfrage an die JNI Schnittstelle gesendet. 4. Die JNI Schnittstelle sendet über eine native Methode die Anfrage an das Perm Modul. Weiter wird noch eine ID für das Datenbanksystem, das mit dem MidPerm verbunden ist, gesendet, um den richtigen SQL Dialekt für die Transformation von der internen Darstellung zur Anfrage zu erhalten. 5. Das Perm Modul startet die Submodule, die in einem der oberen Abschnitten beschrieben wurden. Falls ein Submodul irgendwelche Information aus dem Datenbankkatalog benötigt oder überprüfen muss, ob ein Datenbankobjekt vorhanden ist, wird eine Metadaten Anfrage an die JNI Schnittstelle gesendet. 6. Die JNI Schnittstelle sendet die Anfrage aus dem Perm Modul, die einen Datenbankkatalogzugriff entspricht, an die Katalog LookUp Schnittstelle. Diese benutzt die JDBC Schnittstelle, um die benötigten Informationen aus dem Datenbankkatalog abzufragen. Danach werden die Informationen an die JNI Schnittstelle gesendet und zurück an das Perm Modul. Diese werden benutzt, um die SQL Anfrage zu bearbeiten und umzuschreiben. 7. Nachdem der Query Transformer die interne Darstellung des Perm Moduls in den richtigen SQL Dialekt umgewandelt hat, wird sie an die JNI Schnittstelle gesendet. (Rückgabewert der nativen Methode im Punkt 4) 8. Um das Resultat der umgeschriebenen Anfrage zu erhalten, wird sie über die JDBC Schnittstelle an das Datenbanksystem gesendet, welches das Resultat an den PermDriver zurückgibt. 9. Das Resultat wird über die JDBC Schnittstelle an den Benutzer gesendet. 3.2.1 Diskussion Durch die Definierung des Katalog LookUp Interface wird eine einfache Anbindung einer Datenbank ermöglicht, um die Kataloginformationen an das Perm Modul zu senden. Falls eine neue Methode für den Katalogzugriff hinzugefügt werden muss, kann dies leicht über dieses Interface auf alle benutzten Datenbanken weitergeleitet werden. 28 Durch die Trennung der Kommunikation zwischen dem Perm Modul und JNI und der Kommunikation zwischen der Datenbank und der Katalog LookUp Schnittstelle wird eine grosse Austauschbarkeit der einzelnen Teile ermöglicht. Weiter wird nur an einer Stelle mit der Datenbank kommuniziert, was wiederum eine einfachere Integration eines weiteren DBMS erlaubt. Dadurch dass eine Kommunikation zwischen Java und C über JNI stattfinden muss, kann ein Overhead entstehen. 29 4 Implementierung In diesem Kapitel wird ein tieferer Einblick in die Implementierung des MidPerm Moduls gegeben. Es werden die benutzten Technologien gezeigt und deren Vor- und Nachteile beschrieben. 4.1 Einleitung Wie schon im vorherigen Kapitel erklärt, wird die JNI Technologie verwendet, um Methoden von C aus Java aufzurufen und umgekehrt. Eine Methode, die eine Funktion aus der dynamischen Bibliothek aufruft, wird in Java als native Methode definiert. Um JNI verwenden zu können, muss das Perm Modul in eine native Bibliothek umgewandelt werden. Diese muss dann im MidPerm eingebunden werden, um so auf den Provenance Umschreibeprozess zugreifen zu können. Um eine Datenbank anzusprechen und eine Kommunikation aufzubauen, wird ein JDBC Treiber benutzt, der von den meisten Datenbanken benutzt werden kann. Um MidPerm zu benutzen, wurde ein eigener JDBC Treiber entwickelt, der im nächsten Abschnitt beschrieben wird. 4.2 Implementierung des eigenen JDBC-Treiber Der implementierte Treiber verhält sich wie der Standardtreiber des jeweiligen DBMS. Der einzige Unterschied ist, dass er Provenance Anfragen behandeln kann. Die PermDriver Klasse implementiert die Schnittstelle Driver aus dem Java SQL Paket. Um den eigenen Treiber benutzen zu können, muss zuerst auch die Treiber Klasse mittels des Namens ”driver.PermDriver” geladen werden. Danach gibt man eine normale Datenbank-URL mittels dem Zusatz ”:perm”, das Passwort und den Benutzernamen der Datenbank an. Dadurch erhält man ein PermConnection Objekt zurück, das eine Verbindung zu der Datenbank darstellt, und die Verbindung zur Datenbank wird aufgebaut. Die PermConnection Klasse implementiert die Schnittstelle PermConnectionInterface, die wiederum vom Connection Interface abgeleitet ist. Da eine Provenance Anfrage an das Perm Modul weitergeleitet werden muss, wurde noch eine PermStatement Klasse definiert. Diese implementiert die PermStatementInterface Schnittstelle, die von dem Interface Statement abhängig ist. Die zwei Schnittstellen wurden deshalb definiert, um die Integration des Perm Moduls zu vereinfachen und um eine einfache Austauschbarkeit der einzelnen Implementierungen zu ermöglichen ohne grosse Änderungen an dem Treiber und an den Anwendungen, die diesen Treiber benutzen, vornehmen zu müssen. Bevor eine SQL Anfrage an das Perm Modul gesendet wird, erfolgt zuerst eine kurze Überprüfung des Textes, um zu klären, ob es sich um eine Provenance Anfrage handelt oder nicht. 30 Falls ja, wird die Anfrage an die Perm Bibliothek über JNI gesendet. Diese erhält einen SQL Text und führt verschiedene Überprüfungen und Umschreibungen durch. Zuletzt wird eine umgeschriebene Anfrage zurückgegeben. Diese kann dann mittels JDBC an das DBMS gesendet werden, um ein Resultat zu erhalten. Bevor aber die Anfrage über JNI gesendet werden kann, muss die native Bibliothek wissen, welcher SQL Dialekt für die umgeschriebene Anfrage verwendet werden soll. Dies ist deshalb nötig, damit MidPerm den richtigen Query Transformer anwenden kann, um den richtigen SQL Dialekt auf die Umschreibung der internen Darstellung zu einer Provenance Anfrage verwenden zu können. Diese Information wird aus der Datenbank URL herausgelesen. Zurzeit können zwei Datenbanken benutzt werden, HSQLDB und Postgres. Eine wichtige Eigenschaft des JDBC Treibers ist, dass jede Implementierung auch eine Instanz der abgeleiteten Klasse enthält. Zum Beispiel das PermStatement enthält ein Objekt des Statement Interfaces und die PermConnection Klasse enthält ein Connection Objekt. Dies erlaubt nur diejenigen Methoden zu verändern, die auch wegen dem Perm Modul verändert werden müssen. Die restlichen Methoden werden einfach über den Standardtreiber durchgeschleust. Um den PermDriver richtig verwenden zu können, muss noch eine ”PermDriver.properties” Datei definiert werden. Darin wird die Zuordnung von einer Klassen-URL zu einer Datenbankklasse definiert. Dadurch werden bei der Initialisierung dem Treiber die richtigen Klassen mitgeteilt, die er laden muss. Die Abbildung 4.1 gibt einen Überblick über die Beziehungen zwischen den einzelnen Interfaces und den implementierten Klassen. Abbildung 4.1: Perm Treiber Architektur 31 4.2.1 Diskussion Eine alternative Lösung wäre einen völlig eigenständigen Treiber zu implementieren, der das Java SQL Paket vollständig implementiert. Dies hätte den Vorteil, dass man den Treiber vollständig an das Perm Modul anpassen könnte. Ein weiterer Vorteil wäre, dass man keine Instanzen der abgeleiteten Klasse übergeben muss. Dafür entsteht aber ein grosser Aufwand, da man alle Methoden selbst implementieren muss, obwohl man für die Nutzung des Perm Moduls nur ein paar wenige verändern muss. Eine ganz simple Lösung, die keine Veränderung an dem originalen JDBC Treiber verlangt, ist, dass man alle Anfragen über das Perm Modul umschreiben und erst dann die Anfrage ausführen lässt. Dies ist wohl die ineffizienteste Variante, die am meisten Overhead verursacht. Die benutzte Lösung versucht die Vorteile beider Varianten zu kombinieren. Es werden nicht alle Anfragen einfach an das Perm Modul weitergeleitet, um so den Overhead möglichst klein zu halten. Die Implementierung wird einfach gehalten, dadurch dass man eine lokale Referenz auf die Superklasse hat. 4.3 JNI Schnittstelle Die JNI Schnittstelle definiert die Verbindung zwischen Java und dem nativen Code. Dieser Teil muss für jede Datenbank, die man anbinden möchte, implementiert werden. Die beiden Schnittstellen JNINativeInterface und JDBCCallbackInterface dürfen nicht durch zwei verschiedene Klassen definiert werden. Es muss also nur eine Klasse geben, die beide Schnittstellen implementiert, sonst schlägt die Initialisierung durch das MidPerm fehl. 4.3.1 JNINativeInterface Ein Teil der Schnittstelle zu C ist das Interface JNINativeInterface. Dieses Interface definiert drei einfache Methoden, die in der implementierten Klasse als Native definiert werden müssen und eine Methode, die überprüfen muss, ob ein übergebenes Connection Objekt noch offen ist. Die Erste initialisiert das Perm Modul und sollte nur einmal aufgerufen werden. Bei der Initialisierung wird aber nicht nur die dynamische Bibliothek initialisiert, sondern es werden auch die Rückrufmethoden auf ihre Existenz überprüft. Was eine Rückrufmethode ist, wird im Kapitel 4.3.2 beschrieben. Falls eine solche Methode fehlt, wird ein Fehler ausgegeben mit dem Methodennamen und der benötigten Signatur. Weil die Überprüfung der Methoden in der Klasse erfolgt, die die Initialisierung aufruft, müssen die Rückrufmethoden in der gleichen Klasse sein wie die Methoden des JNINativeInterface. Die zweite Methode sendet eine Anfrage an das Perm Modul und startet die eigentliche Analyse und Umschreibung. Man sendet aber nicht nur den SQL Text, sondern auch eine Identifikationsnummer für die Datenbank. Diese Nummer wird dafür gebraucht, um den richtigen Query Transformator aufzurufen. Wenn zum Beispiel eine Postgres Datenbank angebunden wurde, dann muss man für jede Anfrage die Nummer zwei als zweiten Parameter übergeben. Welche Datenbanken angebunden sind und welche Identifikationsnummern diese haben, können in dem Kommentar von executeQueryJNI nachgelesen werden. Mit der dritten nativen 32 Methode können einige Parameter des MidPerms verändert werden. 4.3.2 Katalog LookUp Interface Die JDBCCallbackInterface Schnittstelle beinhaltet alle Methoden, die vom Perm Modul benötigt werden, um Informationen aus dem Datenbankkatalog zu erhalten. Diese Methoden müssen für jede angebundene Datenbank neu implementiert werden und sind deshalb als Schnittstelle definiert. Dieses Interface wurde definiert, um eine Kapselung zwischen den SQL Katalogzugriffen und den JNI Aufrufen zu bewirken. Um die benötigten Informationen leichter zu übergeben, wurden verschiedene Datenstrukturen aus dem Perm Modul kopiert und als Java Klassen implementiert. Diese Strukturen werden als JNI Objekt zur C Seite übergeben und so kann man mittels nur einem Aufrufs viele Informationen an die native Bibliothek senden. Die Strukturen verwenden viele Komponenten, aber nicht alle davon werden bei der Analyse, sowie bei der Umschreibung benötigt. Deshalb können auch einige Komponenten der jeweiligen Struktur mittels Dummy Elementen initialisiert werden. Im folgenden Abschnitt werden einige wichtige Datenstrukturen vorgestellt. Strukturen in Java Um Relationen und Sichten zu speichern, benutzt die MidPerm Applikation die C-Struktur Form_pg_class. Diese wird in Java durch die PGClass Klasse beschrieben. In Postgres wurde diese Struktur aber nicht nur für die Speicherung von Relationen und Sichten eingesetzt, sondern sie wird auch für die Speicherung von Indizes und Tabellen genutzt. Dies spielt aber keine Rolle bei der MidPerm Anwendung, da, wie oben schon erwähnt, nur Relationen und Sichten genutzt werden. Die FormPGAttributeClass Klasse ist die Java Variante der C-Struktur Form_pg_attribute. Sie stellt alle Informationen für ein Attribut einer Relation oder einer Sicht zur Verfügung. Für eine nähere Definition welche Komponenten in dieser Struktur vorkommen und welche Bedeutung diese haben, vergleiche [2] Abschnitt System Catalogs das pg_attribute Kapitel. Attribute werden bei der Analyse verwendet. Kommt eine Relation in einer Anfrage vor, werden alle Attribute aus dem Datenbankkatalog gesucht. Diese werden im Perm Modul in eine Liste verwandelt und an die Struktur Form_pg_class gehängt. Es wir also nur einmal für eine benötigte Relation nach den Attributen gesucht. Dabei spielt die Reihenfolge des Attributs in der Tabelle eine wichtige Rolle. Der Index in der Liste entspricht sogleich auch der Position des Attributs in der Datenbankrelation. Die FormPGOperator Klasse entspricht der Form_pg_operator Struktur in C. Diese Klasse beschreibt einen Operator und deren Eigenschaften. Für eine nähere Definition welche Parameter in dieser Struktur vorkommen und welche Bedeutung diese haben, vergleiche [2] in dem Abschnitt System Catalogs das pg_operator Kapitel. Da Postgres und so auch die MidPerm Anwendung das Überladen von Operatoren unterstützt, reicht es nicht einfach aus nur den Operatornamen anzugeben. Man muss noch einige weitere Eigenschaften des Operators herausfinden, damit dieser richtig behandelt und verarbeitet wird. Es werden aber nicht alle Parameter benötigt. Wenn nun zum Beispiel verschiedene Datentypen miteinander verglichen werden, dann muss Postgres die verschiedenen Input und Output Datentypen wissen, die der 33 Operator haben wird, um so allfällige Typenumwandlungen durchzuführen oder Fehler zu erzeugen, falls es sich um nicht kompatible Typen handelt. Dies ist aber für die MidPerm Applikation nicht wichtig, da keine Umwandlungen von Typen durchgeführt werden. Falls der Operator nicht kompatible Typen vergleicht, wird bei der Durchführung der umgeschriebenen Anfrage das Datenbanksystem eine Fehlermeldung ausgeben. Die C-Struktur Form_pg_type wird von der FormPGType Klasse simuliert. Diese wird dazu verwendet, um Datentypen zu beschreiben und zu speichern. Auch hier verwendet MidPerm viele Komponenten in der Datentypstruktur, die aber für die Umschreibung keine Rolle spielen. Dadurch dass die Middleware viele Datentypen unterstützen kann, entsteht das Problem der Typenzuordnung zwischen der angebundenen Datenbank und MidPerm. Dies wird in einem späteren Kapitel besprochen. Die RewriteRuleClass Klasse wird nur bei Sichten angewendet und beschreibt die C-Struktur RewriteRule. Um die Provenance Anfrage richtig umzuschreiben, wird die Basisrelation für jede Sicht benötigt. Mit dieser Relation wird die SQL Anfrage richtig aufgelöst und das Perm Modul kann die benötigten Provenance Daten erzeugen. 4.3.3 Log-System Für das Logging wurden im JDBCCallbackInterface drei Methoden für Info-, Debugging- und Fehlermeldungen bereitgestellt. Diese erhalten eine Textrepräsentation der Fehler, Debuggingoder Infomeldung aus dem Perm Modul und können weiter verarbeitet werden. MidPerm benutzt für das Ausgeben von Fehler-, Debug- und Infomeldungen verschiedene Stufen für das Logging. Zum Beispiel gibt es für Fehlermeldungen normale Fehlermeldungen, aber auch Meldungen für fatale Fehler. Für das Debugging gibt es fünf verschiedene Stufen. Die verschiedenen Levels beschreiben, wie viel Debugging Informationen ausgegeben werden sollen. Es wurde aber nicht für jede Stufe eine eigene Rückrufmethode definiert. Es reicht aus, wenn man nur drei Methoden definiert, die für eine Fehler-, Debug- und Infomeldung zuständig sind. Wie das Log-System schlussendlich für die Anbindung implementiert wird, ist dem Anwender selbst überlassen. Welche Lösung für die angebundenen Datenbanken gewählt wurde, wird in einem späteren Kapitel kurz beschrieben. 4.3.4 Diskussion Um native Methoden aufzurufen, müssen bestimmte Source- und Headerdateien in der nativen Bibliothek vorhanden sein, damit die richtige C Methode von Java aus aufgerufen werden können. Diese beiden Dateien sind aber vom Namen der Javaklasse und dem Package abhängig. Im folgenden kleinen Beispiel wird diese Eigenschaft näher erläutert. Beispiel 2 Im PermModul sind die beiden Dateien ”postgres_PostgresInterface.h” und ”postgres_PostgresInterface.c” vorhanden. Dies bedeutet, dass auf der Java Seite eine Klasse PostgresInterface im Java Paket postgres vorhanden sein muss. Besitzt die Java Klasse nun eine native Methode: public native String execute(String query); 34 dann lautet die entsprechende C Methode: JNIEXPORT jstring JNICALL Java_postgres_PostgresInterface_execute(JNIEnv *env, jobject jobj, jstring jstr) Durch diese Eigenschaft von JNI muss die dynamische Bibliothek und die Schnittstelle zur Anbindung der Datenbank angepasst werden. Um dieses Problem zu umgehen, müsste man auf der Java Seite den Namen und die Position der implementierenden Klasse strikt vorgeben. Eine Möglichkeit, um die Strukturen zu umgehen, ist es für alle benötigten Felder in einer Struktur eine Rückrufmethode zu definieren. Da nun für eine Struktur mehrere Methoden über JNI aufgerufen werden müssen, verursacht dies viel mehr Overhead. 4.4 Perm-Modul In diesem Abschnitt wird das Perm Modul näher beschrieben und wie dieses in der MidPerm Anwendung integriert wurde. Das Perm Modul und die Postgres Komponenten, die für das Erstellen des Anfragebaums verantwortlich sind, wurden aus Postgres heraus in eine dynamische Bibliothek umgewandelt. Für die Abbildungen der internen Struktur in den folgenden Abschnitten wurde eine Relation r mit dem Attribut i verwendet mit den Einträgen 1, 2 und 3. Für die Provenance Anfrage wurde die Anfrage SELECT PROVENANCE * FROM r WHERE i = 2 durch das MidPerm gesendet, um die internen Strukturen dieser Anfrage zu erhalten. Um eine Provenance Anfrage umzuschreiben, gibt es, wie im generellen Ansatz schon beschrieben wurde, vier Submodule. Diese werden in den nachfolgenden Abschnitten näher erläutert. 4.4.1 Parser Der Parser überprüft die syntaktische Korrektheit der Anfrage, dabei werden noch keine Katalogzugriffe getätigt und so gibt es keine Rückrufmethoden, die implementiert werden müssten. MidPerm erzeugt mit Hilfe der Unix Programme Yacc und Lex C-Code, der vom Perm Modul verwendet wird, um das Parsen durchzuführen. Lex wird dazu gebraucht, um eine lexikalische Analyse des SQL Textes durchzuführen. Bevor aber eine solche Prüfung stattfinden kann, müssen reguläre Ausdrücke definiert werden. Lex erkennt dann diese Ausdrücke im SQL Text und kann ihn so unterteilen. In MidPerm werden diese Ausdrücke in der Datei ”scan.l” definiert. [12] Der Lexer alleine reicht aber nicht aus, da nur reguläre Ausdrücke mittels des Scannens erkannt werden. Um nun eine korrekte Syntax festzustellen, muss eine kontextfreie Grammatik definiert werden, die dann von Yacc angewendet werden kann. Diese Grammatik besteht aus 35 vielen grammatikalischen Regeln. Eine Regel besteht aus einer Definition und einer Aktion. Ein kleines Beispiel, um diese zwei Schritte zu erläutern, wäre: Beispiel 3 Der Lexer hat beim Scannen die vier regulären Ausdrücke: Monat, Datum, Tag und Jahr erkannt. Wir definieren nun folgende Regel: Datum: Monat Tag ‘,‘ Jahr Kommt jetzt ein Ausdruck der Art: ’September 12, 2010’ vor, wird er als richtig erkannt und der Yacc liefert keine Fehlermeldung. Alle grammatikalischen Regeln werden in der Datei ”gram.y” definiert. [11] Falls eine Datenbank weitere Ausdrücke als diejenige, die in diesen zwei Dateien definiert sind, unterstützt, müssen einfach weitere Regeln in den oben genannten Dateien eingebaut werden. So kann der Parser leicht angepasst werden, ohne dass am Parser-Prozess Änderungen vorgenommen werden müssen. Falls die Syntax korrekt ist, wird der SQL Text in die interne Darstellung von Postgres verwandelt und ans nächste Submodul weitergeleitet. Wie eine solche interne Darstellung aussehen könnte, wird anhand der im vorherigen Abschnitt erwähnten Beispielanfrage in der Figur 4.2 dargestellt. Da der Baum zu lang für eine Seite ist, wurde die Darstellung zerstückelt. Um den Baum richtig zu lesen, muss man einfach den Pfeilen folgen. Abbildung 4.2: Interne Struktur einer Anfrage, die vom Parser generiert wird. 4.4.2 Analyse Da bis jetzt nur die Syntax überprüft wurde, muss nun eine genaure Analyse durchgeführt werden, um festzustellen, ob die Semantik der Anfrage korrekt ist. Für diesen Schritt wird der vom Parser erzeugte Baum benötigt. Dieser wird Schritt für Schritt durchgegangen und die 36 einzelnen Knoten werden analysiert und mit weiteren Informationen, die für die Umschreibung der Anfrage benötigt werden, ergänzt. In dieser Analyse finden nun die Katalogzugriffe für die SQL Abfrage statt. Für jede Information, die aus der Datenbank geholt werden muss, wurde eine Methode auf der C-Seite definiert, die eine Methode auf der Java Seite aufruft, um die benötigten Daten aus der Datenbank zu erhalten. Diese werden in der Header Datei ”jniCallback.h” definiert. Um eine solche Methode aufrufen zu können, muss zuerst überprüft werden, ob diese Methode überhaupt existiert. Dieser Schritt kann weggelassen werden, da schon durch die Initialisierung des MidPerms garantiert wurde, dass der benötigte Rückruf existiert. Die Initialisierung hat aber auch einen weiteren Grund. Für jede Methode wird eine globale ID gesetzt, die von JNI benutzt wird, um den richtigen Rückruf tätigen zu können. Diese ID kann über die ganze Programmdauer benutzt werden. Es werden verschiedene Daten über den Rückruf an das MidPerm zurückgegeben und manchmal ist es nur ein einfacher Datentyp, der nicht näher bearbeitet werden muss. Hin und wieder werden aber ganze Objekte an das Modul gesendet. JNI definiert für diese Objekte einen eigenen Pointer, der auf das Objekt von Java zeigt und so kann man auf alle Felder dieses Objektes zugreifen. Diese Daten müssen dann an die benötigte C-Struktur übergeben werden. Dieser Schritt wird durch die Funktionen in der Datei ”helperMethods.c ” erledigt. Ein wichtiger Schritt in der Analyse findet bei einer Sicht statt. Da die Definition der Sicht vorhanden sein muss für die Umschreibung, muss diese aus der Datenbank geholt werden und in die interne Darstellung von Perm umgewandelt werden. Diese wird dann als Regel an die interne Darstellung der Sicht angehängt. So kann das Perm Modul die Definition auch für das Umschreiben verwenden. Für diesen Schritt gibt es zwei Möglichkeiten, entweder wird die Basisdefinition durch den Parser und dann durch die Analyse von MidPerm geschickt. Damit sie als Regel an die interne Darstellung einer Sicht angehängt werden kann. Oder man baut sich die Struktur in Java auf und schickt die Liste über eine Methode an das Perm Modul weiter. Das MidPerm verwendet die erste Variante. Falls aber eine solche Liste schon existiert, wenn man zum Beispiel Postgres anbindet, kann mittels einer booleschen Variable in der Klasse RewriteRuleClass, das nochmalige Parsen und Analysieren verhindert werden. Das Resultat der Analyse ist ein Anfragebaum, der so ähnlich aussieht, wie der Baum, der beim Parsen entsteht. Der grosse Unterschied ist, dass nun eine genauere Beschreibung der einzelnen Knoten vorliegt und so die Anfrage präziser definiert wird. Wie eine solche interne Struktur für die Beispielanfrage aussieht, wird in der Figur 4.3 dargestellt. Da der Baum auch hier zu lang für eine Seite ist, wurde die Darstellung zerstückelt. Um den Baum richtig zu lesen, muss man einfach den Pfeilen folgen. 4.4.3 Umschreibung und Provenance Umschreibung Nachdem nun eine korrekte Anfrage vorliegt, müssen alle Sichten in der Anfrage umgewandelt werden. Für diesen Zweck werden die Regeln angewendet, die bei jeder Sicht vorhanden sind. Falls also in einem Eintrag des Anfragebaums eine Sicht vorliegt, wird die Basisdefinition genutzt, um den Baum und so auch die Anfrage umzuschreiben. Wie eine solche Umschreibung 37 Abbildung 4.3: Interne Struktur einer Anfrage, die nach der Analyse generiert wird. durchgeführt wird, kann in [2] im Kapitel 36.3 nachgelesen werden. Sobald alle Sichten expandiert wurden, kann mit der Provenance Umschreibung begonnen werden. Der Anfragebaum wird dabei traversiert und falls ein Knoten für eine Provenance Umschreibung markiert wurde, wird er mittels dem im Kapitel Perm beschriebenen Regeln, umgeschrieben. Die interne Struktur der umgeschriebenen Beispielanfrage wird in Figur 4.4 beschrieben. Da der Baum zu lang für eine Seite ist, wurde die Darstellung zerstückelt. Um den Baum wieder zusammenzufügen folgt man den schwarzen Pfeilen. 4.4.4 Query Transformer Der letzte Schritt der MidPerm Anwendung ist nun, den Abfragebaum in einen SQL Dialekt umzuändern, der dann an die Datenbank übergeben werden kann, um die Anfrage auszuführen. Wie schon im allgemeinen Konzept beschrieben, werden für verschiedene Datenbankanbindungen verschiedene Umschreibemethoden benötigt. Das Transformieren des Anfragebaums in ein SQL Dialekt erfolgt aber immer auf die gleiche Art und Weise. Man traversiert jeden Knoten und je nachdem, was es für ein Knotentyp ist, wird die Anfrage übersetzt. Es wird zuerst der SELECT Teil übersetzt, dann der FROM Teil und so weiter. Falls eine Unteranfrage in irgendeinem Knoten vorhanden ist, wird mittels Rekursion zuerst die Unteranfrage übersetzt, um so die richtige Anfrage zu erhalten. Bei jedem Schritt wird dabei die partielle Anfrage mitgegeben, wenn ein neuer Knoten hinzukommt, wird er ans Ende dieser partiellen Anfrage angehängt. 38 Abbildung 4.4: Interne Struktur einer Anfrage, nachdem sie durch die Umschreiberegeln von Perm umgeschrieben wurde. 39 Nachdem die Anfrage umgewandelt wurde, wird sie über JNI an Java zurückgesendet und erst jetzt wird sie über JDBC an die Datenbank gesendet. 4.4.5 Diskussion Durch die verschiedenen SQL Dialekten kann kein einheitlicher Umschreibeprozess erfolgen. Es müssen deshalb viele verschiedene Query Transformer implementiert werden. Weiter muss MidPerm wissen, welche Datenbank angebunden ist. Dies bedeutet, dass MidPerm immer wieder angepasst werden muss. Falls für eine neue Datenbank noch keine Methode für die Umschreibung der internen Darstellung zu einem SQL Dialekt vorhanden ist, muss zuerst eine solche implementiert werden. Kennt man sich mit dem Code von Postgres und so auch vom MidPerm nicht aus, entsteht ein sehr grosser Aufwand, um eine neue Datenbank an das MidPerm anzubinden. 4.5 Cache und ID- Verwaltung Wie im Kapitel über Postgres erklärt wurde, werden für alle Einträge, wie Relationen, Sichten, Typen und viele Andere mehr Identifikationsnummern benutzt, um die Information in den Systemtabellen zu speichern. Andere Datenbanken besitzen aber keine solchen IDs für Einträge in den Systemtabellen. Deshalb muss eine ID- Verwaltung in Java implementiert werden, um diese Eigenschaft von Postgres zu kopieren. Für diesen Zweck wurden keine Schnittstellen oder sonstige Vorgaben im MidPerm vorgegeben, die eine Datenbankanbindung definieren sollten. MidPerm erwartet also nur die richtige Identifikationsnummer zum richtigen Namen. Diese werden bei der Analyse und bei der Umschreibung benützt. Es reicht aber nicht nur aus die IDs zu erzeugen. Man muss auch zu jeder ID, den benötigten Text und zu jedem Text, die benötigte ID liefern. Um dies zu erreichen, sollte auf der Java Seite eine Zwischenspeicherung der ID mit dem Inhalt durchgeführt werden. Wie diese Speicherung durchgeführt werden kann, wird auch nicht vorgegeben. Die Zwischenspeicherung erfolgt aber nicht nur auf der Java Seite. Für Relationen und Sichten wird ein Teil des Speichersystems von Postgres benutzt. Wurde eine Relation schon mal mit allen benötigten Informationen, wie Attribute, Relationstyp und viele weitere mehr aufgebaut, wird diese in MidPerm gespeichert und kann später wiederverwendet werden ohne einen JNI Aufruf durchführen zu müssen. Wie eine solche Implementierung aussehen könnte wird im Kapitel 4.6.2 näher erläutert. 4.5.1 Diskussion Durch die Einführung einer Cache Verwaltung auf der Java und der C Seite entsteht das Problem, dass bei einer Änderung in der Datenbank, die Relationen im Cache gelöscht oder geändert werden müssten. Beim Benützen einer Relation aus dem Cache müsste zudem zuerst überprüft werden, ob diese der Relation in der Datenbank entspricht. Diese Überprüfung würde den Vorteil des Caches verringern, da die Überprüfung über JNI laufen müsste und so einen 40 Overhead verursachen würde. Eine Lösung für dieses Problem wäre, wenn eine Anfrage einen Fehler durch ein Fehlen einer Relation oder eines Attributes verursacht, dass der Cache geleert wird und man die Anfrage dann nochmals durch das MidPerm schickt. Falls beim zweiten Durchlauf wieder ein Fehler entsteht, ist die Anfrage falsch formuliert. Eine andere Variante, wie man dieses Problem lösen könnte, ist, wenn es irgendeine Änderung im Schema einer Tabelle gibt, wird dies an MidPerm mitgeteilt und die Caches werden geleert. Da keine Vorgaben für die Implementierung des Speicherverhaltens auf der Java Seite vorhanden sind, kann jeder Anbindung dies frei überlassen werden. Falls eine Datenbank schon interne IDs benutzt, so wie in Postgres, ist die Cache- und ID- Verwaltung noch viel einfacher zu implementieren. 4.6 Angebundene Datenbanken Zurzeit sind zwei Datenbanken HSQLDB und Postgres angebunden. Die Implementierung dazu wird in den nächsten zwei Kapiteln näher erläutert. Um Fehler-, Debugging- und Infomeldungen von MidPerm, wie auch aus den implementierten Klassen auszugeben, wurde log4j benutzt. Siehe [19] für weitere Informationen. 4.6.1 Postgres Postgres verwendet für die Systemeinträge schon eigene Identifikationsnummern, diese werden bei der Anbindung auch verwendet. Die Generierung und Speicherung dieser Zahlen kann hier ausgelassen werden. Weiter ist es sehr einfach die Komponenten der Strukturen mit den richtigen Werten aus den Systemtabellen zu füllen. Da eine Struktur einem Eintrag in den Systemtabellen von Postgres entspricht, können diese mittels einer einfachen Anfrage schnell aus der Datenbank ermittelt werden. Wichtig ist dabei zu wissen, das Postgres bei einer normalen Anfrage die Identifikationsnummer des Eintrags nicht ausgibt. Jede Tabelle besitzt eine weitere Spalte OID. Diese definiert eine eindeutige ID für alle Einträge in einer Systemtabelle. Um diese Zahl auszugeben oder verwenden zu können, wird sie bei den SQL-Anfragen mittels der Referenz OID eingesetzt. Beispiel 4 Will man zum Beispiel die Relation mit der ID 2050 suchen. Dann wird folgende SQL-Query angewendet: SELECT OID, * FROM pg_class WHERE OID = 2050 Diese Anfrage erzeugt alle Informationen, die man für die interne Struktur einer Relation benötigt. Für die im Kapitel 4.4 beschriebenen SQL Anfrage erhält man folgende umgeschrieben Anfrage: 41 SELECT r.i, r.i AS prov_public_r_i FROM r WHERE r.i = 2 Es wurden drei Klassen definiert, die eine Anbindung von Postgres ermöglichen. Diese werden in den nächsten drei Abschnitten näher beschrieben. PostgresInterface Die PostgresInterface Klasse ist der Haupteinstiegspunkt für MidPerm. Er enthält alle Methoden, die für das Umschreiben einer Anfrage benötigt werden. Diese Klasse implementiert die zwei Schnittstellen JNINativeInterface und JDBCCallbackInterface, die in den vorherigen Abschnitten erklärt wurden. Damit die Middleware Applikation nicht mehrmals initialisiert werden kann und damit alle Felder in der Java Klasse richtig initialisiert werden, wurde das PostgresInterface als Singleton implementiert. Der Default-Konstruktor dieser Klasse wurde auf private gesetzt. Es können so keine weiteren Konstruktoren definiert werden, die Objekte dieser Klasse erzeugen können. Um eine Referenz zu dieser Klasse zu erhalten, muss die statische Methode getInstance() mit dem Parameter Connection aufgerufen werden. Diese initialisiert die Applikation beim ersten Aufruf und gibt eine Referenz auf die Klasse PostgresInterface zurück. Bei einem zweiten Aufruf wird lediglich eine Referenz auf die schon initialisierte Klasse zurückgegeben und falls das benutzte Connection Objekt in MidPerm geschlossen ist, wird das übergebene Connection Objekt verwendet. PostgresCatalogLookup Diese Klasse definiert alle Zugriffe auf die Systemtabellen der Postgres Datenbank. Alle Anfragen aus dem PostgresInterface werden an das PostgresCatalogLookup gesendet und dieses greift auf alle benötigten Informationen aus dem Datenbankkatalog zu. Falls bei einem JNI Aufruf ein Objekt verlangt wird und nicht ein einfacher Datentyp, werden die im Kapitel 4.3.2 beschriebenen Strukturen benutzt. Um nicht für die gleichen Informationen immer wieder eine teure Anfrage auf den Datenbankkatalog zu machen, wurde ein einfacher Speicher-Mechanismus entwickelt. Dieser wird im nächsten Abschnitt erklärt. PostgresMaps Diese Cache-Klasse wurde für die Speicherung von schon benutzten SQL Anfragen und die daraus entstandenen Informationen erzeugt. In dieser Klasse wurden verschiedene Java HashMaps für Relationen, Attribute, Operatoren und sonstige Text- und Identifikationsnummereinträge definiert. Als Schlüsselwert, um ein Objekt aus einer dieser Hashtabellen zu holen, werden die OIDs aus dem Datenbankkatalog benutzt. Bei der Umschreibung werden aber zum Beispiel nur die Namen einer Relation oder eines Attributes benutzt oder es muss für einen Namen eine OID zurückgegeben werden. Es müssen 42 also nicht ganze Objekte erzeugt werden oder es müssen auch nicht immer alle Informationen aus den Systemtabellen abgefragt werden. Um diesen Schritt überspringen zu können, wurden zwei Hashtabellen für Texteinträge und IDs hinzugefügt. Jedesmal wenn eine Relation, ein Attribut oder ein Operator für das MidPerm erzeugt werden muss, wird nicht nur das Objekt gespeichert, sondern es wird auch der Name selbst gespeichert. Dabei wird einmal der Name als Schlüssel benutzt und einmal die ID selbst. Da ein Name nicht eindeutig ist, wurde für alle Speicherungstypen Endungen hinzugefügt. Beispiel 5 Werden zum Beispiel Informationen für eine Relation mit dem Namen A und der ID 1200 benötigt, dann wird zuerst ein PGClass Objekt erzeugt und gespeichert, aber zusätzlich wird in der Texteinträge Hashtabelle der Wert 1200 / A und in der ID Hashtabelle der Wert A_rel / 1200 hinzugefügt. Ist bei einer anderen Relation ein Attribut mit dem Namen A und der ID 2200 vorhanden und wird dieses gespeichert, werden folgende Einträge gespeichert: 2200 / A und A_att / 2200. Wird nun nach einer ID für ein Attribut gesucht, kann man durch eine Suche mit dem Namen und der Endung _att den richtigen Wert bekommen. Bevor irgendeine Information aus dem Datenbankkatalog geholt werden soll, werden die Hashtabellen überprüft, ob die Informationen nicht schon vorhanden sind, um so eine Anfrage auf die Systemtabellen nicht durchführen zu müssen. 4.6.2 HSQLDB HSQLDB ist eine relationale Datenbank, die in Java entwickelt wurde. Sie ist als Open Source unter [13] verfügbar und auch gut dokumentiert. Da in den Systemtabellen dieser Datenbank keine OIDs benutzt werden, wie sie vom MidPerm gebraucht werden, muss das Speichern und das Generieren der IDs bei der Anbindung berücksichtigt werden. Ein weiteres Problem, dass bei der Anbindung von Postgres nicht entstanden ist, ist, dass HSQLDB nicht alle Datentypen MidPerms kennt. Deshalb musste eine Zuordnung zwischen den HSQLDB Datentypen und den MidPerm Datentypen definiert werden. Für die im Kapitel 4.4 beschriebenen SQL Anfrage erhält man folgende umgeschrieben Anfrage: SELECT r.I,r.I AS prov_public_r_i FROM R r WHERE r.I = 2 Die Implementierung dieser Anbindung wird mittels der folgenden Klassen näher erläutert. HSQLDBInterface HSQLDB implementiert, wie auch Postgres, die zwei Schnittstellen JNIInterface und JDBCCallbackInterface mittels einer Klasse. Sie ist auch wieder als Singleton implementiert aus den gleichen Gründen, wie sie im Postgres Kapitel beschrieben wurden. Dieses Interface gilt als Schnittstelle zwischen MidPerm und der Datenbank. Es werden keine Katalogzugriffe in dieser Implementierung getätigt. 43 HSQLDBCatalogLookup Um Metadaten aus der HSQLDB Datenbank zu erhalten, wurde diese Klasse implementiert. Dabei können die meisten Informationen aus dem DatabaseMetaData Objekt erzeugt werden. Das benutzte Connection Objekt, das bei der Initialisierung der HSQLDB Datenbank erzeugt wird, wird bei der Generierung der HSQLDBCatalogLookup Klasse übergeben und es wird ein DatabaseMetaData Objekt erzeugt. Um Information aus dem Datenbankkatalog zu erhalten, wird dieses Objekt benutzt. DBCatalogContainer Diese Klasse verwaltet die Generierung und Speicherung von IDs für Relationen, Typen, Operator und Attributen. Für diesen Zweck wurden drei Hashtabellen definiert, die folgende Kombinationen speichern: • Die erste Hashtabelle speichert als Schlüssel die ID und als Wert einen Namen. Diese Tabelle beinhaltet alle Textrepräsentationen die für die Umschreibung einer Anfrage benötigt werden. • Die zweite Hashtabelle speichert als Schlüssel den Namen und als Wert die ID. Da Relationen und Attribute den gleichen Namen haben können, wurde die Klasse RelationHolder eingefügt, die im nächsten Abschnitt erläutert wird. • Die dritte Hashtabelle speichert alle möglichen Aggregate als Schlüssel und als Wert die ID für eine solche Aggregatfunktion. Die dritte Tabelle wurde deshalb eingefügt, da es keine Möglichkeit gibt aus den Systemtabellen herauszufinden, welche Aggregat- Funktionen existieren und welche nicht. Deshalb musste zuerst kontrolliert werden, welche dieser Funktionen von HSQLDB unterstützt werden. Nach einer Überprüfung in der Dokumentation, sind folgende Aggregate erlaubt: count, max, min, avg und sum. Für jede wird bei der Initialisierung eine eindeutige ID erstellt und in der ersten und in der dritten Hashtabelle gespeichert. Bei der Überprüfung einer AggregatFunktion oder falls das Perm Modul die ID für eines dieser Aggregate verlangt, können diese Informationen leicht gefunden werden. Weiter enthält der DBCatalogContainer zwei weitere Felder, RelationHolder und TypeHolder, die auch für die Speicherung und Verwaltung der IDs mit einem Namen zuständig sind. Diese zwei Felder werden in den nächsten beiden Abschnitten erklärt. RelationHolder Um das Problem, das Attribute und Relationen in einer Datenbank den gleichen Namen haben können, zu lösen, wurde diese Klasse eingefügt. Diese speichert die Relationen der Datenbank in zwei Hashtabellen mit folgenden Schlüssel / Wert Kombinationen: ID / Name und Name / ID. So können immer die benötigten Informationen an das Perm Modul gesendet werden. Für die Attribute wurde aber keine solche Klasse definiert, diese werden also nicht in einer Hashtabelle gespeichert. Falls ein Attribut gesucht werden muss, wird das immer mit der ID 44 der Relation gemacht und mittels einer Suche über den Datenbankkatalog gelöst. Im Perm Modul wird ein Attribut aus der internen Darstellung einer Relation geholt, die eine Liste aller Attribute als Feld speichert. Ist eine solche Repräsentation noch nicht vorhanden, wird sie erzeugt und im Cache vom MidPerm gespeichert. Die Suche nach einem Attribut wird so nur einmal gemacht. Auf diese Weise kann das Problem, das vielleicht eine falsche ID für eine Relation entsteht oder eine falsche Relation für eine ID verwendet wird, gelöst werden. TypeHolder Wie schon erwähnt wurde, unterstützt HSQLDB nicht alle Datentypen, die im Perm Modul verwendet werden können. Für diesen Zweck muss eine Zuordnung der HSQLDB Typen zu den Perm Typen stattfinden. Diese Zuordnung wird mittels dieser Klasse durchgeführt. Nicht alle Datentypen aus der Datenbank erhalten eine Zuordnung zu einem Typ des Moduls. Es werden nur die wichtigsten Typen zugeordnet. Die restlichen Datentypen werden als Text gespeichert. HSQLDB muss nicht den richtigen Typ wissen, um eine Anfrage richtig auszuführen. Wenn Zahlen und Textwerte unterschieden werden können, reicht das aus, um eine Anfrage ausführen zu können. Bei der Initialisierung des HSQLDBInterface findet die Zuordnung statt. Weiter werden alle Typen der Datenbank mit einer ID und dem Namen des Datentyps in den DBCatalogContainer geladen. Dies erlaubt dem Perm Modul die richtigen Datentypen zu setzen. 45 5 Testen und Debugging Das Testen der MidPerm Applikation basiert auf das von Boris Glavic erstelltem Testprojekt für Perm, das auf DBUnit aufbaut. DBUnit ist eine Erweiterung von JUnit, welches für das Testen von Datenbanken benutzt werden kann. Für weitere Information wird auf [16] hingewiesen. Das Testen wurde für die beiden angebundenen Datenbanken implementiert, falls eine andere Datenbank getestet werden soll, können die Verbindungsparameter angepasst werden, um so das Testen eines anderen Datenbanksystems zu ermöglichen. Bevor aber mit dem Testen begonnen werden kann, muss die Testdatenbank erzeugt werden. Für diesen Zweck gibt es für jede angebundenen Datenbanken ein Skript, welches die Testdaten enthält. Diese können eingelesen werden, um so die benötigten Daten in der Datenbank zu laden. Um die verschiedenen Testklassen für JUnit zu erzeugen, wurden verschiedene XML Dateien definiert. Diese werden eingelesen und durch das Testsystem in JUnit Testklassen verwandelt. Diese Klassen können verwendet werden, um das MidPerm und die angebundene Datenbank zu testen. In der XML Datei definiert man die auszuführenden SQL Anfragen und die erwarteten Resultate für jede Anfrage. Ein Beispiel für eine solche Testanfrage für die kleine Datenbank aus dem Kapitel 4.4 könnte folgendermasse aussehen in einer XML Datei: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties> <comment>Test cases for Perm</comment> <entry key="q1.query"> SELECT PROVENANCE * FROM r; </entry> <entry key="q1.result"> i | prov_public_r_i ----------+----------------1 | 1 2 | 2 3 | 3 </entry> </properties> Jede Klasse entspricht dabei einer JUnit Testklasse, die man ausführen kann. Diese führt jede Anfrage in der XML Datei aus und vergleicht das erzeugte Resultat mit dem erwarteten Resultat. Für MidPerm wurden folgende Testfälle verwendet, diese sollen möglichst alle Funktionalitäten des jeweiligen Query Transfomer testen, um zu sehen, ob die Anfrage auch korrekt in den SQL Dialekt transformiert wird und um zu sehen, ob die angebundene Datenbank die richtigen Informationen aus dem Datenbankkatalog verwendet. 46 • Testfälle für Aggregationsanfragen • Testfälle für Mengenanfragen • Testfälle für Anfragen, die eine Provenance Anfrage als Unteranfrage haben • Testfälle für Selektion- und Projektionsanfragen ohne Joins • Testfälle für Selektion- und Projektionsanfragen mit Joins • Testfälle für verschiedene Kombinationen von korrelierten und unkorrelierten Anfragen Um das Perm Modul debuggen zu können, kann der GDB Debugger benutzt werden. Der GDB ist einer auf UNIX basierter Debugger, welches für verschiedenste Sprachen eingesetzt werden kann. GDB ist aber nicht mehr nur für UNIX einsetzbar, sondern er wurde schon für verschiedene andere Betriebssysteme portiert. Für weitere Informationen siehe [18]. Bevor der GDB für das Perm Modul eingesetzt werden kann, sollte, bevor die Anfrage umgeschrieben wird, MidPerm durch einen Breakpoint angehalten werden. So hat man sichergestellt, dass die native Bibliothek initialisiert wurde. Nun muss die Prozess ID dieser nativen Bibliothek gefunden werden. Dies kann auf dem MAC System zum Beispiel durch den Befehlsfensteraufruf ”ps -A” gefunden werden. Dieser Aufruf zeigt alle laufenden Prozesse mit deren Prozess ID an. Nachdem der GDB gestartet wurde, kann man sich durch die Prozess ID an das Perm Modul anhängen und diesen debuggen. 47 6 Zusammenfassung Die MidPerm Applikation ist ein erster Schritt, das Perm Modul von Postgres auszulagern und so anderen Datenbanksystemen erlaubt, das Provenance System zu benutzen. Das Perm Modul wurde als ein weiteres Submodul im Umschreibeprozess von Postgres implementiert. Durch diese Integration entstanden viele Abhängigkeiten, die zuerst definiert und dann ausgelagert werden mussten. Postgres hat eine Codegrösse von in etwa 800’000 Zeilen Code. Sich innerhalb von wenigen Wochen in ein so grosses System einzuarbeiten, war eine harte Arbeit. Es reichte natürlich nicht nur sich einzuarbeiten. Es mussten auch die Stellen definiert werden, bei dem man die Daten über die JNI Schnittstelle und der JDBC Schnittstelle empfangen soll. Aus diesen Stellen entstand ein einfaches Interface, das für alle Katalogdaten, die vom Perm Modul gebraucht werden, implementiert werden muss. Durch die Entwicklung eines eigenen JDBC Treibers wird eine einfache Einbindung des MidPerms mit einem Datenbanksystem ermöglicht. Da die meisten Datenbankensysteme über eine JDBC Schnittstelle angesprochen werden können, ist ein Einsatz nicht mehr nur auf Postgres begrenzt. Durch die Einführung von zwei einfachen Schnittstellen, die für die Kommunikation zwischen dem Perm Modul, das als native Bibliothek vorhanden ist, und der Datenbankschnittstelle verantwortlich sind, können Datenbanksysteme schnell und einfach für den Einsatz mit Perm entwickelt werden. Da viele Datenbanksysteme verschiedene SQL Dialekte verwenden, wird die Anbindung, falls ein Query Transformer noch nicht vorhanden ist, erschwert. Da aber jeder dieser Transformer auf dem gleichen Prinzip aufgebaut ist und die interne Struktur immer auf die gleiche Weise umgewandelt wird, wird eine Implementierung einer neuen Funktion vereinfacht. Man kann bei der Implementierung eines neuen Query Transformers auf die schon vorhandenen Funktionen zugreifen, um die Art und Weise zu kopieren und gegebenenfalls anzupassen für das neue Datenbanksystem. Ein weiteres Problem bei der Benutzung von MidPerm wird durch JNI selber verursacht. Das Perm Modul muss für die Benutzung mit JNI in eine native Bibliothek umgewandelt werden. Da eine native Bibliothek, die auf einem Betriebssystem generiert wurde, nicht auf einem anderen Betriebssystem läuft, müssen verschiedene native Bibliotheken für verschiedene Betriebssysteme generiert werden. Durch die vielen Freiheiten, dass man das Logging frei implementieren kann, dass die IDVerwaltung frei programmierbar ist und dass man sehr leicht einen Cachespeicher einfügen kann, ist MidPerm sehr gut anpassbar auf die gewünschte Applikation und Datenbank. Es gibt nur sehr wenige Vorgaben, die bei einer Anbindung eines neuen Datenbanksystems erfüllt werden müssen. Das Datenbanksystem muss JDBC als Datenbankschnittstelle unterstützen und es müssen die beiden im Kapitel 4.3 definierte Interfaces implementiert werden. Falls ein Query Transformer schon vorhanden ist, reichen diese beiden Voraussetzungen, um ein neues 48 DBMS anzubinden. Da das Perm Modul die Datentypen von Postgres übernimmt, muss für jedes angebundene Datenbanksystem eine Zuordnung zwischen dem Datentypen des neuen DBMS und dem Datentypen von MidPerm implementiert werden. Um die Anfragen richtig zu bearbeiten, muss das Perm Modul die richtigen Datentypen wissen. Meist reicht aber hier eine einfache Definition der Abbildung für die wichtigsten Datentypen, wie Integer, Double und Boolesche Werte. Alle anderen Datentypen werden auf einen String abgebildet. MidPerm wurde durch viele SQL Anfragen getestet, doch die meisten getesteten SQL Anfragen sind nicht allzu kompliziert aufgebaut. MidPerm müsste noch mit vielen weiteren komplexeren Anfragen getestet und gegebenenfalls angepasst werden. Es kann noch sein, dass nicht alle Katalogzugriffe des Perm Moduls ausgelagert wurden. Falls dies der Fall ist, versucht Perm über die Systemtabellen von Postgres, da dies der Normalfall in der integrierten Version von Perm in Postgres ist, die benötigten Katalogdaten zu suchen. Dies führt zu einem Fehler im Perm Modul, was wiederum zu einem Absturz des ganzen MidPerms führt. Eine Erweiterung solcher Tests kann sehr leicht durch die im Kapitel 5 beschriebenen XML Dateien erzeugt werden, um möglichst alle Katalogzugriffe zu finden und zu testen. Durch die Entkopplung aus Postgres wurde ein Provenance System erzeugt, das für jedes Datenbanksystem, welches die JDBC Schnittstelle unterstützt, angewendet werden kann. Es spielt keine Rolle, welche darunterliegende Datenbank für die Provenance Berechnung benutzt werden soll. Durch die Umschreiberegeln des Perm Moduls wird eine SQL Anfrage erzeugt, die unabhängig vom darunterliegendem Datenbanksystem ist, die Provenance Daten berechnen kann. Durch das vorhandene Testsystem kann sichergestellt werden, dass bei einer Veränderung des MidPerms, die wichtigsten Funktionalitäten getestet werden können und so überprüft werden kann, dass die Veränderungen MidPerm nicht korrumpiert haben. 49 7 Ausblick In diesem Kapitel werden ein paar mögliche zukünftige Erweiterungen und Verbesserungen vorgestellt, die eine Verwendung von MidPerm erleichtern und MidPerm selber noch stabiler laufen lassen würden. Zurzeit wurde Perm nur als native Bibliothek für das Mac OS Betriebssystem generiert. Für die Benützung des MidPerms auf anderen Systemen müssten noch diverse Bibliotheken für verschiedene andere Betriebssysteme generiert werden. Eine weitere Verbesserung bei der nativen Bibliothek müsste bei deren Generierung eingeführt werden. Zurzeit werden alle Module des Postgres System in die native Bibliothek geladen. Es wird z.B. der Optimierer von Postgres auch in das Perm Modul integriert, obwohl dieser nicht für die Umschreibung verwendet wird. Es muss also eine weitere Entkopplung zwischen den nicht benötigten Modulen des Perm Moduls und von Postgres stattfinden, um so die native Bibliothek von unnötigen Funktionen zu befreien. Da noch nicht sichergestellt ist, dass alle Katalogzugriffe des Perm Moduls über JNI ausgelagert wurden, müssten noch verschiedene Tests durchgeführt werden, um allfällige fehlende Katalogzugriffe im Perm Modul zu identifizieren. Diese Katalogzugriffe müssten in der JDBCCallbackInterface Schnittstelle ergänzt werden. Für jeden Zugriff muss auch im Perm Modul in der Header Datei ”jniCallback.h” eine entsprechende Rückruffunktion definiert werden. Zurzeit ist nur ein einfaches Hashing von schon ausgeführten Katalogzugriffen im MidPerm implementiert. Um die teuren Katalogzugriffe über die JDBC Schnittstelle und JNI nicht immer durchführen zu müssen, können bessere Speicherverfahren eingeführt werden, als die Vorhandenen zwei Lösungen von Postgres und HSQLDB. Eine Lösungsvariante wäre, die Speicherung von Relationen und Attributen im Perm Modul selber zu erweitern, um so die JNI Aufrufe zu ersparen. Parallel zur Zwischenspeicherung der Kataloginformationen kann eine generelle ID- Verwaltung implementiert werden. Dies ermöglicht eine einfachere Anbindung einer neuen Datenbank, da für ein neues Datenbanksystem nur die Katalogzugriffe und gegebenenfalls ein Query Transformer implementiert werden muss. Das Hashing sollte eng mit dem Problem einer Änderung in der Datenbankstruktur verknüpft werden. Falls z.B. eine Relation verändert wird, im Speicher ist aber immer noch die alte Relation vorhanden, kann bei einer Anfrage ein falsches Resultat oder eine Fehlermeldung ausgegeben werden. Die Änderungen in der Datenbankstruktur müssen MidPerm mitgeteilt werden, damit entweder die veränderten Datenobjekte im Speicher angepasst werden oder der ganze Speicher gelöscht wird. MidPerm verwendet die in Postgres definierten Datentypen. Wenn eine neue Datenbank angebunden werden soll, muss eine Zuordnung zwischen den Datentypen von MidPerm und den Datentypen der angebundenen Datenbank implementiert werden. Eine generelle Implementierung einer solchen Zuordnung kann wie bei einer generellen ID- Verwaltung die Anbindung 50 vereinfachen. Die Zuordnung muss mit der ID- Verwaltung und dem Hashing gekoppelt werden, da jeder Datentyp auch eine ID besitzen muss und die Datentypen auch Zwischengespeichert werden sollten, um die Zuordnung nur einmal zu machen. Ein wichtiger Punkt, der noch getestet werden soll, ist der Mehrfachzugriff auf das MidPerm. Bis jetzt sind nur wenige Tests durchgeführt worden, die sicherstellen, dass MidPerm auch bei gleichzeitigen Zugriffen von mehreren Anwendern, noch läuft. Falls MidPerm durch den Mehrfachzugriff instabil läuft, muss eine Lösung gefunden werden, um mehreren Anwendern den gleichzeitigen Zugriff auf MidPerm zu erlauben. Für MidPerm wurde aber nicht nur eine Testumgebung erzeugt. Es wurde auch ein Performance Tester, der von Boris Glavic für Postgres implementiert wurde, angepasst, um ihn für MidPerm benutzen zu können. Leider reichte die Zeit nicht aus, um einen Vergleich zwischen MidPerm und Postgres mit Perm integriert durchzuführen. Eine solche Analyse wäre wichtig um herauszufinden, in welchem Masse die JNI Aufrufe und die Katalogzugriffe über den JDBC Treiber das ganze MidPerm System verlangsamen. 51 Abkürzungsverzeichnis DBMS GDB GIS HSQLDB ID JNI JDBC JVM OID PERM PReServ SQL VDC VDL YACC Database Management System GNU Project Debugger Geographische Informationssysteme HyperSQL Database Identifikationsnummer Java Native Interface Java Database Connectivity Java Virual Machine Object Identifier Provenance Extension of the Relational Model Provenance Recording for Services Structured Query Language Virtual Data Catalog Virtual Data Language Yet Another Compiler Compiler 52 Literaturverzeichnis [1] B. Glavic and K. R. Dittrich, Data provenance: A categorization of existing approaches, in Proc. BTW’07, pp. 227-241. [2] The PostgreSQL Development Group. PostgreSQL 8.3.0 Documentation, University of California, 2008. [3] P. Buneman, S. Khanna, and W. C. Tan, Why and where: A Characterization of Data Provenance, in Proc. ICDT’01, pp. 316-330. [4] P. Groth, S. Miles, and L. Moreau, PReServ: Provenance Recording for Services, AHM’05, Nottingham. [5] I.T. Foster, J.S. Vöckler, M. Wilde, and Y. Zhao, Chimera: A Virtual Data System for Representing, Querying and Automating Data Derivation, in SSDBM’02, pp. 37-46, Washington DC. [6] I.T. Foster, The virtual data grid: a new model and architecture for data-internsive collaboration, in SSDBM’2003, pp. 11-11, Washington DC. [7] A. Woodruff, and M. Stonebraker, Supporting Fine-grained Data Lineage in a Database Visualization Environment, in ICDE’97, pp. 91-102, Washington DC. [8] H. Chen, H. Lim, and J. Xia, Concept Architecture of PostgreSQL http://www.cs. uwaterloo.ca/~h8chen/course/798/ConceptArch.pdf (besucht am: 4. Mai 2010) [9] B. Glavic, G. Alonso, Perm: Processing provenance and data on the same data model through query rewriting, ICDE’09. [10] B. Glavic, G. Alonso, Perm Overview, http://www.ifi.uzh.ch/dbtg/ Projects/current_projects/perm/ (besucht am: 10. Dezember 2009) [11] S.C. Johnson, Yacc: Yet Another Compiler-Compiler,AT&T Bell Laboratories, http: //dinosaur.compilertools.net/yacc/index.html, New Jersey. (besucht am: 25. April 2010) [12] M.E. Lesk and E. Schmidt, Lex - A Lexical Analyzer Generator, http://dinosaur. compilertools.net/lex/index.html (besucht am: 25. April 2010) [13] The HSQL Development Group, HSQLDB- 100% Java Database, http://hsqldb. org/ (besucht am: 16. Dezember 2009) 53 [14] S. Liang The Java Native Interface Programmer’s Guide and Specification, USA, Addison-Wesley Juni 1999. [15] L. Chiticariu, W.C. Tan, G. Vijayvargiya, DBNotes: A Post-It System for relational databases based on provenance, ACM’05, Baltimore, Maryland, Juni 14-16. [16] DbUnit, http://www.dbunit.org/ (besucht am: 10. Mai 2010) [17] D.P. Lanter, Design Of A Lineage-Based Meta-Data Base For GIS, in Cartography and Geographic Information Systems, vol. 18, 1991, pp. 255-261. [18] R.M. Stallman, R.H. Pesch, Debugging with GDB, Seventh Edition, Free Software Foundation, Boston, Februar 99. [19] Apache Logging Services, http://logging.apache.org/ (besucht am: 12. Mai 2010) 54