Skript zur Vorlesung Datenbanken VertiefungSeite 1 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 1 Übersicht OpenIngres-Komponenten CA-OpenIngres ist ein auf SQL-92 basierendes Relationales Datenbank Management System (RDBMS), welches zusätzlich zu den eigentlichen Datenbankfunktionen eine Reihe von einfach zu benutzenden Werkzeugen zur Abfrage bis hin zur Entwicklung komplexer Anwendungssysteme besitzt (Database Front Ends). Neben dem visuellen Zugriff auf eine Datenbank über sog. Frames enthält OpenIngres jetzt eine vollständige Implementierung von SQL-92 (Entry Level + Erweiterungen). Daneben bietet Ingres mit Ingres/4GL und OpenROAD (früher Windows4GL) vollständige Anwendungsentwicklungssprachen der 4. Generation an (zeichenorientiert und mit graphischer Oberfläche). Zeichenorientierte Programmgeneratoren erleichtern die Entwicklung von Datenbankanwendungen weiter. Weiterhin unterstützt Ingres verteilte Datenbanken. Ingres (Interactive Graphics and Retrieval System) stammt aus einem Projekt an der Universität von Kalifornien in Berkeley (Michael Stonebraker und Eugene Wong, 1973-1975). Ergebnisse waren neben der eigentlichen relationalen Datenbank mit 5 Zugriffsmethoden die Datenbanksprache QUEL (Query Language). Betriebssystem war Unix. Seit 1979 wird Ingres kommerziell vermarktet (Fa. RTI, Relational Technology Incorporated). Konversion von Unix nach VMS. Später wurde Ingres von der Firma ASK übernommen, 1994 dann von CA. Im Laufe der Zeit kamen die folgenden Komponenten hinzu • QBF: Query by Forms • Graph: für graphische Darstellungen • RBF: Report by Forms • ABF: Application by Forms Stärke im Bereich verteilter Datenbanken: • Ingres/NET: Client/Server Technologie • Ingres/STAR: Verteilung der Daten im Netz • Multiserver-Architektur: Dynamische Zuweisung zwischen Front-Ends und Back-Ends (Datenbank-Servern) Anfang 1990 • Knowledge-Management Komponente • Object-Management Komponente • Windows4GL: Graphische Benutzeroberfläche für Datenbankanwendungen 1995 • SQL-92 basiertes OpenIngres • Verbessertes Windows4GL: OpenROAD Skript zur Vorlesung Datenbanken VertiefungSeite 2 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 1.1 Ingres-Datenbank Ingres besteht aus zwei Hauptbestandteilen: • Ingres Benutzerschnittstelle (User Interface) • Ingres Data Manager Startet man eine Benutzerschnittstelle, so wird man automatisch zu einem Data Manager Prozeß verbunden. Client/Multi-Server Architektur: Client1 Client2 Client3 Ingres Tools und Applications Ingres Tools und Applications Ingres Tools und Applications Ingres/Net Ingres/Net Ingres/Net Ingres/Net Ingres/Net Ingres Data Manager Ingres Data Manager Ingres Database Ingres Database Server1 Server2 Die eigentliche Datenbank speichert dabei • Daten (Data Management) • Regeln (Knowledge Management) und • Objekte (Object Management). Sie besteht u.a. aus einem Query Optimizer und dem Data Dictionary. Besonderheiten: Unterstützung von • Kompilierten Datenbankprozeduren (in Ingres 4GL oder SQL) für sich wiederholende Transaktionen • Verschiedenen Methoden zur Performance-Steigerung (Fast Commit, Group Commit, Multiblock-Lese- und Schreiboperationen). Skript zur Vorlesung Datenbanken VertiefungSeite 3 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Optionales Knowledge-Management unterstützt • Regelsystem zur Einhaltung von Geschäftsregeln und Regeln zur referentiellen Integrität. Formuliert in Ingres 4GL oder SQL. Mit werteabhängiger Aktivierung. • Alarmsignal für Datenbankereignisse (Database Event Alerts): Aufruf von Anwendungen auf bestimmte Ereignisse hin (z.B. Nachbestellung, Erinnerungsschreiben). • Ressourcen-Kontrolle in Verbindung mit dem Query-Optimizer • Zugriffs-Kontrolle Optionales Object-Management unterstützt • Definition eigener Datentypen anstelle der Standard Datenbankdatentypen (Zahlen, Zeichen, ...), z.B. nichtmetrische Maße, Koordinatenpaare, Vektoren, Bitmaps • Manipulation dieser neuen Datenobjekte mittels neuer Operatoren und Funktionen (diese auch für Standard-Datentypen) Weitere Komponenten: • Ingres/Gateways: Zugriff auf Nicht-Ingres Datenbanken (Rdb und RMS, DB2 und IMS, ALLBASE/SQL). • Ingres/STAR: Zugriff auf mehrere Datenbanken gleichzeitig. Achtung: Unterscheidung: Mehrere Tabellen in einer Datenbank vs. mehrere Datenbanken. Eine Datenbank ist immer eine Kollektion von mehreren Tabellen. Diese müssen nicht unbedingt einen inhaltlichen Zusammenhang haben (sollten aber). 1.2 Visueller Zugriff und Programmie Programmierung Die Anwenderschnittstelle von Ingres unterteilt sich in Komponenten, • die auch von einem Endbenutzer verwendet werden können (menügetriebenes, visuelles System) und • solche zur Anwendungsentwicklung. Mit Hilfe der menügetriebenen, visuellen Werkzeuge ist das Erstellen von Tabellen und Dateneingabe oder -abfragen leicht zu formulieren, ebenso Verknüpfungen von Tabellen. Dies funktioniert auch alles mit reinen zeichenorientierten Terminals und ohne den Einsatz einer konkreten Abfragesprache. Selbst Anwendungen können so erstellt werden. Zusätzlich wird SQL als Abfragesprache unterstützt, hinzu kommt noch Ingres/4GL sowie das objektorientierte, graphische OpenROAD zur Anwendungsentwicklung. Skript zur Vorlesung Datenbanken VertiefungSeite 4 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Das Ingres User Interface: Query by Forms Visuals Forms E ditor Application by Forms ABF Ingres Datenbank Ingres Vision Ingres Windows 4GL SQL Terminal Monitor Report by Forms Ingres M enu 1.3 Datenbanksprache SQL Basiert auf dem relationalen Datenbankmodell. Ursprünglich Abkürzung für Structured Query Language. Aus dem Entwicklungsprojekt System R der IBM hervorgegangen (1974-1979). Vorgänger von SQL war die Sprache SEQUEL. Erste Implementierungen bei Oracle (1980), dann SQL/DS und später DB2 (IBM). Erst 1985 für Ingres implementiert. Im Oktober 1986 als ANSIStandard übernommen. Moderate Erweiterungen geschahen 1989 (SQL-89). Eine umfangreiche Erweiterung des ANSI-Standards (SQL2 bzw. SQL-92) ist gerade verabschiedet worden. ⇒ OpenIngres Zur Zeit wird schon wieder an SQL3 gearbeitet. Einzelheiten in einem späteren Kapitel. 1.4 Zugriff auf eine Ingres Datenbank Generelle Syntax zum Aufruf von Ingres Komponenten aus einer Unix-Shell heraus: kommando [v_node::]dbname Um auf eine Datenbank auf einem anderen Knoten (Rechner) zugreifen zu können, muß natürlich Ingres/NET installiert sein und man muß den v_node Namen dieses Rechners wissen. Der Ingres System Administrator hat mit Hilfe des netu Programms diesen Namen definiert. Skript zur Vorlesung Datenbanken VertiefungSeite 5 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Ebenfalls mit Hilfe von netu muß die Zugangsberechtigung auf den Datenbank-Knoten definiert werden. Bestimmte Kommandos sind nicht über das Netzwerk aufrufbar. Dazu gehören createdb, accessdb und rollforwarddb. 1.5 Erstellen einer Datenbank Erzeugen der Datenbank mit createdb, Vergabe von Zugriffsrechten durch accessdb. Beide müssen auf dem Datenbankserver laufen. Beispiel: Der User ingres ist auf dem Datenbankserver eingeloggt. Auch der Benutzer i123 sei berechtigt, Datenbanken zu erzeugen. createdb -ui123 -p dbv95_einname Dies erzeugt eine „private“ Datenbank. Der User i123 ist DBA und zunächst der einzige Nutzungsberechtigte der Datenbank. Als nächstes muß das Nutzungsrecht für einen weiteren Benutzer gegeben werden. accessdb Dieses Utility ist Frame-orientiert. Selektiere Database. Es erscheint der Prompt Database: Eingabe des Datenbanknamens, z.B. dbv96_einname. Wähle Authorized Users und gib den/die login-Namen des/der zu authorisierenden User ein. Selektiere Save, dann Quit. Skript zur Vorlesung Datenbanken VertiefungSeite 6 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 2 Zugriff über Forms und Menus Wichtiger Begrif bei Ingres: Das Frame. Kombination aus Maske und Menüs. Voraussetzung für das korrekte Funktionieren in vielfältigen Rechnerumgebungen: Definition des richtigen Terminaltyps. setenv TERM_INGRES terminalname terminalname ist Sun OS sunf Sun OS, OpenWindows sun-cmdf HP-UX hpterm Wird üblicherweise beim Einloggen (nach Abfrage) in .cshrc automatisch durchgeführt. 2.1 IngMenu Aufruf durch ingmenu dbname Haupt-Frame als Menü zu allen Front-End Komponenten mit Funktionenaufruf. Database: xyz INGRES/MENU Tables Forms JoinDefs Reports Applications Queries Create/examine tables or query/report on table data Create/edit/use forms for customized data access Create/edit/use join definitions on multiple tables Create/edit/run reports Create/edit/run Vision and ABF applications Query data using Query-By-Forms or a query language Place the cursor un your choice and press „Select“ Select Shell Help End Quit Unterpunkte: • Tables Erzeugen, ändern, abfragen, Abfrage von Informationen über Tabellen Skript zur Vorlesung Datenbanken VertiefungSeite 7 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Forms Erzeugen, editieren, benutzen von selbstdefinierten Forms • JoinDefs Erzeugen, editieren, benutzen von Join-Definitionen zur Verknüpfung mehrerer Tabellen • Reports Erzeugen, editieren, ausführen von Reports • Applications Erzeugen, ändern, ausführen von 4GL-Anwendungen, ABF: Application by Forms • Queries Abfragen mittels Query-by-Forms (QBF) oder SQL Selektion durch Positionierung des Cursors und Enter bzw. GO. Sonstige Bedienung durch Funktionstasten. Springen von Form zur Menüleiste durch den Menu Key. Abhängig vom Terminaltyp (Sun OpenWindows: R1, HP-UX: F6). Geht das Menü über die Frame-Breite hinaus, so erreicht man die anderen Teile durch mehrfaches Drücken des Menu Key. Man kann Funktionen aus der Menü-Zeile auch durch Eingabe des Namens wählen. Zurück zur Form mit Return Key. Spezielle Key-Funktionen: Tab Control-P Return Control-E Cursor auf nächstes Feld oder nächste Spalte Cursor auf voriges Feld oder vorige Spalte Cursor auf nächstes Feld und gleichzeitig werden alle Daten bis zum Ende des aktuellen Feldes gelöscht. In Table Fields geht Cursor zur nächsten Spalte bzw. nächsten Zeile, wenn in letzter Spalte Toggle Insert und Overstrike Mode Help Key ist bei OpenWindows auf R2 gemapped. Konflikt mit OpenWindows-Zuordnung. Abhilfe: Shift R2 benutzen! 2.2 Tabellen 2.2.1 Erzeugen von Tabellen Sobald man Zugriff auf eine Datenbank hat, kann man dort in der Regel Tabellen anlegen. Beim Anlegen einer Tabelle muß man die Felder (Attribute) der Tabelle definieren. Dazu gehören Name und Datentyp. Als StandardDatentypen gibt es in Ingres die folgenden: • c(n) String fester Länge von maximal 2000 druckbaren ASCII-Zeichen. Nicht druckbare Zeichen werden zu Blanks konvertiert. Blanks werden bei Vergleichen ignoriert. Kompatibilität zu früheren Versionen. • char(n) String fester Länge von maximal 2000 druckbaren oder nichtdruckba- Skript zur Vorlesung Datenbanken VertiefungSeite 8 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ren ASCII-Zeichen. Bei Vergleich wird der kürzere String mit Blanks aufgefüllt. Kompatibel zu dem gleichnamigen Typ in ANSI SQL. Ebenfalls feste Länge. • text(n) String variabler Länge von maximal 2000 ASCII (extended) Zeichen außer NULL. Kürzerer (sonst gleicher) String ist kleiner als längerer. Kompatibilität zu früheren Versionen. • varchar(n) String variabler Länge von maximal 2000 ASCII (extended) Zeichen. Alle Zeichen sind erlaubt, auch NULL. Bei Vergleich wird der kürzere String mit Blanks aufgefüllt. Kompatibel zu dem gleichnamigen Typ in ANSI SQL. • integer1 1 byte integer -128 .. +127 • integer2 oder smallint 2 byte integer -32 768 .. +32 767 • integer4 oder integer 4 byte integer • float4 4 byte floating point 8.43 ⋅10-37 .. 3.37 ⋅1038 • float8 8 byte floating point 4.19 ⋅10-307 .. 1.67 ⋅10308 • date Sog. „abstract data type“ (bei Ingres). 12 Bytes. Datum, Zeit oder Zeitintervall, formatierbar über Environment-Variable (II_DATE_FORMAT) • money Sog. „abstract data type“ (bei Ingres). 8 Bytes. Geldzahlenwerte, formatierbar über Environment-Variable (II_MONEY_FORMAT („$“), II_DECIMAL („.“ oder „,“) und II_MONEY_PREC(2)). II_DATE_FORMAT: Default Eingabe-Datumsformate sind: dd-mmm-yyyy auch Ausgabeformat mm/dd/yy mmddyy Wird setenv II_DATE_FORMAT GERMAN gesetzt, so ist auch das Eingabeformat dd.mm.yy möglich (Ausgabe ???). II_MONEY_FORMAT: Beispiele: Skript zur Vorlesung Datenbanken VertiefungSeite 9 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 setenv II_MONEY_FORMAT L:$ → $100 setenv II_MONEY_FORMAT T:DM → 100DM setenv II_MONEY_FORMAT 'T: FF' → 100 FF Null-Wert ist ein bestimmter Wert eines Attributs, der besagt, daß dem Attribut (noch) kein Wert zugewiesen wurde: Nicht-initialisierter Wert. Ungleich dem Zahlenwert Null oder der ASCII 0! Sind Null-Werte erlaubt, so enthalten nicht-initialisierte Felder diesen speziellen Wert. Sind Null-Werte nicht erlaubt, so muß man in dem Default-Feld angeben, ob Defaults (0, Leerstring) für nicht ausdrücklich initialisierte Felder eingesetzt werden sollen („y“). Ist die Eingabe hier „n“, so muß das Feld immer einen Wert zugewiesen bekommen. Keys und Indexes sollen uns zunächst noch nicht interessieren. TABLES - Create a Table Enter the name of the new table: Enter the column specification for the new table: Column Name Insert Delete Blank Data Type Key # Nulls Move GetTableDef Defaults ListChoices Help Cancel Sichern der Eingaben mit Save! 2.2.2 Ändern von Tabellen Ab OpenIngres 2.0: Siehe hierzu den SQL-Befehl Alter Table! Die Struktur einer Tabelle kann nur noch mit etwas Mühe geändert werden. Nehmen wir an, wir haben die (leere) Tabelle A: 1. Erzeuge eine neue Tabelle B 2. Kopiere mit GetTableDef die Struktur von A nach B 3. Führe die notwendigen Änderungen an der leeren Tabelle B durch 4. Speichere die Definition von B 5. Lösche A mit dem Befehl Destroy 6. Benenne B nach A um: Skript zur Vorlesung Datenbanken VertiefungSeite 10 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Erzeuge Tabelle A • Hole Struktur von B mittels GetTableDef • Speichere A • Lösche B Enthält die umzustrukturierende Tabelle bereits Daten, so hilft nur das Entladen der Tabelle via SQL (bulk Copy) und das neue Laden der umstrukturierten Tabelle. 2.3 Nützliche Utilities catalogdb Auflistung • aller für den Benutzer zugreifbaren Datenbanken, • die dem System bekannten location Namen, • Benutzer Rechte. createdb Kreiert eine neue Datenbank auf dem jetzigen Knoten. Man benötigt entsprechende Rechte. Der Erzeuger der datenbank wird der Database Administrator (DBA) für diese Datenbank. destroydb Löscht eine Datenbank mit allen zugehörigen Komponenten (Tabellen, JoinDefs, Forms, ...). Auch dazu benötigt man natürlich die entsprechenden Rechte. 2.4 Query by Forms QBF: Ausfüllen eines Formulars zur Definition der Abfrage (ähnlich wie Query by Example – QBE). Resultate stehen danach in dem Formular. 2.4.1 Einfache Datenmanipulation Alle Datenzugriffe werden bei Ingres Query genannt, auch das Hinzufügen von neuen Datensätzen. Aufruf über ingmenu und Query, dann Unterpunkt QBF. QBF arbeitet in zwei Phasen: • Definition phase: Festlegung der Daten, die gesucht, geändert oder eingefügt werden sollen • Execution phase: Ausführung der Operationen (Append, Retrieve oder Update) Objekt der Abfrage können sein: • Tables Einfache Tabellen oder Views • JoinDefs Zwei oder mehrere verknüpfte Tabellen (Joins) • QBFnames Eine benutzerdefinierte Form (Maske), die mit einer Tabelle oder einer JoinDef verknüpft ist. Skript zur Vorlesung Datenbanken VertiefungSeite 11 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 In der execution phase kann man die folgenden Operationen mit dem selektierten Objekt durchführen: • Append: Hinzufügen von Datensätzen • Retrieve: Ansehen von Datensätzen • Update: Ändern von Datensätzen Ingres unterscheidet in Forms zwischen • Simple fields: Einfache Felder in der Form. Jede Form zeigt einen Satz (Reihe) der Tabelle und • Table fields: Tabellarische Felder (mehrere Reihen einer Tabelle) Nach dem Hinzufügen neuer Sätze darf man den Aufruf von Append nicht vergessen. Sonst werden die Sätze nicht wirklich hinzugefügt. Bei der Retrieve-Operation füllt man einzelne Felder der Form mit Suchmustern aus. Die Ausführung liefert dann keinen, einen Satz oder mehrere Sätze als Ergebnis. Suchmuster Bedeutung Blank Alle Sätze Exakter Inhalt Satz mit genau diesem Muster * 0 oder mehrere beliebige Zeichen ? genau ein beliebiges Zeichen [aAbB] oder [A-D] Eines der Zeichen oder des Zeichenbereiches in den eckigen Klammern Die Wildcards können nur mit Character-Daten benutzt werden. Es können auch Vergleichsoperationen vorkommen: Operator Bedeutung > größer als < kleiner als >= größer oder gleich <= kleiner oder gleich != ungleich or Oder-Verknüpfung and Und-Verknüpfung Skript zur Vorlesung Datenbanken VertiefungSeite 12 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Bedingungen für eine Spalte können noch mit and oder or verknüpft werden. and kann dabei durch ein Leerzeichen ersetzt werden. Will man Leerzeichen als Bestandteil von Suchstrings angeben, so muß der String in Anführungszeichen eingeschlossen werden. Klammerung ist möglich. String-Vergleiche sind case-sensitiv! Bedingungen für Spalten in einer Zeile werden und-verknüpft, Bedingungen für Spalten in untereinander stehenden Zeilen (nur bei Table field möglich) werden oder-verknüpft. Die Ergebnisse können über Aufruf von Order auch sortiert werden. 2.4.2 Komplexe Datenmanipulation Verknüpfung von Tabellen mit JoinDefs zur Vorbereitung von Abfrage mehrerer Tabellen. Aufrufen von JoinDefs aus ingmenu. Dann Create. Zwei Arten von Joins (Angabe unter Role): • Master to master: Relationale equi-joins, eins-zu-eins • Master to detail: eins-zu-viele. Joins können auch verkettet werden. Sie können jedoch nur eine Master/Detail Definition enthalten. Ein Join mit zwei Tabellen: Employee Table Gemeinsame Werte in einer JOIN Spalte Ref 1224 Name Address Anne Jones 44 Peabody Flats London Project Tasks Table Task ID Dev 12 Task ID Dev 12 Task Name Produce Sales Year End Report Ref 1224 Task Name Produce Sales Year End Report Ref 1224 Name Anne Jones Nach Auswahl der zu verknüpfenden Tabellen und ihrere Rollen (man kann ihnen noch abgekürzte Namen geben), müssen die Verknüpfungsspalten definiert werden. Dazu ruft man den Menüpunkt Joins auf. Unter dem weiteren Menüpunkt Rules kann man nun bereits hier einfache Regeln zur Wahrung der referentiellen Integrität angeben. Skript zur Vorlesung Datenbanken VertiefungSeite 13 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 QBF - JoinDef Update & Delete Rules Update Information: To enable modification of join fields in UPDATE mode, enter „Yes“ under Update? Column Column Update? abteilung.abt_nr mitarbeiter.abt_nr no yes Delete Information: To disable deletion of rows in table during UPDATE mode, enter „No“ under Delete? Column Role Table Name (or Abbreviation) Delete? MASTER DETAIL abteilung mitarbeiter no yes Joins Forget Help End In dem ersten Block kann man angeben, ob Änderungen in der Join-Spalte der beiden Tabellen erlaubt sind. Durch geschickte Angabe kann man z.B. spezifizieren, was mit dem anderen Feld geschehen soll, wenn das eine geändert wird: Ebenfalls ändern oder nicht. In dem zweiten Block entscheidet man, ob das Löschen der Sätze der verknüpften Tabellen erlaubt ist oder nicht. Also z.B. ob beim Löschen eines Satzes der einen Tabelle auch der/die entsprechenden Sätze der anderen Tabelle gelöscht werden sollen. Durch Aufruf von ChangeDisplay kann man zusätzlich angeben, daß bestimmte Spalten nicht angezeigt werden sollen. Durch Aufruf von Go kann man die neu definierte JoinDef sofort ausführen dabei überprüfen. Nicht vergessen, mit Save zu sichern! Skript zur Vorlesung Datenbanken VertiefungSeite 14 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 3 Einstieg in SQL SQL besteht aus mehreren Untergruppen: • Data Definition Language (DDL) Kreieren, Modifizieren und Löschen von Tabellen, Erzeugen von Indizes • Data Manipulation Language (DML) Abfrage-Sprache sowie Eingabe, Modifikation und Löschen von Tabellen-Reihen • Definition von Views (Teil von DDL) • Authorisierung (Teil von DDL) Zugriffsrechte auf Tabellen und Views • Integrität In SQL-89 nur limitierte Form der Integritätsüberprüfung, in SQL-92 mehr • Transaktionskontrolle Spezifikation des Anfangs (implizit) und des Endes einer Transaktion 3.1 Datendefinition Grundsätzlich muß zunächst eine Datenbank definiert werden: createdb MeineDatenbank Dann kann man in den SQL-Editor gehen und Tabellen kreieren. Zwei Formen der Anweisung: CREATE TABLE TabName(SpaltenName1 SpaltenTyp1 [,SpaltenName2 SpaltenTyp2, ..]) [WithKlausel]; oder CREATE TABLE TabName(SpaltenName1 SpaltenTyp1 [,SpaltenName2 SpaltenTyp2, ..]) AS SelectAnw [WithKlausel]; Tabellennamen dürfen bei Ingres nicht mit „ii“ beginnen (reserviert für interne Zwecke). Mögliche Datentypen sind Numerisch: • integer (4 Bytes, ≡ integer4), • smallint (2 Bytes, ≡ integer2), • integer1, • decimal, • float (8 Bytes, ≡ float8), • float4 (4 Bytes, ≡ real) Character, feste Länge • c Skript zur Vorlesung Datenbanken VertiefungSeite 15 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • char (≡ character) Character, variable Länge • text • varchar • long varchar Abstract • date • money • object_key • table_key Binär • byte • byte varying • long byte Beispiele für DATE Formate: Absolute Angabe: 28-may-1993 28-may-1993 07:58:00 Zeitintervall: 10 yrs 9 mon 8 days 7 hrs 6 mins 55 secs Logical Key Data Types Dazu gehören object_key und table_key. Haben datenbankweiten oder tabellenweiten eindeutigen Wert. Z.B. für Primärschlüssel. Können system_maintained oder not system_maintained sein. Bei system_maintained erzeugt Ingres selbst eindeutige Werte. Dann ist dieser Wert nicht mehr änderbar. Auch Probleme beim Kopieren und Ändern von Datenbanken. Alle Varianten sind nicht zu empfehlen!! Die Datentypen können noch weiter qualifiziert werden: • [ [ WITH ] DEFAULT default_spec | WITH DEFAULT | NOT DEFAULT ] • [ WITH NULL | NOT NULL ] • [ [ CONSTRAINT constraint_name ] column_constraint {, [ CONSTRAINT constraint_name ] column_constraint } ] Dabei kann column_constraint einer oder mehrere der folgenden Punkte sein: • unique • primary key Skript zur Vorlesung Datenbanken VertiefungSeite 16 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • REFERENCES table_name [ (column_name) ] • CHECK (check_specification) Default-Spezifikation: • not default Wert für diese Spalte muß vorliegen • with default ohne Wert setzt DBMS ein: 0 für numerische und money-Felder, leerer String für Character und date • [ with ] default default_spec ohne Wert setzt DBMS den angegebene Default-Wert ein. Muß typkompatibel sein. Für Character-Spalten ist zusätzlich erlaubt: „user“, „current_user“, „system_user“ Möglichkeit von NULL-Werten: • with null Default. Ohne Wert, nimmt Spalte den NULL-Wert an. • not null Ohne Default-Spezifikation muß diese Spalte zwingend Wert zugewiesen bekommen, mit Default-Spezifikation wird der Default Wert zugewiesen, falls kein Wert durch den Benutzer geliefert wird. Mögliche Kombinationen: • with null Die Spalte akzeptiert NULLs. Ohne Wert, nimmt Spalte den NULL-Wert an. • with null with default Die Spalte akzeptiert NULLs. Ohne Wert, nimmt Spalte den DefaultWert an. • with null not default Die Spalte akzeptiert NULLs. Der Benutzer muß einen Wert liefern. • not null with default Die Spalte akzeptiert keine NULLs. Ohne Wert, nimmt Spalte den Default-Wert an. • not null not default oder not null Die Spalte akzeptiert keine NULLs. Der Benutzer muß einen Wert liefern. Typisch für Primärschlüssel. Constraints: • unique Muß in Verbindung mit not null spezifiziert werden: unique not null! create table dept (dname char(10) unique not null, ...) Soll die Kombination mehrerer Spalten unique sein, so muß dies auf Tabellenebene spezifiziert werden. Skript zur Vorlesung Datenbanken VertiefungSeite 17 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 create table depts(dname char(10) not null, dlocation char(10) not null, constraint unique_dept unique (dname, dlocation)); • check Constraint Benutzerdefinierte Constraints bezüglich einer oder mehrerer Spalten create table emps (name char(25), sal money, constraint check_salary check (sal > 0); /* Boolean expr */ Auch für mehrere Spalten: create table dept(dname char(10), location char(10), budget money, expenses money, constraint check_amount check (budget > 0 and expenses <= budget)); • Referential Constraints Eine Eingabe wird bezüglich einer Spalte in einer anderen Tabelle (oder auch derselben Tabelle) validiert: Fremdschlüssel. create table emp (ename char(10), edept char(10) references dept(dname)); oder create table mgr (name char(10), empno char(5), ..., foreign key (name, empno) references emp); Hier müssen für emp die beiden Spalten name und empno als primary key constraint definiert sein. • Primärschlüssel Constraints Erlaubt es anderen Tabellen, auf die hier definierten Spalten in referential constraints bezug zu nehmen: create table partnumbers (partno int primary key, ...); primary key impliziert die unique und not null Constraints! Man unterscheidet Column-Level und Table-Level Constraints: Column-Level Constraint: create table mytable (name char(10) not null, id integer references idtable(id), age integer check (age>0)); Table-Level Constraint: create table yourtable (firstname char(20), lastname char(20), unique(firstname, lastname)); Wird der Versuch unternommen, eine Tabelle derart zu ändern, daß ein so definierter Constraint verletzt würde, so wird der gesamte SQL-Befehl abgebrochen und ein Fehler erzeugt. Skript zur Vorlesung Datenbanken VertiefungSeite 18 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Bemerkung: Es gibt noch eine create integrity-Anweisung (nicht ANSI/ISO SQL-92 verträglich). Weiterhin können rules in Verbindung mit DatenbankProzeduren Integritäten erzwingen. Spaltendefinition als Syntaxdiagramm: column definition datatype column name domain literal default datetime value function current_user session_user system_user null column-constraint collate-clause Skript zur Vorlesung Datenbanken VertiefungSeite 19 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Constraints als Syntaxdiagramm (unvollständig!): not table constraint null unique constraint primary key constraint name check references ( search-condition table name ( column name ) , Die WithKlausel beschreibt weitere Eigenschaften einer Tabelle, die wir auch schon von der forms-basierten Tabellenerzeugung kennen: WITH LOCATION = (...) [NO]JOURNALING [NO]DUPLICATES Angabe von • Plattenspeicherbereich für die Tabelle • Sicherung des ursprünglichen Inhalts aller modifizierten Reihen • Zulassung identischer Reihen oder nicht Mit der zweiten Variante (inkl. SELECT-Anweisung) kann man die neue Tabelle mit dem Ergebnis der SELECT-Anweisung z.B. aus einer anderen Tabelle laden. 3.1.1 Erzeugen von Beispiel-Tabellen Entity-Relationship Modell: abteilung projekt mitarbeiter arbeiten ) Skript zur Vorlesung Datenbanken VertiefungSeite 20 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Detaillierteres Modell: abteilung mitarbeiter abt_nr m_nr abt_name stadt m_name m_vorname abt_nr arbeiten m_nr pr_nr projekt aufgabe einst_dat pr_nr pr_name mittel Hieraus ergibt sich die folgende physikalische Tabellenstruktur: abteilung Feld Typ Domain Null Default abt_nr char(4) n n abt_name char(20) n n stadt char(15) j Muenchen PK FK zu Tabelle x mitarbeiter Feld Typ Domain Null Default m_nr integer n n m_name char(20) n n m_vorname char(20) n n abt_nr char(4) n n PK FK zu Tabelle x abteilung projekt Feld Typ Domain Null Default pr_nr char(4) n n pr_name char(25) n n mittel float8 j n PK x FK zu Tabelle Skript zur Vorlesung Datenbanken VertiefungSeite 21 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 arbeiten Feld Typ Domain Null Default PK FK zu Tabelle m_nr integer n n x mitarbeiter pr_nr char(4) n n x projekt aufgabe char(15) j n einst_dat date j n Hieraus ergibt sich nun das folgende SQL-Script (DDL): CREATE TABLE abteilung (abt_nr CHAR(4) NOT NULL CONSTRAINT abt_prim PRIMARY KEY, abt_name CHAR(20) NOT NULL, stadt CHAR(15) WITH DEFAULT 'Muenchen'); CREATE TABLE mitarbeiter (m_nr INTEGER NOT NULL CONSTRAINT mit_prim PRIMARY KEY, m_name CHAR(20) NOT NULL, m_vorname CHAR(20) NOT NULL, abt_nr CHAR(4) CONSTRAINT mit_for REFERENCES abteilung); CREATE TABLE projekt (pr_nr CHAR(4) NOT NULL CONSTRAINT pro_prim PRIMARY KEY, pr_name CHAR(25) NOT NULL, mittel FLOAT8); CREATE TABLE arbeiten (m_nr INTEGER NOT NULL, pr_nr CHAR(4) NOT NULL, aufgabe CHAR(15), einst_dat DATE, CONSTRAINT arb_prim PRIMARY KEY (m_nr, pr_nr), CONSTRAINT arb_for_mit FOREIGN KEY (m_nr) REFERENCES mitarbeiter, CONSTRAINT arb_for_pro FOREIGN KEY (pr_nr) REFERENCES projekt); Als Voreinstellung werden Tabellen in einer unstrukturierten Speicherstruktur heap erzeugt. Hierbei werden neue Reihen einfach an das Ende angefügt. Weitere Speicherstrukturen können sein: • hash • isam Zugriff über berechneten Schlüssel Zugriff über Index • btree Zugriff über Baumstruktur Wir werden diese Speichermodelle später ausführlich besprechen. Skript zur Vorlesung Datenbanken VertiefungSeite 22 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Das universellste Speichermodell ist btree. Die Speicherstruktur kann man nach der Tabellenerzeugung ändern: MODIFY abteilung TO btree UNIQUE ON abt_nr; MODIFY mitarbeiter TO btree UNIQUE ON m_nr; MODIFY projekt TO btree UNIQUE ON pr_nr; MODIFY arbeiten TO btree UNIQUE ON m_nr, pr_nr; Die hier definierten Tabellen existieren physikalisch. Zusätzlich kann man quasi virtuelle Tabellen kreieren, sog. Views. Sie werden aus den darunterliegenden physisch vorhandenen Tabellen abgeleitet. Dazu gibt es die Anweisung CREATE VIEW ... die den View basierend auf einer SELECT-Anweisung erzeugt. Wie behandeln Views später. Eine weitere Struktur, die hier zunächst nur erwähnt werden sollte, ist ein Sekundärindex für andere Felder (nicht Primärschlüssel) einer Tabelle, der mit CREATE INDEX ... erzeugt wird. Auch dazu später. Die Tabellen werden nun bevölkert: abteilung abt_nr projekt abt_name stadt pr_nr pr_name mittel a1 Beratung Muenchen p1 Apollo 120000 a2 Diagnose Muenchen p2 Gemini 95000 a3 Freigabe Stuttgart p3 Merkur 186500 Skript zur Vorlesung Datenbanken VertiefungSeite 23 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 arbeiten mitarbeiter m_nr pr_nr aufgabe einst_dat m_nr m_name 10102 p1 Projektleiter 01.10.1988 25348 Keller Hans a3 10102 p3 Gruppenleiter 01.01.1989 10102 Huber Petra a3 25348 p2 Sachbearbeiter 15.02.1988 18316 Mueller Gabriele a1 18316 p2 01.06.1989 29346 Probst Andreas a2 29346 p2 15.12.1987 9031 Meier Rainer a2 2581 p3 Projektleiter 15.10.1989 2581 Kaufmann Brigitte 9031 p1 Gruppenleiter 15.04.1989 28559 p1 01.08.1988 28559 p2 Sachbearbeiter 01.02.1989 9031 p3 Sachbearbeiter 15.11.1988 29346 p1 Sachbearbeiter 01.04.1989 28559 Mozer m_vorname Sibille abt_nr a2 a1 mit_erweiter m_nr m_name m_vorname abt_nr wohnort 25348 Keller Hans a3 Muenchen 10102 Huber Petra a3 Landshut 18316 Mueller Gabriele a1 Rosenheim 29346 Probst Andreas a2 Augsburg 9031 Meier Rainer a2 Augsburg a2 Muenchen a1 Ulm 2581 Kaufmann Brigitte 28559 Mozer Sibille 3.1.2 Löschen von Objekten Generelle Form der Anweisung: DROP objektart obj_name Z.B.: DROP TABLE tab_name Das Löschen einer Tabelle löscht natürlich auch alle Daten, aber auch zugehörige Indizes. 3.1.3 Abändern der Tabellenstruktur Werden Tabellen im Rahmen der folgenden Manipulationen gelöscht, so werden auch alle dazugehörigen Objekte, wie Views oder Indices, gelöscht! Ab OpenIngres 2.0 wird der SQL-Befehl Alter Table unterstützt. Diese Variante wird in den folgenden Fällen als erste Möglichkeit genannt. 3.1.3.1 Hinzufügen einer Spalte Zu ändernde Tabelle sei test. OpenIngres 2.0: ALTER TABLE test ADD [COLUMN] NeueSpalte Spaltentyp Skript zur Vorlesung Datenbanken VertiefungSeite 24 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Spaltentyp ist definiert wie bei Create Table. Die Klauseln not null with default, with null with default und not null not default sind jedoch nicht erlaubt. Die Spalte wird immer an das Ende der Satzdefinition angefügt. Alte Methode: CREATE Table temp AS SELECT test.*, VarChar(' FROM test; ') AS NeueSpalte DROP test; CREATE TABLE test AS SELECT * FROM temp; DROP temp; Ist die neue Spalte in der Mitte der Spalten plaziert: CREATE Table temp AS SELECT Sp1, Sp2, VarChar(' ') AS NeueSpalte, Sp3, Sp4, Sp5 FROM test; 3.1.3.2 Löschen einer Spalte OpenIngres 2.0: ALTER TABLE test DROP [COLUMN] Spalte RESTRICT | CASCADE Wenn Cascade spezifiziert wird, werden automatisch alle von der gelöschten Spalte abhängigen Objekte (views, integrity constraints, grants, indexes) gelöscht. Wenn Restrict spezifiziert wird und Abhängigkeiten existieren, wird die Spalte nicht gelöscht. Alte Methode: Lasse eine Spalte bei SELECT aus: CREATE Table temp AS SELECT Sp1, Sp2, Sp4, Sp5 FROM test; usw. 3.1.3.3 Ändern des Namens einer Spalte CREATE Table temp AS SELECT Sp1 AS Name, Sp2 AS Vorname, Sp4, Sp5 FROM test; usw. 3.1.3.4 Ändern des Datentyps einer Spalte Änderung der Spalte 4 von float4 zu float (8 Bytes): CREATE Table temp AS SELECT Sp1, Sp2, float8(Sp3) AS Sp3, Sp4 FROM test; Skript zur Vorlesung Datenbanken VertiefungSeite 25 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 usw. Vergrößerung einer VarChar-Spalte: CREATE Table temp AS SELECT Sp1, squeeze(pad(Sp2) + ' FROM test; ') AS Sp2, Sp3, Sp4 usw. 3.1.4 Vergabe von Rechten für Objekte Der DBA muß seinen Gruppenmitgliedern Rechte für Tabellen etc. geben. Das geschiegt mit dem grant Befehl: grant priv {, priv} | all [privileges] [excluding (columnname {, columnname})] on [table] | procedure objname {, objname} to userid {, userid} | public [with grant option] oder ... to [user] | group | role | public [auth_id {, auth_id}] ... Beispiel: grant all on MyTable to group c100 Sonstige Privilegien: • select • update • insert • delete Vergib DBA-Privileg für Datenbank: grant db_admin on database dbv96_xxx to group c100 grant db_admin on database dbv96_xxx to i124 3.1.5 Integrität innerhalb einer Tabelle Definition möglich mit Hilfe der folgenden Anweisung: CREATE INTEGRITY ON tablename [corr_name] IS search_condition Beispiel: CREATE INTEGRITY ON employee IS salary <= 150000; Achtung bei NULLable-Feldern (a kann hier NULL sein: CREATE TABLE test (a int, b int NOT NULL); CREATE INTEGRITY ON test IS a > 10; Fehler treten auf, da die Integritätsbedingung NULL nicht einschließt: INSERT INTO test (b) VALUES (5); Insert INTO test VALUES (NULL, 5); Richtige Integritätsbedingung: Skript zur Vorlesung Datenbanken VertiefungSeite 26 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 CREATE INTEGRITY ON test IS (a > 10) OR (a IS NULL); 3.1.6 Gruppen Über Gruppen können Rechte für eine Liste von Ingres Benutzern (User IDs) vergeben werden. Von User ingres erzeugt. create group groupid {, groupid} [with users = (userid {, userid})] und alter group groupid {, groupid} add users (userid {, userid}) | drop users (userid {, userid}) | all sowie drop group groupid {, groupid} Skript zur Vorlesung Datenbanken VertiefungSeite 27 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 4 Windows4GL 4.1 Grundlagen Windows4GL-Anwendung • Graphische Benutzerschnittstelle (GUI) mit Application Editor als „Screenpainter“ und Menü-Editor zum Erstellen • Daten, z.B. von Tabellen können angezeigt und manipuliet werden (via embedded SQL) • Serie von „Fenstern“ (frames) mit Objekten • Ereignisse (events) an Objekten können Aktionen auslösen • Aktionen definiert in Windows4GL-Sprache, niedergelegt in Scripts (den Objekten oder dem „Fenster“ zugeordnet) • Jedes Event löst den Windows4GL-Code in einem Event Block aus Komponenten einer Applikation: • Frames mit Objekten und Scripts (User- und Ghost-Frames) • Frame- und Field-Templates • Prozeduren (Datenbank-, 4GL-, 3GL-) • Include Scripts • Globale Variablen • Globale Konstanten • Benutzer Klassen (User Classes) Maustasten bei Benutzung des Editors: • Linke Maustaste: Select • Mittlere Maustaste: Details • Rechte Maustaste: Properties Starten durch windows4gl datenbank Beispiele in der Datenbank w4gldemo. Erstes Fenster: Applikationen in der Datenbank Nach Anwahl der Applikation: Component Catalog. Komponententypen s.o. 4.2 Frame-Editor Frames sind die Bausteine der Applikation: • Bilden Benutzerschnittstelle • Zeigen Informationen • Stellen Kontrollelemente zur Verfügung • Anwender − wechselt von Frame zu Frame − gibt Daten ein oder läßt Daten anzeigen − klickt Buttons oder wählt Menüpunkte aus Skript zur Vorlesung Datenbanken VertiefungSeite 28 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Entwickler − legt Layout und Aktionen fest − legt Abläufe im Frame fest − legt Ausführungsreihenfolge der Frames fest − bestimmt, wie Daten in Frames dargestellt und zwischen Frames übertragen werden • Werden im Frame-Editor entwickelt und getestet Es gibt zwei vordefinierte Frame Templates: • Menu Frame Default Menu Bar (File Menü mit Close Operation) • Dialog Box Frame Ohne Menu Bar, mit Ok und Cancel Buttons Jedes Frame oder Frame Template hat einen zugeordneten Frame Style: Default visuelle Charakteristiken für jeden Feldtyp Õ Standardisierung des Erscheinungsbildes. Ablauf der Anwendung = Folge von Frame Aufrufen. Möglichkeiten: • Callframe Ruft Frame als modales Fenster auf • Openframe Ruft Frame als nichtmodales Fenster auf. Benutzer kann auch mit dem aufrufendem Frame wechselwirken. • Gotoframe Das aufrufende Frame wird geschlossen. Kontrolle geht an gerufenes Frame über. Beispiel aus dem video_list Frame der Videos Anwendung: on click view_button = begin ... vlist[i].details_frame = openframe video_detail(video_info=vlist[], read_only = TRUE) with windowtitle = vlist[].title; ... end; Innerhalb einer Form gibt es die folgenden Felder, die im Editor plaziert werden können: • Entry Fields Anzeige und Eingabe von Daten • Button Fields Auslösung von Aktionen durch Klick • Toggle Fields Durch Klick auf einen von zwei Zuständen setzbar (Ein/Aus) Skript zur Vorlesung Datenbanken VertiefungSeite 29 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Enumerated Fields (Option-, List-) Liste von Werten • Analog Fields Analoge Anzeige ganzzahliger Zahlenwerte (zwischen Min und Max) • Image Fields Bilder • Shape Fields Zur Dekoration (Hintergrund) • Palette Fields Gruppe von Buttons, jeder mit einem Image, jeder repräsentiert eine wählbare Option • Control Button Field Bietet ein Menü möglicher Operationen (z.B. bei einem Table Field) • Free Trim, Boxed Trim Reiner Text • Composite Fields Enthalten beliebeige andere Felder Aufruf aus dem Component Catalog. Frame selektieren; mit Edit aus File-Menü oder mit Property-Taste. Neues Frame mit File/Create. 4.3 Windows4GL am Beispiel Es gibt drei Arten von Scripts: • Frame Scripts • Scripts für individuelle Felder oder Menüpunkte • Include Scripts • Scripts für Methoden von User Classes. 4.3.1 Das Frame „Hauptmenü“ Hier haben wir ein Frame Script. Frame Scripts könne aus drei Teilen bestehen: • Optionaler initialize Block Deklaration von Parametern, lokalen Variablen und lokalen Prozeduren des Frames • Event Blocks Aufgerufen durch Aktionen des Benutzers oder des Programms (u.a. auch Initialisierung des Frames) • Optional der Code lokaler Proceduren Ein Beispiel für ein Frame Script: Skript zur Vorlesung Datenbanken VertiefungSeite 30 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 initialize = begin /* Fülle Table Field mit Auswahl */ /* und Frame Namen */ selection_table[1].frame_name = 'video_list'; selection_table[1].frame_desc = 'Video Liste'; selection_table[2].frame_name = 'cust_maint'; selection_table[2].frame_desc = 'Kunden-Verwaltung'; selection_table[3].frame_name = 'sales_chart'; selection_table[3].frame_desc = 'Verkaufsstatistik'; selection_table[4].frame_name = 'check_out'; selection_table[4].frame_desc = 'Ausleihe'; /* Zeige alle Einträge */ field(selection_table).NumVisibleRows = selection_table.LastRow(); end; on click go_button = begin callframe :selection_table[].frame_name; end; on click close_button, on windowclose = begin exit; end; Schauen wir uns eines der Frames in der nächsten Stufe an: die Liste aller Videos video_list. Daraus kann ein Eintrag ausgewählt werden. Ein DetailFrame kann aufgerufen werden. Mit diesem sind dann bestimmte Operationen möglich. Skript zur Vorlesung Datenbanken VertiefungSeite 31 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 initialize ( user_name = varchar(32) not NULL, dba_name = varchar(32) not NULL, i = integer, video = VIDEO_ROW, /* Klasse eines Video Eintrags */ close_details_frame = FrameExec, delete_details_frame = FrameExec, new_details_frame = array of FrameExec ) = begin /* Wenn Benutzer DBA ist, mache Edit Button sichtbar */ select :user_name = dbmsinfo('username), :dba_name = dbmsinfo('dba'); commit; if user_name != dba_name then field(view_edit_buttons.edit_button).CurBias = FB_INVISIBLE; field(create_button).CurBias = FB_INVISIBLE; endif; /* Lade Table Field mit allen Videos */ i = 1; select :vlist[i].vid_no = vid_no, :vlist[i].title = title, ... :vlist[i].timestamp = timestamp from v_video order by title begin i = i + 1; end; commit; end; on click view_edit_buttons.view_button = begin if vlist[].details_frame is NULL then vlist[].details_frame = openframe video_details ( video_info = vlist[], read_only = true) with windowtitle = vlist[].title; field(view_edit_buttons).CurBias) = FB_DIMMED; end; Skript zur Vorlesung Datenbanken VertiefungSeite 32 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 on childentry vlist = begin if vlist[].details_frame is NULL then field(view_edit_buttons).CurBias = FB_CHANGEABLE; else field(view_edit_buttons).CurBias = FB_DIMMED; end; on userevent 'UpdateEntry' = begin video = VIDEO_ROW(CurFrame.MessageObject); /* Welche Zeile im Table View enthält das Video? */ if find_video_row(video_list = vlist, details_frame = video.details_frame, row = byref(i)) = true then if vlist[i].title != video.title then /* Wenn Titel sich geändert hat, muß er neu */ /* einsortiert werden */ vlist.RemoveRow(rownumber=i); /* Erzeuge leeren Eintrag an alphabetisch */ /* richtiger Stelle */ callproc insert_video_row(video_list = vlist, title = video.title, row = byref(i)); field(vlist).ActiveRow = i; field(view_edit_buttons).CurBias = FB_DIMMED; endif vlist[i] = video; endif end; 4.4 Felder in einer Form Man unterscheidet • Active Fields Wechselwirkung mit dem Benutzer möglich • Inactive Fields Nur Anzeige von Text, Daten, Bildern • Composite Fields Aus Einzelfeldern zusammengesetzt, gemeinsam manipulierbar Gemeinsame Eigenschaften aller Felder: • Field Property Sheets Frame zur Manipulation der Eigenschaften eines Feldes Skript zur Vorlesung Datenbanken VertiefungSeite 33 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Field Biases Definiert, ob das Feld sichtbar ist und wie der Benutzer mit ihm wechselwirken kann. Ein Frame kann in einem von 6 Framemodi sein: • Update Die zugehörigen Biases erlauben Read- und Update-Operationen • Query ditto • Read Die zugehörigen Biases erlauben Read-, aber keine UpdateOperationen • User1, User2 oder User3 Benutzerdefinierte Frame Modi In jedem Modus kann ein Objekt einen von 12 Biases annehmen. Diese bestimmen die Sichtbarkeit des Objektes und die Art der möglichen Interaktion mit ihm (z.B. ob editierbar oder nicht): Kategorie Bias Möglichkeiten Interactive Changeable Select, Tab, Edit Landable Select, Tab Visible Sichtbar Dimmed Geschwächte Darstellung, keine Wechselwirkung möglich Invisible Unsichtbar, keine Wechselwirkung möglich Flexible Select, Move, Resize Resizable Select, Resize Moveable Select, Move Markable Select ClickPoint Akzeptiert ClickPointEvent (Cursor Koord.) DragBox Eine Dragbox kann durch das Feld gezeichnet werden DragSegment Ein Liniensegment kann durch das Feld gezeichnet werden Passive Select Draw • Variable Declared Feld Normalerweise selektiert: OpenROAD deklariert automatisch eine mit dem Feld verknüpfte Variable. Unter deren Namen ist der Wert des Feldes zugreifbar. Skript zur Vorlesung Datenbanken VertiefungSeite 34 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 NIcht gewählt: Um dynamisch zur Laufzeit deklarierte Variablen zu benutzen. • Mouse Move Text und Mouse Down Text Texte, die bei der entsprechenden Aktion im Status Bar erscheinen 4.4.1 Entry Fields Single-Line oder Multi-Line. Ineinander überführbar (wenn Datentyp Varchar). Ergebnis: eine Variable. Kein Titel damit verknüpft! Eigenschaften: • Variable Name: Bezug bei Programmierung • Data Type: Beschränkung auf Basisdatentypen möglich (default varchar) − Varchar, Smallint, Integer, Float, Money, Date, StringObject • Length (nur für varchar) • Nullable: Setzbar zu NULL • Mandatory • Default value: Initialisierung des Feldes • In Tab Sequence: Durch Tab erreichbar (wenn nicht: nur durch Maus erreichbar) • Script Weitere Optionen: Single-Line oder Multi-Line, Password (ohne Echo), Scrollbar (bei Multi-Line), Format für non-text Datentypen (Templates), Force Case und Bias (s.u.). 4.4.2 Button Eigenschaften: • Variable Name • Label text • Previous Field (s.u.) • Bias und Script Effekt des Button-Click auf das vorherige Feld: • Validated: Für das vorherige Feld wird ein SetValue Ereignis erzeugt (Daten Validierung) • Not Validated: Z.B. für Cancel Button • Loses Focus: Input Focus geht auf neues feld über (Text Cursor, z.B. für zwei Text Felder) 4.4.3 Toggle Zwei Zustände. Unterschiedliche Texte oder Bilder. Zusätzlicher Indikator ist optional. Skript zur Vorlesung Datenbanken VertiefungSeite 35 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Mehrere Toggles zu Stack zusammenfaßbar. Eigenschaften: • Variable Name (Typ Integer) • Label Art (Text oder Image) • Off/On Label Text bzw. Image • Has Indicator • Default • Previous Field (s.o.) • Bias und Script 4.4.4 Enumerated Fields Radio-, List- oder Option-Field. Alle liefern nur einen Wert zurück! Alle haben praktisch dieselben Eigenschaften, nur unterschiedliche Erscheinungsformen: Eigenschaften: • Variable Name: Varchar (Text) oder Integer (Value) • Nullable • Value List • Orientation • Default • Bias und Script 4.4.5 Analog Fields Slider-, Bar- (für Bar-Charts) oder Scrollbar-Field. Ähnliche Eigenschaften. Scrollbar im Prinzip wie Slider, Aussehen wie Standard-Scrollbars. Aktion muß selbst programmiert werden („independent scrollbar“, z.B. horizontaler Scrollbar für Table Field). Eigenschaften: • Variable Name: Integer • Min und Max • Default Wert • Bias und Script 4.4.6 Image Fields Vom Typ GIF, XBM oder Sun Raster. Darstellbar nur durch 4GL Code. Eigenschaften: • Variable Name: BitmapObject • Clipped • Previous Field • Bias und Script Skript zur Vorlesung Datenbanken VertiefungSeite 36 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 4.4.7 Inactive Fields Keine Interaktion mit dem Benutzer. Shapes und Trim. Shapes: Line, Rectangle, Ellipse Trim: Image, Free text, Boxed text Eigenschaften: • Variable Name • Bias 4.4.8 Composite Fields Können gemeinsam manipuliert werden. Auch im Script unter einem Namen. • Subform Zusammenfassung mehrerer Felder in rechteckigen Grenzen • Stack Field Zusammenfassung mehrerer Felder, die vertikal oder horizontal ausgerichtet sind. Relative Position bleibt bestehen • Matrix Field Zusammenfassung mehrerer Felder in Matrix-Form. Relative Position bleibt bestehen. • Viewport Subwindow für ein großes Feld mit horizontalem und vertikalen Scrollbar. Normalerweise für Images und Subforms. • Flexible Form Zusammenfassung mehrerer Felder in flexiblen Grenzen, d.h. die Flexible Form paßt sich bei Verschiebung einzelner ihrer Felder in der Größe an. Anwendung nur während des Editierens! • Table Field Stellt Active Fields organisiert in Zeilen und Spalten dar. Jede Spalte ist ein Stack identischer aktiver Felder. Man kann die Anzahl der dargestellten Zeilen spezifizieren. Optionaler vertikaler Scrollbar erlaubt das Scrollen. Z.B. Tabelleninhalt in Zeilen und Spalten, vertikal oder horizontal. 4.5 Menu-Editor Klassische Pull-Down Menüs. Zugang über Frame/Menu... Menu Frames besitzen bereits ein Default Menü, Dialog Frames können noch eines erhalten. Komponenten: • Button Anklicken löst Aktion aus • Toggle Ein-/Ausschalter • List Auswahl aus Liste • Slide-Off Menu Menüpunkt zur Einleitung eines Submenüs Skript zur Vorlesung Datenbanken VertiefungSeite 37 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Menu-Separator Optische Abgrenzung Button, Toggle und List sind mit dem Click-Event verknüpft. Eigenschaften (teilweise kommen spezielle hinzu): • Variable Name • Script • Label Text • Speed Key (vordefinierte oder benutzerdefinierte Tasten, z.B. Funktionstasten), nur für Buttons und Toggles • Focus Behaviour • Bias • Status Text Biases für Menü-Elemente: • Enabled • Disabled • Invisible 4.6 Scripts 4.6.1 Ereignisgesteuerte Programmierung Auch „event-based“. Ein Event ist ein Ereignis, das die Ausführung von Code anstoßen (triggern) kann. Der Code ist eine Sequenz von Windows4GL-Kommandos. Syntax eines Event Blocks: ON event1 [variablename1] {, ON event2 [variablename2]} = [DECLARE localvariablelist [ENDDECLARE]] BEGIN statementlist END; Typische anwender-initiierte Events: • Maus-Klick auf Button oder Feld • Selektion eines Menüs • Betreten, Verlassen eines Feldes • Ändern von Feldinhalten • Öffnen und Schließen eines Frames Außerdem gibt es programmierte Events. 4.6.2 Scripts Ein oder mehrere Eventblöcke. Scripts können zugeordnet werden: Skript zur Vorlesung Datenbanken VertiefungSeite 38 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Frame • Field • Menu Item Frame-Script Syntax: [INITIALIZE [([parameterlist])] = [DECLARE [localvariablelist] [localprocedureforwardreference] [ENDDECLARE]] [BEGIN statementlist END[;]] {eventblock[;]} {localprocedure [;]} Beispiel Frame Script: /* Initialisiere das Frame */ INITIALIZE ( id = integer not NULL, name = varchar(30) not NULL, ) = DECLARE i = integer ENDDECLARE BEGIN i = 0; END; ON CLICK clear_button = BEGIN id = 0; name = ''; END; Field und Menu Item Script Syntax: [INITIALIZE = [DECLARE [localvariablelist] [localprocedureforwardreference] [ENDDECLARE]] Skript zur Vorlesung Datenbanken VertiefungSeite 39 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 [BEGIN statementlist END[;]] {eventblock[;]} {localprocedure [;]} dabei ist eventblock ON event1 {, ON event2} = [DECLARE localvariablelist [ENDDECLARE]] BEGIN statementlist END[;] Beispiel Field Script für „close_button“: /* Beende die Applikation */ ON CLICK = BEGIN return; END; Beispiel Menu Item Script für „menu.options_menu.help_menu“: /* Zeigt Benutzerhilfe an */ ON CLICK = BEGIN message 'Geben Sie in beide Felder Werte ein ' + 'und wählen Sie eine Rechenoperation'; END; Initialize Blöcke: • In Frame Script vor denen der Field Scripts ausgeführt • Kommandoausführung vor dem Maskenaufbau • Enthält Deklaration der Parameterliste, die beim Aufruf übergeben werden kann • Parameter könne wie lokale Variablen benutzt werden Declare Block: • Lokale Variablen • Vorwärts-Referenzen für lokale Prozeduren (Prozedurköpfe) User Class Script Syntax [INITIALIZE = DECLARE localprocedureforwardreference [ENDDECLARE] Skript zur Vorlesung Datenbanken VertiefungSeite 40 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 {METHOD methodname [([parameterlist])] = [DECLARE localvariablelist [ENDDECLARE]] BEGIN statementlist END[;]} {localprocedure [;]} 4.6.3 Windows4GL-Basissprachelemente Verzweigung: IF status = 1 THEN 4GLstatement1; 4GLstatement2; ELSEIF status = 2 THEN <4GL statements> ELSEIF status = 3 THEN IF value = 0 THEN <4GL statements> ELSE <4GL statements> ENDIF ELSE <4GL statements> ENDIF; FOR-Schleife: FOR i = startexpr TO | DOWNTO endexpr DO statement; {statement;} ENDFOR; Der Ablauf einer ENDLOOP CONTINUE RESUME RETURN For-Loop kann mittels /* Abschluß der Schleife */ /* Zum Beginn des nächsten Durchlaufs */ /* Beendet Schleife und Event Block */ /* Current Frame/Procedure/Method wird beendet */ geändert werden. Skript zur Vorlesung Datenbanken VertiefungSeite 41 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 While-Schleife: WHILE bed1 DO <4GL statements> WHILE bed2 DO IF bed3 THEN CONTINUE; ENDIF; <4GL statements> IF bed4 THEN ENDLOOP; ENDIF; ENDWHILE; ENDWHILE; Zuweisungen und Ausdrücke: ergebnis = feld2 * (feld3 + feld4); bereich = pi * r ** 2; Kunde.Name = 'Müller'; Kundenliste[4].Adresse = 'Landstr. 8'; IF gehalt IS NULL THEN ... ; IF persnr > 0 AND status != 2 THEN ... ; IF name LIKE '%a' THEN ... ; IF name NOT LIKE '%\[abd\]' ESCAPE '\' THEN ... ; Modale Popup-Messagebox: MESSAGE 'Geben Sie bitte in beide Felder Werte ein!'; MESSAGE 'Der Wert ist: ' + Varchar(feld1); Modale Eingabeaufforderung: antwort = PROMPT 'Geben Sie bitte Ihren Namen ein'; antwort muß dabei eine Varchar-Variable sein. Fenster hat Ok und Cancel Buttons. Cancel verändert Variable nicht. Rücksprung: Aufruf von RETURN kehrt zum Vaterframe zurück, EXIT beendet die laufende Anwendung. 4.7 Objekte, Klassen, Variablen, Konstanten Jede Komponente einer Windows4GL-Applikation ist ein Objekt. Stellen Methoden zur Verfügung, haben Eigenschaften. System Classes sind vordefimniert. User Classes sind benutzerdefinierbare Klassen mit Attributen und Methoden. Skript zur Vorlesung Datenbanken VertiefungSeite 42 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 4.7.1 Variablen Feld Objekte sind mit zwei Variablen verbunden: • Die Datenvariable − Speichert die angezeigten Daten − Wird beim Anlegen des Feldes automatisch erzeugt − Beispiel: EntryField mit Namen celsius IF celsius > 100 THEN ... • Die Referenzvariable − Zeigt auf das Feld-Objekt − Erlaubt die Manipulation der Objekteigenschaften (Größe, Farbe, ...) − Zum Zugriff auf diese Referenzvariable muß man die field-Funktion benutzen. Beispiel: FIELD(celsius).BgColor = CC_RED − Schlüsselwort FIELD gib Zugang zu Attributen und Methoden des Objektes • Nur wenn die Referenzvariable auf ein Feld- oder Menu Item zeigt (die mit einem Datenwert verknüpft sein können), ist zum Zugang zu den Attributen und Methoden die field-Funktion notwendig, bei Referenzvariablen auf sonstige Objekte nicht! • Beispiele: CurFrame.TopForm.SetToDefault(); FIELD(ende_btn).SetAttribute(BgColor = CC_RED); mitarbeiter_array.Clear(); letzte = mitarbeiter_array.LastRow(); FIELD(optfld).ValueList.ChoiceItems[i].EnumText = name; 4.7.2 System Classes System Classes sind vordefinierte Windows4GL-Klassen mit eingebundenen Attributen und Methoden. Bilden eine Klassenhierarchie (Verallgemeinerung - Spezialisierung). Enthalten alle Funktionalität, um eine GUI-basierte RDBMS-Anwendung zu entwickeln. Beispiel: Die System Class EntryField: Typische Methoden und Attribute von Eingabefeldern: Einige Attribute: • ForceCase Umwandlung groß/klein • IsMultiLine einzeilig/mehrzeilig • TypeFace Schrifttyp • IsItalic kursiv Skript zur Vorlesung Datenbanken VertiefungSeite 43 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Einige Methoden: • MarkAllText() gesamten Text markieren • UnmarkAllText() Markierung aufheben • MarkSubText() Teil des Textes markieren Vererbung aus der Klassenhierarchie: Attribute der Klasse EntryField: Attribut Datentyp Beschreibung geerbt von BgColor integer Hintergrundfarbe FieldObject ForceCase smallint Umwandlung groß/klein neu IsMultiLine smallint einzeilig/mehrzeilig neu OutlineColor integer Farbe der Umrandung ActiveField Script StringObject 4GL-Script des Feldes FieldObject 4.7.3 User Classes Definiert mit dem User Class Editor. Quasi RECORDs wie in Pascal. Definition von Attributen (inkl. visibility), Methoden und des Scripts für alle Methoden. 4.7.4 Variablen Typen Einfache Variablen • Enthalten nur einen Wert • Jeder der Basistypen (integer, varchar, ...) • Name referenziert Wert • Beispiele: person = 'Smith'; Einzelne Felder von Referenzvariablen (s.u.) können wieder einfache Variablen sein: a.city ar[i].city • Deklaration: name = datatype [WITH NULL | NOT NULL] (WITH NULL ist default); z.B.: i = integer; Referenz Variablen • Zeiger auf ein Objekt einer gegebenen Klasse • Ohne Objekt hat die Referenz Variable den Wert NULL • Zur Manipulation von Attributen und Aufruf von Methoden • Zugriff auf Feld- und Menü-Objekte über FIELD-Funktion Skript zur Vorlesung Datenbanken VertiefungSeite 44 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Als Parameter können auch Objekte (d.h. deren Ref. Var.) übergeben werden • Mehrere Referenz Variablen können auf dasselbe Objekt zeigen • Beispiele für FIELD-Funktion (es sei ein Slider Field unter dem Namen temperature in dem Frame definiert): i = integer; j = integer; fld = sliderfield; IF (temperature > 100) THEN FIELD(temperature).BgColor = CC_RED; i = FIELD(temperature).MinValue + 10; j = FIELD(temperature).MaxValue; ENDIF; Dasselbe läßt sich auch folgendermaßen errreichen: IF (temperature > 100) THEN FIELD(temperature).SetAttribute(BgColor = CC_RED); FIELD(temperature).GetAttribute (MinValue=ByRef(i), MaxValue=ByRef(j)); i = i + 10; ENDIF; Oder als dritte Möglichkeit: IF (temperature > 100) THEN fld = FIELD(temperature); fld.BgColor = CC_RED; i = fld.MinValue + 10; j = fld.MaxValue; ENDIF; Dynamische Arrays • Eine mit Namen versehene Menge von beliebig vielen Zeilen • Alle Zeilen sind Referenz Variablen, die auf Objekte derselben Klasse zeigen • Automatische Anpassung der Anzahl beim Hinzufügen und Löschen Man unterscheidet lokale und globale Variablen. Lokale Variablen • werden implizit beim Erstellen von Objekten angelegt • können explizit im INITIALIZE-Block, im Kopf einer Prozedur oder im DECLARE-Block angelegt werden • sind dem Endanwender verborgen. Globale Variablen • werden im Global Variable Editor definiert • sind global in der gesamten Applikation verfügbar Skript zur Vorlesung Datenbanken VertiefungSeite 45 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • sind nicht mit Feldern oder Menü-Komponenten verbunden • werden nicht dem Endanwender angezeigt • können von jedem Objekttyp sein. 4.7.5 Konstanten Man unterscheidet System und benutzerdefinierte Konstanten. System Konstanten • sind vordefiniert • werden zum Setzen und Abfragen von Attributen benutzt • Beispiele: FALSE TRUE CC_BLUE CC_LIGHT_GREEN CC_RED LW_THIN LW_THICK LW_VERYTHICK FIELD(celsius).BgColor = CC_RED; IF (FIELD(ef).IsItalic = TRUE) THEN ... /* setzen */ /* prüfen */ Benutzerdefinierte Konstanten • Definiert im Constant Editor • Einer von drei Datentypen: integer, float oder varchar • Nicht im Script änderbar • Immer global 4.8 Vernüpfung mit Datenbank Vier primäre Kommandos zum Zugriff auf eine Datenbank: • SELECT, • UPDATE, • INSERT und • DELETE. Weitere unterstützende SQL-Befehle: • COMMIT, • ROLLBACK, • INQUIRE_SQL, Direkte reine SQL-Befehle über • EXECUTE IMMEDIATE (stehen in VarChar-Variablen) Cursor-Befehle • OPEN • FETCH • CLOSE Skript zur Vorlesung Datenbanken VertiefungSeite 46 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Verwendung von DataStream Objekten. Zwei Subklassen: • SQLSelect • QueryObject 4.8.1 SELECT-Befehl Übergabe der selektierten Daten an einfache Variable, Referenz Variable oder Array Variable. Syntax: [REPEATED] subselect {UNION [ALL] subselect} [ORDER BY orderspecification {, orderspecification}] [BEGIN statementlist END;] mit subselect als: SELECT [ALL|DISTINCT] resultexpression {, resultexpression} FROM tablename [corrname] {, tablename [corrname]} [WHERE searchcondition] [GROUP BY columnname {, columnname}] [HAVING searchcondition] Zwei Arten des Selects: • Singleton Select: Lies ein Tupel • Select Loop: Lies mehrere Tupel in eine Ergebnis Tabelle Das Schlüsselwort distinct verhindert, daß dasselbe Tupel mehrfach in der Ergebnis Tabelle erscheint. Das Schlüsselwort repeated sagt der Datenbank-Engine, daß sie den Query Execution Plan aufbewahrt, so daß eine erneute, gleiche Abfrage in derselben Applikation schneller bearbeitet werden kann. resultexpression hat eine der beiden folgenden Formen: :simple_variable = dbexpression oder dbexpression AS :simple_variable 4.8.1.1 Singleton Select Nur eine Reihe wird selektiert. (Variablen bekommen einen „:“ vorgestellt): Skript zur Vorlesung Datenbanken VertiefungSeite 47 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ON exit acctno_form.acctno = BEGIN /* Hole die Information des ausgewählten Kunden */ SELECT :checkout_form.cname = cname, :checkout_form.acctno = acctno, Kann in :checkout_form.cphone = cphone VarChar stehen FROM customer WHERE acctno = :acctno_form.acctno; COMMIT; END; Gibt ein SELECT mehr als einen Wert zurück, so muß man entweder eine Select Loop anwenden oder einen Cursor benutzen. 4.8.1.2 Select Loop Annahme: Referenz Variable client mit den Feldern name, address und phone ist definiert. Zunächst wieder der Singleton Select: name_var = 'Maier'; ... SELECT :client.name = cname, :client.address = caddr, :client.phone = cphone FROM customer WHERE cname = :name_var; COMMIT; Nehmen wir nun an, wir haben ein Array von client Referenz Variablen: INITIALIZE = DECLARE client = ARRAY OF client_class, i = INTEGER ENDDECLARE ON CLICK selectButton = BEGIN client.Clear(); i = 1; SELECT :client[i].name = cname, :client[i].address = caddr, :client[i].phone = cphone FROM customer BEGIN i = i + 1; END; COMMIT; END; Dies ist eine Select Loop. Alle selektierten Reihen werden in das Array geschrieben. Skript zur Vorlesung Datenbanken VertiefungSeite 48 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 4.8.2 Delete Syntax: [REPEATED] DELETE FROM tablename [corrname] [WHERE searchcondition] Beispiel: DELETE FROM mitarbeiter WHERE persnr = :persnr_efd; 4.8.3 Insert Syntax: [REPEATED] INSERT INTO tablename [(columnname {, columnname})] VALUES (expression {, expression}) | subselect Beispiel: INSERT INTO mitarbeiter (PersNr, Name) VALUES (:KeyI + 1, :name_efd); COMMIT; Insert aus einem TableView: i = CheckOutForm.CheckOut.FirstRow(); WHILE i <= CheckOutForm.CheckOut.LastRow() DO ... IF CheckOutForm.CheckOut[i]._RowState = RS_NEW THEN INSERT INTO v_checkout (acctno, vid_no, tape_no, date_out, date_in, price) VALUES ( :CheckOutForm.acctno, :CheckOutForm.CheckOut[i].vid_no, :CheckOutForm.CheckOut[i].tape_no, :CheckOutForm.CheckOut[i].date_out, :CheckOutForm.CheckOut[i].date_in, :CheckOutForm.CheckOut[i].price); ENDIF; ... i = i + 1; ENDWHILE; COMMIT; 4.8.4 Update Syntax: [REPEATED] UPDATE tablename [corrname] [FROM tablename [corrname] {, tablename [corrname]}] SET columnname = dbexpr {, columnname = dbexpr} [WHERE searchcondition] Beispiel: UPDATE mitarbeiter SET gehalt = gehalt * :erhoeh_efd WHERE name = :name_efd; Skript zur Vorlesung Datenbanken VertiefungSeite 49 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Bedingung kann dynamisch sein: bed = 'abteilung LIKE ''' + :abt_efd + '%'''; UPDATE mitarbeiter SET gehalt = gehalt * :erhoeh_efd WHERE :bed; 4.8.5 Cursor Hierbei werden die Daten vom Backend zum Frontend in kleineren Paketen transportiert: Sequentielle Verarbeitung. Die Performance ist dabei schlechter als bei der Select Loop, die Syntax ist komplexer, aber die Möglichkeiten der Gestaltung sind mfangreicher. Beispiel: /* Beispiel für ein CURSOR SELECT Achtung: Keine vollständige Fehlerbehandlung! */ /* Deklaration einer Referenzvariable der Klasse CursorObject */ INITIALIZE () = DECLARE tc = CursorObject ENDDECLARE ON CLICK auswahl = BEGIN IF tc.State != CS_CLOSED THEN CLOSE tc; ENDIF /* Öffne Cursor OPEN tc FOR SELECT persnr, name, geburtstag, chef, gehalt, kinder FROM mitarbeiter WHERE abteilung = :abteilung AND region = :region FOR UPDATE OF name, chef, kinder; CurFrame.SendUserEvent(eventname = 'Next'); END; */ Skript zur Vorlesung Datenbanken VertiefungSeite 50 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ON CLICK weiter, ON USEREVENT 'Next' = BEGIN FETCH tc INTO :sf.persnr :sf.name :sf.gehalt :sf.chef :sf.kinder :sf.geburtstag = = = = = = persnr, name, gehalt, chef, kinder, geburtstag; IF tc.State = CS_NO_MORE_ROWS THEN MESSAGE 'Keine weiteren Sätze'; ENDIF; END; Nach jedem Fetch werden die Attribute • State • RowCount (Zahl der insgesamt bisher fetched Rows) des Cursor-Objektes sowie • iirowcount (1 bei erfolgreichem Fetch, 0 sonst) und • iierrrornumber (z.B. es stimmen die Variablen nicht mit den Spalten überein) gesetzt. ON CLICK aendern = BEGIN UPDATE mitarbeiter SET name = :sf.name, chef = :sf.chef, kinder = :sf.kinder WHERE CURRENT OF tc; CurFrame.SendUserEvent(eventname = 'Next'); END; ON CLICK loeschen = BEGIN DELETE FROM mitarbeiter WHERE CURRENT OF tc; CurFrame.SendUserEvent(eventname = 'Next'); END; Skript zur Vorlesung Datenbanken VertiefungSeite 51 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ON CLICK speichern = BEGIN IF tc.State != CS_CLOSED THEN CLOSE tc; COMMIT; ENDIF; CurFrame.TopForm.SetToDefault(); END; ON CLICK ende_btn = BEGIN IF tc.State = CS_OPEN THEN CLOSE tc; ROLLBACK WORK; ENDIF; RETURN; END; 4.8.6 Zugriff mit Hilfe von DataStream Objekten Flexible Möglichkeiten: • Dynamische Änderungen zur Abfrage • Vorwärts- und Rückwärts-Bewegung in der Ergebnismenge ist möglich. Zwei Unterklassen: • SQLSelect: nur Lesen; Benutzer schreibt Select-Befehl • QueryObject: auch Add, Update, Delete; System generiert Select-Befehl In allen Fällen sind die folgenden Schritte nötig: • Erzeuge die SQLSelect oder QueryObject Instanz • Spezifiziere die Abfrage (setze die Attribute des QueryObject entsprechend) • Spezifiziere die Ziel-Felder oder -Variablen • Rufe die Open-Methode auf, um die Daten abzufragen • Rufe andere Methoden auf, um die Daten anzuzeigen • Rufe die Close-Methode auf. Es gibt 4 Beispiel-Frames in der Videos Anwendung (w4gldemo Datenbank): • customer_browse und customer_browse2: SQLSelect Objekt • customer_maintenance2: QueryObject Objekt, Änderungen erlaubt • DynamicQuery: Dynamischer Aufbau eines QueryObject 4.8.6.1 Open Methode Hierbei werden die Daten bereits selektiert. Es gibt 4 unterschiedliche Modes: Skript zur Vorlesung Datenbanken VertiefungSeite 52 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Mode QY_CACHE QY_CURSOR QY_DIRECT QY_ARRAY Beschreibung Daten werden in einer Datei „gecached“. Rückwärtsnavigation und wahlfreier Zugriff sind möglich Verwendet einen DBMS Cursor Analog zu einer Select Loop Daten werden direkt in ein Array oder Table Field geladen Im letzten Fall muß die Array-Klasse dieselben Attributnamen haben wie die Spalten in der Datenbank-Anfrage. 4.8.6.2 SQLSelect Zunächst muß das SQLSelect Objekt deklariert und instanziiert werden. dann wird der Select-String aufgebaut. INITIALIZE () = DECLARE ss = SQLSelect; /* Direkt instanziiert */ selectstring = VarChar(200) NOT NULL; wClause = VarChar(60) NOT NULL; ... ENDDECLARE BEGIN selectstring = 'SELECT acctno, cphone, cname, caddr,' + ' ccity, cstate, czip, cdistrict,' + ' cstatus, cacctbal FROM v_customer'; Als nächstes muß für jedes selektierte Feld ein Ziel-Feld oder eine Zielvariable angegeben werden: ss.Columns[1].Targets[1].Expression = 'customer.acctno'; ss.Columns[2].Targets[1].Expression = 'customer.cphone'; ss.Columns[3].Targets[1].Expression = 'customer.cname'; ss.Columns[4].Targets[1].Expression = 'customer.caddr'; ... FOR i = 1 TO ss.Columns.LastRow DO ss.Columns[i].Targets[1].IsSelectTarget = TRUE; ENDFOR; END; Das Attribut IsSelectTarget muß für jedes Ziel angegeben werden! Wird ein Attribut der Datenbanktabelle in mehreren Zielen dargestellt, so wird der Index von Targets inkrementiert. Bisher ist die Where-Klausel noch nicht spezifiziert. Wir betrachten zwei Fälle: Abfrage nur eines bestimmten Satzes: Skript zur Vorlesung Datenbanken VertiefungSeite 53 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 reply.Value = ''; status = CurFrame.ReplyPopup( messagetext = 'Gib Kundennumer:', reply = reply); IF status != PU_OK THEN RESUME; ENDIF; wclause = ' WHERE acctno = ' + reply.Value; Abfrage aller Sätze mit bestimmten Status, sortiert nach Kundenname: wclause = ' WHERE cstatus = 0 ORDER BY cname'; Nun muß die gesamte Query dem richtigen Attribut des SQLSelect Objektes zugewiesen werden: ss.Query.Value = selectstring + wclause; Die Abfrage wird aber erst ausgeführt, wenn die Open Methode aufgerufen wird. Wird das SQLSelect Objekt im QY_ARRAY Modus geöffnet, so erscheinen die Werte der Anfrage automatisch in den Ziel-Feldern/Variablen. In allen anderen Modi stehen die beiden folgenden Methoden zur Verrügung: • NextRow lädt die Daten Reihe für Reihe von der datenbank in den internen Puffer • Load lädt die Daten aus dem internen Puffer in die Ziel-Felder/Variablen Parameter der Open Methode: Parameter Typ Default Beschreibung QueryMode integer keiner QY_ARRAY, QY_CACHE, QY_CURCOR oder QY_DIRECT CheckCols integer FALSE TRUE: Überprüfe Query auf existierende Spalten IsRepeated integer FALSE TRUE: Erzeuge eine Repeat Query MaxRows integer 0 max. Anzahl zu füllender Array-Elemente (nur in QY_ARRAY Mode) Scope Scope akt. Scope Scope für die Auswertung der Ausdrücke Attribut in dem Columns array Targets Attribut Alternativ kann das Scope-Attribut des SQLSelect Objektes auf das des aktuellen Frames gesetzt werden: ss.Scope = CurFrame.Scope; IF ss.Scope != CurFrame.Scope THEN /* Ist es wirklich */ ... /* Fehler */ /* gesetzt? */ ENDIF; Nun öffnen wir die Verbindung zur Datenbank mit der Open Methode (hier wird Scope als Parameter übergeben): Skript zur Vorlesung Datenbanken VertiefungSeite 54 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 status = ss.Open(querymode = QY_CACHE, checkcols = TRUE, Scope = CurFrame.Scope); IF status != ER_OK THEN ROLLBACK; MESSAGE 'SQLSelect error at open'; RESUME; ENDIF; COMMIT; IF ss.ErrorNo != 0 AND ss.IsDBError = TRUE THEN /* Es gab einen DB Fehler */ ROLLBACK; MESSAGE 'DBMS Fehler beim Abfragen von ...'; RESUME; ELSEIF ss.MaxRow = 0 THEN /* Keine Reihen gefunden */ ... ELSEIF ss.MaxRow > 1 THEN /* Enable Next-Button */ FIELD(NextBtn).CurBias = FB_CHANGEABLE; ENDIF; status = ss.NextRow(); /* Lade erste Reihe */ status = ss.Load(); /* Lades sie in Target */ END; Navigieren innerhalb der Ergebnismenge: ON CLICK NextBtn = BEGIN ss.NextRow(); ss.Load(); IF ss.CurRow = ss.MaxRow THEN FIELD(NextBtn).CurBias = FB_DIMMED; ENDIF; FIELD(PrevBtn).CurBias = FB_CHANGEABLE; END; ON CLICK PrevBtn = BEGIN ss.PrevRow(); ss.Load(); IF ss.CurRow = 1 THEN FIELD(PrevBtn).CurBias = FB_DIMMED; ENDIF; FIELD(NextBtn).CurBias = FB_CHANGEABLE; END; Wahlfreier Zugriff: ss.FetchRow(rowindex = 4); Erster Satz: Skript zur Vorlesung Datenbanken VertiefungSeite 55 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ss.FetchRow(rowindex = 1); Schließen des SQLSelect Objektes: IF ss.State = QS_ACTIVE THEN ss.Close(); ENDIF; Man kann die Queries noch weiter parametrisieren: S. CA-OpenRoad Programming Guide! 4.8.6.3 QueryObject Erlauben es auch, an den Inhalten der Datenbank Änderungen vorzunehmen. Das QueryObject erlaubt es jedoch nicht, eine Query wie bei SQLSelect zu formulieren (QueryObject.Query.Value ist readonly). Stattdessen muß man eine anzahl von Attributen setzen, auf deren Basis dann das QueryObject das Query Statement selbst erzeugt. Die folgenden System Classes sind dabei involviert: • DataStream • QueryCol • QueryParm • SQLSelect Weiterhin • QueryObject • QueryTable Auch die Behandlung der Target Felder erfordert mehr Attribute: Attribut Default Beschreibung Expression keiner Enthält Namen des Target-Feldes, z.B. „feld1“ oder „tbl[i].col1“ IsDBHandleField FALSE Nur für String und Bitmap Objekte IsInsertTarget FALSE TRUE, wenn Spalte Target für Insert ist IsSelectTarget FALSE TRUE, wenn Spalte Target für Select ist IsUpdateTarget FALSE TRUE, wenn Spalte Target für Update ist IsDeleteWhere FALSE TRUE, wenn Spalte in Where-Klausel für Delete verwendet wird IsUpdateWhere FALSE TRUE, wenn Spalte in Where-Klausel für Update verwendet wird Der Gang der Handlung am Beispiel: • Parameter zum Aufbau der SELECT Klausel setzen BEGIN qo.Tables[1].TableName = 'v_customer'; Skript zur Vorlesung Datenbanken VertiefungSeite 56 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 qo.Columns[1].ColumnName = 'acctno'; qo.Columns[2].ColumnName = 'cphone'; qo.Columns[3].ColumnName = 'cname'; qo.Columns[4].ColumnName = 'caddr'; qo.Columns[5].ColumnName = 'ccity'; qo.Columns[6].ColumnName = 'cstate'; qo.Columns[7].ColumnName = 'czip'; qo.Columns[8].ColumnName = 'cdistrict'; qo.Columns[9].ColumnName = 'cstatus'; qo.Columns[10].ColumnName = 'cacctbal'; FOR i = 1 TO qo.Columns.LastRow DO qo.Columns[i].Targets[1].Expression = 'customer.' + qo.Columns[i].ColumnName; qo.Columns[i].Targets[1].IsSelectTarget = TRUE; qo.Columns[i].Targets[1].IsUpdateTarget = TRUE; qo.Columns[i].FromTable = qo.Tables[1]; ENDFOR; • Die Spalte acctno ist Primärschlüssel der Tabelle v_customer. Daher soll sie nicht änderbar sein. Andererseits ist sie die zuständige Spalte für die Where-Klausel bei Updates und Deletes. qo.Columns[1].Targets[1].IsUpdateTarget = FALSE; qo.Columns[1].Targets[1].IsUpdateWhere = TRUE; qo.Columns[1].Targets[1].IsDeleteWhere = TRUE; • Im nächsten Schritt wird die Order By Klausel gesetzt (analog geht man für Group By vor): qo.Columns[3].OrderBy = 1; qo.Columns[3].AsName = 'cname'; END; Die Navigation in dieser Anwendung erfolgt über ein Menü (man kann das analog auch mit Buttons machen). • Zunächst wird das QueryObject geschlossen, falls es noch offen war, und die Menü-Einträge werden disabled: IF qo.State = QS_ACTIVE THEN qo.Close(); FIELD(menu.get_menu.next_account).CurBias = MB_DISABLED; FIELD(menu.get_menu.prev_account).CurBias = MB_DISABLED; ENDIF; • Mit dem folgenden Stück Code wird (z.B. vor dem Schließen eines Frames) geprüft, ob an einem Feld eine Änderung vorgenommen wurde: Skript zur Vorlesung Datenbanken VertiefungSeite 57 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 IF CurFrame.TopForm.HasdataChanged = TRUE THEN status = CurFrame.ConfirmPopup( messagetext = 'Ein Feld wurde verändert ...' + 'Trotzdem fortfahren?'); IF status != PU_OK THEN RESUME; /* Brich Aktion ab */ ENDIF; ENDIF; • Aktionen, wie Insert, Update, Delete werden durch Buttons gesteuert. Bis die ersten Daten geladen wurden ist nur Insert möglich: FIELD(insert_button).CurBias = FB_CHANGEABLE; FIELD(update_button).CurBias = FB_DIMMED; FIELD(delete_button).CurBias = FB_DIMMED; • Weiterhin werden an dieser Stelle alle Felder gelöscht und der Cursor in das feld cname des Matrix-Feldes customer positioniert: CurFrame.TopForm.SetToDefault(); CurFrame.InputFocusField = FIELD(customer.cname); • Als nächstes wird in Abhängigkeit der Benutzerwünsche die WhereKlausel aufgebaut. Es gibt 4 Wahlmöglichkeiten: 1. Ein neuer Account soll eingegeben werden: Keine Where-Klausel. 2. Benutzer gibt einen bestimmten existierenden Account ein. 3. Alle Accounts sollen selektiert werden: Leere Where-Klausel 4. Nur Accounts mit cstatus=0. Der folgende Code leistet dies: IF wahl = 1 THEN RESUME; /* Nur Insert möglich */ ELSEIF wahl = 2 THEN reply.value =''; status = CurFrame.ReplyPopup( messagetext = 'Gib Account-Nr', reply = reply); IF status != PU_OK THEN RESUME; /* negative Antwort */ ELSE qo.RunTimeWhere.Value = 'acctno =' + reply.Value; error_msg = 'Kein Kunde ' + reply.Value + ' gefunden!'; ENDIF; ELSEIF wahl = 3 THEN qo.RunTimeWhere.Value = ''; /* Alle Reihen */ error_msg = 'Es gibt keine Kunde'; ELSE qo.RunTimeWhere.Value = 'cstatus = 0'; error_msg = 'Es gibt keine Kunde mit geschlossenen Accounts'; ENDIF; Skript zur Vorlesung Datenbanken VertiefungSeite 58 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Nach all diesen Vorbereitungen kann es nun losgehen. • Wir öffnen das QueryObject: status = qo.Open(querymode = QY_CACHE); IF status != ER_OK THEN MESSAGE 'Fehler beim Öffnen des QueryObjects'; RESUME; ENDIF; COMMIT; /* Multiuser-Fähigkeit ist gefährdet! */ • Anschließend werden jetzt mögliche Fehlerbedingungen des QueryObjects geprüft: IF qo.ErrorNo != 0 AND qo.IsDBError = TRUE THEN /* Ein DB Error ist aufgetreten */ MESSAGE 'DBMS Error ist aufgetreten'; ELSEIF qo.MaxRow = 0 THEN MESSAGE error_msg; /* Keine Reihen gefunden */ ELSE /* Wenigstens 1 Reihe erhalten */ status = qo.NextRow(); status = qo.Load(); FIELD(insert_button).CurBias = FB_DIMMED; FIELD(update_button).CurBias = FB_CHANGEABLE; FIELD(delete_button).CurBias = FB_CHANGEABLE; IF qo.MaxRow > 1 THEN /* Mehr als 1 Reihe */ FIELD(menu.get_menu.next_account).CurBias = MB_ENABLED; ENDIF; ENDIF; END; Update mit QueryObjects Event Block dafür: ON CLICK update_button = BEGIN status = qo.DBUpdate( ZeroRowsIsError = TRUE, MaxRows = 1); IF status != ER_OK OR qo.ErrorNo != 0 THEN /* Fehler */ ROLLBACK; MESSAGE 'DBMS Fehler ' + VarChar(qo.ErrorNo); ELSE qo.CommitToCache(); COMMIT; CurFrame.TopForm.HasDataChanged = FALSE; ENDIF; END; Skript zur Vorlesung Datenbanken VertiefungSeite 59 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Delete mit QueryObjects Der Event-Block dafür ist komplizierter, da man darauf reagieren muß, daß im Cache nun eine Reihe weniger ist (evtl. gar keine mehr). ON CLICK delete_button = BEGIN /* Lösche Kunden aus Datenbank */ status = qo.DBDelete(ZeroRowsIsError = TRUE, MaxRows = 1); IF status != ER_OK OR qo.ErrorNo != 0 THEN /* Fehler */ ROLLBACK; MESSAGE 'DBMS Fehler ' + VarChar(qo.ErrorNo) + ' beim Löschen!'; ELSE qo.CommitToCache(); /* Alles ok */ COMMIT; CurFrame.TopForm.HasDataChanged = FALSE; IF qo.MaxRow = 0 THEN MESSAGE 'Keine Daten mehr anzuzeigen!'; CurFrame.TopForm.SetToDefault(); /* Lösche Felder */ FIELD(update_button).CurBias = FB_DIMMED; FIELD(delete_button).CurBias = FB_ DIMMED; ELSE IF qo.CurRow > qo.MaxRow THEN qo.PrevRow(); ENDIF qo.Load(); IF qo.CurRow = qo.MaxRow THEN FIELD(menu.get_menu.next_account).CurBias = MB_DISABLED; ENDIF; IF qo.CurRow = 1 THEN FIELD(menu.get_menu.prev_account).CurBias = MB_DISABLED; ENDIF; ENDIF; /* Es gibt noch Daten */ ENDIF; /* Gelöscht o.k. */ END; Insert mit QueryObjects In dem Beispiel des customer_maintenance2 Frames wird nicht die DBInsert Methode benutzt, da der Benutzer den Insert-Button auch dann drücken kann, wenn noch kein QueryObject offen ist. Daher wird hier die normale SQL Insert-Anweisung benutzt: Skript zur Vorlesung Datenbanken VertiefungSeite 60 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ON CLICK insert_button BEGIN IF CurFrame.TopForm.HasDataChanged = FALSE THEN MESSAGE 'Bitte zuerst Daten eingeben!'; ELSE /* Erzeuge neue eindeutige Kunden-Nummer */ :custome.acctno = CurFrame.DBSession.SequenceValue( table_name = 'v_customer', Column_name = 'acctno'); INSERT INTO v_customer (acctno, ..., cacctbal) VALUES (:customer.acctno, ..., :customer.cacctbal); IF CurFrame.DBSession.ErrorNumber != 0 THEN error_msg = 'Kann keinen neuen Kunden eintragen! ' + dbms_error_message(); ROLLBACK; MESSAGE error_msg; ELSE COMMIT; CurFrame.TopForm.SetToDefault(); ENDIF; ENDIF; END; Hier wird die Prozedur dbms_error_message aufgerufen: PROCEDURE dbms_error_message () = BEGIN IF CurFrame.DBSession.ErrorNumber != 0 THEN RETURN 'Fehler Code ist ' + VarChar(CurFrame.DBSession.ErrorNumber) + ' '; ELSE RETURN ''; ENDIF; END; Schließen eines QueryObjects Hier kann zur Sicherheit abgefragt werden, ob auch alle in dem Frame geänderten Daten in die Datenbank geschrieben wurden: Skript zur Vorlesung Datenbanken VertiefungSeite 61 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ON CLICK close_button, ON WINDOWCLOSE = BEGIN IF CurFrame.TopForm.HasDataChanged = TRUE THEN status = CurFrame.ConfirmPopup(MESSAGETEXT = 'Alle geänderten Felder gehen verloren!'); ELSE status = PU_OK; ENDIF; IF status = PU_OK THEN qo.Close(); RETURN; /* Schließe das Frame */ ENDIF; END; 4.8.7 Versionskontrolle eines Tabellentupels mit Hilfe einer Rule Tabelle MeineTabelle habe die Spalten s1, ..., s12 und zusätzlich die Spalte version vom Typ Integer. Dabei sei s1 der Primärschlüssel. Die folgende Rule sorgt dafür, daß bei jedem Update der „normalen“ Tabellenspalten die Versionsnummer hochgezählt wird: CREATE RULE MeineTabelleVersion AFTER UPDATE(s1,...,s12) OF MeineTabelle EXECUTE PROCEDURE AendereVersion(key = old.s1); Die zugehörige Datenbank-Prozedur (muß vorher definiert worden sein; alle Benutzer brauchen grant): CREATE PROCEDURE AendereVersion (key VarChar(10) NOT NULL) AS BEGIN UPDATE MeineTabelle SET version = version + 1 WHERE s1 = :key; END; Der folgende Update basiert auf dem gemerkten Schlüssel und der gemerkten alten Versionsnummer: UPDATE MeineTabelle SET s2 = ..., ... SET s12 = ... WHERE (s1 = :oldKey) AND (version = :oldVersion); COMMIT; Hat sich die Versionsnummer geändert, wird das Tupel nicht mehr gefunden: iirowcount ist 0. Skript zur Vorlesung Datenbanken VertiefungSeite 62 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 4.9 Arrays und Table Fields Ein Table Field ermöglicht es, den Inhalt eines Arrays dem Benutzer darzustellen. Es ist ein composite field, welches aus active fields arrangiert in Reihen und Spalten besteht. Jede Spalte stellt ein Attribut der mit dem Array assoziierten Klasse dar. Jede Zeile des Table Fields entspricht einer Zeile des Arrays. Ein Table Field kann unter Umständen nur einen Subset der Attribute des Arrays darstellen, jedoch nicht mehr. Table Fields erscheinen per Default als Tabelle und besitzen Tabellen- und Spalten-Titel, Control Buttons und Scroll Bars. Arrays können auch ohne zugehöriges Table Field benutzt werden. 4.9.1 Arrays Ein Array ist eine mit Namen versehene Menge von Zeilen, wobei jede Zeile eine Referenzvariable auf ein Objekt einer System oder User Klasse ist. Die Attribute dieser Klasse stellen die Felder/Spalten des Arrays dar. Deklaration: INITIALIZE ( i_arr = ARRAY OF IntegerObject ) = ... INITIALIZE () = DECLARE mit_arr = ARRAY OF mit_class ENDDECLARE ... Zugriff auf Arrays: • Komplettes Objekt: Arrayname • Eine bestimmte Zeile: Arrayname[Zeilennummer] • Eine bestimmte Spalte: Arrayname[*].Spaltenname • Ein bestimmtes Attribut einer Zeile: Arrayname[Zeilennummer].Spaltenname • Anwenden von Methoden: Arrayname.Methode() Beispiele: arr2 = arr1; arr2 = arr1.Duplicate(); zeile = arr[3]; arr[3].name = name_efd; arr4[3].arr5[1].wert = 88; arr.Clear(); Zeilenindex • Zeilen werden beim Laden sequentiell ab 1 aufsteigend numeriert („Leerzeilen“ nicht möglich) Skript zur Vorlesung Datenbanken VertiefungSeite 63 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Gelöschte Zeilen werden an den Anfang gestellt und ab 0 rückwärts numeriert (0, -1, -2, ...) Laden eines Arrays: ... i = 1; arr.Clear(); SELECT :arr[i].name = name, :arr[i].abteilung = abteilung FROM mitarbeiter BEGIN i = i+1; END; ... Methoden für Arrays: ArrayObject.Clear(); /* Löscht alle Zeilen */ integer = ArrayObject.InsertRow( ROWNUMBER=integer, ROWOBJECT=Object ); integer = ArrayObject.RemoveRow( ROWNUMBER=integer ); integer = ArrayObject.SetRowDeleted( ROWNUMBER=integer ); integer = ArrayObject.Sort( attributname = order, ... ); Rückgabewerte der drei mittleren Methoden: ER_OK, ER_OUTOFRANGE, ER_ROWNOTFOUND Werte für order: AS_ASC, AS_DESC Attribute von Arrays: integer = ArrayObject.FirstRow; integer = ArrayObject.LastRow; integer = ArrayObject.AllRows; /* Anzahl Zeilen */ integer = ArrayObject._RowState; Mögliche Werte von _RowState: RS_CHANGED, RS_DELETED, RS_NEW, RS_UNCHANGED, RS_UNDEFINED Suche in einem Array: i = arr.FirstRow; WHILE ( i <= arr.LastRow ) DO IF arr[i].Spalte = Suchbedingung THEN ... ENDIF i = i + 1; ENDWHILE; Es gibt auch eine Find Methode! Skript zur Vorlesung Datenbanken VertiefungSeite 64 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 4.9.2 Table Fields Ein TableField besteht aus ActiveFields, die in Zeilen und Spalten angeordnet sind. • Jede Spalte ist ein Stack identischer ActiveFields • Jede Zeile hat dieselbe Struktur, ähnlich einer Reihe aus einer Datenbanktabelle ActiveFields können von jedem Typ (einer Spezialisierungs-Klasse) sein, meistens sind es EntryFields. Andere Möglichkeit wären ToggleFields. TableField ist eine Unterklasse von CompositeField. Die Zahl der sichtbaren Zeilen ist wählbar und zur Laufzeit änderbar. Anzeige über Scrollbars. Es gibt vertikale (Default) und horizontale TableFields Ein TabelField wird immer automatisch mit einem dynamischen Array verknüpft, über das auf die Daten im TableField zugegriffen werden kann. Objekte eines TableFields: TableField, ColumnField (eines für jede Spalte), CellAttribute (für jede Zelle, normalerweise abgeschaltet) TableField-Komponenten: Individual Column Titel Table Field Background Table Titel Table Header Control Button Prototype Field Individual Cell Table Body Column Field Beispiele für den Zugriff auf die Elemente des Table Field: • Table Titel (VarChar(256)) FIELD(tablefield).Title = title_string; • Table Header (StackField) Scrollbar Skript zur Vorlesung Datenbanken VertiefungSeite 65 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 FIELD(tablefield).TableHeader.attribute = value; • Individuelle Zelle FIELD(tablefield[n].columnname).attribute = value; Beispiele für Manipulation von Table Field Komponenten (Name des Table Fields sei tab): FIELD(tab).TableHeader.BgPattern = FP_SHADE; FIELD(tab).TableHeader.OutlineStyle = LS_DASH; FIELD(tab).Title='Mitarbeiter'; FIELD(tab).TitleTrim.BgColor = CC_PALE_BLUE; FIELD(tab[*].gehalt).Title='Rente'; FIELD(tab[*].gehalt).TitleTrim.BgColor = CC_RED FIELD(tab). ControlButton = NULL; FIELD(tab).TableBody.OutLineWidth = LW_THIN; FIELD(tab).NumVisibleRows = 8; FIELD(tab[*].gehalt).bgpattern = FP_SHADE; EntryField(FIELD(tab[*].gehalt).ProtoField).IsBold = TRUE; /* Alle Zeilen dieser Spalte ändern */ FIELD(tab[5].name).BgColor = CC_RED; /* CellAttributes must be On */ FIELD(tab).HasScrollBars = FALSE; Zugriff auf die Inhalte des Table Fields: • Alle Funktionalitäten von dynamischen Arrays • Quasi ein Fenster über dem dynamischen Array • Dieselbe Syntax Skript zur Vorlesung Datenbanken VertiefungSeite 66 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Zusätzliche Möglichkeiten: − Leere eckige Klammern: aktuelle Zeile sf = tab[]; − Attribute CurRow, ActiveRow und TopRow erlauben Zusammenhang zwischen Table Field und Array. Hole Array-Index der aktuellen Zeile: i = FIELD(tab).CurRow; /* wird beim Verlassen des TableFields NICHT geändert */ i = FIELD(tab).ActiveRow; /* wird beim Verlassen des TableFields auf 0 gesetzt */ Hole Index der ersten im Table Field sichtbaren Zeile: i = FIELD(tab).TopRow; /* Attribut ist jetzt auch schreibbar! */ − Über das Attribut _RowState kann der Zustand einer Zeile ermittelt werden: Symbolische Konstanten dafür: RS_UNDEFINED RS_NEW RS_UNCHAGED RS_CHANGED RS_DELETED Änderungen des _RowState durch Wertzuweisung zum Attribut möglich, normalerweise aber durch Benutzereingaben oder die Methode SetRowDeleted(). Unsichtbare Spalten im Table Field Wenn die Array-Klasse mehr Elemente hat, als das Table Field Spalten besitzt, so sind die überzähligen Spalten des Arrays effektiv verborgen. Weitere Beispiele: Selektiere eine bestimmte Zeile: ON CLICK menu_button.ChangeRow = BEGIN resp = prompt 'Enter the number of row you want to see'; status = custtable.SetInputFocus(row=int4(resp)); ... BEGIN Prüfe Frame Modus und ändere das Operations Menu Skript zur Vorlesung Datenbanken VertiefungSeite 67 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 INITIALIZE ( ... ) = BEGIN IF CurFrame.CurMode = FM_READ THEN FIELD(custtable).CurOps = OP_NONE; ELSEIF CurFrame.CurMode = FM_UPDATE THEN FIELD(custtable).CurOps = OP_APPEND; ENDIF; ... END; Formatierung einer mehrzeiligen Überschrift FIELD(movie[*].director).title = 'Director''s' + HC_NEWLINE + 'Name'; Eine bestimmte Spalte unsichtbar machen SELECT :user_name = DBMSInfo('username'), :dba_name = DBMSInfo('dba'); COMMIT; IF user_name != dba_name THEN FIELD(custtable[*].acctbal).CurBias = FB_INVISIBLE; Zwei Varianten für Einlesen in ein TableField und Updates aus einem TableField heraus: Prinzipiell kann diese Vorgehensweise hier nicht generell empfohlen werden. Das gezeigte Beispiel verwendet pessimistische Concurrency Control und ist deadlockgefährdet! Version mit User-Event: /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ Application: Frame: tablefield_update tableupdate This example displays two columns from the 'part' table of the database, and then allows the user to browse and edit the data. When the user selects the 'load' button, the data is loaded into the table field, and a new transaction is begun. When the user selects the 'update' button, the changes are applied to the 'part' table. This uses a UserClass called 'part_object' that contains three attributes: partno - the part number. short_desc - the short description. partno_save - the part number saved on the side (to use for updating). This is not displayed. This allows us to keep a 'hidden' column in the tablefield that will not be displayed, but contains the partno key, unchanged by the user. INITIALIZE ( Skript zur Vorlesung Datenbanken VertiefungSeite 68 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 i = integer /* Temporary counter */ ) = BEGIN CurFrame.SendUserEvent (eventname = 'load_data'); END; ON CLICK load_button, ON USEREVENT 'load_data' = BEGIN COMMIT; /* Start a new transaction */; parttable.Clear(); /* Clear out array */ i = 1; SELECT :parttable[i].partno = partno, :parttable[i].partno_save = partno, :parttable[i].short_desc = short_desc FROM part BEGIN i = i + 1; END; END; ON CLICK update_button = BEGIN i = parttable.FirstRow(); /* No error checking is done for simplicity of example */ WHILE i <= parttable.LastRow() DO IF parttable[i]._rowstate = RS_DELETED THEN REPEATED DELETE FROM part WHERE partno = :parttable[i].partno; ELSEIF parttable[i]._rowstate = RS_NEW THEN REPEATED INSERT INTO part (partno, short_desc) VALUES (:parttable[i].partno, :parttable[i].short_desc); ELSEIF parttable[i]._rowstate = RS_CHANGED THEN /* Does not allow direct update of key value */ REPEATED UPDATE part SET short_desc = :parttable[i].short_desc, partno = :parttable[i].partno WHERE partno = :parttable[i].partno_save ENDIF; i = i + 1; ENDWHILE; COMMIT; END; Version mit lokaler Prozedur: /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ Application: Frame: tablefield_update tableupdate This example displays two columns from the 'part' table of the database, and then allows the user to browse and edit the data. When the user selects the 'load' button, the data is loaded into the table field, and a new transaction is begun. When the user selects the 'update' button, the changes are applied to the 'part' table. This uses a UserClass called 'part_object' that contains three attributes: partno - the part number. short_desc - the short description. partno_save - the part number saved on the side (to use for updating). This is not displayed. This allows us to keep a 'hidden' column in the tablefield that will not be displayed, but contains the partno key, unchanged by the user. INITIALIZE Skript zur Vorlesung Datenbanken VertiefungSeite 69 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ( ) = DECLARE i = integer not null; load_data = procedure; ENDDECLARE BEGIN callproc load_data; END; /* Temporary counter */ /* Vorwärts Deköaration */ ON CLICK load_button = BEGIN callproc load_data; END; ON CLICK update_button = BEGIN /* No error checking is done for simplicity of example */ FOR i = parttable.FirstRow TO parttable.LastRow DO IF parttable[i]._rowstate = RS_DELETED THEN REPEATED DELETE FROM part WHERE partno = :parttable[i].partno; ELSEIF parttable[i]._rowstate = RS_NEW THEN REPEATED INSERT INTO part (partno, short_desc) VALUES (:parttable[i].partno, :parttable[i].short_desc); ELSEIF parttable[i]._rowstate = RS_CHANGED THEN /* Does not allow direct update of key value */ REPEATED UPDATE part SET short_desc = :parttable[i].short_desc, partno = :parttable[i].partno WHERE partno = :parttable[i].partno_save ENDIF; ENDFOR; COMMIT; END; PROCEDURE load_data() = DECLARE i = integer ENDDECLARE BEGIN COMMIT; /* Start a new transaction */; parttable.Clear(); /* Clear out array */ i = 1; SELECT :parttable[i].partno = partno, :parttable[i].partno_save = partno, :parttable[i].short_desc = short_desc FROM part BEGIN i = i + 1; END; END; 4.10 Frameaufrufe und Kommunikationstechniken Planung der Anwendung. Die grundsätzliche Struktur wird davon bestimmt, in welcher Reihenfolge und in welchem Modus Frames nacheinander geöffnet werden (z.B. immer nur ein aktives Frame oder mehrere aktive Frames). Z.B.: Ein Hauptmenü-Frame, aus dem dann die Unterfunktionen aufgerufen werden. Auf Menüleiste kann ganz verzichtet werden. Beispiel: „Videos Application“ aus w4gldemo. 4.10.1 Auswahl über ein Table Field Ausgangpunkt ist Dialogframe mit Open und Quit Buttons: In Tablefield steht Auswahlliste: Markieren, dann Open drücken. Skript zur Vorlesung Datenbanken VertiefungSeite 70 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Frame Script: INITIALIZE = BEGIN /* Fuelle Table Field mit Frame Namen und Auswahl Text */ SelTable[1].FrameName = 'video_list'; SelTable[1].Descr = 'Video List'; SelTable[2].FrameName = 'customer_maintenance'; SelTable[2].Descr = 'Customer Maintenance'; SelTable[3].FrameName = 'sales_barchart'; SelTable[3].Descr = 'Sales Charts'; SelTable[4].FrameName = 'check_out'; SelTable[4].Descr = 'Check Out/In Videos'; /* Passe Groesse der Tabelle an FIELD(SelTable).NumVisibleRows = SelTable.LastRow; END; */ ON CLICK go_button = BEGIN /* Name des zu rufenden Frames ist in der verborgenen Spalte */ callframe :SelTable[].FrameName; END; ON CLICK close_button, ON windowclose = BEGIN exit; END; Bezeichnet aktuell markierte Zeile 4.10.2 Kommunikaton zwischen Frames Mehrere Möglichkeiten: • Parameterübergabe • Globale Variable • Globale Konstanten • User Events (lösen Aktionen in einem anderen Frame aus: SendUserEvent-Methode) • Datenbank Events (Kommunikation zwischen zwei Anwendungen, die über dieselbe Datenbank miteinander verbunden sind; erstellt über SQL-Skript, ausgelöst durch Datenbank) 4.10.3 Scope • Frames sind zugreifbar von allen anderen Frames der Anwendung. Skript zur Vorlesung Datenbanken VertiefungSeite 71 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Datenbankprozeduren müssen registriert werden, um direct vom 4GLCode aufgerufen werden zu können. • 4GL-Prozeduren und Variable, die in der declare-Sektion eines 4GLScript definiert sind, sind nur lokal zu diesem Script sichtbar. Sind sie im Frame-Script definiert, so gelten sie auch für alle EventBlöcke von Feldern dieses Frames. • Lokale Prozeduren werden am Ende eines Scripts – hinter allen EventBlöcken – implementiert. • Field-Variable sind lokal im Frame sichtbar • Variablen, die innerhalb der Klammern eines initialize-Blocks definiert sind, sind außerhalb des Frames als Parameter sichtbar. 4.10.4 Frameaufrufe Bei jedem Frame-Aufruf wird eine Instanz der Klasse FrameExec erzeugt. Enthält Informationen über den augenblicklichen Zustand des laufenden Frames (Information über Felder: InputFocusField, CurMode des Frames, das letzte event: Messagexxx). Weiterhin ermöglicht es die Kommunikation zwischen Frames (SendUserEvent-Methode, aber auch Beep(), PopUp-Fenster ). Referenzierung über System-Variable CurFrame. Verschiedene Möglichkeiten des Aufrufs: • openframe Öffnet neues Frame, voriges ist weiterhin aktivierbar. • callframe Öffnet neues, modales Frame, andere Frames sind nicht aktivierbar (blockiert, Maus erscheint als Uhr) • gotoframe Schließt voriges Frame, geht zu dem neuen Frame, kein Zurück Achtung, wenn mehrere aktive Frames offen sind! Normalerweise hat die Anwendung nur eine Datenbankverbindung (Session). Jedes Commit oder Rollback in einem der Frames wirkt sich auf die offene Transaktion aus! 4.10.4.1 openframe Aufruf: FrameExecVar = openframe framename ( [parameterlist] ) [ with [parentframe=FrameExecVar | NULL ] | [optionlist] ] mit parameterlist: parameter_name=expression {, parameter_name=expression} parameter_name ist Feldname oder lokale Variable des neuen Frames. expression kann Felder, Konstanten oder Variablen des rufenden Frames enthalten. mit optionlist: option=value {, option=value} Skript zur Vorlesung Datenbanken VertiefungSeite 72 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Das aufrufende Frame bleibt aktiv während das aufgerufene Frame läuft. Um eine Kommunikation zu ermöglichen, erhält das aufrufende Frame eine Referenz-Variable auf das aufgerufenen Frame als Funktionswert zurück (Typ FrameExec). Für dieses Detail noch kein DetailFrame offen Beispiel: ON CLICK view_edit_buttons.edit_button = BEGIN IF vlist[].details_frame IS NULL THEN Hier ist Referenz auf vlist[].details_frame = openframe video_detail neues Frame (video_info = vlist[], read_only = FALSE) gespeichert; with windowtitle = vlist[].title; Datentyp FrameExec FIELD(view_edit_buttons).CurBias = FB_DIMMED; ENDIF; END; Frameparameter Frameattribute der Framedefinitio n Anderes Beispiel: graphic_frame = openframe video_graphic (vid_graphic_bitmap = video.vid_graphic_object) with window_title = video.title, windowxleft = CurFrame.WindowWidth, windowytop = 0; Später kann diese Referenz benutzt werden: IF menu.view_menu.show_graphic = FALSE THEN graphic_frame.Terminate(); graphic_frame = NULL; ENDIF; 4.10.4.2 callframe Variable = callframe framename [ (parameterlist) ] [with optionlist] Bis auf Rückgabewert alles wie oben. Rückgabewert durch Aufruf: return expression im aufgerufenen Frame (Datentypen müssen übereinstimmen). Übergebene Parameter können auch by reference sein (nur bei callframe!): callframe myframe (number = ByRef(int_var), check_rec = ByRef(ref_var), movies = ByRef(array_var)); Wird eine Referenzvariable by reference übergeben, so kann diesem Pointer ein anderes Objekt zugewiesen werden! Skript zur Vorlesung Datenbanken VertiefungSeite 73 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 4.10.4.3 gotoframe gotoframe framename [ (parameterlist) ] [with optionlist] Das rufende Frame wird geschlossen! Daher ohne Rückgabewert! Das aufgerufene Frame ersetzt das aufrufende! Daher ist Rücksprung in das Elternframe des aufrufendem Frames möglich! Sogar unter Ausnutzung des Return-Parameters, wenn Datentypen übereinstimmen. Keine ByRef-Funktion! Aufruf: 4.10.4.4 FrameExec Objekt CurFrame gib Zugriff auf das Frame-Objekt. Ändern der Größe des Fensters, der Hintergrundfarbe, des Feldes, welches den Input Fokus besitzt: CurFrame.InputFocusField = FIELD(customer.cname); 4.10.4.5 Pop-up Windows Die Arten: Pop-up Fenster Typ Beschreibung Information Pop-up Dialog-Box mit einer Message und OK-Button Confirm Pop-up Zusätzlich ein Cancel-Button Reply Pop-up Message, Prompt Line (zur Eingabe), OK-Button File Pop-up Message, File Selection Area, Cancel- und OK-Button, Reply als Ergebnis Information- und Reply-Pop-ups auch mit message bzw. prompt. Sonst: CurFrame.InfoPopUp (messagetext=varchar(256) [, messagetype=integer]); message varchar(256); CurFrame.ConfirmPopUp(messagetext=varchar(256)); CurFrame.ReplyPopUp (messagetext=varchar(256) reply=StringObject); reply = prompt varchar(256); CurFrame.FilePopUp (messagetext=varchar(256), reply=StringObject); Alle liefern einen Integer-Wert als Ergebnis: PU_OK oder PU_CANCEL Dasselbe geht auch aus einer 4GL-Procedure: CurProcedure.InfoPopUp(parms); Skript zur Vorlesung Datenbanken VertiefungSeite 74 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Beispiel: ON CLICK delete_button = BEGIN status = CurFrame.ConfirmPopup(messagetext = 'Achtung: Sie löschen jetzt!'); IF status != PU_OK THEN resume ELSE ... ENDIF; END; ReplyPopup und FilePopup haben eine Reply-Parameter (file_name ist ein StringObject): file_name.Value = ''; status = CurFrame.FilePopup(messagetext = 'Gib Name des Files:', reply = file_name); IF status = PU_OK THEN ... 4.10.4.6 Schließen eines Frames: return 4.10.5 Framekommunikation mit User Events 4.10.5.1 Events empfangen Syntax: ON USEREVENT ['Eventname'] = BEGIN kommandoliste; END; Ohne Eventname: Für alle User Events, für die das Frame keinen Usereventblock hat. Nur im Frame Script! Beispiele: ON USEREVENT 'uhrzeit' = BEGIN uhrzeit_efd = DATE('NOW'); END; ON USEREVENT = BEGIN MESSAGE 'Event ohne Block erhalten!!!'; END; 4.10.5.2 Events versenden Methode SendUserEvent der Klasse FrameExec. Sendet an das durch die FrameExec-Variable bezeichnete Frame das Event. Skript zur Vorlesung Datenbanken VertiefungSeite 75 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Syntax: FrameExecVar.SendUserEvent( EVENTNAME = varchar(256) [, MESSAGEOBJECT = Object [, MESSAGEINTEGER = Integer [, MESSAGEFLOAT = Float [, MESSAGEVARCHAR = VarChar [, DELAY = Float [, FOCUSBEHAVIOR = Integer ] ] ] ] ] ]); Messageparameter dienen der Datenübertragung. Können in dem zugehörigen empfangenden Eventblock unter den oben genannten Namen empfangen werden. FOCUSBEHAVIOR triggert im empfangenden Frame ein SetValue-Event für das aktive Feld (FT_SETVALUE) evtl. mit zusätzlichem Exit-Event (FT_TAKEFOCUS). Default ist, daß nichts geschieht (FT_NOSETVALUE). Das geschieht, bevor der aktuelle Event-Block bearbeitet wird. DELAY läßt das Event erst nach der angegebenen Zahl Sekunden in der Event-Queue erscheinen. Beispiel: INITIALIZE () = BEGIN CurFrame.SendUserEvent(EVENTNAME = 'uhrzeit'); END; ON USEREVENT 'uhrzeit' = BEGIN uhrzeit_efd = DATE('NOW'); CurFrame.SendUserEvent( EVENTNAME = 'uhrzeit', DELAY = 60); END; Beispiel für Datenübertragung: /* Absenderframe */ FE_ptr.SendUserEvent( EVENTNAME MESSAGEOBJECT MESSAGEINTEGER = 'update_zeile', = arr[i], = i); /* Empfängerframe */ ON USEREVENT 'update_zeile' = BEGIN zeile = mit_class(CurFrame.MessageObject); /* Type Cast*/ i = CurFrame.MessageInteger; ... END; Anderes Beispiel: Ein Child-Frame, welches von dem Parent-Frame geöffnet wurde, soll dem Parent-Frame eine Information zurück übermitteln. Skript zur Vorlesung Datenbanken VertiefungSeite 76 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Vom main_frame werde ein detail_frame geöffnet für eine bestimmte Reihe des Table Field testtable (in main_frame). Schicke Referenz /* Script for Main_Frame */ auf main_frame ON CLICK open_detail = mit BEGIN OPENFRAME detail_frame ( parentframe = CurFrame, rownumber = FIELD(testtable).CurRow); Zeilennummer testtable[].IsOpen = TRUE; wird später END; gebraucht ... ON USEREVENT 'Detail Close' = BEGIN testtable[CurFrame.MessageInteger].IsOpen = FALSE; END; /* Script for Detail Frame */ INITIALIZE( parentframe = FrameExec, rownumber = integer) = BEGIN /* Load detail information */ ... END; ... ON CLICK close_button = BEGIN parentFrame.SendUserEvent ( eventname = 'Detail Close', messageinteger = rownumber); RETURN; END; Beispiel für SetValue Event in Verbindung mit Lookup: Lokale Variablen, werden bei Aufruf gesetzt! Schicke Info zurück Skript zur Vorlesung Datenbanken VertiefungSeite 77 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 INITIALIZE () = DECLARE dept_in_database = VarChar(100) with NULL ENDDECLARE ... ON SetValue department = BEGIN dept_in_database = NULL; SELECT :dept_in_database = dept FROM department_table WHERE dept = :department; IF dept_in_database IS NULL THEN CurFrame.InfoPopUp( messagetext = 'Bad department value ' + department, messagetype = MT_ERROR); RESUME; ENDIF; END; 4.10.5.3 Löschen von User Events aus der Queue Methode PurgeUserEvent der Klasse FrameExec. Syntax: int_var = FrameExecVar.PurgeUserEvent( [EVENTNAME = varchar(256)]); Return-Wert ist Anzahl der gelöschten Events. Ohne Eventname werden alle Events für dieses Frame gelöscht. 4.10.5.4 Synchronisation Warten auf ein spezifisches User Event mit der Methode WaitFor(). • Nur für Referenzvariable CurFrame aufrufbar • Event darf nicht vom eigenen Frame kommen • Andere inzwischen ankommende Events landen in der Queue • Frame ist geblockt, bis Event kommt • Messageparameter nur, wenn Returnvariable angegeben ist • Anwendung: SendUserEvent zurück an anderes Frame als Acknowledgement Syntax: Event = CurFrame.WaitFor(EVENTNAME = varchar(256)); 4.10.5.5 Datenbankevents Erlauben es, daß eine Anwendung Statusinformation an andere Anwendungen schickt. • Werden über SQL-Befehle erstellt Skript zur Vorlesung Datenbanken VertiefungSeite 78 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Können von allen Applikationen derselben Datenbank benutzt werden • 256 Byte Information kann verschickt werden Um Datenbankevents zu nutzen: • Erzeuge Datenbankevent mit SQL-Anweisung CREATE DBEVENT • Benutze in der empfangenden Applikation die SQL-Anweisung REGISTER DBEVENT, um Datenbankevents empfangen zu können. • Schreibe in dem Frame-Script den Datenbankeventblock • Benutze in der sendenden Anwendung die SQL-Anweisung RAISE DBEVENT Die SQL-Befehle (Eventname sei ev1): • DBEvent erstellen CREATE DBEVENT ev1; • Löschen DROP DBEVENT ev1; • Rechte vergeben GRANT RAISE, REGISTER ON DBEVENT ev1 TO PUBLIC; • Registrierung einschalten REGISTER DBEVENT ev1; • Registrierung ausschalten REMOVE DBEVENT ev1; • Events versenden RAISE DBEVENT ev1 'Text'; Wenn eine Anwendung ein RAISE DBEVENT ausführt, sendet die Anwendung das Ereignis und den damit verbundenen Text an das DBMS. Dieses sendet das Ereignis daraufhin an jede Anwendung, die dafür registriert ist. In Windows4GL pollen registrierte Anwendungen regelmäßig auf Datenbankevents. Die Pollrate ist definiert durch das DBEventPollrate Attribut des SessionObject. Für jedes Datenbankevent wird ein DBEventObject erzeugt, welches dem entsprechenden Frame zugestellt wird. W4GL-Befehle: • Events auswerten: ON DBEVENT 'ev1' = BEGIN Feld1_efd = CurFrame.DBEvent.DBEventName; Feld2_efd = CurFrame.DBEvent.DBEventOwner; Feld3_efd = CurFrame.DBEvent.DBEventDatabase; Feld4_efd = CurFrame.DBEvent.DBEventText; Feld5_efd = CurFrame.DBEvent.DBEventTime; END; Skript zur Vorlesung Datenbanken VertiefungSeite 79 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Pollrate einstellen: /* Default ist 30 sec */ CurSession.DBEventPollrate = 5; • Event registrieren (i.a. in dem Initialisierungsblock des Frames) REGISTER DBEVENT ev1; • Event „raisen“ RAISE DBEVENT ev1 'Text'; 4.11 System Referenz Variablen 4.11.1 CurFrame • Referenz auf das aktuelle Frame. • Instanz der Klasse FrameExec Attribute (nicht alle): • CurMode Augenblicklicher Frame-Modus: FM_READ, FM_UPDATE, FM_QUERY, FM_USER1..3 Änderung dieses Feldwertes hat Auswirkungen auf Biases! callframe framename() WITH CurMode=FM_READ; • TriggerField Feld, welches den augenblicklichen Event erzeugt hat (z.B. Entry- und Exit-Events, ChildEntry und -Exit) • OriginatorField Auslösendes Feld, welches in dem Event-Block vorkommt, der den Code für den augenblicklichen Event enthält: ON CLICK TestRadioField, ON CLICK TestMenuList = BEGIN IF CurFrame.OriginatorField = FIELD(TestRadioField) THEN TestMenuList = TestRadioField; ELSE TestRadioField = TestMenuList; ENDIF; ... END; • InputFocusField Feld des Frames, welches augenblicklich den Input-Fokus hat. • TargetField Exit: Nächstes Feld, Entry: Aktuelles Feld • PreviousField Entry: Feld, welches zuvor Input-Fokus hatte, Exit/Menu: NULL • TopForm Enthält die mit dem Frame verknüpfte Form (d.h. alle Felder ohne Menü). Anwendungen: CurFrame.TopForm.HasDataChaged; /* Attribut */ CurFrame.TopForm.SetToDefault(); /* Methode */ Skript zur Vorlesung Datenbanken VertiefungSeite 80 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • ParentFrame • BgColor, BgPattern • WindowTitle, HasScrollBars, IsResizable • WindowPlacement WP_SCREENRELATIVE, WP_SCREENCENTERED, WP_INTERACTIVE, WP_PARENTRELATIVE, WP_PARENTCENTERED, WP_FLOATING INITIALIZE (relx = Integer, lely = Integer) = ... IF CurFrame.InputFocusField IS NOT NULL THEN relx = CurFrame.InputFocusField.AbsXLeft + 500; rely = CurFrame.InputFocusField.AbsXBottom + 500; ELSE relx = 1000; rely = 1000; ENDIF; callframe relframe (...) WITH WindowPlacement = WP_PARENTRELATIVE, WindowXLeft = relx, WindowYTop = rely; ... Methoden (nicht alle): • Terminate() • SendUserEvent, WaitFor, PurgeUserEvent • Flush() CurFrame.Flush() läßt alle offenen Änderungen im Frame erscheinen. Normalerweise geschieht das erst am Ende des Event-Blocks. • Alle ...Popup(..) Methoden, geerbt von ProcExec 4.11.2 CurProcedure • Referenz auf die aktuelle Prozedur. • Lokale Variable der Klasse ProcExec, Super Class von FrameExec. • Attribut ObjectSource. Anwendungsbeispiel: /* Aufruf vom Frame */ callproc disp_message (msr = 'This is a Message'); ... /* und die Prozedur ... */ PROCEDURE disp_message(msg = VarChar(256)) = BEGIN CurProcedure.InfoPopup(messagetext = msg); END; • Methode Beep() • Duplicate(); Skript zur Vorlesung Datenbanken VertiefungSeite 81 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Alle XxxxPopup(..) Methoden 4.11.3 CurSession • Instanz der Klasse SessionObject. • Ist die eigentliche Anwendung. • Informationen zur Laufzeitumgebung (DEC, Motif) Attribute: • ScreenHeight un ScreenWidth (in 1/1000 inches), dasselbe auch in Pixels. • DBMSErrorPrinting EP_NONE: Keine Ausgabe, EP_OUTPUT: Nach Standard Output, EP_INTERACTIVE: Interaktives Popup-Fenster. Methode: value = SessionObject.GetEnv(name = varchar); Environment-Variablen. 4.12 Events Man unterscheidet die folgenden Event-Typen • Frame Events • Field Events • Menu Events Events werden folgendermaßen bearbeitet: 1. Wenn das Event erzeugt wird, wird es an das Ende der Event-Queue der Anwendung gesetzt 2. Erreicht das Event den Anfang der Warteschlange der Anwendung, wird es an das Ende der Warteschlange des entsprechenden Frames gesetzt 3. Erreicht das Event den Anfang der Event-Queue des Frames, wird der entsprechende Event-Block des Frames ausgeführt. Die Reihenfolge der Abarbeitung der Events ist nicht notwendigerweise dieselbe wie die der Erzeugung der Events! Grundsätzlich wird immer ein Event-Block fertig bearbeitet, bevor das nächste Event an die Reihe kommt. Per Default sind während der Abarbeitung eines Event-Blocks alle offenen Frames blockiert, so daß in der Zeit keine zusätzlichen Ereignisse erzeugt werden können. Kann durch setzen von BlockFrames auf False geändert werden. 4.12.1 Frame Events Alle nicht-feldspezifische Events (Wechselwirkung des Benutzers mit dem Frame-Fenster), alle User Events und DBEvents. Außerdem erhält das Frame von allen untergeordneten Feldern entsprechende Child-Events. Beispiele: • ON WindowClose = Schließen einer Frame-Fensters Skript zur Vorlesung Datenbanken VertiefungSeite 82 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • ON Terminate = Beenden eines Frames und aller untergeordneten Frames (z.B. bei Parent-Frame ausgelöst) • ON ChildEntry = • ON UserEvent = • ON DBEvent = Die Ausführung eines Events kann durch Aufruf von Resume im EventBlock unterbrochen werden, z.B. auf WindowClose hin. 4.12.2 Field Events Events, die speziell für ein Feld oder eine Feldgruppe spezifiziert sind. Für die verschiedenen Objekte ist eine große Zahl von Events zugelassen. • On Click ... = Feld mit Bias FB_LANDABLE oder FB_CHANGEABLE Definiert für: ImageField ImageTrim SliderField BarField ScrollBarField ButtonField ToggleField ListField RadioField OptionField MenuButton MenuToggle MenuList Bei Enumerated Fields wird Click Event bei jedem Click erzeugt, auch wenn der Wert sich dabei nicht ändert. Sonst: SetValue. Achtung: Für Help oder Cancel Aktionen „Not Validated“ setzen! • ON DoubleClick ... = Für alle Form Fields definiert, nicht für Menu Fields. Nicht für Felder mit Bias FB_VISIBLE, nicht für Entry Fields mit FB_CHANGEABLE DoubleClick Events folgen jeweils einem Click Event! • ON ClickPoint ... = Feld mit Bias FB_CLICKPOINT Für alle Form Fields definiert, nicht für Menu Fields. Gibt Koordinaten in 1/1000 Inch relativ zum oberen linken Punkt des Empfängers: INITIALIZE () = DECLARE x_coord_of_click = Integer, y_coord_of_click = Integer, country_name = VarChar(100) ENDDECLARE BEGIN ... FIELD(mapfield).CurBias = FB_CLICKPOINT; ... END; ... Skript zur Vorlesung Datenbanken VertiefungSeite 83 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ON ClickPoint mapfield = BEGIN x_coord_of_click = CurFrame.XStart; y_coord_of_click = CurFrame.YStart; country_name = CallProc convert_to_country( x_point = x_coord_of_click, y_point = y_coord_of_click, map_height = FIELD(mapfield).Height, map_width = FIELD(mapfield).Width); CurFrame.InfoPopup(messagetext = 'Country clicked was ' + country_name); END; • ON Details ... = Feld mit dem Details-Button (mittl. Maustaste) angeklickt Nicht für Menu Fields, nicht für Eingabefelder mit Input-Fokus und FB_CHANGEABLE. Funktioniert auch auf Hintergrund (Frame) ON Details tblfield, ON ChildDetails tblfield = BEGIN callframe generic_help(field='tblfield'); END; • ON DragBox ... = Feld mit Bias FB_DRAGBOX und Benutzer zieht Dragbox in dem Feld. Liefert die Koordinaten der DragBox • ON DragSegment ... = Feld mit Bias FB_DRAGSEGMENT und Benutzer zieht ein Dragsegment in dem Feld. Liefert Anfangs- und End-Koordinaten der Linie • ON Entry ... = Für alle skalaren Felder, nicht für Menu, Shape oder Composite Fields. Nicht für Button und Enumerated Fields (per Default). FocusBehaviour muß FT_TABTO oder FT_TAKEFOCUS sein. Hauptanwendung bei Eingabefeldern: ON Entry name_field = BEGIN message_field = 'Bitte Name eingeben!'; END; Nicht für Table Fields geeignet; siehe dazu ChildEntry! • ON Exit ... = Wenn Anwender dabei ist, das Feld mit dem augenblicklichen Input Fokus zu verlassen. Triggered bevor der Input Fokus tatsächlich gewechselt hat. Nicht definiert für Menu, Shape und Composite Fields. Muß validierbar sein! Wird auch getriggert, wenn Daten nicht verändert wurden (d.h. Anwender ist nur durch das Feld durch. Sonst SetValue!) Beispiel: Ändere Tab-Reihenfolge: Skript zur Vorlesung Datenbanken VertiefungSeite 84 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ON Exit testfield = BEGIN IF testfield = 2 THEN CurFrame.InputFocusField = FIELD(field2); Resume; ELSEIF testfield = 3 THEN CurFrame.InputFocusField = FIELD(field); Resume; ENDIF; END; • ON InsertRow ... = Table Fields: ON InsertRow tblfield = BEGIN tblfield[].columnfld = fill_in_default(...); END; • ON Moved ... = Feld mit Bias FB_MOVEABLE oder FB_FLEXIBLE und Benutzer bewegt das Feld. Selten verwendet. • ON Properties ... = Feld mit dem Properties-Button (rechte Maustaste) angeklickt, alle Form Fields. Nicht für Menu Fields. ON Properties tblfield, ON ChildProperties tblfield = BEGIN callframe generic_help(field='tblfield'); END; • ON Resized ... = Feld mit Bias FB_RESIZABLE oder FB_FLEXIBLE und Benutzer ändert die Größe des Feldes • ON Scroll ... = Click auf Scrollbar von Table Field ode Viewport. • ON Select ... = Feld mit einem Select-Bias und Benutzer selektiert das Feld. Alle Form Fields, nicht für Menu Fields. Eher ChildSelect. • ON SelectionChanged ... = Das SelectedList Attribut des FrameExec-Objekts hat sich geändert. Nur im Frame Script spezifizierbar. • ON Unselect ... = Feld mit einem Select-Bias und Benutzer deselektiert das Feld • ON SetValue ... = Feld mit Bias FB_CHANGEABLE. Erzeugung des Events wird verzögert, bis Feld verlassen wird. U.a. nicht für Composite Fields. Wurde das Event durch das Verlassen eines Eingabefeldes getriggert, so Skript zur Vorlesung Datenbanken VertiefungSeite 85 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 kann Resume benutzt werden, um den neuen Wert ungültig werden zu lassen. INITIALIZE () = DECLARE dept_in_database = VarChar(100) WITH NULL ENDDECLARE ... ON SetValue department = BEGIN dept_in_database = NULL; REPEATED SELECT :dept_in_database = dept FROM department_table WHERE dept = :department; IF dept_in_database IS NULL THEN CurFrame.InfoPopup( messagetext = 'Bad department value ' + department, messagetype = MT_ERROR); Resume; ENDIF; END; • On Terminate ... = Frame und alle Subframes werden geschlossen. Nur im Frame Script. ON Terminate = BEGIN IF CurFrame.ConfirmPopup(messagetext='Save chages?') = PU_OK THEN /* Rette alle Änderungen */ ENDIF; END; Häufig kommt es bei einer Benutzerinteraktion zu einer Kette von Events (Event Chains). Wird z.B. ein Entry Field A verlassen, indem ein anderes Entry Field B angeclickt wird (oder durch Tab), so kommt es zu der folgenden Event-Kette: 1. SetValue Event für Feld A 2. Exit Event für Feld A 3. Entry Event für Feld B Oder: Jedesmal, wenn ein Benutzer ein Feld selektiert: 1. UnSelect Event für das vorige Feld 2. Select Event für das neue Feld 3. SelectionChanged Event für das Frame Hat ein bestimmtes Feld mehrere Eventblöcke zugeordnet, ist es wichtig die Reihenfolge der Erzeugung der Events zu berücksichtigen. Skript zur Vorlesung Datenbanken VertiefungSeite 86 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Eine andere wichtige Gruppe von Field Events sind die sog. Child Events. Eine Aktion an einem Child Field ruft ein Child Event für das Parent Field hervor. Sei ein Button A in einer Subform X enthalten, so erzeugt ein Click auf den Button A die folgende Sequenz: 1. Click Event für Button A 2. ChildClick Event für Subform X 3. ChildClick Event für das Frame Dies ist auch die Reihenfolge, in der die Events in die Warteschlange der Anwendung eingereiht werden. Ein Beispiel für Child Events innerhalb eines Frame-Scripts: ON ChildEntry = BEGIN CurFrame.TriggerField.BgColor = CC_PALE_CYAN; END; ON ChildExit = BEGIN CurFrame.TriggerField.BgColor = CC_PALE_BLUE; END; Bei einer Event Chain werden zunächst alle Child-Events für ein bestimmtes Event in die Warteschlange eingereiht, erst dann kommt das nächste Event der Kette. Unser Beispiel von oben mit den beiden Entry-Fields: Frame Subform Feld1 Feld2 1. 2. 3. 4. 5. 6. 7. 8. 9. SetValue für Feld1 ChilSetValue für Subform ChildSetValue für Frame Exit für Feld1 ChildExit für Subform ChildExit für Frame Entry für Feld2 ChildEntry für SubForm ChildEntry für Frame • ON ChildKlick ... = Für Composite Fields, wenn ein Child Field einen Click Event bekommen hat. Beispiel: Skript zur Vorlesung Datenbanken VertiefungSeite 87 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ON ChildClick testtable[*].testcolumn = BEGIN /* Current row will reflect the row that contained */ /* the image that was clicked */ callframe ShowDetail(picture = testtable[].testcolumn); END; Anderes Beispiel: Zwei Image Trim Felder sollen auf Click hin invers dargestellt werden. Fasse beide Felder in picture_subform zusammen: ON ChildClick picture_subform = BEGIN FormField(CurFrame.TriggerField).IsReverse = TRUE; END; • ON ChildKlickPoint ... = Wie oben! • ON ChildDetails ... = Analog! Anwendung: Selektion einer Reihe aus einer Tabelle. Feld muß den Bias FB_LANDABLE haben! Im folgenden Beispiel hat das Table Field testtable ein Feld object_name: ON ChildDetails testtable = BEGIN IF FIELD(testtable).CurRow > 0 THEN callframe EditRow( name = testtable[].object_name ); ELSE /* Keine Zeile war ausgewählt */ ENDIF; END; • ON ChildDoubleClick ... = Analog wie DoubleClick. Anwendung wie bei ChildDetails Event • ON ChildEntry ... = Nützlich für Entry Events für Entry Fields innerhalb von Spalten eines Table Fields: ON ChildEntry emptable[*].salary = BEGIN help_field = 'Gib Gehalt in Tausend DM ein'; END; Bei Exit dann entsprechend: ON ChildExit emptable[*].salary = BEGIN help_field = ''; END; CurFrame.TriggerField.Name würde bei ChildEntry den Namen des genauen Feldes zurückgeben. • ON ChildExit ... = Beispiel: Wird die Zeile eines Table Fields verlassen? Wenn ja könnte eine Validierung durchgeführt werden. Skript zur Vorlesung Datenbanken VertiefungSeite 88 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ON ChildExit testtable = BEGIN IF FIELD(testtable).WhichRow( cellfield = CurFrame.TriggerField) != (FIELD(testtable).WhichRow( cellfield = CurFrame.TargetField) THEN /* Das nächste Feld ist in einer anderen Zeile */ /* oder außerhalb des Table Fields */ ... ENDIF; • ON ChildSetValue ... = Beispiel: ON ChildSetValue emptable[*].salary = BEGIN if emptable[].salary > 100000 THEN CurFrame.InfoPopup( message='Gehalt muß weniger als 100000 sein', messagetype = MT_ERROR); resume; ENDIF; END; 4.12.3 Menu Events Alle Events für Menüs. Menü-Felder haben Click Event definiert. Menu Toggle und Menu List haben zusätzlich SetValue Event Beispiele: ON Click menu.datei.neu = ... ON SetValue menu.hilfe_tgl = ... 4.12.4 Einschub Dynamisches Initialisieren eines EnumFields: INITIALIZE () = DECLARE i = Integer, tmp = VarChar(20) ENDDECLARE Skript zur Vorlesung Datenbanken VertiefungSeite 89 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 BEGIN FIELD(Auswahl).ValueList.ChoiceItems.Clear(); i = 1; REPEATED SELECT DISTINCT :tmp = Spalte FROM Tabelle WHERE ... BEGIN FIELD(Auswahl).ValueList.ChoiceItems[i].EnumText = tmp; FIELD(Auswahl).ValueList.ChoiceItems[i].EnumValue = i; i = i + 1; END; COMMIT; FIELD(Auswahl).CurEnumValue = 3; /* oder .CurEnumText = '...' */ FIELD(Auswahl).UpdChoiceList(); END; Andere Möglichkeit: ... FIELD(Auswahl).ValueList.AddTextItem(enumvalue=i, textvalue=tmp); ... 4.13 Einbinden von Prozeduren und Funktionen 4.13.1 Allgemeines • Definition im Component Catalog (DB- und 3GL-Prozeduren werden hier nur bekannt gemacht) • Einmal erstellte Prozedur kann aus jedem W4GL-Script der aktuellen Applikation aufgerufen werden • Globale 4GL-Prozedur ist global in Anwendung; nicht frame-spezifisch. Daher kann auf Feld-Namen NICHT Bezug genommen werden! • Lokale 4GL-Prozedur wird lokal in einem Frame-, Field- oder ProzedurSkript definiert. Aufrufbar in dem definierenden Skript oder in Skripts untergeordneter Felder • Vier - Typen von Prozeduren: 4GL-Prozeduren (W4GL-Code, in der Applikation), global und lokal Datenbankprozeduren (SQL-Code, in der Datenbank) 3GL-Prozeduren (C, Cobol, Fortran, Pascal, ...) 4.13.2 Aufrufsyntax 4GL-Prozeduren: [ReturnVar=] CALLPROC Prozedurname [ (Parm = Ausdr | ByRef(Variable) {, Parm = Ausdr | ByRef(Variable)} ) ] Skript zur Vorlesung Datenbanken VertiefungSeite 90 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Datenbank-Prozeduren: [IntegerVar=] CALLPROC Prozedurname [ (Parm = Ausdr {, Parm = Ausdr} ) ] 4.13.3 4GL Prozeduren • Globale Prozeduren erzeugt mit dem Procedure Editor • Lokale Prozeduren werden in einem Frame-, Field- oder Prozedur-Skript definiert; die Prozedur wird mit Hilfe der PROCEDURE-Anweisung implementiert (nach allen Event-Blöcken). • Es können Parameter und lokale Variablen spezifiziert werden. • Parameterübergabe = Setzen von Werten von lokalen Variablen • ByRef-Übergabe ist möglich • Frame-Felder sind nicht zugreifbar • Folgende Komponeten können aufgerufen werden: - Frames - 4GL-, 3GL-, Datenbank-Prozeduren Beispiel: PROCEDURE insert_video_row( video_list = ARRAY OF VIDEO_ROW, title = VarChar(50) NOT NULL, row = Integer NOT NULL ) = BEGIN ... END; Aufruf: CALLPROC insert_video_row(video_list = vlist, title = video.title, row = ByRef(i)); Beispiel für eine lokale Prozedur: INITIALIZE () = DECLARE load_data = procedure; ... ENDDECLARE BEGIN callproc load_data; END; PROCEDURE load_data() = DECLARE i = integer ENDDECLARE /* Vorwärts Deköaration */ Skript zur Vorlesung Datenbanken VertiefungSeite 91 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 BEGIN COMMIT; /* Start a new transaction */; parttable.Clear(); /* Clear out array */ i = 1; SELECT :parttable[i].partno = partno, :parttable[i].partno_save = partno, :parttable[i].short_desc = short_desc FROM part BEGIN i = i + 1; END; END; 4.13.4 Datenbankprozeduren • Sind Objekte der Datenbank • Formuliert in SQL • Ausführungsrecht reicht • Aufruf wie 4GL-Prozeduren, aber keine ByRef-Übergabe möglich • Return-Wert ist immer Integer • Vorteil: Geringe Kommunikation zwischen Client und Server: Bessere Performance. Einsetzbar zur Gewährleistung der Datenintegrität. • Aufrufmöglichkeit auch durch Datenbank-Regeln (Rules) Syntax zum Erstellen: [CREATE] PROCEDURE Prozedurname [ (ParmName [=] ParmTyp {, ParmName [=] ParmTyp } ) ] = | AS [ DECLARE VarName {, VarName } [=] VarTyp; {, VarName {, VarName } [=] VarTyp;} ] BEGIN statement {; statement} [;] END; Beispiel: /* DB-Prozedur zum Löschen aus der Tabelle mitarbeiter */ CREATE PROCEDURE DelMitarbeiter (p_persnr = Integer NOT NULL) AS DECLARE p_abteilung = VarChar(10) NOT NULL; p_region = Char(5) NOT NULL; Skript zur Vorlesung Datenbanken VertiefungSeite 92 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 BEGIN SELECT abteilung, region INTO p_abteilung, p_region FROM mitarbeiter WHERE persnr = :p_persnr; IF iirowcount < 1 THEN return -1; ENDIF; DELETE FROM mitarbeiter WHERE persnr = :p_persnr; IF iirowcount != 1 OR iiErrorNumber != 0 THEN return -1; ENDIF; UPDATE abteilung SET mitarb = mitarb - 1 WHERE abteilung = :P_abteilung AND bezirk = :p_region; IF iiErrorNumber != 0 THEN return -1; ENDIF; return 1; END; /* Alles o.k. */ 4.14 Einige Ergänzungen zu SQL 4.14.1 Spezielle Ingres Konstanten now Aktuelles Datum und Zeit select date('now') today Aktuelles datum select date('today') null undefinierter oder unbekannter Wert user Aktueller User-ID 4.14.2 Zeit Intervalle Beispiele: date('today') + date('2 years 2 months'); date('now') + date('12 hours 30 minutes'); Anzahl der Tage seit 1.1.1900: num_days = int4(interval('days', 'today' - date('1/1/00'))) Skript zur Vorlesung Datenbanken VertiefungSeite 93 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Zurück: (date('1/1/00') + concat(char(num_days), ' days')) 4.14.3 Funktionen zur Typkonversion Name Operand Typ Resultat Typ Beschreibung c(expr) jeder c Jeder Wert zu c String char(expr) jeder char Jeder Wert zu char String date(expr) c, char, varchar, text date Ergebnis ist interne Datumsdarstellung dow(expr) date c Wochentag, z.B. 'Mon', Länge 3 float4(expr), float8(expr) c, char, varchar, float4 bzw. float text, float, money, alle Integers Ergebnis Gleitkomma hex(expr) c, char, varchar, text Zweimal so lang wie ArgumentString: hex('A') liefert '61' int1(exp), int2(exp), int4(exp) c, char, varchar, integer1 bzw. text, float, money, smallint bzw. alle Integers integer Dezimalbrüche werden abgeschnitten money(expr) c, char, varchar, text, float, alle Integers money Interne money Darstellung, Dezimalbrüche evtl. gerundet text(expr) jeder text text String ohne trailing blanks von c oder char Strings varchar(expr) jeder varchar varchar String ohne trailing blanks von c oder char Strings varchar 4.14.4 Numerische Funktionen Name Operand Typ Resultat Typ abs(n) alle numerischen und money wie n atan(n) alle numerischen und money float cos(n) alle numerischen und money float exp(n) alle numerischen und money float Skript zur Vorlesung Datenbanken VertiefungSeite 94 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 log(n) alle numerischen und money float mod(n,b) integer, smallint, integer1 wie b sin(n) alle numerischen und money float sqrt(n) alle numerischen und money float Argumente der Winkelfunktionenin Radian. Konvertierung: radians = degrees/360 * 2 * pi 4.14.5 String Funktionen Name Resultat Typ Beschreibung concat(c1,c2) jeder Character Typ Resultat Länge ist Summe der Einzellängen. c und char Ergebnisvariablen werden evtl. mit Blanks aufgefüllt left(c1,len) jeder Character Typ Erste len Zeichen. c und char Ergebnisvariablen haben dieselbe Länge wie c1 padded mit Blanks length(c1) smallint Länge des Strings, für c und char ohne trailing Blanks locate(c1,c2) smallint Stelle des ersten Vorkommens von c2 in c1. Wird c2 nicht gefunden, ist das Resultat size(c1)+1. lowercase(c1) jeder Character Typ zu lower case pad(c1) varchar, text right(c1,len) jeder Character Typ Erste len Zeichen von rechts. Blanks werden nicht zuvor entfernt. shift(c1,nshift) jeder Character Typ Verschiebt String nshift Stellen nach rechts (nshift>0) oder links (nshift<0) size(c1) smallint Deklarierte (maximale) Länge squeeze(c1) varchar, text Komprimiert „white space“. Entfernt am Anfang und Ende. Dazwischen jeweils max. 1 Blank trim(c1) varchar, text Schneidet trailing Blanks ab uppercase(c1) jeder Character Typ zu Großbuchstaben Fügt Blanks auf maximale Länge ein charextract(c1,n) jeder Character Typ Resultat ist das n-te Byte. Ist n größer als Stringlänge: Blank 4.14.6 Datums-Funktionen Datums-Units: Second, Minute, Hour, Day, Week, Month, Quarter, Year. Skript zur Vorlesung Datenbanken VertiefungSeite 95 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Name Resultat Typ Beschreibung date_trunc(unit, date) date Datum abgeschnitten auf unit Date_part(unit, date) integer Gibt Komponente (unit) des Datums als Integer date_gmt(date) Jeder Character Typ GMT-Format: yyyy_mm_dd hh:mm:ss GMT interval(unit, date_interval) float _date(s) Jeder Character Typ 9 Character String mit Datum s Sekunden nach 1.1.1970 GMT _time(s) Jeder Character Typ 5 Character String mit Zeit s Sekunden nach 1.1.1970 GMT Gibt Komponente (unit) des Zeitintervalls als Float. 4.14.7 Aggregat-Funktionen Liefert einen einzelnen Wert aufgrund des Inhalts einer Spalte zurück. Können in select, insert, update und delete SQL-Befehlen benutzt werden. Am meisten in select. SELECT :sum_efd = sum(employee.salary) FROM employee WHERE dept = 23; Die Aggregat-Funktionen von SQL: Name Resultat Typ Beschreibung count integer Anzahl vorkommende Sätze sum integer, float, money, date (interval) Gesamtsumme der Spalte avg float, money, date (interval) Mittelwert (sum/count) max wie das Argument Maximal-Wert der Spalte min wie das Argument Minimum-Wert der Spalte 4.14.8 Fehlerbehandlung Nach jeder Datenbankanweisung sollte die Fehlerfreiheit der letzten Aktion überprüft werden. Zwei Attribute der DBSessionObject Klasse gestatten die Überprüfung von Fehlerbedingungen: ErrorNumber und DBMSError geben Auskunft über den Verlauf der letzten SQL-Anweisung. Sind die primären Mittel zur Überprüfung auf Fehler. ErrorNumber und DBMSError werden nach jedem SQL-Befehl gesetzt. • DBMSError enthält den lokalen Fehlercode des zugrunde liegenden Datenbanksystems, z.B. Ingres oder DB2. Skript zur Vorlesung Datenbanken VertiefungSeite 96 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • ErrorNumber ist der generische Fehlercode, den Ingres unabhängig vom dahinterstehenden DBMS aufgrund dessen lokalen Fehlercodes zuweist (auch für Ingres-Fehler). Z.B. weist Ingres einem time-out Fehler die lokale Fehlernummer 4702 zu. Andere DBMSe mögen dafür einen anderen Fehlecode benutzen. Aber Ingres bildet alle diese lokalen Fehler auf denselben generischen Fehlecode ab. Überprüfung des generischen Fehlercodes macht die Anwendungen portabel. Bei fehlerfreier Ausführung des letzten Datenbankbefehls werden beide Variablen auf 0 gesetzt. Eine Möglichkeit, diese Variablen in einer Prozedur weiter auszuwerten: PROCEDURE dbms_error_message () = BEGIN IF CurFrame.DBSession.ErrorNumber != 0 THEN return 'Fehler Code ist ' + VarChar(CurFrame.DBSession.ErrorNumber) + '. Spezifischer DBMS Fehler ist ' + VarChar(CurFrame.DBSession.DBMSError); ELSE return ''; ENDIF; END; Aufruf: IF CurFrame.DBSession.ErrorNumber != 0 THEN message 'Es ist ein Fehler aufgetreten: ' + dbms_error_message(); ENDIF; Mit dem Attribut DBMSErrorPrinting der Systemklasse SessionObject kann festgelegt werden, in welcher Form SQL-Fehler am Bildschirm erscheinen: • EP_NONE keine Ausgabe • EP_OUTPUT im Standardoutput Fenster (Unix-Shell Fenster) • EP_INTERACTIVE in Popup-Fenster (Default) Eine weitere wichtige Hilfe ist die Systemvariable IIRowCount. Sie enthält die Anzahl der von dem letzten SQL-Befehl „betroffenen“ Zeilen. • Nach einem SELECT enthält sie die Anzahl der Reihen der Antworttabelle • Nach einem UPDATE bedeutet IIRowCount=0, daß kein Update durchgeführt wurde, weil z.B. keine Reihe der Tabelle der WHERE-Bedingung entspricht. Über den Befehl INQUIRE_SQL kann man sich den Status der zuletzt ausgeführten SQL-Anweisung holen: Skript zur Vorlesung Datenbanken VertiefungSeite 97 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 ... IF CurFrame.DBSession.ErrorNumber != 0 THEN ROLLBACK; Message 'Es hat einen Fehler gegeben!'; INQUIRE_SQL( :v_fehler = ERRORNO, :v_text = ERRORTEXT, :v_anzahl = ROWCOUNT ); ENDIF; ERRORNO ist dabei die generische Fehlernummer. Eine generelle Fehlerbehandlungsroutine: Zunächst Teil eines Scripts aus der Video-Applikation: UPDATE v_video ...... ; IF error_handler(frm=CurFrame, retry_evt='Retry') != ER_OK THEN resume; ENDIF; ... Die Fehlerbehandlungsroutine prüft zunächst, ob ein Deadlock aufgetreten ist. In diesem Fall sendet die Prozedur ein User-Event an das Frame zurück, um die Transaktion neu zu beginnen. War ein anderer Datenbank-Fehler aufgetreten, so wird der Fehler in einem Pop-up Fenster angezeigt. Der Fehler oder die ER_OK Bedingung wird zurückgegeben. PROCEDURE error_handler ( frm = FrameExec, retry_evt = VarChar(32), ) = DECLARE err_no ENDDECLARE /* Calling Frame */ /* Event to signal retry, if deadlock occured */ = Integer NOT NULL BEGIN err_no = CurProcedure.DBSession.ErrorNumber; IF err_no = 49000 THEN /* Deadlock */ message 'Deadlock occured'; frm.SendUserEvent(retry_evt); ELSEIF err_no != Er_OK THEN message 'DBMS error ocurred: ' + dbms_error_message(); ENDIF; return err_no; END; 4.14.9 Transaktionen Eine Transaktion besteht aus einer oder mehreren Datenbankanweisungen, die eine logisch unteilbare Arbeitseinheit ausführen. Skript zur Vorlesung Datenbanken VertiefungSeite 98 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Beispiel: Ein Mitarbeiter soll eine neue Personalnummer bekommen. Dazu müssen außer in der Tabelle mitarbeiter auch Sätze in der Tabelle arbeiten geändert werden. Nur wenn beide Tabellen aktualisiert wurden, ist die Datenbank wieder konsistent. UPDATE mitarbeiter SET m_nr = 39831 WHERE m_nr = 10102; UPDATE arbeiten SET m_nr = 39831 WHERE m_nr = 10102; Commit; Eine Transaktion beginnt automatisch mit der ersten Datenbankanweisung der Anwendung bzw. der Session oder nach einer vorherigen Commit oder Rollback-Anweisung. Per Default dauert die Transaktion solange, bis eine ausdrückliche Commit oder Rollback-Anweisung gegeben wird oder bis die Session oder die Anwendung beendet wird. Alle Locks, die während der Transaktion in Kraft traten, werden bis zum Ende der Transaktion gehalten. Alle Änderungen an der Datenbank werden erst nach Ende der Transaktion für andere Benutzer sichtbar. Mit Hilfe der SQL-Anweisung SAVEPOINT savepointname können bestimmte Punkte in dem Programm gekennzeichnet werden. In diesem Fall ist dann ein partielles Rollback bis zu einem dieser Punkte möglich. INSERT INTO emp_table VALUES (...); UPDATE ... ; SAVEPOINT first; INSERT ... ; DELETE ... ; if error on delete ROLLBACK TO first; ELSEIF other errors ROLLBACK; ... Man kann Ingres anweisen, jede einzelne Datenbankanweisung automatisch zu „commiten“. set autocommit on | off Default ist off. Mit on sollte man sehr vorsichtig sein! Transaktionen werden durch die Anwendung abgebrochen durch • die ROLLBACK-Anweisung oder • ein abgelaufenes Time-out, falls es aktiviert ist. In bestimmten Fällen beendet Ingres selbst eine Transaktion: • Bei einem Deadlock wird die den Deadlock erzeugende Transaktion abgebrochen Skript zur Vorlesung Datenbanken VertiefungSeite 99 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Der Transaction log file kann voll sein • Ein Datenbankfehler ist aufgetreten Im letzteren Fall entscheidet die Wahl von set session with on_error = rollback statement | transaction ob nur die letzte Datenbankanweisung oder die ganze Transaktion zurückgerollt wird. Default ist statement. In bestimmten Fällen wird immer die Transaktion zurückgerollt: • Deadlock • Forced Abort • Lock quota exceeded Anhand von SELECT DBMSInfo('transaction_state') oder inquire_sql(in_transact = transaction); stellt man fest, ob die Transaktion durch Ingres abgebrochen wurde. 4.14.10 Regeln (Rules (Rules)) Benutzerdefinierter Mechanismus, der bei bestimmten Veränderungen an der Datenbank eine Datenbank-Prozedur aufruft. Typen von Datenbankveränderungen: • Insert-, Update- oder Delete-Operation in einer bestimmten Tabelle • Update einer oder mehrerer Spalten einer Tabelle • Änderungen, die einer bestimmten Bedingung zwischen mehreren Spalten einer Tabelle genügen Erzeugt mit CREATE RULE wieder gelöscht mit DROP RULE Anwendungen: • Sicherstellung der Referentiellen Integrität • Sicherstellung anderer, allgemeiner Integritätsregeln • Sicherstellung anderer Regeln 4.14.10.1 Sicherstellung der Referentiellen Integrität Primärschlüssel - Fremdschlüssel Problematik. Tabellen abteilung und mitarbeiter. Spalte abt_nr ist Primärschlüssel in abteilung und Fremdschlüssel in mitarbeiter (Master-Detail Beziehung). Fremdschlüsselwerte müssen sich auf einen identischen Primärschlüssel beziehen; dieser muß existieren. Skript zur Vorlesung Datenbanken VertiefungSeite 100 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 Diese Bedingung kann von einer Datenbankregel beim Ändern von Fremdschlüsseln oder Einfügen entsprechender Sätze mit Fremdschlüsseln überprüft werden. Bei Verstoß kann die Regel unterschiedlich reagieren: • Der Fremdschlüsselwert, der die Integritätsregel verletzt, wird abgewiesen (rejected). D.h. es muß ein rollback geschehen. Dabei hilft die raise error Anweisung. Durch sie wird das DBMS und die Anwendung informiert, daß die Anweisung, die die Regel gefeuert hat, eine Integritätsregel verletzt hat und daher zurückgewiesen wurde. • Die zweite Möglichkeit ist, den Fremdschlüssel ohne passenden Primärschlüssel statt des geforderten Wertes mit NULL zu belegen (nullify). Man kann sich auch andere Möglichkeiten denken. • Die dritte Möglichkeit wird Kaskadieren (cascading) genannt. Handelt es sich um eine Insert- oder Update-Operation, so kann man u.U. den Fremdschlüssel auch als Primärschlüssel verwenden, d.h. diesen ändern oder einen neuen Satz anlegen. Umgekehrt kann man auch entsprechende Reaktionen für die Sätze mit den passenden Fremdschlüsselwerten definieren, wenn ein Primärschlüssel geändert oder die entsprechende Reihe ganz gelöscht wird. • Auch hier kann eine Änderung zurückgewiesen werden. • Beim Löschen werden in allen Sätze mit passenden Fremdschlüsseln diese auf NULL gesetzt • Oder man nutzt die Möglichkeit des Kaskadierens. Beim Ändern werden auch alle zugehörigen Fremdschlüsselwerte geändert. Beim Löschen werden alle Sätze mit passenden Fremdschlüsseln ebenfalls gelöscht. 4.14.10.2 Sicherstellung anderer, allgemeiner Integritätsregeln Angenommen, in unserer Tabelle abteilung sei eine weitere Spalte anz_ma mit der Anzahl der Mitarbeiter in der Abteilung (wenn auch redundante Information). Wenn immer ein neuer Mitarbeiter der Abteilung zugeordnet wird (oder diese verläßt), soll diese Spalte aktualisiert werden. Dies kann eine Regel leisten, unabhängig von der Anwendungslogik. CREATE RULE neuer_ma AFTER INSERT INTO mitarbeiter EXECUTE PROCEDURE neu_ma(abt_nr = mitarbeiter.abt_nr); Jedesmal, wenn ein neuer Mitarbeiter der Tabelle mitarbeiter hinzugefügt wird, wird die Regel automatisch die Tabelle abteilung aktualisieren. 4.14.10.3 Sicherstellung anderer Regeln Man kann Regeln dazu benutzen, bestimmte Geschäftsregeln in der Datenbank zu verankern. Mit der folgenden Regel wird sichergestellt, daß bestimmte Reihen einer Tabelle nur von einem bestimmten Anwender eingefügt oder gelöscht werden können: Skript zur Vorlesung Datenbanken VertiefungSeite 101 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 CREATE RULE check_auth AFTER INSERT, DELETE FROM opcodes WHERE opcodes.scope = 'share' AND user != 'system' EXECUTE PROCEDURE reject_and_log_operation; Oder das Beispiel vom Nachbestellen: CREATE RULE reorder_item AFTER UPDATE(in_stock) OF items WHERE items.in_stock < 100 EXECUTE PROCEDURE reorder_items(id = items.id, items_left = items.in_stock); 4.14.10.4 Beispiel Hiermit wird die Integrität zwischen Mitarbeiter, Manager und Department beim Einfügen neuer Mitarbeiter oder bei einer Änderung für einen Mitarbeiter sichergestellt: • Die Fremdschlüssel in die Manager und Department Tabellen müssen gültig sein • Der Manager muß auch zu der neuen Abteilung des ren Die Regel: CREATE RULE check_emp AFTER INSERT, UPDATE EXECUTE PROCEDURE valid_mgr_dept(ename = mname = dname = Mitarbeiters gehö- OF employee new.name, new.mgr, new dept); Die Datenbankprozedur: CREATE PROCEDURE valid_mgr_dept(ename VarChar(30), mname VarChar(30), dname VarChar(10)) AS DECLARE msg VarChar(80) NOT NULL; check_val Integer; mgr_dept VarChar(10); Skript zur Vorlesung Datenbanken VertiefungSeite 102 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 BEGIN SELECT Count(*) INTO :check_val FROM manager WHERE name = :mname; IF check_val = 0 THEN msg = 'Error 1: Manager "' + :mname + '" not found'; RAISE ERROR 1 :msg RETURN; ENDIF; SELECT Count(*) INTO :check_val FROM dept WHERE name = :dname; IF check_val = 0 THEN msg = 'Error 2: Department "' + :dname + '" not found'; RAISE ERROR 2 :msg RETURN; ENDIF; SELECT dept INTO :mgr_dept FROM manager WHERE name = :mname; IF mgr_dept != dname THEN msg = 'Error 3: Manager "' + :mname + '" and department "' + :dname + '" do not match'; RAISE ERROR 3 :msg RETURN; ENDIF; msg = 'Employee "' + :ename + '" updated ' + '(mgr = "' + :mname + ', ' + dname +'")'; MESSAGE :msg; INSERT INTO emplog VALUES (:msg); END; RAISE ERROR errornumber [errortext] erzeugt einen Datenbankfehler. Wurde die Datenbankprozedur durch eine Regel gefeuert, so rollt Ingres alle Änderungen der Original-Datenbankanweisung, die die Regel auslöste, und alle Änderungen, die die Prozedur bis dahin durchgeführt hat, zurück. errornumber ist eine lokale DBMS-Fehlernummer. Die generische Fehlernummer ist 41300. errornumber und errortext werden an die Anwendung zurückgegeben. Aber nur errortext wird angezeigt. Regeln für Fehlerzustände in Verbindung mit Rules: • SQL-Befehl und Prozedur, die durch Rule gestartet wird, sind eine Einheit! Daher kein Commit oder Rollback in einer DB-Prozedur, die durch eine Rule initiiert wird! Skript zur Vorlesung Datenbanken VertiefungSeite 103 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 • Ein SQL-Befehl kann mehrere Rules auslösen. Die Reihenfolge ist jedoch undefiniert! • Bei Update und Delete feuert die Rule bei jedem betroffenen Satz. • Eine von einer Rule gestartete DB-Prozedur kann wieder eine Rule feuern! Nach 20 Level bricht Ingres die Kette ab! • Tritt ein Fehler in Verbindung mit einer Rule auf, so erleidet der gesamte auslösende SQL-Befehl einen fatalen Fehler: Rollback SQL-Befehl und Rule • Fehler entsteht auch durch „raise error“ 4.15 Aufruf von Subsystemen 4.15.1 Ingres-Subsysteme Das Kommando CALL ruft ein Ingres-Subsystem oder eine Applikation mit oder ohne Parametern auf. CALL subsystem ([parmname1 = wert1, ...]); • Alle Frames werden geblockt bis das aufgerufene Subsystem beendet wird • Implizites Commit ohne Warnung, wenn aus einer offenen Transaktion aufgerufen • Wenn in der laufenden Applikation kein Connect zu einer Datenbank besteht, kann nur RUNIMAGE aufgerufen werden. Subsystem Typen: • CALL APPLICATION ABF-Anwendung • CALL ISQL Interactive SQL • CALL SQL SQL Terminal Monitor • CALL QBF Query-By-Forms • CALL REPORT Report-Writer • CALL RUNDBAPP In derselben Datenbank gespeicherte Windows4GL-Anwendung • CALL RUNIMAGE Windows4GL-Anwendung in Runimage-Form Beispiele: COMMIT; CALL rundbapp(APPLICATION = 'inventory', STARTCOMPONENT = 'parts'); COMMIT; CALL runimage(FILENAME = 'mitarbeiter', STARTCOMPONENT = 'hauptmenu'); COMMIT; CALL qbf(); Skript zur Vorlesung Datenbanken VertiefungSeite 104 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 COMMIT; CALL report(NAME = 'mitarbeiter', MODE = 'column'); 4.15.2 Unix-Aufrufe Mittels des CALL SYSTEM Kommandos werden Betriebssystem-Kommandos aufgerufen. • Können im Hintergrund oder Vordergrund laufen (bei Unix). Hintergrund: Windows4GL-Anwendung bleibt aktiv! • Kein Returncode möglich Syntax und Beispiele: CALL SYSTEM [cmdstring]; CALL SYSTEM 'vi datei.txt'; CALL SYSTEM 'runimage tetris.img'; CALL SYSTEM 'xcalc &'; 4.16 Hilfsfunktionen für OpenROAD-Anwendungen 4.16.1 Erstellen eines Images der Anwendung Speicherung der lauffähigen Anwendung in einer normalen Unix-Datei. Läuft mit wesentlich besserer Performance. makeimage [-uusername] [-f] database application file Parameter: -f Forced Recompilation: Alle Komponenten werden neu übersetzt Starten einer solchen Anwendung: runimage file parameters 4.16.2 Starten einer Anwendung in einer Datenbank Ohne OpenROAD aufzurufen. rundbapp database application parameters 4.16.3 Kopieren einer Anwendung in eine Textdatei Die ganze Anwendung oder Komponenten von ihr. In Verbindung mit der Import-Funktion geeignet, um eine Anwendung auf eine andere Plattform zu portieren: exportapp [-ccomponent] database application file Das Gegenstück: importapp [-ccomponent] database application file Hierbei gibt es noch mehrere mögliche Parameter, s. Handbuch! Skript zur Vorlesung Datenbanken VertiefungSeite 105 FH Wiesbaden, FB Informatik, Prof. Dr. Dreher, SS 1996 4.16.4 Report einer Anwendung Erzeugt eine Dokumentation der Anwendung, z.B. Liste aller Skripts: documentapp [-ccomponent] [-s] database application file Parameter: -s Nur die 4GL-Quellcodes -a Alles (alle Attribute aller Felder): Sehr umfangreich