Rheinische Friedrich-Wilhelms-Universität Bonn Institut für Informatik Abteilung III Diplomarbeit ARA Eine Erweiterung des relationalen Datenbanksystems Access um Trigger Sascha Münch Erkrath, den 15. Oktober 2001 Erstgutachter: Prof. Dr. Rainer Manthey Danksagung An dieser Stelle möchte ich einigen Personen danken: Meinem Betreuer für die vielen wertvollen Diskussionen und Anregungen: Prof. Dr. Rainer Manthey Für die wertvollen TEX-Tips: dem IRC-Channel #rrr Für das Ghostreading: Hans Brocks Für seine hä?“-Kommentare und Ghostreading: ” Michael Horn Für moralische Unterstützung, Ghostreading und das Rückenfreihalten während der ganzen Zeit: meinen Eltern Inhaltsverzeichnis 1 Einleitung 9 2 Grundlagen über Datenbanken 2.1 Datenbanksystem und Datenbankmanagementsystem 2.2 Relationale Datenbanken . . . . . . . . . . . . . . . . 2.3 Aktive Datenbanken . . . . . . . . . . . . . . . . . . 2.4 Trigger in SQL3 . . . . . . . . . . . . . . . . . . . . . 2.4.1 Syntax . . . . . . . . . . . . . . . . . . . . . . 2.4.2 Semantik . . . . . . . . . . . . . . . . . . . . 2.5 Aufbau von Microsoft Access 97 . . . . . . . . . . . . 2.5.1 Access und die Jet-Engine . . . . . . . . . . . 2.5.2 Konventionen der Namensgebung . . . . . . . 3 Konzept einer Erweiterung 3.1 Entwurf einer Triggersprache für Access . . . . . . 3.2 Prinzipielle Erweiterungsmöglichkeiten in Access . . 3.2.1 OLE-DB . . . . . . . . . . . . . . . . . . . . 3.2.2 Datenkontrolle von außen . . . . . . . . . . 3.2.3 Access als Datenvermittler . . . . . . . . . . 3.2.4 Datenmanipulation über eine interne Klasse 3.3 Entwurf eines Triggermanagers für Access . . . . . 3.3.1 Architektur . . . . . . . . . . . . . . . . . . 3.3.2 Funktionalität des Triggermanagers . . . . . 3.3.3 Behandlung von Fehlschlägen . . . . . . . . 4 Implementierung 4.1 Architektur und Funktionalität . . . . . 4.1.1 Triggerverwaltung . . . . . . . . . 4.1.2 Triggermanager . . . . . . . . . . 4.2 Ausgewählte Aspekte . . . . . . . . . . . 4.2.1 Interne Verwaltung von Triggern 4.2.2 Ablaufsteuerung . . . . . . . . . . 4.2.3 Fehlerbehandlung . . . . . . . . . 5 Zusammenfassung und Ausblick 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 13 14 16 17 17 19 21 23 26 . . . . . . . . . . 29 30 35 36 37 38 39 40 40 41 43 . . . . . . . 45 45 48 52 54 55 56 60 63 8 Kapitel 1 Einleitung In modernen Datenbankmanagementsystemen (kurz: DBMS) ist es heute möglich, aktive Regeln zu definieren, mit deren Hilfe ein Datenbanksystem (kurz DBS) selbständig auf Änderungen der Daten reagieren kann. In diesem Zusammenhang spricht man auch von Triggern statt aktiven Regeln. Bei Microsoft-Access97 1 handelt es sich um ein DBMS, das zur Anwendung als Einzelplatzsystem konzipiert worden ist. Aus diesem Grund sind einige Funktionalitäten nicht implementiert worden. Durch die fehlende Mehrbenutzerunterstützung fehlt es Access an Sperrmechanismen, die zum Beispiel verhindern sollen, daß ein Nutzer versucht Daten zu ändern, die von einem anderen Nutzer gerade gelöscht worden sind. Ebenso fehlt das heute weit verbreitete Konzept der Trigger, mit denen ein DBS selbständig auf Änderungen von Daten reagieren kann. Im Rahmen der Diplomarbeit soll Access um dieses Konzept erweitert werden. Durch die Erweiterung von Access um Trigger wird es dann möglich sein Anwendungen zu entwickeln, die sich durch neue Trigger an neue Vorgaben anpassen lassen. Im allgemeinen handelt es sich bei heutigen Datenbankmanagementsystemen um relationale Systeme, zu denen auch Access zu zählen ist. Enthält das DBMS auch eine Komponente, die es erlaubt Trigger zu verwenden, um aktiv auf Änderungen des Datenbankzustandes zu reagieren, so spricht man von einem aktiven DBMS. Um ein Gefühl dafür zu bekommen, wo überall Trigger sinnvoll eingesetzt werden können, seien hier ein paar Beispiele genannt. Eine Anwendung für ein aktives DBMS stellt das von vielen Autofahrern gefürchtete Verkehrszentralregister des Kraftfahrt-Bundesamtes in Flensburg, besser bekannt unter dem Begriff Verkehrssünderkartei oder Punktekonto, dar. Hier könnte das aktive DBS, bei erreichen oder überschreiten eines bestimmten Punktestandes, die zustänige Fahrerlaubnisbehörde über den Punktestand informieren, damit hier entschieden 1 Microsoft-Access97 ist eingetragenes Warenzeichen der Firma Microsoft. Im weiteren Verlauf wird nur noch kurz von Access die Rede sein. 9 10 KAPITEL 1. EINLEITUNG werden kann, ob der Führerschein eingezogen werden muß oder nicht. Zu den genauen Meldewegen siehe auch [KBA]. Eine weitere Möglichkeit Trigger zu verwenden findet man im Rechnungswesen. In einem Warenwirtschaftssystem kann für jeden Kunden eine individuelle Höchstgrenze an offenen Rechnungen bzw. deren Summen hinterlegt werden. Wird beim Erstellen einer neuen Rechnung dieser Betrag überschritten, wird der Kunde automatisch im System gesperrt. Sobald im System die Höchstgrenze durch eine Zahlung unterschritten wird, kann der Kunde wieder freigegeben werden. Um auch ein Beispiel auf dem Fachbereich Informatik zu betrachten, möge man sich eine Datenbank für ein Prüfungsamt vorstellen, in unserem Fall das Prüfungsamt für Informatik an der Universität Bonn. In dieser Datenbank werden die Studenten des Fachbereiches Informatik verwaltet und ihre Prüfungen protokolliert. Wird eine bestandene Prüfung in das System eingegeben, könnte eine aktive Datenbank prüfen, ob alle notwendigen Bedingungen erfüllt sind, um automatisch das Diplomzeugnis für diesen Studenten zu erstellen und auszudrucken. Eine auf Basis eines aktiven DBMS erstellte Prüfungsamtsdatenbank könnte dann, im Falle einer neuen Diplomprüfungsordnung, mit neuen Regeln an Änderungen angepaßt werden. Bei einer Prüfungsamtsdatenbank wird es sich in der Regel um eine Einzelplatzanwendung handeln, da recht sensible persönliche Daten gespeichert werden. Eine sinnvolle Wahl wäre hier also die Verwendung von Access. Neben anderen sinnvollen Anwendungen für ein Triggerkonzept in Access läßt sich hier gut die Notwendigkeit für ein solches Konzept erkennen. Trotz der Mächtigkeit des Konzeptes gibt es bisher keine Erweiterung von Access um Trigger. Lediglich eine Anbindung von Access an einen SQL-Server würde eine Nutzung von Triggern erlauben, was aber wieder zusätzlichen Aufwand bedeuten würde, sowohl in der Programmentwicklung als auch später in der Wartung. Zusätzlich würde ein wesentlich leistungsstärkerer Computer benötigt, oder aber es wäre eine Vernetzung mit einem Server nötig, was wieder gegen eine eventuell geforderte Einzelplatzlösung sprechen würde. Aus den oben genannten Gründen für eine Erweiterung ergab sich die Aufgabenstellung, Access um ein Triggerkonzept zu erweitern. Hierzu war ein Entwurf und später auch die Realisierung eines eigenen Konzeptes nötig. Dieses Konzept mußte die spezielle Situation berücksichtigten, daß ein direktes Eingreifen in den Ablauf der Datenmanipulation nicht möglich ist. Angelehnt an die Trigger aus dem SQL-Standard wurde ein Konzept ent- 11 wickelt, welches sich in Access einfügt und Access um eine Komponente zur Triggerauswertung zu einem aktiven DBMS erweitert. Dabei mußte das Konzept der Trigger aus dem Standard an einigen Stellen angepaßt werden. Es sind sowohl Änderungen nötig gewesen, die die Funktionalität eingeschränkt haben, als auch Änderungen, die das Triggerkonzept für Access um zusätzliche Möglichkeiten erweitert haben. Daraus entstand dann die Triggersprache ARA, was für Aktive ” Regeln in Access“ steht. Die im Rahmen dieser Diplomarbeit realisierten Funktionen sind Teil einer Access-Datei und sorgen dafür, daß die Komponenten zur Auswertung von ARA-Trigger zur Verfügung stehen. Im anschließenden Kapitel werden die Grundlagen von relationalen und aktiven Datenbanken diskutiert. Anschließend wird das Konzept einer Erweiterung von Access im allgemeinen und der Entwurf einer Triggersprache besprochen. Im Kapitel 4 wird dann eine konkrete Implementierung der Sprache ARA besprochen. In meinen Schlußbetrachtungen werde ich auf noch mögliche Erweiterungen meiner Implementierung und Änderungsmöglichkeiten eingehen. 12 KAPITEL 1. EINLEITUNG Kapitel 2 Grundlagen über relationale und aktive Datenbanken Um die vorliegende Arbeit in einem einheitlichen Kontext betrachten zu können, werden in diesem Kapitel einige Grundlagen aus dem Bereich der Datenbanken und insbesondere von Access kurz vorgestellt. Für die Grundlagen von Datenbanksystemen sei auf [KE96] verwiesen. Für die Nutzung von Access sei [HS97] empfohlen, wo aber kein wissenschaftlich-theoretischen Zugang zu diesem System geboten wird, sondern eher ein praxisorientierter Weg gewählt wird. Eine Sammlung interessanter Funktionen und möglichen Lösungswegen von Standardproblemen mit Access findet man in [KH3]. 2.1 Datenbanksystem (DBS) und Datenbankmanagementsystem (DBMS) Da im weiteren Verlauf von Datenbanksystem (DBS) und DatenbankmanagementSystem (DBMS) die Rede sein wird, es aber keine einheitliche Definition der Begriffe in der Literatur gibt, werden diese Begriffe, wie sie im Laufe der Arbeit verstanden werden sollten, hier kurz erklärt. Eine klare Beschreibung ergibt sich bereits aus der Abbildung 2.1. Hieran erkennt man recht deutlich, daß das DBMS lediglich eine Verwaltungssoftware ist, die die Zugriffe auf die jeweilige Datenbank verwaltet. Zusammen mit anwendungsabhängigen Datenbanken (im Bild mit DBi gekennzeichnet) bildet das DBMS ein DBS, das dann wiederum vom Anwender oder Anwendungsprogrammen genutzt werden kann. Das DBMS hat, neben der allgemeinen Verwaltung und dem Zugriff auf die Daten, die Aufgabe einer Verwaltung des Mehrbenutzerbetriebs mit dem Schutz vor Datenverlust bei gemeinsamen Zugriffen auf die Daten. Daneben werden durch den Einsatz von einem DBMS auch Redundanzen und Inkonsistenzen vermieden, wenn ein DBMS genutzt wird statt einer Speicherung der Daten in iso13 14 KAPITEL 2. GRUNDLAGEN ÜBER DATENBANKEN Abbildung 2.1: Architektur eines Datenbanksystems lierten Dateien. Ebenfalls erleichtert der Einsatz eines DBMS die Entwicklung einer Anwendung, da nicht jedesmal eine für diesen Anwendungsfall passende Dateiverwaltung entwickelt werden muß, sondern man sich vollkommen auf die logische Ebene der Datenrepräsentation beschränken kann. Die hier beschriebenen Vorzüge und Begriffe gelten so für jedes DBS. Im weiteren Verlauf werden aber nur relationale Datenbanken und hier insbesondere Access betrachtet. 2.2 Relationale Datenbanken Das Konzept der relationalen Datenbanken geht auf das relationale Datenmodell von Edward E. Codd zurück. Das von ihm 1970 entwickelte Modell wird in [C70] eingeführt. Bei den kommerziell genutzten DBMS ist das relationale Datenmodell das meist benutzte und bis heute aktuell. Im Gegensatz zu den heute historisch zu nennenden Ansätzen der Datenver- 2.2. RELATIONALE DATENBANKEN Name Dallmann Krawolitzky Ruprecht ... Vorname Sandro Stefan Erik ... Straße Neanderstr. 2 Rathelbecker Weg 7 Heinrich-Heine-Alle 347 ... 15 Ort Erkrath Düsseldorf Neuss ... Tabelle 2.1: Tabelle einer Adressverwaltung waltung nach dem Netzwerkmodell oder dem hierarchischen Modell arbeitet das relationale Datenmodell mit einer mengenorientierten Verarbeitung der Daten. Auch die Strukturierung der Daten ist vergleichsweise einfach. Es gibt nur flache Tabellen (Relationen), in denen die Daten abgelegt werden. Die Zeilen der Tabellen bilden die einzelnen Datenobjekte, die sich je Tabelle im Aufbau gleichen. Die Tabelle 2.1 zeigt ein Beispiel, wie eine Tabelle zur Adressverwaltung aussehen könnte. Die so in Tabellen (Relationen) gespeicherten Daten werden durch vom DBMS vorgegebene Operationen ausschließlich mengenorientiert verarbeitet und verknüpft. Dabei spielt die Anzahl der Datensätze in einer solchen Menge keinerlei Rolle, es können sowohl Mengen manipuliert werden, die leer sind, oder aber auch Mengen die alle Datensätze einer Tabelle enthalten. Liegen die Relationen als physisch gespeicherte Tabellen vor, so spricht man auch von Basisrelationen. Im Gegensatz dazu können die Ergebnisse von Manipulationsbefehlen in sogenannten Views vorliegen, die dann zwar nicht physisch auf einem Datenträger abgelegt worden sind, sich aber dennoch so wie Basisrelationen ansprechen lassen. Das Erstellen von Views und auch die Manipulation der Daten geschieht mittels einer Anfragesprache wie SQL1 . Sie stellt sowohl Elemente zur Definition des Datenmodell zur Verfügung, als auch zur Abfrage. Sowohl die Anfragesprache SQL als auch das relationale Datenmodell selbst beruhen auf der mathematischen Grundlage der relationalen Algebra. Somit lassen sich Optimierungen der Operationen auf den Daten mit Hilfe der von mathematischen Methoden realisieren. Ebenso ist es sichergestellt, daß die Operationen auf den Relationen immer wieder Relationen zum Ergebnis haben. 1 Standard Query Language 16 2.3 KAPITEL 2. GRUNDLAGEN ÜBER DATENBANKEN Aktive Datenbanken Bei aktiven Datenbanken handelt es sich um eine Erweiterung des Datenmodells eines DBMS um aktive Regeln. Diese aktiven Regeln werden auch Trigger (trigger engl. für auslösen) genannt. Diese Regeln sollen dafür sorgen, daß beim Eintreten eines bestimmten Ereignisses eine vorgegebene Reaktion ausgelöst wird. Im Falle von Datenbankensystemen entspricht dies dem Ausführen von bestimmten Funktionen. Die Idee, die hinter diesem Konzept steckt, ist folgende: In der realen Welt, die durch das DBS modelliert werden soll, existieren häufig Regeln, die in bestimmten Situationen der realen Welt automatisch angewendet werden sollen. So könnte zum Beispiel ein Händler für Ersatzteile von Motorrädern direkt seinem Kunden eine Karte schicken, wenn das vom Kunden bestellte Ersatzteil im Lager eintrifft. Natürlich soll diese Karte nur dann verschickt werden, wenn das bestellte Ersatzteil und nicht ein beliebiges Ersatzteil eintrifft. Denkbar sind auch Anwendungen im Bereich der Finanzen, wo man automatisch bei einem bestimmten Aktienkurs eine Aktie kaufen oder verkaufen läßt oder eine Verwendung in der sogenannten Verkehrssünderkartei in Flensburg. Hat ein Autofahrer ein bestimmtes Limit an Punkten überschritten, wird der Fahrer automatisch in die Relation der lokalen Polizeidienststelle für den sofortigen Führerscheinentzug eingetragen. Bei den oben genannten Beispielen ist es immer so, daß die Datenbank selbständig auf eine neu eingetretene Situation hin, je nach Zustand der Datenbank, reagieren soll und aktiv wird. Dieses Prinzip läßt sich natürlich nicht nur auf Datenbanken übertragen, sondern auch auf andere Systeme. In jedem Bereich der Programmierung ist eine ereignisorientierte Programmierung denkbar. Wie man auch schon an den Beispielen erkennen kann haben die aktiven Regeln einen dreigeteilten Aufbau. Nach diesen drei Teilen nennt man die aktiven Regeln auch ECA-Regeln, wobei die Abkürzung ECA für Event (Ereignis), Condition (Bedingung) und Action (Aktion) stehen. Beim Eintreten eines festgelegten Ereignisses wird getestet, ob die Bedingung erfüllt ist. Ist dies der Fall wird die Aktion ausgeführt. Um bei Regeln mit gleichem Event-Teil eine eindeutige Reihenfolge der Regelausführung garantieren zu können, wird jeder Regel auch noch eine eindeutige Priorität mitgegeben. Anhand dieser Priorität kann dann hinterher eine Reihenfolge festgelegt werden in der die Regeln nacheinander ausgeführt werden. Um auf das Beispiel mit Händler für Ersatzteile zurückzukommen: Bei einer neuen Einlagerung (Event) wird überprüft ob das vom Kunden bestellte Ersatzteil dabei ist (Condition). Sollte dies der Fall sein, wird dem Kunden eine Information zugesandt (Action). 2.4. TRIGGER IN SQL3 17 Das Konzept der aktiven Regeln kann bei jeder Art von Datenbank verwendet werden. Im weiteren Verlauf werde ich mich aber auf die Verwendung von Triggern in relationalen Datenbanken, wie sie oben beschrieben wurden, beschränken. 2.4 Trigger in SQL3 In diesem Abschnitt wird die Behandlung von Triggern nach dem SQL3-Standard beschrieben. Da der Standard relativ jung ist, gibt es neben der Publikation des Standardisierungsgremium [ANSI] meist nur Literatur, die von Mitgliedern des Gremiums stammen. Hierbei erklärt [CPM] sehr anschaulich, unter anderem durch den Vergleich mit anderen Systemen, das Modell des Ablaufes von Triggern, während [KMC] eher einen praxisbezogeneren Zugang über mehrere Beispiele sucht. Eine gute Zusammenfassung und Einstieg in die Materie bietet [BP01]. In der Dissertation wird, aus Gründen der einfacheren Handhabung, ein normalisiertes SQL eingeführt. Für Rückgriffe auf SQL2 wurde [PT00] verwendet. Hier wird, laut Vorwort, zwischen CORE SQL als Sprachkern von SQL-99“ ” und darüber hinausgehenden Features unterschieden2 . Behandelt wird allerdings nur der Sprachumfang von CORE SQL, der aber nur Teile von SQL2 umfaßt und auch keine Trigger enthält. 2.4.1 Syntax Die Definition von Triggern erfolgt nach dem Standard durch einen SQL-Befehl, der mit dem Schlüsselwort Create eingeleitet wird. Folgendes Grundgerüst ist für die Definition vorgesehen: <trigger definition> ::= CREATE TRIGGER <trigger name> <trigger action time> <trigger event> ON <table name> [ REFERENCING <old or new values alias list> ] <triggered action> Hierbei steht <trigger name> für einen beliebigen Bezeichner, der den Trigger identifiziert. <table name> bezeichnet eine vorhandene Basisrelation der Datenbank. Die weiteren Variablen sind wie folgt festgelegt: <trigger action time> ::= BEFORE | AFTER 2 Begründet wird dieses Verhalten damit, daß es keine Implementation von SQL2 gab, die den vollen Funktionsumfang abbildet. Nun ist also nur noch ein geringer Sprachkern für SQL3 nötig, der mit zusätzlichen Featers des Standards erweitert werden kann. 18 KAPITEL 2. GRUNDLAGEN ÜBER DATENBANKEN <trigger event> ::= INSERT | DELETE | UPDATE [ OF <trigger column list> ] <triggered action> ::= [ FOR EACH { ROW | STATEMENT } ] [ WHEN <left paren> <search condition> <right paren> ] <triggered SQL statement> <triggered SQL statement> ::= <SQL procedure statement> | BEGIN ATOMIC { <SQL procedure statement> <semicolon> } ... END Dabei steht <trigger column list> für eine Liste von Spalten der betroffenen Tabelle ohne das dort Mehrfachnennungen der selben Spalte zulässig sind. <left paren> und <right paren> stehen für ein Klammernpaar, welches die <search condition> umschließt. Die <search condition> wird allerdings innerhalb der Triggerdefinition des Standards nicht weiter eingeschränkt, daher können hier durchaus auch komplexe Anfragen enthalten sein. Zu Beachten ist allerdings, daß für eine sinnvolle Auswertung der Bedingung die <search condition> lediglich einen Wahrheitswert zurückliefert und keine ganze Relation. Für den Bereich der referenzierten Tabellen <old or new values alias list> schreibt der Standard vor, daß für den Befehl Insert als <trigger event> weder die alte Tabelle noch die alte Reihe3 referenziert werden darf. Alt bedeutet hier die ursprünglichen Daten, wie sie vor dem Ausführen des Befehls vorhanden waren. Umgekehrt gilt für den Befehl Delete ein Verbot für die neue Tabelle und Reihe, also den Daten, die nach dem Ausführen des Befehls noch existieren. Die genaue Syntax der Definition lautet <old or new values alias> ::= OLD [ ROW ] [ AS ] <old values correlation name> | NEW [ ROW ] [ AS ] <new values correlation name> | OLD TABLE [ AS ] <old values table alias> | NEW TABLE [ AS ] <new values table alias>, wobei die Variablen die neuen Identifizierer für die jeweilige Reihe oder Tabelle darstellen. 3 im Sinne von ROW als Datenreihe/Datensatz der Tabelle 2.4. TRIGGER IN SQL3 19 <SQL procedure statement> stellt einen Aufruf einer definierten SQL-Prozedur dar und <semicolon> bezeichnet das Zeichen ;“, welches hier als Trenn” zeichen zwischen den einzelnen Prozeduraufrufen dient. Bei <SQL procedure statement> kann es sich aber auch durchaus um einen direkt angegebenen SQLBefehl handeln und muß sich nicht unbedingt um eine Stored-Procedure handeln. 2.4.2 Semantik Die auf eine Basisrelation abgesetzten Änderungsanweisungen werden von der Triggerkomponenten überprüft und die zu den Änderungen zugehörigen Trigger ermittelt. Der Zusammenhang zwischen einem Trigger und einer Datenänderung auf einer Relation wird in der Triggerdefinition festgelegt. Hier wird der Änderungsbefehl (Insert, Update oder Delete) und die Tabelle angegeben auf die die Änderungsoperation zugreifen muß, damit die Triggerkomponente diesen Trigger weiter bearbeitet. Man spricht in diesem Zusammenhang auch davon, daß ein Trigger feuert. Je nach festgelegtem Ausführungszeitpunkt wird der gefeuerte Trigger in die Warteschlange der Before- oder der After-Trigger eingefügt. Diese Einfügungen in die Schlangen geschieht nach den Prioritäten der jeweiligen Trigger, wobei der Trigger mit der höchsten Priorität am Anfang der Schlange steht. Die Prioritäten ergeben sich im SQL durch den Zeitpunkt, zu dem die einzelnen Trigger in die Datenbank eingetragen wurden, wobei der älteste Trigger die höchste Priorität erhält. Sollte allerdings ein Trigger neu hinzugefügt werden und eine höhere Priorität als die niedrigste erhalten, so ist es notwendig, die bisherigen Trigger zu verschieben4 . Anschließend kann der neue Trigger an der freien Stelle eingefügt werden. Bevor die Aktion eines Trigger ausgeführt werden kann, wird die Formel der When-Bedingung ausgewertet. Allerdings ist im Standard der Zeitpunkt dieser Auswertung nicht angegeben. Dieser kann also jederzeit zwischen bzw. während des Eintragens in die Warteschlangen bis hin zum Zeitpunkt des eigentlichen Ausführens des Triggers sein. Dadurch kann sich, je nach Zustand der Datenbank, im Ergebnis ein jeweils anderer Datenbank-Zustand ergeben. Dies liegt darin begründet, daß die Auswertung der Bedingung je nach Datenbank-Zustand unterschiedliche Ergebnisse liefern kann. Die eigentliche Ausführung der Aktion geschieht dann in der Reihenfolge Before-Trigger, auslösendes Ereignis mit Immediate-Integritätsprüfung und dann die After-Trigger, wobei die Before-Trigger 4 Da die Prioritäten durch das Datum der Erstellung des Triggers festgelegt werden, müssen hierfür im Endeffekt die alten Trigger erst gelöscht werden. 20 KAPITEL 2. GRUNDLAGEN ÜBER DATENBANKEN und After-Trigger in ihrer Reihenfolge durch die jeweiligen Erzeugungszeitpunkte festgelegt sind. Die Reihenfolge der Ausführung von Änderungsanweisungen und Triggern ist im Bild 2.2 aus [BP01] nochmal deutlich gemacht. Abbildung 2.2: Ausführungsreihenfolge von SQL3-Triggern Bei den Before-Triggern ist zu beachten, daß deren Aktionen keine weiteren Basisdaten ändern können, sondern nur den Transitionsvariablen neue Werte zuweisen können. In diese Variablen werden die Änderungen gespeichert, die die Aktionen der Before-Trigger und das auslösende Ereignis erzeugen. Die Variablen stellen dann die effektiven Änderungen dar und können auf die Relation übertragen werden. Durch das Verwenden der Transitionsvariablen können durch Before-Trigger keine neuen Trigger ausgelöst werden. Dem gegenüber können die Aktionen der After-Trigger sehr wohl weitere Trigger auslösen. Daher werden diese fast so ausgeführt wie normale Änderungsanweisungen. Der einzige Unterschied besteht nur in der Verwaltung der Liste der gefeuerten After-Trigger. Diese Liste wird nur für Aktionen geführt, die nicht von Triggern ausgelöst worden sind. Löst die Aktion eines After-Triggers einen weiteren After-Trigger aus, so wird dieser in die ursprüngliche Liste der gefeuerten After-Trigger nach seinem jeweiligen Zeitpunkt der Erzeugung einsortiert. Somit wird für jede Änderung eine eigene Before-Liste verwaltet, aber nur eine globale After-Liste. Diese Liste der noch nicht ausgeführten After-Trigger ist ein Bestandteil des auslösenden Ereignisses, durch das die Trigger ausgelöst worden sind. Sollte die Änderungsanweisung, oder ein durch sie ausgelöster Trigger, fehlschlagen bzw. durch die Integritätsprüfung zurückgewiesen werden, so werden die bisherigen Änderungen rückgängig gemacht. Dabei ist aber zu beachten, daß die Integritätsprüfung unter SQL zu zwei verschiedenen Zeitpunkten stattfinden kann. Wird eine Aktion aufgrund einer immediately-Prüfung zurückgewiesen, wird nur die Aktion selbst rückgängig gemacht. Wird aber die gesamte Transaktion aufgrund einer deferred-Prüfung zurückgewiesen, müssen alle Änderungen zurückgenommen werden die zu dieser Transaktion gehörten. 2.5. AUFBAU VON MICROSOFT ACCESS 97 2.5 21 Aufbau von Microsoft Access 97 Bei Microsoft Access 97 oder nur kurz Access handelt es sich um ein relationales DBMS, welches von der Firma Microsoft konzipiert wurde, damit Anwender leicht und flexibel ihre Daten lokal verwalten können. Um diese Ziele erreichen zu können, wurden ein paar Einschränkungen gegenüber dem Leistungsumfang gemacht. Trotzdem handelt es sich bei Access um ein recht leistungsfähiges DBMS für private und kleine geschäftliche Belange. Die interne Versionsnummer, die Microsoft in diesem Produktzyklus nicht durchgängig5 genutzt hat, trägt die Nummer 8.0, wobei es keine weiteren Versionen der Reihe 8 gibt, sondern lediglich zwei Service Releases zur Fehlerbehebung. Einen guten Überblick über die Funktionsvielfalt gibt [MS96], eine detaillierte Erklärung findet sich bei [HS97]. Bei [MS97] findet man eine allgemeine Beschreibung von VBA. Access legt alle Informationen, die zu einer Anwendung gehören, normalerweise in einer Datei mit der Endung .mdb“ ab. In dieser Datenbankdatei werden ” somit üblicherweise die Anwendung und die anwendungsbezogenen Daten abgelegt und verwaltet. Daher ist es möglich durch Kopieren einer Datei sowohl Anwendung als auch Daten auf ein anderes System zu portieren. Voraussetzung, um die Anwendung auf einem anderen Rechner nutzen zu können, ist dann lediglich eine Installation von Access. Somit ist die von Access verwendete Datei keine Datenbank im Sinne der oben diskutierten Datenbanksystemen. Da aber die Funktionalität einer Datenbank vorliegt, soll hier auch weiterhin von einer Datenbank die Rede sein. Dies deckt sich auch mit der Literatur zum Thema Access. Um die Wartung bei kommerziellen Anwendungen zu erleichtern, wird die Datenbank vor der Auslieferung an einen Kunden in zwei Teile geteilt. Hierbei werden die Daten in eine separate Datenbank verschoben und aus dem Teil mit der Anwendung entfernt. Um die Möglichkeit des Zugriffes auf die Tabellen wieder zu gewährleisten, wird mittels Referenzen die Datenbank mit den Tabellen in die Datenbank mit der Anwendung eingebunden. So läßt sich dann später nur die Datenbank mit der Anwendung austauschen, ohne daß die Daten gelöscht werden bzw. erst in die neue Datenbank übertragen werden müßten. An technischen Beschränkungen weist Access eine Größenbeschränkung von maximal 2 Gigabyte pro Datenbank auf. Aber auch bei heutigen Rechnerleistungen6 ist das Öffnen einer Datenbank mit nur“ einem Gigabyte mit zu langen ” Wartezeiten versehen. Um mit dem DBS noch arbeiten zu können, also es für 5 so wurde von Access 2 auf Access95 gewechselt, welches aber die interne Versionsnummer 7.0 trägt 6 Zum Zeitpunkt der Erstellung der Diplomarbeit waren die Standardrechner der Computermärkte mit 128 bis 256 MB RAM und rund 30 bis 50 GB Festplatte ausgerüstet. Bei den Prozessoren wurden Intel Pentium IV mit 1,4 GHz bzw. AMD Athlon mit 1,7 GHz verbaut. 22 KAPITEL 2. GRUNDLAGEN ÜBER DATENBANKEN seine eigentlich geplante Aufgabe zu nutzen, ist eine Dateigröße von 500 Megabyte als zumutbare Obergrenze anzusehen. Daher sind Anwendungen größerer Art nicht mit Access realisierbar. Zusätzlich zur Größenbeschränkung haben die Entwickler von Access nur ein Einzelplatzsystem geplant. Das DBMS verfügt also über keine Komponenten, die konkurrierende Datenzugriffe erlauben oder verwalten würden. Es existiert nur ein System zum Sperren von Daten, um einen Schutz vor einem gleichzeitigen Schreibzugriff zu realisieren. Dieser Schutz ist aber auch bei einer Einzelplatzanwendung eher hinderlich, da nicht auf Datensatzebene die Daten gesperrt werden, sondern in Blöcken zu rund zwei Kilobyte. Daher kann es zu Problemen kommen, wenn zwei Datensätze gleichzeitig zum Schreiben geöffnet werden sollen und diese beide im selben Schreibschutzblock von zwei Kilobyte liegen. Hier hat sich mittlerweile eine Vorgehensweise entwickelt, um dieses Problem zu umgehen, die die Datenbank allerdings mehr als nötig vergrößert. Zur Problemlösung wird die Struktur der Relationen (hier Tabelle genannt) so vergrößert, daß sie ein vielfaches von zwei Kilobyte betragen und somit keine zwei Datensätze in dem selben Sperrblock liegen können. Neben der Möglichkeit, die Tabellen direkt an ein Formular zu binden und so Datenmanipulationen grafisch aufbereitet vorzunehmen, sind auch programmgesteuerte Manipulationen möglich. Zum einem wird die Programmiersprache VBA7 von Access zur Verfügung gestellt, über die cursorbasierte Änderungen mittels RecordSets möglich sind. Zum anderen wird eine Implementierung von SQL zur Verfügung gestellt, die als Embedded-SQL über VBA-Funktionen genutzt werden können. Ein Fenster zur direkten Eingabe von SQL-Befehlen existiert nicht, wohl aber ein Direktfenster, in dem VBA-Befehle direkt ausgeführt werden können. So ist es möglich, über die VBA-Funktion DoCmd.RunSQL() direkt SQL-Befehle durchführen zu lassen. Bei dem verwendeten SQL handelt es sich um eine teilweise Realisierung von SQL2. Hier wurden allerdings auch wieder Zugeständnisse gemacht, die sich aus den geplanten Leistungsspektrum wohl so für die Entwickler ergeben haben. So wird zum Beispiel der Datentyp Decimal oder eine Verknüpfung als outer join in Access nicht unterstützt. Auch sind nicht beliebig tiefe Verschachtelungen erlaubt, und die Anzahl der Tabellen einer Abfrage ist auf 32 beschränkt. Bei komplexeren Anfragen kann die Begrenzung auf maximal 40 Ands in einer Whereoder Having-Klausel schon eine Aufteilung der Abfrage auf zwei Abfragen verursachen. Problematischer sind allerdings die Probleme die entstehen, wenn Sequenzen benötigt werden, da auch diese nicht unterstützt werden. 7 Visual Basic for Applications 2.5. AUFBAU VON MICROSOFT ACCESS 97 2.5.1 23 Access und die Jet-Engine Genauso wie beim Leistungsumfang gibt es auch beim Design einige Besonderheiten. Ein DBS auf Basis von Access läßt sich von seiner Struktur her in die drei Teile Access als grafische Oberfläche, die Jet-Engine und die Datenbank aufteilen. Eine Aufteilung des DBMS Access in zwei Teile ist allerdings so nicht möglich, da einige grundlegende Funktionen mit der Datenbank verbunden sind. Abbildung 2.3: Architektur von Access Der erste Teil der Struktur von Access sorgt etwas für Verwirrung, da der Teil, den man als Access kennt, nur eine grafische Schnittstelle für den Benutzer darstellt. Es dient dem Benutzer nur dazu, eine grafische Zugriffsmöglichkeit auf die Objekte der Datenbank zur Verfügung zu stellen. Eine Übersicht, wie die einzelnen Komponenten von Access zusammenarbeiten gibt Abbildung 2.3. Das eigentliche DBMS stellt die Jet-Engine dar, die von der grafischen Oberfläche Befehle zur Datenmanipulation erhält. Die Jet-Engine ist in Form von eigenständigen DLL-Dateien8 aufgebaut und wird auch in Programmiersprachen wie VisualBasic von Microsoft zur Anbindung von Datenbanken an eine Anwendung benutzt. Sieht man Access im Kontext der Definition eines DBMS ganz streng, so ergibt sich hieraus, daß die grafische Oberfläche nur eine Anwendung ist, die auf das DBMS zugreift. Diese Sichtweise dürfte aber in diesem Fall zu eng gesteckt sein, da zu einem DBMS auch eine Anwendung zur Verwaltung, Wartung und Pflege der Datenbanken gehört. Dies stellt aber eben Access durch seine grafische Oberfläche zur Verfügung. 8 Dynamic Link Library, ein System unter Windows, das es ermöglicht, daß eine Funktion von mehreren Programmen genutzt werden kann und erst bei Bedarf gelinkt“ werden ” 24 KAPITEL 2. GRUNDLAGEN ÜBER DATENBANKEN Den dritten Teil der oben erwähnten Aufteilung stellt die Datenbank selbst dar. Eine Besonderheit dürften hier allerdings die gespeicherten Objekte sein. Neben den Tabellen der Anwendung und den Systemtabellen werden hier auch alle Objekte der Anwendung selbst abgelegt. Abbildung 2.4: Datenbankfenster einer Anwendung Unter Access kann eine Anwendung aus insgesamt sechs verschiedenen Objektarten bestehen, wie man bereits am Datenbankfenster in Abbildung 2.4 sehen kann: • Tabellen: Die Tabellen stellen die Relationen der Anwendung dar und dienen zum Speichern der anwendungsbezogenen Daten. Es existieren auch von Access angelegte Systemtabellen, die zwar teilweise auch gelesen, aber nicht verändert werden können. Auch deren Semantik ist nicht weiter dokumentiert. Bei einer Nutzung können nur bereits entschlüsselte Komponenten sinnvoll verwendet werden. So bietet zum Beispiel die Tabelle MSysRelationships eine Übersicht über die definierten Beziehungen zwischen den Tabellen. • Abfragen: Eine Abfrage stellt eine SQL-Anweisung dar, die programmgesteuert ausgeführt werden kann oder vom Nutzer manuell aufgerufen wird. Die Eingabe kann mittels einer grafischen Oberfläche zusammengestellt werden, es ist aber auch die direkte Eingabe von Befehlen in SQL-Syntax möglich. Es sind sowohl Abfragen zur Manipulation der Daten, als auch Abfragen zur Zusammenstellung und Anzeige von Daten möglich. Die Anzeige ist auch unter dem Begriff View bekannt. 2.5. AUFBAU VON MICROSOFT ACCESS 97 25 • Formulare: Ein Formular dient der grafischen Gestaltung einer Anwendung. Es kann sowohl funktionale Elemente wie Buttons enthalten, aber auch Elemente zur Daten Ein- und Ausgabe. Ein typisches Element, das die Einund Ausgabe von Daten unterstützt, ist das Textfeld. Neben der Definition des grafischen Aussehens kann ein Formular auch Funktionen enthalten, die von Access unter bestimmten Bedingungen aufgerufen werden. So wird zum Beispiel die Funktion Private Sub Form KeyPress(KeyAscii As Integer) automatisch aufgerufen, wenn das Formular den Fokus hat und eine Taste, deren ASCII-Wert in der Variable KeyAscii übergeben wird, gedrückt wird. Die Funktionen werden in einem Modul (siehe unten) in dem Formular gespeichert, sind aber nicht als eigenständige Module im Datenbankfenster sichtbar. • Berichte: Berichte dienen unter Access dazu, Informationen zur Ausgabe aufzubereiten. Die Ausgabe kann sowohl auf dem Bildschirm geschehen als auch auf einem Drucker ausgegeben werden. Im Gegensatz zu den Formularen ist keine Dateneingabe möglich. Hauptaufgabe ist die Ausgabe der Daten auf einem Drucker, was durch Access auch insofern unterstützt wird, daß bei einer programmgesteuerten Öffnung eines Berichtes die Standardausgabe der Drucker ist. Daher ist auch zu erklären, warum ein Bericht unter Access beim Erstellen zuerst die Farbe Weiss für den Hintergrund zugeordnet bekommt. Genauso wie ein Formular kann ein Bericht Funktionen enthalten, die situationsabhängig ausgeführt werden. Als Beispiel sei hier die Funktion Private Sub Report Page() genannt, die automatisch beim Erstellen einer neuen Seite aufgerufen wird. • Makros: Bei einem Makro handelt es sich um ein relativ einfaches Programm verglichen mit den Möglichkeiten, die Module bieten. Ein Makro wird aus Befehlen zusammengesetzt, die in einem Auswahlfeld zur Verfügung stehen. Dort sind insgesamt 49 Befehle aufgelistet, die mit unterschiedlichen Parametern versehen werden können. Da aber bei Makros keine Variablen unterstützt werden, ist der Einsatzbereich meist auf den des AutoexecMakros beschränkt, welches beim Laden der Datenbank von Access automatisch ausgeführt wird. Hier werden dann in der Regel nur Funktionen zur Initialisierung und ein Formular mit einem Menü aufgerufen. • Module: Ein Modul enthält VBA-Funktionen und Variablen, die von anderen Funktionen aus genutzt werden können, wenn sie nicht mit dem Schlüsselwort Private deklariert werden. Dann sind diese Elemente nur innerhalb dieses Moduls nutzbar. Bei der Nutzung der Module als Container für Funktionen handelt es sich um die normale“ Nutzung der Mo” dule. Zusätzlich zu den normalen“ Modulen gibt es auch noch Klassen” module. Hierbei handelt es sich um Module mit einer Objektstruktur, wie man sie von objektorientierten Programmiersprachen her kennt. Leider ist 26 KAPITEL 2. GRUNDLAGEN ÜBER DATENBANKEN der Funktionsumfang nicht mit dem einer OO-Programmiersprache zu vergleichen. So ist das Prinzip der Vererbung nicht realisiert worden, es gibt aber zumindest die Möglichkeit von einem Klassenmodul mehrere Instanzen gleichzeitig zu nutzen. Bei den Funktionen aus den Modulen der Formulare und Berichte handelt es sich dann auch um Funktionen aus Klassenmodulen, wobei die Klasse das jeweilige Formular oder der Bericht ist. Durch die Verwendung von Klassen erreicht man so eine Zusammenfassung von Funktionen, die nur als Funktionen eines Objektes einen Sinn ergeben. So wird zum Beispiel die Funktion KeyPress von Access einem Formular zugeordnet und ist bei einer Tabelle nicht verfügbar. Neben den oben erwähnten Klassenmodulen von Formularen und Berichten stellt Access weitere Klassen in VBA zur Verfügung. Im weiteren Verlauf der Diplomarbeit sind die Klassen Database und DoCmd wichtig. Mit Hilfe der Klasse Database kann auf die Elemente einer Datenbank zurückgegriffen werden. Dabei sind zum Beispiel auch Zugriffe auf Quelltext in Modulen und Formularen möglich. Hierbei beschränkt sich die Art des Zugriffes nicht auf reine Leseoperationen, sondern es sind auch Änderungen möglich. Die Klasse Database stellt zusätzlich noch den Datentyp RecordSet und Operationen hierauf zur Verfügung, über die sich dann auch die Tabelleninhalte ändern lassen. Diese Datenänderungen finden allerdings ausschließlich cursorbasiert statt. Durch die Klasse DoCmd stellt Access dem Anwender eine Möglichkeit zur Verfügung in VBA Befehle auszuführen, die sonst nur in Makros oder über Menüs zur Verfügung stehen. 2.5.2 Konventionen der Namensgebung Für die Wahl der Namen für die einzelnen Objekte gibt es unter Access kaum Einschränkungen. Es sind sowohl Leerzeichen, als auch einige Sonderzeichen und Umlaute erlaubt. Für die spätere Anwendung ist es jedoch sinnvoll, wenn auf Umlaute und Sonderzeichen verzichtet wird und die Leerzeichen durch einen Platzhalter, wie zum Beispiel dem Zeichen “, ersetzt werden. Verzichtet man nicht ” auf die Möglichkeit Sonder- und Leerzeichen zu verwenden, so kann es innerhalb von VBA-Funktionen und SQL-Anweisungen nötig sein, die Objektnamen zur eindeutigen Identifizierung in eckige Klammern zu setzen. Eine Besonderheit bei der Namensgebung weist noch die Verwendung der Präfixe MSys“ und USys“ auf. Mit dem Präfix MSys“ kennzeichnet Access ” ” ” die eigenen Systemtabellen und mit dem Präfix USys“ können Objekte angelegt ” werden, die wie Systemobjekte gehandhabt werden. Dies bedeutet, daß USys“” Objekte nur dann im Datenbankfenster eingeblendet werden, wenn in den Optionen es so ausgewählt wurde, wie es in Abbildung 2.5 angegeben ist. In der 2.5. AUFBAU VON MICROSOFT ACCESS 97 27 Abbildung 2.5: Optionen in Access Abbildung sind auch noch zusätzlich die ausgeblendeten Objekte zur Anzeige ausgewählt worden. Ob ein Objekt ausgeblendet werden soll, läßt sich für jedes Objekt einzeln in seinen Eigenschaften festlegen. Trotz der absoluten Freiheit in der Namensgebung für die einzelnen Objekte haben sich mehrere, teilweise auch sehr unterschiedliche, Konventionen eingebürgert, wie die einzelnen Objekte zu benennen sind. Es gibt sowohl Varianten in denen Postfixe oder Präfixe verwendet werden um die Objekte zu kennzeichnen, oder aber auch nur Überlegungen eindeutige Namen zu vergeben, die sprechend genug sind, um den Objekttyp daraus herleiten zu können. Im weiteren Verlauf der Arbeit werden zur eindeutigen Identifizierung der einzelnen Objekte folgende Postfixe für die Objekte verwendet: • Tabellen: • Abfragen: ” tbl“ (abgeleitet von table) qry“ (abgeleitet von query) ” • Formulare: frm“ (abgeleitet von form) ” • Berichte: rep“ (abgeleitet von report) ” • Module: mdl“ (abgeleitet von modul ) ” 28 KAPITEL 2. GRUNDLAGEN ÜBER DATENBANKEN Kapitel 3 Konzept einer Erweiterung von Access um Trigger In diesem Kapitel wird zunächst die Definition einer Triggersprache behandelt, die sich in Access einfügt. Dabei wurden sowohl die Trigger aus SQL3 berücksichtigt, als auch die Gegebenheiten, die sich durch die Syntax und Semantik von SQL und VBA innerhalb von Access ergeben. Die sich daraus ergebende Definition von Triggern ist zwar von SQL3 abgeleitet, kann aber, aufgrund von Einschränkungen, nicht mehr als Trigger nach dem SQL-Standard bezeichnet werden. Diese Einschränkungen ergeben sich bei der Verwendung von Access dadurch, daß es nicht möglich ist, genug Einfluß auf die interne Verarbeitung der Datenmanipulation durch Access zu nehmen. Ebenso sorgen Erweiterungen der zu definierenden Triggersprache dafür, daß nicht mehr von SQL3-Triggern gesprochen werden kann, da diese Erweiterungen die Trigger um eine Vielzahl von Möglichkeiten1 erweitern. Anschließend wird es um die Möglichkeiten gehen, die sich bieten, das Datenbanksystem Access im Allgemeinen erweitern zu können. Diskutiert werden hierbei auch Wege, die aufgrund ihrer späteren unhandlichen“ Nutzung ausfal” len. Damit ist zum Beispiel die Möglichkeit gemeint, bei der die Kommunikation zwischen der Erweiterung und deren Anwender über eine Tabelle abgewickelt wird, also eine Tabelle als Schnittstelle benutzten. Somit wäre für Access ein völlig neues Konzept der Übergabe von Befehlen nötig, die es in dieser Form bisher noch nicht in Access gab. Die Erweiterung würde sich also nicht mehr in das vorhandene System einpassen. 1 Da eine Erweiterung um den Sprachbestand von VBA geschieht, ist hier keine genauere Abschätzung möglich. 29 30 KAPITEL 3. KONZEPT EINER ERWEITERUNG 3.1 Entwurf einer Triggersprache für Access Von Grund auf lassen sich für eine mögliche Triggersprache für Access keine Einschränkungen machen. Man könnte hier also frei aus allen bereits vorhandenen Konzepten wählen oder aber ein eigenes Konzept entwickeln. Dies betrifft sowohl die Syntax als auch die Semantik. Da sich dieses Konzept für Trigger von anderen Konzepten unterscheiden wird, wird im weiteren Verlauf von ARA-Triggern die Rede sein, wenn es um ein mögliches Konzept für eine Triggersprache in Access geht. Da Access bereits eine Abfragesprache unterstützt, die auf SQL beruht, liegt es nahe, auch die ARA-Trigger aus diesem Konzept zu nehmen bzw. sie als Grundlage für weitere Anpassungen zu verwenden. Dies hat den Vorteil, daß Literatur zu diesem Konzept existiert, und daß diese bereits in einigen anderen DBMS so oder mit leichten Variationen2 umgesetzt worden ist. Hierdurch ist auch keine große Umgewöhnung für den Anwender nötig, der zwischen den Systemen wechselt. Würde man ein Konzept entwickeln, welches zum Beispiel auf Basis der formulargebundenen Eingabe beruht und das dann eventuell nicht nur auf Datenänderungen reagiert, sondern sogar auf einzelne Tastendrücke oder Mausbewegungen, so würde dies bestimmt auch ein leistungsfähiges Konzept ergeben. Es würde aber dazu führen, daß der Anwender einem völlig neuen Konzept gegenübersteht. Dieses Konzept würde der Anwender zum einen nicht kennen und sich einarbeiten müssen, zum anderen erhält er ein System, welches so nicht auf andere DBMS portiert werden kann. Daher ist ein Konzept auf Basis der SQL-Trigger am sinnvollsten, da man hier, von einer gewissen Abstraktion abgesehen, den Umgang mit diesem Konzept bereits von anderen DBMS kennt. Der Ansatz auf Basis der formulargebundenen Eingabe wird auch in [K00] verfolgt, in dem der Weg beschrieben wird Insert-, Update- und Delete-Trigger über Funktionen des Eingabe-Formulars zu realisieren. Dabei werden die FormularFunktionen von Access genutzt, die bei vorgegebenen Aktionen benutzerdefinierte Funktionen ausführen können. Diese Art der Trigger haben allerdings den Nachteil, daß sie nicht universell nutzbar sind, sondern für jedes Formular neu definiert und geschrieben werden müssen. Ebenso sind diese Trigger nicht für programmgesteuerte Datenmanipulationen3 zu nutzen. Es erfolgt auch keine automatische Prüfung der Manipulationen durch einen Triggermanager oder Access, sondern es werden lediglich 2 vergl. die unterschiedliche Umsetzung von Triggern in den DBMS DB/2 von IBM und Oracle mit Oracle8 3 SQL-Befehle oder RecordSet-Manipulationen in VBA-Funktionen 3.1. ENTWURF EINER TRIGGERSPRACHE FÜR ACCESS 31 Funktionen, die an das entsprechende Formular gebunden sind, bei Änderungen durch das Formular aufgerufen. Bei dieser Art der Realisierung von Triggern, wäre aber die Erkennung der Änderungen und das Feststellen der ausgelösten Trigger vom Anwender jedesmal neu zu implementieren. Es gibt hierbei keine zentrale Funktion, die die Programmausführung übernehmen könnte, um die Änderungen der Daten und das Ausführen der Trigger zu übernehmen und zu überwachen. Es müßte auch für jedes Formular eine eigene Überwachung der Datenänderung implementiert werden. Somit wäre es nicht möglich an einer zentralen Stelle Trigger für alle Formulare zu definieren und deren Ausführung zu überwachen. Wie bereits in Kapitel 2.5 beschrieben, handelt es sich bei dem in Access verwendeten SQL nicht um eine vollständige Implementierung des SQL2-Standards. Daher ist es notwendig Einschränkungen am Funktionsumfang der Trigger im Vergleich mit den Triggern aus dem SQL-Standard zu machen. Diese Einschränkungen beziehen sich sowohl auf die Syntax, als auch auf die Semantik der ARATrigger, stellen aber für Access immer noch eine sinnvolle Ergänzung um Trigger dar. Da man den SQL-Standard jedoch verläßt, kann man noch zusätzliche Ideen in das Konzept einbringen. Es lassen sich auch Erweiterungen in die Sprache der ARA-Trigger einfügen, die sich hervorragend in das typische Verhalten von Access eingliedern. So ist es bisher unter Access möglich, in SQL-Anfragen VBAFunktionen zu verwenden. Geht man hier noch einen Schritt weiter, dann lassen sich VBA-Funktionen auch ohne eine SQL-Umgebung nutzen. Wie dies das Triggerkonzept unter Access im Vergleich zu SQL3-Triggern erweitern kann, werde ich später erläutern. Genauso wie im Standard, sind die ARA-Trigger nach dem ECA-Prinzip in drei Teilen Event, Condition und Action aufgebaut. In dem Abschnitt, der als Event bezeichnet wird, wird festgelegt, auf welches Ereignis in welcher Tabelle der ARA-Trigger reagieren soll und wann er reagieren soll. Als mögliche Ereignisse stehen hier die SQL-Befehle Insert, Update und Delete zur Verfügung. Natürlich können diese SQL-Befehle auch durch Absetzen anderer Kommandos simuliert werden. So entspricht das Anlegen eines neuen Datensatzes mit Hilfe von RecordSet-Operationen einem Insert. Die festlegbaren Ausführungszeitpunkte entsprechen auch wieder dem, was man schon aus dem Standard kennt. Der ARA-Trigger kann entweder vor (before) oder nach (after ) der eigentlichen Datenmanipulation ausgeführt werden. In dem Teil, in dem die Condition angegeben wird, ist neben einer normalen SQL-Bedingung bei den ARA-Triggern auch eine VBA-Funktion4 zugelas4 Für die Syntax wurde hier die Objektschreibweise, wie sie in Access üblich ist, verwendet. 32 KAPITEL 3. KONZEPT EINER ERWEITERUNG sen, die als Rückgabewert den Datentyp Boolean liefert. Hierdurch sind nun auch Bedingungen möglich, die den Leistungsumfang vom Access-SQL sprengen würden. So ist es nun zum Beispiel möglich, mittels API5 -Funktionen in VBA den aktuell angemeldeten Windows-Nutzer auszulesen und mit dem übergebenden Namen auf Gleichheit zu testen. Ein gute Einführung in das Thema der API-Programmierung ist bei [A00] oder aber auch bei [P01] zu finden. Ebenso könnten über diese VBA-Funktionen temporale Bedingungen realisiert werden, wenn es zu Vergleichen mit der aktuellen Systemzeit oder anderen Systemdaten kommen soll. Allerdings sollten diese Funktionen keine Seiteneffekte durch Manipulieren von Daten aufweisen, da es sich hier ja lediglich um eine Überprüfung einer Bedingung handelt. Hier ist der Anwender gefordert die nötige Disziplin walten zu lassen. Die Vielfalt an Möglichkeiten, die sich durch diese Erweiterung bietet, läßt sich hier durch die erwähnten Beispiele nur erahnen, wird aber wohl noch durch die unten besprochene Erweiterung der Actions übertroffen. Damit die VBA-Funktionen bei der Ausführung auch vom System gefunden werden können, ist es wichtig, daß sie in einem Modul innerhalb Access vorhanden und anwendungsweit erreichbar sind. Sowohl Klassenmodule als auch formulargebundene Funktionen können nicht aufgerufen werden, da sie für Access nicht immer erreichbar sind. Funktionen in Formularen zum Beispiel sind nur dann aufrufbar, wenn das Formular geöffnet ist. Ebenso sind als Private deklarierte Funktionen nur von Funktionen innerhalb des selben Moduls erreichbar. Für eine leichtere Wartung und zur besseren Übersicht ist es sogar empfehlenswert, die Funktionen die von ARA-Triggern aufgerufen werden können, in einem eigenen Modul zusammenzufassen. Hierbei kann es sich aber nur um eine Empfehlung handeln, deren Nutzen der Anwender für sich selbst entscheiden und auch dementsprechend umsetzen muß. Im letzten Abschnitt der ARA-Trigger, in dem die sogenannte Action angegeben wird, gelten ähnliche Beschränkungen wie für den Teil der Angabe der Condition. Es ist allerdings zu beachten, daß Access durch seinen relativ geringen SQL-Sprachumfang einige Beschränkungen für die auszuführende Aktion erfordert. So ist es nicht möglich, Sequenzen von Änderungsanweisungen anzugeben und ausführen zu lassen. Da aber auch hier VBA-Funktionen zugelassen sind, läßt sich diese Beschränkung leicht umgehen. Des weiteren sind durch die Funktionen auch Ausgaben auf dem Bildschirm möglich, um so zum Beispiel auf versäumte Fristen hinzuweisen oder dergleichen. Durch den in Kapitel 3.2.4 beschriebenen Aufbau der Systemerweiterung wird Eine Funktion Check() ist daher mittels VBA.Check() aufzurufen. 5 Application Programming Interface - Eine Schnittstelle, die es erlaubt auf Funktionen von Windows zurückzugreifen 3.1. ENTWURF EINER TRIGGERSPRACHE FÜR ACCESS 33 während der Ausführung der Funktion keine weitere Datenmanipulation im Hintergrund durchgeführt. Werden im Programmcode nur MsgBox 6 -Befehle verwendet, um Informationen anzuzeigen, dann wird der Programmablauf von Access angehalten, bis die jeweilige Messagebox geschlossen wurde. Dadurch ist dann auch sichergestellt, daß der Bildschirm nicht mit mehreren Nachrichten in undefinierbarer oder für den Nutzer in einer nicht mehr logischen Reihenfolge überfüllt wird. Zusätzlich zur Ausführung von Befehlen zur Datenmanipulation kann bei Before-Triggern noch ein Abbruch aller ARA-Trigger und der auslösenden Aktion hervorgerufen werden. Der Befehl Avoid im Bereich Action bewirkt einen Rollback aller Änderungen die durch die auslösende Aktion und ihrer Before-Trigger hervorgerufen wurden. Die Beschränkungen, den die Before-Trigger aus dem SQL-Standard unterliegen, gelten analog für die ARA-Trigger. So können die Before-Trigger nur auf die durch das auslösende Ereignis manipulierten Daten zurückgreifen und diese nochmals ändern. Wichtig ist auch noch zu beachten, daß es unter Access keine Möglichkeit gibt, den Zeitpunkt der Integritätsprüfung festzulegen. Es werden alle Aktionen auf der Datenbank sofort auf Einhaltung der Integrität geprüft (immediately) und gegebenenfalls abgelehnt. Es ist somit also nicht, wie bei SQL3-Triggern, möglich einen Trigger zu definieren, der eine Verletzung der Integrität vor der eigentlichen Überprüfung durch das System korrigiert, wie dies bei einer deferred-Überprüfung möglich wäre. Wie später in Kapitel 4.2.2 diskutiert, erfolgt bei den Triggern unter Access dann auch kein Rollback aller Trigger und der auslösenden Aktion, falls die letzte Aktion fehlschlagen sollte. Im Gegensatz zu den Triggern aus dem SQL3-Standard erhalten die ARATrigger aber nicht ihre Priorität ausschließlich über den Zeitpunkt ihrer Erstellung. Stattdessen wird hier mit einer Numerierung der Trigger gearbeitet, wobei die Zahl 1 die höchste Priorität darstellt. Auch ein nachträgliches Ändern der Prioritäten ist zulässig. Wird ein Trigger mit einer bereits vorhandenen Priorität eingefügt, so werden die Prioritäten neu vergeben. Dies geschieht so, daß die Reihenfolge in der Prioritätenliste der bereits vorhandenen Trigger beibehalten wird und der einzufügende Trigger vor dem Trigger liegt, der vor dem Einfügen dieselbe Priorität hatte. Daraus ergibt sich folgender Algorithmus zur Einfügung eines neuen Triggers: 6 VBA-Befehl, der eine Dialogbox mit freiem Text und wählbarer Anzahl an Buttons anzeigt 34 KAPITEL 3. KONZEPT EINER ERWEITERUNG IF Trigger_neu.Priority <= MAX(Trigger.Priority) THEN Trigger = Trigger mit Priority = max(Trigger.Priority) WHILE Trigger_neu.Priority <= Trigger.Priority Trigger.Priority = Trigger.Priority + 1 Trigger = Trigger mit Priority = Trigger.Priority - 2 WEND FI INSERT Trigger_neu Auch wenn die ARA-Trigger nach den SQL-Triggern entworfen worden sind, läßt sich keine direkte Eingabesyntax angeben, da die Eingabe der ARA-Trigger in einem Formular stattfindet. Es ergibt sich aber folgender Aufbau, der die ARATrigger beschreibt und einige Ähnlichkeiten mit den in Kapitel 2.4 besprochen SQL-Triggern aufweist: <TID> [<Priority>] [<Triggername>] E: {Before|After} {Update|Insert|Delete} <Table> C: [<Condition>] A: <Action> <Created> <Modified> Wobei die Schlüsselwörter folgende Bedeutung haben: • <TID> Trigger identifier, der von Access automatisch vergeben wird und nicht mit angegeben werden kann. Er dient zur internen Verwaltung der Trigger. • <Priority> Priorität, die der Trigger erhalten soll, wenn er in ARA eingefügt wird. Dabei kann es sich um einen bereits vergebenen Wert handeln, da vorhandene Trigger, von den Prioritäten her, nach hinten geschoben werden. Ein Weglassen der Angabe würde zu einer Priorität führen, die um eins höher liegt, als die höchste bereits vergebene Priorität. • <Triggername> Name des Triggers, der es dem Anwender ermöglichen soll, die Trigger anhand von sprechenden Namen zu unterscheiden. Eine Angabe ist nicht nötig, erleichtert aber die Wiedererkennung des Triggers für den Anwender. • <Table> von der Aktion betroffene Tabelle • <Condition> Bedingung, unter der die eigentliche Aktion ausgeführt werden soll. Dies entspricht einer Where-Bedingung einer SQL-Anweisung oder ist eine VBA-Funktion mit Rückgabe eines Wahrheitswertes, die mit dem Schlüsselwort VBA. eingeleitet wird. 3.2. PRINZIPIELLE ERWEITERUNGSMÖGLICHKEITEN IN ACCESS 35 • <Action> Aktion die ausgeführt werden soll, wenn ein bestimmtes Ereignis eingetreten ist und bestimmte Bedingungen gelten. Es kann sich hier um eine SQL-Anweisung in Access-Syntax handeln, oder aber um eine VBAFunktion, die mit dem Schlüsselwort VBA. eingeleitet wird. • <Created> Zeitpunkt des Anlegens des Triggers. Dieser Wert kann nicht angegeben werden, sondern wird von dem Eingabeformular vergeben. Die Speicherung dient der Feststellung, wann ein Trigger in ARA eingefügt wurde, um Daten identifizieren zu können, die vor der Eingabe des Triggers schon eingegeben wurden. • <Modified> Zeitpunkt der letzten Änderung des Triggers. Dieser Wert kann nicht angegeben werden, sondern wird von dem Eingabeformular vergeben. Die Speicherung dient der Feststellung, wann ein Trigger zuletzt geändert wurde, um Daten identifizieren zu können, die vor der Änderung des Triggers schon eingegeben wurden. Dies entspricht weitgehend dem Aufbau der SQL-Trigger, wie er bereits in Kapitel 2.4 beschrieben wurde. Zweckentfremdet wurde nur der Zeitpunkt der Erstellung des Triggers, um Schemaevolutionen der Datenbank verfolgen zu können. 3.2 Prinzipielle Erweiterungsmöglichkeiten in Access Es soll hier erstmal um die Möglichkeiten gehen, die Access bietet, um Erweiterungen in das System einzubringen, die es ermöglichen Access im Allgemeinen, bzw. im Besonderen um Trigger, zu erweitern. Dies stellt eine Verallgemeinerung des Problems dar, Access um eine Triggersprache zu erweitern. Prinzipiell wäre es der einfachste und auch der eleganteste7 Weg, die Jet-Engine um die erforderlichen Funktionen zu erweitern. Damit würde sich zum einen aus der Sicht des Anwenders nichts an Access und seinen Strukturen und Befehlen ändern, und zum anderen ließe sich so eine Triggersprache in bestehende Anwendungen nachträglich einfügen. Dabei wären dann noch nicht einmal Änderungen an der eigentlichen Anwendung nötig. Da aber Microsoft die Sourcen zu Access und der Jet-Engine nicht offen gelegt hat, ist es so erstmal nicht möglich, direkt in den Verarbeitungsprozeß der Daten einzugreifen. Stattdessen müssen Alternativen entwickelt werden, die es einem erlauben, die Datenmanipulationen nachträglich zu korrigieren oder aber schon im voraus die Manipulationsbefehle analysieren zu können. Wichtig hierbei ist auch, daß die Umstellungen bei der Benutzung dieser Komponenten sich einheitlich in die Entwicklungsumgebung von Access einfügen, da es sich um eine Erweiterung von Access handeln soll, die zwar einen funktionalen Nutzen bringt, 7 Elegant im Sinne von technisch korrekt 36 KAPITEL 3. KONZEPT EINER ERWEITERUNG aber dennoch eine in sich logische Einheit mit Access bildet. Um nun Access zu erweitern, gibt es mehrere Alternativen, die unterschiedliche Vor- und Nachteile aufweisen. Die Möglichkeiten reichen vom Ersetzen der üblichen Zugriffsmechanismen auf die Datenbankdatei, über externe Programme ohne einen direkten Kontakt zur eigentlichen Anwendung, bis hin zu einer internen Programmerweiterung in Access durch Module. 3.2.1 OLE-DB OLE-DB steht für Object Linked and Embedded DataBase und ist von Microsoft dazu gedacht, einen eigenen Treiber zur Anbindung an Datenbanken zu entwickeln. Innerhalb diesen Treibers können dann, je nach Anforderungen, die Daten der Anwendung auf andere Strukturen in der Datenbank abgebildet werden. So ist es zum Beispiel denkbar, daß auf mehrere Datenbanken zugegriffen wird, um die Daten zu speichern die in der Anwendung als eine Relation sichtbar sind, oder aber daß die zugrunde liegenden Daten überhaupt nicht in einer Datenbank vorliegen, sondern in einem ASCII-Textfile. Ebenso wäre es denkbar, daß die Daten in der Datenbankdatei verschlüsselt vorliegen und durch den Treiber erst entschlüsselt werden bzw. beim Schreiben verschlüsselt werden. Einen weiteren Anwendungsbereich sieht Microsoft darin, daß man hier auch logische Strukturen abbilden kann, die dem Anwender der Datenbank verborgen bleiben sollen. So könnte man zum Beispiel die Stammdaten einer Anwendung aus einer Textdatei entnehmen, weil die Daten aus historischen Gründen so noch vorliegen, und die eigentlichen Anwendungsdaten werden bereits in einer Datenbank verwaltet. Innerhalb dieser Verwaltung der Daten lassen sich dann auch noch zusätzliche Funktionen abbilden, mit denen man eine Systemerweiterung erstellen kann. Nach den Anleitungen, die Microsoft zum Erstellen von OLE-DB-Treibern zur Verfügung stellt8 , ist das Verwenden der Treiber in jeder Anwendung möglich und erlaubt den Umgang mit den Daten wie gewohnt. Da dieses Thema in der sehr guten Einführung in die ActiveX-Programmierung in [A] wie auch in [M] leider nur aus der Sicht von VisualBasic zur Anbindung an andere Systeme und Programme behandelt wird, steht nur die Originalliteratur von Microsoft als Grundlage zur Verfügung. Leider geht aus der Dokumentation nicht hervor, daß sich diese Beschreibungen auf den Umgang mit Daten in reinen Programmiersprachen, wie zum Beispiel VisualBasic, bezieht. Hier wird, um eine Anbindung an eine Datenbank nutzen 8 siehe http://www.microsoft.com/data/ 3.2. PRINZIPIELLE ERWEITERUNGSMÖGLICHKEITEN IN ACCESS 37 zu können, in ein Formular ein ActiveX-Steuerelement eingebunden, über das die Kommunikation mit der Datenbank abgewickelt wird. Diese Vorgehensweise ist zwar auch unter Access möglich, bedeutet aber extreme Einschränkungen für den Anwender. Es wären bei dieser Art der Nutzung keine direkten Datenmanipulationen an den Datentabellen möglich. Bei der Verwendung von Formularen und Berichten müßte der Programmierer selber den Datentransfer zwischen der ActiveX-Komponenten und dem Formular/Bericht verwalten. Es entfällt also die Möglichkeit, Datenfelder von Formularen und Berichten direkt an die Felder einer Datenquelle bzw. Tabelle zu binden, was ein entscheidenes Merkmal von Access ist. Des weiteren sind auch keine Zugriffe auf die Daten mittels SQL möglich, da diese Zugriffe normalerweise über die Jet-Engine abgewickelt werden, diese aber keinen Zugriff mehr auf die Daten hat. Somit sind die Vorteile, die Access durch seine einfache Bindung von Daten an Berichte und Formulare bietet, nicht mehr gegeben. Access würde dadurch auf dieselbe Ebene wie eine konventionelle Programmiersprache gestellt werden. Dies kann in meinen Augen nicht mehr als sinnvolle Lösung angesehen werden, und daher wurde dieser Weg nicht mehr weiterverfolgt. 3.2.2 Datenkontrolle von außen Da es sich bei Access um ein DBMS handelt, das seine Daten in einer Datei (Dateiendung mdb) ablegt, liegt der Schluß nahe, diese Datei mit einem zusätzlichen Programm auf Änderungen zu kontrollieren. Dieser Ansatz führt aber von vornherein zu einer Anzahl von Problemen, die teilweise nicht mehr sinnvoll zu lösen wären. Da auch das Dateiformat von Microsoft nicht offen gelegt wurde, ist eine direkte Bitmanipulation und Bitvergleich nicht sinnvoll möglich. Es ist zwar möglich festzustellen, ob sich an der Datenbankdatei etwas geändert hat, aber wo diese Änderung aus logischer Sicht stattfand ist nicht feststellbar. Hinzukommt, daß Access die Datenbank bereits ändert, wenn sie nur geöffnet wird9 . Es wird mindestens das Datum der Datei geändert. Die Zugriffe auf die Datenbankdatei müssen also immer über die Jet-Engine durchgeführt werden, die aber nur Cursor-basierte oder SQL-basierte Aktionen auf der Datenbankdatei zuläßt. So müßte also grundsätzlich eine Kopie der Datenbank vorliegen, die als Referenz dient, ob und welche Daten verändert wurden. Um die Änderung selbst feststellen zu können müßten beide Dateien miteinander verglichen werden. Da auch die geänderte Relation unbekannt ist, müßten also alle Relationen mit ihrem Original“ verglichen werden. Bei größeren Datenmengen ist dies nicht mehr ” in einer annehmbaren Zeit zu schaffen. Des weiteren müßte die ursprüngliche Anwendung solange angehalten werden, bis alle Änderungen in der Datenbank gefunden worden wären, da es sonst zu einem inkonsistenten Datenbankzustand 9 ein Öffnen einer Datenbank ist nicht möglich solange sie schreibgeschützt ist 38 KAPITEL 3. KONZEPT EINER ERWEITERUNG kommen könnte. Ein Vorteil dieses Verfahrens wäre, daß sogar bei bereits existierenden Anwendungen ein Triggerkonzept nachrüstbar wäre, ohne die eigentliche Anwendung verändern zu müssen. Da der Vorteil aber nicht die oben genannten Nachteile aufwiegen kann, findet dieser Ansatz im weiteren keine Beachtung mehr. 3.2.3 Access als Datenvermittler Hierbei handelt es sich um eine Abwandlung der Kontrolle durch ein externes Programm, was aber nicht alle schwerwiegenden Bedenken gegen die Kontrolle von außen ausräumen kann. Abbildung 3.1: Prinzip der Schleuse Dieses Konzept beruht auf der Idee, daß man Access bzw. die mdb-Datei, in der die Daten gespeichert werden, zusätzlich in einer Vermittlerrolle zwischen der Anwendung und der Erweiterung verwendet. Es werden über eine speziell zur Verfügung gestellten Tabelle die Änderungswünsche an die Erweiterung übermittelt, die die Analyse der Datenänderungen durchführen soll. Diese führt als eigenständiges und externes Programm die Änderungen durch (siehe hierzu auch Abbildung 3.1). Daraus ergeben sich aber auch wieder Probleme mit der Ausführung. Zum einen müßte die Erweiterung ständig selber kontrollieren, ob in der Tabelle zum Datenaustausch neue Befehle enthalten sind, da es unter Access keinen Mechanismus zur Benachrichtigung über Datenänderungen gibt. Zum anderen gibt es den Nachteil, daß auch hier wieder inkonsistente Daten bei Leseoperationen möglich wären. Zwar wären lesende Datenbankzugriffe wie gewohnt möglich, aber es tritt dann auch hier wieder das Problem auf, daß bei umfangreichen Änderungen die ursprüngliche Anwendung auf die Fertigstellung der Änderungen warten müßte, also durch die Erweiterung angehalten werden 3.2. PRINZIPIELLE ERWEITERUNGSMÖGLICHKEITEN IN ACCESS 39 müßte. Würde die Anwendung nicht bis zum Ende der Änderungen angehalten, dann könnte die Anwendung fehlerhafte Daten lesen. 3.2.4 Datenmanipulation über eine interne Klasse Geht man davon aus, daß die Manipulation der Daten unter Access bei einer fertigen Anwendung nicht mehr durch direkte Tabellenmanipulationen gemacht werden, sondern stattdessen immer nur durch die in Access vorhandene Programmiersprache bzw. durch SQL-Befehle realisiert werden, so kann man auch einen Weg wählen, der die direkte Änderung der Daten in den Tabellen unberücksichtigt läßt. Das Programm dieses Ansatzes muß auch nicht mehr in der Lage sein, diese direkten Änderungen registrieren zu können. Während des Programmablaufes gibt es mehrere Möglichkeiten Daten zu ändern: 1. Absetzen von SQL-Befehlen mit dem VBA-Befehlen DoCmd.RunSQL(SQL-String) oder CurrentDB.Execute(SQL-String). Diese beiden Befehle führen den SQL-String auf der Datenbank aus. 2. Cursor-basierte Änderungen mittels eines RecordSets, der über den Befehl CurrentDB.OpenRecordSet(SQL-String) geöffnet werden kann. Dabei können über den SQL-String die Daten der enthaltenen Tabellen bereits durch eine WHERE-Bedingung eingeschränkt werden. 3. Eingaben in ein Formular, das auf einer Tabelle beruht und direkt an diese gebunden ist. Für die ersten beiden Möglichkeiten muß nur diese Schnittstelle nachgebildet werden, damit der bisherige Programmcode ohne Änderungen genutzt werden kann. Für die Nutzung der Trigger wäre somit keine weitere Einschränkung und Umgewöhnung nötig. Leider läßt es Access nicht zu, einen Befehl neu- bzw. umzudefinieren der bereits existiert. Daher ist es nötig, hier eine neue Klasse einzuführen, die dann anstelle der Klassen DoCmd oder CurrentDB verwendet werden muß. Da aber die Methode über dieselbe Syntax verfügen kann wie das Original, wäre dies eine durchaus vertretbare Umstellung. Bei dem dritten Punkt, den Eingaben in ein Formular, ist allerdings eine etwas andere Syntax notwendig. Hier gibt es die Möglichkeit Access eine Funktion ausführen zu lassen, bevor die Änderungen, die in dem Formular gemacht worden sind (Private Sub Form_BeforeUpdate(Cancel As Integer)), in die zugrundeliegende Tabelle zurückgeschrieben werden. Setzt der Programmierer hier nun eine Funktion ein, die die Daten ausliest und mittels embedded-SQL diese Änderungen durchführt, so können auch diese Änderungen durch den Triggermanager 40 KAPITEL 3. KONZEPT EINER ERWEITERUNG erfaßt werden. Da diese Funktion nur einmal erstellt werden muß bzw. vom Triggermanager zur Verfügung gestellt werden kann, stellt dies auch keine besonders große Einschränkung dar. Da unter den realisierbaren Methoden die hier beschriebene Variante mit den geringsten Einschränkungen aufwarten kann, wurde zur Realisierung von Triggern unter Access diese Möglichkeit gewählt. Um sie möglichst gut gegen Fehlbedienung zu schützen, und um sie der Syntax der zu ersetzenden Befehlen anzugleichen, sollte es sich hierbei auch um ein Klassenmodul von Access handeln. Als Einschränkung dieser Methode ist allerdings zu nennen, daß ein manuelles Manipulieren der Tabellen nicht erkennbar ist und der Anwender neue Funktionen zur Datenmanipulation verwenden muß. Diese Funktionen erhalten aber dieselbe Syntax wie die originalen Funktionen zur Datenmanipulation. Weitere Vorteile sind, daß sich für Leseoperationen nichts ändert und daß die direkte (Lese-)Bindung von Formularen und Berichten wie gewohnt möglich ist. Da es sich bei der Erweiterung um eine Klasse innerhalb der eigentlichen Anwendung handelt, wird sie auch in der selben Instanz von Access ausgeführt, somit sind während der Datenmanipulation durch die Erweiterung keine weiteren Leseoder Schreiboperationen möglich, die inkonsistente Datensätze erzeugen könnten. Die genaueren Details der Implementierung werden in Kapitel 4 besprochen. 3.3 Entwurf eines Triggermanagers für Access Wie schon in Kapitel 3.1 erläutert, lehnt sich das Konzept von ARA sehr an das der Trigger aus dem SQL3-Standard an. Diese Ähnlichkeit setzt sich auch bei dem zu entwerfenden Triggermanager fort. Er wird soweit es möglich ist, dem Vorgehen im Standard folgen. Eingeschränkt wird dies nur durch den Funktionsumfang von Access und dem dort implementierten SQL-Umfang. 3.3.1 Architektur Der Triggermanager hat als Teil des DBMS die Aufgabe auf ein Ereignis hin, die diesem Ereignis zugeordneten Trigger zu ermitteln und auszuführen. Da es sich hierbei um eine Komponente des DBMS handelt, die jedes Ereignis eines DBS registrieren können sollte, wird der Triggermanager als Teil des DBMS realisiert werden. Eine Realisierung des Triggermanager in Access als Teil des DBMS ist aber leider hier nicht möglich, da die Jet-Engine, das eigentliche DBMS von Access, nur als fertiges Produkt zur Verfügung steht und nicht als Quelltext. Es bie- 3.3. ENTWURF EINES TRIGGERMANAGERS FÜR ACCESS 41 tet auch keinerlei Möglichkeiten Erweiterungen in einer Art Steckkasten-Prinzip hinzuzufügen. Auch die grafische Oberfläche von Access bietet keinerlei Möglichkeiten in den Ablauf der Arbeiten in der Jet-Engine einzugreifen. Es bleibt so nur die Lösung, den Triggermanager als Präprozessor zur Jet-Engine zu realisieren. Da dieser Triggermanager die ARA-Trigger zur Verfügung stellen soll und er Teil des gesamten Konzeptes ist, wird im folgenden vom Triggermanager für Access als einer Komponenten von ARA die Rede sein. Der Präprozessor selbst verhält sich wie eine Anwendung zur Jet-Engine. Für ihn sind die Zugriffe auf die Daten auch nur über die Zugriffsstrukturen möglich, die von Access und der Jet-Engine zur Verfügung gestellt werden. Da Access nur die grafische Schnittstelle zur Jet-Engine bildet, sind Zugriffe auf Daten also nur über die Jet-Engine-API möglich. Für eine Anwendung, die die API von ARA nutzt, werden die nötigen Funktionen zur Verfügung gestellt, um die Datenmanipulationsbefehle an das DBMS weiterzureichen. Trotz der Bereitstellung der nötigen Befehle zum Absetzen von Datenmanipulationsbefehlen, stehen der Anwendung nach wie vor die Funktionen der Jet-Engine-API zur Verfügung. Hierdurch sind dann unter anderem die direkte Bindung von Berichten an Tabellen zur Datenausgabe möglich. 3.3.2 Funktionalität des Triggermanagers Die grobe Struktur des Programmablaufes Triggermanager läßt sich aus Abbildung 3.2 entnehmen. Das Ereignis wird dem Triggermanager zur Analyse übergeben. Sollte es sich gemäß der Erweiterungen um eine VBA-Funktion handeln, wird diese sofort ausgeführt. Eine weitere Abarbeitung des Triggermanagers ist nicht mehr nötig. Die eventuell in der Funktion enthaltenen Manipulationsanweisungen führen wieder zu einem erneuten Aufruf des Triggermanagers, falls auch hier wieder die Befehle zur Datenmanipulation von ARA verwendet wurden, statt der Funktionen, die von Access aus angeboten werden. Zur Verwaltung der ausgelösten Trigger werden zwei Warteschlangen benutzt. Jeweils eine Warteschlange ist für die Before-Trigger bzw. für die After-Trigger zuständig. Nach der Ermittlung und Einsortierung der Before- und After-Trigger in die jeweilige Triggerwarteschlange werden die Before-Trigger der Priorität nach abgearbeitet, beginnend mit der höchsten Priorität von 1. Dies bedeutet, daß die Before-Trigger der Reihe nach ausgeführt werden, solange keine Ausführung fehlschlägt. Ein Fehlschlag kann aus mehreren Gründen auftreten. Es können zum einen syntaktisch falsche Befehle als Aktion in der Triggerdefinition vorliegen die von Access nicht ausgeführt werden können. Andererseits kann es vorkommen, daß Access Änderungen zurückweist, weil sie den auf der Tabelle definierten Integritätsbedingungen widersprechen. Auch ein Defekt der Datenbankdatei ist nicht 42 KAPITEL 3. KONZEPT EINER ERWEITERUNG Abbildung 3.2: Ablauf des Triggermanagers als Grund für einen Fehlschlag auszuschließen. Dann ist allerdings ein weiteres Arbeiten mit dieser Datei fast unmöglich und führt im allgemeinen auch zum Absturz der Anwendung und von Access. Ausgeführt werden die Before-Trigger direkt auf den Originaldaten, nachdem eine Kopie der betroffenen Tabelle angefertigt wurde. Diese Vorgehensweise steht im Gegensatz zur Arbeitsweise, wie sie nach dem SQL-Standard vorgesehen ist. Dort werden die geänderten Daten der Before-Trigger und des Ereignisses in Transitionsvariblen gesammelt und anschließend in einem Durchgang in die Datenbank übertragen. Unter Access ist dies so nicht möglich, da zur Ermittlung der zu ändernden Daten die Aktionen der Trigger erst ausgeführt werden müßten, um dann die Differenz der zwischen einer Kopie des Originals und des geänderten Originals feststellen zu können. Sollte die Auswertung der Condition positiv verlaufen sein, wird anschließend die auslösende Aktion ausgeführt. Sollte diese nicht fehlerfrei ausgeführt werden können, so werden auch hier alle Änderungen zurückgerollt und eine Fehlermeldung zurückgeliefert. Auch hier werden keine After-Trigger mehr ausgeführt. 3.3. ENTWURF EINES TRIGGERMANAGERS FÜR ACCESS 43 Danach kommt es zur Ausführung der After-Trigger. Da diese aber im Gegensatz zu den Before-Trigger wieder andere Trigger auslösen können, können die After-Trigger nicht nach dem gleichen Verfahren ausgeführt werden wie die Before-Trigger. Der After-Trigger mit der höchsten Priorität wird aus der Warteschlange entfernt und seine Bedingung überprüft. Ist diese Überprüfung positiv verlaufen, wird der im Aktionsteil hinterlegte SQL-Befehl in einem rekursiven Aufruf wieder dem Triggermanager übergeben, und somit zu einem Ereignis wird, daß wieder neue Trigger auslösen kann. Im Falle einer negativen Überprüfung wird der After-Trigger aus der Warteschlange bearbeitet, der nun die höchste Priorität besitzt. Das oben erwähnte Ausführen eines Triggers bedeutet allerdings nicht unbedingt, daß dieser selbst Datenmanipulationen vornimmt. Ein Trigger gilt bereits als korrekt ausgeführt und somit nicht als Fehlschlag, wenn die Ausführungsbedingung (Condition) ausgewertet werden konnte und das Ergebnis negativ ausfiel. Falls sie positiv ausgewertet wurde, wird die Aktion ausgeführt und kann somit noch zu einem Fehlschlag führen, andernfalls wird die auszuführende Aktion nicht weiter beachtet. Bei dem rekursiven Aufruf des Triggermanagers ist allerdings zu beachten, daß die verwendete Warteschlange für die After-Trigger nicht neu angelegt wird. Stattdessen wird die in der ersten Iteration global angelegten Warteschlange verwendet, die noch nicht ausgeführte After-Trigger enthalten kann. Die Warteschlange der Before-Trigger ist allerdings leer, da alle Before-Trigger ja vor der auslösenden Aktion ausgeführt worden sind und nur die After-Trigger wieder wie eine auslösende Aktion abgearbeitet werden. Da die Aktion des Triggers nun als Ereignis behandelt wird, kann es wieder zur Auslösung von weiteren Triggern kommen. Die so ausgelösten After-Trigger werden ihrer Priorität nach in die Warteschlange einsortiert und später nach dem gleichen rekursiven Verfahren ausgeführt. Die ausgelösten Before-Trigger werden nach dem oben beschriebenen System vor der (getriggerten) auslösenden Aktion ausgeführt. Sollten keine weiteren Trigger mehr in den Warteschlangen enthalten sein, so ist die auslösende Aktion mit allen gefeuerten Triggern komplett abgearbeitet und die Programmsteuerung wird wieder an das aufrufende Programm übergeben. 3.3.3 Behandlung von Fehlschlägen Während der Abarbeitung des Ereignisses und seiner Trigger kann es zu Fehlschlägen kommen, die je nach Art des Fehlschlages unterschiedliche Vorgehens- 44 KAPITEL 3. KONZEPT EINER ERWEITERUNG weisen bei der weiteren Abarbeitung der Triggerwarteschlangen erfordern. Da ein physikalischer Fehler der Datenbankdatei in der Regel keine weiteren Manipulationen an den Daten erlaubt, genügt es hier einen Abbruch aller Aktionen zu veranlassen und an die Anwendung einen Fehlercode zurückzuliefern, der die physikalische Beschädigung signalisiert. Sollte es doch zu einem Fehlschlag, während der Abarbeitung von Triggern auf der Before-Warteschlange kommen, der ein Weiterarbeiten der Anwendung erlaubt, so führt dies zu einem vollständigen Abbruch aller, durch das Ereignis induzierten, Änderungsoperationen. Dies bedeutet auch, daß alle Änderungen rückgängig gemacht werden. Dies geschieht durch ein Zurückkopieren der unveränderten Kopie auf die geänderte Version der Datentabelle. Anschließend gibt der Triggermanager eine Fehlermeldung an das aufrufende Programm zurück. Eine Ausführung der After-Trigger findet nicht mehr statt, da ja das auslösende Ereignis selbst auch nicht ausgeführt wurde. Die After-Trigger würden somit sonst auf eine Aktion reagieren, die sich so nie im Datenbestand widerspiegeln würde. Eine Sonderstellung in der Fehlerbehandlung nimmt der Abbruch der Ausführung durch den Befehl Avoid ein, der im Action-Teil eines Before-Triggers angegeben werden kann. Es handelt sich dann um eine korrekte Ausführung des Before-Triggers. Da aber alle Änderungen zurückgerollt werden und keine weiteren Trigger mehr feuern können, haben sich also letztendlich keine Änderungen in den Daten ergeben. Da es sich hier um einen Sonderfall handelt, liefert der Triggermanager eine Fehlermeldung an die ihn aufrufende Funktion zurück. In der Anwendung kann dann darauf reagiert werden, daß die abgesetzte Aktion zu einem Trigger führte, der zu einem Abbruch aller korrespondierenden Änderungen führte. Kapitel 4 Implementierung Die im Rahmen der Diplomarbeit entwickelte Erweiterung trägt den Namen ARA. Im folgenden wird die Architektur von ARA und die Funktionalität ausgewählter Komponenten besprochen. Anschließend werden einige Systemvoraussetzungen von ARA und Access und interne Abläufe in ARA diskutiert. 4.1 Architektur und Funktionalität Wie bereits im Kapitel 2.5 beschrieben existiert in Access ein Klassensystem, um Funktionalität zusammenfassen zu können und zu kapseln. Bei der Realisierung von ARA handelt es sich deshalb auch um eine Klasse in Access, die eine gemeinsame Schnittstelle zur Datenmanipulation zur Verfügung stellt. So können die internen Funktionen verborgen bleiben und dennoch die nötige Funktionalität innerhalb von Access angeboten werden. Ebenso sind so auch mehrere Instanzen von ARA möglich, die die Abarbeitung von SQL-Befehlen in den VBAFunktionen ermöglicht, die als Action in den ARA-Triggern angegeben sind. Dabei sind die Funktionen zur Datenmanipulation denen nachempfunden, die durch diese Systemerweiterung ersetzt werden sollen. Hierbei handelt es sich insbesondere um die Befehle DoCmd.RunSQL() und CurrentDB.Execute(). Bei DoCmd handelt es sich um eine Klasse von Access, die Funktionen, die über die Entwicklungsumgebung von Access über Menüs erreichbar sind, dem Anwender innerhalb von VBA zur Verfügung stellt. Hierbei ist das Element RunSQL von besonderer Bedeutung für uns, da hierüber Datenmanipulationsbefehle an die Jet-Engine übergeben werden können. Bei CurrentDB handelt es sich um eine Instanz der Klasse Database, die die aktuelle Datenbank referenziert. Der Befehl Execute dient wieder zum direkten Ausführen von SQL-Befehlen zur Datenmanipulation. 45 46 KAPITEL 4. IMPLEMENTIERUNG Beiden Befehlen ist allerdings gemeinsam, daß sie nur SQL-Kommandos zur Datenmanipulation abarbeiten können, aber keine Befehle zur Abfrage von Daten. Hierzu gibt es unter Access den Datentyp RecordSet, der ein cursorbasiertes Auslesen und auch Ändern von Tabellen erlaubt. Abbildung 4.1: Strukturdiagramm von ARA ARA selbst unterteilt sich in zwei große Komponenten, die die eigentlichen Funktionalitäten enthalten. Zum einen ist hier der Teil, der zur Eingabe der Trigger dient, zu nennen und zum anderen der Triggermanager, der die Aufgaben während der Datenmanipulation wahrnimmt. Zusätzlich zu den beiden großen Teilen von ARA gibt es noch weitere Bestandteile, wie man aus Abbildung 4.1 ersehen kann. Diese dienen der Kommunikation mit dem Anwender oder der Speicherung der Trigger in Access. 4.1. ARCHITEKTUR UND FUNKTIONALITÄT 47 Der für den Anwender sichtbare und auch für ihn wichtigste Teil befindet sich in dem Klassenmodul ARA. Hierbei handelt es sich um eine Anpassungsschicht, die die oben erwähnten Befehle an die Syntax des Triggermanagers anpaßt und dem Anwender zur Verfügung stellt. Zusätzlich zu diesen Befehlen gibt es auch noch Funktionen, die das Verhalten der Systemerweiterung bestimmen. So läßt sich hier mittels des Befehls ARA.Create Trigger() das weiter unten beschriebene Formular zur Eingabe der Trigger öffnen oder mittels ARA.Display() bestimmen, ob die internen Abläufe des Triggermanagers in einem separaten Fenster angezeigt werden sollen. Wie bereits in Kapitel 2.5 erläutert, lassen sich ausgeblendete Objekte und Systemobjekte getrennt voneinander einblenden. Um einen möglichst hohen Schutz aller Objekte von ARA zu erreichen, ohne Zugriffsrechte definieren zu müssen, sind alle Objekte sowohl als ausgeblendet als auch, bis auf das Klassenmodul ARA, als Systemobjekte markiert. Bei dem Klassenmodul ARA wurde darauf verzichtet, es als Systemobjekt zu kennzeichnen, weil damit auch eine Namensänderung, zum Beispiel USysARA, einher gegangen wäre. Dies geschah sowohl aus ästhetischen Gründen, als auch aus dem Grund, daß diese Benennung in Access eher ungewöhnlich für die spätere Nutzung wäre. Auf das Definieren von Zugriffsrechten wurde bei der Entwicklung verzichtet, um dem Leser dieser Arbeit es dennoch zu ermöglichen, die Funktionen einzusehen und gegebenenfalls nach eigenen Wünschen zu ändern oder zu vervollständigen. Trotzdem sollte aber der Anwender von, für ihn unnötigen, Interna ferngehalten werden. Anhaltspunkte zu möglichen Erweiterungen findet man auch in Kapitel 5 beschrieben. Die strikte Trennung der beiden Bereiche Triggermanager und Kommunikation ist daher wichtig, um bei einem fertigen Programm den Nutzer der Anwendung1 freizustellen bzw. ihn nicht durch unnötige technische Details zu verwirren. Diese Teile können dann eventuell aus dem System entfernt oder durch, für den Anwender, unsichtbare Komponenten ersetzt werden. Des weiteren macht diese Trennung einen Sinn, da man so nun die Kommunikation mit dem Anwender neu gestalten kann, ohne daß dies einen Einfluß auf die Arbeit des Triggermanagers hat. Ebenso wäre ein Austausch des Triggermanagers gegen eine Komponente denkbar, die die Aufgabe in einer anderen Art und Weise löst. 1 Um hier keine Verwirrungen aufkommen zu lassen: der Anwender entwickelt eine Anwendung unter Zuhilfenahme von Access und ARA als Systemerweiterung. Der Nutzer ist dann derjenige, der die Anwendung benötigt und sie als Hilfsmittel bei seiner täglichen Arbeit einsetzen will. Beim Nutzer sind dann nicht unbedingt Interesse an Systemerweiterungen und Kenntnisse über Programme zu erwarten. 48 KAPITEL 4. IMPLEMENTIERUNG Eine funktionale Verbindung zwischen den beiden großen Teilen, der Triggerverwaltung und dem Triggermanager, bildet die Speicherung der Triggerdefinitionen in der Tabelle USysTrigger tbl. Diese Tabelle liegt, genau wie die anderen Objekte von ARA, in der selben Datenbankdatei wie die nutzende Anwendung. In ihr werden sowohl die nötigen Informationen zu den Triggern und auch zusätzliche Informationen zur Verwaltung der Trigger abgelegt. 4.1.1 Triggerverwaltung Abbildung 4.2: Datenbankfenster einer Anwendung Da es sich bei den ARA-Triggern um einen neuen Bestandteil von Access handelt, müßte er wie ein siebter Objekttyp (vergleiche Kapitel 2.5.1), aus dem eine Access-Anwendung bestehen kann, behandelt werden. Demnach müßte das Datenbankfenster aus Abbildung 4.2 noch um einen zusätzlichen Reiter mit der Beschriftung Trigger“ erweitert werden. Da dies leider nicht möglich ist, wurde ” eine eigene Eingabemöglichkeit für Trigger geschaffen. Es handelt sich dabei um ein Formular, mit dem sowohl neue Trigger definiert, als auch bestehende Trigger geändert oder gelöscht werden können. Das Eingabeformular ist in dem Datenbankfenster unter dem Namen USysTrigger frm 2 abgelegt und nur dann sichtbar, wenn sowohl Systemobjekte als auch ausgeblendete Objekte eingeblendet wurden. Die Beschriftung des Formulars im geöffnetem Zustand soll die Funktionalität erklären und lautet Trigger Design ” 2 Vergl. zur Namensgebung auch Kapitel 2.5 4.1. ARCHITEKTUR UND FUNKTIONALITÄT 49 Abbildung 4.3: Eingabeformular für ARA-Trigger and Manipulation Tool“. Wie man an Abbildung 4.3 erkennen kann, läßt sich das Eingabeformular in drei funktionale Teile unterteilen. Im oberen Teil kann man bereits definierte Trigger zum Anzeigen auswählen. Die ausrollbare Liste zeigt dabei alle Trigger sortiert nach ihrem Namen an. Des weiteren wird die Priorität und die Tabelle, auf deren Änderungen der Trigger reagiert, im ausgerollten Zustand angezeigt. Der jeweils aktuelle Trigger läßt sich dann mit dem Bleistift“ zum Bearbei” ten freigeben. Vorher sind keine Manipulationen am Trigger möglich. Diese Art des Vorgehens wurde gewählt, damit beim Betrachten der Trigger nicht unbeabsichtigt Änderungen vorgenommen werden, die evtl. zu einer Fehlersuche in der Anwendung führen könnten. Durch Wahl des Buttons +“ kann ein neuer Trigger dem Bestand hinzu” gefügt werden. Dabei ist der aktuell angezeigte Trigger irrelevant. Der Button X“ bewirkt ein Löschen des aktuell angezeigten Triggers. Damit ” es hier nicht zu einer versehentlichen Löschung kommt, muß diese dann noch in einem separaten Fenster bestätigt werden, was nach Klicken auf diesen Button 50 KAPITEL 4. IMPLEMENTIERUNG erscheint. Die Definition des zu löschenden Triggers wird aus der zugrundeliegenden Tabelle USysTrigger tbl entfernt. Die im Condition- oder Action-Teil eventuell verwendeten VBA-Funktionen werden allerdings nicht gelöscht, da sie unter Umständen bei anderen Triggern oder an einer anderen Stelle in der Anwendung benötigt werden. Bei den oben erwähnten Operationen zur Speicherung und Löschung der Trigger übernimmt Access einen Großteil des Verwaltungsaufwandes. Das unter Access übliche Verfahren, ein Formular mit einer Tabelle zu verknüpfen, indem es im Formular als Datenherkunft angegeben wird, wurde auch hier angewendet. Das Formular wurde mit der in Kapitel 4.2.1 beschriebenen Tabelle USysTrigger tbl verknüpft. Die im Formular verwendeten Textfelder entsprechen nun den in der Tabelle verwendeten Feldern. Im weiteren Verlauf übernimmt nun Access den Abgleich der im Formular geänderten Daten mit der Tabelle und sorgt auch für eine korrekte Anzeige des Tabelleninhaltes im Formular. Der mittlere Teil des Formulars läßt sich in einen linken und rechten Bereich unterteilen. Im linken Teil findet man die eigentlichen Informationen, die für einen Trigger nach dem ECA-Prinzip notwendig sind. Im einzelnen sind dies die Angabe der Tabelle und des Ereignisses (Events) auf den hin der Trigger gefeuert werden soll. Des weiteren können dort der Ausführungszeitpunkt (ActionTime), die Bedingung (Condition) und die auszuführende Aktion (Action) angegeben werden. Die Felder auf der rechten Seite des Formulars haben teilweise nur administrative Bedeutung, wie zum Beispiel der Name des Triggers. Das Feld Created ist eingefügt worden um bei später auffallender Dateninkonsistenz feststellen zu können, ob dieser Trigger von Beginn an existierte bzw. ob er im Verlauf der Datenbankentwicklung verändert wurde (Modified ). Dies setzt allerdings voraus, daß alle Datensätze ebenfalls mit einem Erstellungs- und Änderungsdatum versehen sind. Durch die Wahl von Auswahlfeldern statt der Möglichkeit einer freien Eingabe wurde versucht, Fehlerquellen so gut wie möglich auszuschließen und dennoch eine komfortable Eingabeunterstützung zu bieten. So sind zum Beispiel in dem Auswahlfeld der Tabellen nur Tabellen enthalten, die bereits in Access existieren. Die Namen der Tabellen sind in der Systemtabelle MSysObjects enthalten und können dort ausgelesen werden. Der hierzu nötige SQL-Ausdruck, der auch gleichzeitig die ausgeblendeten und die Systemobjekte herausfiltert, sieht wie folgt aus: 4.1. ARCHITEKTUR UND FUNKTIONALITÄT 51 SELECT DISTINCTROW MSysObjects.Name, MSysObjects.Type, MSysObjects.Flags FROM MSysObjects WHERE MSysObjects.Name Not Like "USys*" AND MSysObjects.Type=1 AND MSysObjects.Flags=0 ORDER BY MSysObjects.Name; Der angegebene SQL-Ausdruck wird auch in dem Eingabeformular verwendet, um die Auswahlliste mit den Tabellennamen zu füllen. Hierdurch sind keine Trigger definierbar, die auf Änderungen in den unzureichend dokumentierten Systemtabellen und den Triggertabellen reagieren sollen. Dies schützt vor einer Situation, die durch den Triggermanager nicht handhabbar ist. Da es unter Access für den Anwender nicht zugelassen ist Schreiboperationen auf den Systemtabellen durchzuführen, könnte ein Before-Trigger, spätestens aber die auslösende Aktion, einen Rollback verursachen. Der Rollback würde dann durch Löschen der Tabelle und Neuschreiben der unmodifizierten Daten erfolgen. Allerdings führt schon der Löschversuch wieder zu einer Ablehnung durch Access, wodurch der Triggermanager nun nicht mehr angemessen reagieren kann, da sowohl die Modifikation der Daten als auch die Wiederherstellung der originalen Daten zu Fehlern führen. Die Felder Event, ActionTime und StateOrRow haben festgelegte Werte zur Auswahl, die sich nur zur Entwurfszeit des Formulars ändern lassen. Des weiteren findet eine Prüfung der Condition bei der Eingabe statt. Dies soll sicherstellen, daß es sich hierbei um eine syntaktisch korrekte Eingabe handelt. Aus diesem Grund ist es auch für die zulässigen VBA-Funktionen wichtig, daß diese keinerlei Datenmanipulationen durchführen, da sonst schon beim Anlegen oder Ändern eines Triggers hier Daten geändert werden würden. Die Überprüfung selbst wird an Access übergeben. Dies wurde dadurch realisiert, daß ein RecordSet der in der Condition verwendeten Tabelle geöffnet wird und die Condition in Form der Where-Klausel angehangen wird. Diese Überprüfung ergibt in VBA ein relativ kurzes Codesegment : strSQL = "SELECT * FROM " & ConTab & " WHERE " & Condition & ";" On Error Resume Next Set rs = CurrentDb.OpenRecordset(strSQL, dbOpenSnapshot) Bei der Variablen Condition handelt es sich um den Inhalt des Textfeldes der in dem Conditon-Feld des Eingabeformulars steht. Der Inhalt der Variablen ConTab wird durch feststellen der beteiligten Tabellen festgelegt. Eine Überprüfung einer VBA-Condition gestaltet sich noch einfacher. Die Funktion kann 52 KAPITEL 4. IMPLEMENTIERUNG direkt mit dem VBA-Befehl Eval() ausgewertet werden. Für den im Action-Feld eingegebenen Befehl läßt sich die Prüfung so allerdings nicht realisieren, da sonst schon beim Überprüfen des Triggers die Daten verändert werden würden. Es läßt sich aber dennoch eine Funktion realisieren, bei der die Jet-Engine wieder die eigentliche Kontrolle übernimmt. Zur Überprüfung von SQL-Befehlen wird dabei so vorgegangen, daß eine Kopie der zu verändernden Tabelle angelegt wird und der SQL-Befehl auf dieser kopierten Tabelle ausgeführt wird. Dabei wird von der Tabelle nur die Struktur kopiert, aber nicht ihre Daten, da sich diese je nach Datenbankzustand immer unterscheiden und auf die Syntax des SQL-Befehls keinen Einfluß haben. Die Daten werden nicht kopiert, da die Tabellenerstellung einer leeren Tabelle schneller realisierbar ist als das Kopieren von vielen Datensätzen. Bei eventuell verwendeten VBAFunktionen statt SQL-Befehlen kann keine testweise Ausführung der Funktion veranlaßt werden, da hierzu ein Parsen der VBA-Syntax nötig wäre. Es ist dennoch möglich, die einzelnen Module der Datenbank, daraufhin zu überprüfen, ob eine solche Funktion in dem Modul vorliegt. Aus der Art und Weise wie die eingegebenen SQL-Befehle auf ihre korrekte Syntax getestet werden ergibt sich, daß nur Trigger definiert werden können, die im aktuellen Zustand der Schemaevolution korrekt durchgeführt werden können. Sollte eine programmgesteuerte Schemaänderung erwartet werden, so können die Trigger, die in diesem neuen Schema feuern sollen, noch nicht definiert werden. Alle für die Eingabe nötigen Funktionen befinden sich im Klassenmodul des Formulars und werden somit auch mit dem Formular gespeichert bzw. kopiert beim Kopieren des Formulars in eine andere Datenbankdatei. Daher wird für die Eingabe der Trigger kein weiteres Modul benötigt. Lediglich für den späteren Programmablauf ist das Modul mit dem Triggermanager nötig. Allerdings dient das Klassenmodul ARA dazu, daß das versteckte Formular aufgerufen werden kann, ohne daß der Anwender die Systemobjekte einblenden muß. Dies schließt natürlich nicht die Existenz der Tabelle USysTrigger tbl mit ein, da hier die Definition der Trigger abgelegt wird. 4.1.2 Triggermanager Der eigentliche Triggermanager befindet sich im Modul USysTrigger mdl und besteht hauptsächlich aus der Funktion main(), die die Ablaufsteuerung übernimmt. Diese Funktion main() bekommt als Parameter den auszuführenden SQLString übergeben, wie auch die Konstante -1 als Wert für den aktuell aktiven Trigger. Bei diesem Wert handelt es sich um die interne Verwaltungsnummer des 4.1. ARCHITEKTUR UND FUNKTIONALITÄT 53 Trigger, dessen Aktionsteil nun gerade ausgeführt wird. Da es sich beim ersten Aufruf nicht um einen Trigger handelt, sondern um ein auslösendes Ereignis, wird die Konstante -1 verwendet. In der Funktion main() findet die gesamte Abarbeitung des auslösenden Ereignisses und der dadurch gefeuerten Trigger statt. Es werden in der Funktion sowohl die Trigger ermittelt und in Warteschlangen abgelegt, als auch wieder aus den Warteschlangen genommen um sie auszuführen. Dabei werden die Aktionen der After-Trigger wieder an die Funktion main() zur Ausführung übergeben, nachdem ihre Bedingung zur Ausführung aus dem Bereich Condition geprüft worden ist. Das Überprüfen der Condition erfolgt nach einem ähnlichen Verfahren wie bei der Eingabe. Es wird ein RecordSet angelegt, welcher die Condition in der Where-Klausel enthält. Enthält der RecordSet anschließend keine Daten, so wird dies als falsch (False) gewertet, andernfalls als wahr (true). Dadurch, daß die Aktion der Trigger als Parameter bei der Rekursion übergeben wird, erreicht man, daß die Aktionen der After-Trigger wie auslösende Ereignisse behandelt werden. Lediglich die bereits vorhandenen und eventuell gefüllten Warteschlangen führen dazu, daß die Aktionen neuer After-Trigger nicht von der rekursiv aufgerufenen Funktion abgearbeitet werden. Da sie in die globale Warteschlange einsortiert werden, ist es egal, ob sie durch ein auslösendes Ereignis verursacht wurden oder durch das Feuern eines Triggers. Eine Ausnahme bilden die Trigger, die durch andere Trigger ausgelöst wurden, allerdings dann, wenn der Trigger fehlschlägt, durch den sie ausgelöst wurden. Dann werden die Trigger wieder aus der Warteschlange entfernt die durch den fehlgeschlagenen Trigger ausgelöst wurden, da diese sonst auf eine Aktion hin ausgeführt werden würden, die selber überhaupt nicht ausgeführt wurde. Dies entspricht einem lokalen Rollback des fehlgeschlagenen Triggers. Die weiteren Funktionen im Modul USysTrigger mdl dienen lediglich als Hilfsfunktionen für den Triggermanager. So gibt es unter anderem eine Funktion parseTBedingung() die, wie bereits eine ähnliche Funktion im Eingabeformular, den Condition-Teil eines Triggers auswertet und einen Wahrheitswert zurückliefert. Ebenso dient die Funktion getTrigger() als Hilfsfunktion, um die betroffene Tabelle eines auslösenden Ereignisses zu bestimmen oder eine weitere Funktion, um die Triggerwarteschlangen nach den Prioritäten zu sortieren. Weitere Objekte aus Access sind nur insofern nötig, als der Triggermanager die Tabelle USysTrigger tbl und USysTriggerdetails tbl als Speicherort der Triggerdefinitio- 54 KAPITEL 4. IMPLEMENTIERUNG nen benötigt. Funktionen anderer Module werden jedoch nicht gebraucht. 4.2 Ausgewählte Aspekte Im Rahmen dieses Abschnittes wird hier nun auf einige besondere Aspekte der Programmierung von einzelnen Funktionen eingegangen. Dabei handelt es sich sowohl um innere Abläufe von ARA als auch um allgemeine Funktionsweisen im Zusammenhang mit Access. Bei der genutzten Access-Version handelt es sich um Microsoft-Access 97. Diese wurde mit dem Office 97 Service Release 2b (SR2b) 3 auf den aktuellen Stand gebracht, da es mit anderen Versionen unter anderem zu Datenverlusten kommen kann, siehe hierzu auch die Beschreibung in [FAQ] unter Punkt 7.2 BOOKMARKBUG. Zusätzliche Systemeinschränkungen zu denen von Access sind nicht nötig, da es sich bei der Erweiterung um Access-interne Funktionen handelt, die nicht auf das Betriebssystem oder sonstige Resourcen direkt zurückgreifen. Access und ARA laufen somit unter dem Betriebssystem Microsoft-Windows 95 bis hin zum Microsoft-Windows 20004 . Da es sich bei VBA um eine Interpretersprache handelt, wurde versucht, so viele Funktionen von Access und der Jet-Engine zu nutzen wie möglich. Dadurch sind teilweise auf den ersten Blick ungewohnte Vorgehensweisen nötig geworden. Da die Jet-Engine sowohl einen SQL-Parser enthält, als auch die benötigten Strukturen für einen Dateizugriff, würden die Schreib- und Leseoperationen nach wie vor über die Jet-Engine abgewickelt werden. Die ungewohnten Vorgehensweisen sollten aber schneller und effektiver arbeiten, als ein für diesen Zweck neu entwickelter SQL-Parser. Diese Annahme beruht auf zwei Tatsachen. Zum einen handelt es sich bei der Jet-Engine um eine kompilierte DLL-Datei, und zum anderen wird die Jet-Engine seit mehreren Versionen kontinuierlich weiterentwickelt und optimiert. Einen wirklichen Beweis für einen Geschwindigkeitsvorteil erhält man allerdings erst, wenn man versuchen würde, einen optimierten SQL-Parser in VBA zu erstellen. Ohne weitere Beachtung der Access-Programmierumgebung und des Betriebsystems werde ich im weiteren Verlauf den Aufbau der Systemerweiterung erklären sowie auf einzelne Details der Implementierung eingehen. Diese sollten sich auch unter anderen Access-Versionen nachvollziehen lassen. 3 Übersicht aller Service Releases unter http://office.microsoft.com/germany/downloads/Default.aspx bzw. für SR2b http://office.microsoft.com/germany/downloads/9798/sr2off97detail.aspx bzw. für SR1 (für SR2b nötig) http://office.microsoft.com/germany/downloads/9798/sr1off97detail.aspx 4 Windows 95 und Windows 2000 sind eingetragene Warenzeichen der Firma Microsoft 4.2. AUSGEWÄHLTE ASPEKTE 4.2.1 55 Interne Verwaltung von Triggern Da ein normales“ DBMS seine Metadaten in einer Datenbankdatei speichert, ” liegt es nahe, die Definitionen der Trigger ebenfalls in einer Datenbankdatei abzulegen. Da alle Objekte von ARA in einer Datenbank gespeichert sind, werden auch die Informationen über die Trigger in dieser Datenbank gespeichert. Es handelt sich dabei um die Tabellen USysTrigger tbl und USysTriggerdetails tbl, die aufgrund ihres Präfixes im Namen von Access als Systemtabellen normalerweise nicht angezeigt werden. Zusätzlich sind diese Tabellen auch noch als ausgeblendete Objekte markiert, was dazu führt, daß sie nicht angezeigt werden, wenn die Systemobjekte eingeblendet werden. Ein direktes Eintragen von Triggerdefinitionen durch den Anwender in die Tabellen ist zwar sowohl per Hand direkt in die Tabellen als auch per ARA.RunSQLBefehl möglich, aber nicht unbedingt erwünscht und sinnvoll. Bei diesen direkten Manipulationen der Triggerdaten kann keine Überprüfung der eingegebenen Informationen geschehen, und somit könnten dort Trigger eingetragen werden, die aufgrund von Schreibfehlern nie feuern oder aber beim Feuern grundsätzlich einen Fehler verursachen, da zum Beispiel die Condition nicht ausgewertet werden kann. Im Gegensatz zu einer Speicherung der Triggerdefinition in einem Feld wie es der Create Trigger-Befehl aus dem Standard suggeriert, werden die Trigger in ARA in einzelne Informationen aufgeteilt abgespeichert. Hierdurch lassen sich die Vorteile eines DBMS nutzen. So wird zum Beispiel die Spalte, die den Tabellennamen enthält indiziert, damit hierüber ein schnellerer Zugriff auf den eventuell zu feuernden Trigger möglich ist. Abbildung 4.4: USysTrigger tbl im Entwurfsmodus Neben den eigentlich benötigten Daten der Definition werden in der Tabelle 56 KAPITEL 4. IMPLEMENTIERUNG noch zusätzliche Daten zur Vereinfachung oder aus Gründen der einfacheren Fehlersuche gespeichert. Siehe hierzu auch Abbildung 4.4 in der die Entwurfsansicht von USysTrigger tbl abgebildet ist. Der Name des Triggers wird in dem Feld TName gespeichert, da es sich bei Name um ein in Access reserviertes Wort handelt. Bei Datenzugriffen kann es dann zu Fehlermeldungen kommen, falls aus der Syntax nicht genau erkennbar ist, ob es sich nun um das Feld oder die Funktion Name handelt. Um hier von Anfang an Probleme zu vermeiden, wurde ein anderer Begriff für den Feldnamen gewählt. Zur Fehlersuche wurden auch die Felder Created und Modified angelegt. Sie sollen dazu dienen, den Zeitpunkt des Erstellen bzw. der letzten Änderung zu protokollieren, damit bei inkonsistenten Daten nachvollzogen werden kann, ob diese Daten eventuell vor dem Anlegen oder Ändern des Triggers zuletzt modifiziert worden sind. Diese Möglichkeit dürfte vor allem bei der späteren Erweiterung von Anwendungen (Schemaevolution) interessant werden, da hier dann schon Daten in der Datenbank vorliegen, auf die nicht mit einem neu eingegebenen Trigger reagiert werden konnte. Als Beispiel sei hier nur kurz eine Datenbank für das Prüfungsamt zur Verwaltung der Studenten, ihrer Prüfungen und sonstigen Nachweisen erwähnt. Hier würde eine neue Diplomprüfungsordnung auch zu neuen Triggern führen, die auf die neuen Bedingungen zu reagieren hätten. Neben den erwähnten Datums-Feldern wurde auch ein Feld zur eindeutigen Identifizierung der Trigger eingeführt. Hierbei handelt es sich um das Feld TID mit dem Datentyp Autowert. Es dient dazu, die eingegebenen Trigger während des Programmablaufes zu verwalten und zu speichern. Da der Datentyp Autowert unter Access für jede Tabelle einen eigenen Zähler verwaltet, der auch nicht zurückgestellt wird, falls ein Datensatz gelöscht wird oder es zu Fehlern beim Update kommt, enthält dieses Feld nicht zwangsläufig eine durchgehende Numerierung der Trigger. Dies stellt zwar kein Problem dar, bedeutet aber, daß der TID nicht zur Vergabe der Prioritäten verwendet werden kann. Um bei den Prioritäten sicher zu gehen, daß es keine zwei Trigger mit der selben Priorität gibt, findet bei der Eingabe im Formular eine Überprüfung statt, ob dieser Prioritätswert bereits vorhanden ist. Sollte dies der Fall sein werden die Trigger nach dem im Kapitel 3.1 beschriebenen Verfahren neu durchnumeriert. 4.2.2 Ablaufsteuerung Nachdem die betroffene Tabelle festgestellt wurde, deren Daten durch die SQLAnweisung geändert werden sollen, wird eine Kopie der Tabelle angelegt. Diese Kopie dient als Sicherung, um im Falle eines Abbruchs der Änderungen die Ori- 4.2. AUSGEWÄHLTE ASPEKTE 57 ginaldaten wiederherstellen zu können. Bei dem Erstellen der Kopie, werden alle Integritätsbedingungen, die direkt an die Tabelle gebunden sind, mit kopiert. Integritätsbedingungen, die auf andere Tabellen verweisen werden nicht kopiert. Es handelt sich hierbei um Integritätsbedingungen auf Basis von Fremdschlüsseln. Diese Integritätsbedingungen werden unter Access Beziehungen genannt, was sich von dem Begriff relations aus dem Bereich von ER-Diagrammen herleiten läßt. Aufgrund der Struktur der Triggerbearbeitung wird diese Kopie aber nur bei der Verwendung von Before-Triggern benötigt. Hierbei kann es zu einem Zurückrollen der Aktionen der Before-Trigger und der auslösenden Aktion kommen. Bei den After-Triggern ist nur ein Zurückrollen der jeweiligen Aktion des Triggers möglich, falls diese fehlschlägt. Dieses Zurückrollen wird dann aber von Access automatisch durchgeführt und benötigt hierfür keine Kopie der Originaldaten. Prinzipiell wird die Kopie der Tabelle nur bei der Verwendung von BeforeTriggern als Backup benötigt, wird aber erst nach dem letzten auszuführenden After-Trigger gelöscht. Dies geschieht aus mehreren Gründen. Zum einen läßt sich so ermitteln, welche Datensätze geändert worden sind, um dies in einem Log des Triggermanagers anzeigen lassen zu können. Zum anderen lassen sich hierüber dann auch RowLevel-Trigger realisieren, die bei jedem geänderten Datensatz feuern sollen und nicht, wie die StatementLevel-Trigger, nur bei jedem Manipulationsbefehl. Abbildung 4.5: Beziehung zwischen zwei Mastertabellen und einer Detailtabelle Für den weiteren Ablauf ist es nun nötig, daß die Kopie der Daten nicht mit den Beziehungen des Originals versehen worden ist. Sollte bei der Definition der Beziehungen die referenzielle Integrität mit Löschweitergabe festgelegt worden sein, so können nun die Datensätze mit ihren Detaildatensätzen gelöscht werden. Wären allerdings die Beziehungen auch kopiert worden, so wäre eine solche Löschung nicht möglich, da der Datensatz der Detailtabelle auch mit ei- 58 KAPITEL 4. IMPLEMENTIERUNG nem Datensatz der Kopie verbunden wäre. Es würden also die Löschoperationen fehlschlagen, obwohl dies bei der Umgehung des Triggermanagers nicht der Fall wäre. Abbildung 4.5 zeigt ein ER-Diagramm, was diese Beziehung zwischen zwei Mastertabellen und einer Detailtabelle verdeutlicht. Im weiteren Verlauf der Ausführung werden die Before-Trigger und die auslösende Aktion auf der Originaltabelle ausgeführt. Dies geschieht auf der Tabelle mit den Originaldaten und nicht auf der Kopie, da hier direkt von Access die referenzielle Integrität geprüft werden kann. Sollte es zu einer Ablehnung einer Änderung kommen, so kann dann ein Rollback durch zurückschreiben der Kopie erreicht werden. Im einzelnen läuft das Zurückschreiben der Kopie so ab, daß zuerst die Referenzen vom Original auf die Kopie kopiert werden und somit gesichert sind. Anschließend braucht nur noch das Original gelöscht zu werden und die Kopie den Namen des alten Originals erhalten. Somit ist die Kopie wieder zum Original geworden. Im Programmcode liest sich dieser Algorithmus wesentlich einfacher und ist zudem auch noch kürzer: Private Sub transfer_tempTable(TIDTable As String, TIDparent As Long) ’ ersetzt die Tabelle TIDTable durch die temporaere ARA-Tabelle DoCmd.SetWarnings False double_Relation TIDTable, cARATab & TIDparent & "-" & TIDTable DoCmd.DeleteObject acTable, TIDTable DoCmd.Rename TIDTable, acTable, cARATab & TIDparent & "-" & TIDTable DoCmd.SetWarnings True End Sub Dabei ist acTable eine Access-Konstante, und bei cARATab handelt es sich um eine Konstante, die den Namen der temporären Tabellen vorangestellt wird, damit die Namen nicht zufälligerweise den Namen aus der Anwendung entsprechen5 . Die Funktion double Relation dupliziert die Beziehungen von einer Tabelle auf eine zweite, die den selben Aufbau hat. Dies wird durch Auslesen der Relationen der Datenbank, die Access in dem Objekt CurrentDb.Relations zur Verfügung stellt, und anschließendem Eintragen der entsprechenden Beziehungen realisiert. 5 Es handelt sich hierbei um den String –ARA–“, was eine zufällige Nutzung dieses Namens ” sehr unwahrscheinlich macht 4.2. AUSGEWÄHLTE ASPEKTE 59 Das Kommando DoCmd.SetWarnings wird zum Aus- und Einschalten der Warnungen von Access verwendet. Diese werden normalerweise automatisch ausgegeben, wenn man versucht Objekte aus dem Datenbankfenster von Access zu löschen. Da diese Meldungen während des Programmablaufes zum einen sehr häufig erscheinen könnten und zum anderen den Nutzer nur verwirren würden, wurden die Meldungen immer vor solchen Aktionen ausgeschaltet und danach wieder eingeschaltet. Das Einschalten der Meldungen führt allerdings dazu, daß in dem Anwendungsprogramm diese Meldungen nicht bei Programmbeginn ausgeschaltet werden können und dann immer ausgeschaltet bleiben. Sie sind also vor jeder Aktion, die eine Meldung hervorrufen könnte wieder auszuschalten, bzw. nach jedem Aufruf des Triggermanagers. Sollte es zu dem oben beschriebenen Rollback kommen, so werden anschließend auch alle After-Trigger aus der Warteschlange entfernt und die Programmsteuerung mit einer Fehlermeldung wieder an die aufrufende Funktion übergeben. Somit wurden dann keine Datenänderungen vorgenommen. Anders als im Standard gibt es bei den ARA-Triggern allerdings auch die Möglichkeit, ein Rollback manuell auszulösen. Hierzu wird der Befehl Avoid für den Action-Teil des Before-Triggers reserviert. Dies bietet die Möglichkeit eine auslösende Aktion immer oder unter bestimmten Bedingungen abbrechen zu lassen. So könnte man, zum Beispiel bei einer Datenbank für das Prüfungsamt das Eintragen einer neuen Prüfung verhindern, wenn vorgegebene Fristen nicht eingehalten wurden. Ebenso wäre ein Löschen einer Prüfung nur solange erlaubt, bis eine Note für diese Prüfung eingetragen wurde. Vorher kann der Prüfungstermin immer noch verschoben werden. Nachdem die Before-Trigger und die auslösende Aktion ausgeführt wurden, werden die After-Trigger der Warteschlange ausgeführt. Dies geschieht durch einen rekursiven Aufruf der Funktion main(), die die gesamte Steuerung bislang übernommen hat. Dabei wird vorher geprüft, ob die Bedingung des ConditionTeils des ARA-Triggers erfüllt ist. Das genaue Vorgehen bei der Überprüfung der Condition ist bereits weiter oben ausführlich diskutiert worden. Zu beachten ist noch, daß die After-Trigger aus der Warteschlange nur dann abgearbeitet werden, wenn die Rekursion die erste Rekursionsstufe6 wieder erreicht hat. Somit wird ein unnötig großer Stack vermieden, der sonst benötigt würde, um die einzelnen Rekursionsaufrufe zu verwalten. Durch diese Verlegung des rekursiven Aufrufs, kommt es maximal zu einem Rekursionsschritt. Weitere Schritte sind nicht möglich, da die Warteschlange nicht bei Werten für TIDparent 6 Die Variable TIDparent enthält beim ersten Aufruf den Wert -1, bei dem erst jeweils die Trigger der Warteschlange abgearbeitet werden. 60 KAPITEL 4. IMPLEMENTIERUNG abgearbeitet werden, die nicht -1 entsprechen. Dieses Vorgehen dient einem stabileren Ablauf von Access, da sich immer wieder Probleme mit Abstürzen ergeben, wenn Access einen zu großen Stack aufbaut. 4.2.3 Fehlerbehandlung Abbildung 4.6: Fehlermeldung im Entwurfsmodus Wer schon mit Access gearbeitet hat, wird sehr schnell feststellen, daß Access ein sehr einfaches Konzept bei der Fehlerbearbeitung verfolgt. Da es sich bei VBA, der internen Programmiersprache von Access, um eine Interpretersprache handelt, liegen die fehlerverursachenden Programmzeilen noch im Quelltext vor. Daher wird im Fehlerfall von Access das Modul mit der passenden Programmzeile geöffnet und eine Fehlermeldung ausgegeben. Wie man an Abbildung 4.6 erkennen kann, ist dies zum Debuggen einer Anwendung recht hilfreich, ist aber eher hinderlich für normale“ Nutzer einer fertigen Anwendung, da diese durch ” den Programmtext eher verschreckt werden, als daß sie mit dem Fehler umgehen könnten. Um nun einen Fehler programmgesteuert abfangen zu können, stellt VBA ein paar Befehle zum Errorhandling zur Verfügung. Diese Funktionen können auch dazu genutzt werden, eigene Programmfehler zu erzeugen. Die Auswertung dieser selbst erzeugten Fehler kann dann in der aufrufenden Funktion geschehen. Somit kann eine Fehlermeldung von einer Funktion von ARA zur Anwendung zurückgeliefert werden. Der in Abbildung 4.7 gezeigte Programmtext dient nur dazu, den in der Funktion Fehlerhaft() erzeugten Fehler in der aufrufenden Funktion abzufangen und 4.2. AUSGEWÄHLTE ASPEKTE 61 Abbildung 4.7: Programmtext zum Erzeugen und Abfangen eines Fehlers mittels eigenem Programm auszuwerten. Damit nun die Fehlerbehandlung in ARA richtig funktionieren kann, ist auch eine entsprechende Anpassung in der aufrufenden Funktion nötig. Hierbei bleiben dem Anwender allerdings mehrere Möglichkeiten: • Sollte der Anwender nicht daran interessiert sein, ob und welche Fehler in ARA verursacht wurden, weil davon ausgegangen wird, daß sowohl ARA, als auch die übergebene SQL-Funktion absolut fehlerfrei arbeiten, so kann vollkommen auf ein Fehlerhandling verzichtet werden. • Geht der Anwender zwar davon aus, daß Fehler auftreten können, aber er nicht an einer weiteren Verarbeitung der Fehlermeldung interessiert ist, so ist vor dem Aufruf von ARA der Befehl On Error Resume Next und danach On Error Goto 0 zu verwenden. Der erste Befehl bewirkt, daß ein Fehler keinen Sprung in den Unterbrechungsmodus bewirkt, sondern setzt die Ausführung mit der Anweisung fort, die unmittelbar auf die fehlerverursachende Anweisung folgt. Hier wäre dann auch ein Verarbeiten der Fehlermeldung möglich. Dies wäre jedoch nur eine Fehlerbehandlung, die Fehler und Meldungen von ARA erkennen und 62 KAPITEL 4. IMPLEMENTIERUNG bearbeiten könnte. Der zweite Befehl hat zur Folge, daß ab hier wieder alle Fehler zu einem Wechsel in den Unterbrechungsmodus führen. Dadurch ist zum Beispiel ein Debuggen der eigenen Anwendung schrittweise möglich. • Geht man allerdings davon aus, daß der Anwender eine Anwendung schreibt, die alle Fehler abfängt, so wird diese dann im Kopf jeder Prozedur einen Fehlerhandle haben, der ungefähr so aussieht: Public Function min(Wert1 As Long, Wert2 As Long) As Long ’ Minimum von zwei Werten On Error Goto min_Error_handle Am Ende der Funktion könnte dann der Fehler mit folgendem Programmcode weiterverarbeitet werden: Exit Function min_Error_handle: If Err.Number > vbObjectError Then ’Fehlerbehandlung fuer ARA-Meldungen Else ’Fehlerbehandlung fuer Access-Meldungen End If End Function Hierbei muß allerdings die Aufteilung zwischen den ARA-Meldungen und den Access-Meldungen nicht geschehen. Je nach Anwendungsbedarf kann auch eine gemeinsame Verarbeitung der Fehlermeldungen sinnvoll sein. Sollte keine der Möglichkeiten zur Fehlerbehandlung genutzt werden, so wird Access im Falle eines auftretenden Fehlers in den Unterbrechungsmodus wechseln und an der Stelle stehen bleiben, an der die Fehlermeldung von ARA generiert wird. Diese Stelle ist natürlich weder für den Anwender noch für den späteren Nutzer relevant bzw. überhaupt verständlich. Kapitel 5 Zusammenfassung und Ausblick In der Diplomarbeit wurde die Erweiterung ARA entwickelt, die es Anwendern von Access erlaubt, Trigger in einem Programm zu nutzen. Dabei ist in Kapitel 3.1 eine Triggersprache entwickelt worden, die es einem im Umgang mit SQLTriggern erfahrenen Anwender erlaubt, einen leichten Einstieg in die Verwendung der ARA-Trigger zu finden. Ebenso wird ein mit Access erfahrener Anwender schnell die ARA-Trigger nutzen können. Dies ist durch die leichte Eingabe und deren teilweise Beschränkung auf vorgegebene Werte erzielt worden. Abbildung 5.1: Datenbankfenster von Access mit Triggern Die im Kapitel 4 besprochene Implementierung der Triggersprache ARA versucht, sich so weit wie möglich an die von Access vorgegebenen Verfahrensmuster anzunähern. Dies ist nur teilweise gelungen, da die Definition der Trigger nicht mit einem eigenen Reiter im Datenbankfenster abgelegt werden konnte. Wie in 63 64 KAPITEL 5. ZUSAMMENFASSUNG UND AUSBLICK der manipulierten Abbildung 5.1 angedeutet, wäre dies über die Erstellung eines eigenen Datenbankfensters möglich. Zusätzlich zur Anpassung des Datenbankfensters wäre auch noch eine Auslagerung aller Komponenten in einen Assistenten eine sinnvolle Erweiterung zur besseren Nutzung von ARA. Hierdurch wäre es möglich, die Funktionalität von ARA durch einen Assistenten in der Datenbank zur Verfügung zu stellen. Bei der Verwendung eines Assistenten ließe sich auch ein eventuelles Update von ARA einfach in Anwendungen einbringen, die bereits ARA verwenden. Eine Änderung in der Anwendung selbst wäre dann nicht mehr nötig. Um den neuen Versionen von Access gerecht zu werden, ist auch eine Portierung von ARA auf Access 2000 und Access 2002 (Office XP ) vorstellbar. Da aber Einzelheiten, vor allem zu Access 2002, noch recht unbekannt sind, ist bisher unklar, inwieweit ein Zugriff auf die Systemtabellen noch möglich ist bzw. ob es vielleicht sogar möglich ist, schreibend auf die Systemtabellen zuzugreifen. ARA selbst ließe sich auch noch um folgenden Komponenten erweitern: • Triggerdefinition während des Programmablaufes: Durch Einführen des SQLBefehls Create Trigger wäre es möglich, Trigger zu definieren, ohne dabei das Formular zur Triggerverwaltung zu öffnen. Dadurch wären Schemaevolutionen und darauf angepaßte Trigger während der Ausführung des Anwendungsprogramms möglich. Es wird allerdings zusätzlich zumindest noch der SQL-Befehl Drop Trigger zum Löschen des Triggers benötigt. Auch Funktionen zum Auslesen oder Ändern der Trigger wären mit Sicherheit interessant für diesen Anwendungsfall. • Erkennung von Schemaevolution: Durch Feststellen der Schemaänderungen ließen sich Trigger ermitteln, die nach der Änderung nicht mehr korrekt feuern könnten, weil die Trigger auf Spalten operieren, die durch die Änderungen wegfallen würden. Somit könnte man die Fehler reduzieren, die bei der Ausführung von Triggern auftreten können, weil das zugrundeliegende Schema sich seit der Erstellung der Trigger geändert hat. • globales Setzen von Warnings: Die Möglichkeit zum Festlegen erstellen, ob ARA die Warnings beim Zurückgeben der Programmkontrolle wieder freigeben soll, oder ob sie dauerhaft unterdrückt werden können. Da ich ARA für eine interessante Erweiterung halte und sie auch bei zukünftigen Projekten mit Access einsetzen werde, wird es immer wieder Erweiterungen geben. Um die Erweiterungen allen Interessierten zur Verfügung zu stellen, wird es in diesem Zusammenhang eine Homepage unter http://www.muench-stahl.de/sascha/ara geben. Literaturverzeichnis [A] Dan Appleman, com/aktivX-komponenten mit visual basic 6 entwickeln, Markt + Technik [A00] Dan Appleman, Win32 Puzzle-Buch, Galileo Press, 2000 [ANSI] SQL/Foundation ISO/IEC 90075-2:1999 (E) 1999 [BP01] Birgit Pieper, Inkrementelle Integritätsprüfung und Sichtenaktualisierung in SQL, Disertation, Institut für Informatik III, Universität Bonn, 2001 [C70] Edward E. Codd, The relational model for large shared data bases, Communications of the ACM, volume 13, pages 337-387, 1970 [C90] Edward E. Codd, A relational model for databases management, Addison-Wesley, 2. Auflage, 1990 [CPM] Roberta Cochrane, Hamid Pirahesh, Nelson Mattos, Integrating Triggers and Declarative Constraints in SQL Database Systems, Proceedings of the 22nd VLDB Conference Mumbai(Bombay), India, 1996, S. 567 ff [D83] C. J. Date, An Introduction to Database Systems, volume II, AddisonWesley, 1983 [D87] C. J. Date, A guide to the SQL standard, Addison-Wesley, 1987 [D90] C. J. Date, Relational database writings, 1985-1989, Addison-Wesley, 1990 [DS00] Detlef Drews, Heinz Schwab, Workshop Visual Basic 6, Addison-Wesley, 2000 [FAQ] Karl Donaubauer, http://www.donkarl.com, Access-FAQ, 2001 [H00] Rudolf Huttary, visual basic 6 referenz, Markt + Technik, 2000 [HK00] Helmut Kopka, LATEX Band1 Einführung, Addison-Wesley, 3. Auflage, 2000 65 66 LITERATURVERZEICHNIS [HS97] Manfred Hoffbauer, Christoph Spielmann, Access 97 - Das Programmierbuch, SYBEX-Verlag, 1997 [K00] Dominik Kreutz, Entwurf und Implementierung eines Informationssystems für das Prüfungsamt Informatik, Diplomarbeit, Institut für Informatik III, Universität Bonn, 2000 [KBA] Kraftfahrt-Bundesamt, http://www.kba.de/ [KE96] Alfons Kemper, André Eickler, Datenbanksysteme, Oldenbourg, 1996 [KH3] Klaus Oberdahlhoff, KnowHow.mdb Vers. 3.0 - Aufgesammelte Funktionen für Access 97, unter anderem bei http://www.access-guru.de/ [KMC] Krishna Kulkarni, Nelson Mattos, Roberta Cochrane, Active Database Features in SQL3 [M] Peter Monadjemi, Jetzt lerne ich Datenbank-Programmierung mit Visual Basic 6, Markt + Technik [MS96] Microsoft, Erstellen von Anwendungsprogrammen mit Access 97, Microsoft Corporation, 1996 Microsoft [MS97] Microsoft, Microsoft Office 97 Visual Basic Programmierhandbuch, Microsoft Corporation, 1997 [MSDB] Microsoft, http://www.microsoft.com/data, Developer Network, 2001 [MSDN] Microsoft, http://msdn.microsoft.com, Microsoft Developer Network, 2001 [OH97] Microsoft, Onlinehilfe zu Microsoft Access 97, 1997 [P01] Klaus Prinz, Win32 API professionell, Addison-Wesley, 2001 [P83] Pons-Globalwörterbuch, Englisch-deutsch Klett, Stuttgart, 1983 [PT00] Wolfgang Panny, Alfred Taudes, Einführung in den Sprachkern von SQL99, Springer, 2000 Erklärung gemäß §19 (7) der Diplomprüfungsordnung für den Studiengang Informatik an der Rheinischen Friedrich-Wilhelms-Universität Bonn vom 30. Juni 1993: Hiermit erkläre ich, daß ich die vorliegende Diplomarbeit selbständig durchgeführt und keine anderen als die angegebenen Quellen und Hilfsmittel benutzt habe sowie Zitate kenntlich gemacht habe. Erkrath, den 15. Oktober 2001 67