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

 << zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch für Linux, Unix und Windows
– 2., aktualisierte und erweiterte Auflage 2006
Buch: C von A bis Z

C von A bis Z
1.116 S., mit CD, Referenzkarte, 39,90 Euro
Galileo Computing
ISBN 3-89842-643-2
gp Kapitel 27 MySQL und C
  gp 27.1 Aufbau eines Datenbanksystems
    gp 27.1.1 Warum wurde ein Datenbanksystem (DBS) entwickelt?
    gp 27.1.2 Das Datenbank-Management-System (DBMS)
    gp 27.1.3 Relationale Datenbank
    gp 27.1.4 Eigene Clients mit C für SQL mit der ODBC-API entwickeln
  gp 27.2 MySQL installieren
    gp 27.2.1 Linux
    gp 27.2.2 Windows
    gp 27.2.3 Den Client mysql starten
  gp 27.3 Crashkurs (My)SQL
    gp 27.3.1 Was ist SQL?
    gp 27.3.2 Die Datentypen von (My)SQL
    gp 27.3.3 Eine Datenbank erzeugen
    gp 27.3.4 Eine Datenbank löschen
    gp 27.3.5 Datenbank wechseln
    gp 27.3.6 Eine Tabelle erstellen
    gp 27.3.7 Die Tabelle anzeigen
    gp 27.3.8 Tabellendefinition überprüfen
    gp 27.3.9 Tabelle löschen
    gp 27.3.10 Struktur einer Tabelle ändern
    gp 27.3.11 Datensätze eingeben
    gp 27.3.12 Datensätze auswählen
    gp 27.3.13 Ein fortgeschrittenes Szenario
    gp 27.3.14 Datensatz löschen
    gp 27.3.15 Datensatz ändern
    gp 27.3.16 Zugriffsrechte in MySQL
    gp 27.3.17 Übersicht über einige SQL-Kommandos
  gp 27.4 Die MySQL C-API
    gp 27.4.1 Grundlagen zur Programmierung eines MySQL-Clients
    gp 27.4.2 Client-Programm mit dem gcc- unter Linux und dem Cygwin gcc-Compiler unter Windows
    gp 27.4.3 MySQL Client-Programme mit dem VC++ Compiler und dem Borland Freeware Compiler
    gp 27.4.4 Troubleshooting
    gp 27.4.5 Das erste Client-Programm – Verbindung mit dem MySQL-Server herstellen
    gp 27.4.6 MySQL-Kommandozeilen-Optionen
    gp 27.4.7 Anfrage an den Server
  gp 27.5 MySQL und C mit CGI
    gp 27.5.1 HTML-Eingabeformular
    gp 27.5.2 CGI-Anwendung add_db.cgi
    gp 27.5.3 CGI-Anwendung search_db.cgi
  gp 27.6 Funktionsübersicht
  gp 27.7 Datentypenübersicht der C-API


Galileo Computing - Zum Seitenanfang

27.4 Die MySQL C-API  downtop

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.


Galileo Computing - Zum Seitenanfang

27.4.1 Grundlagen zur Programmierung eines MySQL-Clients  downtop

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:

gp  Die MySQL Bibliothek (lib)
gp  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>

Galileo Computing - Zum Seitenanfang

27.4.2 Client-Programm mit dem gcc- unter Linux und dem Cygwin gcc-Compiler unter Windows  downtop

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.


Galileo Computing - Zum Seitenanfang

27.4.3 MySQL Client-Programme mit dem VC++ Compiler und dem Borland Freeware Compiler  downtop

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

Abbildung
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.


Galileo Computing - Zum Seitenanfang

27.4.4 Troubleshooting  downtop

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.


Galileo Computing - Zum Seitenanfang

27.4.5 Das erste Client-Programm – Verbindung mit dem MySQL-Server herstellen  downtop

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;
}

Galileo Computing - Zum Seitenanfang

27.4.6 MySQL-Kommandozeilen-Optionen  downtop

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;
}

Abbildung
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)

Galileo Computing - Zum Seitenanfang

27.4.7 Anfrage an den Server  toptop

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:

gp  Das Client-Programm sendet eine Anfrage an den Server, wobei der Server nicht antwortet.
gp  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:

gp  Sie erstellen eine SQL-Anfrage an den Server.
gp  Der Server erhält die Anfrage und überprüft diese auf syntaktische Fehler.
gp  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:

gp  Der MySQL-Server ist gar nicht in Betrieb. Klingt banal, aber kommt häufig vor.
gp  Sie haben keine entsprechenden Zugriffsrechte, was unter Linux öfter der Fall ist.
gp  Die Syntax der Anfrage ist falsch.
gp  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:

gp  Anfrage an den Server (mysql_real_query)
gp  Daten der Anfrage abholen (mysql_store_result)
gp  Anzahl der Zeilen ermitteln, die abgeholt wurden (mysql_num_rows)
gp  Zeilenweises Einlesen der Daten (mysql_fetch_row)
gp  Anzahl der Spalten der aktuellen Zeile ermitteln (mysql_num_fields)
gp  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.

 << zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z
C von A bis Z
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Shell-Programmierung






 Shell-Programmierung


Zum Katalog: Linux-UNIX-Programmierung






 Linux-UNIX-Programmierung


Zum Katalog: C/C++






 C/C++


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Reguläre Ausdrücke






 Reguläre Ausdrücke


Zum Katalog: Linux






 Linux


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 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.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de