MidPerm - Realisierung einer Provenance SQL

Werbung
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
Herunterladen