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

Inhaltsverzeichnis
Vorwort
Vorwort des Gutachters
1 Einstieg in C
2 Das erste Programm
3 Grundlagen
4 Formatierte Ein-/Ausgabe mit »scanf()« und »printf()«
5 Basisdatentypen
6 Operatoren
7 Typumwandlung
8 Kontrollstrukturen
9 Funktionen
10 Präprozessor-Direktiven
11 Arrays
12 Zeiger (Pointer)
13 Kommandozeilenargumente
14 Dynamische Speicherverwaltung
15 Strukturen
16 Ein-/Ausgabe-Funktionen
17 Attribute von Dateien und das Arbeiten mit Verzeichnissen (nicht ANSI C)
18 Arbeiten mit variabel langen Argumentlisten – <stdarg.h>
19 Zeitroutinen
20 Weitere Headerdateien und ihre Funktionen (ANSI C)
21 Dynamische Datenstrukturen
22 Algorithmen
23 CGI mit C
24 MySQL und C
25 Netzwerkprogrammierung und Cross–Plattform-Entwicklung
26 Paralleles Rechnen
27 Sicheres Programmieren
28 Wie geht’s jetzt weiter?
A Operatoren
B Die C-Standard-Bibliothek
Stichwort

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch
Buch: C von A bis Z

C von A bis Z
3., aktualisierte und erweiterte Auflage, geb., mit CD und Referenzkarte
1.190 S., 39,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1411-7
Pfeil 24 MySQL und C
Pfeil 24.1 Aufbau eines Datenbanksystems
Pfeil 24.1.1 Warum wurde ein Datenbanksystem (DBS) entwickelt?
Pfeil 24.1.2 Das Datenbank-Management-System (DBMS)
Pfeil 24.1.3 Relationale Datenbank
Pfeil 24.1.4 Eigene Clients mit C für SQL mithilfe der ODBC-API entwickeln
Pfeil 24.2 MySQL installieren
Pfeil 24.2.1 Linux
Pfeil 24.2.2 Den Client »mysql« starten
Pfeil 24.3 Crashkurs (My)SQL
Pfeil 24.3.1 Was ist SQL?
Pfeil 24.3.2 Die Datentypen von (My)SQL
Pfeil 24.3.3 Eine Datenbank erzeugen
Pfeil 24.3.4 Eine Datenbank löschen
Pfeil 24.3.5 Datenbank wechseln
Pfeil 24.3.6 Eine Tabelle erstellen
Pfeil 24.3.7 Die Tabelle anzeigen
Pfeil 24.3.8 Tabellendefinition überprüfen
Pfeil 24.3.9 Tabelle löschen
Pfeil 24.3.10 Struktur einer Tabelle ändern
Pfeil 24.3.11 Datensätze eingeben
Pfeil 24.3.12 Datensätze auswählen
Pfeil 24.3.13 Ein fortgeschrittenes Szenario
Pfeil 24.3.14 Datensatz löschen
Pfeil 24.3.15 Datensatz ändern
Pfeil 24.3.16 Zugriffsrechte in MySQL
Pfeil 24.3.17 Übersicht über einige SQL-Kommandos
Pfeil 24.4 Die MySQL-C-API
Pfeil 24.4.1 Grundlagen zur Programmierung eines MySQL-Clients
Pfeil 24.4.2 Client-Programm mit dem gcc unter Linux und dem Cygwin–gcc-Compiler unter Windows
Pfeil 24.4.3 MySQL Client-Programme mit dem VC++ Compiler und dem Borland Freeware Compiler
Pfeil 24.4.4 Troubleshooting
Pfeil 24.4.5 Das erste Client-Programm – Verbindung mit dem MySQL-Server herstellen
Pfeil 24.4.6 MySQL-Kommandozeilen-Optionen
Pfeil 24.4.7 Anfrage an den Server
Pfeil 24.5 MySQL und C mit CGI
Pfeil 24.5.1 HTML-Eingabeformular
Pfeil 24.5.2 Die CGI-Anwendung »add_db.cgi«
Pfeil 24.5.3 Die CGI-Anwendung »search_db.cgi«
Pfeil 24.6 Funktionsübersicht
Pfeil 24.7 Datentypenübersicht der C-API


Rheinwerk Computing - Zum Seitenanfang

24.4 Die MySQL-C-API Zur nächsten ÜberschriftZur vorigen Überschrift

Die MySQL-C-API ist eine Bibliothek, die 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, die 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.


Rheinwerk Computing - Zum Seitenanfang

24.4.1 Grundlagen zur Programmierung eines MySQL-Clients Zur nächsten ÜberschriftZur vorigen Überschrift

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 fallen 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:\mysql\lib\opt (»c:« sei das Laufwerk) 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 unter /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.


Rheinwerk Computing - Zum Seitenanfang

24.4.2 Client-Programm mit dem gcc unter Linux und dem Cygwin–gcc-Compiler unter Windows Zur nächsten ÜberschriftZur vorigen Überschrift

Zuerst kompilieren Sie das geschriebene Client-Programm, in dem Sie mit dem Compiler-Flag -I (Include) angeben, an welcher Stelle sich die Headerdateien von MySQL befinden. Bei Linux mit gcc sieht das so aus:

gcc -c -I/usr/include/mysql myclient.c

Bei Windows unter der Cygwin-Umgebung mit gcc sieht das so aus:

gcc -c -I"c:\mysql\include" myclient.c

Jetzt verfügen Sie über eine Objektdatei in dem Verzeichnis, in dem Sie den Quellcode kompiliert haben. Als Nächstes müssen Sie diese Objektdatei zu einer ausführbaren Datei linken. Damit dies auch funktioniert, müssen Sie dem Linker mitteilen, wo sich die Client-Bibliothek von MySQL befindet. Dies erledigen Sie mit dem Compiler-Flag -L, mit dem der Pfad zur Bibliothek angegeben wird, und dem Flag -l, mit dem Sie die Bibliothek angeben, die 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, die 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.


Rheinwerk Computing - Zum Seitenanfang

24.4.3 MySQL Client-Programme mit dem VC++ Compiler und dem Borland Freeware Compiler Zur nächsten ÜberschriftZur vorigen Überschrift

Bei beiden Compilern gehe ich davon aus, dass diese bereits auf Ihrem System installiert sind. Installationsanleitungen diverser Compiler finden Sie unter https://www.rheinwerk-verlag.de/2132/.

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 Headerdateien "mysql.h" und "my_global.h" mit ein:

#include "my_global.h"
#include "mysql.h"

Bei den Projekteigenschaften (die Sie bei Visual C++ 2008 Express Edition beispielsweise über Taste ALT + Taste F7 erreichen) müssen Sie jetzt noch die Pfade zu den Headerdateien und der Bibliothek angeben, damit der Compiler die Headerdatei und der Linker die Bibliothek findet.

Den Pfad zum include-Verzeichnis der MySQL-C-API können Sie über Projektprojektname-Eigenschaften und im sich dann öffnenden Fenster über KonfigurationseigenschaftenC/C++Allgemein bei Zusätzliche Includeverzeichnisse auswählen (bei mir lautet das Verzeichnis beispielsweise "C:\Program Files\MySQL\MySQL Server 5.1\include").

Auch den Pfad zur Bibliothek libmysql.lib können Sie über Projektprojektname-Eigenschaften bei KonfigurationseigenschaftenLinkerBefehlszeile im Textfeld Zusätzliche Optionen eingeben (bei mir lautet das Verzeichnis beispielsweise "C:\Program Files\MySQL\MySQL Server 5.1\lib\opt\libmysql.lib").

Abbildung 24.7 Projekteigenschaften wie den Pfad zur Bibliothek und Headerdateien von MySQL einstellen

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 (beispielsweise 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, 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, die 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 sehen Sie 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.


Rheinwerk Computing - Zum Seitenanfang

24.4.4 Troubleshooting Zur nächsten ÜberschriftZur vorigen Überschrift

Die MySQL-C-API verwendet auch Funktionen aus der Headerdatei <math.h> (floor()). Bei manchen Linux-Distributionen müssen Sie <math.h> mit dem Compiler-Flag -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 die Bibliothek für Ihren Compiler selbst. Wie Sie dabei vorgehen, können Sie dem MySQL-Manual entnehmen.


Rheinwerk Computing - Zum Seitenanfang

24.4.5 Das erste Client-Programm – Verbindung mit dem MySQL-Server herstellen Zur nächsten ÜberschriftZur vorigen Überschrift

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 ein Handle für eine Datenbankverbindung, das 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 dieses Handle jetzt Speicherplatz zu reservieren und zu initialisieren, wird die Funktion mysql_init() verwendet. Die Syntax zu dieser Funktion lautet:

MYSQL *mysql_init(MYSQL *mysql);

Wird diese Funktion mit einem NULL-Zeiger aufgerufen, wird zuerst Speicherplatz für ein MYSQL-Objekt alloziert 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 ein initialisiertes 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 Tabelle 24.8 finden Sie die Bedeutungen der einzelnen Parameter der Funktion mysql_real_connect().


Tabelle 24.8 Bedeutung der Parameter der Funktion »mysql_real_connect()«

Parameter Bedeutung
MYSQL *mysql

die Adresse einer existierenden MYSQL-Struktur, die 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, der 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, die ein leeres Passwort-Feld besitzen.

const char *db

Der 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, die 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 das MYSQL-Handle des ersten Parameters zurück. Bei einem 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, die die einzelnen Fehler abfragt. Sie können die Funktionen mysql_errno() und mysql_error() verwenden. Hier sehen Sie 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>, die sich im selben Verzeichnis wie <mysql.h> befindet. Fehler des Serverprogramms befinden sich in der Headerdatei <mysqld_error.h>.


Ein Fehlerstatuscode allein 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 das 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. Dazu folgt 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 sehen Sie jetzt alle Funktionen als ausführbares Programm:

/* mysql1.c */
#include <stdio.h>
#include <stdlib.h>
#if defined __WIN32__ || _MSC_VER
   #include "my_global.h"
   #include "mysql.h"
#else
   #include <mysql.h>
#endif

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

Rheinwerk Computing - Zum Seitenanfang

24.4.6 MySQL-Kommandozeilen-Optionen Zur nächsten ÜberschriftZur vorigen Überschrift

Im Listing zuvor wurde die Verbindung zum MySQL-Server über die Funktion mysql_real_connect() mit Default-Werten bzw. NULL-Zeigern aufgebaut. Dass 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 Tabelle 24.9.


Tabelle 24.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, das 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 ist das die Datei my.cnf und unter Windows häufig auch 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, die in den eben genannten Konfigurations-Files zwischen eckigen Klammen stehen, beispielsweise 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. Hier sehen Sie das Listing zu load_default():

/* mysql2.c */
#include <stdio.h>
#include <stdlib.h>
#if defined __WIN32__ || _MSC_VER
   #include "my_global.h"
   #include "mysql.h"
#else
   #include <mysql.h>
#endif

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 24.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)

Rheinwerk Computing - Zum Seitenanfang

24.4.7 Anfrage an den Server topZur vorigen Überschrift

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, die Sie im MySQL-Crashkurs erstellt haben. Falls Sie diese bereits gelöscht haben, erstellen Sie diese bitte nochmals. Hier sind 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 Rückgabewert 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:

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

Hier sehen Sie 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. Sie darf allerdings im Gegensatz zu der Anfrage, die Sie beim MySQL-Crashkurs kennengelernt haben, kein Semikolon oder \g am Ende haben. 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 ist er 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 sollten Sie immer die Funktion mysql_real_query() bevorzugen.


Wenn Sie mit der Funktion mysql_real_query() eine Anfrage an den Server stellen und Sie keine Antwort bekommen, könnte es an einem der folgenden Fehler liegen:

  • Der MySQL-Server ist gar nicht in Betrieb. Das klingt banal, kommt aber 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, mit dem Sie auf die im Crashkurs erstellte Datenbank dvd_archiv zugreifen.

Sofern Sie die Datenbank dvd_archiv bereits wieder gelöscht haben oder falls 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, die 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 "my_global.h"
   #include "mysql.h"
#else
   #include <mysql.h>
#endif

/* Wegen sehr vielen malloc()-Aufrufen im Listing
 * wurde hier zur Verkürzung des Listings ein Makro
 * geschrieben, das 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 sehen Sie 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 an Ihre
 * Bedürfnisse 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(), die 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 mit der SQL-Anweisung INSERT neue Daten in die Datenbank 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 = strchr(titel, '\n')) != NULL)
      *ptr = '\0';  /* newline durch \0 ersetzen */

   printf("Hauptrolle : ");
   fgets(hauptrolle, 254, stdin);
   if( (ptr = strchr(hauptrolle, '\n')) != NULL)
      *ptr = '\0';  /* newline durch \0 ersetzen */

   printf("FSK        : ");
   fgets(temp, 4, stdin);
   if( (ptr = strchr(temp, '\n')) != NULL)
      *ptr = '\0';  /* newline durch \0 ersetzen */
   sscanf(temp, "%u", &fsk);

   printf("Gedreht    : ");
   fgets(temp, 5, stdin);
   if( (ptr = strchr(temp, '\n')) != NULL)
      *ptr = '\0';  /* newline durch \0 ersetzen */
   sscanf(temp, "%u", &gedreht);

   /* jetzt wird der Anfragestring erstellt */
   str[0]= "INSERT INTO filmdaten (titel, hauptrolle, fsk, "
            "gedreht) VALUES ('";
   str[1] = malloc(strlen(titel)+1);
   strcpy(str[1], titel);
   str[2] = "','";
   str[3] = malloc(strlen(hauptrolle)+1);
   strcpy(str[3], hauptrolle);
   str[4] = "',";
   str[5] = malloc(3);
   sprintf(str[5], "%2u", fsk);
   str[6] = ",";
   str[7] = malloc(5);
   sprintf(str[7], "%4u", gedreht);
   str[8] = ")";

   for (i=0; i < 9; i++)
      size+=strlen(str[i]);
   /* Speicherplatz für den Anfragestring reservieren */
   query = 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. Ich gehe hierbei davon aus, 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 Taste Enter quittieren, damit dieses Feld leer bleibt.


Hinweis

Wollen Sie vermeiden, dass NULL-Werte vorkommen oder doppelte Einträge in der MySQL-Datenbank vorgenommen werden, so können Sie jeden Datensatz in einer Tabelle mit einem Schlüssel, dem sogenannten 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 = 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] = 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 = strchr(del, '\n')) != NULL)
      *ptr = '\0';  /* newline durch \0 ersetzen */
   str[3] = 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 = 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("%ld %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(), die 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, die 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 folgt 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 = 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 = strchr(change, '\n')) != NULL)
      *ptr = '\0';  /* newline durch \0 ersetzen */

   printf("Welche Daten sollen ersetzt werden"
          " (Ersetzungskriterium)\n");
   printf("[1]=Titel  [2]=Hauptrolle  "
          "[3]=FSK  [4]=Datum  : [ ]\b\b");

   fgets(temp, 3, stdin);
   if( (ptr = 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 = 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] = 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, "%u", &integer);
      str[3] = malloc(5);
      sprintf(str[3], "%4d", integer);
   }
   else { /* ... dann ist es ein str */
      str[3] = malloc(strlen(replace)+3);
      strcpy(str[3], "'");
      strcat(str[3], replace);
      strcat(str[3], "'");
   }
   str[4] = " WHERE ";
   if(auswahl1 > 0 && auswahl1 < 5) {
      str[5] = 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, "%u", &integer);
      str[7] = malloc(5);
      sprintf(str[7], "%4d", integer);
   }
   else { /* ... dann ist es ein str */
      str[7] = 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 = 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("%ld %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(), die alle angeforderten Daten vom Server in den Speicher des Client-Programms lädt. Hier sehen Sie 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, die 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);

Wenn Sie mit den Daten der Struktur MYSQL_RES fertig sind, dann sollten Sie den Speicher wieder freigeben, um Memory Leaks zu vermeiden. Die Syntax dazu lautet:

void mysql_free_result(MYSQL_RES *result);

Zugegeben, das war jetzt ein ziemlicher Brocken, der Ihnen hier vor die Füße geschmissen wurde. Daher folgt 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 = malloc(strlen(name)+2);
   strcpy(string,name);
   strcat(string, "'");
   query = 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 diese werden dann ausgegeben. Zuvor wird wieder der Anfragestring erstellt. Um jetzt die Feldwerte eines einzelnen Feldes zu ermitteln, verwenden Sie die Funktion mysql_fetch_field(), die die folgende Syntax besitzt:

MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *results);

Die Informationen des Feldes werden in der Struktur MYSQL_FIELD gespeichert. Tabelle 24.10 bietet einen kurzen Überblick darüber, welche Daten Sie aus dieser Struktur entnehmen können.


Tabelle 24.10 Variablen der Struktur »MYSQL_FIELD«

Variable Bedeutung
char *name;

Der Name des Felds als String, nicht zu verwechseln mit dem Inhalt des Felds. Im Programmbeispiel sind dies titel, hauptrolle, fsk und gedreht.

char *table;

Der Name der Tabelle, die 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 kommen dafür infrage:

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 kommen dafür infrage:

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. Hier wird zum Beispiel geprüft, 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 maximale 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 maximale Länge ... */
      if (col_len < field->max_length)
      /* ... dann bekommt col_len den Wert der maximal
             erlaubten 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))
      /* ... bekommt col_len den Wert 4 für den String
         "NULL" ->keine Daten. */
         col_len = 4;
      /* maximale 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(), die 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.



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
  
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: C von A bis Z

 C von A bis Z
Jetzt bestellen


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

 Buchtipps
Zum Rheinwerk-Shop: C/C++






 C/C++


Zum Rheinwerk-Shop: Einstieg in C






 Einstieg in C


Zum Rheinwerk-Shop: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: C++ Handbuch






 C++ Handbuch


Zum Rheinwerk-Shop: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


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




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


Nutzungsbestimmungen | Datenschutz | Impressum

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

Cookie-Einstellungen ändern