27.4 Die MySQL C-API
 
Die MySQL C-API ist eine Bibliothek, welche in C geschrieben wurde und die es Ihnen erlaubt, Client-Programme mit Zugriff auf die MySQL-Datenbank zu erstellen.
Tipp Die meisten Client-Programme bei MySQL wurden in C geschrieben und liegen in freier Form zum Studieren der Quellcodes vor. Wollen Sie den Quellcode von Client-Programmen wie mysql oder mysqladmin genauer unter die Lupe nehmen, müssen Sie sich die Quellcode-Distribution herunterladen. Im Verzeichnis client finden Sie dann außer den erwähnten Client-Programmen weitere Beispiele dazu.
|
Abgesehen von der MySQL C-API gibt es noch MySQL APIs zu Perl, PHP, C++, Python, Tcl und einen Eiffel-Wrapper, welche aber alle – wie auch die C API – die mysqlclient-Bibliothek verwenden. Eine Ausnahme stellt hierbei nur JAVA dar. Um mit JAVA auf den MySQL-Server zuzugreifen, wird der JDBC-Treiber benötigt.
27.4.1 Grundlagen zur Programmierung eines MySQL-Clients
 
Bevor Sie beginnen eigene Client-Programme zu schreiben, sollten Sie zunächst in Erfahrung bringen, wie Sie ein solches Programm erstellen können. Das Kompilieren und Linken eines Client-Programms ist gerade unter Windows recht umständlich. Umständlich daher, weil die Bibliothek libmysql.lib mit dem Visual C++-Compiler erzeugt wurde. Für denjenigen, der diesen Compiler unter Windows verwendet, kann das egal sein. Aber viele Benutzer mit anderen Compilern werden recht schnell frustriert aufgeben, eigene MySQL-Client-Programme zu schreiben, wenn sich die Bibliothek nicht mit dem Compiler verträgt.
Dabei darf neidisch auf die Linux-Anwender geschaut werden, da es auf diesem System überhaupt keine Probleme gibt, denn dort wird der Compiler gcc verwendet. Die Auswahl des Compilers und das Herumärgern mit der Bibliothek fällt also schon einmal weg. Damit Sie einen MySQL-Client programmieren können, benötigen Sie Folgendes:
|
Die MySQL Bibliothek (lib) |
|
Die MySQL Headerdateien (include) |
Unter Windows ist bei einer Standardinstallation von MySQL in der Regel beides vorhanden. Die Bibliothek sollten Sie dabei im Verzeichnis (»c:« sei das Laufwerk) c:\mysql\lib\opt vorfinden und die Headerdateien im Verzeichnis c:\mysql\include.
Bei Linux sollten Sie die Bibliothek im Verzeichnis /usr/lib oder /usr/lib/mysql oder auch /usr/local/lib/mysql finden. Die Headerdatei ist in der Regel unter /usr/include/mysql oder auch /usr/local/include/mysql abgelegt. Bei einer RPM-Installation unter Linux kann es auch sein, dass Sie zur Entwicklung eigener Clients noch ein extra RPM installieren müssen (Developer RPM).
Wollen Sie jetzt den Client-Quellcode, den Sie erstellt haben, kompilieren und linken, müssen Sie angeben, wo sich die Headerdateien und die Bibliothek von MySQL befinden.
Bei Windows müssen Sie außerdem die DLL libmysql.dll in ein Systemverzeichnis kopieren. Zumeist sollte dies das Verzeichnis c:\Windows\system32 sein. Außerdem sollten Sie in der Quelldatei unter Windows den Header <windows.h> noch vor der Headerdatei <mysql.h> inkludieren. Etwa so:
#if defined __WIN32__ || _MSC_VER
#include <windows.h>
#endif
#include <mysql.h>
27.4.2 Client-Programm mit dem gcc- unter Linux und dem Cygwin gcc-Compiler unter Windows
 
Zuerst kompilieren Sie das geschriebene Client-Programm, in dem Sie mit dem Compilerflag –I (Include) angeben, an welcher Stelle sich die Headerdateien von MySQL befinden. Bei Linux mit gcc:
gcc –c –I/usr/include/mysql myclient.c
Windows unter der Cygwin-Umgebung mit gcc:
gcc –c –I"c:\mysql\include" myclient.c
Jetzt verfügen Sie über eine Objektdatei in dem Verzeichnis, in welchem Sie den Quellcode kompiliert haben. Als Nächstes müssen Sie diese Objektdatei zu einer ausführbaren Datei linken. Damit dies auch klappt, müssen Sie dem Linker mitteilen, wo sich die Client-Bibliothek von MySQL befindet. Dies erledigen Sie mit dem Compilerflag –L, mit dem der Pfad zur Bibliothek angegeben wird, und dem Flag –l, mit dem Sie die Bibliothek angeben, welche hinzugelinkt werden soll. Die Eingabe für Linux lautet:
gcc –o myclient myclient.o -L/usr/lib/mysql -lmysqlclient
Und mit Windows unter der Cygwin-Umgebung:
gcc –o myclient.exe myclient.o -L"c:\mysql\lib\opt" -llibmysql
Bei Windows sollten Sie die dynamische Bibliothek libmySQL hinzulinken, welche ein Wrapper zum Laden von libmySQL.dll ist. Es ist aber auch möglich, die statische Bibliothek mysqlclient.lib hinzuzulinken.
Wenn alles bis hierher glatt verlaufen ist, können Sie den Client beim Namen aufrufen.
27.4.3 MySQL Client-Programme mit dem VC++ Compiler und dem Borland Freeware Compiler
 
Bei beiden Compilern wird davon ausgegangen, dass diese bereits auf Ihrem System installiert sind. Installationsanleitungen diverser Compiler finden Sie unter http://www.pronix.de.
MS-Visual C++
Erstellen Sie wie gewöhnlich ein leeres Win32-Konsolen-Fensterprojekt. Fügen Sie dem Projekt eine C++-Datei hinzu. In diese Datei können Sie jetzt wie gewohnt Ihren Quellcode eingeben. Binden Sie die Headerdatei <mysql.h> über die Pfadangabe mit ein:
#include <stdio.h>
#if defined __WIN32__ || _MSC_VER
#include <windows.h>
#endif
#include "c:\mysql\include\mysql.h"
Jetzt können Sie den Quellcode kompilieren, aber noch nicht linken. Zuvor müssen Sie dem Linker noch den Pfad zur mysql-Bibliothek mitteilen. Dies realisieren Sie über Projekt_$punkt_Einstellungen. Im Register Linker im Feld Objekt-/Bibliothek-Module fügen Sie jetzt am Ende folgenden Pfad hinzu:
c:\mysql\lib\opt\libmysql.lib
 Hier klicken, um das Bild zu Vergrößern
Abbildung 27.7
Pfadangabe zur MySQL-Bibliothek für den Linker
Jetzt können Sie das Programm linken und ausführen. Voraussetzung ist natürlich auch, dass Sie die DLL libmySQL.dll in ein Systemverzeichnis kopiert haben (bspw. system32).
Borland Freeware Compiler
Im bin-Verzeichnis des Borland-Compilers befindet sich das Tool coff2omf, mit dem Sie die im Microsoft-Croff-Format vorliegende libmysql in ein für Borland übliches Format, dem omf, konvertieren können:
coff2omf c:\mysql\lib\opt\libmysql.lib c:\borland\bcc55\lib\
libmysql.lib
Damit kopieren Sie auch gleich die im OMF-Format erzeugte Bibliothek libmysql in das Bibliothekenverzeichnis des Borland-Compilers. Das war es auch schon. Jetzt können Sie das Client-Programm kompilieren:
bcc32 –c –I"c:\mysql\include" myclient.c
Nun befindet sich eine Objektdatei im entsprechenden Verzeichnis, welche Sie mit den Dateien c0x32.obj, import32.lib sowie cw32.lib und der Bibliothek libmysql zusammenlinken. Die Objektdatei c0x32 und die beiden Bibliotheken import32 und cw32 sind notwendig für eine Konsolenanwendung. Hier die vollständige Linkeranweisung zu einer ausführbaren Datei:
ilink32 c0x32.obj myclient.obj , myclient.exe , ,
import32.lib cw32.lib libmysql.lib
Bei dieser umständlichen Schreibweise empfiehlt es sich, eventuell eine Batchdatei zu erstellen. Nun können Sie den Client mit dem Namen starten.
27.4.4 Troubleshooting
 
Die MySQL C-API verwendet auch Funktionen aus der Headerdatei <math.h> (floor()). Bei manchen Linux-Distributionen müssen Sie <math.h> mit dem Compilerflag –lm hinzulinken:
gcc –o myclient myclient.o -L/usr/lib/mysql –lmysqlclient -lm
Bei Solaris müssen außerdem noch folgende zwei Bibliotheken hinzugelinkt werden:
gcc –o myclient myclient.o -L/usr/lib/mysql –lmysqlclient –lm
–lsocket –lnsl
Erhalten Sie einen undefined reference-Fehler bei den Funktionen compress und uncompress, müssen Sie die Bibliothek zlib mit –lz hinzulinken:
gcc –o myclient myclient.o -L/usr/lib/mysql –lmysqlclient –lm
–lsocket –lnsl –lz
Erhalten Sie vom Compiler hingegen undefined reference-Fehler auf verschiedene Funktionen, dann verträgt sich die mysqlclient-Bibliothek nicht mit dem Compiler. Dann müssen Sie sich entweder einen Compiler besorgen, mit dem sich diese Bibliothek verträgt, oder Sie laden sich die Quellcode-Distribution herunter und übersetzen sich die Bibliothek für Ihren Compiler selbst. Wie Sie dabei vorgehen, können Sie dem MySQL-Manual entnehmen.
27.4.5 Das erste Client-Programm – Verbindung mit dem MySQL-Server herstellen
 
Ihr erstes MySQL-Client-Programm wird das einfachste sein. Das Programm stellt ein Grundgerüst für die weiteren Client-Programme dar, die Sie noch schreiben werden. Bevor Sie nämlich umfangreiche Datenbankoperationen vornehmen, müssen Sie sich erst mit dem MySQL-Server verbinden.
Zuerst müssen Sie für ein MySQL-Objekt Speicherplatz reservieren und es initialisieren. Ein MYSQL-Handle erstellen Sie wie folgt:
MYSQL *my;
Die Struktur MYSQL repräsentiert einen Handle für eine Datenbankverbindung, welchen Sie für fast alle MySQL-Funktionen benötigen. Die Struktur MYSQL können Sie sich in der Headerdatei <mysql.h> gern genauer ansehen.
Um für diesen Handle jetzt Speicherplatz zu reservieren und zu initialisieren, wird die Funktion mysql_init() verwendet. Hier die Syntax zu dieser Funktion:
MYSQL *mysql_init(MYSQL *mysql);
Wird diese Funktion mit einem NULL-Zeiger aufgerufen, wird zuerst Speicherplatz für ein MYSQL-Objekt alloziiert und gleich darauf initialisiert. Zurück gibt diese Funktion dann ein neues MYSQL-Objekt. Rufen Sie diese Funktion hingegen mit einem MYSQL-Objekt auf, wird nur das Objekt initialisiert und die Adresse des Objekts zurückgegeben. In beiden Fällen gibt diese Funktion entweder einen initialisierten MYSQL-Handle zurück oder im Fehlerfall den (C-typischen) NULL-Zeiger.
MYSQL *my;
my = mysql_init(NULL);
if(my == NULL) {
fprintf(stderr, "Fehler beim Initialisieren \n");
exit (EXIT_FAILURE);
}
Die erfolgreiche Ausführung der Funktion mysql_init() ist Voraussetzung für die nächste Funktion mysql_real_connect(), mit der versucht wird, eine Verbindung mit dem MySQL-Server aufzubauen. Die Syntax dieser Funktion ist ein wenig lang, aber trotzdem recht klar:
MYSQL *mysql_real_connect( MYSQL *mysql,
const char *host,
const char *user,
const char *passwort,
const char *db,
unsigned int port,
const char *unix_socket,
unsigned int client_flag );
Ohne erfolgreiche Ausführung dieser Funktion ist es nicht möglich, irgendeine weitere Funktion aus der C-API zu verwenden (mit Ausnahme von mysql_get_client_info()). In der folgenden Tabelle finden Sie die Bedeutungen der einzelnen Parameter der Funktion mysql_real_connect().
Tabelle 27.8
Bedeutung der Parameter der Funktion mysql_real_connect()
Parameter
|
Bedeutung
|
MYSQL *mysql
|
Die Adresse einer existierenden MYSQL-Struktur, welche zuvor mit der Funktion mysql_init() initialisiert wurde.
|
const char *host
|
Ein Hostname oder eine IP-Adresse; wird hierfür NULL oder "localhost" angegeben, wird versucht, eine Verbindung zum lokalen Host aufzubauen. Bei Betriebssystemen mit Sockets und Named Pipes werden diese Verbindungen statt TCP/IP verwendet, um eine Verbindung mit dem Server aufzubauen.
|
const char *user
|
Der Username, welcher beim MySQL-Login verwendet wird; bei Angabe von NULL wird der aktuell eingeloggte User verwendet.
|
const char *passwort
|
Das Passwort für den user; wird hier NULL angegeben, kann nur auf die user-Tabellen zugegriffen werden, welche ein leeres Passwort-Feld besitzen.
|
const char *db
|
Name der Datenbank (Datenbank muss existieren); bei NULL wird eine vorgegebene Datenbank verwendet.
|
unsigned int port
|
Es wird die Portnummer der TCP/IP-Verbindung verwendet, welche der host-Parameter festlegt. Ansonsten wird dabei Port 0 verwendet.
|
const char *unix_socket
|
Entweder NULL oder ein String, der ein Socket oder Named Pipe festlegt; der Verbindungstyp wird dabei vom host-Parameter festgelegt.
|
unsigned int client_flag
|
In der Regel wird hierfür der Wert 0 angegeben. Es sind aber auch spezielle Optionen, einzeln oder kombiniert, möglich:
CLIENT_COMPRESS, CLIENT_FOUND_ROWS, CLIENT_IGNORE_SPACE, CLIENT_INTER_ACTIVE, CLIENT_NO_SCHEMA, CLIENT_ODBC, CLIENT_SSL
|
Die Funktion mysql_real_connect() gibt als Rückgabewert bei Erfolg den MYSQL-Handle des ersten Parameters zurück. Bei Fehler wird NULL zurückgeliefert. Somit sieht der vorzeitige Code folgendermaßen aus:
MYSQL *my;
my = mysql_init(NULL);
if(my == NULL) {
fprintf(stderr, "Initialisierung fehlgeschlagen\n");
exit (EXIT_FAILURE);
}
if( mysql_real_connect (
my, /* Zeiger auf MYSQL-Handler */
def_host_name, /* Host-Name */
def_user_name, /* User-Name */
def_passwort, /* Passwort für user_name */
def_db_name, /* Name der Datenbank */
0, /* Port (default=0) */
NULL, /* Socket (default=NULL) */
0 /* keine Flags */ ) == NULL)
Da beim Aufruf der Funktion mysql_real_connect() fast ein gutes Dutzend Fehler auftreten kann, wäre es sehr aufschlussreich zu erfahren, was denn nicht funktioniert hat.
Zum Glück müssen Sie hierfür keine spezielle Routine schreiben, welche die einzelnen Fehler abfragt. Sie können die Funktion mysql_errno() und mysql_error() verwenden. Zuerst die Syntax der Funktion mysql_errno():
unsigned int mysql_errno(MYSQL *mysql);
Schlägt die Verbindung mit dem Handler mysql fehl, gibt diese Funktion einen Fehlerstatuscode zurück. Wird kein Fehler festgestellt, gibt mysql_errno() 0 zurück.
Hinweis Fehler, die bei einem Client-Programm auftreten können, finden Sie in der Headerdatei <errmsg.h>, welche sich im selben Verzeichnis wie <mysql.h> befindet. Fehler des Serverprogramms befinden sich in der Headerdatei <mysqld_error.h>.
|
Ein Fehlerstatuscode alleine nützt dem Anwender des Client-Programms allerdings recht wenig. Sie könnten jetzt die einzelnen symbolischen Konstanten der Headerdatei <errmsg.h> auswerten, etwa so:
unsigned int error;
...
if( mysql_real_connect (
my, /* Zeiger auf MYSQL-Handler */
def_host_name, /* Host-Name */
def_user_name, /* User-Name */
def_passwort, /* Passwort für user_name */
def_db_name, /* Name der Datenbank */
0, /* Port (default=0) */
NULL, /* Socket (default=NULL) */
0 /* keine Flags */ ) == NULL)
error = mysql_errno(my);
if( error == CR_CONN_HOST_ERROR )
fprintf(stderr, "Keine Verbindung zu Host\n");
Die C-API für MySQL hat aber auch hierfür eine Funktion parat. Die Syntax lautet:
char *mysql_error(MYSQL *mysql);
Diese Funktion gibt eine entsprechende Fehlermeldung auf dem Bildschirm aus, die von der zuletzt aufgerufenen API-Funktion provoziert wurde. Trat kein Fehler auf, gibt die Funktion einen leeren String (Index[0] == '\0') zurück. Somit können Sie, falls die Funktion mysql_real_connect() NULL zurückgibt, folgendermaßen eine Fehlermeldung auf dem Bildschirm ausgeben (my sei der MYSQL-Handle):
fprintf (stderr, "Fehler mysql_real_connect(): %u (%s)\n",
mysql_errno (my), mysql_error (my));
Hiermit wird im Fall eines Fehlers der Statuscode und ein String mit entsprechender Fehlermeldung ausgegeben. Jetzt haben Sie eine Verbindung mit dem MySQL-Server hergestellt und könnten damit arbeiten. Aber dazu mehr im nächsten Abschnitt.
Hinweis Wollen Sie sich die Fehlermeldungen in deutscher Sprache ausgeben lassen, geben Sie Folgendes in der Kommandozeile ein:
mysqld --language=german
|
Wenn eine Verbindung zum Server aufgebaut wurde, sollte diese auch irgendwann wieder freigegeben werden. Eine saubere Beendigung des Clients, also des MYSQL-Handles, erreichen Sie mit der Funktion mysql_close():
void mysql_close(MYSQL *mysql);
Dies sind die grundlegenden Aufgaben, um eine Verbindung mit dem MySQL-Server herzustellen und am Ende auch wieder zu beenden. Dazu jetzt alle Funktionen als ausführbares Programm:
/* mysql1.c */
#include <stdio.h>
#include <stdlib.h>
#if defined __WIN32__ || _MSC_VER
#include <windows.h>
#endif
#include <mysql.h>
// Für VC++:
//#include "c:\mysql\include\mysql.h"
int main (int argc, char **argv) {
MYSQL *my;
/* Handle initialisieren */
my = mysql_init(NULL);
if(my == NULL) {
fprintf(stderr, " Initialisierung fehlgeschlagen\n");
return EXIT_SUCCESS;
}
/* Mit dem Server verbinden */
if( mysql_real_connect (
my, /* Zeiger auf MYSQL-Handler */
NULL, /* Host-Name */
NULL, /* User-Name */
NULL, /* Passwort für user_name */
NULL, /* Name der Datenbank */
0, /* Port (default=0) */
NULL, /* Socket (default=NULL) */
0 /* keine Flags */ ) == NULL) {
fprintf (stderr, "Fehler mysql_real_connect():"
"%u (%s)\n",mysql_errno (my), mysql_error (my));
}
else
printf("Erfolgreich mit dem MySQL-Server verbunden\n");
/* Hier befindet sich der Code für die Arbeit mit MySQL */
/* Verbindung trennen */
mysql_close (my);
return EXIT_SUCCESS;
}
27.4.6 MySQL-Kommandozeilen-Optionen
 
Im Listing zuvor wurde die Verbindung zum MySQL-Server über die Funktion mysql_real_connect() mit Default-Werten bzw. NULL-Zeigern aufgebaut. Das dies in der Praxis häufig nicht so ist, dürfte klar sein. Dieses Thema ist im Prinzip nicht Teil dieses Buchs, doch einige Worte sollen hierzu trotzdem gesagt werden. Vielleicht erinnern Sie sich noch, wie der mysql-Client aufgerufen wurde:
mysql –u root –h localhost
Die Bedeutung dieser einzelnen Flags und aller weiteren finden Sie in der folgenden Tabelle:
Tabelle 27.9
MySQL-Kommandozeilen-Optionen (Flags)
Parameter
|
Kurze Form
|
Lange Form
|
Hostname
|
-h host_name
|
--host=host_name
|
Username
|
-u user_name
|
--user=user_name
|
Passwort
|
-p oder
-p your_password
|
--password oder
–password=your_password
|
Portnummer
|
-P port_num
|
--port=port_num
|
Socket Name
|
-S socket_name
|
--socket=socket_name
|
Damit Ihr Client, den Sie programmieren, ebenso Flags auswerten kann wie der mysql-Client, müssen Sie entweder die Argumente aus der Kommandozeile selbst auswerten oder die einzelnen Argumente im Programm abfragen. Sollten Sie auf einem System arbeiten, welches die Bibliothek getopt() beinhaltet, rate ich Ihnen, diese dafür zu verwenden.
Wollen Sie wissen, wie die Default-Optionen für die aktuelle Verbindung mit MySQL lauten, können Sie die Funktion load_defaults() einsetzen. Die Syntax lautet:
void load_defaults(const char *conf_file, const char **groups,
int *argc, char ***argv);
Mit dieser Funktion laden Sie aus conf_file – unter Linux die Datei my.cnf und unter Windows my.ini – die Optionen der Gruppen groups. Zusätzlich werden noch die Kommandozeilenargumente zur Auswertung verwendet.
Für die Variable groups können Sie einen oder mehrere Strings angeben. Dies sind die Zeilen, welche in den eben genannten Konfigurations-Files zwischen eckigen Klammen stehen, bspw. unter Windows:
[WinMySQLadmin]
user=Jonathan
password=sql
host=localhost
...
Das letzte Element in der Gruppe muss ein NULL-Zeiger sein:
char *groups[] = {
"client", "WinMySQLadmin", NULL
};
Für den String conf_file wird in der Regel immer "my" verwendet. Bevor Sie jetzt die Funktion load_default() verwenden können, müssen Sie noch die Funktion my_init() aufrufen. Hierzu das Listing zu load_default():
/* mysql2.c */
#include <stdio.h>
#include <stdlib.h>
#if defined __WIN32__ || _MSC_VER
#include <windows.h>
#endif
#include <mysql.h>
// Für VC++:
//#include "c:\mysql\include\mysql.h"
int main (int argc, char **argv) {
int i;
char *groups[] = {
"client", "WinMySQLadmin", NULL
};
my_init ();
printf ("Ursprüngliche Argumente:\n");
for (i = 0; i < argc; i++)
printf ("argv[%d] : %s\n", i, argv[i]);
load_defaults ("my", (const char **)groups, &argc, &argv);
printf ("Angepasste Argumente nach load_default():\n");
for (i = 0; i < argc; i++)
printf ("argv[%d] : %s\n", i, argv[i]);
return EXIT_SUCCESS;
}
 Hier klicken, um das Bild zu Vergrößern
Abbildung 27.8
Angepasste Argumente mit load_default()
Wollen Sie die MySQL-Umgebungsvariablen MYSQL_TCP_PORT für die Portnummer und MSQL_UNIX_SOCKET für den Socket-Namen verwenden, können Sie dafür die Funktion getenv() aus der Headerdatei <stdlib.h> nutzen:
char *p;
int port_num = 0;
char *socket_name = NULL;
if ((p = getenv ("MYSQL_TCP_PORT")) != NULL)
port_num = atoi (p);
if ((p = getenv ("MYSQL_UNIX_PORT")) != NULL)
socket_name = p;
...
if( mysql_real_connect (
my, /* Zeiger auf MYSQL-Handler */
NULL, /* Host-Name */
NULL, /* User-Name */
NULL, /* Passwort für user_name */
NULL, /* Name der Datenbank */
port_num, /* Port */
socket_name, /* Socket */
0 /* keine Flags */ ) == NULL)
27.4.7 Anfrage an den Server
 
Ich empfehle Ihnen jetzt eine nochmalige Wiederholung der SQL-Anweisungen, da Sie einige davon auf den nächsten Seiten mit der C-API wieder verwenden werden. Als Beispiel soll hier gezeigt werden, wie Sie auf eine bereits vorhandene Datenbank zugreifen können. Dabei dient die Datenbank dvd_archiv als Grundlage, welche Sie im MySQL-Crashkurs erstellt haben. Falls Sie diese bereits gelöscht haben, erstellen Sie diese bitte nochmals. Hier die einzelnen Schritte dafür:
mysql> CREATE DATABASE dvd_archiv;
mysql> USE dvd_archiv;
mysql> CREATE TABLE filmdaten (
-> titel CHAR(255), hauptrolle CHAR(255),
-> fsk TINYINT, gedreht YEAR);
Zum Schluss dieses Kapitels werden Sie dazu ein etwas umfangreicheres Beispiel erstellen.
Wie schon beim mysql-Client-Programm gibt es auch bei der C-API zwei Arten von Anfragen an den Server:
|
Das Client-Programm sendet eine Anfrage an den Server, wobei der Server nicht antwortet. |
|
Das Client-Programm sendet eine Anfrage an den Server, und dieser gibt dem Client einen Rückgabewert. |
Wenn Sie wollen, können Sie sich dies wie bei den Funktionen vorstellen. Es gibt Funktionen mit einem und Funktionen ohne einen Rückgabewert. Eine Anfrage an den Server können Sie mit der Funktion mysql_real_query() (bzw. auch mysql_query()) stellen. Jede Anfrage an den Server läuft folgendermaßen ab:
|
Sie erstellen eine SQL-Anfrage an den Server. |
|
Der Server erhält die Anfrage und überprüft diese auf syntaktische Fehler. |
|
Der Server führt die Anfrage aus und gibt das Resultat zurück. Ob ein Wert zurückgegeben wird, hängt von der Art der Anfrage ab. So gibt beispielsweise die Anfrage INSERT keinen Rückgabewert zurück, im Gegensatz zu einer Anfrage mit dem Kommando SELECT. |
Zuerst die Syntax der Funktion für das Stellen einer Anfrage:
int mysql_query(MYSQL *mysql, const char *anfrage);
int mysql_real_query( MYSQL *mysql, const char *anfrage,
unsigned long laenge );
Damit wird die SQL-Anweisung anfrage ausgeführt, welche allerdings im Gegensatz zur Anfrage, die Sie beim MySQL-Crashkurs kennen gelernt haben, kein Semikolon oder \g am Ende haben darf. Bei dem String anfrage handelt es sich außerdem um einen nullterminierten String. Der Rückgabewert dieser Funktion lautet 0, wenn alles glatt verlief, ansonsten ungleich 0.
Hinweis Für den Fall, dass Sie eine Anfrage mit Binärdaten stellen wollen, müssen Sie auf jeden Fall die Funktion mysql_real_query() verwenden.
|
Dies ist erforderlich, da Binärdaten das Stringende-Zeichen '\0' enthalten können, was bei mysql_query() das Ende der Anfrage bedeutet.
Da mit mysql_query() keinerlei Angaben zur Länge des Strings gemacht werden, ist diese Funktion ein Kandidat für einen Buffer Overflow – deshalb sollte immer die Funktion mysql_real_query() bevorzugt werden.
|
Wenn Sie eine Anfrage an den Server mit der Funktion mysql_real_query() stellen, und es will nicht klappen, könnte es an einem der folgenden Fehler liegen:
|
Der MySQL-Server ist gar nicht in Betrieb. Klingt banal, aber kommt häufig vor. |
|
Sie haben keine entsprechenden Zugriffsrechte, was unter Linux öfter der Fall ist. |
|
Die Syntax der Anfrage ist falsch. |
|
Die Anfrage ist ungültig. Beispielsweise wollen Sie den Inhalt einer Tabelle ausgeben, die gar nicht existiert. |
Jetzt folgt auf den nächsten Seiten das versprochene Programm, womit Sie auf die im Crashkurs erstellte Datenbank dvd_archiv zugreifen.
Sofern Sie die Datenbank dvd_archiv bereits wieder gelöscht haben, oder Sie ebenso wie der Autor zu den Personen gehören, die gerne kreuz und quer lesen, finden Sie hier nochmals die MySQL-Befehle (für den mysql-Client) um die entsprechende Datenbank mitsamt der Struktur anzulegen, welche Sie für das folgende Listing benötigen:
CREATE DATABASE IF NOT EXISTS dvd_archiv;
CREATE TABLE filmdaten (
titel CHAR(255),
hauptrolle CHAR(255),
fsk tinyint,
gedreht YEAR
);
Einen ersten Überblick, welche Funktionen auf den folgenden Seiten erstellt werden, können Sie den Angaben der Funktionsprototypen und der main()-Funktion des Programms entnehmen:
/* mysql3.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined __WIN32__ || _MSC_VER
#include <windows.h>
#endif
#include <mysql.h>
// Für VC++:
//#include "c:\mysql\include\mysql.h"
/* Wegen sehr vielen malloc()-Aufrufen im Listing
* wurde hier zur Verkürzung des Listings ein Makro
* geschrieben, welches aber keine Schule machen soll!!
*/
#define malloc(size) \
malloc(size);\
if( (size) == NULL)\
{\
printf("Kein Speicher mehr ...\n");\
return;\
}
/* Funktionsprototypen */
void check_error(void);
void verbinden(void);
void verbindung_schliessen(void);
void db_waehlen(char *);
void filmdaten_anfuegen(void);
void filmdaten_loeschen(void);
void filmdaten_aendern(void);
void alle_daten_ausgeben(void);
void schauspieler_suchen(char *);
void print_line(MYSQL_RES *);
MYSQL *mysql;
/* Ab hier können Sie die einzelnen Funktionen der
* kommenden Seiten einfügen
*/
int main (int argc, char **argv) {
int auswahl;
char darsteller[255];
char *ptr;
printf("Baue verbindung zur Datenbank auf ...\n\n");
verbinden();
db_waehlen("dvd_archiv");
do {
printf("\n-1- Filmdaten hinzufuegen\n");
printf("-2- Filmdaten loeschen\n");
printf("-3- Filmdaten aendern\n");
printf("-4- Alle Filmdaten ausgeben\n");
printf("-5- Film suchen\n");
printf("-6- Programm beenden\n\n");
printf("Ihre Auswahl : ");
scanf("%d",&auswahl);
getchar();
switch(auswahl) {
case 1: filmdaten_anfuegen();
break;
case 2: filmdaten_loeschen();
break;
case 3: filmdaten_aendern();
break;
case 4: alle_daten_ausgeben();
break;
case 5: printf("Suchkriterium Schauspieler "
"(Name eingeben): ");
fgets(darsteller, 254, stdin);
if((ptr=(char *)strchr(darsteller, '\n'))!= NULL)
/* newline durch \0 ersetzen */
*ptr = '\0';
schauspieler_suchen(darsteller);
break;
case 6: printf("...beende Verbindung zur Datenbank\n");
break;
default:printf("Falsche Eingabe\n\n");
}
} while(auswahl != 6);
verbindung_schliessen();
return EXIT_SUCCESS;
}
Auf den ersten Blick sieht das Programm noch nicht wie ein MySQL-Client-Programm aus. Die API-Funktionen zum Verbinden mit dem Server, die Fehlerüberpüfung und das Schließen einer Verbindung wurden aus der main()-Funktion entfernt und sind somit modular verfügbar. Hier die Funktionen verbinden(), check_error(), verbindung_schliessen() und db_waehlen():
/* Bricht bei Fehler (mysql_error != 0) das Programm ab */
void check_error(void) {
if (mysql_errno(mysql) != 0) {
fprintf(stderr, "Fehler: %s\n", mysql_error(mysql));
exit(EXIT_FAILURE);
}
}
/* Baut eine Verbindung zum Datenbankserver auf.
* Passen Sie ggf. Usernamen und Passwort und, sofern
* andere Parameter benötigt werden, diese Ihren
* Umständen selbst an
*/
void verbinden(void) {
mysql=mysql_init(mysql);
check_error();
mysql_real_connect(mysql, "localhost", "root",
NULL, NULL, 0, NULL, 0);
check_error();
}
/* Serververbindung wieder schließen und den Speicher für die
* Struktur MYSQL wieder freigeben */
void verbindung_schliessen(void) {
mysql_close(mysql);
}
/* Falls die Datenbank bei der Funktion verbinden() nicht
* angegeben wurde oder Sie die Datenbank wechseln wollen, dann
* verwenden Sie diese Funktion */
void db_waehlen(char *db) {
mysql_select_db(mysql, db);
check_error();
}
Die einzelnen Funktionen stellen bis auf die Funktion db_waehlen() nichts Neues mehr für Sie dar. In der Funktion db_waehlen() finden Sie die API-Funktion mysql_select_db(), welche die folgende Syntax hat:
int mysql_select_db(MYSQL *mysql, const char *db);
Mit dieser Funktion wechseln Sie in die Datenbank mit dem Namen db. Mit dem MYSQL-Handle mysql können Sie jetzt auf diese Datenbank mit weiteren Operationen zugreifen. Haben Sie keine Zugriffsrechte oder existiert diese Datenbank nicht, liefert sie einen Wert ungleich 0 zurück. Bei Erfolg hingegen ist der Rückgabewert 0. Diese Funktion entspricht also der SQL-Anweisung USE.
Jetzt werden Sie eine einfache Anfrage an den Server stellen. Es sollen dabei neue Daten in die Datenbank mit der SQL-Anweisung INSERT eingefügt werden. Damit Sie jetzt nicht mehr so weit zurückblättern müssen, hier noch einmal das erforderliche SQL-Kommando:
INSERT INTO filmdaten (titel, hauptrolle, fsk, gedreht) VALUES
(’Der Patriot’, ’Mel Gibson’, 16, 2001);
Die vollständige Funktion filmdaten_anfuegen():
/* Daten mit mysql_real_query() in die Datenbank schreiben */
void filmdaten_anfuegen(void) {
char titel[255], hauptrolle[255], temp[6];
unsigned int fsk, gedreht;
int i, size=0;
char *str[9], *query;
char *ptr;
printf("\n\nFilmtitel : ");
fgets(titel, 254, stdin);
if( (ptr = (char *) strchr(titel, '\n')) != NULL)
*ptr = '\0'; /* newline durch \0 ersetzen */
printf("Hauptrolle : ");
fgets(hauptrolle, 254, stdin);
if( (ptr = (char *) strchr(hauptrolle, '\n')) != NULL)
*ptr = '\0'; /* newline durch \0 ersetzen */
printf("FSK : ");
fgets(temp, 4, stdin);
if( (ptr = (char *) strchr(temp, '\n')) != NULL)
*ptr = '\0'; /* newline durch \0 ersetzen */
sscanf(temp, "%d", &fsk);
printf("Gedreht : ");
fgets(temp, 5, stdin);
if( (ptr = (char *) strchr(temp, '\n')) != NULL)
*ptr = '\0'; /* newline durch \0 ersetzen */
sscanf(temp, "%d", &gedreht);
/* Jetzt wird der Anfragestring erstellt */
str[0] = "INSERT INTO filmdaten (titel, hauptrolle, fsk, "
"gedreht) VALUES ('";
str[1] =(char *) malloc(strlen(titel)+1);
strcpy(str[1], titel);
str[2] = "','";
str[3] =(char *) malloc(strlen(hauptrolle)+1);
strcpy(str[3], hauptrolle);
str[4] = "',";
str[5] =(char *) malloc(3);
sprintf(str[5], "%2d", fsk);
str[6] = ",";
str[7] =(char *) malloc(5);
sprintf(str[7], "%4d", gedreht);
str[8] = ")";
for (i=0; i < 9; i++)
size+=strlen(str[i]);
/* Speicherplatz für den Anfragestring reservieren */
query =(char *) malloc(size + 1);
strcpy(query, str[0]);
for(i = 1; i < 9; i++)
strcat(query, str[i]);
/* Zum Testen für die Konsole */
/* printf("%s",query); */
/* Jetzt die Anfrage an den Datenbankserver */
mysql_real_query(mysql, query, strlen(query));
check_error();
free(query);
}
Der Großteil dieser Funktion vollführt nichts anderes, als den Anfragestring für die Funktion mysql_real_query() dynamisch zu erstellen. Es wird hierbei davon ausgegangen, dass Sie mittlerweile gute Kenntnisse in C besitzen, sonst hätte es wohl kaum Sinn, sich mit dem Thema MySQL und C zu befassen. Wenn alles glatt verlief, befindet sich in der Datenbank dvd_archiv ein neuer Eintrag in der Tabelle filmdaten. Diese Funktion beachtet auch Einträge, bei denen Sie zum Beispiel keine Daten eingeben und einfach mit (¢) quittieren, damit dieses Feld leer bleibt.
Hinweis Wollen Sie vermeiden, dass keine NULL-Werte oder doppelte Einträge in der MySQL-Datenbank vorgenommen werden, so können Sie jeden Datensatz in einer Tabelle mit einem Schlüssel, dem so genannten Primärschlüssel, belegen. Dieser Schlüssel wird bei Erstellung der Tabelle mit dem CREATE TABLE-Kommando übergeben. Mit der Option NOT NULL sorgen Sie dafür, dass keine leeren Werte gespeichert werden, und mit PRIMARY KEY legen Sie fest, dass keine doppelten Einträge für ein bestimmtes Feld gespeichert werden dürfen. Genaueres dazu entnehmen Sie bitte der MySQL-Dokumentation oder entsprechender Literatur.
|
Als Nächstes soll die Funktion zum Löschen eines Datensatzes in der Tabelle filmdaten geschrieben werden:
void filmdaten_loeschen(void) {
char del[255], temp[4];
char *item[] = {"titel", "hauptrolle", "fsk", "gedreht" };
char *ptr;
char *str[5], *query='\0';
int auswahl, i, size=0;
unsigned long affected;
printf("Nach welchem Kriterium wollen Sie Daten loeschen\n");
printf("[1]=Titel [2]=Hauptrolle [3]=FSK "
"[4]=Datum : [ ]\b\b");
fgets(temp, 3, stdin);
if( (ptr = (char *) strchr(temp, '\n')) != NULL)
*ptr = '\0'; /* newline durch \0 ersetzen */
sscanf(temp, "%d", &auswahl);
str[0] = "DELETE FROM filmdaten WHERE ";
if(auswahl > 0 && auswahl < 5) {
str[1] =(char *) malloc(strlen(item[auswahl-1])+1);
strcpy(str[1], item[auswahl-1]);
}
else {
printf("Kein solches Kriterium vorhanden!!!\n\n");
return;
}
str[2] = " = '";
printf("Bitte angaben fuer \'%s\' machen: ", item[auswahl-1]);
fgets(del, 254, stdin);
if( (ptr = (char *) strchr(del, '\n')) != NULL)
*ptr = '\0'; /* newline durch \0 ersetzen */
str[3] =(char *) malloc(strlen(del)+1);
strcpy(str[3], del);
str[4] = "'";
for (i=0; i < 5; i++)
size+=strlen(str[i]);
/* Speicherplatz für den Anfragestring reservieren */
query =(char *) malloc(size + 1);
strcpy(query, str[0]);
for(i = 1; i < 5; i++)
strcat(query, str[i]);
/* Als Test für die Konsole */
/* printf("%s",query); */
/* Jetzt die Anfrage an den Datenbankserver */
mysql_real_query(mysql, query, strlen(query));
check_error();
if((affected=(unsigned long)mysql_affected_rows(mysql))<= 0 ){
printf("Kein Datensatz von dieser Anfrage betroffen\n");
check_error();
}
else
printf("%d %s von dieser Anfrage betroffen\n\n",
affected, (affected == 1) ? "Datensatz war" :
"Datensaetze waren" );
free(query);
}
Auch hier dient der Löwenanteil der Funktion dazu, den Anfragestring für die Funktion mysql_real_query() dynamisch zu erstellen. Neu in der Funktion filmdaten_loeschen ist die API-Funktion mysql_affected_rows(), welche nach der Funktion mysql_real_query() aufgerufen wurde. Die Syntax dieser Funktion lautet:
my_ulonglong mysql_affected_rows(MYSQL *mysql);
Diese Funktion gibt die Anzahl der Zeilen zurück, welche von der letzten SQL-Anweisung wie DELETE, INSERT oder UPDATE betroffen war. Meistens wird diese Funktion unmittelbar nach einem Aufruf von mysql_real_query() verwendet. War kein Datensatz von der letzten Anfrage betroffen, liefert diese Funktion 0 zurück. Trat ein Fehler bei der Funktion auf, ist der Rückgabewert –1.
Hinweis Weil es auf manchen Systemen zu Problemen mit mysql_affected_rows() und dem Rückgabewert des primitiven Datentyps my_longlong kommen kann, empfiehlt es sich, ein Casting mit unsigned long durchzuführen, um Probleme zu vermeiden.
|
Als Nächstes die vollständige Funktion filmdaten_aendern():
void filmdaten_aendern(void) {
char change[255],replace[255], temp[4];
char *item[] = {"titel", "hauptrolle", "fsk", "gedreht" };
char *ptr;
char *str[8], *query;
int auswahl1, auswahl2, i, size=0;
unsigned int integer;
unsigned long affected;
printf("Welche Daten wollen Sie aendern (Suchkriterium)\n");
printf("[1]=Titel [2]=Hauptrolle "
"[3]=FSK [4]=Datum : [ ]\b\b");
fgets(temp, 3, stdin);
if( (ptr = (char *) strchr(temp, '\n')) != NULL)
*ptr = '\0'; /* newline durch \0 ersetzen */
sscanf(temp, "%1d", &auswahl1);
printf("Welchen Inhalt suchen Sie fuer %s:",item[auswahl1–1]);
fgets(change, 254, stdin);
if( (ptr = (char *) strchr(change, '\n')) != NULL)
*ptr = '\0'; /* newline durch \0 ersetzen */
printf("Welche Daten sollen ersetzt werden"
" (Ersetzkriterium)\n");
printf("[1]=Titel [2]=Hauptrolle "
"[3]=FSK [4]=Datum : [ ]\b\b");
fgets(temp, 3, stdin);
if( (ptr = (char *) strchr(temp, '\n')) != NULL)
*ptr = '\0'; /* newline durch \0 ersetzen */
sscanf(temp, "%1d", &auswahl2);
printf("Welchen Inhalt soll %s haben: ",item[auswahl2–1]);
fgets(replace, 254, stdin);
if( (ptr = (char *) strchr(replace, '\n')) != NULL)
*ptr = '\0'; /* newline durch \0 ersetzen */
/* Alle Daten vorhanden, um Querystr zu erzeugen */
str[0] = "UPDATE filmdaten SET ";
if(auswahl2 > 0 && auswahl2 < 5) {
str[1] =(char *) malloc(strlen(item[auswahl2–1])+1);
strcpy(str[1], item[auswahl2–1]);
}
else {
printf("Kein solches Kriterium vorhanden!!!\n\n");
return;
}
str[2] = "=";
/* Integerwerte ? */
if(auswahl2==3 || auswahl2==4) {
sscanf(replace, "%d", &integer);
str[3] =(char *) malloc(5);
sprintf(str[3], "%4d", integer);
}
else { /* ... dann ist es ein str */
str[3] =(char *) malloc(strlen(replace)+3);
strcpy(str[3], "'");
strcat(str[3], replace);
strcat(str[3], "'");
}
str[4] = " WHERE ";
if(auswahl1 > 0 && auswahl1 < 5) {
str[5] =(char *) malloc(strlen(item[auswahl1–1])+1);
strcpy(str[5], item[auswahl1–1]);
}
else {
printf("Kein solches Kriterium vorhanden!!!\n\n");
return;
}
str[6] = "=";
/* Integerwerte ? */
if(auswahl1==3 || auswahl1==4) {
sscanf(change, "%d", &integer);
str[7] =(char *) malloc(5);
sprintf(str[7], "%4d", integer);
}
else { /* ... dann ist es ein str */
str[7] =(char *) malloc(strlen(change)+3);
strcpy(str[7], "'");
strcat(str[7], change);
strcat(str[7], "'");
}
for (i=0; i < 8; i++)
size+=strlen(str[i]);
/* Speicherplatz für den Anfragestr reservieren */
query =(char *) malloc(size + 1);
strcpy(query, str[0]);
for(i = 1; i < 8; i++)
strcat(query, str[i]);
/* printf("%s",query); */
/* Jetzt die Anfrage an den Datenbankserver */
mysql_real_query(mysql, query, strlen(query));
check_error();
if((affected=(unsigned long)mysql_affected_rows(mysql))<=0) {
printf("Kein Datensatz von dieser Anfrage betroffen\n");
check_error();
}
else
printf("%d %s von dieser Anfrage betroffen\n\n",
affected, (affected == 1) ?"Datensatz war" :
"Datensaetze waren");
free(query);
}
Der Ablauf der Funktion filmdaten_aendern() ähnelt dem der Funktion fimdaten_loeschen() zuvor, nur dass hier ein anderer Anfragestring (UPDATE) erstellt wird.
Die Funktionen, mit denen Sie bisher eine Anfrage an den Server gestellt haben, haben noch keine Daten zurückgegeben. Aus dem MySQL-Crashkurs wissen Sie ja noch, dass Funktionen wie SELECT, EXPLAIN oder SHOW bewirken, dass etwas auf dem Bildschirm ausgegeben wird.
Der Vorgang, Daten vom Server zu empfangen, wird ebenfalls mit der Funktion mysql_real_query() ausgelöst. Danach folgt ein Aufruf der API-Funktion mysql_store_result(), welche alle angeforderten Daten vom Server in den Speicher des Client-Programms lädt. Hier die Syntax der Funktion:
MYSQL_RES *mysql_store_result(MYSQL *mysql);
Alle angeforderten Daten befinden sich jetzt in der Struktur MYSQL_RES. Tritt beim Einlesen der Daten ein Fehler auf, wird NULL zurückgegeben. NULL wird allerdings auch bei SQL-Anweisungen wie INSERT, also Anweisungen ohne Rückgabewert, zurückgegeben. Ob also tatsächlich ein Fehler auftrat, müssen Sie mit mysql_errno() oder mysql_error() überprüfen.
Nach dem Aufruf der Funktion mysql_store_results() befinden sich die Daten jetzt in der Struktur MYSQL_RES. Wollen Sie jetzt wissen, wie viele Zeilen sich in der Struktur MYSQL_RES befinden, können Sie folgende Funktion aufrufen:
my_ulonglong mysql_num_rows(MYSQL_RES *result);
Die Funktion ist ähnlich wie mysql_affected_rows(). Auch hier empfiehlt sich aus Portabilitätsgründen ein unsigned long-Casting des Rückgabewerts. Diese Funktion ist natürlich optional und muss nicht unbedingt nach der Funktion mysql_store_result() aufgerufen werden. Dennoch erweist diese sich häufig als recht nützlich.
Um die Daten aus der Struktur MYSQL_RES zu lesen, benötigen Sie eine Funktion, die das kann. Und das ist die Funktion mysql_fetch_row():
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
Diese Funktion liest Zeile für Zeile aus der Struktur MYSQL_RES ein, bis ein NULL-Zeiger zurückgegeben wird; also ähnlich wie die Standardfunktion fgets(), nur nicht bis zum nächsten Newline, sondern bis zum nächsten Datensatz. NULL wird auch zurückgegeben, wenn ein Fehler aufgetreten ist. MYSQL_ROW präsentiert eine Zeile von Daten, welche als Array-gezählte Byte-Zeichenkette implementiert ist.
Wollen Sie jetzt wissen, wie viele Spalten der aktuelle Datensatz in der Struktur MYSQL_RES hat, dann müssen Sie die Funktion mysql_num_fields() verwenden:
unsigned int mysql_num_fields(MYSQL_RES *results);
Sind Sie fertig mit den Daten der Struktur MYSQL_RES, dann sollten Sie den Speicher wieder freigeben, um Memory Leaks zu vermeiden. Die Syntax dazu lautet:
void mysql_free_result(MYSQL_RES *result);
Zugegeben, dass war jetzt ein ziemlicher Brocken, der Ihnen hier vor die Füße geschmissen wurde. Daher nochmals eine kurze Zusammenfassung, wie Sie die Anfrage vom Server abholen und bearbeiten können:
|
Anfrage an den Server (mysql_real_query) |
|
Daten der Anfrage abholen (mysql_store_result) |
|
Anzahl der Zeilen ermitteln, die abgeholt wurden (mysql_num_rows) |
|
Zeilenweises Einlesen der Daten (mysql_fetch_row) |
|
Anzahl der Spalten der aktuellen Zeile ermitteln (mysql_num_fields) |
|
Speicherplatz wieder freigeben (mysql_free_result) |
Hierzu folgt jetzt die Funktion schauspieler_suchen(), die alle diese Funktionen demonstriert.
void schauspieler_suchen(char *name) {
unsigned long anzahl_reihen;
unsigned int i;
MYSQL_ROW row;
MYSQL_RES *mysql_res;
char *query, *string;
char *select = "SELECT * FROM filmdaten WHERE hauptrolle='";
string =(char *) malloc(strlen(name)+2);
strcpy(string,name);
strcat(string, "'");
query =(char *) malloc(strlen(select)+strlen(string)+1);
strcpy(query, select);
strcat(query, string);
/* Jetzt die Anfrage an den Datenbankserver */
mysql_real_query(mysql, query, strlen(query));
check_error();
/* Daten der Anfrage abholen */
mysql_res = mysql_store_result(mysql);
check_error();
/* Anzahl der gefundenen Datensätze ermitteln */
anzahl_reihen = (unsigned long) mysql_num_rows (mysql_res);
printf ("Anzahl gefunden: %lu\n\n", anzahl_reihen);
/* Gefundenen Datensatz bzw. Datensätze ausgeben */
while ((row = mysql_fetch_row (mysql_res)) != NULL) {
/* Einzelne Spalten der Zeile ausgeben */
for (i = 0; i < mysql_num_fields(mysql_res); i ++)
printf ("%s ",row[i]);
printf("\n");
}
/* Speicherplatz wieder freigeben */
mysql_free_result(mysql_res);
free(string);
free(query);
}
In dieser Funktion wird in der Datenbank nach Filmen eines bestimmten Schauspielers gesucht und ausgegeben. Zuvor wird wieder der Anfragestring erstellt. Um jetzt die Feldwerte eines einzelnen Felds zu ermitteln, verwenden Sie die Funktion mysql_fetch_field(), welche die folgende Syntax besitzt:
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *results);
Die Informationen des Felds werden in der Struktur MYSQL_FIELD gespeichert. Hierzu ein kurzer Überblick, welche Daten Sie aus dieser Struktur entnehmen können.
Tabelle 27.10
Variablen der Struktur MYSQL_FIELD
Variable
|
Bedeutung
|
char *name;
|
Name des Felds als String, nicht zu verwechseln mit dem Inhalt des Felds. Im Programmbeispiel sind dies titel, hauptrolle, fsk und gedreht.
|
char *table;
|
Name der Tabelle, welche dieses Feld enthält als String. Im Programmbeispiel ist der Name filmdaten.
|
char *def;
|
Der Vorgabewert des Felds als String. Wird nur gesetzt, wenn die Funktion mysql_list_fields() verwendet wird.
|
enum enum_field_types;
|
Der Datentyp des Felds. Folgende Werte können dies sein:
FIELD_TYPE_DECIMAL, FIELD_TYPE_TINY,
FIELD_TYPE_SHORT, FIELD_TYPE_LONG,
FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE,
FIELD_TYPE_NULL, FIELD_TYPE_TIMESTAMP,
FIELD_TYPE_LONGLONG,FIELD_TYPE_INT24,
FIELD_TYPE_DATE, FIELD_TYPE_TIME,
FIELD_TYPE_DATETIME, FIELD_TYPE_YEAR,
FIELD_TYPE_NEWDATE,FIELD_TYPE_ENUM,
FIELD_TYPE_SET,FIELD_TYPE_TINY_BLOB,
FIELD_TYPE_MEDIUM_BLOB,
FIELD_TYPE_LONG_BLOB,FIELD_TYPE_BLOB,
FIELD_TYPE_VAR_STRING,FIELD_TYPE_STRING,
FIELD_TYPE_GEOMETRY
Die Bedeutung der einzelnen Flags spricht teilweise für sich. Für eine genauere Erläuterung sei die Dokumentation der C-API empfohlen. Um zu testen, ob das Feld ein numerisches ist oder nicht, können Sie das Makro IS_NUM() verwenden.
|
unsigned int length;
|
Die Breite des Felds, die Sie in der Tabellendefinition festgelegt haben. Im Programmbeispiel ist dies für titel 255, für hauptrolle 255 und für fsk, gedreht jeweils 4.
|
unsigned int max_length;
|
Maximale Breite des Felds. Diese Funktion ist ideal zur Ausgabe einer Tabelle auf dem Bildschirm.
|
unsigned int flags;
|
Einige Bit-Flags für das Feld. Folgende Flags können das sein:
NOT_NULL_FLAG, PRI_KEY_FLAG,UNIQUE_KEY_FLAG,
MULTIPLE_KEY_FLAG. BLOB_FLAG,
UNSIGNED_FLAG, ZEROFILL_FLAG
BINARY_FLAG, ENUM_FLAG,
AUTO_INCREMENT_FLAG, TIMESTAMP_FLAG
SET_FLAG, NUM_FLAG, PART_KEY_FLAG
GROUP_FLAG, UNIQUE_FLAG
Die Bedeutung der einzelnen Flags spricht teilweise für sich. Für genauere Informationen sei die C-API-Dokumentation empfohlen. Um die einzelnen Flags zu testen, können die IS_-Makros verwendet werden. Beispiel, ob der Inhalt eines Felds nicht NULL ist:
IS_NOT_NULL(flag);
|
unsigned int decimals;
|
Anzahl von Dezimalstellen für numerische Felder
|
Die folgenden zwei Funktionen alle_daten_ausgeben() und print_line() sollen jetzt noch die Funktion mysql_fetch_field() veranschaulichen:
void alle_daten_ausgeben(void) {
unsigned int i, col_len;
MYSQL_ROW row;
MYSQL_RES *res;
MYSQL_FIELD *field;
/* Jetzt die Anfrage an den Datenbankserver */
mysql_real_query(mysql, "SELECT * FROM filmdaten",
strlen("SELECT * FROM filmdaten"));
check_error();
/* Anfrage vom Server in die Struktur MYSQL_RES laden */
res = mysql_store_result(mysql);
check_error();
/* offset = 0 bedeutet auf den Anfang der Zeile setzen */
mysql_field_seek (res, 0);
/* Damit bei der Ausgabe ein einheitliches Bild entsteht,
* sollen die Daten für die max. Länge einer Spalte bei jeder
* einzelnen (MYSQL_FIELD)-Spalte verändert werden */
for (i = 0; i < mysql_num_fields(res); i++) {
field = mysql_fetch_field (res);
/* Länge des Namens in der Spalte ermitteln */
col_len = strlen (field->name);
/* Ist die Länge des Elements in der Spalte kleiner als
die max. Länge ... */
if (col_len < field->max_length)
/* ... dann bekommt col_len den Wert der max. erl. Länge
der Spalte */
col_len = field->max_length;
/* Für den Fall, dass eine Spalte keine Daten
beinhaltet ... */
if (col_len < 4 && !IS_NOT_NULL (field->flags))
/* col_len bekommt den Wert 4 für den String
"NULL" ->kein Daten */
col_len = 4;
/* max. Länge von Spalten-Info verändern */
field->max_length = col_len;
}
/* Namen der Tabelle ausgeben */
printf("Daten der Tabelle: [ %s ]\n", field->table);
print_line(res);
printf("|");
/* Alles wieder auf den Anfang stellen */
mysql_field_seek (res, 0);
/* Jetzt den Tabellenkopf ausgeben (titel, hauptrolle, fsk,
* gedreht) */
for (i = 0; i < mysql_num_fields (res); i++) {
field = mysql_fetch_field (res);
printf (" %-*s |", field->max_length, field->name);
}
printf("\n");
print_line(res);
/* Jetzt die Daten aus der Struktur MYSQL_RES zeilenweise
* einlesen */
while ((row = mysql_fetch_row (res)) != NULL) {
mysql_field_seek (res, 0);
printf("|");
for (i = 0; i < mysql_num_fields (res); i++) {
/* Spalte für Spalte abarbeiten */
field = mysql_fetch_field (res);
/* Keine Daten in dieser Spalte */
if (row[i] == NULL)
printf (" %-*s |", field->max_length, "NULL");
/* Handelt es sich um ein numerisches Feld */
else if (IS_NUM (field->type))
/* Dann wird der Inhalt rechtsbündig formatiert
* ausgegeben */
printf (" %*s |", field->max_length, row[i]);
else
/* Der Wert der Spalte ist ein String, also
* linksbündige Ausgabe */
printf (" %-*s |", field->max_length, row[i]);
}
printf("\n");
}
print_line(res);
mysql_free_result(res);
}
void print_line(MYSQL_RES *res) {
MYSQL_FIELD *field;
unsigned int i, j;
mysql_field_seek (res, 0);
/* Erstes Zeichen der Linie */
printf("+");
for (i = 0; i < mysql_num_fields(res); i++) {
field = mysql_fetch_field(res);
/* max_length '-' Zeichen jeder Spalte ausgeben */
for (j = 0; j < field->max_length + 2; j++)
printf("-");
/* Am Ende der Spalte '+' ausgeben */
printf("+");
}
printf("\n");
}
Mit diesen beiden Funktionen werden alle Daten der Tabelle filmdaten sauber in Tabellenform auf dem Bildschirm ausgegeben. Zusätzlich wurde hier die Funktion mysql_field_seek() verwendet, womit der Feldcursor auf das angegebene Offset gesetzt wird. Da hier als Offset 0 angegeben wurde, wird immer bis zum Anfang einer Zeile gesucht. Die Funktion mysql_fetch_field(), welche danach aufgerufen wird, ruft die Felddefinition der Spalte ab, die mit dem Offset MYSQL_FIELD_OFFSET verknüpft ist. Wollen Sie beispielsweise an eine Position des letzten mysql_fetch_field() zurückspringen, können Sie sich eine Position mit der Funktion mysql_field_tell() merken und den Rückgabewert dieser Funktion als Offset an die Funktion mysql_field_seek() übergeben.
|