Embedded SQL mit C und PostgreSQL

Werbung
Embedded SQL mit C und PostgreSQL
Holger Jakobs – [email protected], [email protected]
2008-06-26
Inhaltsverzeichnis
1 Einleitung
2 SQL in Programmen
2.1 SQL-Kommandos im Quelltext
2.2 Verbindung zur Datenbank . . .
2.3 Datenbank ↔ Programm . . . .
2.4 Transaktionen . . . . . . . . . .
1
.
.
.
.
2
3
3
3
5
3 Erstes Beispielprogramm
3.1 Compilation eines SQL-Programms . . . . . . . . . . . . . . . . . . . . . .
3.2 Precompiler-Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
5
5
4 Das SELECT-Kommando in Programmen
5
5 Weitere Embedded-SQL-Kommandos
5.1 Programmieraufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Hinweise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
7
7
6 mehrere Ergebnistupel
6.1 Bulk Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2 Cursor-Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
8
9
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7 Implizite Fehlerbehandlung
11
8 weitere Aufgaben
11
Hinweis: Alle Beispielprogramme finden Sie im Datenbank-Portal unter Quelltexte“.
”
1 Einleitung
SQL kann nicht nur mit interaktiven Kommandos in psql benutzt werden, sondern SQLKommandos können auch in Programme eingebettet werden, die in klassischen Programmiersprachen geschrieben wurden, z. B. C oder C++. Voraussetzung ist, dass neben dem
1
2 SQL IN PROGRAMMEN
Compiler für die jeweilige Sprache der passende Precompiler vorhanden ist, der die eingebetteten SQL-Kommandos in Quelltext der jeweiligen Sprache umsetzt. Im folgenden wird
nur C verwendet, aber es ist mit C++ genauso möglich. Für C++ bietet sich allerdings
auch die Verwendung eines objektorientierten Interfaces an, was unter dem Namen libpq++ zur Verfügung steht. Für C gibt es wahlweise ein Interface mit Bibliotheksaufrufen
unter dem Namen libpq. Diese Interfaces sind nicht so portabel wie Embedded SQL.
Darüber hinaus ist der Zugriff auf Datenbanken über genormte Schnittstellen von Bedeutung, hierbei ist besonders das plattformunabhängige JDBC zu erwähnen, das in einem
separaten Dokument erläutert wird.
Grundsätzlich funktioniert die Programmierung mit Embedded SQL mit allen Programmiersprachen und mit allen SQL-Datenbanken im wesentlichen gleich – sofern sie Embedded SQL überhaupt unterstützen. In Details gibt es aber Unterschiede. Dieser Text bezieht
sich ausdrücklich auf PostgreSQL und C auf Linux-Rechnern.
2 Verwendung von SQL in Programmen
In Programmen werden vorwiegend Datenmanipulationsbefehle benutzt, seltener Administrationskommandos für die Datenbank, die die Struktur verändern.
Es gibt davon vier:
SELECT Suchen von Daten
INSERT
Einfügen von Tupeln
DELETE Löschen von Tupeln
UPDATE Ändern von Attributwerten
Diese Befehle können auf verschiedene Arten Datenmanipulationen durchführen. Nicht alle
Kombinationen sind möglich. Die möglichen Operationen sind angegeben (S=SELECT,
F=FETCH, I=INSERT, D=DELETE, U=UPDATE, WC=DELETE/UPDATE WHERE
CURRENT):
1. Einfache Datenmanipulation: (S, I, D, U)
Alle Operationen betreffen die Übertragung von max. einem Tupel aus der Datenbank
ins Programm oder umgekehrt.
2. Sequentielle Tabellenverarbeitung: (S, F, WC)
Mit Hilfe eines Cursors (Tupelzeiger) können viele Tupel nacheinander bearbeitet
werden.
3. Bulk-Operation: (S, F, I)
Es werden viele Tupel mit einem Befehl bearbeitet.
4. Dynamische Operation: (S, F, I, D, U)
Die Operation wird erst zur Laufzeit spezifiziert. Obwohl bei PostgreSQL alle Ope-
2
2 SQL IN PROGRAMMEN
2.1 SQL-Kommandos im Quelltext
rationen vom Prinzip her dynamisch sind, wird die Erstellung voll dynamischer Programme in diesem Dokument nicht erläutert.
2.1 SQL-Kommandos im Quelltext
Die eingebetteten SQL-Kommandos müssen als solche für den Precompiler kenntlich gemacht werden. Ein SQL-Kommando sieht so aus:
exec sql ... ;
Alles zwischen exec sql und dem Semikolon wird vom SQL-Precompiler in reinen CQuelltext umgewandelt. Das Original-Kommando wird also durch C-Code ersetzt. Gelegentlich kann man es noch im C-Code erkennen, aber das ist nicht immer der Fall. Der
SQL-Precompiler-Output wird anschließend mit dem C-Compiler ganz normal compiliert
und unter Verwendung datenbankspezifischer Bibliotheken gelinkt.
2.2 Verbindung zur Datenbank
Vom fertigen Programm muss eine Verbindung zur Datenbank hergestellt werden, so wie
das bei psql auch der Fall ist. Bei psql wird allerdings beim Aufruf ohne Angabe einer
Datenbank versucht, eine Datenbank zu öffnen, die genauso heißt wie der aufrufende Benutzer. Dies ist bei Embedded-SQL-Programmen nicht der Fall – hier muss man ausdrücklich
ein connect to verwenden. Dies sieht im Programm so aus:
exec sql connect to spieler;
Hierbei ist das Wort to wahlfrei, d. h. es kann auch weggelassen werden. Beim Aufbau der
Verbindung zur Datenbank spieler wird ggf. die Umgebungsvariable PGHOST verwendet,
die den Namen des Datenbankservers enthält. Ist die Variable nicht gesetzt, wird eine
lokale Datenbank dieses Namens angesprochen. Möchte man die Variable PGHOST nicht
berücksichtigen, gibt man für die Datenbank dbname @server an. Wahlweise kann man
sogar einen ganzen URL angeben:
(tcp|unix):postgresql://server [:port ][/dbname ][?options ]
Beispiel: tcp:postgresql://localhost/spieler
2.3 Kommunikation zwischen Datenbank und Programm
Zwischen Programm und Datenbank werden Daten ausgetauscht, z. B. Datenbank → Programm beim select und umgekehrt beim insert. Dies geschieht über sogenannte HostVariablen, die in den SQL-Kommandos durch einen vorangestellten Doppelpunkt gekennzeichnet werden. Es kann sich bei den Variablen um globale oder auch lokale Variablen
handeln. Nullwerte werden, da sie nicht in den Variablen selbst darstellbar sind, über zusätzliche Variablen vom Typ int dargestellt. Diese werden Nullwert-Indikatoren genannt
und haben bei Abfragen folgende Werte:
3
2.3 Datenbank ↔ Programm
Wert
−1
0
>0
2 SQL IN PROGRAMMEN
Beschreibung
Das Attribut hatte den Nullwert
Es war ein Wert vorhanden, der übertragen wurde.
Es war ein Zeichenkettenwert vorhanden, der aber abgeschnitten
wurde.
Man sollte stets Nullwert-Indikatoren verwenden, wenn ein Attribut Nullwerte zulässt, damit es zur Laufzeit keine unerwarteten Fehler gibt. Beim Einfügen von Daten verwendet
man ebenfalls Nullwert-Indikatoren, die man mit −1 füllt, wenn man einen Nullwert übertragen möchte, und ansonsten mit 0. Man kann auch bei not-null-Zeichenketten-Attributen
Indikatoren verwenden, aber dann ist der Indikatorwert 0, wenn alles gut gegangen ist. Für
den Fall, dass die Variable zu kurz war, um den Wert des Attributs aufzunehmen, enthält
die Indikatorvariable die Anzahl Zeichen, die hätten übertragen werden sollen.
Das Datenbanksystem muss dem Programm über die reinen auszutauschenden Daten
hinaus auch noch mitteilen, ob die gewünschten Operationen ausgeführt werden konnten.
Dazu dient die SQL-Communication Area, kurz sqlca. Sie muss im Programm global deklariert werden, was durch das Kommando exec sql include sqlca; geschieht.
Es handelt sich dabei um eine Datenstruktur, von der u. a. folgende Komponenten zur
Status-Überprüfung benutzt werden: sqlcode, sqlerrm.sqlerrmc, sqlerrd[2] (siehe Abschnitt 6.1 auf Seite 8), sqlwarn[0] (siehe Abschnitt 7 auf Seite 11), sqlwarn[1] und
sqlwarn[2] (siehe Kommentare in vom Precompiler umgewandeltem Quelltext).
Die Bedeutung der Variablen sqlcode ist folgende:
Werte
sqlcode = 0
sqlcode < 0
sqlcode = 100
Bedeutung
kein Fehler, keine Warnung
Fehler, Kommando nicht ausgeführt
kein passendes Tupel gefunden
Weitere Werte sind datenbankabhängig. Hier einige von PostgreSQL:
sqlcode-Werte Bedeutung
−201
Zu viele Argumente, evtl. vergessene Host-Variablen in einer
into :var1, :var2 -Liste.
−202
Zu wenige Argumente
−203
Mehrere Ergebnistupel bei einem Single Select.
−221
Keine Verbindung zur Datenbank.
Weitere Werte schauen Sie bitte in den PostgreSQL-Unterlagen nach.
Tip: In dem vom Precompiler erzeugten C-Code ist die SQL Communication Area schön
kommentiert enthalten. Schauen Sie mal rein!
Schon ab dem ANSI-SQL-Standard von 1992 (SQL-92) ist neben dem wenig informativen
sqlcode auch die Fehlerbehandlung mit dem umfangreicheren sqlstate vorgesehen. Dieser
ist in PostgreSQL seit Version 7.4 ebenfalls enthalten. Der ANSI-SQL-Standard von 1999
(SQL-99) kennt nur noch den 5 Zeichen umfassenden sqlstate.
4
4 DAS SELECT-KOMMANDO IN PROGRAMMEN
2.4 Transaktionen
2.4 Transaktionen
Wenn Sie Ihre Programme mit pgc (für C) bzw. pgcpp (für C++) übersetzen, dann wird die
Option -t für den Precompiler gesetzt. Diese bewirkt, dass jedes einzelne SQL-Statement
sofort ausgführt, d. h. commitet wird. Dies ist auch bei psql der Fall.
Wenn Sie mehrere zusammenhängende Änderungen an der Datenbank vornehmen wollen, müssen Sie daher explizit begin work und commit work bzw. rollback work verwenden (jeweils mit exec sql davor).
Ohne die Option -t würde automatisch immer eine Transaktion begonnen, so dass Änderungen ohne ein anschließendes commit work immer zurückgefahren würden. Nach einem
commit work oder einem rollback work würde auch immer sofort eine neue Transaktion
begonnen, so dass das Kommando begin work nicht benutzt werden müsste oder dürfte.
Dies entspräche dann dem ANSI-SQL-Standard, der begin work gar nicht kennt. Wenn Sie
möchten, können Sie es sich ja entsprechend einrichten oder wahlweise in Ihrem Programm
ein exec sql set autocommit to off verwenden.
Der ANSI-SQL-Standard von 1999 (SQL-99) kennt allerdings ein START TRANSACTIONStatement, das aber nur dazu dient, einen Isolation Level“ für die Transaktion anzugeben.
”
Das geht in PostgreSQL seit Version 7.4 auch, früher statt dessen mit SET TRANSACTION.
3 Erstes Beispielprogramm
Das Beispielprogramm finden Sie in der Datei sqlprogc_beispiel1.sql; es wird die bekannte Sportlerdatenbank spieler verwendet (bzw. die Tabelle spieler darin).
3.1 Compilation eines SQL-Programms
Ein C-Programm mit Embedded SQL muss mehrfach übersetzt werden. Daher haben wir
auf unserer Maschine ein kleines Script für diesen Zweck, das genauso heißt wie die Endungen der Quelltexte: pgc für C und pgcpp für C++. Tatsächlich ist es nur ein einziges
Script, siehe /usr/local/bin/pgc
3.2 Vom SQL-Precompiler umgewandelte EXEC-SQL-Aufrufe
Übersetzen Sie das Beispielprogramm sqlprogc_beispiel1.pgc und drucken Sie den
Quellcode sowie den erzeugen C-Quelltext sqlprogc_beispiel1.c aus. Vergleichen Sie
die Dateien und suchen Sie die exec sql-Anweisungen im übersetzten Code.
4 Das SELECT-Kommando in Programmen
Nach der Eingabe von Daten mit dem insert-Kommando wollen wir nun Daten aus dem
Bestand abfragen. Vom select-Kommando gibt es mehrere Varianten: das Single Select,
das Bulk Select und das Sequential Select. Das erste wird hier erläutert, das Bulk Select
5
5 WEITERE EMBEDDED-SQL-KOMMANDOS
in Abschnitt 6.1 auf Seite 8 und das Sequential Select (auch Cursor-Select genannt) in
Abschnitt 6.2 auf Seite 9.
Das Single Select muss so formuliert sein, dass das Ergebnis nicht mehr als ein einziges
Tupel umfasst; andernfalls kommt es zu einem Fehler (−203 in sqlca.sqlcode).
Das Programm stellt für das Ergebnis des Kommandos nur einen Datensatz zur Verfügung, der sofort gefüllt wird, sofern ein Tupel gefunden wird. Beispiel für eine Deklaration:
exec sql begin declare section;
char
SpielName [16];
/* alle Zeichenketten 1 Byte laenger, */
char
Vorname
[ 4];
/* damit Platz fuer Terminator \0 ist. */
char
PLZ
[ 6];
char
Ort
[21];
exec sql end declare section;
Zeichenketten sollten grundsätzlich 1 Byte länger als in SQL deklariert werden, damit
Platz für das Terminatorzeichen '\0' ist, was auch automatisch angehängt wird. Das SelectStatement sieht im Programm dann so aus:
exec sql select spielname, vorname, plz, ort
into :spielname, :vorname, :plz, :ort
from spieler
where spielnr = :spielnr;
Dieses Select erzeugt wegen der Eindeutigkeit der Spielernummer garantiert höchstens ein
Tupel. Die Variable sqlcode aus der sqlca kann den Wert 100 enthalten, wenn kein passenden Tupel enthalten ist, oder es kann ein Fehler auftreten, wenn mehr als ein passendes
Tupel gefunden wurde. Die Select-Kommandos können auch bei einem Single Select beliebig
komplex sein. Ein komplettes Beispielprogramm mit einem Single-Select finden Sie unter
singleselect.pgc. Schauen Sie es sich mit besonderem Augenmerk auf die Behandlung
von Nullwerten an.
Bei allen Attributen, die Nullwerte enthalten können (also nicht mit not null versehen sind), ist es zwingend erforderlich, dass ein Nullwert-Indikator verwendet wird. Dabei
handelt es sich um eine Integer-Host-Variable, die hinter dem Attribut ohne Komma dazwischen angegeben wird. Diese Variable wird mit einer Null gefüllt, wenn ein gültiger Wert
geliefert wurde (also logisch false). Falls im Attribut ein Nullwert stand, bekommt der
Nullwert-Indikator einen Wert ungleich Null (also logisch true).
5 Weitere Embedded-SQL-Kommandos
Im folgenden sollen Sie außer dem Single Select und dem Insert auch Delete und Update
einsetzen. Delete unterscheidet sich nicht von der interaktiven Version. Bei Update lautet
die Syntax: exec sql update tabellenname set attribut = attributwert /:hostvariable where bedingung ;
6
5 WEITERE EMBEDDED-SQL-KOMMANDOS
5.1 Programmieraufgaben
5.1 Programmieraufgaben mit Embedded SQL zur
Krankenhaus-Datenbank
Schreiben Sie ein C-Programm, das folgendes leistet:
1. Erfassen von Daten (insert)
ˆ
ˆ
ˆ
ˆ
ˆ
ˆ
ˆ
Station
Zimmer
Medikament
Patient (Zimmer darf nicht überbelegt werden!)
Angestellter/Arzt (Fach + Telefonnummer oder keins von beiden!)
Behandlung
Verordnung
2. Lesen von Daten (single select)
Daten für ein Zimmer anzeigen, d. h. Stationsname, Zimmernummer und Anzahl der
freien und belegten Betten.
3. Verändern von Daten (update)
ˆ Verlegen eines Patienten von einem Zimmer in ein anderes, wobei auch hier das
neue Zimmer nicht überbelegt werden darf.
ˆ Eintragen/Ändern/Löschen einer Diagnose
4. Löschen von Daten (delete)
Entlassung eines Patienten, wobei nach Eingabe der Patientennummer sämtliche den
Patienten betreffenden Daten aus der Datenbank gelöscht werden.
Möchten Sie Ihren Quelltext in mehrere Dateien zerlegen, verwenden Sie statt des aus C
bekannten #include abc.h die Anweisung exec sql include abc , damit das Einfügen
bereits beim Precompiling ausgeführt wird. Andernfalls kennt der Precompiler die Inhalte
der eingefügten Dateien nicht und meldet Fehler.
5.2 Hinweise zu den Programmieraufgaben
Arbeiten Sie mit Haupt- und Untermenüs, wobei das Hauptmenü die obigen Punkte zur
Auswahl anbietet. Beginnen Sie jede einzelne Transaktion mit einem begin work und
schließen Sie sie mit commit work ab. Ohne dies würde jede einzelne Aktion auf der Datenbank in einer eigenen Mini-Transaktion ablaufen (sogenanntes auto-commit). Möchten
Sie zwischendurch eine Transaktion abbrechen, so verwenden Sie rollback work.
Vor der Programmierung sollen Struktogramme entworfen werden, die die SQL-Anweisungen bereits enthalten. Dies dient der Konzentration auf Abläufe und Datenbanken, ohne
gleichzeitig an die Programmiersprache denken zu müssen.
Bauen Sie Fehlerbehandlungen mit Klartext-Meldungen in Deutsch für alle Fehlersituationen ein, die Sie vorhersehen können, beispielsweise Station nicht vorhanden!“ bei der
”
Zimmereingabe. Nur für nicht vorhersehbare Fehler soll die Standard-Fehlerbehandlung
einer Funktion StatusAnzeige() oder ähnlich verwendet werden.
7
6 MEHRERE ERGEBNISTUPEL
6 Abfragen mit mehreren Ergebnistupeln
Wenn bei einem SELECT mehr als ein einziges Tupel herauskommen kann, reicht ein Single
Select nicht aus. Man muss ein Bulk Select oder eine sequentielle Tabellenverarbeitung mit
cursor und fetch machen.
6.1 Bulk Select – Abfragen mehrerer Tupel auf einmal
Bei einem Bulk Select wird die Ergebnistabelle auf einmal in den Datenbereich des Programms übertragen. Das bedeutet, dass das Programm einen ausreichend großen Datenbereich zur Verfügung stellen muss. Der Programmierer muss also schon zur Compilationszeit abschätzen, wieviele Tupel/Datensätze beim Select entstehen können.
Im Gegensatz zum Single Select werden hier nicht einzelne Host-Variablen angelegt,
sondern Arrays.
exec sql begin declare section;
int nr
[20];
char spielname [20][30];
exec sql end declare section;
Bei obiger Deklaration war keine Indikatorvariable notwendig, weil alle Attribute mit not
null versehen sind. Werden Indikatorvariablen benötigt, so sind sie ebenfalls als Array zu
deklarieren. Die Aufrufsyntax des Bulk Select unterscheidet sich vom gewöhnlichen Select
kaum und sieht beispielsweise so aus:
exec sql select spielnr, spielname
into :nr, :spielname
from spieler;
In der Variablen sqlerrd [2] der SQL Communication Area teilt uns SQL mit, wieviele
Tupel in unser Array übertragen worden sind. Dies ist eine weitere Statusinformation über
den sqlcode hinaus. Zur Weiterverarbeitung in unserem Programm benötigen wir diese
Angabe z. B. in einer for-Schleife:
for (i=0; i<sqlca.sqlerrd[2]; i++) {
printf ("Spieler[%d]: %d, %s\n",
i, nr[i], spielname[i]);
}
Sollte es mehr Spieler geben als in die Arrays passen, so gibt es wieder den Fehlerstatus -203
in sqlca.sqlcode. Die Variable sqlca.sqlerrd[2] enthält jetzt zwar auch die Anzahl der
passenden Tupel, aber die Daten sind nicht übertragen worden und dürfen daher auch nicht
verwendet werden.
Ein vollständiges Beispielprogramm finden Sie in bulkselect.pgc
8
6 MEHRERE ERGEBNISTUPEL
6.2 Cursor-Select
6.2 Sequentielle Tabellenverarbeitung mit Cursor
Bei der sequentiellen Tabellenverarbeitung wird von SQL im Hintergrund eine Tabelle erzeugt, die das Ergebnis des Select enthält. Das Programm benötigt wie beim Single Select
nur die Variablen für ein einziges Tupel, weil diese aus der Hintergrundtabelle wie aus einer
sequentiellen Datei gelesen werden. Ein Cursor dient als Satzzeiger auf die Hintergrundtabelle.
Bei diesem sequential Select“ muss man nicht vorher wissen, wieviele Tupel die Ergeb”
nistabelle enthalten wird. Dafür ist die Anzahl der Zugriffe auf die Datenbank viel höher,
und Sperren auf den Datenbestand bestehen im Allgemeinen länger.
Folgende Embedded-SQL-Kommandos gibt es für das Arbeiten mit sequential Selects:
declare cursorname cursor for selectcommand ;
verbindet einen Cursor über seinen Namen mit einem spezifizierten Select-Kommando. Der
Cursor ist ein Zeiger auf ein Tupel der Ergebnistabelle, die durch das Select erzeugt werden
kann. Die Ergebnistabelle wird durch declare cursor noch nicht erzeugt. (Hinweis: Bei
PostgreSQL tut dieses Kommando gar nichts; der Precompiler merkt sich nur die CursorDeklaration für später.)
open cursorname ;
führt das mit dem Cursor verbundene Select-Kommando tatsächlich durch und erzeugt
die Ergebnistabelle (active set) im Hintergrund. Der Cursor steht jetzt vor dem ersten
Tupel. Ab jetzt können Sperren auf die Datenbank bestehen. Bei PostgreSQL darf ein
Cursor nur innerhalb einer Transaktion geöffnet werden, d. h. nicht im Autocommit-Modus.
Andernfalls erscheint die Meldung, dass das Deklarieren (nicht das Öffnen) eines Cursors
nur innerhalb einer Transaktion erlaubt sei. (Hinweis: Bei PostgreSQL wird erst jetzt der
Cursor tatsächlich deklariert und gleichzeitig geöffnet.)
fetch cursorname into hostvariablen ;
bewegt den Cursor auf das nächste Tupel der Ergebnistabelle und überträgt die Werte des
neuen Tupels in die Hostvariablen.
delete from tablename where current of cursorname ;
löscht das Tupel, auf das der Cursor zeigt. Der Cursor steht anschließend vor dem nächsten
Tupel (nicht auf dem nächsten). Diese Kommandovariante funktioniert bei PostgreSQL
erst ab Version 8.3.
update tablename set columnname = expression where current of cursorname ;
verändert in dem Tupel, auf das der Cursor zeigt, das genannte Attribut entsprechend dem
im Ausdruck angegebenen Wert. Es können mehrere Anweisungen columnname = expression verwendet werden, die durch Komma getrennt werden. expression kann ein
SQL-Ausdruck, eine Hostvariable (ggf. mit Nullwertindikator), ein Literal oder NULL sein.
Diese Kommandovariante funktioniert bei PostgreSQL erst ab Version 8.3.
9
6.2 Cursor-Select
6 MEHRERE ERGEBNISTUPEL
close cursorname ;
deaktiviert die Ergebnistabelle (active set). Nach close sind fetch, delete where current
und update where current nicht mehr möglich. Durch commit work und rollback work
werden alle Cursor ebenfalls geschlossen.
Tipp: Als Alternative zu update/delete where current empfiehlt es sich bei PostgreSQL bis Version 8.2 einschließlich, den Primärschlüssel beim Select mit abzufragen und
ein update/delete mit Bezug auf diesen auszuführen. Da where current ohnehin nur bei
sehr einfachen Abfragen (nicht bei Verwendung von Aggregatfunktionen und auch nicht
bei Verbundoperationen) funktioniert, ist dies auch bei anderen Datenbanken die gängige
Vorgehensweise, z. B. auch empfohlen von Oracle.
Um die Strafentabelle mit sequentieller Tabellenverarbeitung anzuzeigen, programmiert
man beispielsweise so (ohne die notwendige Fehlerprüfung):
exec sql declare kursor cursor for
select zahlnr, strafen.spielnr, spielname, datum, betrag
from strafen natural join spieler;
exec sql open kursor;
while (1) {
exec sql fetch kursor
into :zahlnr, :spielnr, :spielname, :datum, :betrag;
if (sqlca.sqlcode != 0) break;
printf ("%4d %4d %-30s %10s %10.2f\n",
zahlnr, spielnr, spielname, datum, betrag);
}
exec sql close kursor;
Es finden bei der sequentiellen Tabellenverarbeitung dieselben Fehlercodes Anwendung wie
beim Single Select. Es wird aus der Ergebnistabelle gelesen, bis kein Tupel mehr übrig ist
– dann wird sqlca.sqlcode den Wert 100 erhalten.
Das vollständige Programm (mit der notwendigen Fehlerprüfung) finden Sie in der Datei
cursor_strafen.pgc
Aufgabe: Bauen Sie in die Patientenaufnahme ein, dass bei der Eingabe eines belegten
Zimmers eine Liste aller anderen Zimmer derselben Station angezeigt wird, in denen noch
Betten frei sind. Sind auf der Station keine Zimmer mehr frei, soll auch dies im Klartext
gemeldet werden.
10
8 WEITERE AUFGABEN
7 Implizite Fehlerbehandlung
Bislang haben wir alle Fehler, die in den Programmen auftreten konnten, explizit behandelt,
d. h. die Rückmeldungen von SQL wurden von unseren Programmcode ausgewertet. SQL
bietet aber noch eine andere Art der Fehlerbehandlung an, bei der man weniger schreiben
muss, dafür aber auch weniger Möglichkeiten hat. Am Anfang des Programms wird eine
whenever-Anweisung verwendet:
exec sql whenever condition action ;
Die Parameter dieser Anweisung sind:
Anweisungsteil Schlüsselwort Bedeutung
condition
sqlerror
sqlcode < 0
sqlwarning
sqlwarn [0] = ’W’
not found
sqlcode = 100
action
stop
Programmende mit rollback work
continue
keine Aktion, evtl. Fehler ignorieren
goto label
Sprung zur Marke (igitt!!)
do break
führt break aus (nur PostgreSQL)
sqlprint
zeigt Meldungen an (nur PostgreSQL)
Solange keine whenever-Anweisung im Programm verwendet wurde, gilt die Aktions-Voreinstellung continue. Das Kommando whenever condition continue wird nur dazu
gebraucht, ein früheres whenever mit einer anderen Aktion wieder auf diesen voreingestellten Wert zurückzusetzen. Die mit whenever festgelegten Aktion gelten solange, bis ein
erneutes whenever verwendet wird. Die whenever-Anweisungen werden vollständig vom
Precompiler ausgewertet und bewirken die Erzeugung von entsprechenden C-Anweisungen.
Eine stop-Aktion hält das Programm an, zeigt aber leider keinerlei Meldung über den
Grund an. Eine continue-Aktion kann man nur dann einstellen, wenn man die Fehlerbehandlung selbst übernimmt. Ignorierte Fehler führen zu nicht absehbaren Reaktionen des
Programms. Das Springen zu einer Marke sollte nur in Notfällen verwendet werden. Eine
do break-Aktion gibt es nur bei PostgreSQL; außerdem ist hierbei zu berücksichtigen, dass
ein break nur in einer Schleife oder einem switch-Statement auftauchen darf. Fügt der
Precompiler es an anderer Stelle ein, so erzeugt das einen Fehler beim C-Compiler. Eine
sqlprint-Aktion ist ebenfalls nur bei PostgreSQL zu verwenden und zeigt Fehler lediglich
an, ohne sie wirklich zu behandeln. Das kann zu Fehlersuche verwendet werden, wenn man
ansonsten alle Fehler selbst behandelt.
8 weitere Aufgaben
1. Auch wenn Sie es außerhalb dieser Übung niemals verwenden: Schlagen Sie in einem
C-Buch nach, wie man mit goto umgeht und bauen Sie es in ein Programm mit
whenever ein.
11
8 WEITERE AUFGABEN
2. Verwenden Sie whenever in einem Ihrer Programme und schauen Sie nach, was der
SQL-Precompiler aus Ihren SQL-Statements macht. Was verändert sich gegenüber
der Voreinstellung? Vergleichen Sie mit SQL-Precompiler-Outputs ohne whenever.
Tip hierzu: Vergleichen Sie die zwei C-Quellcodes mit tkdiff (geht nur unter X).
3. Schauen Sie sich das Beispielprogramm whenever.pgc zur Verwendung von whenever
an. Bauen Sie in das Programm folgende Fehler ein und schauen Sie, wie das Programm darauf reagiert:
a) connect-Problem: Geben Sie eine Datenbank an, die nicht existiert.
b) select-Problem: Geben Sie eine Tabelle oder ein Attribut an, das nicht existiert.
c) select-Problem: Verwenden Sie bei einem Attribut einen falschen Datentyp.
(Dieser Fehler wird nicht in jedem Fall gefunden, weil das Datenbanksystem
oder der vom embedded SQL eingefügte Code hier nach Möglichkeit Typenkonversionen einbaut.)
$Id: SQL-Programmierung-C-Postgres.tex,v 1.2 2008-06-26 10:57:13 hj Exp $
12
Herunterladen