Rheinische Friedrich-Wilhelms-Universität Bonn Institut für Informatik III ARTA Entwurf und Implementierung einer Triggersprache mit Zeitereignissen für Access-Datenbanken Diplomarbeit von Kazem Fadaei August 2002 Betreuer: Prof. Dr. Rainer Manthey Inhaltsverzeichnis 1 Einleitung 1 2 Grundlagen von Datenbanken 2.1 Aktive Datenbanken . . . . . . . . . . . . 2.2 Trigger in SQL3 . . . . . . . . . . . . . . . 2.2.1 Syntax von SQL3-Triggersprache . 2.2.2 Semantik von SQL3-Triggersprache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 8 11 11 13 3 Visual Basic for Applications 3.1 Sprachgrundlagen . . . . . . . . . . . 3.2 Module, Klassenmodule und Objekte 3.3 API . . . . . . . . . . . . . . . . . . 3.4 Fehlerbehandlung in VBA . . . . . . 3.5 Transaktionen in VBA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 18 20 23 26 29 . . . . 31 31 33 36 38 . . . . . . 39 39 47 49 51 53 58 . . . . . . . . . . . . . . . . . . . . . . 63 63 4 MS-Access 4.1 Tabellen und Beziehungen 4.2 Formulare . . . . . . . . . 4.3 Datenzugriffsobjekte . . . 4.4 Sicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Konzept 5.1 Das Konzept eines Präprozessors . . . . 5.2 Syntax von ARTA-Triggersprache . . . . 5.2.1 Syntax eines Triggers . . . . . . . 5.2.2 Syntax eines Eventmusters . . . . 5.3 Semantik der ARTA-Triggerbearbeitung 5.4 Verwaltung von Zeitereignissen . . . . . 6 Implementierung 6.1 Architektur von ARTA I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2 6.3 6.4 6.5 6.6 6.7 Benutzeroberfläche . . . . . . . . . . . . . . . 6.2.1 Verwaltung von Triggern . . . . . . . . 6.2.2 Verwaltung von Ereignismustern . . . . Speichern von Regeln . . . . . . . . . . . . . . Klassen, Module und Objekte von ARTA . . 6.4.1 Klassenmodule . . . . . . . . . . . . . 6.4.2 Module und Objekte . . . . . . . . . . Befehlsausführungssystem . . . . . . . . . . . Fehlerbehandlung . . . . . . . . . . . . . . . . Besondere Aspekte . . . . . . . . . . . . . . . 6.7.1 Trigger Execution Context . . . . . . . 6.7.2 Timer . . . . . . . . . . . . . . . . . . 6.7.3 Behandlung vergangener Zeitereignisse 7 Zusammenfassung und Ausblick II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 67 68 70 72 72 75 77 79 81 81 84 86 89 Kapitel 1 Einleitung Das MSr1 Access-Datenbankmanagementsystem ist ein relationales und passives Datenbankmanagementsystem. In einem passiven Datenbankmanagementsystem werden Operationen ausgeführt, die unmittelbar von Anwendern oder einem Programm angefordert werden. In einem aktiven Datenbankmanagementsystem können aber auch Situationen beschrieben werden, bei denen das Datenbankmanagementsystem automatisch reagiert. Eine Situation in einer Datenbank ist eine Kombination aus einem Datenbankzustand und einem Ereignis. In einem aktiven Datenbankmanagementsystem werden in diesem Sinne interessante Situationen mit Hilfe von aktiven Regeln beschrieben. Aktive Regeln werden auch als ,,Trigger” bezeichnet. Eine aktive Regel besteht aus drei Teilen: Ereignisteil (Event), Bedingungsteil (Condition) und Aktionsteil (Action). Aktive Regeln werden deswegen auch als ECA-Regeln bezeichnet. Ereignisse treten entweder bei Datenbankänderungen (Änderungsereignis) oder bei Erreichen eines bestimmten Zeitpunktes (Zeitereignis) ein. Ein aktives Datenbankmanagementsystem soll Anwendern Werkzeuge anbieten, mit deren Hilfe sie Trigger definieren können. Trigger ist eine andere Bezeichnung zu aktiven Regeln. Dazu soll ein aktives Datenbankmanagementsystem auch Ereignisse erkennen und die Trigger bearbeiten, die durch diese Ereignisse gefeuert werden. In der vorliegenden Arbeit wird es darum gehen, das Access-Datenbankmanagementsystem um eine Triggersprache zu erweitern und diese Erweiterung zu implementieren. Die Erweiterung von Access um eine Triggersprache wird in Form einer Access-Anwendung realisiert. Eine Access-Anwendung ist eine Software, die in der Programmiersprache VBA2 unter Einsatz von AccessKomponenten programmiert wird. Eine Access-Anwendung ist auf das Access1 MS ist die Abkürzung für Microsoft. Im weiteren Verlauf der Arbeit wurde auf das Copyright Symbol von Microsoft verzichtet. 2 Visual Basic for Applications 1 2 KAPITEL 1. EINLEITUNG Programm angewiesen und kann nur unter diesem Programm ausgeführt werden. Das Konzept der in dieser Arbeit vorgestellten und implementierten Triggersprache basiert auf SQL3-Standard. In SQL3 sind Trigger aktive ECA-Regeln, die alle eindeutig benannt sind. In SQL3 wird nur das Auftreten von Änderungsereignissen (ON DELETE, ON INSERT und ON UPDATE) erkannt. Mit der hier vorgestellten und implementierten Triggersprache werden zusätzlich auch Zeitereignisse (absolute und periodische) berücksichtigt. Ein absolutes Zeitereignis tritt nur einmal durch Erreichen eine bestimmten Zeitpunktes ein. Ein periodisches Zeitereignis tritt wiederholt und regelmäßig in gleichen Zeitabständen ein. Zusätzlich zu absoluten und periodischen Zeitereignissen existiert noch ein weiterer Zeitereignistyp: relative Zeitereignisse. Ein relatives Zeitereignis wird durch eine Kombination aus einem absoluten bzw. periodischen Zeitereignis mit einer Zeitdauer spezifiziert, z.B. ,,am zweiten Tag der Schulferien” oder ,,letzten Montag jedes Monats”. Zeitereignisse sind vor allem in objektorientierten Datenbanksystemen realisiert worden, z.B. HiPAC3 (High Performance Active Database System), Ode4 , SAMOS5 (Swiss Active Mechanism Based Object-Oriented Database Systems). In den meisten dieser Systeme werden alle Zeitereignistypen, ,,absolut”, ,,periodisch” und auch relativ spezifiziert. Für jedes Zeitereignismuster soll in dieser Arbeit eine Deadline mit der Einheit ,,Minute” definiert werden. Mit Zuordnung einer Deadline zu einem Zeitereignis(muster) wird angegeben, dass die Bearbeitung von (durch dieses Zeitereignis gefeuerten) Triggern innerhalb dieser deadline angefangen werden muss. Nach Ablauf dieser Deadline wird das Auftreten dieses Zeitereignisses ignoriert und dessen Trigger werden nicht mehr bearbeitet. Um das Konzept von Zeitereignissen in dieser Arbeit zu implementieren, werden Prozeduren der API6 Schnittstelle eingesetzt. Mit Hilfe von API-Schnittstelle werden Timer gesetzt, die in separaten Prozessen im Betriebsystem laufen und deswegen die Datenverarbeitung (in Access) und deren Geschwindigkeit nicht beeinträchtigen. Für das in dieser Arbeit implementierte System wurde der Name ARTA (Active Real Time Access Database system) ausgewählt. Bei einem ARTATrigger wird eine Parameterübergabe vom Bedingungsteil an den Aktionsteil 3 Vgl. [AKHaWi] Vgl. [AKHaWi] 5 Vgl. [AKPat12] 6 Application Programming Interface 4 3 möglich sein, nachdem Parameter eindeutig für den Trigger definiert (deklariert) wurden. Diese Parameter dürfen nur bei Änderungsereignismustern eingesetzt werden. Im Kapitel 2 geht es um die Grundlagen von Datenbanken und im Besonderen von aktiven Datenbanken. Anschließend wird ein kurzer Exkurs zu Echtzeitsystemen (Real Time Systems) und aktiven Echtzeitdatenbanksystemen (Aktive Real Time Database systems) gegeben. In Kapitel 3 wird die Programmiersprache VBA und deren Einsatz in Access dargestellt. Access und seine Komponenten werden im Kapitel 4 untersucht. Formularklassenmodule und Formularereignisse, die bei der Implementierung von ARTA eine wichtige Rolle spielen, werden besonders beschrieben. Das Konzept für ein aktives Access-Datenbankmanagementsystem ist Thema von Kapitel 5. Dort wird gezeigt, dass aufgrund der Ereigniserkennungsproblematik, besonders bei Änderungsereignissen, der Entwurf eines Präprozessors in Form einer Access-Anwendung unvermeidbar ist. Im Kapitel 6 werden die bei der Implementierung von ARTA angewandten Methoden beschrieben. Im weiteren werden die ARTA-Klassen(module) und die aus diesen Klassen initiierten Objekte beschrieben, die bei der Implementierung von ARTA eingesetzt werden. Das Kapitel 7 gibt eine Zusammenfassung, der einige Vorschläge zur Weiterentwicklung der Arbeit folgen. 4 KAPITEL 1. EINLEITUNG Kapitel 2 Grundlagen von Datenbanken In diesem Kapitel werden Grundbegriffe von Datenbanken beschrieben. Industrie und Forschung sehen sich heute mit gewaltigen Mengen von Informationen konfrontiert und werden von der sogenannten Informationsflut überrollt. Dabei lässt sich in jeder Hinsicht darüber streiten, wie der Begriff ,,Information” zu definieren ist. Für diese Arbeit soll die folgende Erklärung genügen: ,,Information” ist jede Art von Wissen, das für eine Gruppe von Menschen von Interesse ist und verwaltet werden kann. Beispielsweise sind diese Informationen über Telefonkunden einer Telefongesellschaft, Informationen über genetische Merkmale von Menschen (eines Landes), Informationen über Diplomanden an der Universität Bonn. Informationen dieser Menge können nicht mehr ausschließlich mit traditionellen Methoden verwaltet werden. Diese können z.B. nicht mehr nur als Dokumente irgendwo archiviert werden, da sie später - nicht zuletzt aufgrund ihres Umfanges - nur mit großem Aufwand wieder auffindbar sind. Heutzutage bieten Computer die Möglichkeit Informationen platzsparender zu bewahren und sie mit hoher Geschwindigkeit wieder aufzufinden; An dieser Stelle kommen Begriffe wie ,,Datenbank”, ,,Datenbanksystem” und ,,Datenbank Mangement System” ins Spiel. Eine Datenbank ist eine Sammlung von zusammengehörenden Informationen, z.B. Informationen über Kontoinhaber einer Bankfiliale oder Informationen über die eingeschriebenen Informatikstudenten an der Universität Bonn. Eine Datenbank wird indirekt von einem Mensch verwaltet. ,,Indirekt” bedeutet, dass der Datenbankbenutzer einem System seine Anforderungen angibt. Danach werden diese Anforderungen in dem System überprüft und ausgeführt. Dieses System ist eine Software, die ,,Datenbank Management System” (DBMS) genannt wird. Das Datenbankmanagementsystem darf nicht von Daten (Informa5 6 KAPITEL 2. GRUNDLAGEN VON DATENBANKEN tionen) abhängig sein, d.h. ein Datenbankmanagementsystem muss in der Lage sein jede Art von Daten zu speichern, und soll dem Benutzer die notwendigen Mechanismen anbieten, um diese Daten verwalten zu können. Die Kombination eines Datenbankmanagementsystems mit mehreren Datenbanken nennt sich ein Datenbanksystem. Das Bild 2.1 stellt ein Datenbanksystem (DBS) dar, das aus einem Datenbankmanagementsystem (DBMS) und mehreren Datenbanken (DBn ) besteht. Benutzer DBS DBMS DB 1 DB 2 ... DB n Abbildung 2.1: Datenbankmanagementsystem und Datenbanksystem Außer Anwendungsunabhängigkeit bzw. Datenunabhägigkeit zeichnet sich ein Datenbankmanagementsystem besonders durch die folgenden Eigenschaften aus: • Dauerhaftigkeit: Ein Datenbankmanagementsystem hat die Aufgabe, die Daten solange aufzubewahren, bis sie von einem Benutzer absichtlich gelöscht werden. Die Daten in einer Datenbank dürfen nicht ohne direkte oder indirekte Benutzeranforderung gelöscht werden bzw. verloren gehen. • Integrität: Eine Datenbank darf keine unsinnigen (inkonsistenten) Daten beinhalten, d.h. die Daten müssen der Realität entsprechen. Ein Datenbankmanagementsystem soll dem Benutzer Werkzeuge anbieten, mit deren Hilfe er Integritätsregeln aufstellen kann. Nach der Definition der Integritätsregeln wird ein Datenbankmanagementsystem beachten, dass keine Daten entstehen können, die diesen Regeln widersprechen. 7 • Benutzerfreundlichkeit: Ein Datenbankmanagementsystem sollte so einfach wie möglich zu bedienen sein. Das bedeutet, dass dem Endbenutzer eine Sprachschnittstelle zur Verfügung gestellt wird. Dabei soll ein Datenbankmanagementsystem auch in der Lage sein, die Nutzung der Datenbank von mehreren Benutzern zu ermöglichen. Das relationale Model ist das in heutigen Datenbankmanagementsystemen am weitesten verbreitete Datenmodel. Eine Datenbank im relationalen Model bezeichnet man als eine relationale Datenbank. Im Falle einer relationalen Datenbank besteht diese aus einer Anzahl von ,,Relationen”. Eine Relation bezeichnet auch eine ,,Datenbasis” einer relationalen Datenbank. Jede Relation ist eine Menge von Tupeln. Ein Tupel ist ein Datensatz, der aus Attributen besteht. Ein Datensatz ist ein atomares Element einer Relation. Jede Relation erfüllt die Bedingung, keine zwei gleichen Tupeln besitzen zu dürfen. Jede Relation hat einen eindeutigen Namen innerhalb einer Datenbank. Jedes Attribut der Tupel einer Relation hat einen eindeutigen Namen innerhalb der Relation. Die Reihenfolge von Tupeln in einer Relation sind nicht zu beachten. Eine Relation kann als eine Tabelle angesehen werden, deren Zeilen die Tupeln und deren Spalten die Attribute repräsentieren. Name Fadaei Herrmann Schmidt Schumacher ... Vorname Kazem Joachim Michael Beate ... Student Mtr.Nr Email 658698 [email protected] 19911000 [email protected] 19951000 [email protected] 21869458 [email protected] ... ... Beispiel 2.1: Die Studenten-Tabelle In dem Beispiel 2.1 ist eine Relation in Form einer Tabelle gegeben. Die Tabelle enthält Informationen über eine Anzahl von Studenten. Jede Zeile repräsentiert Informationen über eine Studentin bzw. einen Studenten. Die Informationen jeder Zeile teilen sich in vier Teile, die verschiedene Attribute zu jedem Datensatz beinhalten. 8 KAPITEL 2. GRUNDLAGEN VON DATENBANKEN 2.1 Aktive Datenbanken In der Regeln verhalten sich die traditionellen Datenbanken passiv. In einer passiven Datenbank wird jede Operation (vor allem delete, insert bzw. update) nur dann ausgeführt, wenn sie unmittelbar von einem Benutzer oder einem Programm angefordert wird. Von aktiven Datenbanken wird erwartet, auf bestimmte Situationen in einer Datenbank automatisch zu reagieren und bestimmte Aufgaben zu erledigen. ,,Ein Datenbanksystem heißt aktiv, wenn es zusätzlich zu den üblichen (passiven) DBS-Fähigkeiten in der Lage ist, definierbare Situationen in der Datenbank (und wenn möglich darüber hinaus) zu erkennen und als Folge davon bestimmte (ebenfalls definierbare) Reaktionen auszulösen.”1 Ein aktives Datenbankmanagementsystem soll dem Benutzer einen Mechanismus anbieten, um dem Datenbankmanagementsystem mitteilen zu können, in welcher Situation welche Operation auszuführen ist. Es stellt dazu eine Regelsprache zur Verfügung, mit deren Hilfe sogenannte aktiven Regeln definiert und in einer bestimmten Form (Wissensmodel) gespeichert werden können. Eine aktive Regel besteht aus drei Teilen: Ereignisteil (Event), Bedingungsteil (Condition) und Aktionsteil (Action). Die aktiven Regeln heißen deswegen ECA-Regeln. Eine andere Bezeichnung für aktive Regeln ist das Wort ,,Trigger”. Die Menge aller aktiven Regeln in einer Datenbank heißt Regelbasis. Mit der Definition einer Regel wird das Datenbanksystem aufgefordert, beim Eintreffen des Ereignisses E die Aktion A auszuführen, wenn die Bedingung C erfüllt ist. Die Kombination aus Ereignis und Bedingung eines Triggers bezeichnet eine für den Trigger ,,interessante Situation”, bei der die Triggeraktion ausgeführt werden soll. Trifft das Ereignis E ein, so ist die Regel (der Trigger) mit dem Ereignisteil E zu feuern (bzw. auszulösen oder zu triggern). Ereignis: Ein Ereignis ist ein Geschehen in einer Datenbank an einem bestimmten Zeitpunkt2 : ein punktuelles Geschehen. Ein Ereignis kann primitiv oder zusammengesetzt sein. • Primitive Ereignisse sind einfache bzw. atomare Ereignisse, z.B. ,,ON DELETE FROM tablename”. 1 2 [AKDiGa], S.7. [AKPaDi] 2.1. AKTIVE DATENBANKEN 9 • Zusammengesetzte Ereignisse sind Ereignisse, die sich aus mehreren primitiven Ereignisse unter Benutzung von Verknüpfungsoperationen3 bilden, z.B. ,,ON INSERT TO table1 AND ON UPDATE OF table2 ”. Ein primitives Ereignis kann wiederum entweder ein Datenbankereignis (Änderungsereignis) oder ein Zeitereignis sein. Ein Datenbankereignis tritt bei Änderung an einer Tabelle bzw. an einem Feld dieser Tabelle ein, d.h. beim Löschen (delete), Hinzufügen (insert) und Modifizieren (update). Zeitereignisse Ein Zeitereignis tritt bei Erreichen eines Zeitpunktes ein. Zeitereignissse können in drei Formen spezifiziert werden: • Ein absolutes Zeitereignis tritt nur einmal an einem eindeutig definierten Zeitpunkt ein, z.B. ,,Am 31.08.2002 um 14:55 Uhr”. • Ein periodisches Zeitereignis tritt in regelmäßigen Zeitabständen wiederholt ein, z.B. ,,Jeden Mittwoch um 10:00 Uhr”. • Ein relatives Zeitereignis ist eine Kombination aus einem absoluten bzw. periodischen Zeitereignis mit einer Zeitdauer, z.B. ,,Ein Tag vor dem Semesterende” oder ,,Am dritten Arbeitstag jedes Monats”. Bei der Beschreibung von Zeitereignissen stößt man unweigerlich auf den Begriff ,,Aktives Echzeit-Datenbanksystem (Aktive Real Time DBS)”, das unten kurz beschrieben wird. Ein Computersystem heißt Echtzeitsystem (Real Time System, RTS), wenn das System ein korrektes Ergebnis nur innerhalb eines bestimmten Zeitintervalls produziert. Außerhalb dieses Zeitintervalls gibt das RTS bestenfalls kein Ergebnis zurück. Ein aktives Datenbanksystem, das die ,,Echtzeit”-Eigenschaft erweist, wird aktives Echtzeit-Datenbanksystem (Aktive Real Time Database System, ARTDBS) genannt. Zeitereignisse sind die wichtigsten Aspekte eines ARTDBS. In einem ARTDBS soll genau beobachtet werden, wann ein Zeitereignis eintritt und wie lange dessen Bearbeitung dauert. Die Genauigkeit (die Granularität) der Zeitmessung hängt von dem jeweiligen System (Computer bzw. Netz) ab. Als Granularität, der kleinsten Zeiteinheit in einem ARTDBS, wird normalerweise eine ,,Minute” genommen. Die Syntax einer aktiven Regel in einem ARTDBS kann wie folgt aussehen4 : 3 4 Operationen aus Ereignisalgebra: OR, AND, NOT, Sequenz Vgl. [AKPat21], S410 10 KAPITEL 2. GRUNDLAGEN VON DATENBANKEN ON event E IF condition C DO <COMPLETE> action A <WITHIN t seconds> Es gibt noch Fragen, die durch die Semantik beantwortet werden müssen. Eine Regel R1 in einem ARTDBS könnte z.B. wie folgt aussehen: ON 31.08.2002 14:55 IF Bedingung C1 DO Aktion A1 innerhalb von 5 Minuten. Die erlaubte Zeitdauer ist bei diesem Beispiel (R1 ) im Aktionsteil festgeschrieben. Die Semantik einer derartigen Regel könnte z.B. folgendermaßen lauten: Falls die Bedingung C1 am 31.08.2002 um 14:55 Uhr (t:=31.08.2002 14:55) gilt (erfüllt ist), soll die Aktion A1 ausgeführt werden und muss innerhalb von 5 Minuten (bis zum 31.08.2002 um 15:00 Uhr [oder bis t+5 Minute]) abgearbeitet sein. Im Bild 2.2 wurde die Bearbeitung dieser Regel bezüglich einer Zeitachse geschildert. 31.08.2002 14:55 31.08.2002 15:00 Zeit / Minute Bearbeitungsanfang Obergrenze für die Bearbeitung Abbildung 2.2: Zeitgrenze von 5 Minute Betrachten wir nun die Regel R2 mit einer anderen Syntax: ON t in [31.08.2002 14:55, 31.08.2002 15:30] IF Bedingung C2 DO Aktion A2. Die Ausführung von A2 wird angefangen, wenn die Bedingung C2 innerhalb des Zeitintervalls ,,[31.08.2002 14:55, 31.08.2002 15:30]” erfüllt ist. Der Unterschied zwischen R1 und R2 zeigt sich, wenn das System den Eintritt des Zeitereignisses nicht direkt am 31.08.2002 um 14:55 Uhr, sondern (Sekunden bzw. Minuten) später meldet. In einer solchen Situation beginnt das System beide 2.2. TRIGGER IN SQL3 11 Aktionen auszuführen (unter Annahme, dass beide Bedingungen erfüllt sind). Die Ausführung der Aktion der ersten Regel (R1 .A1 ) gibt kein (korrektes) Ergebnis, falls die Ausführung über die (Zeit-) Obergrenze hinaus geht. Die Aktion der zweiten Regel (R2 .A2 ) wird vollständig ausgeführt, ohne die (Zeit-) Obergrenze zu beachten. Wenn diese Regeln (R1 , R2 ) direkt am ,,[31.08.2002 14:55, 31.08.2002 14:55]” gefeuert werden und das System mit der Bearbeitung bis ,,[31.08.2002 14:55, 31.08.2002 15:30]” fertig ist, so haben beide Regeln dieselbe Wirkung. Das folgende Beispiel, Regel R3 , kombiniert die jeweilige Syntax der beiden oben angegebenen Regeln: ON t in [31.08.2002 14:55, 31.08.2002 15:30] IF Bedingung C3 DO Aktion A3 innerhalb von 5 Minuten. Diese Regel darf innerhalb des Zeitintervalls [31.08.2002 14:55, 31.08.2002 15:30] gefeuert werden. Wenn C3 erfüllt ist, soll die Bearbeitung von A3 innerhalb 5 Minuten (t+5 Minute) fertig sein5 . 2.2 Trigger in SQL3 SQL, Structure Query Language, ist eine weit verbreitete Sprache zur Definition und Manipulation von relationalen Datenbanken. Die erste standardisierte Version (SQL-86) wurde im Jahre 1986 realisiert und die aktuellste standardisierte Version von SQL ist SQL3. Der Einsatz der Spracherweiterung um die Triggerfunktionalität wurde zum ersten Mal in SQL3 vorgesehen, obwohl die Triggerfunktionalität bei manchen SQL2-Implementierungen schon bereits unterstützt wurde. Ein Trigger ist in SQL3 eine (mit einem eindeutigen Namen) gekennzeichnete ECA-Regel. Ein SQL3-Trigger besteht aus folgenden Komponenten: einer Tabelle, dem Datenmanipulationsbefehl (DML-Operation), einer Bedingung und einer Aktion. Ein Trigger wird in SQL3 explizit durch den Befehl ,,CREATE TRIGGER” definiert. 2.2.1 Syntax von SQL3-Triggersprache Die Syntax für die Trigger-Definition in SQL3 sieht wie folgt aus: <trigger definition> ::= CREATE TRIGGER <trigger name> 5 In der vorliegenden Arbeit werden Zeitereignisse der Form von R2 unterstützt. 12 KAPITEL 2. GRUNDLAGEN VON DATENBANKEN <trigger action time> <trigger event> ON <table name> [ REFERENCING <old new values alias list>] <triggered action> <trigger action time> ::= BEFORE | AFTER <trigger event> ::= INSERT | DELETE | UPDATE [ OF <trigger column list> ] <trigger column list> ::= <column name 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 <old or new values alias list> ::= <old or new values alias>... <old 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> <old values table alias> ::=<identifier> <new values table alias> ::=<identifier> <old values correlation name> ::= <correlation name> 2.2. TRIGGER IN SQL3 13 <new values correlation name> ::= <correlation name> Dabei bedeuten die Parameter wie folgt: <column name list> ist eine Liste bestehend aus Spaltennamen der betroffenen Tabelle. In der Liste darf keine Spalte doppelt oder gar mehrfach vorkommen. <left paren> <search condition> <right paren>: Mit Diesem Ausdruck wird eine Bedingung formuliert. Der linke und rechte Teil können Parameter oder Konstanten sein, die durch den Vergleichsoperator (<search condition>) miteinander verglichen werden. <SQL procedure statement> ist ein SQL-Ausdruck. <identifier>’s sind eindeutige Namen für den alten bzw. neuen Zustand der betroffene Tabelle. <correlation name> sind eindeutige Namen für den alten bzw. neuen Zustand der betroffene Zeilen. 2.2.2 Semantik von SQL3-Triggersprache Ein SQL-Ausdruck kann mehrere Trigger feuern. Die gefeuerten Trigger sind entweder Before- oder Aftertrigger. Zusätzlich kann ein Trigger ein ROW-Level (FOR EACH ROW) oder ein STATEMENT-Level (FOR EACH STATEMENT) sein. Als ein wichtiger Punkt ist bei der Triggerbearbeitung zu beachten: Ein Beforetrigger darf keine weiteren Trigger feuern, d.h. wenn die Bedingung eines gefeuerten Beforetriggers erfüllt ist, soll die Aktion des Triggers direkt ausgeführt werden, ohne darauf zu achten, ob durch die Ausführung weitere Trigger gefeuert werden könnten. Die Semantik der Triggerbearbeitung in SQL3 ist wie folgt: 1. Der SQL-Ausdruck ,,Si ” soll ausgeführt werden. Tritt ein Datenbankereignis E aufgrund der Ausführung dieses SQL-Ausdruck ,,Si ” ein , so werden an dieser Stelle drei Parameter berechnet: (a) die Menge der Beforetrigger BTrs, die durch E gefeuert werden, (b) die Menge der Aftertrigger ATrs, die durch E gefeuert werden, (c) den sogenannten ,,Trigger Execution Context” (T ECi ), der aus folgenden Teilen besteht : • dem Namen der betroffenen Tabelle, • dem Datenmanipulationsbefehl (DELETE, INSERT oder UPDATE), • einer Menge von Transitionen bestehend aus zwei temporären Tabellen: 14 KAPITEL 2. GRUNDLAGEN VON DATENBANKEN i. OLD TABLE beinhaltet die alten Werte der Betroffenen Zeilen (vor der Ausführung des SQL-Befehls), ii. NEW TABLE beinhaltet die neuen Werte der Betroffenen Zeilen (nach der Ausführung des SQL-Befehls). 2. Ist die Menge der Transitionen leer (keine Datenänderung), sollen alle ROW-level Trigger aus der Menge der Beforetrigger ,,BTrs” entfernt werden. 3. Die Trigger in der Menge BTrs werden nach dem Erzeugungsdatum sortiert. Nach dem Sortieren der ,,BTrs” wird jeder Trigger ,,Tr” aus dieser Menge wie folgt bearbeitet: • Ist ,,Tr” ein STATEMENT-Level Trigger, so wird die Tr.Condition ausgewertet. Ist das Ergebnis = ,,True”, wird jedes STATEMENT der Tr.Action ausgeführt. • Ist ,,Tr” ein LOW-Level Trigger, so wird für jede Transition: die Tr.Condition ausgewertet. Gibt die Auswertung ,,True” zurück, wird jedes STATEMENT von Tr.Action ausgeführt. 4. An dieser Stelle wird der SQL Ausdruck ,,Si ” ausgeführt. 5. Die Integrität der Datenbank wird hier getestet und ggf. die Folgeänderungen vorgenommen6 . 6. Ist die Menge der Transitionen leer (keine Datenänderung), werden alle ROW-level Trigger aus der Menge Aftertrigger ,,ATrs” entfernt. 7. Trigger in der Menge ATrs werden nach dem Erzeugungsdatum sortiert. Nach dem Sortieren der ,,ATrs” wird jeder Trigger ,,Tr” aus dieser Menge wie folgt bearbeitet: • Ist ,,Tr” ein STATEMENT-Level Trigger, so wird die Tr.Condition ausgewertet. Ist das Ergebnis der Auswertung = ,,True”, wird jedes STATEMENT der Tr.Action als neue Eingabe ,,Sj ” an den Schritt (1.) weitergegeben. • Ist ,,Tr” ein LOW-Level Trigger, so wird für jede Transition: die Tr.Condition ausgewertet. Gibt die Auswertung ,,True” zurück, wird die Ausführung jedes STATEMENTs ,,Sj ” von Tr.Action an dem Schritt (1.) weiter durchgeführt. 6 Vgl. [AKPat10], [SQLMeSi], [SQLStnd], [SQLTrKo] 2.2. TRIGGER IN SQL3 15 Vor der Auswertung einer Triggerbedingung sowie vor der Bestimmung eines neuen SQL-Befehls aus der Triggeraktion sollen die Parameter (OLD TABLE, NEW TABLE, OLD ROW und NEW ROW) aus der Menge der Transitionen berechnet und in Triggerbedingungen bzw. in Triggeraktionen eingesetzt werden. 16 KAPITEL 2. GRUNDLAGEN VON DATENBANKEN Kapitel 3 Visual Basic for Applications In diesem Kapitel wird die Programmiersprache ,,Visual Basic for Applications” (VBA) beschrieben. VBA ist wie jede andere gut entwickelte Programmiersprache sehr umfangreich und deswegen in einem Kapitel dieser Arbeit nicht vollständig darzustellen. In diesem Kapitel wird eine kurze Einführung in diese Programmiersprache gegeben. VBA wird in dem Umfang beschrieben, wie es zum Verständnis der Arbeit erforderlich ist1 . VBA ist ein Dialekt der Programmiersprache Basic und hat viele Gemeinsamkeiten mit der Programmiersprache Visual Basic(VB), die selbst ein Dialekt von Basic ist. VBA ist keine eigenständige Programmiersprache, d.h. ein VBA-Programm ist an das Anwendungsprogramm gebunden, in dem es entwickelt wurde. Ursprünglich wurde VBA dazu konzipiert, als Makroprogrammiersprache in MS-Office-Komponenten eingesetzt zu werden. Anwendungen werden unter MS-Office-Komponenten vor allem in MS-Access und MS-Excel mit VBA programmiert. Es existieren keine grundlegenden Unterschiede zwischen VB und VBA. Jedoch liegt ein wesentlicher Unterschied darin, dass die VBA-Programme direkt an MS-Office-Komponenten gebunden sind und alleine nicht existieren können. Wenn auch VBA eine weitaus größere Anwendungslandschaft innehat, wird an dieser Stelle nur von VBA in Verbindung mit Access die Rede sein, da in dieser Arbeit der Einsatz der VBA in Access im Vordergrund steht. Mit Hilfe der VBA-Befehle können Access Komponenten und ihr Verhalten kontrolliert werden. Dies kommt vor allem bei Formularereignissen in Form der ,,Code behind Forms” vor, in denen der VBACode vollständig mit dem Formular verbunden ist. 1 Ausführlich wurde VBA vor allem in [A00AN], [A00BK], [A97AN], [A0SQLBB] und [VB6Ko] beschrieben. 17 18 3.1 KAPITEL 3. VISUAL BASIC FOR APPLICATIONS Sprachgrundlagen Variablen sind die Elemente, die in einem Programm vorübergehend Inhalte bekommen und genutzt werden, um Berechnungen in dem Programm durchzuführen. Variablen sollen vor ihrer Verwendung deklariert werden, d.h. der Kompiler soll über ihrer Existenz informiert werden. In VBA können die Variablen auf zwei verschiedene Arten deklariert werden: die implizite und die explizite Art der Deklaration. Bei der impliziten Deklaration werden die Variablen direkt und automatisch durch deren erste Anwendung im Programm deklariert. Bei expliziten Deklarationen müssen Variablen zusätzlich vor deren ersten Anwendung deklariert werden. Mit dem Befehl ,,Dim Variablename [As typ]” erfolgt die Deklaration einer Variablen mit dem Namen Variblename vom Datentyp typ. Im Buch [A00AN] wird empfohlen, die Variablen bereits vor der ersten Anwendung zu deklarieren. Indem der Ausdruck ,,option Explicit” am Anfang des Codes eingesetzt wird, kann der Programmierer zur Deklaration der Variablen vor deren ersten Anwendung gezwungen werden. Wird dieser Befehl am Anfang eines jeden Codes hinzugefügt, so wird der VBA-Kompiler benachrichtigt: Jede Variable muss vor dem ersten Gebrauch in diesem Code deklariert werden. Beim Erzeugen neuer Codes, wird dann der Befehl Option Explicit am Anfang des Codes automatisch hinzugefügt, wenn der dafür vorgesehene Eintrag im VBAFenster [Extras → Optionen → Editor → Code-Einstellung (Variablendeklaration erforderlich)] markiert wird. Der Datentyp einer Variablen gibt an, welche Art von Inhalten dieser Variable zugewiesen werden dürfen. Der Datentyp kann eine Zahl, eine Zeichenfolge, ein Datum, etc. bezeichnen. Wird bei der Deklaration einer Variable kein Datentyp angegeben, so ist der Datentyp standardmäßig Variant. Variant kann jede Art von Inhalten speichern. Folgende Datentypen können zur Deklaration einer Variablen verwendet werden: Byte, Boolean, Integer, Long, Single, Double, Currency, Decimal, Date, Object, String, Variant(mit Zahlen), benutzerdefinierter Datentyp. Konstanten sind Variablen, deren Inhalte nicht veränderbar sind. Die Inhalte von Konstanten werden in einem Programm nur bei der Deklaration zugewiesen. Konstanten beinhalten in der Regel bestimmte globale Größen. Solche Größen werden häufig im Programm und zwar in verschiedenen Modulen und Klassenmodulen gebraucht und verwendet. Die manuellen Änderungen einer 3.1. SPRACHGRUNDLAGEN 19 Konstanten, d.h. die Änderung der Zeile, in der diese Konstante deklariert wurde, kann eine Änderung an allen Stellen verursachen, in denen diese Konstante angewendet wurde. Der Gültigkeitsbereich von Variablen wird durch die Schlüsselwörter Private, Public und Static definiert, z.B. ,,Private strPrivaterText As String”, ,,Public strGlobalerText As String”, ,,Static strStatistigerText As String”. Eine in einer Prozedur deklarierte Variable ist nur innerhalb dieser Prozedur gültig. Eine Variable darf nur innerhalb einer Prozedur als Static deklariert werden, und sie behält ihren Inhalt auch nach dem Ende der Prozedurausführung. Eine Variable darf nur am Anfang eines Moduls bzw. Klassenmoduls als Private oder Public deklariert werden. Als Private deklarierte Variablen haben ihre Gültigkeit nur innerhalb eines Moduls bzw. innerhalb eines Klassenmoduls. Sie dürfen von jeder Prozedur in diesem Modul bzw. in diesem Klassenmodul angewendet werden. Die als Public deklarierten Variablen sind von jeder Komponente in Access nutzbar. In VBA existieren zwei Arten von Prozeduren, Sub und Function. Beide können als Public, Private oder/und Static deklariert werden. Diese Prozeduren lassen sich wie folgt definieren: [Private | Public] [Static] Sub name([Argumentliste]) [Anweisungen] [Exit Sub] [Anweisungen] End Sub [Private | Public][Static]Function name([Argumentliste])[As Typ] [Anweisungen] [name=Ausdruck] [Exit Function] [Anweisungen] [name=Ausdruck] End Funktion Die Argumentliste beinhaltet Variablen, die an die Prozedur übergeben werden. Die Variablen der Argumentliste werden genauso deklariert, wie sie in einem Modul oder Klassenmodul deklariert werden. Diese Variablen werden mit Kommata voneinander getrennt. In dem folgenden Beispiel wurde eine SubProzedur ,,Test” mit vier Eingabe-Parametern definiert. 20 KAPITEL 3. VISUAL BASIC FOR APPLICATIONS public Sub Test(ByVal dbl als Double, _ ByRef str as Sting, _ int as Integer_ var) Anweisungen End Sub Im Beispiel wurden auch neue Begriffe verwendet: ByVal und ByRef. Innerhalb der Prozedur ,,Test” können die Variablen dbl, str, int und var verwendet werden, dabei dürfen auch die Inhalte dieser Variablen geändert werden. Wenn die Variablen als ByRef deklariert sind, werden die Variablenänderungen nach der vollständigen Prozedurausführung an das aufrufenden Programm weitergeleitet. Alle Variablen, die als ByVal deklariert sind, behalten ihre ursprünglichen Inhalte. Die Standardeinstellung bei der Deklaration einer Variablen in der Argumentliste ist ByRef. Im Beispiel werden die Variablen int und var standardmäßig als Reference an die Prozedur übergeben. Es wird empfohlen die Übergabevariablen immer als ByVal zu deklarieren, damit die Werte nicht versehentlich geändert werden. Die Zeilen werden in VBA mit einem Hochkomma am Anfang jeder Zeile als Kommentar bezeichnet, und werden vom Kompiler ignoriert. Weitere Ausführungen zu den grundlegenden Sprachelementen von VBA, z.B. IF-Anweisungen, Schleifen, Sprungbefehle, etc. finden sich in der o.a. Literatur. 3.2 Module, Klassenmodule und Objekte Es lässt sich darüber streiten, ob VBA eine Objektorientierte Programmiersprache ist oder nicht. Aus der Sicht einer traditionellen, rein prozeduralen Sprache wie c ist VBA als eine objektorientierte Programmiersprache einzustufen. Aus der Sicht einer objektorientierten Sprache wie c++ oder Java ist VBA als eine nicht vollständig objektorientierte Programmiersprache anzusehen, da beispielsweise die Vererbung in VBA nicht existiert. In VBA sind Objekte wie in jeder objektorientierten Programmiersprache beteiligt. Objekte: Im allgemeinen ist ein Objekt in VBA ein programmierbares Element. Formulare, Tabellen, Berichte und sogar auch die in Formularen vorhandenen Kontrollelemente, werden in VBA als Objekte bezeichnet. Ein Objekt ist eine Sammlung von Eigenschaften und Methoden. Eigenschaften sind in diesem Sinne die Attribute eines Objektes, die das Verhalten dieses Objektes beeinflussen. Zum Beispiel ist die Hintergrundfarbe eines Formulars eine Eigenschaft dieses Objektes. 3.2. MODULE, KLASSENMODULE UND OBJEKTE 21 Eine (Objekt)Methode ist eine Funktion, die im Objekt eingebaut ist und eine bestimmte Aufgabe erledigt, z.B. die Prozedur ,,Private Sub Form Open(Cancel As Integer)” wird innerhalb eines Formular-Klassenmoduls eingesetzt und programmiert. Diese Prozedur wird später beim Öffnen des Formulars ausgeführt. Die Eigenschaften und Methoden können als Private oder Public definiert sein. Die als Private deklarierten Eigenschaften bzw. Methoden dürfen nur von Methoden benutzt werden, die in demselben Objekt definiert sind. Die hingegen als Public definierten Methoden können überall dort aufgerufen werden, wo das Objekt angewendet wird. Es gibt in VBA unter Access zwei verschiedene Objektarten. Zum einen gibt es die Access-Objekte (die Access-Komponenten), zum anderen Objekte, die vom Programmierer erzeugt werden können. Die Access-Objekte stehen in einer Hierarchie auf verschiedenen Stufen: Die AccessObjekthierarchie wurde in dem Buch [A00AN] im Kapitel 9 geschildert und ausführlich beschrieben. Was aber in ARTA eine wichtige Rolle spielt, ist der Begriff ,,Auflistung”. Eine Auflistung in der Access-Hierarchie ist eine Menge von Objekten des gleichen Typs. Diese Objekte sind in der Auflistung in einer vorbestimmten Reihenfolge zusammengefasst. Eine Auflistung kann in einer Schleife ,,durchlaufen” werden. Beim ,,Durchlaufen einer Auflistung” wird auf jedes Objekt und dessen Eigenschaften und Methoden zugegriffen. Zum Beispiel enthält die Auflistung Forms alle Formulare, die in einer Access-Datenbank vorhanden sind. Die Auflistung Controls enthält alle Steuerelemente eines Formulars oder eines Berichts. In dem folgenden Beispiel2 durchläuft eine Prozedur (,,AlleOffenenFormulare”) die Auflistung ,,Forms” und gibt die Namen aller Formulare in dieser Auflistung aus. Dann wird die Auflistung Controls jedes Formulars durchlaufen und die Namen aller auf dem Formular befindlichen Steuerelemente ausgegeben. In dem Beispiel sind auch Teile der Objekthierarchie angegeben: Forms → {Form1 , Form2 , . . . } und Formn → {Control1 , Control2 , . . . } mit n∈ {1, 2, . . . }. Außer der in Access existierenden Auflistungen können auch benutzerdefinierte Auflistungen erzeugt und programmiert werden, die AccessKomponenten bzw. benutzerdefinierte Objekte beinhalten können. In VBA besteht ebenfalls die Möglichkeit, Objekte zu programmieren, die in solchen Auflistungen zusammengefasst werden. Klassenmodule spielen in diesem Zusammenhang eine wichtige Rolle. Aus diesem Grund werden im folgenden Klassenmodule und deren enge Verwandte ,,Module” gesondert beschrieben. Eine Prozedur wird in VBA innerhalb einer Umgebung definiert. Diese Umgebung kann ein Modul oder ein Klassenmodul sein. 2 Das Beispiel ist aus der Online-Hilfe von Access 2000 übernommen. 22 KAPITEL 3. VISUAL BASIC FOR APPLICATIONS Sub AlleOffenenFormulare() Dim frm As Form, ctl As Control ’ Forms-Auflistung durchlaufen. For Each frm In Forms ’ Name des Formulars ausgeben. Debug.Print frm.Name ’ Controls-Auflistung jedes Formulars durchlaufen. For Each ctl In frm.Controls ’ Namen der Steuerelemente ausgeben. Debug.Print ">>>"; ctl.Name Next ctl Next frm End Sub Ein Modul ist eine Sammlung von Prozeduren. Module lassen sich als Werkzeug von anderen Objekten nutzen. In einem Modul dürfen Prozeduren als Privat oder Public definiert werden. Die als Private definierten Prozeduren können nur den im selben Modul existierenden Prozeduren als Hilfsfunktionen dienen. Die in einem Modul als Public definierten Prozeduren können von Funktionen in anderen Modulen oder Objekten aufgerufen werden. Ein Modul erzeugt man, indem man mit der Maus im VBA-Fenster (auf Einfügen → Modul) anklickt. Ein Modul muss unter einem eindeutigen Namen in der Access-Datenbank gespeichert werden. Klassenmodule sind im Gegensatz zu Modulen umfangreicher, da sie mehr Funktionalitäten beinhalten. Ein Klassenmodul wird durch Mausklick (auf Einfügen → Klassenmodul) erzeugt und muss unter einen eindeutigen Namen gespeichert werden. Klassenmodule sind die eigenständigen Objektklassen, aus denen Objekte instantiieren können. Objektorientiertes Programmieren in VBA setzt den Einsatz von Klassenmodulen zugrunde. In einem Klassenmodul werden Eigenschaften, Methoden und Variablen definiert, bzw. programmiert. Die in einem Klassenmodul definierten Variablen dienen dem Programmierer später als Speicherbehälter. Die Datenkapselung in VBA wird unterstützt, indem Objektvariablen nur durch Methoden verändert werden. In einer Auflistung können auf Methoden und Eigenschaften jedes Elementes zugegriffen werden. Dadurch werden die Methoden der Unterklasse (Objekte dieser Auflistung) in der Oberklasse (Auflistung) zur Verfügung gestellt. Auf Eigenschaften eines Objektes wird in der Regel nicht direkt zugegriffen, wenn dies auch für globale Variablen prinzipiell möglich ist, sondern mit 3.3. API 23 Hilfe der als Public definierten Methoden. VBA stellt dafür eine Methode zur Verfügung: den Einsatz der Property-Routinen3 (Property Get, Property Let und Property Set) in Klassenmodulen. Die Eigenschaften (Variablen) eines Klassenmoduls werden als ,,Private” deklariert; Mit Hilfe von PropertyRoutinen werden Inhalte dieser Eigenschaften modifiziert oder abgelesen. Property-Routinen haben zwei grundlegende Vorteile. 1. Es kann programmiert werden, welcher Inhalt einer Eigenschaft zugewiesen werden darf. 2. Gleichzeitig können bei der Zuweisung bestimmte Aufgaben erledigt werden. (Diese Können z.B. direkten Einfluss auf andere Variablen nehmen.) Angenommen, ein Klassenmodul wird mit dem Namen ,,clsTest” definiert, so wird mit dem Befehl ,,Dim objTest As New clsTest” eine Instanz (ein neues Objekt) zu dieser Klasse erzeugt. Auf die Eigenschaften und Methoden dieses Objektes wird mit objTest< .|! >EigenschaftOderMethode zugegriffen. Auflistungen werden in Form von Klassenmodul programmiert. Eine Auflistung soll die notwendigen Methoden und Eigenschaften beinhalten. Zusätzlich braucht eine Auflistung eine Variable vom Typ ,,Collection”, in die Objekte gespeichert werden können. Mit Hilfe der Methode ,,Add” können Elemente in die Auflistung aufgenommen werden. Die Methode ,,Remove” hilft Elemente aus der Auflistung zu entfernen. Mit Hilfe der Befehle, Item, Count, BOL, EOL, MoveFirst, MoveLast, MoveNext, MovePrev kann die Auflistung durchlaufen und auf Elemente der Auflistung zugegriffen werden. Diese Methoden können beliebige Namen tragen und o.a. Namen sind nur als Vorschläge gedacht. Vorausgesetzt, es wurde eine Auflistung objColl von Objekten der Klasse clsTest definiert, so können auf die Eigenschaften bzw. Methoden des ersten Elements dieser Auflistung mit objColl.Item(0).EigenschaftOderMethode zugeriffen werden. Durch das ”Durchlaufen” einer Auflistung sind alle ihre Objekte erreichbar. Das Durchlaufen einer Auflistung kann mittels verschiedener Methoden durchgeführt werden. In [A00AN] sind diese und weitere Hinweise bereits ausführlich erläutert worden. 3.3 API In diesem Abschnitt soll eine Technik vorgestellt werden, die im ARTA für die Realisierung von Timern benutzt wird. 3 Ausführliche Erläuterungen dazu enthält [A00AN]. 24 KAPITEL 3. VISUAL BASIC FOR APPLICATIONS API (Application Programming Interface) ist eine Software-Schnittstelle, die dem Programmierer ermöglicht Windows Funktionalitäten in seinem Programm zu nutzen. Diese Funktionalitäten können praktisch alles darstellen, was in einem Windows Betriebsystem abläuft, z.B. Fensterverwaltung, Zeitverwaltung, Hardwareverwaltung, . . . . Jede Funktion von API ist in einer DLL Datei (,,Dynamic Link Library”), die die Endung ,,.dll’ hat, eingebunden. DLL Dateien sind in der Regel im Windows Stammverzeichnis gespeichert. Eine DLL-Datei wird erst dann in den Arbeitsspeicher transportiert, wenn sie tatsächlich benötigt wird. Die Funktionen einer DLL-Datei können in jedem Programm, das unter Windows läuft, benutzt werden. In VBA müssen die Funktionen von API erst deklariert werden, damit sie aufgerufen werden können. Durch die Deklaration einer API-Funktion wird dem Programm mitgeteilt, in welcher DLL-Datei diese Funktion zu finden ist, welche Parameter an diese Funktion übergeben werden und wie die Rückgabe dieser Funktion aussieht. Die Deklaration einer API-Funktion wird wie folgt durchgeführt: [Private | Public] Declare [Function | Sub] name Lib filename.dll Alias newname (ParameterListe) [As Datentyp] Die Funktionen sind unter dem Windows Betriebsystem je nach Thema und Funktionalität in verschiedenen DLL-Dateien zusammengefasst. In der Tabelle 3.1 sind die wichtigsten bzw. die am häufigst genutzten DLL-Dateien sowie die in diesen DLL-Dateien behandelten Themenschwerpunkte aufgelistet. Informationen über eine API-Funktion, wie Funktionsname, ÜbergabeparameDLL-Datei User32.dll Kernel32.dll Gdi32.dll Advapi32.dll Wininet.dll Wsock32.dll Shell32.dll Die Themen der Funktionen Allgemeine Benutzerfunktionen, Nachrichtenverwaltung, Windowselemente, Timer. Betriebsystembezogene Funktionen wie Speicherhandling, Systeminformationen. Zeichenwerkzeuge, Gerätekontext bezogene Operationen. Nutzung der Windows Registry, Nutzerrechte, Sicherheit und EventLogging. Internet-Funktionen des Internet Explorers. Netzwerk und rechnerübergreifende Kommunikation. Dateifunktionen, Dateiicons, starten von Anwendungen, Dateidialoge. Tabelle 3.1: DLLs und ihre Funktionalitäten 3.3. API 25 ter, Rückgabetyp und Format der Callback-Funktion4 können nicht unmittelbar herausgefunden werden, aber z.B. mittels des Hilfsprogramms ,,WinAPIViewer”. Das Programm wird mit den Microsoft Office 2000 Developer (MOD) mitgeliefert und kann im VBA-Editor-Fenster über MOD-Add-Ins aufgerufen werden. Callback-Funtion: Manche API-Funktionen setzten die Adresse einer sogenannten Callbackbzw. Rückruf-Funktion als einen ihrer Eingabeparameter voraus. Diese Funktion wird von der API-Funktion bzw. vom Betriebsystem später automatisch aufgerufen. Die Adresse dieser Funktion wird mit dem Schlüsselwort AddressOf, gefolgt von dem Rückruf-Funktionsnamen, an die API-Funktion angegeben. Im folgenden Beispiel ist der Einsatz dieses Schlüsselwortes in der Funktion ,,SetTimer” zu sehen. SetTimer ist eine API-Funktion, die in der Datei ,,User32.dll” abgelegt ist. SetTimer setzt die Adresse einer Rückruf-Funktion als ihren vierten Parameter voraus. Angenommen, diese Rückruf-Funktion heißt TimerProc: SetTimer soll dann wie folgt aufgerufen werden SetTimer hwnd, 12345, 5000, AdressOf TimerProc, wodurch dann die Funktion TimerProc nach 5000 Millisekunden automatisch aufgerufen wird. Auf die Funktion ,,SetTimer” und deren Einsatz wird in Kapitel 6 erneut eingegangen. Rückruf-Funktionen müssen in einem Modul definiert werden, da sie direkt aufgerufen werden; Deswegen können Funktionen in Klassenmodulen und in Formularen nicht als Rückruf-Funktionen benutzt werden. Bei Rückruf Funktionen muss die Parameterliste exakt mit denen übereinstimmen, die die Hauptfunktion voraussetzt. Die meisten Rückruf-Funktionen sollten schnellst -möglich ausgeführt werden, da sonst Access unvorhergesehen abstürzt. Die Arbeit mit API-Funktionen und besonders mit denen, die eine Rückruf-Funktionsadresse als Parameter voraussetzten, erfordert genaue Aufmerksamkeit. Access wird bei einer Anomalie sehr schnell zum Absturz gebracht, auch wenn es sich um eine harmlose Anomalie bei der Anwendung einer API-Funktion handelt. Weiterführende Literatur zu diesem Thema: [VB6Ko] und [APIHnd]. 4 Der Begriff ,,Callback-Funktion” wird gesondert beschrieben 26 KAPITEL 3. VISUAL BASIC FOR APPLICATIONS 3.4 Fehlerbehandlung in VBA Für ein erfolgreiches Programmieren ist es erforderlich, Fehler am laufenden Programm zu suchen und zu behandeln. Es gibt grundsätzlich drei verschiedenen Arten von Fehlern: Fehler beim Kompilieren treten auf, wenn im Code Befehle falsch geschrieben oder Funktionen falsche Eingabeparameter zugewiesen werden. Diese Funktionen können entweder in VBA vordefiniert oder vom Benutzer vorprogrammiert sein. Beim Kompilieren werden solche Fehler entdeckt und dementsprechend gemeldet. Damit das Programm lauffähig ist, müssen diese Fehler behoben werden. Das Endergebnis wird also nicht fehlerhaft sein, weil die Fehler bereits vorher entfernt worden sind. In Access existiert eine ,,automatische Syntaxprüfung”, durch die der Programmierer während der Programmeingabe auf offensichtliche Schreibfehler sofort hingewiesen wird. Die automatische Syntaxprüfung kann im Microsoft Visual Basic Fenster unter [Extras → Optionen → Editor (Code-Einstellungen)] ein- oder ausgeschaltet werden. Es empfiehlt sich, die automatische Syntaxprüfung immer einzuschalten, um somit Schreibfehler zu vermeiden bzw. direkt aufzuspüren und zu beseitigen. Logische Fehler werden nicht vom Compiler erkannt und daher erst sichtbar, wenn das Programm vollständig ausgeführt wurde. In einer Testphase sollte ein Programm nach solchen Fehlern durchsucht und gegebenenfalls vor seinem eigentlichen Einsatz korrigiert werden. VBA stellt Mechanismen sowie Befehle zur Verfügung, um nach logischen Fehlern zu suchen. Ein in VBA eingesetztes Werkzeug, das sich für die Fehlersuche als nützlich erweist, ist der ,,Debugger”. Um solche Fehler zu vermeiden ist beim Programmieren ein optimaler Überblick über den Code notwendig. Dies ist gegeben, wenn Empfehlungen hinsichtlich der Gestaltung eines Programmcodes berücksichtigt werden: Z.B. sollte ein Programmcode gut kommentiert sein und aus mehreren kleinen Prozeduren bestehen.5 Laufzeitfehler treten während der Ausführung eines Programms auf. Access meldet solche Fehler durch eine entsprechende Fehlermeldung und beendet das Programm standardmäßig. Laufzeitfehler lassen sich im Code behandeln, wenn Access-VBA entsprechend eingestellt ist. Im Registerdialogfeld von VBA kann eingestellt werden, wie VBA auf Laufzeitfehler reagieren soll (Extras → Optionen → Allgemein → Unterbrechen bei Fehlern). Wird der Eintrag Bei nicht verarbeiteten Fehlern markiert, so reagiert Access nicht auf 5 Vgl. [A00AN], [A00BK], [A97AN]. 3.4. FEHLERBEHANDLUNG IN VBA 27 ,,behandelte Fehler” und überlässt die Behandlung solcher Fehler den VBABefehlen. Jeder aufgetretene Fehler wird von Access in einem Err-Objekt abgelegt. Von diesem Err-Objekt können anschließend die Eigenschaften des Fehlers abgelesen werden. Das Err-Objekt beinhaltet Methoden, die zur Fehlerbehandlung eingesetzt werden können.6 Die Behandlung von Laufzeitfehlern wird im Code mit On Error Goto Label aktiviert. Dadurch wird jeder Laufzeitfehler zwischen On Error Goto Label und der mit Label markierten Zeile abgefangen. Wie dieser Fehler behandelt werden soll, ist erst ab der Zeile Label vorgegeben. In der Tabelle 3.2 sind die Befehle aufgelistet, mit deren Hilfe die Fehler in (Access-)VBA behandelt werden: Die Fehler in einer Funktion werden an die aufrufende Funktion weitergeleitet. Eine On Error -Fehlerbehandlung gilt für die Prozeduren bzw. Funktionen, für die sie vereinbart wurden und für alle Unterprogramme, d.h. für alle Prozeduren und Funktionen, die in dem Programm mit der Fehlerbehandlung aufgerufen werden. Das gilt nicht für Programmteile oder Unterprogramme, für die eine eigene Fehlerbehandlung programmiert wurde. Diese Methode der Fehlerbehandlung wird vorwiegend bei Programmen eingesetzt, in denen Fehler auch in Unterfunktionen vorkommen können, die dann jedoch die gleiche Wirkung auf das Gesamtergebnis des Programms haben. Im folgenden ist als Beispiel eine einfache Fehlerbehandlung in einer Sub-Funktion dargestellt: Sub Fehlerbehandlung() On Error GoTo FB_Error Anweisungen FB_Exit: Exit Sub FB_Error: Die Fehlerbehandlung passiert hier! Resume FB_Exit End Sub Bei Eintritt eines Laufzeitfehlers wird die Ausführung des Programms an die Zeile FB Error weitergeleitet, in der die eigentliche Fehlerbehandlung durchgeführt wird. Nach der Fehlerbehandlung wird die Ausführung des Programm 6 Vgl. [A00AN], S.233 28 KAPITEL 3. VISUAL BASIC FOR APPLICATIONS Fehlerbehandlung weiterleiten On Error Goto Label On Error Resume Next On Error Goto 0 Verhalten nach Fehlerbehandlung Resume Resume Next Resume Label der Beschreibung Die Fehlerbehandlung wird mit diesem Befehl an die Zeile mit der Sprungmarke bzw. mit der Zeilennummer Label weitergeleitet. Beim Eintritt eines Fehlers ignoriert das Programm die Ausführung der fehlerhaften Zeile und macht mit der nächsten Zeile weiter. Deswegen wird das Programm beim Eintritt eines Fehlers nicht angehalten! Mit diesem Befehl wird die Fehlerbehandlung wieder an Access zurückgegeben, d.h. nach diesem Befehl hält das Auftreten eines Fehlers die Programmausführung an. Beschreibung Nach der Fehlerbehandlung beginnt die Ausführung des Programms von der Zeile an, in der der Fehler aufgetreten ist. Nach der Fehlerbehandlung wird das Programm von der Zeile weiter ausgeführt, die der fehlerhaften Zeile unmittelbar folgt. Nachdem der Fehler behandelt wurde, wird das Programm von der Zeile Label weiter ausgeführt. Tabelle 3.2: Befehle zur Fehlerbehandlung in VBA 3.5. TRANSAKTIONEN IN VBA 29 an die Zeile FB Exit weitergeleitet, in der eine ordnungsgemäße Beendigung des Programms mit dem Befehl Exit Sub gewährleistet ist.7 3.5 Transaktionen in VBA Mehrere Datenbankoperationen können in Access zu einer Transaktion zusammengefasst werden. Das Zusammenfassen einer Anzahl von Datenbankänderungen zu einer Transaktion gewährleistet, dass entweder alle Änderungen vorgenommen werden oder überhaupt keine. Bei der Ausführung von Datenbankänderungen in einer Transaktion muss die Einhaltung der ACID8 -Eigenschaften vom Datenbankmanagementsystem garantiert werden. Bei einer Aktionsabfrage kann eingestellt werden, ob die Änderungen, die von dieser Abfrage vorgenommen werden, in einer Transaktion zusammengefasst werden sollen. Im VBA-Code wird allerdings eine andere Methode angewendet, um Transaktionen zu erzeugen und zu verwalten. In VBA steht das DAO9 -Workspace-Objekt mit seinen Methoden für die Transaktionsverwaltung zur Verfügung. Das Workspace-Objekt enthält drei Methoden zur Transaktionsverwaltung: • BeginTrans informiert Access, dass eine Transaktion begonnen wurde. Von da an werden alle Änderungen zwischengespeichert. • ComitTrans schließt die Transaktion ab, dabei werden alle Änderungen festgeschrieben. • Rollback setzt alle Änderungen zurück, die innerhalb dieser Transaktion vorgenommen wurden. In einem Access-VBA-Code können bis zu fünf Transaktionen ineinander verschachtelt werden. Abschließend wird im folgenden Beispiel dargestellt, wie eine Transaktion im Code verwaltet wird.10 7 Vgl. [A00AN], [A00BK], [A97AN]. Atomicity Consistency Isolation Durability: s. [ISKeEi], [ISVrl],[ISVos] 9 Vgl. Abschnitt 4.3 10 Vgl. [A00AN], [A00BK] und [A97AN] 8 30 KAPITEL 3. VISUAL BASIC FOR APPLICATIONS Sub TransAktion() Dim wksp As DAO.Workspace Set wksp = DBEngine.Workspaces(0) wksp.BeginTrans If (Erfolgreiches Datenbank-Update) THEN wksp.CommitTrans Else wksp.Rollback End If End Sub Kapitel 4 MS-Access ,,Access” ist in der ersten Version 1.0 im Jahre 1993 auf den Markt gekommen. Diese Version hatte wenige Assistenten und erfüllte trotzdem grundlegende Anforderungen eines Datenbankmanagmentsystems für PC’s. Diese Version und die nachfolgenden Versionen 1.1 und 2.0 waren 16-Bit Anwendungen und wurden vor allem mit den Betriebsystemen Windows 3.1 und 3.11 eingesetzt. Mit der nächsten Version ,,7.0” wurde die neue Generation von MS Access, d.h. 32-Bit Anwendung, ins Leben gerufen wurde. Nacheinander wurden mit den neuen Versionen Mängel und Schwachpunkte beseitigt und neue Komponenten hinzugefügt, wie z.B. bessere Assistenten. 4.1 Tabellen und Beziehungen Access ist ein relationales Datenbankmanagementsystem und daher werden in einer Access-Datenbank die Informationen in Relationen bzw. Tabellen gespeichert. Jede Tabelle besteht aus Spalten, die innerhalb der Tabelle eindeutige Namen haben. Jede Zeile einer nicht leeren Tabelle ist ein Datensatz. Die Tabellen, Spalten und Zeilen in Access repräsentieren jeweils Relationen, Attribute und Tupel. Jede Tabelle darf bis zu 255 Spalten (bzw. Felder) haben. Die Feldernamen dürfen aus bis zu 64 Zeichen bestehen. Jeder Spalte kann ein Datentyp zugeordnet werden und das bedeutet, dass die Einträge in dieser Spalte nur den angegebenen Datentyp haben dürfen. Für jede Tabelle kann ein Primärschlüssel bestimmt werden. Der Primär -schlüssel ist eine Spalte, in der kein Wert mehrfach vorkommen darf. Damit werden die Datensätze (Informationen in Zeilen) eindeutig bestimmt. Der Primärschlüssel kann entweder manuell erzeugt werden oder automatisch durch Anweisung an Access. Der automatisch erzeugte Schlüssel ist ein Autowert31 32 KAPITEL 4. MS-ACCESS Feld, das den Datentyp ,,Long Integer” hat. Ein Feld mit dem Typ ,,Autowert” erhält bei jedem neuen Datensatz automatisch einen neuen eindeutigen Wert. Als Primärschlüssel kann auch eine Gruppe von Feldern einer Tabelle gewählt werden, wobei die Kombination dieser Felder Eindeutigkeit des Datensatzes gewährleistet. Beziehungen und Integrität Beziehungen werden zwischen der Tabellen aufgebaut. Eine Beziehung wird zwischen zwei Tabellen aufgebaut, indem der Primärschlüssel einer Tabelle mit einem Feld bzw. einer Menge von Feldern einer anderen Tabelle verbunden wird. Das Feld bzw. die Menge von Feldern der anderen Tabelle heißen in diesem Falle ,,Fremdschlüssel”. In Access gibt es drei Beziehungstypen: • 1:1-Beziehung ist die einfachste Art. Dabei existiert für jeden Datensatz der Quelltabelle genau einen Datensatz in der Zieltabelle. • 1:n-Beziehung ist die am häufigst eingesetzte Beziehung in Access. Dabei können zu einem Datensatz in der Quelltabelle mehrere (n) Datensätze in der Zieltabelle existieren. • n:m-Beziehung ist eine Beziehung, die angibt, dass ein Datensatz in der Tabelle 1 n Gegenstücke in der Tabelle 2 hat und ein Datensatz in Tabelle 2 m Gegenstücke in der Tabelle 1 hat. Der Aufbau dieser Beziehung ist nur mit Hilfe einer Zwischentabelle möglich. Integrität wird in Access mit Hilfe von zwei Mechanismen unterstützt. Durch den ersten Mechanismus wird die Gültigkeit eines Datensatzes geprüft, und im Falle eines Verstoßes gegen die Gültigkeitsregeln wird eine Fehlermeldung ausgegeben und die Datensatzänderung zurückgesetzt. Diese Regeln lassen sich für eine Tabelle, sowie für einzelne Spalten aufstellen. Der zweite Mechanismus kommt erst mit dem Aufbau einer Beziehung zustande. Jede Beziehung hat bestimmte Eigenschaften, die eingestellt werden können. Im Eigenschaftfenster einer Beziehung werden mehrere Möglichkeiten angeboten, Eigenschaften spezifisch für die Beziehung zu bestimmen. Als eine Eigenschaft einer Beziehung kann ,,Mit referentieller Integrität” angegeben werden. Dadurch darf ein neuer Datensatz in einer Tabelle mit einem Fremdschlüssel nicht eingefügt werden, wenn kein entsprechender Wert in der referenzierten Tabelle existiert. Die referenzierte Tabelle ist die Tabelle, deren Primärschlüssel an der Beziehung beteiligt ist. Als weitere Eigenschaft einer Beziehung kann auch eingegeben werden, was mit den Datensätzen in einer Tabelle mit Fremdschlüssel passieren soll, wenn die Werte in der referenzierten Tabelle geändert oder gelöscht werden. 4.2. FORMULARE 33 Abfragen und SQL-Befehle: Eine Abfrage (View in Access) ist eine Access-Komponente, mit deren Hilfe bestimmte Datensätze aus einer oder mehreren Tabellen ausgewählt werden können. Mit einer Abfrage ist es auch möglich, die Anzahl einer Datenauswahl, den Durchschnitt einer Datenauswahl und weitere Details zu bestimmen. Eine Abfrage in Access ist nicht nur eine Oberfläche, die dem Benutzer bestimmte Informationen zeigt, die ausgewählten Datensätze einer Abfrage können auch geändert werden. Je nach Abfragetyp kann die Ausführung der Abfrage bestimmte Aufgaben erledigen. Es existieren in Access verschiedene Abfragetypen: Auswahlabfrage, Kreuztabellenabfrage, Tabellenerstellungsabfrage, Aktualisierungsabfrage, Anfügeabfrage und Löschabfrage. Welche Aufgaben mit Hilfe welcher Abfragen erledigt werden können, ist aus dem Namen ersichtlich1 . Desweiteren ist noch die Beziehung zwischen Access-Abfragen und SQL zu beleuchten. Im Hintergrund jeder Abfrage versteckt sich ein SQL-Ausdruck. Bei einer Auswahlabfrage fängt der SQL-Ausdruck z.B. mit ,,SELECT . . . ” an. QBE steht für Query By Example. Die Abfragen-Entwurfansicht, die Benutzerschittstelle zur Herstellung von Abfragen, wird als QBE bezeichnet. In der Entwurfansicht einer Abfrage lassen sich jede Art von Abfragen, auch komplizierte, formulieren. Diese Methode hilft vor allem Anfängern, SQL-Ausdrücke zu formulieren. Auch die Profis werden davon profitieren, weil die SQL-Ausdrücke sich mit Hilfe von QBE einfach und ohne Schreibfehler formulieren lassen. 4.2 Formulare Formulare sind Access Komponenten, die als Benutzeroberfläche benutzt werden. Formulare können gebunden oder ungebunden sein. Ein gebundenes Formular unterscheidet sich von einem ungebundenen Formular in der Datenquelle. Ein Formular kann mit einer Tabelle oder einer Gruppe von Tabellen verknüpft werden. Die Datensätze der Tabelle können innerhalb des gebundenen Formulars direkt bearbeitet werden. Die Änderungen an Datensätzen innerhalb eines gebundenen Formulars werden sofort in der Tabelle sichtbar. Ein ungebundenes Formulars ist mit keiner Tabelle verknüpft, und wird nur als Oberfläche genutzt. Mit den dafür vorgesehenen Assistenten können Formulare im Access-Hauptfenster erstellt werden. Wie oben bereits erwähnt, ist ein Formular ein Access-Objekt, das aus mehreren Kontrolelementen (Steuerelementen) bestehen kann. Ein Steuerelement ist selbst ein Access-Objekt, und kann wiederum aus Unterobjekten bestehen. Bezeichnungsfelder, Textfelder, Kombinationsfelder, Listenfelder, Optionsfelder, Buttons (Befehlsschaltflächen) 1 Weitere Details über Abfragen in [A00AN], [A00BK] und [A97AN]. 34 KAPITEL 4. MS-ACCESS und etc. sind Steuerelemente, die in ein Formular eingebaut werden können. Jedes dieser Steuerelemente hat eigene spezielle Funktionalitäten, die in einem Formular benutzt werden. Z.B. können Buttons dafür eingesetzt werden, um per Mausklick VBA-Funktionen auszuführen. Formular-Klassenmodule: Hinter jedem Formular versteckt sich ein Klassenmodul. Dieses Klassenmodul wird gleichzeitig mit dem Formular erstellt und ist somit ein Teil des Formulars. Was mit dem Formular passiert, wird auch sein Klassemmodul beeinflussen. Wird z.B. ein Formular gelöscht, wird auch sein Klassenmudol gelöscht. In einem Formular-Klassenmodul können Funktionen und Variablen definiert werden. Aus einem Formular kann ein Objekt (eine Instanz) instantiiert werden. Dieses Objekt kann aber nur dann weiter angewendet werden, wenn erst das zugehörige Formular geöffnet wird. Das Formular kann entweder einfach per Mausklick geöffnet werden oder innerhalb einer Prozedur bei der ein Objket vom Formular intantiiert und dessen Eigenschaft Visible auf True gesetzt wird. Formular-Ereignisse sind die wichtigsten Eigenschaften von Formularen, die in einer Access-Anwendung benutzt werden. Auf dem Bild 4.1 ist das Register des Eigenschaftfensters eines Formulars dargestellt. In einem Formular ist es möglich einzustellen, dass beim Vorkommen eines bestimmten Ereignisses eine bestimmte Prozedur ausgeführt wird. Diese Prozedur wird im Klassenmodul definiert, deren Programmierung dem Benutzer überlassen wird. Die EreignisProzeduren haben vordefinierte Namen. In folgendem Beispiel ist die Prozedur, die beim Löschen eines Datensatzes ausgeführt wird gezeigt. Private Sub Form_Delete(Cancel As Integer) Anweisungen End Sub. Nicht nur für ein Formular können Ereignisse definiert und programmiert werden, sonder auch für seine Steurelemente, z.B. die Ausführung einer Prozedur beim Eintritt des Ereignisses. Die Prozedur im folgenden Beispiel wird beim Auftritt des Ereignisses ,,Der Mausklick auf dem Button btn EditAction” ausgeführt. Private Sub btn_EditAction_Click() Anweisungen End Sub 4.2. FORMULARE 35 Abbildung 4.1: Formular-Ereignisse 36 4.3 KAPITEL 4. MS-ACCESS Datenzugriffsobjekte Jet Database Engine ist der Kern einer Access-Datenbank. Auf alle Daten einer Access-Datenbank können nur mit Hilfe von Jet DB Engine zugegriffen werden. Anforderungen an eine Access-Datenbank muss über diesen Kern gehen. Dieser Kern steht zwischen den Daten und den übrigen Teilen einer Access-Datenbank. Im Grunde genommen hat ein Access-Datenbankmanagementsystem alle Funktionalitäten, um eine Datenbank zu verwalten, insbesondere die entsprechende Benutzeroberfläche und die Jet Database Engine. Als Benutzeroberflächen sind z.B. Formulare, Berichte und Abfragen zu nennen. Die Aufgabe von Jet DB Engine ist es, die Änderungen, die über eine Benutzeroberfläche vorgenommen werden, an die Datenbank weiterzuleiten. Im Bild 4.2 ist gezeigt, wie die Access-Komponente mit dem Jet DB Engine verbunden sind. In Access besitzen die Datenzugriffsobjekte eine Hierarchie, deren Spitze Microsoft Access MS Access Benutzeroberfläche MS Access Applikation Jet Database Engine MS Acces Datenbank Abbildung 4.2: Access un Jet DB Engine das Objekt DBEngine ist. DBEngine ist eine Datenbankmaschine und bezeichnet den Jet Database Engine in Access. DBEngine ist keine Auflistung sondern ein Einzelobjekt. Die Hierarchie wurde im Buch [A00AN] auf Seite 322 geschildert. DAO oder ADO: In Access 2000 existieren zwei Datenzugriffsschnitstellen. Die Klassische Datenzugriffsschnitstelle ist DAO (Data Access Objekts). Diese Schnittstelle stellt 4.3. DATENZUGRIFFSOBJEKTE 37 viele Objekte und Methoden zur Verfügung, um auf Daten einer Access Datenbank zuzugreifen. DAO ist für den Zugriff auf relationale Datenbanken vorgesehen. Die zweite Schnittstelle ist ADO (AktiveX Data Objekts). ADO wurde so entwickelt, um nicht nur auf relationale Datenbanken sondern auch auf anderen Datenquellen zuzugreifen. Mit Access 2000 sollte DAO durch ADO abgelöst werden. Es ist der Firma Microsoft aber nicht gelungen, alle ADO-Komponente rechtzeitig fertig zu stellen. Deswegen ist der Einsatz von DAO in einer Access 2000 Datenbank-Anwendung unvermeidbar. DAO ist beim Einsatz in lokalen Access Datenbanken sehr stabil. Recordset ist ein häufig eingesetztes Datenzugriffsobjekt. Mit Hilfe von Recordset können die Datensätze einer Tabelle oder einer Abfrage verwaltet werden. In einem Recodset werden die Datensätze einer Tabelle getrennt voneinander zwischengespeichert. Um auf einen Datensatz in einem Recordset zuzugreifen, soll das Objekt ,,Recordset” in einer Schleife durchlaufen werden. Daher kann zu einem Zeitpunkt nur ein Datensatz zu modifiziert werden. Execute ist ein Befehl, unter den ein SQL-Ausdruck in VBA ausgeführt wird. Execute ist eine Methode des Objektes Database. Das folgende Beispiel zeigt den Einsatz von Recordset in einer Prozedur. Im Beispiel werden alle Datensätze der Tabelle Angestellte so modifiziert, dass der Lohn jedes Angestellten um 20% erhöht wird. Public Sub MehrLohnMitRecordset() Dim rstAng As DAO.Recordset Set rstAng = CurrentDb().OpenRecordset("Angestellte") With rstAng Do While rstAng.EOF = False .Edit !Lohn = !Lohn * 1.2 .Update rstAng.MoveNext Loop End With End Sub Dieses Ziel könnte auch mit dem Einsatz von Execute und einem SQL-Ausdruck wie im folgenden Beispiel erreicht werden. Public Sub MehrLohnMitExecute() CurrentDb().Execute "UPDATEAngestellte SET Angestellte.Lohn =" _ & "Angestellte.Lohn * 1.2;" End Sub 38 KAPITEL 4. MS-ACCESS Im allgemeinen ist eine Ausführung eines SQL-Ausdrucks schneller als die Datenmanipulation mit Hilfe von Recordset. 4.4 Sicherheit Für die Sicherheit sind in Access verschiedene Mechanismen vorgesehen. Diese Mechanismen sind wie folgt: • Datensicherheit: Die Access Datenbank kann mit einem Passwort geschützt werden. In diesem Fall wird jeder Benutzer beim Öffnen der Datenbank aufgefordert, ein Passwort einzugeben. Diejenigen Benutzer, die das Passwort kennen, erhalten Zugriff auf die Datenbank und können (alle) Daten in der Datenbank bearbeiten. • Datensicherheit: In einer Access Datenbank existiert ein Mechanismus, mit dessen Hilfe Benutzer und Gruppen definiert werden können. Den Benutzern und Gruppen können so Zugriffsrechte exakt erteilt werden und es wird definiert, wer welche Access-Komponente und darin enthaltene Daten sehen, ändern oder löschen darf. Das ist die höchste Datensicherheit in Access. • Codesicherheit: Die Codes in einer Access Datenbank können mit einem Passwort geschützt werden. Kennt ein Benutzer das Passwort, so darf er Codes bearbeiten. • Codesicherheit: Codes können auch gesichert werden, indem von der Datenbank eine MDE-Datei erstellt wird. In der MDE-Datei können keine Codes bzw. keine Access-Komponenten mit Codes (Formulare, Berichte, etc.) bearbeiten werden. Jede Komponente in Access muss einen Namen haben. Die Komponenten, deren Name mit MSys oder USys anfangen sind als Systemkomponenten zu interpretieren. MSys steht für die Access-Systemkomponente und USys für die Komponente, die der Benutzer als Systemkomponente bezeichnen möchte. Jede Access Komponente kann zusätzlich als ausgeblendet gekennzeichnet werden. Im Access-Hauptfenster (unter Extras → Optionen → Ansicht) kann eingestellt werden, ob die jeweilige Systemkomponente und/oder ausgeblendete Komponente angezeigt werden dürfen. Dieser Mechanismus kann genutzt werden, um bestimmte Access-Komponente vor der direkten Sicht der Benutzer zu verstecken. Trotzdem stellt dieser Vorgang keine dieser Komponenten richtig sicher. Kapitel 5 Ein Konzept für eine aktive Erweiterung von Access Access ist kein aktives Datenbankmanagementsystem und bietet demzufolge Anwendern keine Möglichkeit an, um Trigger und Ereignismuster definieren zu können. In diesem Kapitel wird untersucht, ob eine Trigger-Funktionalität in Access realisiert werden kann. Im weiteren wird ein Konzept vorgestellt, wie eine Erweiterung von Access um eine Regelsprache aussehen kann. Access kann um eine Triggersprache erweitert werden, ohne dass ernsthafte Einschränkungen zu erwarten sind. Da in Access und Jet Engine die Sourcecodes nicht veränderbar sind, lassen sich die notwendigen Anforderungen, die ein aktives Datenbankmanagementsystem erfüllen muss, nicht direkt im Access-Sourcecode programmieren. Diese Anforderungen betreffen vor allem Triggerverwaltung und Ereigniserkennung. Damit Trigger und Ereignismuster definiert und Ereignisse erkannt werden können, müssen diese Systeme in einem aktiven Datenbankmanagementsystem existieren. 5.1 Das Konzept eines Präprozessors Eine Access-Anwendung ist eine Kombination von Access-Komponenten, deren Zweck die Erledigung einer bestimmten Aufgabe ist. Die Trigger-Funktionalität kann in Form einer Access-Anwendung unter Benutzung der Programmiersprache VBA programmiert werden. Diese Anwendung hilft zunächst dem Benutzer, Trigger zu definieren. Weitere Aufgabe der Anwendung ist die Ereigniserkennung und die Bearbeitung der durch dieses Ereignis gefeuerten Trigger. Alle Datenbankänderungen werden in Access mit Hilfe der Jet Database Engine erledigt. Die Jet Engine ist eine Schnittstelle zwischen den Daten und der Benutzeroberfläche in Access. Die Oberflächen sind Access-Komponenten, durch 39 40 KAPITEL 5. KONZEPT die Daten einer Access-Datenbank manipuliert werden können. Insbesondere wegen der Ereigniserkennung muss diese Anwendung zwischen Benutzerobeflächen und der Jet Engine stehen. Bei diesem Konzept ist die Access-Anwendung eine Schnittstelle zwischen Datenänderungsanforderungen und der Jet Engine (bzw. den Daten). Die Anwendung funktioniert hier also wie ein Präprozessor zwischen Anwendern und dem Access(-Prozessor). Diesen Präprozessor nennen ich ARTA. ARTA steht für ,,Active Real Time Access Database System”. Das Bild 5.1 schildert die Beziehung zwischen ARTA und Access. Benutuzer ARTA Jet DB Engine Daten Abbildung 5.1: ARTA in Access Wie der Name ARTA sagt, wird mit diesem Präprozessor versucht, die Aspekte ,,Aktivität” und ,,Zeitabhängigkeit” zu berücksichtigen. Durch ARTA wird das Access-Datenbankmanagementsystem in ein aktives Access-Datenbankmanagementsystem überführt, in dem auch Zeitereignisse definiert und wahrgenommen werden können. Dabei werden verschiedenen Typen von Ereignismustern definiert und die entsprechenden Ereignisse aufgefangen und bearbeitet. In ARTA existieren zwei Ereignistypen, Zeitereignisse und Änderungsereignisse. Änderungsereignisse treten bei Datensatzmanipulationen (an Access Tabellen) auf, d.h. bei Löschen (DELETE), Hinzufügen (INSERT) oder bei Änderung an Datensätzen (UPDATE). Zeitereignisse werden in zwei Untertypen aufgeteilt, absolute und periodische Zeitereignisse. Die Kombination der Programmiersprache VBA mit dem Access-Datenbankmanagementsystem und der unter Access verwendeten SQL-Anfragesprache ergibt ein potentes Werkzeug, mit dessen Hilfe man verschiedene Anwendungen für Access programmieren kann. Dieses Werkzeug reicht für die Erweiterung von Access um eine Triggersprache völlig aus. Das Access-Datenbankmanagementsystem bietet dem Programmierer seine Komponenten, z.B. Tabellen und Formulare an, mit deren Hilfe die Trigger-Informationen verwaltet und gespei- 5.1. DAS KONZEPT EINES PRÄPROZESSORS 41 chert werden können. In VBA kann auf alle Access-Komponenten zugegriffen werden und mit Hilfe von VBA-Methoden können Datenbankänderungen automatisch durchgeführt und beobachtet werden. Die Anfragesprache SQL wird in Access in einem speziellen Dialekt unterstützt. Dieser Dialekt ist keine Einschränkung für die Formulierung der notwendigen SQL-Ausdrücke und erfüllt alle Anforderungen, die bei der Manipulation der Daten gestellt werden. Mit Access-SQL ist auch die Formulierung von DDL1 -Befehlen z.B. ,,Create Table ...” möglich. SQL-Ausdrücke lassen sich als Datenquelle für Formulare und Abfragen formulieren. Um die Einfachheit von Access bezüglich der Datendefinition beizubehalten, werden als Eingabe in ARTA nur die drei DML-Befehle DELETE, INSERT, UPDATE erlaubt sein. In ARTA wird es z.B. nicht möglich sein, Tabellen zu definieren, oder Tabellendefinitionen (Evolution) zu ändern. Dazu stehen dem Benutzer alle Funktionalitäten von Access wie gewohnt zur Verfügung. SQL-Ausdrücke können auch in einem VBA-Code ausgeführt werden (database.Execute(SQL)). Das ist besonders interessant, da auch die Folgeänderungen in Form eines SQL-Ausdrucks eingegeben und im Code automatisch ausgeführt werden können. Diese Folgeänderungen sind insbesondere die Änderungen, die durch die Ausführung des Aktionsteils eines gefeuerten Triggers zustande kommen können. Die Benutzung von ARTA läuft unter einer Einschränkung. Diese Einschränkung hat ihre Ursache in der Ereigniserkennungsproblematik. In einer herkömmlichen Access-Datenbank werden Daten einer Tabelle wie folgt geändert: • Die Änderung kann direkt an der Tabelle durchgeführt werden, nachdem die Tabelle geöffnet wurde. • Die Änderung kann indirekt durch ein Formular, das mit der Tabelle verknüpft ist, durchgeführt werden. • Daten einer Tabelle können auch in einer Abfrage, deren Datenquelle diese Tabelle ist, manipuliert werden. Keine dieser Methoden eignet sich für die Erweiterung um eine Triggersprache, weil Datenänderungen bei diesen Methoden nicht als Ereignisse aufzufangen sind. Das wird noch problematischer, wenn andere Trigger durch den Aktionsteil eines aktuell gefeuerten Triggers gefeuert werden sollen. Diese sogenannten Folgeänderungen müssen automatisch durchgeführt werden. Die in Access üblichen Datenbankänderungesarten sollten durch ARTA übergangen werden, d.h. alle Datenbankänderungsbefehle sollten in ARTA eingegeben werden. 1 Data Definition Language 42 KAPITEL 5. KONZEPT Die Ausführung dieser Befehle wird dann von ARTA kontrolliert durchgeführt. Dafür wird in ARTA eine Oberfläche vorgesehen, in der alle Änderungsbefehle der Form ,,DELETE, INSERT, UPDATE” eingegeben werden sollen. Ein Mechanismus in Access sorgt hier für Missverständnisse. Die Frage stellt sich, warum die Formular-Ereignisse nicht benutzt werden können, um Ereignisse aufzufangen. Ereignisse könnten doch ,,innerhalb” eines Formulars automatisch erkannt werden. Trigger könnten so in dem Formular-Klassenmodul mit Hilfe von Funktionen und Subs formuliert werden. Ein Formular muss mit einer bestimmten Tabelle verknüpft sein, damit der Benutzer Datenänderungen an der Tabelle über dieses Formular vornehmen kann. Daten der jeweiligen Tabelle müssen deshalb nur über dieses Formular modifiziert werden, um sie als Ereignisse wahrgenommen zu werden. Trigger müssen deswegen einzeln zu jeder Tabelle in einem separaten Formular mit Hilfe der Programmiersprache VBA ,,programmiert” werden. Hier fehlt jedoch ein Zentralsystem für die Triggerbearbeitung. Im übrigen können die vom Aktionsteil eines Triggers angeforderten Datenänderungen an einer anderen Tabelle nicht durchgeführt werden, es sei denn aus einem Formular werden andere Formulare geöffnet und die Folge-Änderungen werden in neu geöffneten Formularen durchgeführt. Dabei ist zu beachten, dass alle beteiligten Formulare mit geeigneten Tabellen verknüpft sein müssen. Diese Art von Triggerdefinition und Triggerbearbeitung ist nicht realistisch. Zusammengefasst sind die Formular-Ereignisse nicht geeignet, um ein automatisches Triggerbearbeitungssystem zu realisieren. Formular-Ereignisse eignen sich, die Definition und Änderung von Triggern zu überwachen. Dafür wird einzig die Syntaxprüfung durch die Formular-Ereignisse mit Hilfe von VBA programmiert. Ein Trigger in ARTA besteht aus allgemeinen Informationen über den Trigger und aus drei weiteren Grundteilen: Ereignismusterteil (Event), Bedingungsteil (Condition) und Aktionsteil(Action). Die allgemeinen Informationen eines Triggers sind vor allem der Name und die Priorität des Triggers. Im Falle eines Änderungsereignisses können auch Parameter vordefiniert werden, die Informationen über die betroffenen Datensätze (Zeilen) der Tabelle enthalten. Die betroffenen Zeilen sind die Datensätze, die durch die Ausführung eines SQL-Datenbankänderungsbefehls geändert werden. Die Anzahl der betroffenen Zeilen können auch Null sein, d.h. trotz der SQL-Ausführung werden keine Datensätze eingefügt, gelöscht oder geändert. Der Ereignismusterteil eines Triggers hat einen eindeutigen Namen. Mit diesem Namen sind die weiteren Informationen des Ereignismusters verknüpft. Die weiteren Informationen sind vor allem das Erzeugungsdatum, das Datum der letzten Änderung des Ereignismusters 5.1. DAS KONZEPT EINES PRÄPROZESSORS 43 und der Ereignismustertyp. Je nach Ereignismustertyp gehören dem Ereignismuster spezifische Eigenschaften an. Zum Ereignismuster vom Typ ,,Änderungsereignis” gehören der Tabellenname und der DML2 -Befehl. Einem Ereignismuster vom Typ ,,Zeit-EreignisatOnce” gehören date, time und deadLine an. Begin-date, Begin-time, deadLine und die Periode gehören zu einem Ereignismuster vom Typ ,,Zeit-Ereignisperiodic”. Zeitereignisse werden in einem gesonderten Abschnitt beschrieben. Änderungsereignismuster in ARTA sind dem SQL3-Standard3 sehr ähnlich, da ARTA auf dem SQL3 basiert. Im Unterschied zum SQL3-Standard werden in ARTA Änderungsereignisse nur auf der Ebene von Tabellen definiert. Eine Datenänderung auf einem bestimmten Feld einer Tabelle kann ja als eine Datenänderung auf der Tabelle interpretiert werden. In SQL existiert keine ja/nein Abfrage, mit SELECT formulierte Abfragen geben eventuell ausgewählte Datensätze zurück. Für den Bedingungsteil eines Triggers in ARTA können ja/nein Abfragen durch die Definition bestimmter Funktionen realisiert werden. Eingabeparameter einer solchen Funktion könnte z.B. ein SQL-Ausdruck sein und die Funktion gibt zurück, ob durch diesen SQL-Ausdruck Datensätze ausgewählt werden (z.B. ExistElement(SQL)). Eine mögliche Triggerbedingung ist auch ein Vergleich vordefinierter Parameter, Konstanten oder der aktuellen Zeit miteinander, z.B. NewRowPar.Verlaengerung um > 42. Standardmäßig wird eine leere Triggerbedingung als ,,ja(True)” interpretiert. Der Aktionsteil eines Trigger ist ein ausführbarer Ausdruck. Als Triggeraktion ist in ARTA im Unterschied zu SQL3 ein einziger zusammengehörender Ausdruck erlaubt. Dieser Ausdruck kann eine in VBA vorhandene oder vom Benutzer neu eingebaute Funktion sein. In einer vom Benutzer eingebauten Funktion kann programmiert werden, dass mehr als ein Befehl ausgeführt wird. Durch diese Methode kann der Aktionsteil eines Triggers eine Funktionalität aufweisen, die auch in SQL3 vorgesehen ist. Diese Funktionen sollten als globale Funktionen und in Modulen (nicht in Klassenmodulen) definiert sein, da die in einem Klassenmodul definierten Funktionen nur durch initiierte Objekte aufrufbar sind. Aber die in einem Modul als global definierte Funktion ist überall in VBA und sogar in Access-Komponenten ausführbar. Dabei ist hinzuzufügen, dass diese Methode nicht für jeden Benutzer geeignet ist. Diese Methode ist eher für die Benutzer geeignet, die die Programmiersprache VBA 2 3 Data Manipulation Language Vgl. 2.2 44 KAPITEL 5. KONZEPT beherrschen. In der Implementierung von ARTA werden Funktionen vorgestellt, die auch nicht VBA-Programmierer anwenden können. Unterschiede zwischen den Triggersprachen in ARTA und SQL3: Eine Zusammenfassung der Unterschiede zwischen der SQL3- und ARTATriggerfunktionalität wurde in der Tabelle 5.1 gegeben. Im Vergleich zu SQL3 ist die Behandlung von Zeitereignissen in ARTA als eine Erweiterung zu betrachten. In SQL3 werden nur Änderungsereignisse spezifiziert, die im Ereignisteil von Triggern beschrieben werden. Zusätzlich dazu werden in ARTA auch Zeitereignisse behandelt, die im Ereignisteil von ARTA-Triggern beschrieben werden. Zeitereignisse Für die Realisierung von Zeitereignissen bzw. Timern existieren folgende Möglichkeiten in Access und VBA: • Formularereignis: (Bei Zeitgeber) Über das Formularereignis ,,Bei Zeitgeber” kann in einem Formular eingestellt werden, dass eine Prozedur in seinem Klassenmodul periodisch in gleichen Zeitabständen (in Millisekunden) ausgeführt wird. • Durch Benutzung des Befehls ,,DoEvents” in einer Prozedur kann eine bestimmte Funktion in gleichen Zeitabständen aufgerufen werden. In der folgenden Prozedur wird alle 4 Sekunden die aktuelle Zeit ausgegeben: Public Sub Timer() Dim dte As Date Do While True Debug.Print Now() dte = Now() Do While (4 > DateDiff("s", dte, Now())) DoEvents Loop Loop End Sub • Mit Hilfe von API-Schnittstelle. Für die Realisierung von Timern in ARTA wurde die Methode von APISchnittstelle ausgewählt. Die Methoden und die Benutzung von API-Funktionen wurden im Kapitel 6 ausführlich beschrieben. Die API-Schnittstelle wurde aus 5.1. DAS KONZEPT EINES PRÄPROZESSORS Art der Ereignisse Definition von Triggern und Ereignismustern: Triggerbearbeitung 45 SQL3 ARTA In SQL3 werden Zeitereignisse überhaupt nicht unterstützt. Ereignismuster haben keine Namen (Bezeichnungen), so muss für jeden Trigger ein Ereignismuster angegeben werden. In ARTA besteht die Möglichkeit, Zeitereignisnuster zu definieren. Jedes Ereignismuster hat einen Namen (Bezeichnung), so kann ein schon definiertes Ereignismuster bei mehreren Triggern benutzt werden. Ereignismuster können nur bis zu der Tabellenebene spezifiziert werden Im Aktionsteil eines Triggers kann nur ein einziger Befehl stehen. Dieser Befehl kann ein Funktionsaufruf oder ein SQL-Ausdruck sein. Ereignismuster können bis zu der Felderebene spezifiziert werden Im Aktionsteil eines Triggers, zwischen ,,BEGIN ATOMIC” und ,,END” können mehrere SQLAusdrücke eingesetzt werden. Das funktioniert wie eine Prozedur, in der auch z.B. Schleifen vorkommen dürfen. Integritätanforderungen werden bei der Triggerbearbeitung berücksichtigt Zeitereignisse werden nicht abgefangen. Integritätanforderungen werden bei der Triggerbearbeitung nicht berücksichtigt. Zeitereignisse, die durch geeignete Ereignismuster spezifiziert sind, werden abgefangen und deren Trigger werden bearbeitet. Tabelle 5.1: Unterschiede zwischen der SQL3- und ARTA-Triggersprache 46 KAPITEL 5. KONZEPT folgenden Gründen ausgewählt. Jeder API-Timer läuft in einem eigenen Prozess im Betriebsystem parallel zu Access. Deswegen braucht ein API-Timer im Vergleich zur Methode ,,Formularereignis” kein Access-Objekt. Ein API-Timer wird direkt vom Betriebsystem verwaltet. So braucht er keine großen Resourcen, dagegen beschäftigt die Methode mit dem Befehl ,,DoEvents” ständig den Rechnerprozessor. Ein Nachteil - oder besser formuliert: eine Eigenschaft - von API ist die hohe Sensibilität, die im Abschnitt 3.3 genauer beschrieben wurde. Das ist besonders bei API-Timern problematisch, weil diese Funktionen eine Rückruffunktion benötigen. Die Rückruffunktionen dürfen nicht lange warten müssen und sollten schnell bearbeitet werden. Es gibt Konfliktzustände, bei denen Access ohne Vorwarnung abstürzt: z.B. beim mehrmaligen Aufruf derselben Rückruffunktion durch mehrere Timer. Um dieses Problem zu lösen wird vereinbart, dass gleichzeitig nur ein einziger Timer eingesetzt werden darf. Konflikte kommen nach dieser Methode überhaupt nicht zustande, weil eine Ausführung der Rückruffunktion zu Ende gehen soll, bevor die Rückruffunktion erneut aufgerufen wird. Die sichere Ausführung eines (SQL-)Befehls Was passiert mit der Bearbeitung eines SQL-Befehls bzw. von Triggern (die durch ein Ereignis gefeuert wurden) wenn Access vorher geschlossen wird? Es kann möglicherweise vorkommen, dass ein Benutzer versucht Access zu schließen, obwohl die Datenbankänderungen nicht vollständig abgeschlossen sind. solche Datenbankänderungen werden erst dann einen Sinn machen, wenn alle zusammengehörenden Trigger vollständig bearbeitet werden. Bei folgendem Beispiel darf Access erst nach dem Schritt ,,n)” geschlossen werden. 0)Anfang der Bearbeitung von SQL S 1)S erzeugt das Ereignis E und E feuert {T1 ,T2 ,T3 }, 2)T1 .Aktion erzeugt das Ereignis E1 und E1 feuert {T11 ,T12 ,. . . }, 3)T2 .Aktion erzeugt das Ereignis E2 und E2 feuert {T21 ,T22 ,. . . }, ... n)Ende der Bearbeitung von SQL S. Zur Lösung dieses Problems wird wieder ein Formularereignis benutzt. Die Prozedur ,,Private Sub Form Unload(Cancel As Integer)” wird beim Schließen des Formulars (Formular-Ereignis: ,,Bei Entladen”) aufgerufen. Wenn die Variable ,,Cancel=True” ist, darf (kann) das Formular nicht geschlossen werden, und demzufolge kann Access auch nicht geschlossen werden. Bei Beginn einer Triggerbearbeitung wird in einem (unsichtbar) geöffneten Formular die Variable ,,Cancel” auf ,,True” gesetzt. Nach der Beendigung der Triggerbearbeitung 5.2. SYNTAX VON ARTA-TRIGGERSPRACHE 47 wird wieder die Variable ,,Cancel” auf ,,False” zurückgesetzt. Private Sub Form_Unload(Cancel As Integer) If Not Me.chk_EnableClose Then Cancel = True End If End Sub ,,chk EnableClos” ist ein Kontrollkästchen, das im Formular eingebaut wurde. Mit der Inhaltsänderung dieses Kontrollkästchens wird indirekt der Variableninhalt ,,Cancel” wie folgt kontrolliert: Forms("USys_frm_ControlSystemClose")!chk_EnableClose = False/True Damit wird gewährleistet, dass die Bearbeitung vollständig durchgeführt werden kann. 5.2 Syntax von ARTA-Triggersprache Die Syntax eines triggers in ARTA-Triggersprache sieht bis auf ein paar Einschränkungen fast genauso aus wie die Syntax eins Triggers bei SQL3. Ein Trigger wird unter ARTA nicht in Form eines Textes definiert. Er wird in einem Formular definiert, in dem bestimmte Felder für Einträge vorgesehen sind. Bei der Syntax-Beschreibung von ARTA in BNF4 -Form existieren deswegen Ausdrücke, die bei einer Triggerdefinition nicht eingegeben werden. Diese Ausdrücke werden zum besseren Verständnis der Grammatik benutzt. Die Syntax eines Triggers in BNF-Form sieht wie folgt aus: <trigger definition> ::= CREATE TRIGGER <trigger name> <trigger priority> <trigger activity> <trigger event> <trigger condition> <trigger action> <trigger condition> ::= WHEN {<left paren> <search condition> <right paren> |<function>} <trigger action> ::= <SQL Statement> | <VBA function> 4 Backus-Naur-Form: Vgl. [AutoHoUl] 48 KAPITEL 5. KONZEPT <trigger event> ::= <modify> | <atOnce> | <periodic> <modify>::= <event name> <trigger action time> <trigger DML> ON <table name> [ REFERENCING <old new values alias list>] FOR EACH {ROW | STATEMENT} <trigger action time> ::= BEFORE | AFTER <trigger DML> ::= INSERT | DELETE | UPDATE <old or new values alias list> ::= <old or new values alias>... <old 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> <old values table alias> ::=<identifier> <new values table alias> ::=<identifier> <old values correlation name> ::= <correlation name> <new values correlation name> ::= <correlation name> <atOnce> ::= <date> <time> <deadLine> 5.2. SYNTAX VON ARTA-TRIGGERSPRACHE 49 <periodic> ::= BEGIN <date> <time> <deadLine> EVERY {<number> <period> |<number> <period> + <number> <period>} <number> ::= 1,2,3,... <period> ::= minutes | hours | days | weeks | months | years Dabei bedeuten die Parameter wie folgt: <trigger name>: Eindeutiger Name für den Trigger. <trigger priority>: Eine eindeutige natürliche Zahl <trigger activity>: ja/nein-Eintrag. <left paren><search condition> <right paren>: An dieser Stelle können ja/neinAbfragen an die Datenbank formuliert werden. <function>: Globale Funktionen, die entweder in VBA oder von Benutzern definiert wurden. <SQL Statement>: Ein SQL-Ausdruck. <event name>: Ein eindeutiger Name für das Ereignismuster. <identifier>: Eindeutige Namen für die Parameter. <correlation name>: Eindeutige Namen für die Parameter. <date>: Das Datum des Tages, an dem ein Ereignis auftritt. <time>: Die Tageszeit. <deadLine>: Eine natürliche Zahl ≥ 5 Minuten. Wie schon erwähnt, werden die Trigger in ARTA formularbasiert verwaltet. Die Syntax von ARTA-Regeln muss in zwei voneinander abhängigen Teilen beschreiben werden. 5.2.1 Syntax eines Triggers Trigger werden in einem Formular definiert, in dem folgende Felder vorgesehen sind: 50 KAPITEL 5. KONZEPT • Ein eindeutiger Triggername: Ein Trigger wird in einer ARTA-Datenbank eindeutig bestimmt und definiert. Der Name darf 1 bis 64 Zeichen aus der Menge der ,,legalen Zeichen” ,,{a,b,. . . ,z,A,B,. . . ,Z,0,1,. . . ,9, }” enthalten. Triggernamen sollten aussagefähig ausgewählt werden, damit die Unterschiede zwischen Triggern aus ihren Name ersichtlich werden. • Eine eindeutige Trigger-Priority: Trigger-Priority ist eine Zahl aus der Menge der natürlichen Zahlen {1,2,3,4. . . }. Durch Trigger-Priority wird die eindeutige Reihenfolge der Bearbeitung der betroffenen Trigger an den Konfliktstellen bestimmt. • Ist aktiv: Die Aktivität eines Triggers gibt an, ob dieser Trigger beim Auftreten eines Ereignisses berücksichtigt werden darf. • Created und Modified Dates: Das Datum des Erzeugens und das Datum der letzten Änderung eines Triggers werden automatisch hier eingesetzt. • Event: Das Ereignismuster beschreibt das Ereignis, dessen Eintritt dieser Trigger feuert. Die Syntax von Ereignismusterdefinitionen wird weiter unten in 5.2.2 beschrieben. • REFERENCING: An dieser Stelle ist die Definition von bis zu vier Parametern möglich. Je nach Art des Ereignismusters können Parameter für NEW TABLE, OLD TABLE, NEW ROW, OLD ROW eingegeben werden. Bei den Zeitereignissen werden keine Parameter definiert. Bei einem Änderungsereignis dürfen je nach DML-Befehl folgende Parameter definiert werden: – Bei dem Befehl DELETE dürfen OLD TABLE und OLD ROW definiert werden. – Bei dem Befehl INSERT dürfen NEW TABLE und NEW ROW definiert werden. – Bei dem Befehl UPDATE dürfen OLD TABLE, OLD ROW, NEW TABLE und NEW ROW definiert werden. • Action-Time: Ob der Trigger vor oder nach der SQL-Ausführung bearbeitet werden soll, 5.2. SYNTAX VON ARTA-TRIGGERSPRACHE 51 wird durch diesen Eintrag bestimmt. Action-Time ist bei einem Änderungsereignismuster entweder BEFORE oder AFTER. Standardeintrag ist ,,BEFORE”. • FOR EACH ist bei einem Änderungsereignismuster entweder ROW oder STATEMENT, wobei der Standardeintrag ,,STATEMENT” ist. • Condition: Unter diesem Eintrag wird eine ja/nein Anfrage formuliert. Diese Anfrage kann aus einem Funktionsaufruf oder einem logischen Vergleichsausdruck bestehen. Konstanten und Parameter, die mit dem Trigger definiert wurden, können in Funktionen als Eingabeparameter und in logischen Ausdrücken als Variablen benutzt werden. In logischen Ausdrücken sind die Vergleichsoperatoren {<, <=, >, >=, =, <>} erlaubt. • Action: Unter diesem Eintrag wird ein Befehl formuliert, der nach dem Feuern des Triggers ausgeführt wird, wenn die Triggerbedingung (Condition) erfüllt ist. Die vom Benutzer eingebauten globalen Funktionen, die Access Funktionen und SQL-Ausdrücke können hier eingegeben werden. Auch im Triggeraktionsteil können Triggerparameter vorkommen. 5.2.2 Syntax eines Eventmusters Ereignismuster müssen gesondert (aber gleichzeitig mit Triggern) definiert werden. Ein vorher gleichzeitig mit einem Trigger definiertes Ereignismuster kann als Ereignisteil eines anderen Triggers benutzt werden. Ein Ereignismuster darf nur dann geändert werden, wenn das Ereignismuster entweder als Eventteil eines einzigen Triggers benutzt worden ist, oder wenn das Ereignismuster falsch eingegeben wurde. Ein Ereignismuster wird in einem Formular mit den folgenden Textfeldern definiert. • Name ist eine eindeutige Bezeichnung des Ereignismusters. • Created und Modified Date sind die Erzeugungs- bzw. Änderungsdaten, die automatisch eingesetzt werden. • Je dem Event-Typ müssen gezwungener Massen andere Eigenschaften für das Ereignismuster definiert werden: – Bei modify event müssen der Datenbankmanipulationstyp, DMLTyp und der Tabellenname eingegeben werden. 52 KAPITEL 5. KONZEPT Ein DML-Befehl kann vom Typ DELETE, INSERT oder UPDATE sein. Bei dem Feld, in das auf diesem Formular dieser DML-Befehl eingegeben wird, ist nur einer der o.g. Befehle als Eingabe möglich. Der Tabellenname darf nur von der Liste der vorhandenen Tabellen in der Datenbank gewählt werden. – Bei time event atOnce sind folgende Informationen relevant: date: der Tag, an dem das Zeitereignis erwartet wird. time: die Uhrzeit im unter date eingegebenen Tag. deadLine: Beim Eintreffen des Zeitereignisses darf der Trigger nur vor dem Ablauf dieser Deadline gefeuert werden. Wenn ein Zeitereignis nach dem Ablauf der Deadline eintritt, werden seine Trigger ignoriert. Die Deadline wird in Minuten eingegeben und darf nicht kleiner als 5 Minute sein. Wenn keine Zahl eingegeben wird, wird die Deadline als unendlich(∞) angenommen. – Bei time event periodic muss eingegeben werden, ab wann und in welchen Zeitabständen solche Zeitereignisse bemerkt werden. Nach einer erfolgreichen Eingabe solcher Ereignismuster wird der nächste Termin automatisch berechnet und als informativer Eintrag angezeigt. Die periodischen Zeitereignisse dürfen nicht mehr geändert werden, sobald sie richtig eingegeben und die nächsten Termine berechnet wurden. Die Einträge sind Begin-date, Begin-time, deadLine und Periode. Die Periode ist die Summe von zwei Teilperioden. Die Deadline hat hier genau die gleiche Funktionalität wie bei dem oben genannten time event atOnce Ereignismuster. Ein Ereignismustertyp hat direkten Einfluss auf folgende Parameter von Triggern. • Die Auswahl eines Ereignismusters vom Typ modify event veranlasst, dass die folgende Felder sichtbar werden: Action-Time und FOR EACH, die oben bereits beschrieben wurden. Wenn der Ereignismustertyp ,,modify event” gewählt wird, so müssen der DML-Befehl und der TabellenName eingegeben werden. Nach der Eingabe des DML-Befehls werden Felder für folgende Triggerparameter sichtbar. – Ist der DML-Befehl DELETE, dann sind OLD TABLE und OLD ROW sichtbar. – Ist der DML-Befehl INSERT, dann sind NEW TABLE und NEW ROW sichtbar. 5.3. SEMANTIK DER ARTA-TRIGGERBEARBEITUNG 53 – Ist der DML-Befehl UPDATE, dann sind alle Parameter NEW TABLE , NEW ROW , OLD TABLE und OLD ROW sichtbar. In sichtbare Felder können Einträge eingegeben werden. Diese Felder müssen aber nicht ausgefüllt werden, es sei denn der Benutzer hat vor, diese in anderen Triggerteilen (Triggercondition oder Triggeraction) zu benutzen. • Bei Ereignismustern vom Typ atOnce oder periodic sind keine der o.g. Triggerparameter erlaubt und deswegen bleiben die entsprechenden Felder unsichtbar. 5.3 Semantik der ARTA-Triggerbearbeitung 11 12 1 2 10 9 3 8 4 7 6 5 Benutzeroberfläche Verwaltung von Zeitereignissen S VvZE Bearbeitung von Aftertriggern S Feuert S Trigger? Nein S Ausführen BvA Nein Ja Feuert S Aftertrigger? Bearbeitung von Beforetriggern BvB Nein Ende der Bearbeitung Befehlsausführungssystem BAS Abbildung 5.2: Triggerbearbeitung in ARTA Im Bild 5.2 wurde der Flussdiagramm der Triggerbearbeitung in ARTA geschildert. S ist ein (SQL-)Befehl, der entweder von einem Benutzer aus dem ARTA-Hauptfenster (Benutzeroberfläche) eingegeben wird oder die Ausgabe von VvZE5 ist. Tritt ein Änderungsereingis wegen Ausführung des Befehls S 5 Verwaltung von Zeitereignissen wird in 5.4 beschrieben. 54 KAPITEL 5. KONZEPT ein, wird die Menge von gefeuerten Beforetriggern berechnet, in der diese Beforetrigger nach ihren Prioritäten sortiert werden. Nach dem Sortieren werden die Beforetrigger in einer Schleife bearbeitet. Nach der Bearbeitung von Beforetrigger soll der Befehl S ausgeführt werden. Als nächstes wird die Mange von Aftertriggern bestimmt, die an dieser Stelle nach ihren Prioritäten sortiert werden. In einer Schleife werden Aftertrigger bearbeitet, indem Aktionsteile von Aftertriggern als neuer S-Befehl in BAS eingegeben werden und BAS(Aftertrigger.action) ausgeführt wird. Für die Bearbeitung von Before- und Aftertriggern sollen zuerst die vordefinierten Parameterinhalte berechnet werden, weil vor Bedingungsuswertung sowie vor der Ausführung von BAS(Aftertrigger.action) diese Parameter in Triggerbedingungsteil und Triggeraktionsteil durch ihre Inhalte ersetzt werden sollen. Die Parameter werden zu jedem Befehl berechnet und für die Bearbeitung von Before- und Aftertriggern eingesetzt. Die Bearbeitung von einem Befehl wird vollständig in BAS durchgeführt, indem auch die internen Variablen beschrieben werden. Eintritt von Änderungsereignissen Der SQL-Ausdruck ,,S” wird als Eingabe ins Befehlsausführungssystem (BAS) hinzugefügt. In BAS wird zuerst getestet, ob durch Ausführung von S ein definiertes Ereignis eintritt und dieses Ereignis Trigger feuert. Ist das nicht der Fall, so wird S ausgeführt, ohne ein Triggerbearbeitung durchzuführen. Feuert die Ausführung von S Trigger (Tritt ein Änderungsereignis ein), werden die gefeuerten Trigger bearbeitet. Die Semantik der Triggerbearbeitung im ARTA ist bis auf einige Unterschiede auf die im SQL3 vorgestellte Semantik aufgebaut. Im folgenden wird die Prozedur ,,Befehlsausführungssystem (BAS)” beschrieben, die Ausführung eines SQL-Befehls beobachtet und gefeuerte Trigger bearbeitet: Anfang von BAS(S) Nach der Eingabe des SQL-Befehls wird ein so genannter Trigger Execution Context (TEC) erzeugt, der besteht aus • einer Menge von Transitionen TRN, • dem Namen der betroffenen Tabelle, • dem Typ des Ereignisses (DELETE | INSERT | UPDATE). Die Menge der Transitionen besteht aus zwei Transitionstabellen. Die Transitionstabellen beinhalten Datensätze, die durch Korrelationen zwischen zwei 5.3. SEMANTIK DER ARTA-TRIGGERBEARBEITUNG 55 Zuständen der Originaltabelle vor der SQL-Ausführung und nach der SQLAusführung enstehen können. Die betroffenen Datensätze werden in zwei temporären Tabellen gespeichert. Die Ausführung eines SQL-Ausdrucks und die Bearbeitung der von ihm betroffenen Trigger werden in einer Transaktion durchgeführt. Die temporären Tabellen sollen bis Ende der aktuellen Transaktion existieren. Weil die temporären Tabellen nur während der aktuellen Transition benötigt werden, sollen sie dann nach der Beendigung der Transaktion gelöscht werden. Die alten Datensätze werden in der ,,OLD Transitionstabelle” und die neuen Datensätze in der ,,NEW Transitionstabelle” zwischengespeichert. Die Transitionstabellen können Daten enthalten, wenn überhaupt Datensätze von der Originaltabelle betroffen sind. Dabei ist nicht unwahrscheinlich, dass Anwendung eines SQL-Befehls Datenbank nicht ändert. Das WHERE-Teil eines SQLAusdrucks kann so formuliert werden, dass durch den SQL-Ausdruck keine Datensätze ausgewählt werden. Auch der Zustand von Tabellen spielt eine große Rolle, ob Datensätze betroffen werden, wie z.B. aus einer leeren Tabelle können keine Datensätze ausgewählt werden. Je nach DML-Befehl können beide oder nur eine der Transitionstabellen Daten beinhalten: • Bei DELETE kann nur die OLD Transitionstabelle Daten enthalten. • Bei INSERT kann nur die NEW Transitionstabelle Daten enthalten. • Bei UPDATE können beide OLD Transitionstabelle und NEW Transitionstabelle Daten enthalten. Nachdem TEC berechnet und bereitgestellt wurde, soll die Menge BTrs bestimmt werden. BTrs besteht aus Beforetriggern, die durch das Ereignis (Ausführung von S) gefeuert werden. Falls die Menge der Transitionen leer ist, werden alle ROW-Level-Beforetrigger aus der Menge BTrs entfernt, weil ROWLevel-Trigger nur für jede betroffene Zeile gefeuert werden dürfen. Betrifft der SQL-Ausdruck keine Zeile der Tabelle, so wird kein ROW-Level-Trigger bearbeitet. An dieser Stelle werden die Beforetrigger nach ihren Erzeugungsdatum sortiert. Alle (bzw. übrige) Beforetrigger werden in einer Schleife abgearbeitet. Dabei werden die Statement-Level-Trigger nur einmal und ROW-Level-Trigger für jede betroffene Zeile einmal wie folgt bearbeitet. Für jeden Trigger T in BTrs: • T ist ein STATEMENT-Level Trigger: 56 KAPITEL 5. KONZEPT 1. Die Inhalte von Triggerparametern sollen aus den Transitionstabellen berechnet werden. 2. Nach dem Ersetzen die Parameter durch deren Inhalte in den Triggerbedingungs- bzw. in den Triggeraktionsteil von T, wird die Bedingungsauswertung durchgeführt. 3. Ist das Ergebnis der Bedingungsauswertung = True, dann wird Triggeraktion ausgeführt, ohne weitere Trigger zu feuern. • T ist ein ROW-Level Trigger: Hier soll für jede betroffene Zeile: 1. Die Parameterinhalte werden aus den Transitionen berechnet. 2. Die Parameter werden durch ihre Inhalte in Bedingungsteil und Aktionsteil von T ersetzt und die Bedingungsauswertung wird durchgeführt. 3. Gibt Bedingungsauswertung ,,True” zurück, wird Triggeraktion ausgeführt (wieder ohne Trigger zu feuern). Alle Beforetrigger wurden bis hierhin abgearbeitet. An dieser Stelle wird der SQL-Ausdruck S ausgeführt. Nach der S-Ausführung wird die Menge ATrs bestimmt, die aus Aftertriggern besteht, die das Ereignis ,,S-Ausführung” feuert. Ist TRN (die Menge der Transitionen) leer, werden alle ROW-Level-Trigger aus der Menge ATrs entfernt. Für jeden Trigger T in ATrs gilt: • T ist ein STATEMENT-Level Trigger: 1. Die Parameterinhalte sollen aus der Transitionstabellen berechnet werden. 2. Nach dem Ersetzen die Parameter durch ihre Inhalte in Condition und Actionateil von T, wird die Bedingungsauswertung durchgeführt. 3. Ist das Ergebnis der Bedingungsauswertung = True, soll Triggeraktion als eine neue Eingabe S in das Befehlsausführungssystem eingegeben werden und BAS(S:=Triggeraction) ausgeführt werden (Rekursion). • T ist ein ROW-Level Trigger: Für jede betroffene Zeile: 1. Die Parameterinhalte werden aus den Transitionen berechnet. 5.3. SEMANTIK DER ARTA-TRIGGERBEARBEITUNG 57 2. Die Parameter werden durch ihre Inhalte in Bedingungsteil und Aktionsteil von T ersetzt und die Bedingungsauswertung wird durchgeführt. 3. Gibt Bedingungsauswertung ,,True” zurück, soll Triggeraktion als eine neue Eingabe S in das Befehlsausführungssystem eingegeben werden, d.h. BAS(S:=Triggeraction) (Rekursion. Wie es aus der Semantikbeschreibung ersichtlich ist, wird die Bearbeitung von Triggern durch Einsatz der Rekursion erledigt. Durch den rekursiven Aufruf der Prozedur werden voneinander unabhängigen Instanzen von dieser Prozedur erzeugt. Jeder Prozeduraufruf deklariert seine eigenen Variablen (vor allem TEC) und läuft erst dann zu Ende, nachdem alle durch sie aufgerufene Instanzen zu Ende durchgeführt sind. Bearbeitung von Beforetriggern SQL-Ausführung Ereignis Bearbeitung von Aftertriggern E E1 E 11 E2 E 12 ... En E 1m ... ... E i1 ... E i2 ... E ij Abbildung 5.3: Instanzorientierte Triggerbearbeitungssemantik Instanzorientierte Regelbearbeitung Durch Rekursion wird die Triggerbearbeitungssemantik in ARTA instanzorientiert. Mit der instanzorienterten Semantik wird die Triggerbearbeitung in 58 KAPITEL 5. KONZEPT Tiefe durchgeführt, durch die die Bearbeitung von Triggern insoweit in die Tiefe weitergeleitet wird, bis keine Aftertrigger mehr gefeuert werden. Danach wird die Bearbeitung in einer Stufe höher weiter durchgeführt. Im Bild 5.3 wurde die Instanzorientierung der Triggerbearbeitung von ARTA geschildert. 5.4 Verwaltung von Zeitereignissen Zeitereignisse sind in ARTA in zwei Untertypen aufgeteilt: absolute und periodische Zeitereignisse. Absolute Zeitereignisse treten nur einmal an einem bestimmten Zeitpunkt auf. Periodische Zeitereignisse treten wiederholt in gleichen Zeitabständen auf. Ein periodisches Zeitereignismuster wird nach jedem Eintreten seines Zeitereignisses modifiziert, indem für das Zeitereignismuster ein neuer Termin berechnet wird. Dieser neue Termin wird zum ersten Mal nach der erfolgreichen Ereignismusterdefinition berechnet. Der neue Termin, d.h. wann das periodische Zeitereignis nochmal eintritt, ergibt sich sich aus der Addition des Anfangstermins bzw. des aktuellen Termins mit der Periode. Wie schon in diesem Kapitel erwähnt wurde, wird in ARTA immer nur maximal ein Timer gesetzt. Im Bild 5.4 wird die Arbeitsweise der Zeitereignisbearbeitung und System anschalten (Datenbank öffnen) Inerres Zeitereignis Wurde ein Zeit-Ereignis ( ZE ) in der Vergangenheit geschehen und darf noch Trigger feuern(deadLine)? Ist ein Ereignis in weniger als einer Woche zum Nein Zeitpunkt t zu erwarten? schicken. Nein Timer setzen für einen Tag Ja / t Ja / ZE ZE an Zeitereignisbearbeitungssytem 11 12 1 2 10 9 3 8 4 7 6 5 ZE 11 12 1 2 10 9 3 8 4 7 6 5 Timer setzen zum t Warten bis alle durch ZE gefeuerten Trigger bis zum Ende abgearbeitet sind. Abbildung 5.4: Flußdiagram zu der Zeitereignisbearbeitung Timerverwaltung geschildert. Immer beim Einschalten des System sowie nach einer durch ein Zeitereignis veranlasste Datenbankänderung wird nach den in 5.4. VERWALTUNG VON ZEITEREIGNISSEN 59 Vergangenheit aufgetretenen Zeitereignissen gesucht. Solche (alten) Zeitereignisse werden bearbeitet, falls deren Deadlines noch nicht um sind. Wenn keine Zeitereignisse aus der Vergangenheit zu bearbeiten sind, wird ein Timer wie folgt gesetzt: Der Timer wird zu den nächsten Zeitpunkt gesetzt, wenn ein Zeitereignis innerhalb einer Woche in der Zukunft erwartet wird. Tritt aber kein Zeitereignis in einer Woche ein bzw. liegen alle erwarteten Zeitereignisse an Daten über einer Woche in der Zukunft hinaus, so wird ein Timer für einen Tag gesetzt. Ablauf jedes Timers wird als das Eintreten eines Zeitereignisses interpretiert. Wurde sein Timer für ein bestimmtes Datum eingesetzt, das innerhalb einer Woche eintreten sollte, so werden Trigger gefeuert und bearbeitet. Bei Eintreten eines inneren Zeitereignisses, d.h. beim Ablauf des für einen Tag eingesetzten Timers, wird wieder nach einem erwarteten Zeitereignis innerhalb einer Woche gesucht. wird eins gefunden, so wird ein Timer für das Datum dieses Ereignisses gesetzt, sonst wird wieder ein Timer für einen weiteren Tag gesetzt. deadLine: Zu jedem Zeitereignismuster gibt es eine Deadline , deren Einheit Minute ist. Das Bild 5.5 zeigt eine Deadline auf der Zeitachse bezüglich eines Zeitpunkte t1 deadLine Zeit/Min Abbildung 5.5: Allgemeine Deadline-Darstellung ,,t1”. Am Zeitpunkt t1 wird das Zeitereignis E erwartet. Zeitpunkt des Bearbeitungsanfangs der durch E gefeuerten Trigger darf nur innerhalb des Zeitintervalls [t1, t1+Deadline] liegen. Der Zweck der Deadline ist, dass beim Ausführen vorher geforderten Datenänderungen eine gewisse Zeit gebraucht wird. Diese Bearbeitungszeitadauer spielt bei manchen Regeln eine große Rolle (EchtzeitProblem). Es kann vorkommen, dass Bearbeitung eines Triggers innerhalb 10 Minuten nach dem Eintreten eines Zeitereignisses beginnen soll und nach dem 60 KAPITEL 5. KONZEPT Ablauf dieser 10-Minuten darf der Trigger nicht mehr bearbeitet werden. Die 5 minütige Mindestgrenze ist zur Sicherheit vorgesehen. Vorsichtshalber soll der Trigger-Entwickler größere Deadlines einplanen. Die Deadline ist beschränkt auf dem Interval [5 Minute , ∞). Deadline wird auch noch für die Erledigung einer weiteren wichtigen Aufgabe eingesetzt. Eine ARTA-Datenbank wird normalerweise nicht ununterbrochen eingesetzt. Der Einsatz des Systems kann abgebrochen und in einem in unbekannter Zukunft liegenden Zeitpunkt wieder in Anspruch genommen werden, z.B. eine Datenbank für die Verwaltung der Informationen über die eingeschriebenen Informatik-Studenten an der Universität Bonn. Diese Datenbank wird nur an Werktagen und zwar nur während der Arbeitszeiten eingesetzt außerhalb dieser Zeiten ist diese Datenbank abgeschaltet. Im Bild 5.6 wurde Einsatz und der Ruhezustand einer Datenbank im Zusammenhang mit den Zeitereignissen auf einer Zeitachse geschildert. t1 t2 t3 t4 tn ... Einsatz Pause Einsatz Pause Zeit Abbildung 5.6: Systemeinsatz und Ereignisse Die Zeitereignissen, die in dem Ruhezustand des System Trigger hätten feuern müssen, werden wie folgt behandelt: Die Ereignisse, deren Deadline noch nicht vorbei ist, werden berücksichtigt und der Rest wird ignoriert. Ein Trigger, dessen Ereignismuter ein Zeitereignismuster mit einer Deadline ist, ähnelt der Regel R2 , die als Beispiel im Abschnitt 2.1 gegeben wurde. Triggerbearbeitung nach Eintreten eines Zeitereignisses: Die Trigger, die durch Eintritt von Zeitereignissen gefeuert werden, besitzen 5.4. VERWALTUNG VON ZEITEREIGNISSEN 61 keine Parameter. Deswegen hängt das Ergebnis der Triggerbedingungsauswertung von dem aktuellen Datenbankzustand und von der aktuellen Zeit ab. In Bedingungen solcher Trigger können z.B. Zeitstempel bestimmter Datensätze benutzt werden. Durch Vergleichen von Zeitstempeln mit der aktuellen Zeit in der Bedingungsteil eines Triggers können bestimmte Situationen abgefragt werden. Ist ZTrs die Menge von Triggern, die ein Zeitereignis gefeuert hat, werden diese Trigger wie folgt bearbeitet: • Konfliktsituation: Wurden mehrere (mehr als ein) Trigger gefeuret, werden sie nach ihren Prioritäten sortiert. (#ZT rs > 1) • Für jeden Trigger T von ZTrs Bedingung von T auswerten. Ist die Auswertung = ,,True”, setze Aktion von T als eine Eingabe für den Aufruf von BAS6 ein: BAS(T.action). 6 Vgl. 5.3 62 KAPITEL 5. KONZEPT Kapitel 6 Implementierung Im folgenden Kapitel wird die Implementierung von ARTA beschrieben. Dazu wurde die Programmiersprache VBA benutzt. Bei der Realisierung von Benutzeroberflächen wurden Formulare und deren Ereignisse eingesetzt. Triggerdaten und Ereignisdaten werden im Laufe des Programms in internen Variablen in Form von Objekten bereitgestellt. Dafür wurden verschiedene Klassenmodule programmiert, die entweder als einzelne Objekte oder als Auflistungen eingesetzt wurden. Zunächst werden die Architektur und die Komponenten von ARTA vorgestellt. Danach wird jede Komponente detailliert beschrieben. Anschließend werden besondere Aspekte erläutert, die in manchen Stellen zur Lösung von Implementierungsproblemen benutzt wurden. 6.1 Architektur von ARTA Bei der Architekturplanung wurde auf folgende Punkte geachtet: • Datenbankänderungen dürfen nicht direkt mit Hilfe von Access Standardkomponenten vorgenommen werden, weil die Ereignisse dabei nicht bemerkt werden können. Für die Kommunikation mit ARTA wurde eine spezielle Oberfläche vorgesehen, die durch den Einsatz eines Formulars realisiert ist. Diese Oberfläche (bzw. Fenster) nennt sich ARTA-Haupfenster. • Die Existenz eines Werkzeuges, mit dessen Hilfe Trigger bzw. Ereignismuster definiert werden, ist unverzichtbar. Mit diesem Werkzeug sollte auch die Änderung von Triggern und Ereignismustern möglich sein. In ARTA werden Trigger bzw. Ereignismuster formularbasiert verwaltet, d.h. Formulare wurden als Oberflächen so programmiert, dass alle Teile von Triggern bzw. Ereignismustern eingegeben und geändert werden können. 63 64 KAPITEL 6. IMPLEMENTIERUNG ARTA Benutzeroberflächen SQL Eingabeformular 11 12 1 10 2 9 3 8 4 7 6 5 BAS TVS Befehlsausführungssytem Trigger und Eventmuster Definitionsformular Timerverwaltungssystem Regelbasis Jet DB Engine Standard-Komponente von MS-Access Datenbank MS-Access Abbildung 6.1: ARTA und Access 6.2. BENUTZEROBERFLÄCHE 65 • Die Triggerdaten sowie Ereignismusterdaten sollen gesichert werden, es ist folglich eine Regelbasis notwendig. Die Regelbasis wird in ARTA in Access Tabellen gespeichert. • Zur Verwaltung von Zeitereignissen ist eine Uhr bzw. ein darauf aufgebauter Timer notwendig. • Ein internes System, um Ereignisse zu registrieren und gefeuerte Trigger zu bearbeiten, ist eine Basis für die Triggerfunktionalität eines aktiven Datenbankmanagementsystems. Ein solches System ist der Kern jedes aktiven Datenbankmanagementsystems und damit auch von ARTA . ARTA besteht aus Komponenten, die im Bild 6.1 dargestellt sind. Für die Realisierung von ARTA wurden Access Objekte (Komponenten) benutzt, die in ARTA als Systemkomponenten gekennzeichnet sind. Die Namen dieser Objekte fangen daher alle mit der Vorsilbe ,,USys ” an. Zusätzlich ist deren Eigenschaft ,,ausgeblendet” aktiviert. ARTA-Objekte können dadurch unsichtbar und ,,nicht mehr direkt modifizierbar” werden.1 ARTA-Komponenten werden in den folgenden Abschnitten detailliert beschrieben. 6.2 Benutzeroberfläche Die Implementierung von Benutzeroberflächen wurde mit Hilfe von AccessFormularen und deren Eigenschaften realisiert. Zur Realisierung von Oberflächen wurden Formularereignisse eingesetzt, die besonders geeignet sind, Eingaben zu überprüfen. Mit Hilfe von Formularereignissen wird z.B. die Syntax einer Triggerdefinition überprüft. Bei einem Syntaxfehler wird eine entsprechende Fehlermeldung ausgegeben. Mit dem Öffnen einer ARTA-Datenbank wird direkt ein Formular geöffnet, das im Bild 6.3 gezeigt wird. Dieses Formular heißt ARTA-Hauptfenster. Im ARTA-Hauptfenster sind Buttons (Befehlsschaltflächen) vorgesehen, die dem Benutzer helfen andere Fenster zu öffnen oder den SQL-Befehl auszuführen. Mit Hilfe des ARTA-Hauptfensters und die darauf eingebauten Buttons und Tools werden die folgenden Anforderungen erfüllt: • Durch ,,SQL ausführen” wird ein SQL-Befehl, der in einen dafür vorgesehenen Textfeld eingegeben wurde, an das Befehlausführungssystem gesendet. Wird jede Datenbank-Änderung durch dieses Formular ausgeführt, so wird das auftretende Ereignis erkannt und die gefeuerten Trigger bearbeitet. Eine Tabelle ist in ARTA dafür vorgesehen, in der SQL-Befehle (und gegebenenfalls deren Bemerkungen) gespeichert werden. Ein neuer 1 Vgl. Abschnitt 4.4 66 KAPITEL 6. IMPLEMENTIERUNG Abbildung 6.2: ARTA-Hauptfenster Datensatz im ARTA-Hauptfenster bedeutet einen Eintrag in dieser Tabelle. Das Speichern von SQL-Befehlen macht nur Sinn, wenn diese Befehle wiederholend angewendet werden. Das kommt besonders beim Testen von ARTA in Einsatz. • Durch ,,SQL löschen” wird der SQL-Befehl und die dazu gehörige Bemerkung gelöscht. • Durch Mausklick auf dem Button ,,Events” wird ein Fenster geöffnet, in dem Ereignismuster angezeigt werden. Löschbare Ereignismuster dürfen in diesem Fenster gelöscht werden. Löschbar sind diejenigen Ereignismuster, die mit keinem Trigger verknüpft sind. • Durch ,,Trigger bearbeiten” wird ein Fenster zur Triggerverwaltung geöffnet. • Durch ,,Show Messages” sieht man die wichtigsten Informationen über den Triggerbearbeitungsvorgang in einem Fenster. • Durch ,,Show Logfile” wird ein ausführliches Protokoll vom Einsatz des ARTA-Systems angezeigt. Das Fenster, das durch Mausklick auf ,,Trigger bearbeiten” aufgeht, ist eine in ARTA eingebaute Möglichkeit, Trigger und zugehörige Ereignismuster zu verwalten. Im Bild 6.3 sind zwei Versionen eines Access-Hauptfensters vorgestellt. Eine zeigt das originale Access-Hauptfenster, das z.Z. in Access vorliegt. 6.2. BENUTZEROBERFLÄCHE 67 In diesem Access-Hauptfenster sind Möglichkeiten angeboten, neue Access Objekte und Komponente von bestimmter Art zu definieren und zu verwalten. Die andere Darstellung ist eine Wunschsform. Das Access-Hauptfenster könnte so aussehen, wenn Trigger als eine Access-Komponente unterstützt wären. Weil in Access keine Möglichkeit existiert, Trigger und Ereignismuster zu verwalten, wird die Trigger- und Ereignismusterverwaltung formularbasiert durchgeführt. Formulare werden als Benutzeroberflächen (Fenster bzw. Windows) programmiert. In ARTA-Fenstern können Trigger und Ereignismuster definiert, angezeigt, gelöscht oder modifiziert werden können. Die gewünschte Änderung Trigger Abbildung 6.3: Hauptfenster von Access 6.2.1 Verwaltung von Triggern Das Formular zur Verwaltung von Triggern wird ARTA-Triggerfenster genannt. In Access gibt es die Möglichkeit auszuwählen, in welchem Status (Modus) ein Formular aufgemacht wird. Die Eigenschaften eines Formulars bestimmen, ob ein neuer Datensatz eingegeben, der aktuelle Datensatz gelöscht oder geändert werden darf. Diese Eigenschaften können auch mit Hilfe von VBA-Befehlen im Formular-Klassenmodul eingestellt werden. Im folgenden Beispiel wird beim Öffnen eines Formulars eingestellt, dass die Fensterbeschriftung zu ,,Trigger anschauen!” geändert wird und keine Daten eingegeben, gelöscht sowie geändert werden dürfen. 68 KAPITEL 6. IMPLEMENTIERUNG Private Sub Form_Open(Cancel As Integer) Me.Caption = "Trigger anschauen!" Me.AllowAdditions = False Me.AllowDeletions = False Me.AllowEdits = False End Sub Beim Öffnen des Triggerfensters sind die Eigenschaften so eingestellt, dass keine neuen Trigger eingegeben werden dürfen, und der aktuelle Trigger nicht zu ändern ist (Modus ,,Trigger anschauen”). Das Triggerfenster wurde im Bild 6.4 in diesem Modus (,,Trigger anschauen”) gezeigt. Mit dem aktuellen Trigger ist der Trigger gemeint, der im Fenster zu sehen ist. Nach einem Mausklick auf den entsprechenden Button im Triggerfenster werden Einstellungen des Formulars so geändert, dass ein neuer Trigger eingegeben bzw. der aktuelle Trigger gelöscht oder geändert werden kann. Mausklicks auf Buttons haben dieselben Funtionalitäten wie die entsprechenden Befehle, die in SQL3 benutzt werden, z.B. Create Trigger, Delete Trigger, Modify Trigger. Ob ein Trigger aktiv oder nicht aktiv ist, wird durch Markierung des hierzu vorgesehenen Feldes eingestellt. Aktiv sind diejenigen Trigger, die beim Auftritt eines Ereignisses berücksichtigt werden. Nicht aktive Trigger (passive Trigger) werden ignoriert und sind nur intern gespeichert. Trigger, die fehlerhaft sind, werden als passiv gespeichert, wodurch eine korrekte Triggerbearbeitung garantiert wird. Bei Textfeldern mit langen Texten kann der Text mittels speziell dafür vorgesehenen Buttons oder Maus-Doppelklick in einem größeren Fenster angesehen und bearbeitet werden. Dies ist sehr wichtig bei Triggerbedingung und Triggeraktion, die im Triggerfenster nur begrenzten Platz haben und manchmal (bei langen Texten) nicht vollständig angezeigt werden. Diese Technik, lange Texte in einem separaten Fenster zu bearbeiten, ist in jedem Fenster von ARTA vorgesehen, bei dem Textfelder für lange Texte existieren. 6.2.2 Verwaltung von Ereignismustern Ereignismuster werden gleichzeitig mit Triggern erzeugt und modifiziert. Das Löschen eines Ereignismusters wird in einem separaten Fenster durchgeführt, das durch Maus-Klick auf dem entsprechende Button im ARTA-Hauptfenster (Events) geöffnet wird. Das Bild 6.5 zeigt dieses Fenster, in dem die Ereignismuster angeschaut und gelöscht werden können. Das Fenster heißt das ARTA- 6.2. BENUTZEROBERFLÄCHE Abbildung 6.4: Das Trigger-Fenster im Modus ,,Trigger anschauen” 69 70 KAPITEL 6. IMPLEMENTIERUNG Ereignismusterfenster. Im Ereignismusterfenster können vorhandene Ereignis- Abbildung 6.5: ARTA-Ereignis-Muster-Fenster muster nur angeschaut und gelöscht werden. Dieses Fenster hat nur diese Funktionalität und sonst keine. Beim Löschen eines Triggers wird das Ereignismuster dieses Triggers ,,nicht mitgelöscht”. Das Löschen eines Triggers wird im Triggerfenster durchgeführt und das Löschen eines Ereignismusters muss extra im Ereignismusterfenster durchgeführt werden. Das mag vielleicht ein bisschen lästig erscheinen, macht aber Sinn, wenn man beachtet, dass ein Ereignismuster mit mehreren Triggern verknüpft sein darf. Ein Ereignismuster darf nur dann gelöscht werden, wenn mit ihm kein Trigger mehr verknüpft ist. Im Ereignismusterfenster wird beim Anzeigen jedes Ereignismusters seine Löschbarkeit automatisch geprüft und (informativ) angezeigt. Im Ereignismusterfenster sind bestimmte Buttons eingebracht worden, mit deren Hilfe das aktuelle Ereignismuster (wenn es löschbar ist) bzw. alle löschbaren Ereignismuster gelöscht werden können. 6.3 Speichern von Regeln Die Menge aller Trigger heißt Regelbasis, deren Daten gesichert werden müssen. Dafür sind zwei Tabellen in ARTA vorgesehen: In der Tabelle ,,USys trigger” werden Daten von Triggern gespeichert, in der Anderen ,,USys event” werden Daten von Ereignismustern gespeichert. Weil die Name dieser Tabellen mit 6.3. SPEICHERN VON REGELN 71 ,,USys” anfangen, werden diese Tabellen als Access Systemkomponente interpretiert und können unsichtbar werden2 . Diese Tabellen sind miteinander durch eine Access-Beziehung verknüpft. Die Eigenschaften dieser Beziehung sind so eingestellt worden, dass ein Ereignismuster mit mehreren Triggern verbunden werden darf. Abbildung 6.6: Beziehung zwischen der Tabelle von Triggern mit der Tabelle von Ereignismutern USys trigger Der Primärschlüssel besteht in dieser Tabelle aus zwei Felder: ,,trigger id” und ,,trigger name”, wobei ersteres ,,trigger id” nur als eine interne Indentifikatiosnummer einzusehen ist. Dagegen sorgt ,,trigger name” dafür, dass keine zwei Trigger mit dem gleichen Namen versehen werden. Damit wird die Eindeutigkeit von Triggernamen gewährleistet. Die Priorität eines Triggers wird unter dem Feld ,,trigger priority gespeichert. Damit eine sichere Priorität garantiert wird, wurde das Feld so eingestellt, dass 1. sein Inhalt nur eine natürliche Zahl größer als ,,0” sein darf, 2. keine zwei Datensätze mit gleichem Inhalt erlaubt sind, 2 s. Unterabschnitt: 4.4 72 KAPITEL 6. IMPLEMENTIERUNG 3. und das Feld nicht leer sein darf. Das Feld ,,trigger event name” ist in dieser Tabelle ein Fremdschlüssel, dessen Inhalt nur ein Element aus der Menge der vorhandenen Ereignismusternamen sein darf. Doppelte Daten dürfen bei diesem Feld vorkommen, damit mehrere Trigger mit einem Ereignismuster verbunden werden können. Weitere Felder in dieser Tabelle beinhalten Teilinformationen eines Triggers. z.B. Bedingung, Aktion, Aktivität, . . . . USys event Mit ,,event name” werden Ereignismuster in dieser Tabelle eindeutig bestimmt. Der Primärschlüssel besteht auch in dieser Tabelle aus zwei Feldern: ,,event id” und ,,event name”. Alle drei Ereignismustertypen werden in dieser Tabelle zusammen gespeichert. Der Typ eines Ereignismusters wird durch den Inhalt des Feldes ,,event type” gegeben. In diesem Feld ist dementsprechend nur ein Eintrag aus {time event atOnce, time event periodic und modify event} möglich. Nachdem der Ereignismustertyp bestimmt worden ist, werden die für diesen Typ relevante Daten berücksichtigt. 6.4 Klassen, Module und Objekte von ARTA In ARTA wurden Module und Klassenmodule programmiert, um die Informationen von Triggern und Ereignismustern für die Erledigung von weiteren Aufgaben bereitzustellen. Diese Aufgaben sind Ereigniserkennung, die Bearbeitung von Triggern eines aufgetretenen Ereignisses, die Berechnung des nächsten Termins für den Timer und das Setzen des Timers. 6.4.1 Klassenmodule In ARTA wurden folgende Klassenmodule programmiert: USys clsTrigger: Unter einem Objekt dieser Klasse werden Daten eines Triggers gespeichert. USys clsTriggers: Ein Objekt dieser Klasse ist eine Auflistung von Triggern. Ein neuer Trigger wird an der richtigen Stelle der Auflistung so hinzugefügt, dass Trigger automatisch nach Prioritäten sortiert sind. So hat das erste Element eines Objekts dieser Klassen immer die höchste Priorität (niedrigste Prioritätszahl). 6.4. KLASSEN, MODULE UND OBJEKTE VON ARTA 73 USys clsModifyEvent: Ein Objekt dieser Klasse beinhaltet alle Trigger (ein Objekt der Klasse ,,USys clsTriggers”), die beim Auftritt eines bestimmten Änderungsereignisses gefeuert werden. Das Ereignis wird durch eine interne Variable dieser Klasse ,,mstr eventKey” identifiziert, die aus DML-Befehl und Tabellenname (<DML> <table name>) besteht. Dieser Schlüssel wird erst in einer Auflistung von der Klasse USys clsModifyEvents benutzt. USys clsModifyEvents: Ein Objekt dieser Klasse ist eine Auflistung, deren Elemente Objekte der Klasse ,,USys clsModifyEvent” sind. In der Auflistung sind die Ereignismuster und die mit ihnen verbundenen Trigger so eingefügt, dass alle Trigger mit gleichem ,,mstr eventKey” ((<DML><table name>)) unter einem Element zusammengefasst sind. Mit dem Aufruf der folgenden Prozeduren dieser Klasse werden alle Trigger (bzw. alle Beforetrigger und Aftertrigger) zurück gegeben, die beim Auftritt eines Änderungsereignisses ,,strDMLCommon” auf die Tabelle ,,strTablename” gefeuert werden müssen: Public Function getTriggers( _ ByVal strDMLCommon As String, _ ByVal strTablename As String) As USys_clsTriggers Public Function getBeforeTriggers( _ ByVal strDMLCommon As String, _ ByVal strTablename As String) As USys_clsTriggers Public Function getAfterTriggers( _ ByVal strDMLCommon As String, _ ByVal strTablename As String) As USys_clsTriggers USys clsTimeEvent: Ein Objekt dieser Klasse beinhaltet Informationen über ein Zeitereignismuster. Der Typ des Zeitereignisses wird in die interne Variable ,,mstr event type” eingesetzt, und je nach Zeitereignistyp werden spezielle Informationen in andere Variablen eingesetzt. USys clsTimeEvents: Aus dieser Klasse werden Objekte instantiiert, die als Auflistung von Zeitereignisobjekten benutzt werden. Jedes Element einer solchen Auflistung besteht aus Informationen über ein Zeitereignismuster und den Triggern, die beim Auftreten dieses Zeitereignisses gefeuert werden müssen. Wie erwähnt können mehrere 74 KAPITEL 6. IMPLEMENTIERUNG Trigger ein gemeinsames Ereignismuster besitzen. USys clsSetOfTimeEvents: Ein Objekt dieser Klasse ist eine Auflistung von Objekten der Klasse USys clsTimeEvents. Jedes Objekt der Klasse USys clsSetOfTimeEvents besteht aus Triggern, die an einem bestimmten Zeitpunkt gefeuert werden müssen. Diese Trigger können verschiedenen Ereignismuster angehören, die aber alle ein gemeinsames (,,absolutes”) Zeitereignis beschreiben. Mit Hilfe dieser Klasse werden die zwei verschiedenen Zeitereignistypen ,,periodisch und absolut” unter einem Objekt bereitgestellt. Die Prozedur ,,ChangeStatus()” ist in der Klasse USys clsTimeEvent programmiert. Eine Ausführung dieser Prozedur ändert die Eigenschaften des Zeitereignismusters entsprechend dem Typ: Ein absolutes Zeitereignismuster wird als erledigt ,,beDone” vermerkt und für ein periodisches Zeitereignismuster wird der nächste Termin berechnet. USys clsExecute: Diese Klasse beinhaltet die Prozedur ,,Public Sub ExecuteSQL Statement (ByVal strAction As String)”. Diese Prozedur ist sozusagen das Herz des Befehlsausführungssystems (BAS). ,,strAction” ist der Eingabeparameter, der entweder ein direkter SQL-Befehl aus dem ARTA-Hauptfenster oder der Aktionsteil eines Triggers sein kann. Diese Prozedur wird im Abschnitt 6.5 genauer untersucht. USys clsTriggerExecutionContext: Eine Instanz dieser Klasse beinhaltet der sogenannte Trigger execution Context (TEC). In TEC werden bezüglich eines SQL-Ausdruckes folgende Informationen gespeichert: die betroffene Tabelle, der Eereinistyp (DELETE, INSERT oder UPDATE), die Transition. Die Transition besteht aus zwei temporären Tabellen NEW TABLE und OLD TABLE. Die Daten der Transitionstabellen werden in zwei internen Variablen vom Typ ,,Recordset” gespeichert (Private rstOld As DAO.Recordset und Private rstNew As DAO.Recordset). Mit Hilfe von Methoden ,,MoveNextRow, MovePrevRow, MoveFirstRow, MoveLastRow” können diese Variablen durchlaufen werden. USys clsParser: Diese Klasse ist hauptsächlich dafür gedacht, einen SQL-Ausdruck zu analysieren (parsern). Ein SQL-Ausdruck kann sehr kompliziert werden. Es ist sehr aufwendig, einen Parser für die SQL-Standard-Ausdrücke zu programmieren. Die Programmierung eines richtigen SQL-Parsers geht über den Rahmen dieser Arbeit hinaus. Deswegen sind hier nur eingeschränkte SQL-Ausdrücke erlaubt. Prozeduren in dieser Klasse setzten folgende Syntax für SQL-Ausdrücke voraus: INSERT INTO Table . . . , 6.4. KLASSEN, MODULE UND OBJEKTE VON ARTA 75 DELETE [Table.*] FROM Table . . . , UPDATE Table . . . . Zusätzlich sollen diese Ausdrücke mit dem Access SQL-Dialekt übereinstimmen. Deswegen ist es zu empfehlen, SQL-Ausdrücke in einer Access Abfrage (QBEMethode3 ) herzustellen. Eine weitere Einschränkung besteht in der Benennung der Tabellen. Obwohl in Access selbst fast jedes Zeichen für die Benennung der Access-Komponenten erlaubt ist, sollen die Access-Namen in ARTA nur aus Zeichen der Menge {a,b,. . . ,z,A,B,. . . , } bestehen. USys clsTimer und USys clsTimers: Mit Hilfen dieser Klassen wird die Möglichkeit geboten, mehrere Timer zu setzen und zu verwalten. 6.4.2 Module und Objekte Module beinhalten als ,,Global” deklarierte Prozeduren, die direkt aufgerufen werden können. Es ist also für den Aufruf von globalen Prozeduren in einem Modul kein Objekt notwendig. Deswegen können sie an jeder Stelle in VBA bzw. Access vorkommen, wie z.B. in Modulen, Klassenmodulen, Abfragen, Formularen, Makros. In ARTA wurden zwei Module definiert: Das Modul: USys FreeFunctionsARTA In diesem Modul sind einige Funktionen programmiert, die vor allem für die Nutzung in Triggerteilen (Triggerbedingung und Triggeraktion ) vorgesehen sind. Sollten weitere Funktionen zur Erledigung bestimmter Aufgaben notwendig sein, können sie hier einprogrammiert werden. Der Befehl AbortEvent() bricht die Ausführung des aktuellen SQL-Ausdrucks ab. Dieser Befehl sollte in der Aktion eines Beforetriggers eingegeben werden, wenn ein SQL-Ausdruck unter einer bestimmten Bedingung nicht ausgeführt werden darf. AbortAll() bricht die Ausführung von Änderungsbefehle in der aktuellen Transaktion ab und setzt alle Änderungen zurück. WarningByEmail(SQL, changeCommand) funktioniert wie folgt: • Durch die SQL-Abfrage ,,SQL” werden Email-Adressen aus einer geeigneten Tabelle ausgewählt. Dabei ist zu beachten, dass jeweils das erste Feld in der SQL-Abfrage als Email-Adresse auszuwählen ist. 3 Vgl. Abschnitt 4.1, Paragraph ,,Abfragen und SQL-Befehle” 76 KAPITEL 6. IMPLEMENTIERUNG • In einem Warnungsfenster wird der Benutzer darauf hingewiesen, Nachrichten an diese Email-Adressen zu schicken. • Durch ,,changeCommand” wird eine Änderung an einem bestimmten Feld der ausgewählten Datensätze dieser Tabelle angezeigt. ,,changeCommand” hat die Syntax ,,<feldname>=<value>”. Mit einer geeigneten Modifikation (Umprogrammierung) kann diese Prozedur so verwendet werden, dass das Email-Programm vom Betriebsystem automatisch ausgeführt wird und Emails verschickt werden. DatumAdd(number, Interval, date) addiert ,,nummber”-viele ,,Intervale” zu ,,date” und gibt das Ergebnis zurück. ,,nummber” ist eine natürliche Zahl, ,,Intervale” ist ein Element aus der Menge {’Minute’, ’Stunde’, ’Tag’, ’Woche’, ’Monat’, ’Quartal’, ’Jahr’} und ,,date” ist eine Datumangabe. DatumAb(dtefrom, Interval, dteuntil) gibt der Zeitabstand zwischen ,,dtefrom” und , ,,dteuntil” in der Einheit ,,Interval” zurück. Das Modul: USys ToolsAndPublicsARTA Auf diesem Modul wurden die globale Variablen (Objekte) und Funktionen abgelegt, die zur Triggerbearbeitung benutzt werden. Im folgenden sind die wichtigsten Objekte aufgelistet: Unter der Auflistung allTimeEvents (As USys clsSetOfTimeEvents) sind alle Zeitereignismuster abgelegt. Die Zeitereignismuster sind in der Auflistung nach Datum, an dem das zugehörige Zeitereignis erwartet wird, sortiert. Dabei entspricht das erste erwartete Zeitereignis dem erste Zeitereignismuster in der Auflistung. Unter der Auflistung allModifyEvents (As USys clsModifyEvents) sind alle Änderungsereignismuster abgelegt. Jedes Objekt dieser Auflistung ist eine Gruppe von Triggern, die beim Auftreten eines bestimmten Änderungsereignisses an einer bestimmten Tabelle gefeuert werden müssen. allTimers (As USys clsTimers) beinhaltet alle gesetzten Timer in ARTA. Sollte in einer Access-Anwendung ein Makro mit dem Namen ,,Autoexec” erstellt worden sein, wird dieses Makro beim Start der Anwendung (beim Öffnen dieser Datenbank unter Access) automatisch ausgeführt. Diese Eigenschaft wurde in ARTA benutzt, um beim Start interne Variablen (Objekte) zu initialisieren. Im Makro ,,Autoexec” wird die Prozedur ,,Public Function initSystem()” 6.5. BEFEHLSAUSFÜHRUNGSSYSTEM 77 ausgeführt. In der Prozedur ,,initSystem()” werden o.g. Objekte (Variablen bzw. Auflistungen) deklariert und bereit gestellt. 6.5 Befehlsausführungssystem Der Einsatz der ,,Rekursion” ist nach der in Abschnitt 5.3 beschriebenen Triggerbearbeitungssemantik unvermeidbar. Nach dem Aufruf einer rekursiven Prozedur ruft sie sich wieder auf und stellt der neuen Instanz die neuen Ergebnisse zur Verfügung. Zusätzlich soll ein bestimmtes Ergebnis existieren, nach dem die Prozedur sich nicht mehr aufrufen soll. Durch dieses Ergebnis wird das Terminieren der Prozedur garantiert. Die Rekursion für die Triggerbearbeitung findet in der Prozedur ,,ExecuteSQL Statement” statt. Im folgenden wird eine stark vereinfachte Version dieser Prozedur angezeigt. Viele Zwischenschritte und Anweisungen wurden nicht angezeigt, damit der Aspekt der Rekursion besser sichtbar wird. 01)Public Sub ExecuteSQL_Statement(ByVal strAction As String) 02) 03) \\Fuer jeden Trigger von Beforetriggern 04) For Each Tr In beforeTrs 05) Tr.Condition = True => Call ExecuteAction(T.Action) 06) Next Tr 07) 08) \\Die Befortrigger sind bis hierhin alle abgearbeitet, 09) \\und die "eigentliche Ausfuehrung des SQL"-Ausdruckes 10) \\findet hier statt. 11) Call ExecuteAction(strAction) 12) 13) \\Fuer jeden Trigger in Aftertriggern 14) For Each Tr In afterTrs 15) If Tr.Condition = True Then 16) Call ExecuteSQL_Statement(T.Action) 17) End If 18) Next Tr 19)End Sub Die Beforetrigger werden zwischen den Zeilen ,,03)” und ,,06)” abgearbeitet. Für jeden Beforetrigger gilt: Ist dessen Bedingung erfüllt, wird die Funktion ,,ExecuteAction(Triggeraktion)” aufgerufen. An der Zeile ,,11)” wird der SQLAusdruck aufgerufen. Die Aktion jedes Aftertriggers, dessen Bedingung erfüllt 78 KAPITEL 6. IMPLEMENTIERUNG ist, wird als Eingabeparameter für den Aufruf der selben Prozedur in der Zeile ,,16)” benutzt und damit ausgeführt. Zu jeden Aftertrigger wird die Prozedur also nochmal aufgerufen. Jede aufgerufene Prozedur muss warten, bis alle Unterprozeduren mit der Bearbeitung fertig sind. Die Terminierung dieser Prozedur ist aus der Sicht der Rekursion garantiert, d.h. die Bedingung für die Beendigung der Rekursion sind in der ,,For Each”Anweisung (Zeile ,,14)”) gegeben. Wenn alle Aftertrigger abgearbeitet sind, ist die Rekursion zu Ende. Die Tiefe der Rekursion ist jedoch unbekannt. Die Terminierung der Prozedur ist allerdings aus Sicht der Triggerbearbeitung nicht garantiert. Wenn z.B. der SQL-Ausdruck S Aftertrigger {T1 , T2 , . . . , Tn } feuert und der Aktionsteil des Triggers Ti ∈{T1 , T2 , . . . , Tn } derselbe SQL-Ausdruck S ist, kommt eine endlose Schleife zustande.4 Die endgültige Ausführung eines Befehls findet in der Funktion ,,ExecuteAction” statt. Diese Funktion sieht wie folgt aus: Private Sub ExecuteAction(ByVal strCommand As String) If strCommand = "" Then LogFileOutput strCommand & "Keine Aktion!!!!!!!" ElseIf isSQL(strCommand) Then ’ SQL-Ausdruck CurrentDb.Execute strCommand Else Eval (strCommand) End If End Sub Mit dem Befehl ,,CurrentDb.Execute” können nur SQL-Ausdrücke ausgeführt werden. Deswegen wird die Ausführung von ,,nicht SQL-Ausdrücken”, z.B. der Aufruf von Funktionen, dem VBA-Befehl ,,Eval” überlassen. Ein Trigger mit leerem Aktionsteil wird zwar jedes Mal beim Eintritt seines Ereignisses gefeuert, führt jedoch keine Aktionen aus und verursacht deswegen keine Laufzeitfehler. Die rekursive Funktion ,,ExecuteSQL Statement” wird aus der Prozedur ,,doAction(str)” gestartet. Im folgenden wird ,,doAction(str)” angezeigt. In dieser Prozedur beginnt erst durch ,,wks.BeginTrans” eine Transaktion und danach 4 Vgl. [?], S51-67 6.6. FEHLERBEHANDLUNG 79 wird die rekursive Funktion ,,ExecuteSQL Statement” aufgerufen. Ein Fehler, der in ,,ExecuteSQL Statement” bzw. ,,ExecuteAction” eintritt, wird an ,,doAction” weitergeleitet und die Transaktion wird hier mit ,,wks.Rollback” zurückgerollt. Tritt kein Fehler ein, so werden die Änderungen mit ,,wks.CommitTrans” festgeschrieben. Public Sub doAction(ByVal str As String) Dim wks As DAO.Workspace Set wks = DBEngine.Workspaces(0) wks.BeginTrans On Error GoTo Err_doAction Dim clsTExecute As New USys_clsExecute clsTExecute.ExecuteSQL_Statement (str) wks.CommitTrans Exit_doAction: Exit Sub Err_doAction: wks.Rollback Resume Exit_doAction End Sub Die Prozedur ,,doAction(str)” wird an folgenden Stellen aufgerufen: 1. doAction(SQL S) wird im ARTA-Hauptfenster aufgerufen, wenn ein SQLAusdruck S an der Datenbank anzuwenden ist. 2. Beim Eintritt eines Zeitereignisses werden alle Trigger separat nacheinander in einer Schleife überprüft. Ist die Bedingung eines Triggers erfüllt, so wird die Prozedur doAction(Triggeraktion) aufgerufen. Die Auswertung von Bedingungen und die Parameterübergabe an Triggeraktionen werden extra in Unterabschnitt 6.7.1 beschrieben. 6.6 Fehlerbehandlung In der ARTA-Triggerbearbeitung gibt es keine Kompilierungsfehler. Bei ARTA wurde versucht, die Syntax eines Triggers und des dazugehörigen Ereignismu- 80 KAPITEL 6. IMPLEMENTIERUNG sters bei der Definition zu überprüfen. Am Ende dürfen nur die Trigger aktiviert sein und später gefeuert werden, die syntaktisch korrekt definiert sind. Die Sytaxprüfung wird in dem Fenster durchgeführt, in dem der Trigger und das Ereignismuster verwaltet werden. Deswegen ist eine korrekte Syntax jedes Triggers garantiert, bevor er zum Einsatz kommt. Logische Fehler sind in der ARTA-Triggerbearbeitung wie bei jeder Programmiersprache nicht ohne weiteres zu finden. Sie haben negative Auswirkungen auf das Endergebnis. In ARTA ist kein Debugger eingebaut, da der Bau eines Debuggers sehr aufwendig ist und über den Umfang dieser Arbeit hinausgeht. Deshalb sollten Trigger sehr aufmerksam definiert bzw. geändert werden. Ein Laufzeitfehler kann bei der ARTA-Triggerbearbeitung in folgenden Situationen vorkommen: • Auswertungsfehler können bei der Auswertung einer Triggerbedingung eintreten. • Befehlsausführungsfehler treten bei der Ausführung eines Befehls auf. Diese Fehler sind selbst in zwei Untergruppen zu teilen: – SQL-Eingabefehler tritt bei der Ausführung der SQL-Eingabe in der Anfangsphase im ARTA-Hauptfenster auf. – Innen-Befehlsfehler haben sich in den Aktionsteil eines Triggers eingeschlichen und treten erst dann auf, wenn der Trigger gefeuert wird und die Auswertung seiner Bedingung ”True” zurück gibt und seine Aktion ausgeführt wird. Auswertungsfehler werden so behandelt, als ob die Auswertung einer Triggerbedingung ”False” zurückgibt. Eine falsche (unsinnige) Bedingung für einen Trigger bewirkt, dass der Aktionsteil dieses Triggers nie ausgeführt werden kann. Deswegen ist es sehr wichtig Triggerbedingungen mit großer Aufmerksamkeit zu formulieren. Eine leere Trigger-Bedingung ist eine sinnvolle Bedingung und wird immer als True interpretiert. Die SQL-Eingabefehler ist am einfachsten zu behandeln. Solche Fehler werden sofort nach dem Maus-Klick auf den entsprechenden Button im ARTAHauptfenster erkannt. Es wird direkt eine Access-Fehlermeldung ausgegeben, die dem Benutzer auf den Fehler hinweist. Dabei ist zu beachten, dass im ARTAHaptfenster nur die SQL-Befehle der Form DELETE . . . , INSERT . . . und UPDATE . . . erlaubt sind. 6.7. BESONDERE ASPEKTE 81 Innen-Befehlsfehler treten erst im Laufe einer Ausführung ein. Diese Fehler werden durch die Ausführung einer Triggeraktion verursacht. beim Eintreten eines Innen-Befehlsfehlers wird die SQL-Bearbeitung abgebrochen und alle Datenbankänderungen werden zurückgesetzt. Ein SQl-Befehl kann eine Kette von voneinander abhängigen Datenbankänderungen zur Folge haben. Alle Datenbankänderungen dieser Kette müssen vorgenommen werden, damit das Ziel der Definitionen der daran beteiligten Trigger erreicht wird. Datenbankänderungen, die durch einen SQL-Befehl vorgenommen werden, werden in einer Transaktion zusammengefasst. Dadurch werden alle Änderungen als eine Einheit (alle oder keine) durchgeführt. Die InnenBefehlsfehler können auch aufgrund von Integritätsverletzungen auftreten. Die referentielle Integrität kann in Access durch Beziehungen und deren Eigenschaften definiert werden. Um solche Fehler zu vermeiden, sollten Integritäten und Integritätsprüfungen nicht durch Access-Beziehungen sondern durch ARTATrigger definiert werden. Im Code der Prozedur ,,doAction” (Im Abschnitt: 6.5) wurden VBA-Methoden der Transaktionsverwaltung und der Fehlerbehandlung genutzt und miteinander kombiniert. Durch diese Kombination wird der erste Innen-Befehlsfehler gefangen und an die Funktion ,,doAction” zurückgegeben. 6.7 6.7.1 Besondere Aspekte Trigger Execution Context Bei einem Trigger, der durch ein Änderungsereignis gefeuert wird, können Parameter für die Inhalte der betroffenen Tabelle bzw. der betroffenen Zeilen der Tabelle definiert werden. Diese Parameter können im Bedingungs- sowie im Aktionsteil des Triggers vorkommen. Dazu müssen die Änderungen des SQLAusdrucks, durch dessen Ausführung ein Änderungsereignis eintritt und die Trigger gefeuert werden, vor der eigentlichen Ausführung bestimmt werden. Um diese Änderungen vorher zu bestimmen, müssen zwei temporäre Tabellen erzeugt werden. Die erste temporäre Tabelle enthält die betroffenen Datensätze vor der SQL-Ausführung und die andere temporäre Tabelle die betroffene Datensätze nach der SQL-Ausführung auf der Originaltabelle. Die temporären Tabellen werden intern erzeugt und nach einer Triggerbearbeitungssitzung automatisch gelöscht. Es ist lediglich ein Lesezugriff auf Daten der temporären Tabellen erlaubt. Vor einer Bedingungsauswertung, sowie bei der Bereitstellung 82 KAPITEL 6. IMPLEMENTIERUNG eines Aktionsteils sollten die temporären Tabelle vorhanden sein. Bei einem Statement-Level-Trigger ist ein Datenzugriff auf die gesamten Informationen dieser Tabellen relevant und bei einem Row-Level Trigger sind die einzelnen Datensätze interessant. Um einzelne Datensätze zu lesen, müssen diese Tabellen durchlaufen werden. Die Klasse ,,USys clsTriggerExecutionContext” (Klassenmodul) wurde für die Erledigung der o.g. Aufgaben vorgesehen. Ein Objekt dieser Klasse wird bei jedem Aufruf der Funktion ,,ExecuteSQL Statement (strAction)” erzeugt, falls ,,strAction” ein SQL-Ausdruck ist und Trigger feuert. Ein Objekt dieser Klasse wird wie folgt erzeugt: Dim tec As New USys clsTriggerExecutionContext. Nach Erzeugung dieses Objektes stehen seine Methoden zur Verfügung. Mit tec.initTEC (strAction) werden die zwei temporäre Tabellen auf das aktuelle Verzeichnis (auf dem Verzeichnis, in dem die Datenbank gespeichert ist) gespeichert. Die temporären Tabellen haben genau die gleiche Struktur wie die Originaltabelle. Um diese Tabelle herzustellen, wird auch die Technik von ,,Tabellenerstellungsabfragen” eingesetzt. Eine Tabellenerstellungsabfrage hat die Form ,,SELECT <feldliste> INTO neueTabellenname FROM alteTabellenname WHERE bedingung;”. Die Herstellung von temporären Tabellen wird wie folgt durchgeführt: • bei INSERT: 1. newTmp := Orgtable, dabei wird der Befehl ,,DoCmd.CopyObject” eingesetzt. 2. Löschen aller Datensätze aus newTmp mit dem Befehl ,,CurrentDb.Execute ’DELETE * From ’ & newTmp” 3. Iem Original-SQL wird der Tabellenname ,,Orgtable” durch ,,newTmp” ersetzt. Damit entsteht ein neuer SQL-Ausdruck, ,,sqltmp”. 4. ,,sqltmp” ausführen. Damit besteht ,,newTmp” aus den neuen Datensätzen, die mit der Ausführung des SQL-Ausdrucks zur ,,Orgtable” hinzugefügt werden. Bei INSERT hat die ,,oldTmp” keine Daten. • Bei DELETE soll der Befehl ,,CurrentDb.Execute ’SELECT * into ’ & oldTmp & ’ From ’ & Orgtable & ’ ’ & getWhere” ausgeführt werden, wobei der WHERE-Teil mit Hilfe von Prozeduren der Klasse ,,USys clsparser” aus dem Original-SQL berechnet wird. Durch die Ausführung dieses Befehls wird eine Tabelle Namens ,,oldTmp” erstellt, die genau diejenigen 6.7. BESONDERE ASPEKTE 83 Datensätze besitzt, die aus der Originaltabelle gelöscht werden sollen. Bei ,,DELETE” ist die temporäre Tabelle ,,newTmp” leer. • Bei UPDATE: 1. oldTmp := Orgtable mit dem Befehl ,,DoCmd.CopyObject”. 2. in oldTmp werden alle nicht betroffenen Datensätze gelöscht, damit in dieser Tabelle nur die betroffenen Datensätze im alten Zustand übrig bleiben. Dabei werden die Methoden der Klasse ,,USys clsparser” benutzt, um den WHERE-Teil vom Original-SQL zu berechnen und zu negieren. 3. newTmp := oldTmp mit dem Befehl ,,DoCmd.CopyObject”. Nun sind beide temporären Tabellen identisch. 4. Im Original-SQL wird der Tabellenname ,,Orgtable” durch ,,newTmp” ersetzt. Damit entsteht ein neuer SQL-Ausdruck, ,,sqltmp”. 5. ,,sqltmp” ausführen. Die temporären Tabellen werden unter zufällig erzeugten Namen gespeichert. Der Name von oldtmp hat die Vorsilbe ,,USys ARTA OLD ” und der Name von newtmp hat die Vorsilbe ,,USys ARTA NEW ”. Nach der Vorsilbe kommt eine zufällige Zeichenkette, bestehend aus zehn Buchstaben gefolgt von einer zufälligen (achtstelligen) Zahl. Dabei werden die VBA-Befehle ,,Randomize” und ,,Rnd” eingesetzt. Diese Namen werden nach Beendigung einer Triggerbearbeitungssitzung in der Funktion ,,doAction” gelöscht. Zur Sicherheit wird beim Löschen überprüft, ob der Name der o.g. Form entspricht, damit keine wichtige Tabellen gelöscht werden. Durchlaufen der Transitionen In einem Objekt der Klasse ,,USys clsTriggerExecutionContext” sind zwei interne Variablen zuständig für die Bereitstellung der betroffenen Datensätze für den Einsatz in den Triggerteilen. Diese Variablen sind vom Typ ,,Recordset” der Schnittstelle ,,DAO” und werden wie folgt definiert: Private rstOld As DAO.Recordset Private rstNew As DAO.Recordset Nachdem die temporären Tabellen hergestellt sind, werden die betroffenen Datensätze werden in diese Variablen wie folgt übernommen: Set rstNew = CurrentDb().OpenRecordset(USys ARTA NEW . . . ) Set rstOld = CurrentDb().OpenRecordset(USys ARTA OLD . . . ) Mit Hilfe von Methoden, die in dieser Klasse programmiert sind, wie ,,MoveNextRow”, ,,MovePrevRow”, ,,MoveFirstRow”, ,,MoveLastRow” werden die 84 KAPITEL 6. IMPLEMENTIERUNG Recordset-Variablen durchlaufen. Mit Hilfe der Methode ,,makeAction” werden alle Parameter im Aktionsteil eines Triggers durch die Inhalte von temporären Tabellen ersetzt. Dazu werden Methoden der Klasse ,,USys clsparser” sowie ,,USys clsTriggerExecutionContext” benutzt. Eine weitere wichtige Methode dieser Klasse ist ,,ConditionValue”. Diese Methode gibt ja/nein zurück, d.h. dass die Auswertung der Triggerbedingung durch diese Methode erledigt wird. In dieser Methode werden Parameter der temporären Tabellen berechnet und in der Triggerbedingung eingesetzt. Nach dem Einsetzen der Inhalte von Parameter wird das Ergebnis mit Hilfe des Befehls ,,ConditionValue=CBool(Eval(CStr(strconditionText)))” ausgewertet, wobei der Triggerbedingungstext nach dem Parametereinsatz ,,strconditionText” ist. 6.7.2 Timer Timer wurden in ARTA mit Hilfe von API-Schnittstelle realisiert. Dabei wurden zwei Prozeduren aus der DLL-Datei ,,User32.dll” eingesetzt. Zunächst müssen diese Prozeduren wie folgt deklariert werden: Private Declare Function SetTimer _ Lib "User32" _ ( _ ByVal Hwnd As Long, _ ByVal nIDEvent As Long, _ ByVal uElapse As Long, _ ByVal lpTimerFunc As Long _ ) _ As Long Private Declare Function KillTimer _ Lib "User32" _ ( _ ByVal Hwnd As Long, _ ByVal nIDEvent As Long _ ) _ As Long Mit dem Aufruf der Prozedur ,,SetTimer” wird ein Timer gesetzt, der alle ,,uElapse”-Millisekunden ,,periodisch” eine Rückruf-Funktion der Adresse ,,lp- 6.7. BESONDERE ASPEKTE 85 TimerFunc” aufruft. Durch den Aufruf der Prozedur ,,KillTimer” wird der Timer mit der Idenditätsnummer ,,nIDEvent” abgebrochen. Der Einsatz dieser Prozeduren in ARTA ist wie folgt: Zur Realisierung der Timerverwaltung, deren Semantik im Bild 5.4 geschildert wurde, sollen zwei Rückruf-Funktionen vorgesehen werden, ,,TimerProc” und ,,settingTimer”. Der Aufruf von ,,TimerProc” bedeutet, dass ein Zeitereignis aufgetreten ist. Aufruf von ,,settingTimer” bedeutet, dass ein Timer ggf. entweder für ein Datum innerhalb einer Woche oder für ,,einen Tag” gesetzt werden soll. Diese Prozeduren sind wie folgt aufgebaut: Public Function TimerProc(Hwnd, uMsg, idEvent, dwTime) As Long 0)Aktuellen Timer abbrechen Dim lRet As Long lRet = KillTimer(0&, TimerID) 1)Trigger der Zeitereignisses werden bearbeiten 2)Die Variable mit dem Zeitereignismuster modifizieren 3)Die in der Vergangenheit aufgetretenen Zeitereignisse bearbeiten 4)"settingTimer 0, 0, 0, 0" aufrufen End If End Function Public Function settingTimer(Hwnd, uMsg, idEvent, dwTime) 0)Aktuellen Timer abbrechen Dim lRet As Long lRet = KillTimer(0&, TimerID) 1)If Ein Zeitereignis innerhalb einer Woche erwartet TimerID = _ SetTimer(0&, 0&, DauerbisZeitereignis, AddressOf TimerProc) Else TimerID = _ SetTimer(0&, 0&, DauereinesTages, AddressOf settingTimer) End If End Function Diese Prozeduren müssen genau die gezeigte Gestaltung besitzen, damit sie als Rückruf-Funktion von ,,SetTimer” akzeptiert werden. Für die Eingabeparameter von ,,TimerProc” und ,,settingTimer” werden hier immer ,,0” eingesetzt. Diese Parameter ermöglichen in ,,Visual Basic” (VB) mehr Funktionalitäten, z.B. die Verbindung von Timern mit Forms. 86 KAPITEL 6. IMPLEMENTIERUNG 6.7.3 Behandlung vergangener Zeitereignisse Zeitereignisse, die in der Vergangenheit aufgetreten sind sollen berücksichtigt und deren Trigger bearbeitet werden, falls deren DeadLines noch nicht um sind. Durch die Berücksichtigung der Deadlines wird festgestellt, ob Trigger eines Zeitereignisses gefeuert werden dürfen oder nicht. Jedes mal wenn eine zeitlang kein Timer gesetzt war, sollen solche Zeitereignisse in der Vergangenheit gesucht werden. Solche Ereignisse sind zunächst in der Systempause zu suchen, wenn das Accessprogramm oder der Rechner eine zeitlang abgeschaltet war. Diese Zeitereignisse können auch nach der Bearbeitung von Triggern, die durch ein Zeitereignis gefeuert wurden, gefunden werden. Nachdem ein Zeitereignis aufgetreten ist, wird der Timer abgeschaltet und gefeuerte Trigger werden bearbeitet. Die Deadline darf nicht kürzer als 5 Minuten sein. Diese Grenze kann höher angesetzt werden, falls die Dauer von Triggerbearbeitung in einer ARTA-Datenbank sehr groß wird oder falls Trigger eines Zeitereignisses auf jeden Fall/unbedingt bearbeitet werden müssen. Die Bearbeitung solcher Trigger wird mit Hilfe von Funktion ,,processOldTimeEvents” durchgeführt. Annahme: ,,allTimeEvents” ist die Auflistung vom Typ (der Klasse) ,,USys clsSetOfTimeEvents”, die alle Zeitereignismuster beinhaltet. Durch diese Funktion wird folgender Algorithmus realisiert: 1. Begin der Bearbeitung von ,,processOldTimeEvents” 2. In dieser Funktion wird das erste Element der Auflistung ,,allTimeEvents” (die Agenda) von Zeitereignismustern untersucht. Tritt das Zeitereignis in der Zukunft auf, gehe zu (7). 3. Liegt sein Erwartungszeit in der Vergangenheit, so werden seine Trigger bearbeitet. 4. Ausführung des Befehls ,,Call allTimeEvents.addEvents (allTimeEvents.Item.ModifyEventsStaten)”. Das erledigt zwei Aufgaben: (a) Durch ,,allTimeEvents.Item.ModifyEventsStaten” wird das erste Objekt der Auflistung durchsucht und modifiziert. D.h. alle absoluten Zeitereignismuster in diesem Objekt werden als erledigt eingestuft und gekennzeichnet und bei jedem periodischen Zeitereignismuster wird der nächste Termin berechnet. Zusätzlich werden die periodischen Zeitereignismuster mit neuen Terminen zurückgegeben. (b) Die periodischen Zeitereignismuster mit neuen Terminen werden durch den Befehl ,,allTimeEvents.addEvents(. . . )” wieder in die Auflistung aufgenommen. 6.7. BESONDERE ASPEKTE 87 5. Nun wird das modifizierte Objekt aus der Auflistung entfernt: allTimeEvents.Remove 6. Gehe zu der Zeile (1). 7. Ende der Bearbeitung von ,,processOldTimeEvents” ,,processOldTimeEvents” ist eine rekursive Prozedur und endet mit der Erfüllung der Bedingung der Zeile (2). 88 KAPITEL 6. IMPLEMENTIERUNG Kapitel 7 Zusammenfassung und Ausblick In der vorliegenden Arbeit wurde ARTA als eine Erweiterung des Access-Datenbankmanagementsystems um die Triggerfunktionalität implementiert. In ARTA werden Formulare als Benutzeroberflächen eingesetzt, um Benutzern die Verwaltung von Triggern und Ereignismustern zu ermöglichen. Ereignisse, die in ARTA spezifiziert und wahrgenommen werden sind ,,atomar” bzw. primitiv. Dabei können Ereignisse entweder vom Typ ,,Änderungsereignisse” oder vom Typ ,,Zeitereignisse” sein. Die Triggersprache in ARTA basiert insoweit auf der Triggersprache von SQL3, dass sie ,,fast” die gleiche Syntax und Semantik in Bezug auf Änderungsereignisse besitzen. Bezüglich Zeitereignissen sind sie dagegen völlig unterschiedlich, da in SQL3 im Gegensatz zu ARTA Zeitereignisse nicht unterstützt werden. Um Zeitereignisse zu realisieren, wurden in ARTA Timer durch Einsatz geeigneter Funktionen von API-Schnittstellen implementiert. Um Änderungsereignisse erkennen und deren Trigger bearbeiten zu können, sollen alle Datenbankänderungen durch SQL-Befehle und unter dem direkten Einsatz von ARTA vorgenommen werden. Zeitereigniserkennungsgranularität Wenn Access eine Transaktion von Datenbankänderungen durchführt und gleichzeitig die Rückruffunktion des Timers aufgerufen wird (Eintreten eines Zeitereignisses), muss das Zeitereignis eigentlich als nicht geschehen betrachtet werden. Trigger eines solchen Zeitereignisses können nicht direkt bei Ereigniseintritt bearbeitet werden, da in Access eine Transaktion läuft, die durch Ausführung einer Prozedur begonnen wurde. Diese beiden Vorgänge können in Access nicht gleichzeitig bearbeitet werden können. Um dieses Problem zu lösen habe ich in der vorliegenden Arbeit eine Deadline für Zeitereignisse eingeführt. Bei der Eingabe der Deadline kann eine beliebige Granularität für das Erkennen des jeweiligen Zeitereignisses eingeplant werden. Dadurch werden Trigger eines Zeitereignisses gefeuert, obwohl das Zeitereignis nicht direkt an dem definierten 89 90 KAPITEL 7. ZUSAMMENFASSUNG UND AUSBLICK Zeitpunkt sonder Später wahrgenommen wird. Erweiterungs- bzw. Verbesserungsvorschläge Ohne auf den Source Code von Access und DB Engine zugreifen zu müssen, lässt sich ARTA in folgenden Bereichen erweitern: Zusammengesetzte Ereignisse: ARTA unterstützt nur primitive Ereignisse. Solche Ereignisse werden durch Ereignismuster beschrieben, denen bei der Definition eindeutige Namen zugeordnet werden. Diese Benennung von Ereignismustern kann als eine Voraussetzung (eine Basis) für eine Erweiterung von ARTA um zusammengesetzte Ereignisse dienen, indem das Eintreten jedes (interessanten) Ereignisses in einer Ereignishistorie protokolliert wird. Um ein zusammengesetztes Ereignismuster zu erkennen, können die eingetretenen (und protokollierten) Ereignisse aus der Historie abgelesen und mit dem Ereignismuster verglichen werden. Damit werden interessante Datanbanksituationen beschreibbar, z.B. ,,At Zeitereignis AND On Änderungsereignis”, ,,Beim vierten Eintreten von E”. Es stellt sich hier die Frage, ob alle auftretenden Ereignisse protokolliert werden müssen: Wenn alle Ereignisse protokolliert werden, erhält man möglicherweise eine riesige Datenmenge, von der nur ein relativ kleiner Anteil im weiteren gebraucht wird. Um diese großen Mengen überflüssiger Daten zu vermeiden wäre die Realisierung eines intelligenten Systems nötig, das interessante Ereignisse bei der Triggerdefinition herausfiltert. Ein solches System könnte z.B. bei der Definition von zusammengesetzten Ereignismustern nur ,,die beteiligten primitiven Ereignisse” als interessante Ereignisse einstufen, die dann in der Folge protokolliert werden. Relative Zeitereignisse Die Realisierung von relativen Zeitereignissen ist eine weitere Erweiterungsmöglichkeit. Dadurch werden Zeitereignisse erkannt, die eine bestimmte Zeitdauer vor oder nach periodischen bzw. absoluten Zeitereignissen eintreten: z.B. ,,der letzte Freitag vor dem 15.10.2002” oder ,,6 Monate nach der Anmeldung eines Diplomarbeitsthemas”. Verbindung mit anderen Datenbanken Durch die Benutzung von Schnittstellen wie ODBC bzw. JDBC und AccessADO, kann Access mit anderen Datenbanksystemen verbunden werden. In ARTA 91 wurde die Anfragesprache SQL eingesetzt, um automatische Datenbankänderungen mit Hilfe von VBA-Befehlen (z.B. database.Execute(SQL)) durchzuführen. Da SQL in vielen Datenbankmanagementsystemen (Oracle, Sybase, DB2, . . . ) eingesetzt wird, bietet sich eine ARTA-Erweiterung an, um auch in diesen anderen Datenbanken die ARTA-spezifische Triggerfunktionalität anzuwenden. Um eine Kommunikation zwischen Benutzern und Datenbanken in anderen Systemen zu ermöglichen, wird Access als Benutzteroberfläche benutzt. Dabei sollen entschieden werden, ob z.B. Informationen von Triggern in ARTA (Access) bleiben oder in der jeweiligen Datenbank eingesetzt werden sollen. Echtzeit-Datenbanksystem Zeitereignisse werden in ARTA mit einer Deadline versehen. Die Deadline eines Zeitereignisses kontrolliert, dass ein Zeitereignis Trigger nur innerhalb eines Zeitintervals feuern darf bzw. kann. Das Zeitinterval ist [t, t+Deadline], wobei ab dem Zeitpunkt t das Ereignis erwartet wird. Wie lange die Bearbeitung solcher Trigger dauert, ist dabei nicht relevant. Eine mögliche Verbesserungs wäre, auch eine Zeitgrenze für das Bearbeitungsende von Triggern vorzusehen, die durch ein Zeitereignis (oder gar Änderungsereignis) gefeuert werden. Dadurch sollte ein Zeitereignis bzw. Änderungsereignis innerhalb eines Zeitintervales Trigger feuern dürfen und die Bearbeitung von solchen Triggern sollte innerhalb einer Zeitdauer fertig sein. Entwurf eines besseren Parsers Hat sich ein Fehler bei der Formulierung eines SQL-Ausdrucks eingeschlichen, so wird erst bei der Ausführung dieses SQL-Ausdrucks (z.B. durch VBA-Befehl ,,database.Execute(SQL)” oder durch Aktualisierungsabfragen) eine detaillierte Fehlermeldung ausgegeben. Daraus kann erkannt werden, dass in Access ein interner SQL-Parser installiert ist. Leider ist der Access-Parser weder mit Hilfe von Access- noch VBA-Befehlen vollständig benutzbar. So kann z.B. der Name der betroffenen Tabelle im SQL-Ausdruck mit Hilfe von Access- bzw. VBABefehlen nicht gefunden werden. In ARTA sollten SQL-Ausdrücke bis ins Detail analysiert werden, um damit insbesondere die Transitionstabellen herzustellen (Parser-Problem). In der Analyse sollten z.B. der Tabellenname, die betroffenen Spaltennamen und der WHERE-Teil eines SQL-Ausdrucks bestimmt werden. Um eine solche Analyse zu gewährleisten wurde in ARTA ein Parser realisiert, der nicht jeden möglichen SQL-Ausdruck analysieren (parsern) kann. Er beschränkt sich dagegen auf die wesentlichen SQL-Ausdrücke zur Erstellung von Transitionstabellen. Die Realisierung eines Parsers, der SQL-Ausdrücke in vollem Umfang analysieren kann wäre eine deutliche Verbesserung. Der Parser könnte direkt in der Programmier- 92 KAPITEL 7. ZUSAMMENFASSUNG UND AUSBLICK sprache VBA realisiert werden. Man könnte auch einen Parser in einer anderen Programmiersprache (z.B. c++ oder Java) programmieren und ihn dann in einem VBA-Code ausführen lassen. Literaturverzeichnis [ISHaRa] Theo Härder, Erhard Rahm: Datenbanksysteme, Konzepte und Techniken der Implementierung, ISBN 3-540-65040-7, Springer, 1999. [ISVos] Gottfried Vossen: Datenbankmodelle, Datenbanksprachen und Datenbankmanagementsystemen, ISBN 3-486-25339-5, Oldenbourg Wissenschaftsverlag GmbH, 2000. [ISKeEi] Alfons Kemper, André Eickler: Datenbanksysteme, Einführung, R. Oldenbourg Verlag München, Wien, 1999. [ISVrl] Prof. Dr. Rainer Manthey: Vorlesungsskript zur Vorlesung ,,Informationssysteme”, Universität Bonn, Wintersemester 1999/2000. [AKHaWi] Eric N. Hanson, Jennifer Widom: An overview of production rules in database systems, 121-143, The Knowledge Engineering Review, Vol 8:2 1993 Cambridge University Press. [AKWiCe] Jennifer Widom, Stefano Ceri: Introduction to Active Database Systems, Morgan Kaufmann Publishers, 1996. [AKPaDi] Norman W. Paton, Oscar Dı́az: Active Database Systems ACM Computing Surveys, Volume 31, Number 1, March 1999, 63-103. [AKPat10] Krishna Kulkarni, Nelson Mattos, Roberta Cochrane: Active Database Features in SQL3, Chapter 10 in (Norman W. Paton: Active Rules in Database Systems, ISBN 0-387-98529-8, Springer-Verlag, Berlin Heidelberg, 1999). [AKPat12] Stella Gatziu, Klaus R. Dittrich: SAMOS, Chapter 12 in (Norman W. Paton: Active Rules in Database Systems, ISBN 0-387-985298, Springer-Verlag, Berlin Heidelberg, 1999). 93 Eine 94 LITERATURVERZEICHNIS [AKPat21] Jörgen Hansson, Mikael Berndtssen: Active Real-Time Database Systems, Chapter 21 in (Norman W. Paton: Active Rules in Database Systems, ISBN 0-387-98529-8, Springer-Verlag, Berlin Heidelberg, 1999). [AKDiGa] Klaus R. Dittrich, Stella Gatziu: Aktive Datenbanksysteme, Konzepte und Mechanismen, ISBN 3-932588-19-3, dpunkt-Verlag, 2000. [DKCrGH] Armin B. Cremers, Ulrike Griefahn, Ralf Hinze: Deduktive Datenbanken. Eine Einführung aus der Sicht der logischen Programmierung, ISBN 3-528-04700-3, Vieweg & Sohn Verlagsgesellschaft mbH, Braunschweig/Wiesbaden, 1994. [AKVrl] Prof. Dr. Rainer Manthey: Vorlesungsskript zur Vorlesung ,,Aktive Datenbanken und Ereignisorientierte Programmierung”, Universität Bonn, Wintersemester 2000/2001. [DKVrl] Prof. Dr. Rainer Manthey: Vorlesungsskript zur Vorlesung ,,Deduktive Datenbanken”, Universität Bonn, Wintersemester 2000/2001. [VB6Ko] Michael Kofler: Visual Basic, Programmiertechniken, Datenbanken, Internet, ISBN 3-8273-1428-3, Addison Wesley Longman Verlag GmbH, 1999. [VBAHGG] Ken Getz, Mike Gilbert: Visual Basic Language Developers Handbook, chapter 3 & chapter 6 SYBEX, Alameda 2000. [APIHnd] Götz Reinecke: Das API Handbuch, Ein Artikel über die Verwendung des APIs unter Visual Basic, Version 1.02, http://www.activevb.de, 7/2001. [APIApp] Dan Appelman: Dan Appelmans Win32 Puzzle-Buch, Puzzles und Tutorials für Visual Basic-Profis, ISBN 3-934358-21-7, Galileo Press GmbH, Bonn, 2000. [A00AN] Ralf Albrecht, Natascha Nicol: Access 2000 Programmieren, Professionelle Anwendungsentwicklung mit Access und VBA, ISBN 3-8273-1547-6, Addison-Wesley, 2000. [A00BK] Wayne F. Brooks, Lars Klander: Professionelle Access 2000 Programmierung, ISBN 3-8266-0577-2, MITP-Verlag GmbH, Bonn, 1999. LITERATURVERZEICHNIS [A97AN] 95 Ralf Albrecht, Natascha Nicol: Microsoft Access 97, Das Handbuch, Das ganze Softwarewissen, ISBN 3-86063-135-7, Microsoft Press Deutschland, 1998. [A0SQLBB] Irene Brauer, Jürgen Bär: Access 2000 und MS SQL Server im Teamwork, ISBN 3-446-21473-9, Carl Hanser Verlag, München Wien, 2000. [ASQLMH] Norbert Michelmann, Rolph Hettwer, Mitarbeit: Hans-Dieter Schmidt: Datenbankentwicklung und -anpassung mit MS Access und SQL, ISBN 3-8237-6802-6, Verlag H. Stam GmbH, Köln, 2001. [dplLein] Hans-Hermann Leinen: Ein Präprozessor zur Implementierung einer temporalen Erweiterung von SQL, Diplomarbeit an der Universität Bonn, Institut für Informatik III, August 2001. [dplLoeh] Torsten Löhr: Eine Erweiterung der aktiven Regelsprache Phoenix um temporale Ereignisse, Deadlines und Alternativen, Diplomarbeit an der Universität Bonn, Institut für Informatik III, Mai 1997. [dplModi] Jürgen Modic: Eine Erweiterung des Triggerkonzeptes von Phoenix um temporale Ereignisse, Diplomarbeit an der Universität Bonn, Institut für Informatik III, August 2001. [dplMuen] Sascha Münch: ARA, Eine Erweiterung des relationalen Datenbanksystems Access um Trigger, Diplomarbeit an der Universität Bonn, Institut für Informatik III, Oktober 2001. [AutoHoUl] John E. Hopcroft, Jeffrey D. Ullmann: Einführung in die Automatentheorie, formale Sprachen und Komplexitätstheorie, ISBN 3-89319-744-3, Addison-Wesley, 1994. [SQLVrl] Prof. Dr. Rainer Manthey: Vorlesungsskript zur Vorlesung ,,Relationale Datenbanken”, Universität Bonn, Sommersemester 2000. [SQLMeSi] Jim Melton, Alan R. Simon: SQL:1999 Understanding Relational Language Components, Chapter 11, Morgan Kaufmann Publishers. [SQLTrKo] Robert Cochrane, Hamid Pirahesh, Nelson Mattos: Integrating Triggers and Declarative Constraints in SQL Database Systems, Site 567-578, IBM Almaden Research Center, SanJose, CA, www.vldb.org/conf/1996/P567.PDF. 96 [SQLStnd] LITERATURVERZEICHNIS American National Standard for Information Technology: ANSI/ISO/IEC 9075-2-1999, Database Languages -SQLPart2:Foundation (SQL/Foundition), 08.12.1999, 90-92, 387-389, 497-501. Erklärung gemäß §19,7 DPO Hiermit erkläre ich, die vorliegende Diplomarbeit selbständig verfasst und keine anderen als die angegebenen Quellen und Hilfsmittel benutzt sowie Zitate kenntlich gemacht zu haben. Bonn, den 30. August 2002 97