Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Linux-UNIX-Programmierung von Jürgen Wolf
Das umfassende Handbuch – 2., aktualisierte und erweiterte Auflage 2006
Buch: Linux-UNIX-Programmierung

Linux-UNIX-Programmierung
1216 S., mit CD, 49,90 Euro
Rheinwerk Computing
ISBN 3-89842-749-8
gp Kapitel 12 MySQL und PostgreSQL
  gp 12.1 Relationales Datenbanksystem
  gp 12.2 Relationaler Datenbankserver
  gp 12.3 SQL-Server im Überblick
  gp 12.4 MySQL
    gp 12.4.1 Anwendungsgebiete von MySQL
    gp 12.4.2 Schnittstellen von MySQL
    gp 12.4.3 Installation von MySQL
    gp 12.4.4 MySQL-Server starten und stoppen
    gp 12.4.5 Konfigurationsdatei my.cnf
    gp 12.4.6 Kommandozeilenwerkzeuge für und von mysql
    gp 12.4.7 Grafische Clients
    gp 12.4.8 MySQL-Crashkurs
    gp 12.4.9 Datentypen
    gp 12.4.10 Datenbank anlegen, verwenden und löschen
    gp 12.4.11 Tabelle anlegen
    gp 12.4.12 Schlüsselfelder (Tabellen anlegen)
    gp 12.4.13 Indizes
    gp 12.4.14 Tabellentypen (Tabellen anlegen)
    gp 12.4.15 Autowerte definieren
    gp 12.4.16 Tabellen umbenennen und ändern
    gp 12.4.17 Daten einfügen, ändern und löschen
    gp 12.4.18 Daten importieren
    gp 12.4.19 Datenausgabe
    gp 12.4.20 NULL ist 0 oder undefiniert?
    gp 12.4.21 Unscharfe Suche
  gp 12.5 MySQL C-API
    gp 12.5.1 Verbindung mit dem MySQL-Server aufbauen
    gp 12.5.2 Aufgetretene Fehler ermitteln – mysql_errno() und mysql_error()
    gp 12.5.3 Schließt die Verbindung zum Server – mysql_close()
    gp 12.5.4 Erstes Beispiel
    gp 12.5.5 Verschiedene Informationen ermitteln
    gp 12.5.6 Datenbanken, Tabellen und Felder ausgeben (MYSQL_RES)
    gp 12.5.7 Ergebnismenge zeilenweise bearbeiten (MYSQL_ROW)
    gp 12.5.8 Ergebnismenge spaltenweise einlesen (und ausgeben) (MYSQL_FIELD)
    gp 12.5.9 Ein Beispiel
    gp 12.5.10 Ergebnismenge – weitere Funktionen
    gp 12.5.11 Befehle an den Server – mysql_query() und mysql_real_query()
    gp 12.5.12 Weitere Funktionen
    gp 12.5.13 Veraltete Funktionen
    gp 12.5.14 Neue Funktionen ab Version 4.1.x
  gp 12.6 Beispiel eines Newssystems mit MySQL
    gp 12.6.1 Die Headerdatei my_cgi.h
    gp 12.6.2 (Pseudo-)Planung
    gp 12.6.3 Datenbank und Tabellen anlegen
    gp 12.6.4 MySQL-Clients mit GUI
    gp 12.6.5 Randnotiz
  gp 12.7 Neue SQL-Funktionen für die Shell – MySQL erweitern
  gp 12.8 MySQL-Funktionen mit der UDF-Schnittstelle entwerfen
    gp 12.8.1 UDF-Sequenzen
    gp 12.8.2 UDF_INIT-Struktur
    gp 12.8.3 UDF_ARGS-Struktur
    gp 12.8.4 Rückgabewert
    gp 12.8.5 Benutzerdefinierte Funktionen erstellen
    gp 12.8.6 Benutzerdefinierte Funktion kompilieren, installieren und ausführen
  gp 12.9 PostgreSQL – objektrelationales Datenbankverwaltungssystem
    gp 12.9.1 PostgreSQL im Vergleich zu MySQL
    gp 12.9.2 Unterschiede in der Syntax zwischen MySQL und PostgreSQL
    gp 12.9.3 PostgreSQL installieren
    gp 12.9.4 Konfigurationsdateien bei PostgreSQL – (postgresql.conf, pg_hba_conf)
    gp 12.9.5 CRASHKURS PostgreSQL
    gp 12.9.6 PostgreSQL C-API – libpg
    gp 12.9.7 Umgebungsvariablen und Passwortdatei
    gp 12.9.8 PostgreSQL und Threads
    gp 12.9.9 Ausblick


Rheinwerk Computing

12.9 PostgreSQL – objektrelationales Datenbankverwaltungssystem  downtop

Zwar ist MySQL inzwischen die wesentlich verbreitetere Open-Source-Datenbank, doch für anspruchsvollere Anwendungen hinkt sie immer noch weit hinter PostgreSQL her – so dass ich es als Autor nicht ignorieren kann. Die Messlatte von PostgreSQL liegt eher schon in der Höhe von Oracle oder DB2. Aber auch für Webanwendungen, wo MySQL immer noch die unumstrittene Dominanz hat, breitet sich PostgreSQL immer mehr aus. Dabei hat PostgreSQL natürlich auch einige Jahrzehnte Entwicklungszeit hinter sich und entstand, wie so vieles auch, in der kalifornischen Universität in Berkeley aus dem POSTGRES-Paket.


Hinweis   Natürlich gilt auch hier, wie schon bei der Einführung in MySQL, dass diese kurze Einführung keine Alternative zu einem guten Buch oder einer Dokumentation ist. Hier handelt es sich lediglich um einen Streifzug vorbei an PostgreSQL. Ich werde hierbei nur auf das Nötigste eingehen, um im weiteren Verlauf des Buches mit der C-API von PostgreSQL arbeiten zu können.



Rheinwerk Computing

12.9.1 PostgreSQL im Vergleich zu MySQdowntop

Um es gleich vorwegzunehmen: Auf die Frage nach einem direkten Vergleich, welches Datenbanksystem denn nun besser sei, verzichte ich hier. Sie lässt sich wohl auch nicht beantworten, da der Zweck die Wahl der Software bestimmen sollte. Im Internet finden sich diesbezüglich viele Webseiten, die sich mit dem Performance-Vergleich dieser beiden Datenbanken beschäftigen. Daher vorneweg die »Nachteile« von PostgreSQL gegenüber MySQL.

gp  PostgreSQL läuft gewöhnlich nur auf UNIX-artigen Rechnern am effektivsten. Das kann Ihnen als Linux-Programmierer zwar egal sein, aber den großen Firmenbossen ist das nicht egal.
gp  PostgreSQL ist ein wenig schwieriger zu handhaben – dies trifft vor allem auf die Umsteiger von MySQL zu PostgreSQL zu.
gp  Für PostgreSQL gibt es eindeutig weniger Literatur und Dokumentationen – was ein sehr gravierender Punkt ist, um ein Datenbanksystem für die Masse bekannt zu machen.
gp  Der Bekanntheitsgrad von MySQL ist höher – oder haben Sie zuvor schon von PostgreSQL gehört?
gp  Die meisten Internet-Provider und Webhoster bieten nur MySQL an – aber hier ist allmählich ein Umbruch zu spüren.

Wenn für Sie keiner dieser Nachteile einer sein sollte, können Sie zu PostgreSQL greifen. Sie erhalten hiermit ein Datenbanksystem, was mindestens genauso schnell wie MySQL ist (z. T. ist PostgreSQL schneller). Und im Gegensatz zu vielen Falschaussagen werden auch Skriptsprachen wie z. B. PHP oder Perl mittlerweile recht gut unterstützt. Viele Anwendungen laufen mittlerweile nur mit PostgreSQL. Gerade im Administrationsbereich findet man oft Programme mit PostgreSQL-Unterstützung, aber ohne MySQL (Mail-Server, Warenwirtschaftssysteme). Nebenbei muss man auch anmerken, dass PostgreSQL einige Features mehr besitzt als MySQL. Darüber wird hier zwar nicht geschrieben, aber diese (vor allem im professionellen Bereich) Punkte will ich Ihnen nicht vorenthalten:

gp  Views
gp  Procedural Languages
gp  Triggers
gp  Customizable aggregates
gp  bessere Transaktionen
gp  Cluster – Diese ermöglichen es einem Admin, jedem User sein eigenes Cluster zu geben.
gp  Point-in-Time Recovery (ab Version 8.x) – Damit lässt sich eine zerschossene DB vom Zeitpunkt DD.MM.YYYY wiederherstellen. Alles, was dazwischen war, ist Geschichte. Zusätzlich kann man damit wohl Backups in der Zeit nach hinten versetzt fahren; also ein Backup der DB machen, wie sie vor x Sekunden aussah.
gp  Tablespaces Simplify Disk Layout (ab Version 8.x) – Als Admin kann man das Dateisystem für die Datenbanken, Indizes, Schemas usw. auswählen. So wie ich das verstehe, kann man so z. B. einen Index auf einem anderen Dateisystem lagern. Die Performance wäre der Hammer :-)

Hinweis   Ab Version 8 von PostgreSQL benötigt man für Windows keine Cygwin-Umgebung mehr, sondern man kann nativ mit Installer installieren. Somit ist diese Version die erste, die nativ auf MS Windows als Server ausführbar ist - ein weiteres (erhebliches) Plus für PostgreSQL.



Rheinwerk Computing

12.9.2 Unterschiede in der Syntax zwischen MySQL und PostgreSQdowntop

PostgreSQL hält sich im Gegensatz zu MySQL (und auch MS Access) sehr an den ANSI SQL-Standard. Wenn Sie von diesen Datenbanksystemen kommen, kann einiges sehr fremd für Sie sein. Hier einige Dinge, wo Sie ein wenig umdenken müssen:

gp  MySQL verwendet das Zeichen '#' für den Beginn einer Kommentarzeile. PostgreSQL verwendet stattdessen ANSI-konformes '--' (double dash). Dies versteht übrigens MySQL auch!
gp  MySQL verwendet ' oder " zum Quoting von Werten (wie z. B. name = 'Jack' oder name = "Jack"). ANSI schreibt das einzelne Singelquoting ' vor – und PostgreSQL nutzt nur dieses. Doppeltes Quoting wird bei PostgreSQL hingegen für System Identifiers, Feldnamen oder Tabellennamen verwendet (z. B. ... WHERE "first name" = 'Jack'). MySQL verwendet hierfür stattdessen ` (Backtick) – was wiederum nicht dem Standard entspricht.
gp  PostgreSQL ist case-sensitiv bezüglich der Stringvergleiche. Das Feld 'Jack' ist nicht das gleiche wie das Feld 'jacK'. MySQL und auch MS Access sind diesbezüglich case-INsensitiv! Wollen Sie bei PostgreSQL erreichen, dass nicht auf Groß- und Kleinbuchstaben geachtet wird, müssen Sie entweder ILIKE oder *~ verwenden.
gp  Es gibt zwischen PostgreSQL und MySQL einige Unterschiede bezüglich der Verwendung des Datums und der Funktionen, die mit Datum arbeiten.
gp  In MySQL können Sie die logischen C-Operatoren || und && verwenden – was hierbei auch für das Verknüpfen eines logischen ODER und eines logischen UND steht. In PostgreSQL steht das || für das Aneinanderhängen von Strings (was auch dem Standard entspricht (z. B. 'test' || 'string' = 'teststring')).

Es gibt sicherlich noch eine Menge Differenzen, aber diese hier sollten für den Umstieg die wichtigsten gewesen sein.


Rheinwerk Computing

12.9.3 PostgreSQL installieredowntop

Es gibt zwei Möglichkeiten der Installation von PostgreSQL, zum einen wäre dies, das Softwarepaket, was Ihnen Ihre Distribution mitliefert, zu installieren, und zum anderen, die Software selbst zu installieren. Sofern Ihrer Distribution PostgreSQL beiliegt (sollte eigentlich immer der Fall sein), empfehle ich Ihnen erst einmal, dies zu installieren, da Ihnen hiermit eine Menge Konfigurationsarbeit abgenommen wird und Sie gleich nach der Installation mit PostgreSQL beginnen können. Somit können Distributionsinstallierer die folgenden Schritte dieses überspringen.

Sofern Ihre Distribution kein PostgreSQL mitgeliefert hat, sollten Sie über eine Neuanschaffung nachdenken (war nur ein Test, ob Sie noch bei der Sache sind :-)). Wenn Sie also den steinigen Weg der Quelltextdistribution gehen müssen, müssen Sie nach dem Auspacken mit tar den üblichen ./configure- und make-Weg gehen. Zu den unendlich vielen Optionen für spezielle Einstellungen zu ./configure sei die Dokumentation zurate gezogen. Nach dem make-Aufruf kann die eigentliche systemweite Installation mit make install vollzogen werden.

Nachdem das Datenbanksystem von PostgreSQL installiert wurde, benötigen Sie einen Systembenutzer – nicht gleichzusetzen mit Datenbankbenutzern. Der Systembenutzer ist der Besitzer, dem später die Datenbankdateien gehören. Unter SUSE und RedHat wird hierfür postgres als Namen verwendet, pgsql ist noch ein gängiger Name. Einen solchen Benutzer können Sie je nach Distribution (Debian liefert z. B. das wirklich hervorragende adduser) ganz einfach mit folgender Eingabe erzeugen (root):

# useradd -mk /var/lib/empty -s /bin/false postgres

Jetzt können Sie als root anschließend über das su-Kommando auf den Benutzer postgres zugreifen. Bevor Sie dies allerdings tun, müssen Sie zuerst eine initiale Datenbank erzeugen, um PostgreSQL zu starten. Es handelt sich dabei um eine Verzeichnisstruktur, die Sie mit dem Programm initdb erzeugen müssen. Das Programm soll jetzt mit dem Systembenutzer, den Sie eben erstellt haben, als Datenverzeichnis /usr/local/pqsql/data angewendet werden (wieder Root-Rechte nötig).

# mkdir /usr/local/pgsql/data 
# chown postgres /usr/local/pgsql/data 
# su - postgres 
postgres # initdb --pwprompt -D /usr/local/pgsql/data 

Durch den Aufruf von initdb wurde eine Datenbank (genauer: das Datenbank-Cluster initdb erzeugt in dem Sinne keine Datenbank) mit dem Namen template1 erzeugt. Diese Datenbank benötigt PostgreSQL als Vorlage zum Erzeugen von neuen Datenbanken – daher Finger weg von dieser Datenbank.

Hier wurde auch gleich mit dem Flag –pwprompt ein Passwort für den Administrator gesetzt. Wird keines verwendet, dann gibt es kein gültiges Passwort, und es kann alles angegeben werden. Für den Verlauf des Buchs benötigen Sie allerdings keine Passwortabfrage und können somit das Flag erst einmal weglassen. Es lohnt sich, die Optionen von initdb genauer zu betrachten, da Sie mit diesem Programm auch Voreinstellungen für das neue Cluster festlegen. Insbesondere die Optionen –encoding, --locale, --username und –pgdata verdienen genauere Beachtung. Wenn diese Schalter nicht gesetzt werden, benutzt initdb die Umgebungsvariablen der Shell und als Clusterverzeichnis ein Verzeichnis data in dem Verzeichnis, in dem Sie sich gerade befinden. Sie können die meisten der Optionen also auch in der Datei ~/.profile des PostgreSQL Users festlegen, um diese nicht bei jedem Cluster wieder eingeben zu müssen.


Rheinwerk Computing

12.9.4 Konfigurationsdateien bei PostgreSQL – (postgresql.conf, pg_hba_conf)  downtop

In diesem Buch werden Sie die Konfigurationsdatei zwar nicht benötigen, aber es kann recht hilfreich bei Problemen sein – besonders bei den Zugriffsrechten –, wenn Sie wissen, unter welchem Namen Sie diese auf Ihrem System finden.

Die Konfigurationsdateien bei PostgreSQL lauten hier postgresql.conf und pg_hba.conf. Die erste Datei ist dabei die eigentliche Konfigurationsdatei, und mit der zweiten Datei stellt man die Zugriffsbeschränkungen ein. Jedes Cluster besitzt seine eigenen Konfigurationsdateien.

Die Konfigurationsdatei postgresql.conf werden Sie wohl nicht so schnell verändern müssen. Diese ist standardmäßig für bis zu 100.000 Datensätze optimal eingerichtet. Sofern Sie die Datei dennoch verändern wollen oder gar müssen, sei für mehr Details die Dokumentation von PostgreSQL empfohlen. Wenn Sie sich diese Datei betrachten, finden Sie immer folgendes Format vor:

Option=Wert

Damit wird immer für die entsprechende Option ein bestimmter Wert gesetzt. Zeilen, die mit dem Zeichen # beginnen, sind Kommentarzeichen und werden vom PostgreSQL-Server ignoriert. Häufig findet man hier auch einige Option/Wertpaare auskommentiert.

Die Zugriffsrechte werden über die Datei pg_hba.conf eingestellt. Natürlich ist dies nur nötig, wenn eine Authentifizierung durchgeführt wird. In den Beispielen dieses Buchs ist dies nicht nötig. Da diese Datei nur einmal beim Starten geladen wird, muss bei einer Veränderung der Datei der Server nochmals neu gestartet werden, bzw. es reicht ein reload, um nur die Konfiguration neu einzulesen.


Hinweis   An dieser Stelle erfolgte beim Probelesen ein Zwischenruf von einem mir bekannten Datenbank-Guru. Und zwar kommt hier die hervorragende Benutzerverwaltung von PostgreSQL ein wenig zu kurz. In der Praxis sollte man die Daten immer so gut wie irgend möglich schützten – sprich, man sollte dem User schon nahe legen, auch für die UNIX-Socket-Verbindung ein md5-Passwort zu verwenden, das von dem User-Passwort abweicht. Es sollte daher auch immer die Option -U USERNAME hinzugenommen werden. Allerdings sollten Sie bedenken, dass es sich hierbei nur um einen Crashkurs zu PostgreSQL handelt. Würde man hierbei noch mehr ins Detail gehen, so hätten noch zig Seiten mehr geschrieben werden müssen. Für mehr Details sei an dieser Stelle das Buch »PostgreSQL: Das offizielle Handbuch« von Peter Eisentraut empfohlen, das Sie übrigens auch auf der Buch-CD zum Probelesen finden. Wie auch immer, vielleicht ist das auch z. T. das Erfolgsgeheimnis von MySQL. MySQL lässt sich sehr schnell überblicken, was man von PostgreSQL kaum behaupten kann. Man entdeckt hier immer wieder neue Funktionalitäten.


Der Aufbau einer solchen Zeile wird als eine »Regel« bezeichnet. Jede dieser Regeln besteht aus mehreren Teilen, die durch ein Leerzeichen voneinander getrennt werden. Hier eine solche komplette Regel:

host Datenbankname IP-Adresse Netzmaske Authentifizierung

Der erste Regeltyp host ist der wichtigste und gibt den host für die Netzwerkadresse an. Würde anstatt host der Regeltyp local verwendet, so würde eine Verbindung über UNIX Domain Sockets verwendet. Die weiteren Regeltypen bis zur Authentifizierung sprechen für sich. Wollen Sie bezüglich der Datenbank keine Einschränkungen machen, dann können Sie hier einfach all einsetzen – ansonsten geben Sie eine entsprechende existierende Datenbank mit dem Namen an. Bei der Authentifizierung haben Sie mehrere Möglichkeiten zur Verfügung:

gp  trust – Keine Authentifizierung nötig. Der Benutzername und das (falls eingegeben) Passwort werden akzeptiert.
gp  password – Eine Klartext-Passwort-Authentifizierung. Dies kann auch eine Passwortdatei (.pgpass) sein.
gp  crypt – Wie password, nur über das Netzwerk werden die Passwörter verschlüsselt übertragen. Bei Anwendungen mit Threads sollte stattdessen md5 verwendet werden, da crypt nicht thread-sicher ist.
gp  md5 – Wie crypt, nur mit anderem und besserem Algorithmus zur Verschlüsselung.
gp  IdentIdent-Dämon wird abgefragt. Hier kann ein Benutzernamen-Mapping über eine Datei namens pg_ident.conf durchgeführt werden.
gp  reject – Verbindung wird immer abgelehnt.

Rheinwerk Computing

12.9.5 CRASHKURS PostgreSQdowntop

Server starten und stoppen

Hierbei gilt schon, es gibt mehr als nur einen Weg. Daher empfehle ich Ihnen diesbezüglich, die mitgelieferte Dokumentation von PostgreSQL zu lesen. Im Großen und Ganzen ist hierbei kein großer Aufwand nötig. Sie müssen lediglich ein entsprechendes Skript, das Ihnen von der Distribution bei der Installation zur Verfügung steht, mit der Option start zum Starten oder mit der Option stop zum Stoppen aufrufen. Alternativ zum Neustart des Datenbankservers können Sie auch reload oder restart verwenden.

---[unter SUSE]---
$ su
Password:********
# rcpostgresql start
Starting PostgreSQL                                    done
# rcpostgresql stop
Shutting down PostgreSQL                               done
#
---[unter RedHat]---
$ su
Password:********
# service postgresql start
Starting PostgreSQL                                    done
# service postgresql stop
Shutting down PostgreSQL                               done
#
---[generisch]---
$ su
Password:********
# /etc/init.d/postgres* start
Starting PostgreSQL                                    done
# /etc/init.d/postgres* stop
Shutting down PostgreSQL                               done 

Wenn Ihnen keines dieser Skripte zur Verfügung steht, sei Ihnen die Homepage von PostgreSQL empfohlen. Hier werden für diverse Systeme solche Skripte zum Download angeboten. Dies ist z. B. der Fall, wenn Sie PostgreSQL selbst übersetzt haben. Natürlich können Sie auch ohne ein solches Skript den Datenbankserver – wenn auch etwas umständlich – starten und stoppen:

$ su -c 'pg_ctl start -D /usr/local/pgsql/data -l serverlog' \
  postgres

Notfalls können Sie zum Beenden des Datenbankservers auch mit kill die Signale SIGINT und SIGQUIT senden.

Datenbank erzeugen und löschen

Jetzt können Sie als normaler User eine Datenbank erzeugen bzw. löschen. Dazu muss logischerweise der Server laufen und PostgreSQL ordnungsgemäß installiert sein. Eine Datenbank erzeugen können Sie jetzt einfach mit der Eingabe (als Beispiel eine Tabelle namens adressen):

$ createdb adressen
CREATE DATABASE

Etwas ungewöhnlich erscheint hier einem als Umsteiger, dass hierbei noch kein Client verwendet wird. Ebenso sieht es wieder mit dem Löschen einer Datenbank aus. Auch hierzu reicht eine einfache Eingabe in der Kommandozeile mit:

$ dropdb adressen
DROP DATABASE

Natürlich hat und sollte auch nicht jeder User die Privilegien besitzen, Datenbanken zu erzeugen und zu löschen, wie es ihm gefällt. Dies ist hierbei nur dadurch möglich, weil Sie selbst PostgreSQL installiert haben und als DER Benutzer angemeldet sind, der auch den Server gestartet hat. Sollte dies nicht der Fall sein und Sie eine Fehlermeldung zurückbekommen wie

ERROR:  CREATE DATABASE: permission denied
createdb: database creation failed

dann sollten Sie den Administrator um Erlaubnis fragen, eine Datenbank zu erzeugen – oder Sie lassen sich eine für die weitere Arbeit mit diesem Buch erzeugen. Das Thema Zugriffsrechte wird hier allerdings nicht behandelt, schließlich ist es lediglich eine Einführung in PostgreSQL, um anschließend mit dessen C-API eigene Clientanwendungen zu erstellen.

Sie können beliebig viele Datenbanken anlegen, allerdings muss die Datenbank einen Buchstaben als erstes Zeichen beinhalten, und der Name darf nicht länger als 63 Zeichen sein.


Hinweis   Natürlich ist es auch mit PostgreSQL mithilfe des Clientprogramms psql (wie bei MySQL der Client mysql) möglich, Datenbanken mit CREATE DATABASE adressen zu erzeugen und mit DROP DATABASE adressen wieder zu löschen.


Datentypen in PostgreSQL

In PostgreSQL wird Ihnen eine enorme Menge an Datentypen angeboten. Neben den gewöhnlichen Datentypen, die es in MySQL auch gibt, gibt es u. a. noch Datentypen für Geometrie, für Netzwerkadressen, Bitkettentypen und Arrays. Auf alle Datentypen einzugehen, wäre recht umfangreich, weshalb hier nur auf die gängigsten Datentypen eingegangen werden soll – da sich doch die Syntax im Gegensatz zu MySQL erheblich unterscheidet.

Zuerst eine Zusammenfassung der nummerischen Datentypen – also sowohl Ganzzahl als auch Fließkommazahl:


Tabelle 12.11    Nummerische Datentypen

Name Speichergröße Beschreibung Reichweite
smallint 2 Bytes ganze Zahl mit kleiner Reichweite -32768 bis +32767
integer 4 Bytes normale Wahl für ganze Zahlen -2147483648 bis +2147483647
bigint 8 Bytes ganze Zahl mit großer Reichweite -9223372036854775808 bis 9223372036854775807
decimal variabel benutzerdefinierte Präzision, exakt keine Begrenzung
numeric variabel benutzerdefinierte Präzision, exakt keine Begrenzung
real 4 Bytes variable Präzision, inexakt 6 Dezimalstellen Präzision
double precision 8 Bytes variable Präzision, inexakt 15 Dezimalstellen Präzision
serial 4 Bytes selbstzählende ganze Zahl 1 bis 2147483647
bigserial 8 Bytes große selbstzählende ganze Zahl 1 bis 9223372036854775807

Als Nächstes erfolgt ein Überblick zu den Datentypen für Zeichenketten:


Tabelle 12.12    Datentypen für Zeichenketten (Strings)

Name Beschreibung
character varying(n), varchar(n) variable Länge mit Höchstgrenze
character(n), char(n) feste Länge, mit Leerzeichen aufgefüllt
text variable Länge ohne Höchstgrenze

Zum Schluss noch Datentypen zur Datums- und Zeitangabe:


Tabelle 12.13    Datentypen zur Datums- und Zeitangabe

Name Bytes Beschreibung Wert Genauigkeit
timestamp [ (p) ] without time zone 8 Datum und Zeit 4713 v.u.Z - 1465001 u.Z. 1 Mikrosek./14 Stellen
timestamp [ (p) ] with time zone 8 Datum und Zeit 4713 v.u.Z. – 1465001 u.Z. 1 Mikrosek./14 Stellen
interval [ (p) ] 12 Zeitspannen –178000000 - 178000000 Jhr. 1 Mikrosek.
date 4 nur Datum 4713 v.u.Z. –32767 u.Z. 1 Tag
time [ (p) ] without time zone 8 nur Tageszeit 00:00:00.00 – 23:59:59.99 1 Mikrosek.
time [ (p) ] with time zone 12 nur Tageszeit 00:00:00.00+12 – 23:59:59.99–12 1 Mikrosek.


Hinweis   Natürlich gibt es in PostgreSQL auch einen Datentypen für binäre Daten, wie das bei MySQL mit BLOB der Fall ist. Der Typ in PostgreSQL lautet hierbei bytea.


Datenbank verwenden

Wie Sie eine neue Datenbank anlegen bzw. eine bereits vorhandene Datenbank löschen können, haben Sie bereits erfahren. Wenn Sie jetzt auf die Datenbank zugreifen wollen, haben Sie mehrere Möglichkeiten:

gp  Sie verwenden das PostgreSQL-Clientprogramm psql – vergleichbar in MySQL mit dem Client mysql. Mit diesem Clientprogramm können Sie in der Kommandozeile SQL-Befehle eingeben.
gp  Sie verwenden ein grafisches Frontend wie z. B. PgAccess oder Knoda.
gp  Sie verwenden ein Office-Paket mit ODBC-Unterstützung.
gp  Sie schreiben eine eigene Clientanwendung in einer der verschiedenen Sprachanbindungen – was letztendlich auch das Ziel dieses Kapitels ist.

Da es sich hier um einen Crashkurs handelt, verwenden Sie erst das Clientprogramm psql. Sofern Sie die Datenbank adressen von der Einführung noch nicht gelöscht haben, werden Sie diese jetzt wieder verwenden. Um die Datenbank zu verwenden, müssen Sie den Client psql unter der Angabe der Datenbank, die verwendet werden soll, folgendermaßen starten:

$ createdb adressen
CREATE DATABASE
$ psql adressen
Welcome to psql 7.3.2, the PostgreSQL interactive terminal.
Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help on internal slash commands
       \g or terminate with semicolon to execute query
       \q to quit
adressen=#

Sie greifen jetzt auf die Datenbank adressen zu. Dies können Sie in der Zeile, in der sich jetzt auch das Eingabeprompt befindet, erkennen. Die Zeile könnte allerdings auch so aussehen:

adressen=>

Das bedeutet dann, dass Sie nicht der Datenbank-Superuser sind, was auch heißt, dass Sie einige rechtliche Einschränkungen bezüglich des Zugriffs auf die Datenbank haben. Für den weiteren Verlauf im Buch ist dies allerdings nicht so wichtig. In der letzten Zeile befindet sich nun der Prompt und wartet auf Ihre Eingaben. Im Folgenden lassen Sie sich die Version von PostgreSQL anzeigen, und anschließend beenden Sie die Arbeit mit der Datenbank adressen mit \q wieder.

adressen=# SELECT version();
----------------------------------------------------------------
PostgreSQL 7.3.2 on i686-pc-linux-gnu,
compiled by GCC gcc (GCC) 3.3 20030226
(prerelease) (SUSE Linux)
(1 row)
adressen=# \q

psql erfordert als Parameter mindestens den Namen der Datenbank, mit der verbunden werden soll. Über weitere Optionen kann z. B. angegeben werden, auf welchem Server die Datenbank läuft oder welcher Benutzername verwendet werden soll, z. B.:

$ psql -h localhost -U meinusername datenbankname

Hier verbinden Sie sich z. B. mit dem Host localhost und dem User-Namen meinusername mit der Datenbank datenbankname. psql kann noch mit weiteren Optionen gestartet werden – manchmal ist dies auch Voraussetzung, um eine Verbindung aufzubauen. Mehr darüber erfahren Sie mit psqlhelp. Benötigt eine Verbindung ein Authentifizierungsverfahren, so wird man daraufhin nach dem Passwort gefragt.


Hinweis   Auf Benutzer, Gruppen und das Einrichten ihrer Privilegien wird in diesem Buch nicht eingegangen (ich wiederhole mich nur ungern).


Grafische Frontends für PostgreSQL

Der eine oder andere wird hierbei lieber die Kommandozeile mit einem grafischen Client austauschen wollen. Für Umsteiger von MySQL auf PostgreSQL, die gerne und viel mit phpmyadmin gearbeitet haben, gibt es ein postgresche Alternative mit phpPgAdmin.

Hierbei handelt es sich um ein Webfrontend, was einen Webbrowser voraussetzt. Das Frontend verfügt über unzählig viele Funktionen, um mindestens genauso effektiv mit der PostgreSQL-Datenbank zusammenzuarbeiten wie mit dem Kommandozeilenclient psql. Dieses Frontend bietet alles, was auch phpmyadmin dem MySQL-Kenner schon bietet, vom Betrachten der Tabellendaten als HTML-Tabelle zum Bearbeiten von Feldern bis hin zu Kopier- und Dump-Funktionen. Natürlich lassen sich auch hiermit die SQL-Befehle wie aus der Kommandozeile eingeben – weshalb Sie ohne weiteres phpPgAdmin installieren und im weiteren Verlauf des Crashkurses verwenden können. Des Weiteren hat man mit einer Verlinkung zu den passenden Seiten der PostgreSQL-Dokumentation alles in einer Hand (besser in einem Fenster).

Als weiteres grafisches Frontend, womit man sich die üblichen Dialoge zwischen dem Client und den Server erklicken kann, wäre pgaccess. Auch mit diesem Frontend lassen sich alle Arbeiten mit der Datenbank recht einfach und komfortabel (häufig) mit einem Mausklick erledigen. Allerdings ist pgaccess seit der Version 7.3 nicht mehr Bestandteil der PostgreSQL-Distribution und muss extra von http://www.pgaccess.org/ besorgt werden.


Hinweis   Viele dieser Tools funktionieren nicht mit dem UNIX-Socket, da meistens Netzwerk-Sockets dazu benötigt werden, weshalb in postgresql.conf des Clusters der Schalter tcpip_socket = true gesetzt sein muss.


Tabelle anlegen und löschen

Das Anlegen einer neuen Tabelle in der Datenbank unterscheidet sich fast gar nicht von der Syntax von MySQL. Auch hier wird der SQL-Befehl CREATE TABLE wie folgt verwendet:

CREATE TABLE <tabellennamen> ( <spaltennamen> <datentype>, ... );

In der Praxis sieht dies somit genauso wie mit MySQL aus – unter der Berücksichtigung, dass einige Datentypen eine andere Syntax haben.

adressen=# CREATE TABLE adressen(
adressen(# vname varchar(80),
adressen(# nname varchar(80),
adressen(# plz int,
adressen(# ort varchar(80),
adressen(# strasse varchar(100),
adressen(# hausnr int
adressen(# );
CREATE TABLE

Sie können auch hier Leerzeichen, Tabulatoren und Zeilenumbrüche in den SQL-Befehlen beinahe beliebig verwenden. Kommentare können Sie mit zwei Minuszeichen einleiten (--). Alles dahinter wird ignoriert bis zum Zeilenumbruch. Ebenso sieht es mit der Groß- und Kleinschreibung der Befehle aus, die im SQL-Standard schon immer unerheblich war.

Löschen können Sie diese Tabelle wieder mit dem Befehl:

DROP TABLE tabellenname;

Was Sie hier aber nicht durchzuführen brauchen, da Sie diese Tabellen noch für den weiteren Verlauf des Crashkurses verwenden.

Daten in die Tabelle einfügen, verändern und löschen

Nachdem Sie nun die Datenbank angelegt haben und die Tabelle definiert wurde, sind Sie hierbei wieder als Programmierer gefragt, die Daten in diese Tabelle einzufügen. Den Vorgang heben wir uns zunächst für später – nach dem Crashkurs – auf. Auch hierbei können Sie den in MySQL bereits bekannten Befehl INSERT INTO verwenden.

adressen=# INSERT INTO adressen(vname, nname, plz, ort, strasse,
adressen(# hausnr )
adressen-# VALUES (
adressen(# 'Jürgen', 'Wolf', 86316, 'Mering', 'Musterweg', 123 );
INSERT 16988 1

Hierbei wird also der Tabellenname angegeben, gefolgt von den Tabellenspalten und den Werten (VALUES), die in den einzelnen Spalten gespeichert werden sollen. Die Tabellenspalten und Werte werden dabei von Kommata getrennt. Wichtig ist auch hier, dass Sie die Reihenfolge der Tabellenspalten auch bei den Werten einhalten. Dabei müssen Sie die Reihenfolge nicht so stur einhalten, wie Sie die Tabellenstruktur definiert haben. Sie können z. B. auch alles in der umgekehrten Reihefolge schreiben – wie eben schon in MySQL.

Sofern Sie die Reihenfolge der Tabellenspalten im Kopf haben, können Sie auch direkt die Werte in die Tabelle einfügen:

adressen=# INSERT INTO adressen 
adressen-# VALUES('Fatma', 'Wolf', 33333, 'Schlumpfhausen',
adressen(# 'Schlumpfweg', 444);
INSERT 16990 1

Hinweis   Bei der Erstellung Ihrer eigenen Clientanwendungen ist von diesem direkten Einfügen abzuraten. Denn wird während der Programmerstellung z. B. ein Feld hinzugefügt, funktioniert das gesamte bisherige Programm nicht mehr. Daher sollte man sich angewöhnen, die Felder, die man einfügen möchte, mit anzugeben.


Im Gegensatz zu MySQL ist es allerdings mit PostgreSQL nicht möglich, mehrere Datensätze auf einmal einzufügen – da folgt PostgreSQL brav dem ANSI SQL-Standard. Sie können also in der Praxis immer nur eine komplette Adresse in die Datenbank einfügen – nicht zwei oder drei auf einmal.

Analog wie beim Hinzufügen von Daten verhält es sich beim Verändern und Löschen der Daten im Vergleich zu MySQL. Auch in PostgreSQL werden hierfür die Befehle DELETE FROM (zum Löschen von Zeilen) und UPDATE (zum Verändern bzw. Aktualisieren) verwendet.

Datenausgabe

Auch die Datenausgabe lässt sich bei PostgreSQL mit derselben Syntax wie MySQL erledigen. Dabei bedienen Sie sich auch hier wieder des mächtigen Befehls SELECT, dessen komplette Beschreibung wohl mehrere hundert Seiten füllen könnte. Der einfachste Befehl ist natürlich das Anzeigen aller Daten in einer Tabelle:

adressen=# SELECT * FROM adressen;
vname   | nname |  plz  |      ort       |    strasse    | hausnr
--------+-------+-------+----------------+---------------+-------
Jürgen  | Wolf  | 86316 | Mering         | Musterweg     |    123
Jonathan| Wolf  | 12345 | Musterdorf     | Musterstrasse |      1
Fatma   | Wolf  | 33333 | Schlumpfhausen | Schlumpfweg   |    444
(3 rows)

Auf einzelne Werte in der Tabelle können Sie mit der Angabe des Namens der Spalte zurückgreifen (und ausgeben lassen):

adressen=# SELECT ort FROM adressen;
      ort
----------------
 Mering
 Musterdorf
 Schlumpfhausen
(3 rows)

Für spezielle Inhalte können Sie sich auch hierbei des SQL-Befehls WHERE bedienen:

adressen=# SELECT * FROM adressen WHERE ort='Mering';
 vname  | nname |  plz  |  ort   |  strasse  | hausnr
--------+-------+-------+--------+-----------+--------
 Jürgen | Wolf  | 86316 | Mering | Musterweg |    123
(1 row)

Wollen Sie die Ausgabe nach einem bestimmten Kriterium sortieren und ausgeben lassen, können Sie auch hierbei den Befehl ORDER BY verwenden:

adressen=# SELECT * FROM adressen ORDER by plz;
vname   | nname |  plz  |      ort       |    strasse    | hausnr
--------+-------+-------+----------------+---------------+-------
Jonathan| Wolf  | 12345 | Musterdorf     | Musterstrasse |      1
Fatma   | Wolf  | 33333 | Schlumpfhausen | Schlumpfweg   |    444
Jürgen  | Wolf  | 86316 | Mering         | Musterweg     |    123
(3 rows)

Wollen Sie nach allen Ortschaften suchen, die mit der Stringfolge 'M' beginnen, können Sie die unscharfe Suche mittels LIKE verwenden:

adressen=# SELECT * FROM adressen WHERE ort LIKE 'M%';
  vname   | nname |  plz  |    ort     |    strasse    | hausnr
----------+-------+-------+------------+---------------+--------
 Jürgen   | Wolf  | 86316 | Mering     | Musterweg     |    123
 Jonathan | Wolf  | 12345 | Musterdorf | Musterstrasse |      1
(2 rows)

Sie bemerken hier bereits, warum ich den Crashkurs von PostgreSQL kürzer gehalten habe. Die Verwendung beider Datenbanken (und vieler anderer auch) ist eben recht ähnlich. Wenn Sie sich erst einmal gute Kenntnisse eines Datenbanksystems angeeignet haben, fällt es relativ einfach, sich in anderen Systemen zurechtzufinden. Häufig vermisst man hier und da die eine oder andere Bequemlichkeit. Der Vorteil der Kenntnisse von PostgreSQL ist, dass diese sehr ANSI-konform ist, und somit dürfte der Umstieg von PostgreSQL in eine andere Datenbank wie MySQL oder gar Oracle wesentlich einfacher fallen als umgekehrt. Beim Umstieg von MySQL auf PostgreSQL z. B. müssen Sie sich einige Dinge wieder abgewöhnen – was bekanntlich immer schwer fällt.


Hinweis   Sofern Sie jetzt gerne tiefer ins Detail von PostgreSQL einsteigen wollen, kann ich Ihnen von Peter Eisentraut PostgreSQL - Das Offizielle Handbuch, eine Übersetzung (viel mehr als das) der Originaldokumentation der PostgreSQL Global Development Group, empfehlen. Diese Dokumentation ist sowohl als HTML-Format zum Lesen am PC als auch als Buch erhältlich. Die HTML-Version dazu finden Sie auf der Buch-CD.



Rheinwerk Computing

12.9.6 PostgreSQL C-API – libpdowntop

Die Bibliothek, die Sie zur Schnittstelle zu PostgreSQL für die Anwendungsprogrammierung in C benötigen, ist die libpg-Bibliothek. Diese Bibliothek liegt gewöhnlich Ihrer Distribution bei und muss meistens nur noch nachinstalliert werden. Sofern dies nicht der Fall ist, müssen Sie diese von der Webseite http://www.postgresql.org/ beziehen und sich installieren. Mit libpg steht Ihnen eine ganze Sammlung von Funktionen zur Erstellung von Clientanwendungen zur Kommunikation mit dem PostgreSQL-Server zur Verfügung. libpg ist auch die Grundlage für alle anderen Anwendungsschnittstellen zu PostgreSQL wie libpg++ (C++), libpgtcl (Tcl), Perl oder ECPG.


Hinweis   Auch hierbei finden Sie wie schon bei der MySQL C-API mehrere komplette Listings in der Quelltextdistribution im Verzeichnis src/test/examples.


Eine PostgreSQL-Anwendung übersetzen

Um PostgreSQL-Anwendungen zu erstellen, ist nicht allzu viel Know-how nötig. Sie müssen dem Compiler lediglich den Pfad zur Headerdatei angeben und dem Linker den Pfad zur Bibliothek (eigentlich logisch). Folgende drei Punkte sind eigentlich zu beachten:

1. Die Headerdatei <libpq-fe.h> einbinden:
       
    #include <libpq-fe.h>
       
2. Sagen Sie dem Compiler mit der Option -I, wo er die PostgreSQL-Headerdateien findet. In meinem Beispiel sieht dies so aus:
       
    gcc -c -I/usr/include/pgsql programm.c
       
    Es kann sein, dass sich von Distribution zu Distribution das Verzeichnis der Headerdateien woanders befindet (in meinem Fall ist dies /usr/include/). Sollte der Compilerlauf bei Ihnen einen Fehler ausspucken wie No such file or directory, dann können Sie sich das Verzeichnis mit pg_config ermitteln lassen:
       
    $ pg_config --includedir /usr/include/pgsql
       
3. Jetzt müssen Sie aus der Objektdatei noch eine ausführbare Datei machen. Dazu benötigt der Linker noch den Pfad zur entsprechenden libpg-Bibliothek und die Bibliothek selbst. Den Pfad geben Sie mit dem Flag -L an und die Bibliothek mit -l. Da es auch hier Unterschiede zu den Pfadangaben gibt, hilft Ihnen wieder pg_config weiter, um den Pfad zu den Bibliotheken zu ermitteln:
       
$ pg_config --libdir /usr/lib
    Somit sieht in diesem Fall das Linken zu einer ausführbaren Datei folgendermaßen aus:
       
    gcc -o programm programm.o -L/usr/lib/pgsql -lpq
       
    … oder ganz einfach in einem Rutsch und quasi eine überall funktionierende Alternative:
       
gcc -o programm programm.o -I`pg_config –includedir` \ -L`pg_config –libdir` -lpq

Nach dem Vorgang befindet sich in Ihrem Verzeichnis eine ausführbare PostgreSQL-Clientanwendung. Nach dem Trockenschwimmen ist es nun Zeit, ins Wasser zu gehen.

Eine Verbindung zum PostgreSQL-Server herstellen

Die libpg bietet Ihnen gleich mehrere Funktionen an, um eine Verbindung mit einem PostgreSQL-Server aufzunehmen. Hier die Syntax der Funktionen:

#include <libpq-fe.h>
PGconn *PQconnectdb(const char *conninfo);
PGconn *PQsetdbLogin(const char *pghost,
                     const char *pgport,
                     const char *pgoptions,
                     const char *pgtty,
                     const char *dbName,
                     const char *login,
                     const char *pwd);
PGconn *PQsetdb(char *pghost,
                char *pgport,
                char *pgoptions,
                char *pgtty,
                char *dbName);
/* Nicht blockierende Funktionen */
PGconn *PQconnectStart(const char *conninfo);
PostgresPollingStatusType PQconnectPoll(PGconn *conn);

Trotz der unterschiedlichen Syntax machen alle Funktionen im Grunde dasselbe. Jede Verbindung, die mit einer dieser Funktionen erstellt wurde, gibt ein Objekt von Typ Pgconn zurück. Im seltensten Fall wird dabei bei einem Fehler der NULL-Zeiger zurückgegeben (nur im Falle, wenn kein Speicherplatz mehr für PGconn vorhanden war). Daher sollten Sie den Rückgabewert jeder dieser Funktionen immer mit der Funktion Pqstatus() überprüfen lassen. Dazu gleich mehr.

Die Funktion PQconnectdb() öffnet eine Verbindung mit den Parametern, die in dem String conninfo angegeben werden. Es scheint ein wenig ungewöhnlich, aber der Vorteil an dieser Funktion ist, dass hierbei die Parametersammlung ohne Weiteres erweitert werden kann und dass Sie keinen extra Parser schreiben müssen, wenn Sie die Verbindungsdaten in eine Konfigurationsdatei schreiben wollen. Geben Sie einen leeren String an, wird versucht, mit den Vorgabeparametern eine Verbindung zum Server aufzubauen. Sie können aber auch mehrere durch Whitespaces getrennte Parameter verwenden. Jeder dieser Parameter hat das Format schlüssel=wert. Einen leeren Wert können Sie zwischen Apostrophe setzen, wobei diese mit einem Backslash eingeleitet werden sollten. Das Beispiel eines solchen Verbindungsaufbaus kann so aussehen:

conn = Pqconnectdb (
         "dbname=adressen user=tot password=k4p6m3o3 tty=\' \'");

Bekannte Parameterschlüssel, die Sie hierfür verwenden können, wären:

gp  host – Name des Servers, womit eine Verbindung hergestellt werden soll. Ist der Wert ein UNIX Domain Socket anstatt TCP/IP, wird am Anfang ein Schrägstrich verwendet, was ein Verzeichnis einleitet, in dem sich die Socket-Datei befindet (standardmäßig wird das /tmp-Verzeichnis verwendet).
gp  hostaddr – Die IP-Adresse des Servers in Form von Zahlen und Punkten (193.23.32.3)
gp  port – Portnumer, womit eine Verbindung aufgebaut werden soll
gp  dbname – Name der Datenbank
gp  user – Name des Benutzers der Verbindung
gp  password – Benötigt der Server ein Passwort, wird dies damit angegeben.
gp  connect_timeout – Zeit in Sekunden, die dieser Verbindungsfunktion gegeben wird. NULL oder nicht verwendet, heißt unendlich (mit Vorsicht benutzen).
gp  options – Optionen zur Konfiguration, die an den Server geschickt werden
gp  tty – Eine Datei oder ein TTY für die Debug-Ausgabe vom Server
gp  requiressl – Wird dieser Wert auf 1 gesetzt, wird eine SSL-Verbindung mit dem Server verlangt. Bietet der Server das nicht an, schlägt der Funktionsaufruf fehl. Ansonsten bei keiner Angabe oder bei 0 wird der Verbindungstyp mit dem Server ausgehandelt.

Werden bestimmte Parameter nicht verwendet, wird erst (falls vorhanden) in den entsprechenden Umgebungsvariablen gesucht, ansonsten werden entsprechende Vorgabewerte verwendet.

Die Funktion PQsetdbLogin() verwendet eine feste Anzahl von Parametern, hat aber die gleiche Funktion wie schon die Funktion PQconnectdb(). Die einzelnen Parameter sprechen nach der Erklärung der Funktion PQconnectdb(), denke ich, für sich. Diese Funktion war die Vorgängerfunktion von PQconnectdb().

PQsetdb() ist ein Makro, das die Funktion PQsetdbLogin() mit NULL-Zeigern für die Parameter login und pwd aufruft. Die Funktion gilt als veraltet und ist aus Kompatibilitätsgründen noch vorhanden.

Die Funktionen PQconnectStart() und PQconnectPoll() werden verwendet, um eine Verbindung mit dem Datenbankserver zu öffnen, ohne dass die Anwendung während der Eingabe- oder Ausgabeoperationen blockiert wird. Darauf wird allerdings hier nicht eingegangen.

Status und Informationen zur Verbindung ermitteln

Um zu ermitteln, ob die Verbindung zum Server erfolgreich verlief, sollte man immer den Status des Verbindungsaufbaus von Pgconn mit der Funktion PQstatus() überprüfen.

ConnStatusType PQstatus(const PGconn *conn);

In einer gewöhnlichen asynchronen Verbindung können hierbei CONNECTION_OK und CONNECTION_BAD auftreten. CONNECTION_OK signalisiert, dass alles glatt verlaufen ist, und CONNECTION_BAD zeigt an, dass ein Fehler beim Verbindungsaufbau aufgetreten ist. Beim Aufbau einer nicht blockierenden Verbindung mit PQconnectStart() können noch weitere Statusangaben überprüft werden, die hier allerdings nicht näher behandelt werden. Dies wären die Flags:

gp  CONNECTION_STARTED – Auf die Erstellung einer Verbindung wird gewartet.
gp  CONNECTION_MADE – Verbindung wurde aufgebaut, wartet nun auf das Senden.
gp  CONNECTION_AWAITING_RESPONSE – Wartet auf eine Antwort vom Server.
gp  CONNECTION_AUTH_OK – Authentifizierung erhalten; wartet auf Fortsetzung des Verbindungsaufbaus.
gp  CONNECTION_SETENV – Aushandlung der Umgebungsvariablen (gehört zum Teil eines Verbindungsaufbaus)

Wenn die Funktion PQstatus() den Status CONNECTION_OK zurückgegeben hat, bleibt eine Verbindung im Normalfall bis zum nächsten Pqfinish() bestehen. Tritt allerdings aus irgendwelchen Gründen ein Fehler auf, wird bei ggf. nochmaliger Überprüfung mit PQstatus() das Flag CONNECTION_BAD zurückgegeben, und Sie könnten in diesem Fall einen erneuten Verbindungsaufbau mit der Funktion Pqreset() versuchen.

Im Falle einer Fehlermeldung CONNECTION_BAD können Sie den Fehler, der die Verbindung zum Scheitern bzw. nicht zustande gebracht hat, mit der Funktion PqerrorMessage() in Klartext ausgeben lassen.

char *PQerrorMessage(const PGconn* conn);

Eine solche Fehlerüberprüfung eines Verbindungsaufbaus kann z. B. so aussehen:

    conn = PQconnectdb("dbname=adressen");
    if (PQstatus(conn) == CONNECTION_BAD) {
        fprintf(stderr, "Connection to database '%s' failed.\n",
                dbName);
        fprintf(stderr, "%s", PQerrorMessage(conn));
        exit_nicely(conn);
    }

Läuft z. B. der Datenbankserver von PostgreSQL nicht, würde hier folgende Fehlermeldung ausgegeben:

Connection to database 'adressen' failed.
could not connect to server: No such file or directory
        Is the server running locally and accepting
        connections on UNIX domain socket "/tmp/.s.PGSQL.5432"?

Natürlich gilt dies nicht nur für die Funktionen, womit eine Verbindung zum Datenbankserver aufgebaut wird, sondern fast alle libpq-Funktionen geben eine Fehlermeldung mit PQerrorMessage() aus, wenn ein Fehler aufgetreten ist.

Benötigen Sie für die laufende Verbindung Informationen, welche Datenbank verwendet wird oder welcher User damit arbeitet, steht Ihnen hierzu eine ganze Palette an Funktionen zur Verfügung. Alle Funktionen erwarten als Parameter einen Pgconn-Zeiger, den Sie bei einem erfolgreichen Verbindungsaufbau erhalten haben. Alle Werte, die diese Funktionen zurückgeben, sind während der Laufzeit des Objekts PGconn nicht mehr veränderbar. Hier die Funktionen:


Tabelle 12.14    Funktionen, die Informationen zur aktuellen Verbindung zurückgeben

Funktion Bedeutung
char *PQdb(const PGconn *conn); Gibt einen Zeiger auf den Datenbanknamen der Verbindung zurück.
char *PQuser(const PGconn *conn); Gibt den Benutzernamen der Verbindung zurück
char *PQpass(const PGconn *conn); Gibt das Passwort der Verbindung zurück.
char *PQhost(const PGconn *conn); Gibt den Server-Hostnamen der Verbindung zurück.
char *PQport(const PGconn *conn); Gibt den Port der Verbindung zurück.
char *PQtty(const PGconn *conn); Gibt das TTY für die Debug-Ausgaben der Verbindung zurück.
char *PQoptions(const PGconn *conn); Gibt Konfigurationsoptionen, die der Verbindungsanfrage übergeben wurden, zurück.
int PQsocket(const PGconn *conn); Gibt die Deskriptornummer des mit dem Server verbundenen Sockets zurück. Bei –1 wird angezeigt, dass keine Verbindung besteht.
int PQbackendPID(const PGconn *conn); Gibt die Prozesskennung (PID) des Serverprozesses zurück, der mit dieser Verbindung arbeitet.
SSL *PQgetssl(const PGconn *conn); Hier wird eine SSL-Struktur zurückgegeben. Wird SSL nicht verwendet, wird NULL zurückgegeben. Mehr dazu entnehmen Sie ggf. der Anleitung von OpenSSL.

Verbindung beenden oder wieder aufnehmen

Wenn Sie mit der Arbeit des Datenbankservers fertig sind, sollten Sie die Verbindung wieder mit der Funktion PQfinish() schließen.

void PQfinish(PGconn *conn);

Damit wird die Verbindung geschlossen und der Speicher für das PGconn-Objekt wieder freigegeben. Es wird empfohlen, diese Funktionen auch zu verwenden, wenn der Versuch, eine Verbindung zum Server aufzubauen, fehlschlägt, um den eventuell alloziierten Speicher wieder freizugeben.

Es kann passieren, dass eine Verbindung zum Server während der Arbeit verloren geht. In solch einem Fall können Sie mit der Funktion PQreset() versuchen, erneut eine Verbindung zum selben Server aufzubauen.

void PQreset(PGconn *conn);

Diese Funktion schließt die Verbindung zum Server und versucht, eine neue Verbindung zum selben Server mit den gleichen Parametern aufzubauen. Das kann zur Fehlerbehandlung nützlich sein, wenn eine funktionierende Verbindung verloren gegangen ist.

Auch für die nicht blockierenden Verbindungen gibt es zwei Funktionen, die versuchen, eine Verbindung zu schließen und erneut mit demselben Server eine neue, nicht blockierende Verbindung aufzubauen:

int PQresetStart(PGconn *conn);
PostgresPollingStatusType PQresetPoll(PGconn *conn);

Programmbeispiel: Verbindung zum Server herstellen

Hierzu folgt nun ein einfaches Listing, womit eine Verbindung zum (hoffentlich gestarteten) PostgreSQL-Server aufgebaut wird, das einige Informationen zur Verbindung ausgibt und die Verbindung wieder schließt; das Grundgerüst, wenn Sie so wollen. Bitte passen Sie ggf. die Parameter zum Verbindungsaufbau an Ihre Konfigurationen und Systemgegebenheiten an.


Hinweis   Im Beispiel wird davon ausgegangen, dass die Datenbank adressen, die Sie im Crashkurs zu PostgreSQL erzeugt haben, noch existiert.


/* postgre1.c */
#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>
void exit_nicely(PGconn *conn) {
  PQfinish(conn);
  exit(EXIT_FAILURE);
}
int main(void) {
  /* char    *pghost = NULL,
   *         *pgport = NULL,
   *         *pgoptions = NULL,
   *         *pgtty = NULL; */
  char       *dbName = "adressen";
  PGconn     *conn;
  /* ... entsprechende Angaben selbst ergänzen - */
  /* Verbindungsaufbau                            */
  conn = PQconnectdb("dbname=adressen");
  /* Verbindung überprüfen ... */
  if (PQstatus(conn) == CONNECTION_BAD) {
    fprintf(stderr, "Connection to database '%s' failed.\n",
            dbName);
    fprintf(stderr, "%s", PQerrorMessage(conn));
    exit_nicely(conn);
  } 
  else
    printf("Mit PostgreSQL-Server verbunden ...\n");
  /* Informationen zur Verbindung ... */
  printf("Datenbank : %s\n", PQdb(conn));
  printf("User      : %s\n", PQuser(conn));
  /*   printf("Passwort  : %s\n", PQpass(conn));
   *   printf("Host      : %s\n", PQhost(conn));
   *   printf("Port      : %s\n", PQport(conn));
   *   printf("TTY       : %s\n", PQtty(conn));
   *   printf("Options   : %s\n", PQoptions(conn)); */
  /* Verbindung beenden und Aufräumarbeiten durchführen */
  PQfinish(conn);
  return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc -c -I/usr/include/pgsql postgre1.c
$ gcc -o postgre1 postgre1.o -L/usr/lib/pgsql -lpq
$ ./postgre1
Mit PostgreSQL-Server verbunden ...
Datenbank : adressen
User      : tot

Anfragen an den Server

Wenn die Verbindung zum PostgreSQL-Server aufgebaut wurde, können Sie die nun folgenden Befehle verwenden, um Anfragen und Befehle auszuführen. Die Funktion, mit der ein Befehl oder eine Anfrage an den Server geschickt wird, ist PQexec(). Die Syntax dazu lautet:

PGresult *PQexec(PGconn *conn, const char *command);

Bei Erfolg erhalten Sie einen Zeiger auf Pgresult zurück. NULL wird gewöhnlich nur dann zurückgegeben, wenn kein Speicher für die Antwort alloziiert werden konnte oder der Befehl gar nicht erst an den Server geschickt werden konnte. Um mehr Informationen zum Auftreten eines Fehlers zu erhalten, sollten Sie die Funktion PQerrorMessage() verwenden. In der Struktur PGresult finden Sie das vom Server geschickte Ergebnis, das Sie mit den gleich folgenden vielen Funktionen verwenden können.


Hinweis   Sofern Sie mit der libpq-Version vor 6.4 noch vertraut sind, so sollten Sie nicht mehr auf die einzelnen Variablen der Struktur PGresult zugreifen, sondern eine entsprechende Zugriffsfunktion verwenden. Dies nur für den Fall, dass Sie Ihre Anwendung zukunftssicher gestalten wollen.


Status der Anfrage

Ob und was der Aufruf von PQexec() bewirkt hat, können Sie mit der Funktion PQresultStatus() und dem Zeiger auf PGresult ermitteln:

ExecStatusType PQresultStatus(const PGresult *res);

Mögliche Werte, die dabei zurückgegeben werden können, wären:

gp  PGRES_EMPTY_QUERY – Eine leere Zeichenkette wurde an den Server geschickt.
gp  PGRES_COMMAND_OK – Ein Befehl, der keine Daten zurückgibt, wurde erfolgreich ausgeführt (z. B. ein Befehl mit INSERT oder UPDATE).
gp  PGRES_TUPLES_OK – Eine erfolgreiche Anfrage. Anschließend können somit die Funktionen verwendet werden, um die von der Anfrage gelieferten Zeilen zu lesen. PGRES_TUPLES_OK wird auch dann zurückgegeben, wenn eine SELECT-Anfrage kein Ergebnis (0) zurückliefert.
gp  PGRES_COPY_OUT – Ein ausgehender COPY-Datentransfer, der vom Server gestartet wird
gp  PGRES_COPY_IN – Ein eingehender COPY-Datentransfer, der zum Server gestartet wird
gp  PGRES_BAD_RESPONSE – Die Antwort vom Server konnte nicht ausgewertet werden.
gp  PGRES_NONFATAL_ERROR – Ein nicht fataler Fehler
gp  PGRES_FATAL_ERROR – Ein fataler Fehler trat auf. Meistens ein Fehler in der Clientanwendung.

Um aus dem von PQresultStatus() zurückgegebenen Wert eine Stringkonstante zu machen, steht Ihnen die Funktion PQresStatus() zur Verfügung:

char *PQresStatus(ExecStatusType status);

Damit erhalten Sie einen Zeiger auf eine Stringkonstante, die den Statuscode etwas genauer beschreibt.

Die Funktionen PQresultStatus() und PQresStatus()können Sie mit der Funktion PQresultErrorMessage() in einem Rutsch realisieren:

char *PQresultErrorMessage(const PGresult *res);

Liegt hier kein Fehler vor, wird ein leerer String zurückgegeben.

Ergebnis der Anfrage

Benötigen Sie Informationen zum Ergebnis einer Anfrage, finden Sie in der folgenden Tabellen einen ganzen Satz von Funktionen:


Tabelle 12.15    Funktionen, um das Ergebnis einer Anfrage auszuwerten

Funktion Bedeutung
int PQntuples(const PGresult *res); Gibt die Anzahl der Zeilen (Tuples) des Anfrageergebnisses zurück.
int PQnfields(const PGresult *res); Gibt die Anzahl der Spalten in einer Zeile des Anfrageergebnisses zurück.
char *PQfname(const PGresult *res, int spalten_nummer); Gibt den Namen der Spalte spalten_nummer – beginnend bei 0 – zurück.
int PQfnumber(const PGresult *res, const char *field_name); Gibt die Nummer der Spalte zum angegebenen Spaltennamen field_name zurück.
Oid PQftype(const PGresult *res, int spalten_nummer); Hiermit können Sie den Datentyp der Spalte erfahren. Eine Übersicht der OIDs finden Sie in der Datei pg_type.h (meistens im Verzeichnis von libpq im Pfad server/catalog/pg_type.h zu finden).
int PQfmod(const PGresult *res, int spalten_nummer); Gibt die typenspezifischen Modifikationsdaten der Spalte (beginnend mit 0) mit der angegebenen Nummer zurück.
int PQfsize(const PGresult *res, int spalten_nummer); Gibt die Größe in Bytes der Spalte spalten_nummer zurück. Wird –1 zurückgegeben, handelt es sich um einen String variabler Länge.
int PQbinaryTuples( const PGresult *res); Diese Funktion gibt 1 zurück, wenn PGresult binäre Zeilendaten enthält. Bei 0 handelt es sich um Textdaten.

Speicherplatz der Anfrage freigeben

Wenn Sie mit dem Auswerten des Ergebnisses einer Anfrage fertig sind, sollten Sie immer mit der Funktion PQclear() das PGresult-Objekt wieder freigeben, um die berüchtigten Memory Leaks (Speicherlecks) zu vermeiden.

void PQclear(PQresult *res);

Programmbeispiel – Anfrage an den Server

Das nun folgende Beispiel demonstriert Ihnen einige der zuvor erwähnten Funktionen. Verwendet wurde in diesem Beispiel auch die Funktion PQgetvalue(), die im anschließenden nächsten Abschnitt erläutert wird.

/* postgre2.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libpq-fe.h>
/* weitere Typen siehe server/catalog/pg_type.h */
#define INT 23
#define VARCHAR 1043
static void exit_nicely (PGconn * conn) {
  PQfinish (conn);
  exit (EXIT_FAILURE);
}
static void read_fields (PGresult * res) {
  int i;
  int n_fields = PQnfields (res);
  printf("\n");
  for (i = 0; i < n_fields; i++)
    printf ("%s %s, ", PQfname (res, i),
            (PQftype (res, i) == INT) ? "INT" : "VARCHAR");
  printf ("\n\n");
}
static void read_full (PGresult * res) {
  int i, j;
  int n_fields = PQnfields (res);
  int n_line = PQntuples (res);
  printf("\n");
  for (j = 0; j < n_line; j++) {
    for (i = 0; i < n_fields; i++)
      printf ("%-15s", PQgetvalue (res, j, i));
    printf ("\n");
  }
  printf("\n");
}
static void read_info (PGresult * res) {
  int i, size = 0;
  int n_fields = PQnfields (res);
  int n_line = PQntuples (res);
  printf ("Tabelle enthält %d Zeile %d Spalten\n",
          n_line, n_fields);
  printf ("Speichergröße der einzelnen Spalten\n");
  for (i = 0; i < n_fields; i++) {
    printf ("%10s : %d Byte ", PQfname (res, i),
            PQfsize (res, i));
    if(PQfsize(res, i) != -1 ) {
      size += PQfsize (res, i);
      printf("\n");
    } else
      printf("( == Ein String mit variabler Länge)\n");
  }
  printf ("Gesamtspeichergröße ist %d Bytes\n", size);
}
static PGresult *select_all (char *dbName, PGconn * conn) {
  char query[255] = { "SELECT * FROM " };
  PGresult *res;
  strcat (query, dbName);
  res = PQexec (conn, query);
  if (!res || PQresultStatus (res) != PGRES_TUPLES_OK) {
    fprintf (stderr, "Fehler beim Befehl SELECT\n");
    PQclear (res);
    exit_nicely (conn);
  }
  return res;
}
int main (void) {
  /*char *pghost = NULL, *pgport = NULL,  */
  /*    *pgoptions = NULL, *pgtty = NULL; */
  char *dbName;
  char buf[64];
  char db[255] = { "dbname=" };
  PGconn *conn;
  PGresult *res;
  int auswahl;
  printf ("Mit welcher Datenbank wollen Sie arbeiten?: ");
  scanf ("%s", buf);
  dbName = buf;
  strcat (db, buf);
  /* ... bitte ggf. anpassen */
  conn = PQconnectdb (db);
  if (PQstatus (conn) == CONNECTION_BAD) {
    fprintf (stderr, "Verbindung z. Datenbank '%s' verfehlt\n",
             dbName);
    fprintf (stderr, "%s", PQerrorMessage (conn));
    exit_nicely (conn);
  } 
  else
    printf ("Mit PostgreSQL-Server verbunden ...\n");
  do {
    printf ("-1- Info zur Datenbank %s\n", dbName);
    printf ("-2- Alle Spaltennamen ausgeben\n");
    printf ("-3- Komplette Datenbank %s ausgeben\n", dbName);
    printf ("-4- Ende\n");
    printf ("Ihre Auswahl : ");
    scanf ("%d", &auswahl);
    switch (auswahl) {
    case 1:
      res = select_all (buf, conn);
      read_info (res);
      PQclear (res);
      break;
    case 2:
      res = select_all (buf, conn);
      read_fields (res);
      PQclear (res);
      break;
    case 3:
      res = select_all (buf, conn);
      read_full (res);
      PQclear (res);
      break;
    default:
      break;
    }
  } while (auswahl != 4);
  PQfinish (conn);
  return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc -c -I/usr/include/pgsql postgre2.c
$ gcc -o postgre2 postgre2.o -L/usr/lib/pgsql -lpq
$ ./postgre2
Mit welcher Datenbank wollen Sie arbeiten: adressen
Mit PostgreSQL-Server verbunden ...
-1- Info zur Datenbank adressen
-2- Alle Spaltennamen ausgeben
-3- Komplette Datenbank adressen ausgeben
-4- Ende
Ihre Auswahl : 1
Tabelle enthält 5 Zeile, 6 Spalten
Speichergröße der einzelnen Spalten
     vname : -1 Byte ( == Ein String mit variabler Länge)
     nname : -1 Byte ( == Ein String mit variabler Länge)
       plz : 4 Byte
       ort : -1 Byte ( == Ein String mit variabler Länge)
   strasse : -1 Byte ( == Ein String mit variabler Länge)
    hausnr : 4 Byte
Gesamtspeichergröße ist 8 Bytes
-1- Info zur Datenbank adressen
-2- Alle Spaltennamen ausgeben
-3- Komplette Datenbank adressen ausgeben
-4- Ende
Ihre Auswahl : 2
vname VARCHAR, nname VARCHAR, plz INT, ort VARCHAR, strasse VARCHAR, hausnr INT,
-1- Info zur Datenbank adressen
-2- Alle Spaltennamen ausgeben
-3- Komplette Datenbank adressen ausgeben
-4- Ende
Ihre Auswahl : 3
Jonathan    Wolf      12345     Musterdorf     Musterstrasse  1
Fatma       Wolf      33333     Schlumpfhausen Schlumpfweg    444
John        Travolta  555444    Kalifornien    Broadway       1
Jonny       Depp      11234     Hollywood      Kingsroad      1
Jacko       Jackson   1234      New York       5th Avenue     1

Rückgabe des Anfrageergebnisses auslesen

Außer dass Sie u. a. Informationen wie die Anzahl der betroffenen Spalten einer Anfrage ermitteln können, wollen Sie sicherlich auch echte Daten von der Datenbank auslesen können – sprich, den Inhalt der Zeilen und der einzelnen Spalten. Die folgenden Funktionen benötigen alle dieselben Parameter: den Zeiger auf das Anfrageergebnis (PGresult), die Zeilennummer und die Spaltennummer, von denen Sie den Wert haben wollen.

Die einzelnen Spaltenwerte können Sie mit der Funktion PQgetvalue() ermitteln:

char* PQgetvalue(const PGresult *res,
                 int zeilen_nummer,
                 int spalten_nummer);

Damit lesen Sie aus der Anfrage res, die Sie durch eine Ausführung von PQexec() zurückbekommen haben, den Wert von der Zeile zeilen_nummer und der Spalte spalten_nummer aus. Sowohl Zeile als auch Spalte beginnen bei 0 zu zählen. Bei erfolgreicher Ausführung wird ein Zeiger auf einen entsprechenden String mit einem abschließenden Null-Byte zurückgegeben. Da sich die Daten hier in der Struktur PGresult befinden, hält die Lebensdauer des Rückgabewerts bis zum nächsten PQexec()-Aufruf. Für eine längere Benutzung müssen Sie diese Daten in einen anderen Speicherbereich kopieren.


Hinweis   Sofern Sie binäre Daten (z. B. eine Grafik) aus der Anfrage auslesen wollen – die Funktion PQbinaryTuples() gibt ja hierbei 1 zurück –, sind Sie selbst für die richtige Verarbeitung der Daten verantwortlich.


Wollen Sie testen, ob der Wert einer Spalte NULL ist, können Sie die Funktion PQgetisnull() verwenden.

int PQgetisnull(const PGresult *res,
                int zeilen_nummer,
                int spalten_nummer);

Wie immer beginnen Zeilen- und Spaltennummer bei 0. Enthält der Wert einer Spalte den NULL-Wert, wird wahrheitsgemäß 1, ansonsten 0 zurückgegeben.


Hinweis   Auch hier gilt, ähnlich wie schon bei MySQL, dass bei PostgreSQL NULL ein leerer String ist und keine NULL im Sinne von C.


Benötigen Sie die wirkliche Länge des Datenwertes, den Sie mit PQgetvalue() erhalten haben, können Sie die Funktion Pqgetlength() verwenden.

int PQgetlength(const PGresult *res,
                int zeilen_nummer,
                int spalten_nummer);

Zeilen- und Spaltenwerte beginnen bei 0. Die wirkliche Länge hat allerdings nichts mit der Länge der binären Größe zu tun, die Sie mit Pqfsize() erhalten (insbesondere nicht im Textformat).

Ergebnisse anderer Befehle ermitteln

Wenn Sie mit dem Client psql einen SQL-Befehl an den Server geschickt haben, haben Sie meistens eine Statuszeichenkette des Befehls erhalten, z. B.:

INSERT 16994 1

Hier wurde ein Datensatz (»1«) mit dem Befehl INSERT eingefügt. Die OID-Nummer der eingefügten Zeile lautet 16994. Diesen Status können Sie sich natürlich auch auf PGresult mit der Funktion PQcmdStatus() zurückgeben lassen.

char * PQcmdStatus(PGresult *res);

Hinweis   OID ist eine Datensatznummer, die für jeden eingefügten Datensatz (Zeile) vom internen Zähler des Datenbanksystems generiert wird. Diese Nummer ist bei objektrelationalen DB-Systemen nötig, damit alle Datenbankobjekte eindeutig identifizierbar sind. Daher besitzt jede Tabelle ein Feld tableoid, das den Objektidentifizierer der Tabelle enthält, zu dem dieser Datensatz gehört. Da es durchaus möglich ist, dass bei übergroßen Tabellen der Zähler überrundet wird und sich wieder zurücksetzt, sollten Sie sich nicht unbedingt darauf verlassen, dass diese Nummer eindeutig ist. In der Version 7.2. ist es auch möglich, beim Erzeugen einer Tabelle eine Generierung der OID-Nummer mit der zusätzlichen Angabe von WITHOUT OIDS zu unterbinden.


Benötigen Sie hiervon nur die Anzahl der Zeilen, die auf SQL-Befehle wie INSERT, UPDATE oder DELETE eine Auswirkung hatten, so können Sie auch die Funktion PQcmdTuples() verwenden.

char * PQcmdTuples(PGresult *res);

Wenn der Befehl kein INSERT, UPDATE oder DELETE war, wird hierbei eine leere Zeichenkette zurückgegeben.

Benötigen Sie hingegen nur die OID der eingefügten Zeile, die mit dem Befehl INSERT eingefügt wurde, und hat die Zieltabelle auch OID, so können Sie die Funktionen PQoidValue() oder PQoidStatus() verwenden.

Oid PQoidValue(const PGresult *res);
char * PQoidStatus(const PGresult *res);

PQoidValue() gibt entweder einen (ganzzahligen) Wert vom Typ Oid oder im Falle eines Fehlers InvalidOid zurück. Beide Typen sind definiert, wenn die libpq-Headerdatei eingebunden wurde.

PQoidStatus() gibt eine Zeichenkette mit der OID der eingefügten Zeile zurück, wenn die SQL-Anweisung ein INSERT war, ansonsten 0, wenn die Zieltabelle keine OID hatte. Wenn der SQL-Befehl kein INSERT war, wird ein leerer String zurückgegeben.

Mit den beiden Funktionen PQoidValue() und PQoidStatus() bekommen Sie im Endeffekt dasselbe Ergebnis zurück, wobei die Funktion PQoidValue() nicht für Anwendungen mit Threads verwendet werden sollte, da diese nicht thread-sicher ist.

Programmbeispiel – Daten in Datenbank einfügen

Das folgende Beispiel ist eine Erweiterung des Listings zuvor, nur dass hierbei die Funktionen zum Hinzufügen neuer und zum Löschen alter Datensätze hinzugekommen sind. Eine fast komplette Clientanwendung zum Arbeiten mit dem PostgreSQL-Server.

/* postgre3.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libpq-fe.h>
/* weitere Typen siehe server/catalog/pg_type.h */
#define INT 23
#define VARCHAR 1043
static void exit_nicely (PGconn * conn) {
  PQfinish (conn);
  exit (EXIT_FAILURE);
}
static void read_fields (PGresult * res) {
  int i;
  int n_fields = PQnfields (res);
  printf("\n");
  for (i = 0; i < n_fields; i++)
    printf ("%s %s, ", PQfname (res, i),
            (PQftype (res, i) == INT) ? "INT" : "VARCHAR");
  printf ("\n\n");
}
static void read_full (PGresult * res) {
  int i, j;
  int n_fields = PQnfields (res);
  int n_line = PQntuples (res);
  printf("\n");
  for (j = 0; j < n_line; j++) {
    for (i = 0; i < n_fields; i++)
      printf ("%-15s", PQgetvalue (res, j, i));
    printf ("\n");
  }
  printf("\n");
}
static void read_info (PGresult * res) {
  int i, size = 0;
  int n_fields = PQnfields (res);
  int n_line = PQntuples (res);
  printf ("Tabelle enthält %d Zeile %d Spalten\n",
          n_line, n_fields);
  printf ("Speichergröße der einzelnen Spalten\n");
  for (i = 0; i < n_fields; i++) {
    printf ("%10s : %d Byte ", PQfname (res, i),
            PQfsize (res, i));
    if(PQfsize(res, i) != -1 ) {
      size += PQfsize (res, i);
      printf("\n");
    } else
      printf("( == Ein String mit variabler Länge)\n");
  }
  printf ("Gesamtspeichergröße ist %d Bytes\n", size);
}
static PGresult *select_all (char *dbName, PGconn * conn) {
  char query[255] = { "SELECT * FROM " };
  PGresult *res;
  strcat (query, dbName);
  res = PQexec (conn, query);
  if (!res || PQresultStatus (res) != PGRES_TUPLES_OK) {
    fprintf (stderr, "Fehler beim Befehl SELECT\n");
    PQclear (res);
    exit_nicely (conn);
  }
  return res;
}
static void insert_into (char *dbName, PGconn * conn) {
  int i;
  char query[4096] = { "INSERT INTO " };
  char buf[255];
  char *ptr;
  PGresult *res;
  int n_fields;
  strcat (query, dbName);
  strcat (query, " VALUES(");
  res = select_all (dbName, conn);
  n_fields = PQnfields (res);
  for (i = 0; i < n_fields; i++) {
    printf ("Eingabe für %s (%s)\t:", PQfname (res, i),
            (PQftype (res, i) == INT) ? "(INT)" : "(VARCHAR)");
    fgets (buf, 255, stdin);
    ptr = strchr (buf, '\n');
    *ptr = '\0';
    if (PQftype (res, i) == VARCHAR) {
      strcat (query, "'");
      strcat (query, buf);
      strcat (query, "',");
    } 
    else if (PQftype (res, i) == INT) {
      strcat (query, buf);
      strcat (query, ",");
    } 
    else {
      printf ("Konnte Datentyp (OID) nicht ermitteln!\n");
      exit_nicely (conn);
    }
  }
  ptr = strchr (query, '\0');
  *(ptr - 1) = '\0';
  strcat(query, ");");
  PQclear(res);
  //printf ("Query: %s\n", query);
  res = PQexec (conn, query);
  if (!res || PQresultStatus (res) != PGRES_COMMAND_OK) {
    fprintf (stderr, "Fehler beim Befehl INSERT\n");
    PQclear (res);
    exit_nicely (conn);
  }
  printf("Statuszeichenkette (ges.)     : %s\n", 
     PQcmdStatus(res));
  printf("Betroffene Zeilen der Anfrage : %s\n", 
     PQcmdTuples(res));
  printf("OID der Anfrage               : %s\n", 
     PQoidStatus(res));
  PQclear(res);
}
static void delete(char *dbName, PGconn * conn) {
  int i, n_fields;
  char query[4096] = { "DELETE FROM " };
  char buf[255];
  char *ptr;
  PGresult *res;
  int n;
  strcat (query, dbName);
  strcat (query, " WHERE ");
  res = select_all( dbName, conn );
  n_fields = PQnfields (res);
  printf("Nach welchem Kriterium wollen Sie löschen?\n");
  for (i = 0; i < n_fields; i++)
    printf ("%d=%s   ",i, PQfname (res, i));
  printf(":->");
  scanf("%d", &n);
  getchar();
  strcat(query, PQfname(res, n));
  printf("Was wollen Sie löschen?: ");
  fgets(buf, 255, stdin );
  ptr = strchr( buf, '\n');
  *ptr = '\0';
  if (PQftype (res, n) == VARCHAR) {
    strcat (query, "='");
    strcat (query, buf);
    strcat (query, "';");
  } 
  else if (PQftype (res, n) == INT) {
    strcat (query, "=");
    strcat (query, buf);
    strcat (query, ";");
  }
  else {
    printf ("Konnte Datentyp (OID) nicht ermitteln!\n");
    exit_nicely (conn);
  }
  PQclear(res);
  /* printf ("Query: %s\n", query); */
  res = PQexec (conn, query);
  if (!res || PQresultStatus (res) != PGRES_COMMAND_OK) {
    fprintf (stderr, "Fehler beim Befehl INSERT\n");
    PQclear (res);
    exit_nicely (conn);
  }
  printf("Statuszeichenkette (ges.)     : %s\n",
     PQcmdStatus(res));
  printf("Betroffene Zeilen der Anfrage : %s\n",
     PQcmdTuples(res));
  PQclear(res);
}
int main (void) {
  /* char *pghost = NULL, *pgport = NULL,   */
  /*      *pgoptions = NULL, *pgtty = NULL; */
  char *dbName;
  char buf[64];
  char db[255] = { "dbname=" };
  PGconn *conn;
  PGresult *res;
  int auswahl;
  printf ("Mit welcher Datenbank wollen Sie arbeiten?: ");
  scanf ("%s", buf);
  dbName = buf;
  strcat(db, buf);
  conn = PQconnectdb(db);
  if (PQstatus (conn) == CONNECTION_BAD) {
    fprintf (stderr, "Verbindung z. Datenbank '%s' verfehlt\n",
             dbName);
    fprintf (stderr, "%s", PQerrorMessage (conn));
    exit_nicely (conn);
  } 
  else
    printf ("Mit PostgreSQL-Server verbunden ...\n");
  do {
    printf ("-1- Info zur Datenbank '%s'\n", dbName);
    printf ("-2- Alle Spaltennamen ausgeben\n");
    printf ("-3- Komplette Datenbank '%s' ausgeben\n", dbName);
    printf ("-4- Neue Daten in '%s' eingeben\n", dbName);
    printf ("-5- Eine Zeile löschen in '%s'\n",dbName);
    printf ("-6- Ende\n");
    printf ("Ihre Auswahl : ");
    scanf ("%d", &auswahl);
    getchar ();
    switch (auswahl) {
    case 1:
      res = select_all (buf, conn);
      read_info (res);
      PQclear (res);
      break;
    case 2:
      res = select_all (buf, conn);
      read_fields (res);
      PQclear (res);
      break;
    case 3:
      res = select_all (buf, conn);
      read_full (res);
      PQclear (res);
      break;
    case 4:
      insert_into (buf, conn);
      break;
    case 5:
      delete ( buf, conn );
      break;
    default:
      break;
    }
  } while (auswahl != 6);
  PQfinish (conn);
  return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc -c -I/usr/include/pgsql postgre3.c
$ gcc -o postgre3 postgre3.o -L/usr/lib/pgsql -lpq
$ ./postgre3
Mit welcher Datenbank wollen Sie arbeiten_?: adressen
Mit PostgreSQL-Server verbunden ...
-1- Info zur Datenbank 'adressen'
-2- Alle Spaltennamen ausgeben
-3- Komplette Datenbank 'adressen' ausgeben
-4- Neue Daten in 'adressen' eingeben
-5- Eine Zeile löschen in 'adressen'
-6- Ende
Ihre Auswahl : 3
Jonathan  Wolf           12345    Musterdorf     Musterstrasse  1
Fatma     Wolf           33333    Schlumpfhausen Schlumpfweg    4
John      Travolta       555444   Kalifornien    Broadway       1
Jonny     Depp           11234    Hollywood      Kingsroad      1
Arnold    Schwarzenegger 1234     Augsburg       Am Weg         3
-1- Info zur Datenbank 'adressen'
-2- Alle Spaltennamen ausgeben
-3- Komplette Datenbank 'adressen' ausgeben
-4- Neue Daten in 'adressen' eingeben
-5- Eine Zeile löschen in 'adressen'
-6- Ende
Ihre Auswahl : 4
Eingabe für vname ((VARCHAR))   :Frank
Eingabe für nname ((VARCHAR))   :Sinatra
Eingabe für plz ((INT))         :1234
Eingabe für ort ((VARCHAR))     :Kings Road
Eingabe für strasse ((VARCHAR)) :Broadway
Eingabe für hausnr ((INT))      :1
Statuszeichenkette (ges.)     : INSERT 16996 1
Betroffene Zeilen der Anfrage : 1
OID der Anfrage               : 16996
-1- Info zur Datenbank 'adressen'
-2- Alle Spaltennamen ausgeben
-3- Komplette Datenbank 'adressen' ausgeben
-4- Neue Daten in 'adressen' eingeben
-5- Eine Zeile löschen in 'adressen'
-6- Ende
Ihre Auswahl : 3
Jonathan  Wolf           12345    Musterdorf     Musterstrasse  1
Fatma     Wolf           33333    Schlumpfhausen Schlumpfweg    4
John      Travolta       555444   Kalifornien    Broadway       1
Jonny     Depp           11234    Hollywood      Kingsroad      1
Arnold    Schwarzenegger 1234     Augsburg       Am Weg         3
Frank     Sinatra        1234     Kings Road     Broadway       1
...
Ihre Auswahl : 5
Nach welchem Kriterium wollen Sie löschen?
0=vname   1=nname   2=plz   3=ort   4=strasse   5=hausnr   :->2
Was wollen Sie löschen?: 1234
Statuszeichenkette (ges.)     : DELETE 2
Betroffene Zeilen der Anfrage : 2

Rheinwerk Computing

12.9.7 Umgebungsvariablen und Passwortdatedowntop

Der folgende Abschnitt darf als eine Art Anhang zur libpq-Bibliothek verstanden werden. Es sollen Ihnen hier die Umgebungsvariablen gezeigt werden, die Sie verwenden können, um Vorgabewerte für die Verbindungsparameter zu setzen, die von den Funktionen PQconnectdb() und PQsetdbLogin() verwendet werden. Dies ist recht hilfreich, wenn man diese Daten nicht direkt in den Code schreiben will. Folgende Umgebungsvariablen und deren Bedeutung können Sie hierbei setzen – was im Endeffekt einer Wiederholung zu den Verbindungsparametern der Funktion PQconnectdb() darstellt.

gp  PGHOST – Hierbei wird der Servername gesetzt. Bei beginnendem Schrägstrich wird ein UNIX Domain Socket statt TCP/IP verwendet. Der Wert ist dann eine Verzeichnisangabe zu einer Socket-Datei – wo sich diese befindet (Standard: /tmp).
gp  PGPORT – Hierbei wird die vorgegebene TCP-Portnummer oder der UNIX-Domain-Socket-Namen gesetzt.
gp  PGDATABASE – Einen vorgegebenen Datenbanknamen können Sie damit setzen.
gp  PGUSER – Der Benutzername für die Datenbankverbindung
gp  PGPASSWORD – Hier könnten Sie das Passwort setzen, wenn eine Authentifizierung nötig ist – was logischerweise aus Sicherheitsgründen nicht zu empfehlen ist. Es wird hierzu empfohlen, die Datei ./pgpass im Homeverzeichnis zu verwenden (dazu gleich mehr).
gp  PGOPTIONS – Hier werden die Konfigurationsoptionen gesetzt, die an den PostgreSQL-Server geschickt werden sollen.
gp  PGTTY – Hier können Sie eine Datei oder das TTY für die Debug-Ausgabe des Servers angeben.

Hinweis   Natürlich gilt hierbei, wenn Sie eine bestimmte Umgebungsvariable gesetzt haben, aber beim Verbindungsaufbau einen anderen Parameter verwenden, dass der Parameter im Code den Vorrang gegenüber der Umgebungsvariablen erhält.


Die Passwortdatei zur Authentifizierung im Home-Verzeichnis wurde bereits kurz erwähnt. Wenn eine Verbindung ein Passwort benötigt, wird die Datei .pgpass dazu verwendet. Eine Zeile in dieser Datei sollte folgendermaßen aufgebaut sein:

hostname:port:datenbank:benutzername:passwort

Für jede dieser Angaben geben Sie entweder eine Konstante an oder das Zeichen *, das somit für alles passen würde. Befindet sich in der Konstante ein \ oder ein :, müssen Sie ein Backslash davor setzen (\: und \\). Außerdem müssen Sie die Zugriffsrechte auf 0600 setzen, womit nur der Eigentümer Zugriff hat. Werden die Zugriffsrechte nicht gesetzt, wird die Datei .pgpass ignoriert.


Rheinwerk Computing

12.9.8 PostgreSQL und Threaddowntop

Da PostgreSQL im Gegensatz zu MySQL schon mehr im professionellen Bereich eingesetzt wird, ist zwangsläufig auch die Frage nach der Thread-Sicherheit gestellt. Hierbei sollte es seit Version 7.0 keine Probleme geben, sofern Sie nicht mit zwei Threads gleichzeitig auf dasselbe PGconn-Objekt zugreifen. Sie können also nicht hergehen und mit Thread1 eine Zeile in der Datenbank hinzufügen und zur gleichen Zeit mit Thread2 eine Zeile aus der Datenbank entfernen. Bedenken Sie, dass Sie gleichzeitig dieselbe Verbindung verwenden. Verwenden Sie für gleichzeitige Ausführungen lieber mehrere Verbindungen. Das PGresult-Objekt bleibt bis zum nächsten Aufruf auf das Objekt unverändert und kann somit problemlos zwischen den Threads ausgetauscht werden.

Nicht thread-sicher ist die Funktion PQoidStatus() – als Alternative kann hierfür PQoidValue() verwendet werden.

Des Weiteren sollten Sie, sofern Sie eine Authentifizierungsmethode verwenden, nicht die Betriebssystemfunktion crypt() verwenden. Als Alternative wird die Methode md5 empfohlen. md5 ist auf allen Betriebssystemen thread-sicher.


Rheinwerk Computing

12.9.9 Ausblictoptop

Das Kapitel zur Schnittstelle von libpq ließe sich noch ein wenig erweitern – allerdings wäre das Buchs dann sehr einseitig durch die Programmierung von Clientanwendungen für Datenbanken geprägt. Außerdem wurden die grundlegenden und gängigen Funktionen hier beschrieben und erwähnt. Nicht angesprochen wurden Themen wie:

gp  Asynchrone Befehlsverarbeitung und asynchrone Benachrichtigung
gp  Die Fast-Path-Schnittstelle (aus Sicherheitsgründen nicht zu empfehlen)
gp  Hinweismeldungen des Servers verarbeiten und das Nachverfolgen der Server-Client-Kommunikation

Des Weiteren wurde hier nicht behandelt, was aus der Sicht des C-Programmierers doch recht interessant ist, wie Sie benutzerdefinierte Funktionen für PostgreSQL erstellen können (ähnlich wie bei der UDP-Schnittstelle in MySQL). Der Vorgang entspricht hier demselben wie bei der C-API von MySQL. Die Funktionen werden in dynamisch ladbare Module (Shared Libraries) kompiliert und vom Server bei Bedarf oder von Ihnen auf Wunsch nachgeladen.

Für diese und weitere Informationen sei die sehr gute Dokumentation (auch als Buch erhältlich und sehr empfehlenswert) über PostgreSQL auf der Buch-CD (PostgreSQL: Das Offizielle Handbuch – von Peter Eisentraut) empfohlen.

 << zurück
  
  Zum Rheinwerk-Shop
Neuauflage: Linux-UNIX-Programmierung
Neuauflage:
Linux-UNIX-
Programmierung

bestellen
 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: Linux-Server






 Linux-Server


Zum Rheinwerk-Shop: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Rheinwerk-Shop: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Rheinwerk-Shop: Shell-Programmierung






 Shell-
 Programmierung


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
Info





Copyright © Rheinwerk Verlag GmbH 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern