12.5 MySQL C-API
Mit der MySQL C-API (API = Application Interface) sind Sie in der Lage, eigene Clientanwendungen wie mysql (Shell) oder mysqladmin zu entwickeln, womit Sie Zugriff auf den MySQL-Server haben. Um eigene Clientanwendungen zu schreiben, benötigen Sie die Bibliothek zu MySQL (lib) und die entsprechenden Headerdateien (include). Die Bibliothek sollten Sie gewöhnlich im Verzeichnis /usr/lib, /usr/lib/mysql oder /usr/local/lib/mysql vorfinden. Die Headerdatei(en) befindet sich im Verzeichnis, wo sich auch die anderen Include-Dateien aufhalten sollten (/usr/include/mysql oder /usr/local/include/mysql). Sollte sich bei Ihnen in den eben genannten Verzeichnissen nichts dergleichen befinden, haben Sie wahrscheinlich eine RPM-Installation gemacht. In diesem Fall müssen Sie noch extra ein RPM namens Developer RPM nachinstallieren. Gewöhnlich sollte diese RPM Ihrer Distribution beiliegen. Ansonsten müssen Sie bei http://www.mysql.de vorbeischauen und entsprechende RPMs herunterladen und installieren.
Wenn Sie Ihren MySQL Client-Quellcode erstellt haben, müssen Sie dem Compiler sagen, wo sich die Include-Dateien befinden, und der Linker will ebenfalls wissen, welche Bibliotheken er von wo zum Linken verwenden soll. Die Information für den Compiler geben Sie mit dem Flag -I an. Ist der Programmname z. B. mein_Client.c, dann sieht der Compileraufruf wie folgt aus:
$ gcc -c -I/usr/include/mysql mein_Client.c
Nach erfolgreicher Kompilierung befindet sich jetzt im Verzeichnis eine Objektdatei (*.o). Um daraus jetzt eine ausführbare Datei zu machen, müssen Sie dem Linker sagen, wo er die Bibliothek für seine Arbeit findet und welche Bibliothek er dazu verwenden soll. Das Wo erledigen Sie mit dem Flag -L und der Pfadangabe, und welche Bibliothek dazu verwendet werden soll, geben Sie hinter dem Flag -l an. Die komplette Eingabe lautet hierzu also:
$ gcc -o mein_Client mein_Client.o -L/usr/lib/mysql -lmysqlclient
Wenn beim Compilerlauf und beim anschließenden Linken keine Probleme aufgetreten sind, können Sie die Clientanwendung mit dem Namen starten:
$ ./my_Client
Damit Sie aber nicht dauernd so viel tippen müssen, können Sie auch die Datei .bash_profile anpassen, oder besser noch, Sie verwenden ein Makefile.
Bei manchen Systemen kann es zu Problemen mit dem Linkerlauf kommen. Meistens will der Linker dabei nur mehr Input zu weiteren Bibliotheken. Geben Sie dem Linker, was er will, und Sie können in Ruhe weiterarbeiten. Hier einige Libraries, die Sie eventuell hinzulinken müssen:
Tabelle 12.5
Eventuell weitere benötigte Linkerflags
Flag
|
Bedeutung
|
-lm
|
Die C-API verwendet einige Funktionen aus der Headerdatei <math.h>, und einige Linux-Systeme wollen dies immer noch extra angegeben haben.
|
(-lm) –lsocket
-lnsl
|
Diese Flags wollen die Solaris-Rechner haben.
|
-lz (zlib)
|
Diese Bibliothek benötigen Sie bei einem Undefined-Reference-Fehler bei den Funktionen compress und uncompress. Dieser Fehler hat den Autor schon an den Rand eines Nervenzusammenbruchs gebracht.
|
Hinweis[1] Auf Linux braucht man keine der o. g. Extra-Bibliotheken anzugeben, da weitere von MySQL benötigte Bibliotheken bereits in der libmysqlclient.so angegeben sind und daher automatisch eingebunden werden.
Hinweis[2] Sollten Sie dennoch Probleme mit den Pfaden zum Include-Verzeichnis oder den Bibliotheken haben, können Sie hierzu auch das Shellscript mysql_config mit dem Flag --cflags (für die Include-Angaben) und --libs (für die Bibliotheksangaben) verwenden.
Hinweis am Rande Das Verwenden einer neuen API (unabhängig davon, welche das jetzt ist) lässt den Code anfangs immer etwas komplexer und eben fremd aussehen. Sie sollten sich davon aber nicht abschrecken lassen. Gewöhnlich steckt in jeder API ein gut durchdachtes Design, das schnell zu durchschauen ist. Gewöhnlich funktionieren APIs immer nach demselben Prinzip. Man initialisiert etwas, indem man z. B. Speicher reserviert. Dann stellt man eine Verbindung/Beziehung her, und anschließend kann mit dieser Verbindung/Beziehung gearbeitet werden. Am Ende gibt man alles wieder frei. Wenn Sie ein wenig zurückdenken, wird Ihnen auffallen, dass das Prinzip immer dasselbe ist.
|
12.5.1 Verbindung mit dem MySQL-Server aufbauen
Im ersten Beispiel werden Sie sehen, wie Sie eine Verbindung zum MySQL-Server aufbauen, die Verbindung auf Fehler überprüfen und die Verbindung wieder ordentlich schließen können.
MySQL-Objekt alloziieren oder initialisieren – mysql_init()
Bevor Sie eine Verbindung zum Server aufbauen, müssen Sie erst für ein MySQL-Objekt Speicher alloziieren oder es initialisieren. Diese Aufgabe erledigt die Funktion mysql_init():
#include <mysql.h>
MYSQL *mysql_init(MYSQL *mysql)
Geben Sie dieser Funktion als Parameter NULL, wird zuerst Speicherplatz für das MySQL-Objekt reserviert und dieses anschließend gleich initialisiert. Der Rückgabewert ist dabei die Adresse eines neuen MySQL-Objekts. Bei diesem MySQL-Objekt handelt es sich intern natürlich nur um eine Struktur, die sich hinter einem typedef versteckt. Dieses MySQL-Objekt, Sie können es auch gerne Handle oder MySQL-Stream nennen, wird für fast alle Funktionen der MySQL C-API benötigt. Geben Sie hingegen für den Parameter ein MySQL-Objekt an, wird das Objekt initialisiert und die Adresse des Objekts zurückgegeben. mysql_init() ist somit das malloc() für MySQL, wenn Sie so wollen. Reicht der Speicher für ein neues Objekt nicht aus, wird NULL zurückgegeben.
Verbindung zum MySQL-Server aufbauen – mysql_real_connect()
Nach erfolgreicher Ausführung der Funktion mysql_init() wird versucht, mit der Funktion mysql_real_connect() eine Verbindung zum MySQL-Server herzustellen. Natürlich ist auch klar, dass ohne das Zustandekommen einer Verbindung keine weiteren API-Funktionen verwendet werden können (abgesehen von mysql_get_client_info()). Die Syntax zu dieser Funktion:
#include <mysql.h>
MYSQL *mysql_real_connect( MYSQL *mysql, const char *host,
const char *user, const char *passwd,
const char *db, unsigned int port,
const char *unix_socket,
unsigned int client_flag );
Auf den ersten Blick wirkt diese Funktion mit den vielen Parametern recht erschlagend. Aber bei genauerem Hinsehen kann man ein wenig die Parallelen beim Einloggen in der MySQL-Shell (mysql) erkennen. Der erste Parameter ist die Adresse der MySQL-Struktur, die Sie zuvor mit der Funktion mysql_init() alloziiert und initialisiert haben.
Mit dem zweiten Parameter können Sie den Hostnamen oder eine IP-Adresse zum MySQL-Server angeben. Der Vorteil ist, dass Sie sich selbst hier nicht die Mühe machen müssen, Sockets oder Named Pipes zu erstellen, um sich mit dem Server zu verbinden (zumindest ist dies unter Linux und BSD so). Bei einer Angabe von NULL oder localhost wird versucht, sich mit einem UNIX-Socket zu verbinden.
Mit dem dritten Parameter user geben Sie den User-Namen an, den Sie zur MySQL-Login-Benutzererkennung verwenden. Bei der Angabe von NULL wird versucht, den aktuellen Benutzer einzuloggen – was unter Linux der aktuelle Login-Name wäre.
Mit passwd geben Sie das entsprechende Passwort für user an. Bei einer Angabe von NULL gilt hier, dass in der user-Tabelle auf Übereinstimmung mit den Benutzern überprüft wird, die ein leeres Passwortfeld haben (was aus Sicherheitsgründen allerdings nicht zu empfehlen ist).
Mit db geben Sie den Namen der Datenbank an, mit der Sie gleich arbeiten wollen. Bei einer Angabe von NULL wird eine den Vorgaben entsprechende Datenbank für die Verwendung gesetzt. Natürlich können Sie auch erst nach der Verbindung eine bestimmte Datenbank zum Arbeiten auswählen.
Wenn Sie für port nicht 0 angeben, hängt dieser Parameter von der Portnummer der TCP/IP-Verbindung des host-Parameters ab.
Mit dem Parameter unix_socket können Sie einen String angeben, der ein Socket oder eine Namend Pipe festlegt, womit eine Verbindung hergestellt werden soll. Hierbei ist der Verbindungstyp allerdings ebenfalls von dem Parameter host abhängig. Alternativ kann auch hier NULL angegeben werden.
Für den letzten Parameter wird üblicherweise 0 angegeben. Allerdings lassen sich auch spezielle Optionen einzeln oder auch miteinander kombiniert setzen. Folgende Flags und deren Bedeutung können Sie dabei setzen:
|
CLIENT_COMPRESS – Komprimiertes Protokoll benutzen |
|
CLIENT_FOUND_ROWS – Die Anzahl übereinstimmender Zeilen zurückgeben |
|
CLIENT_IGNORE_SPACE – Leerzeichen nach Funktionsnamen zulassen. Macht alle Funktionsnamen zu reservierten Wörtern. |
|
CLIENT_INTERACTIVE – Bei interactive_timeout Sekunden der Inaktivität wird die Verbindung beendet – standardmäßig ist das wait_timeout Sekunden. |
|
CLIENT_NO_SCHEMA – Das Flag ist bei der Fehlersuche einiger Anwendungen hilfreich, die ODBC verwenden. Damit wird die Syntax datenbank.tabelle.spalte verboten. |
|
CLIENT_ODBC – Der Client ist ein ODBC-Client. |
|
CLIENT_SSL – SSL benutzen (verschlüsseltes Protokoll) |
Wenn mysql_real_connect() ordnungsgemäß eine Verbindung aufbauen konnte, wird derselbe Zeiger auf die Adresse der MySQL-Struktur zurückgegeben, den Sie als ersten Parameter in der Funktion angegeben haben. Bei einem Fehler wird NULL zurückgegeben.
Folgende Fehler, die Sie mit der Funktion mysql_errno() ermitteln könnten (dazu gleich mehr), können bei der Funktion mysql_real_connect() auftreten:
|
CR_CONN_HOST_ERROR – Verbindung zum MySQL-Server fehlgeschlagen |
|
CR_CONNECTION_ERROR – Verbindung zum lokalen MySQL-Server fehlgeschlagen |
|
CR_IPSOCK_ERROR – IP-Socket konnte nicht erzeugt werden. |
|
CR_OUT_OF_MEMORY – Nicht mehr genügend Speicherplatz vorhanden |
|
CR_SOCKET_CREATE_ERROR – UNIX-Socket konnte nicht erzeugt werden. |
|
CR_UNKNOWN_HOST – IP-Adresse für den Hostnamen konnte nicht gefunden werden. |
|
CR_VERSION_ERROR – Client und Server verwenden eine unterschiedliche Protokollversion (z. B. Client-Bibliothek ist veraltet). |
|
CR_SERVER_LOST – Verbindung zum Server dauerte länger als connect_timeout Sekunden, oder der Server ist während der Ausführung von init-command gestorben. |
Hinweis Sofern Sie in der Dokumentation von MySQL auf die Funktion mysql_connect() gestoßen sind: Diese ist mittlerweile veraltet und wird nur aus Kompatibilitätsgründen zu älteren Programmen erhalten, die diese Funktion verwenden.
|
12.5.2 Aufgetretene Fehler ermitteln – mysql_errno() und
mysql_error()
Alleine bei der Funktion mysql_real_connect() kann eine Menge Fehler auftreten, wie Sie soeben lesen konnten. Da sich in den Headerdateien <errmsg.h> (Fehlermeldungen vom Client) und <mysqld_error.h> (Fehlermeldungen vom Server), die sich im selben Verzeichnis befindet wie <mysql.h>, mehrere hundert Fehlercodes befinden, könnte ein Suchen danach ohne die Funktionen mysql_errno() und mysql_error() recht schwierig werden. Mit der Funktion
#include <mysql.h>
unsigned int mysql_errno(MYSQL *mysql) ;
können Sie sich, falls ein Fehler auftrat, entsprechenden Fehlercode der zuletzt aufgerufenen API-Funktion zurückgeben lassen. Wird dabei 0 zurückgegeben, ist kein Fehler aufgetreten, ansonsten wird eben entsprechender Fehlercode vom Client oder vom Server (je nach Auftritt des Fehlers) zurückgegeben.
Die Fehlernummern sind einem häufig nicht hilfreich und davon abhängig, ob der Anwender weiß, dass er im Dokumentenverzeichnis der MySQL-Distribution nach der Datei mysqld_error.txt sehen muss. Zum Glück gibt es auch für die MySQL C-API eine perror()-Funktion, die den Fehler im Klartext (zumindest so klar wie möglich) ausgibt:
#include <mysql.h>
const char *mysql_error(MYSQL *mysql) ;
Hierbei wird bei einer Verbindung eine Fehlermeldung (ein String) der zuletzt fehlgeschlagenen API-Funktion zurückgegeben. Wenn keine Fehlermeldung zurückgegeben wird, dann wird ein leerer String (index[0] = '\0') zurückgegeben. Wollen Sie anstatt der Standardsprache Englisch eine andere ausgeben lassen, müssen Sie entweder die MySQL-Client-Bibliothek nochmals neu kompilieren oder den Server mit mysqld und der Option –language=german starten. Was Sie dabei beachten müssen, entnehmen Sie bitte der Dokumentation.
12.5.3 Schließt die Verbindung zum Server – mysql_close()
Wenn Sie die mit mysql_real_connect() eingerichtete Verbindung wieder schließen wollen, dann muss die Funktion mysql_close() verwendet werden:
#include <mysql.h>
void mysql_close(MYSQL *mysql) ;
Damit schließen Sie die Verbindung mit dem MySQL-Handle und geben auch den Speicher dazu wieder frei, der zuvor mit mysql_init() alloziiert oder initialisiert wurde.
12.5.4 Erstes Beispiel
Nachdem Sie die grundlegenden Funktionen nun kennen, können Sie den ersten Schritt machen, um sich mit Ihrer (Client-)Anwendung dem MySQL-Server zu nähern. Das Beispiel macht keine großen Aktionen, es versucht lediglich, eine Verbindung mit dem Server einzugehen, wertet ggf. Fehler aus und schließt anschließend diese Verbindung gleich wieder. Bitte passen Sie die Verbindungsdaten für mysql_real_connect() an Ihrem Account an.
/* mysql1.c */
#include <stdio.h>
#include <stdlib.h>
#include <mysql.h>
/* Bitte Daten anpassen */
#define HOST "localhost"
#define USER "root"
#define PASSWORT "k4p6m3o3"
#define DBNAME NULL
#define PORT 0
#define SOCKET NULL
#define FLAG 0
int main (int argc, char **argv) {
MYSQL *my;
/* Handle initialisieren */
my = mysql_init (NULL);
if (my == NULL) {
fprintf (stderr, " Initialisierung fehlgeschlagen\n");
exit (EXIT_FAILURE);
}
/* Mit dem Server verbinden */
if (mysql_real_connect (my, /* Zeiger auf MySQL-Handler */
HOST, /* Hostname */
USER, /* User-Name */
PASSWORT, /* Passwort für user_name */
DBNAME, /* Name der Datenbank */
PORT, /* Port (default=0) */
SOCKET, /* Socket (default=NULL) */
FLAG /* 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;
}
Das Programm bei der Ausführung:
$ gcc -c -I/usr/include/mysql mysql1.c
$ gcc -o mysql1 mysql1.o -L/usr/lib/mysql -lmysqlclient
$ ./mysql1
Fehler mysql_real_connect():2002 (Can't connect to local MySQL server through
socket '/var/lib/mysql/mysql.sock' (2))
(Hoppala)
$ su
Password: ********
# rcmysql start
Starting service MySQL done
# exit
exit
$ ./mysql1
Erfolgreich mit dem MySQL-Server verbunden
Sofern Sie nicht die passenden Angaben für die Funktion mysql_real_connect() gemacht haben, bekommen Sie die entsprechende Fehlermeldung ausgegeben, was schief gelaufen ist.
Besser ist es natürlich, die Abfrage erst beim Starten des Programms vorzunehmen. Dazu können Sie entweder die Kommandozeilenoptionen auswerten (man popt) oder eine entsprechende Abfrage bei der Anwendung machen. Wie eine solche Abfrage aussehen könnte, demonstriert Ihnen folgendes Beispiel:
/* mysql2.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mysql.h>
#define MAX 255
/* Verbindungsdaten für mysql_real_connect */
static char *connect_param[7];
/* globaler Hilfszähler für die Verbindungsdaten */
static int connect_param_count = 0;
/* Eigene fgets-Funktion, die \n entfernt */
static void my_fgets( char *ptr, unsigned int max ) {
char *help;
fgets(ptr, max, stdin );
if( ( help = strchr( ptr, '\n') ) != NULL )
*help = '\0'; /* Newline durch \0 ersetzen */
}
/* Zusammenstellen der Verbindungsdaten in connect_param */
static void my_cat( char *ptr ) {
connect_param[connect_param_count] = malloc( strlen (ptr) + 1);
if (connect_param[connect_param_count] == NULL) {
printf("Konnte keinen Speicher reservieren ...\n");
exit (EXIT_FAILURE);
}
strcpy( connect_param[connect_param_count], ptr );
connect_param_count++;
}
/* Einlesen der Verbindungsdaten - Ein Drücken von <ENTER> */
/* bezweckt, dass die Standardwerte verwendet werden - */
/* Garantiert allerdings keinen Verbindungsaufbau. */
static void get_connect_param( void ) {
char puffer[MAX];
printf("Host-Name (<ENTER> == default) : ");
my_fgets( &puffer[0], MAX );
if( puffer[0] == '\0' )
connect_param[connect_param_count++] = NULL;
else
my_cat( &puffer[0] );
printf("User-Name (<ENTER> == default) : ");
my_fgets( &puffer[0], MAX );
if( puffer[0] == '\0' )
connect_param[connect_param_count++] = NULL;
else
my_cat( &puffer[0] );
printf("Passwort (<ENTER> == default) : ");
my_fgets( &puffer[0], MAX );
if( puffer[0] == '\0' )
connect_param[connect_param_count++] = NULL;
else
my_cat( &puffer[0] );
printf("Datenbank (<ENTER> == default) : ");
my_fgets( &puffer[0], MAX );
if( puffer[0] == '\0' )
connect_param[connect_param_count++] = NULL;
else
my_cat( &puffer[0] );
printf("Port (<ENTER> == default) : ");
my_fgets( &puffer[0], MAX );
if( puffer[0] == '\0' ) {
puffer[0] = '0';
my_cat( &puffer[0] );
}
else
my_cat( &puffer[0] );
printf("Socket (<ENTER> == default) : ");
my_fgets( &puffer[0], MAX );
if( puffer[0] == '\0' )
connect_param[connect_param_count++] = NULL;
else
my_cat( &puffer[0] );
printf("Flag (<ENTER> == default) : ");
my_fgets( &puffer[0], MAX );
if( puffer[0] == '\0' ) {
puffer[0] = '0';
my_cat( &puffer[0] );
}
else
my_cat( &puffer[0] );
}
/* Mit dem MySQL-Server und den entsprechenden */
/* Verbindungsdaten eine Verbindung herstellen */
static int mein_connect( MYSQL *my ) {
/* Mit dem Server verbinden */
if( mysql_real_connect (
my, /* Zeiger auf MySQL-Handler*/
connect_param[0], /* Hostname*/
connect_param[1], /* User-Name*/
connect_param[2], /* Passwort für user_name */
connect_param[3], /* Name der Datenbank*/
strtoul(connect_param[4], NULL, 0),/* Port(default=0) */
connect_param[5], /* Socket (default=NULL)*/
strtoul(connect_param[6], NULL, 0) /* keine Flags */
) == NULL) {
fprintf (stderr, "Fehler mysql_real_connect():"
"%u (%s)\n",mysql_errno (my), mysql_error (my));
return -1;
}
return 1;
}
/* Speicher freigeben und Verbindung beenden */
static void clean_up_shutdown( MYSQL * my ) {
free( connect_param );
mysql_close (my);
}
int main (int argc, char **argv) {
MYSQL *my;
/* Handle initialisieren */
my = mysql_init(NULL);
if(my == NULL) {
fprintf(stderr, " Initialisierung fehlgeschlagen\n");
exit (EXIT_FAILURE);
}
/* Verbindungsdaten für die Datenbank abfragen */
get_connect_param( );
/* Mit dem Server verbinden */
if( mein_connect( my ) != -1 )
printf("Erfolgreich mit dem MySQL-Server verbunden\n");
/* Hier befindet sich der Code für die Arbeit mit MySQL */
/* Verbindung trennen */
clean_up_shutdown( my );
return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -c -I/usr/include/mysql mysql2.c
$ gcc -o mysql2 mysql2.o -L/usr/lib/mysql -lmysqlclient
$ ./mysql2
Hostname (<ENTER> == default) : localhost
User-Name (<ENTER> == default) : root
Passwort (<ENTER> == default) : k4p6m3o3
Datenbank (<ENTER> == default) : news
Port (<ENTER> == default) : ENTER
Socket (<ENTER> == default) : ENTER
Flag (<ENTER> == default) : ENTER
Erfolgreich mit dem MySQL-Server verbunden
Da ja C-Kenntnisse Voraussetzung für das Buch sind, denke ich, dass Sie den Code selbst nachvollziehen können. Bezüglich der MySQL C-API finden Sie hierbei nichts Neues, außer dass die Ausführung des Programms ein wenig modularisiert wurde.
12.5.5 Verschiedene Informationen ermitteln
Die MySQL C-API bietet auch Funktionen, mit denen Sie diverse Informationen abfragen können. Die Funktionen sind alle recht einfach aufgebaut, weshalb die Erklärung hierfür etwas kürzer ausfällt als gewöhnlich.
Benötigen Sie die Versionsnummer der Client-Bibliothek, dann ist die folgende Funktion Ihr Fall:
#include <mysql.h>
const char *mysql_get_client_info(void) ;
Diese Funktion gibt eine Adresse zurück, die auf eine Zeichenkette mit der Versionsnummer der Client-Bibliothek verweist.
Benötigen Sie Informationen zur benutzten Verbindung (TCP, UNIX-Socket ...), dann können Sie folgende Funktion verwenden:
#include <mysql.h>
const char *mysql_get_host_info(MYSQL *mysql) ;
Die Funktion liefert Ihnen neben dem Typ der benutzten Verbindung auch den Server-Hostnamen zurück. Als Parameter wird ein Zeiger auf die verbundene MySQL-Struktur übergeben.
Um die Protokollversion der aktuellen Verbindung zu ermitteln, wird folgende Funktion verwendet:
#include <mysql.h>
unsigned int mysql_get_proto_info(MYSQL *mysql) ;
Als Parameter wird ein Zeiger auf die verbundene MySQL-Struktur übergeben. Der Rückgabewert (eine vorzeichenlose Ganzzahl) gibt dabei die Protokollversion an.
Um die Versionsnummer des Servers zu ermitteln, steht Ihnen die folgende Funktion zur Verfügung:
#include <mysql.h>
const char *mysql_get_server_info(MYSQL *mysql) ;
Dabei wird eine Zeichenkette von der angegebenen Verbindung (MySQL-Struktur), welche die Serverversionsnummer angibt, zurückgegeben.
Informationen über zuletzt ausgeführte Anfragen erhalten Sie mit der Funktion:
#include <mysql.h>
const char *mysql_info(MYSQL *mysql) ;
Damit wird ein String abgerufen, der einem der folgenden Statements entspricht:
|
INSERT INTO ... SELECT ...
Zeichenkettenformat: Records: 100 Duplicates: 0 Warnings: 0 |
|
INSERT INTO ... VALUES (...),(...),(...)...
Zeichenkettenformat: Records: 3 Duplicates: 0 Warnings: 0 |
|
LOAD DATA INFILE ...
Zeichenkettenformat: Records: 1 Deleted: 0 Skipped: 0 Warnings: 0 |
|
ALTER TABLE
Zeichenkettenformat: Records: 3 Duplicates: 0 Warnings: 0 |
|
UPDATE
Zeichenkettenformat: Rows matched: 40 Changed: 40 Warnings: 0 |
Bei anderen Statements wird NULL zurückgegeben. Das Format der Zeichenkette variiert außerdem vom Anfragetyp, wie eben gesehen (die Zahlen sind dabei nur Beispiele). Beachten Sie, dass mysql_info() einen Nicht-NULL-Wert für das INSERT ... VALUES-Statement nur dann zurückgibt, wenn im Statement mehrfache Wertlisten angegeben sind. Wenn keine Anfrageinformationen zur Verfügung stehen, gibt diese Funktion NULL zurück.
Die eben erwähnten Funktionen sollen anhand eines einfachen Beispiels demonstriert werden.
/* mysql3.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mysql.h>
#define MAX 255
/* Verbindungsdaten für mysql_real_connect */
static char *connect_param[7];
/* globaler Hilfszähler für die Verbindungsdaten */
static int connect_param_count = 0;
/*
* Übernehmen Sie die my_fgets()-, my_cat()-, get_connect_param()-,
* mein_connect()-, cleanup_shutdown()-Funktionen von oben
* (mysql2.c) -> oder den kompletten Code auf der Buch-CD
*/
int main (int argc, char **argv) {
MYSQL *my;
int auswahl;
/* Handle initialisieren */
my = mysql_init (NULL);
if (my == NULL) {
fprintf (stderr, " Initialisierung fehlgeschlagen\n");
exit (EXIT_FAILURE);
}
/* Verbindungsdaten für die Datenbank abfragen */
get_connect_param ();
/* Mit dem Server verbinden */
if (mein_connect (my) != -1)
printf ("Erfolgreich mit dem MySQL-Server verbunden\n");
do {
printf ("Informationen zum MySQL-Server auf"
" Ihrem System\n");
printf ("-----------------------------------"
"------------\n\n");
printf ("- 1 - Aktiven Zeichensatz ausgeben \n");
printf ("- 2 - Versionsnummer des Clients\n");
printf ("- 3 - Hostinformationen\n");
printf ("- 4 - Protokollversion\n");
printf ("- 5 - Versionsnummer des Servers\n");
printf ("- 6 - Infos über die letzten MySQL-Abfragen\n");
printf ("- 0 - Beenden\n\n");
printf ("Ihre Auswahl : ");
scanf ("%d", &auswahl);
switch (auswahl) {
case 1:
printf ("\nZeichensatz auf Ihrem MySQL-System : %s\n\n",
mysql_character_set_name (my));
getchar ();
break;
case 2:
printf ("\nVersionsnummer : %s\n\n",
mysql_get_client_info ());
getchar ();
break;
case 3:
printf ("\nInfos zum Host : %s\n\n",
mysql_get_host_info (my));
getchar ();
break;
case 4:
printf ("\nProtokollversion : %d\n\n",
mysql_get_proto_info (my));
getchar ();
break;
case 5:
printf ("\nVersionsnummer : %s\n\n",
mysql_get_server_info (my));
getchar ();
break;
case 6:
printf ("\nDie letzten Aktionen : %s\n\n",
mysql_info (my));
getchar ();
break;
case 0:
printf ("Bye\n");
break;
default:
printf ("Fehlerhafte Eingabe\n");
}
} while (auswahl != 0);
/* Verbindung trennen */
clean_up_shutdown (my);
return EXIT_SUCCESS;
}
12.5.6 Datenbanken, Tabellen und Felder ausgeben (MYSQL_RES)
Mit den gleich folgenden API-Funktionen können Sie sich die vorhandenen Datenbanken, Tabellen oder Felder mit einer unscharfen Suche ausgeben lassen. Nebenbei werden noch Funktionen der API beschrieben, welche die aktuellen Prozesse auflistet, was sonst von den Aufrufen mysqladmin processlist oder SHOW PROCESSLIST möglich ist.
Alle gleich hier beschriebenen Funktionen geben einen Datentypen von MYSQL_RES zurück. Dabei handelt es sich um eine Struktur, die das Ergebnis einer Anfrage repräsentiert, die Zeilen zurückgibt (SELECT, SHOW, DESCRIBE, EXPLAIN). Bei der Rückgabe spricht man hierbei von einer Ergebnismenge (auch »Resultset« genannt).
Hinweis Wenn Sie die Ergebnismenge in MYSQL_RES nicht mehr benötigen, müssen Sie diese wieder freigeben, um Memory Leaks zu vermeiden. Die Ergebnismenge wird immer mit der Funktion mysql_free_result() oder (natürlich) bei Beendigung der Anwendung freigegeben.
|
Datenbanknamen eines Servers – mysql_list_dbs()
Um Datenbanknamen abzufragen, wird die folgende Funktion verwendet:
#include <mysql.h>
MYSQL_RES *mysql_list_dbs(MYSQL *mysql, const char *wild) ;
Hiermit bekommen Sie eine Ergebnismenge zum MySQL-Handle mysql zurück, die aus den Datenbanknamen besteht, die mit dem regulären Ausdruck des wild-Parameters übereinstimmen. Dabei dürfen Sie für wild die Platzhalterzeichen '%' und '_' verwenden.
Wird hingegen der NULL-Zeiger für wild angegeben, werden alle vorhandenen Datenbanken ausgegeben. Wenn ein Fehler auftrat, wird NULL zurückgegeben. Sie müssen die Ergebnismenge mit mysql_free_result() freigeben.
Tabellennamen einer Datenbank – mysql_list_tables()
Weiter geht's mit:
#include <mysql.h>
MYSQL_RES *mysql_list_tables(MYSQL *mysql, const char *wild) ;
Mit dieser Funktion bekommen Sie als Ergebnismenge Tabellennamen der aktuellen Datenbank zurück, die mit dem regulären Ausdruck wild übereinstimmen. Auch hierbei können für wild die Platzhalterzeichen '%' oder '_' verwendet werden, und auch hierbei werden bei einer Angabe von NULL für den Parameter wild alle Tabellen einer Datenbank aufgelistet. Bei einem Fehler wird NULL zurückgegeben. Natürlich müssen Sie auch hier die Ergebnismenge mit mysql_free_result() freigeben.
Feldnamen einer Tabelle – mysql_list_fields()
Mit der Funktion
#include <mysql.h>
MYSQL_RES *mysql_list_fields( MYSQL *mysql,
const char *table,
const char *wild );
wird eine Ergebnismenge zurückgegeben, die aus den Feldnamen in der angegebenen Tabelle bestehen, die auf den regulären Ausdruck wild passen. Auch hierbei können Sie als Platzhalterzeichen '%' oder '_' angeben. Bei der Verwendung des NULL-Zeigers für wild werden alle Feldnamen der Tabelle ausgegeben. Bei einem Fehler liefert auch diese Funktion NULL zurück. Sie müssen die Ergebnismenge mit mysql_free_result() freigeben.
Aktuelle Server-Threads – mysql_list_processes()
Mit der nächsten Funktion
#include <mysql.h>
MYSQL_RES *mysql_list_processes(MYSQL *mysql) ;
können Sie die Ergebnismenge der aktuellen Server-Threads zur aktuellen Verbindung des MySQL-Handles ermitteln. Das Ergebnis ist dasselbe wie von mysqladmin processlist oder einer SHOW PROCESSLIST-Anfrage. Bei einem Fehler wird NULL zurückgegeben. Auch diese Ergebnismenge müssen Sie mit mysql_free_result() freigeben.
Die Rückgaben der letzten Befehle können durch die Benutzerverwaltung von MySQL eingeschränkt sein. Wenn Sie sich zum Beispiel alle Datenbanknamen auflisten lassen, bekommen Sie nicht zwangsläufig alle Datenbanknamen zurückgeliefert.
12.5.7 Ergebnismenge zeilenweise bearbeiten (MYSQL_ROW)
Wollen Sie eine Ergebnismenge, die sich in der Struktur MYSQL_RES befindet, nur zeilenweise einlesen oder ausgeben lassen, benötigen Sie auf jeden Fall hierzu den Datentyp oder besser die Struktur MYSQL_ROW. Dieser Datentyp repräsentiert eine Zeile von Daten, die als ein Array von gezählten Byte-Zeichenketten implementiert ist.
Um einzelne Zeilen (bei einer Datenbank spricht man ja vom Datensatz) aus der Ergebnismenge zu holen, wird folgende Funktion verwendet:
#include <mysql.h>
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result) ;
Damit wird immer der nächste Datensatz (Zeile) einer Ergebnismenge abgerufen. Als Rückgabewert erhalten Sie einen Zeiger auf die Werte einer Zeile, worauf Sie mit feld[0] bis feld[n-1] zugreifen können. Sind keine Datensätze mehr einzulesen oder trat ein Fehler auf, wird NULL zurückgegeben.
Um auf die einzelnen Felder zuzugreifen, haben Sie soeben erfahren, dass dies mit feld[0] bis feld[n-1] realisierbar ist. Nur wenn Sie bei einer Datenbank die Tabelle um ein weiteres Feld erweitern, bringt Ihnen der konstante Wert n für die Anzahl der Felder nichts mehr. Dann benötigen Sie zum Ermitteln der einzelnen Felder, die sich im Datensatz (Zeile) befinden und die Sie soeben mit mysql_fetch_row() ermittelt haben, die folgende Funktion:
#include <mysql.h>
unsigned int mysql_num_fields(MYSQL_RES *result) ;
Mit dieser Funktion erhalten Sie die Anzahl von Spalten (Felder) der Ergebnismenge, die sich in MYSQL_RES befindet. Somit können Sie jetzt auf die einzelnen Spalten mit feld[0] bis feld[mysql_num_fields(result)-1] zugreifen. Somit sähe ein zeilenweises Auslesen eines Datensatzes wie folgt aus:
MYSQL_RES *res;
MYSQL_ROW row;
/* Datensatz einlesen */
while ((row = mysql_fetch_row (res)) != NULL)
{ /* Anzahl der Spalten ermitteln und Feld für Feld ausgeben */
for (i = 0; i < mysql_num_fields (res); i++)
printf ("%s | ", row[i]);
printf ("\n");
}
Benötigen Sie jetzt auch noch die Länge der Feldwerte in der Zeile, dann können Sie diese mit der Funktion mysql_fetch_lengths() bestimmen. Mehr dazu später.
Wollen Sie hingegen die Anzahl der Datensätze (Zeilen) ermitteln, die sich in der Ergebnismenge befinden, so können Sie auf die Funktion mysql_num_rows() zurückgreifen:
#include <mysql.h>
my_ulonglong mysql_num_rows(MYSQL_RES *result) ;
Hinweis Die Dokumentation der MySQL API empfiehlt aus Portabilitätsgründen des Typs my_ulonglong, den Rückgabewert mit unsigned long zu casten! Dieser Typ stellt einen Bereich von 0 bis 1.84e19 zur Verfügung. Auf manchen »Systemen« funktioniert der Versuch, einen Wert des Typs my_ulonglong auszugeben, nicht – was allerdings mehr ein Problem des Compilers statt des Systems ist.
|
Platzieren und Abfragen eines Zeilencursors – MYSQL_ROW_OFFSET
MYSQL_ROW_OFFSET ist eine typensichere Repräsentation eines Offsets für einen Zeilencursor. Das Offset sollte ein Wert sein, der von einem Aufruf von mysql_row_tell() oder mysql_row_seek() zurückgegeben wird. Dieser Wert ist nicht einfach eine Zeilennummer.
Den Zeilencursor können Sie mit der folgenden Funktion verschieben:
#include <mysql.h>
MYSQL_ROW_OFFSET mysql_row_seek( MYSQL_RES *ergebnis,
MYSQL_ROW_OFFSET offset ) ;
Damit setzen Sie den Zeilencursor auf eine beliebige Zeile in der Anfrage-Ergebnismenge. Zurückgegeben wird der vorherige Wert des Zeilencursors. Diesen Wert können Sie wiederum bei einer späteren Verwendung von mysql_row_seek() als zweiten Parameter übergeben.
Wollen Sie hingegen die aktuelle Position des Zeilencursors ermitteln, so steht Ihnen die folgende Funktion zur Verfügung:
#include <mysql.h>
MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *ergebnis) ;
Mit mysql_row_tell() erhalten Sie die aktuelle Position des letzten mysql_fetch_row()-Aufrufs zurück. Den Rückgabewert können Sie auch hier für eine spätere Verwendung von mysql_row_seek() als zweiten Parameter verwenden. Als Rückgabewert erhalten Sie hierbei die aktuelle Position des Zeilencursors.
Hinweis Da mysql_row_tell() und mysql_row_seek() als Parameter eine Ergebnismenge der Struktur MYSQL_RES benötigen, können Sie diese Funktionen nur nach einem mysql_store_result() und nicht nach einem mysql_use_result() verwenden. Mehr dazu später.
|
12.5.8 Ergebnismenge spaltenweise einlesen (und ausgeben) (MYSQL_FIELD)
Sie werden es sicherlich erahnen, wenn es eine Struktur für die Verbindung zur Datenbank (MYSQL), eine für die Ergebnismenge (MYSQL_RES) und eine für die Zeile/den Datensatz (MYSQL_ROW) gibt, dann ist das nächstkleinste Glied in der Kette das Feld. Dafür steht Ihnen die Struktur MYSQL_FIELD zur Verfügung. Diese Struktur beinhaltet Informationen zu einem Feld wie den Namen, den Feldtyp und die Feldgröße. Sie erhalten solch eine MYSQL_FIELD-Struktur für jedes Feld durch einen wiederholten Aufruf von mysql_fetch_field(). Um Missverständnisse zu vermeiden, Feldwerte sind nicht Teil der MYSQL_FIELD-Struktur. Die Werte selbst sind in der zuvor beschriebenen Struktur MYSQL_ROW enthalten.
Die Elemente der Struktur MYSQL_FIELD sind im Folgenden erklärt.
MYSQL_FIELD – Struktur
|
char *name – Name des Feldes als terminierte Zeichenkette |
|
char *table – Name der Tabelle, die das Feld enthält, sofern es sich nicht um ein berechnetes Feld handelt – bei berechneten Feldern findet sich hier eine leere Zeichenkette. |
|
char *def – Vorgabewert des Feldes als terminierte Zeichenkette. Wird nur gesetzt, wenn Sie die Funktion mysql_list_fields() verwenden. |
|
enum enum_field_types – Der Typ des Feldes, der folgende Werte haben kann: |
Tabelle 12.6
Typ des Feldes in enum enum_field_types
Wert
|
Bedeutung
|
FIELD_TYPE_TINY
|
TINYINT-Feld
|
FIELD_TYPE_SHORT
|
SMALLINT-Feld
|
FIELD_TYPE_LONG
|
INTEGER-Feld
|
FIELD_TYPE_INT24
|
MEDIUMINT-Feld
|
FIELD_TYPE_LONGLONG
|
BIGINT-Feld
|
FIELD_TYPE_DECIMAL
|
DECIMAL- oder NUMERIC-Feld
|
FIELD_TYPE_FLOAT
|
FLOAT-Feld
|
FIELD_TYPE_DOUBLE
|
DOUBLE- oder REAL-Feld
|
FIELD_TYPE_TIMESTAMP
|
TIMESTAMP-Feld
|
FIELD_TYPE_DATE
|
DATE-Feld
|
FIELD_TYPE_TIME
|
TIME-Feld
|
FIELD_TYPE_DATETIME
|
DATETIME-Feld
|
FIELD_TYPE_YEAR
|
YEAR-Feld
|
FIELD_TYPE_STRING
|
CHAR- oder VARCHAR-Feld
|
FIELD_TYPE_BLOB
|
BLOB- oder TEXT-Feld (benutzen Sie max_length, um die maximale Länge festzulegen)
|
FIELD_TYPE_SET
|
SET-Feld
|
FIELD_TYPE_ENUM
|
ENUM-Feld
|
FIELD_TYPE_NULL
|
NULL-Feld
|
FIELD_TYPE_CHAR
|
veraltet; benutzen Sie stattdessen FIELD_TYPE_TINY
|
|
Außerdem können Sie das Makro IS_NUM() verwenden, um zu testen, ob ein Feld einem nummerischen Typ entspricht oder nicht. Beispiel einer solchen Anwendung: |
|
|
if (IS_NUM(field->type))
printf("Feld ist nummerisch\n");
|
unsigned int length – Gibt die Breite des Feldes an, die in der Tabellendefinition festgelegt wurde. |
|
unsigned int max_length – Gibt die maximale Breite des Feldes für die Ergebnismenge (die Länge des längsten Feldwertes für die Zeilen, die tatsächlich in der Ergebnismenge enthalten sind) an. Wenn Sie mysql_store_result() oder mysql_list_fields() benutzen, enthält die Variable die maximale Länge für das Feld. Wenn Sie mysql_use_result() benutzen, ist sie 0. |
|
unsigned int flags – Hier finden Sie die verschiedenen Flags. Wenn dieser Wert nicht 0 ist, dann ist mindestens eines der folgenden Bits gesetzt: |
Tabelle 12.7
Spezielle Feldattribute für die Strukturvariable flags
Flag
|
Bedeutung
|
NOT_NULL_FLAG
|
Feld darf nicht NULL sein.
|
PRI_KEY_FLAG
|
Feld ist Teil eines Primärschlüssels.
|
UNIQUE_KEY_FLAG
|
Feld ist Teil eines eindeutigen Schlüssels.
|
MULTIPLE_KEY_FLAG
|
Feld ist Teil eines nicht eindeutigen Schlüssels.
|
UNSIGNED_FLAG
|
Feld hat das UNSIGNED-Attribut.
|
BINARY_FLAG
|
Feld hat das BINARY-Attribut.
|
AUTO_INCREMENT_FLAG
|
Feld hat das AUTO_INCREMENT-Attribut.
|
ENUM_FLAG
|
Feld ist ein ENUM (veraltet).
|
BLOB_FLAG
|
Feld ist ein BLOB oder TEXT (veraltet).
|
TIMESTAMP_FLAG
|
Feld ist ein TIMESTAMP (veraltet).
|
ZEROFILL_FLAG
|
Feld hat das ZEROFILL-Attribut.
|
|
Sie können diese Flags auch mit den folgenden Makros testen, um einen flag-Wert zu bestimmen: |
|
|
IS_NOT_NULL(flags) – WAHR, wenn der Feldwert als NOT NULL definiert ist.
IS_PRI_KEY(flags) – WAHR, wenn der Feldwert ein Primärschlüssel ist.
IS_BLOB(flags) – (veraltet; testen Sie stattdessen field->type)
|
unsigned int decimals – Die Anzahl von Dezimalstellen für nummerische Felder |
Um die Definition einer Spalte aus der Ergebnismenge zu holen, kann die Funktion
#include <mysql.h>
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result) ;
verwendet werden. Damit Sie die Informationen zu allen Spalten erhalten, müssen Sie diese Funktion mehrmals aufrufen. Bei einem Fehler gibt diese Funktion NULL zurück. NULL wird auch zurückgegeben, wenn keine weiteren Felder mehr vorhanden sind. Ein Beispiel der Anwendung:
MYSQL_FIELD *field;
while((field = mysql_fetch_field(ergebnis))) {
printf("Feldname %s\n", field->name);
}
Wollen Sie ein Array aller MYSQL_FIELD-Strukturen für eine Ergebnismenge auf einmal ermitteln, so können Sie die folgende Funktion verwenden:
#include <mysql.h>
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *result) ;
Jede Struktur stellt hierbei Felddefinitionen für eine Spalte der Ergebnismenge zur Verfügung. Beispiel einer solchen Verwendung:
unsigned int num_fields;
unsigned int i;
MYSQL_FIELD *fields;
/* Anzahl der Felder ermitteln */
num_fields = mysql_num_fields(ergebnis);
fields = mysql_fetch_fields(ergebnis);
for(i = 0; i < num_fields; i++) {
printf("Feld %u ist %s\n", i, fields[i].name);
}
Wollen Sie innerhalb einer Ergebnismenge eine bestimmte Spalte direkt abrufen, dann steht Ihnen die folgende Funktion zur Verfügung:
#include <mysql.h>
MYSQL_FIELD *mysql_fetch_field_direct( MYSQL_RES *result,
unsigned int feldnr ) ;
Mit der Angabe einer Feldnummer feldnr für eine Spalte innerhalb einer Ergebnismenge gibt diese Funktion die Felddefinition dieser Spalte als MYSQL_FIELD-Struktur zurück. Sie können diese Funktion verwenden, um die Definition für eine beliebige Spalte abzurufen. Der Wert von feldnr sollte im Bereich von 0 bis mysql_num_fields(ergebnis)-1 liegen. Beispiel einer solchen Aktion:
unsigned int num_fields;
unsigned int i;
MYSQL_FIELD *field;
num_fields = mysql_num_fields(ergebnis);
for(i = 0; i < num_fields; i++) {
field = mysql_fetch_field_direct(ergebnis, i);
printf("Feld %u ist %s\n", i, field->name);
}
Wie bei der Ergebnismenge von Zeilen mit dem Zeilencursor kann auch bei den Spalten der Feldcursor abgefragt und versetzt werden. Hierfür wird der Datentyp MYSQL_FIELD_OFFSET benötigt. Das ist eine typsichere Repräsentation eines Offsets in einer MySQL-Feldliste (benutzt von mysql_field_seek()). Offsets sind Feldnummern innerhalb einer Zeile, beginnend mit 0.
Um einen Feldcursor zu versetzen, kann die Funktion
#include <mysql.h>
MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES *result,
MYSQL_FIELD_OFFSET offset) ;
verwendet werden. Damit wird der Feldcursor auf das angegebene Offset des zweiten Parameters gesetzt. Folgt anschließend ein Aufruf von mysql_fetch_field(), wird die Felddefinition der Spalte aufgerufen, die mit diesem Offset verknüpft ist. Wollen Sie (wieder) an den Anfang der Zeile springen, können Sie den offset-Wert 0 angeben.
Um die aktuelle Position des Feldcursors zu ermitteln, kann die Funktion mysql_field_tell() verwendet werden:
#include <mysql.h>
MYSQL_FIELD_OFFSET mysql_field_tell(MYSQL_RES *result) ;
Den Rückgabewert von der aktuellen Position des Feldcursors können Sie anschließend auch wieder für die Funktion mysql_field_seek() verwenden.
Benötigen Sie die Anzahl der Spalten, die für die letzte Anfrage zu einer Verbindung (Achtung! Nicht Ergebnismenge!) gestellt wurde, dann können Sie die folgende Funktion verwenden:
#include <mysql.h>
unsigned int mysql_field_count(MYSQL *mysql) ;
Gewöhnlich wird diese Funktion benutzt, wenn mysql_store_result() NULL zurückgab (und Sie somit keinen Ergebnismengen-Zeiger haben). In diesem Fall können Sie mysql_field_count() aufrufen, um festzustellen, ob mysql_store_result() ein leeres Ergebnis hätte zurückgeben sollen oder nicht. Dies ermöglicht es Ihnen, die richtigen Entscheidungen zu treffen, ohne dass Sie wissen müssen, ob es sich bei der Anfrage um ein SELECT handelte oder nicht.
Hinweis Die Funktion ist erst bei der MySQL-Version 3.2.24 eingeführt worden. Daher sollten Sie bei einer älteren Version die Funktion mysql_num_fields() stattdessen verwenden (oder noch besser, schnellstens ein Update machen).
|
12.5.9 Ein Beispiel
Bevor Sie auf den nächsten Seiten weitere API-Funktionen zu MySQL kennen lernen, folgt hierzu wieder ein Beispiel, das einige der Funktionen, über die Sie auf den Seiten zuvor etwas gelesen haben, in der Praxis demonstriert. Mit dem Listing können Sie alle vorhandenen Datenbanken, alle Tabellen einer Datenbank und alle Spalten einer Tabelle auflisten lassen. Außerdem können Sie alle vorhandenen Prozesse auflisten lassen – einfach alles, was eben in der (etwas langatmigen) Theorie beschrieben wurde.
/* mysql4.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql.h>
#define MAX 255
/* Verbindungsdaten für mysql_real_connect */
static char *connect_param[7];
/* globaler Hilfszähler für die Verbindungsdaten */
static int connect_param_count = 0;
static void print_line (MYSQL_RES * res);
/*
* Übernehmen Sie die my_fgets()-, my_cat()-, get_connect_param()-,
* mein_connect()-, cleanup_shutdown()-Funktionen von oben
* (mysql2.c) -> oder den kompletten Code auf der Buch-CD
*/
/* Listet alle vorhandenen Datenbanken auf */
static void list_DB (MYSQL * my) {
MYSQL_RES *res;
MYSQL_ROW row;
int zeilen;
unsigned int i;
res = mysql_list_dbs (my, NULL);
zeilen = (unsigned long) mysql_num_rows (res);
printf ("Datenbanken gefunden : %d\n", zeilen);
while ((row = mysql_fetch_row (res)) != NULL)
/* mysql_num_flields() wäre hier nicht nötig */
for (i = 0; i < mysql_num_fields (res); i++)
printf ("%s\n", row[i]);
mysql_free_result (res);
getchar ();
}
/* Listet alle Tabellen einer Datenbank auf */
static void list_table_DB (MYSQL * my) {
MYSQL_RES *res;
MYSQL_ROW row;
int zeilen;
unsigned int i;
res = mysql_list_tables (my, NULL);
if (res == NULL) {
fprintf (stderr, "Fehler bei mysql_list_tables():"
"%u (%s)\n", mysql_errno (my), mysql_error (my));
return;
}
zeilen = (unsigned long) mysql_num_rows (res);
printf ("Tabellen in der Datenbank : %d\n", zeilen);
while ((row = mysql_fetch_row (res)) != NULL) {
for (i = 0; i < mysql_num_fields (res); i++)
printf ("%s", row[i]);
printf ("\n");
}
mysql_free_result (res);
getchar ();
}
/* Listet alle Felder einer Tabelle auf */
static void list_fields_DB (MYSQL * my) {
unsigned int i, col_len;
MYSQL_RES *res;
MYSQL_FIELD *field;
char buffer[MAX];
printf ("Bitte eine Tabelle auswählen: ");
my_fgets (buffer, MAX);
res = mysql_list_fields (my, buffer, NULL);
if (res == NULL) {
fprintf (stderr, "Fehler bei mysql_list_tables():"
"%u (%s)\n", mysql_errno (my), mysql_error (my));
return;
}
/* 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" ->keine 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);
printf("Mit mysql_fetch_field()\n");
/* Demonstriert mysql_fetch_field() */
print_line (res);
printf ("|");
/* Alles wieder auf den Anfang stellen */
mysql_field_seek (res, 0);
/* Jetzt den Tabellenkopf ausgeben */
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);
printf("Gleiches mit mysql_fetch_fields()\n");
/* Dasselbe nochmals mit mysql_fetch_fields() */
print_line (res);
printf ("|");
/* Alles wieder auf den Anfang stellen */
mysql_field_seek (res, 0);
field = mysql_fetch_fields (res);
for (i = 0; i < mysql_num_fields (res); i++) {
printf (" %-*s |", field[i].max_length, field[i].name);
}
printf ("\n");
print_line (res);
mysql_free_result (res);
}
static 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");
}
/* Listet alle vorhandenen Prozesse auf */
static void list_process (MYSQL * my) {
MYSQL_RES *res;
MYSQL_ROW row;
int zeilen;
unsigned int i, col_len;
MYSQL_FIELD *field;
res = mysql_list_processes (my);
zeilen = (unsigned long) mysql_num_rows (res);
printf ("Prozesse gefunden : %d\n", zeilen);
/* 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;
}
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);
mysql_field_seek (res, 0);
while ((row = mysql_fetch_row (res)) != NULL) {
printf ("|");
for (i = 0; i < mysql_num_fields (res); i++) {
field = mysql_fetch_field (res);
printf (" %-*s |",field->max_length, row[i]);
}
printf ("\n");
mysql_field_seek (res, 0);
}
print_line (res);
mysql_free_result (res);
}
int main (int argc, char *argv[]) {
MYSQL *my;
int auswahl;
/* Handle initialisieren */
my = mysql_init (NULL);
if (my == NULL) {
fprintf (stderr, " Initialisierung fehlgeschlagen\n");
exit (EXIT_FAILURE);
}
/* Verbindungsdaten für die Datenbank abfragen */
get_connect_param ();
/* Mit dem Server verbinden */
if (mein_connect (my) != -1)
printf ("Erfolgreich mit dem MySQL-Server verbunden\n");
do {
printf ("\nInformationen zu den Datenbanken auf dem"
" MySQL-Server\n\n");
printf ("- 1 - Alle vorhandenen Datenbanken ausgeben \n");
printf ("- 2 - Tabellen einer Datenbank auflisten\n");
printf (" -3 - Spalten der Tabelle auflisten\n");
printf ("- 4 - Prozessliste anzeigen\n");
printf ("- 0 - Beenden\n\n");
printf ("Ihre Auswahl : ");
scanf ("%d", &auswahl);
switch (auswahl) {
case 1:
list_DB (my);
getchar ();
break;
case 2:
getchar ();
list_table_DB (my);
break;
case 3:
getchar ();
list_fields_DB (my);
break;
case 4:
getchar ();
list_process (my);
break;
case 0:
printf ("Bye\n");
break;
default:
printf ("Fehlerhafte Eingabe\n");
}
} while (auswahl != 0);
/* Verbindung trennen */
clean_up_shutdown (my);
return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -c -I/usr/include/mysql mysql4.c
$ gcc -o mysql4 mysql4.o -L/usr/lib/mysql -lmysqlclient
$ ./mysq4
Hostname (<ENTER> == default) : localhost
User-Name (<ENTER> == default) : root
Passwort (<ENTER> == default) : k4p6m3o3
Datenbank (<ENTER> == default) : adressen
Port (<ENTER> == default) : ENTER
Socket (<ENTER> == default) : ENTER
Flag (<ENTER> == default) : ENTER
Erfolgreich mit dem MySQL-Server verbunden
Informationen zu den Datenbanken auf dem MySQL-Server
-----------------------------------------------
- 1 - Alle vorhandenen Datenbanken ausgeben
- 2 - Tabellen einer Datenbank auflisten
-3 - Spalten der Tabelle auflisten
- 4 - Prozessliste anzeigen
- 0 - Beenden
Ihre Auswahl : 1
Datenbanken gefunden : 7
adressen
dvd_archiv
mail_archiv
mein_test
mysql
news
test
...
...
Ihre Auswahl : 2
Tabellen in der Datenbank : 1
privat
...
...
Ihre Auswahl : 3
Bitte eine Tabelle auswählen : privat
Daten der Tabelle: [ privat ]
+-------+-------+------+------+---------+--------+-------+
| vname | nname | plz | ort | strasse | hausnr | phone |
+-------+-------+------+------+---------+--------+-------+
...
...
Ihre Auswahl : 4
Prozesse gefunden : 2
+--+----+---------+--------+-----------+----+------+-------+
|Id|User|Host |db |Command |Time|State | Info |
+--+----+---------+--------+-----------+----+------+-------+
|1 |root|localhost|adressen|Processlist|0 |(null)| (null)|
|3 |root|localhost|(null) |Sleep |6 | | (null)|
+--+----+---------+--------+-----------+----+------+-------+
12.5.10 Ergebnismenge – weitere Funktionen
Jetzt folgen hier noch weitere Funktionen im Bezug auf die Ergebnismenge (MYSQL_RES), die sich recht allgemein einsetzen lassen.
Besitzt eine Ergebnismenge das gesamte Anfrageergebnis, dann können Sie mit der Funktion
#include <mysql.h>
void mysql_data_seek( MYSQL_RES *result,
my_ulonglong offset );
bis zu einer beliebigen Zeile in der Ergebnismenge suchen. Die Funktion kann daher nur in Verbindung mit der Funktion mysql_store_result() verwendet werden – nicht mit mysql_use_result(). Als Offset sollte ein Wert zwischen dem Bereich 0 bis mysql_num_row(max)-1 verwendet werden.
Benötigen Sie die Länge der Spalten der aktuellen Zeile einer Ergebnismenge, um etwa Feldwerte zu kopieren, dann kann hierfür folgende Funktion verwendet werden:
#include <mysql.h>
unsigned long *mysql_fetch_lengths(MYSQL_RES *result) ;
Der Vorteil dieser Funktion ist, dass diese nicht die Funktion strlen() aufruft, was sich positiv in der Performance auswirkt und sich somit auch bestens für Binärdaten eignet. Denn bei Binärdaten würde die Funktion strlen() ein falsches Ergebnis für die Felder zurückgeben, die Nullzeichen enthalten. Ist das Feld leer oder beinhaltet Nullwerte, gibt mysql_fetch_length() 0 zurück – was dabei zutrifft, können Sie mit mysql_fetch_row() ermitteln. Hier ein Beispiel, wie das in der Praxis geht:
MYSQL_ROW zeile;
unsigned int anzahl_felder;
unsigned int i;
anzahl_felder = mysql_num_fields(ergebnis);
while ((zeile = mysql_fetch_row(ergebnis))) {
unsigned long *laengen;
laengen = mysql_fetch_lengths(ergebnis);
for(i = 0; i < anzahl_felder; i++) {
printf("[%.*s] ",
(int) laengen[i], zeile[i] ? zeile[i] : "NULL");
}
printf("\n");
}
Als Rückgabewert liefert Ihnen mysql_fetch_length() ein Array von vorzeichenlosen Ganzzahlen zurück, das die Größe einer jeden Spalte beinhaltet. Bei einem Fehler wird NULL zurückgegeben.
12.5.11 Befehle an den Server – mysql_query() und mysql_real_query()
Sicherlich haben Sie sich schon überlegt, wie Sie wohl SQL-Anfragen ŕ la SELECT, INSERT ... an den MySQL-Server mithilfe der C-API stellen können.
Eine SQL-Anfrage können Sie mit der Funktion mysql_query() bzw. mysql_real_query() stellen. Der Vorgang einer solchen Anfrage ist folgender:
|
Eine SQL-Anfrage an den Server |
|
Server empfängt die Anfrage und überprüft diese auf syntaktische Fehler. |
|
Server führt die angefragte Aktion aus und gibt abhängig von der Art der Anfrage ein Resultat zurück. So wird, im Gegensatz zu einer SELECT-Anfrage, bei einer INSERT-Anfrage nichts zurückgegeben. |
Die Funktion, mit der Sie eine SQL-Anfrage an den Server stellen können, ist:
#include <mysql.h>
int mysql_query(MYSQL *mysql, const char *anfrage);
int mysql_real_query( MYSQL *mysql, const char *anfrage,
unsigned long laenge );
Damit können Sie SQL-Anweisungen (Anfragen) als nullterminierten String ausführen, der am Ende allerdings im Gegensatz zur MySQL-Shell kein Semikolon oder \g haben darf. Verläuft bei der Anfrage alles planmäßig, wird 0, ansonsten bei einem Fehler ungleich 0 zurückgegeben.
Da mysql_query() wegen der Bedeutung des String-Endezeichens nicht für Binärdaten geeignet ist, müssen Sie hierfür die Funktion mysql_real_query() mit der Angabe der Länge (laenge) des Strings als extra Parameter verwenden.
Hinweis 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 die SQL-Anfrage an den Server nicht klappen will, sollten Sie den Fehler mit mysql_errno() und mysql_error() überprüfen. Folgende Fehler können dabei auftreten:
|
CR_COMMANDS_OUT_OF_SYNC – Befehle wurden nicht in der korrekten Reihenfolge ausgeführt. |
|
CR_SERVER_GONE_ERROR – Der MySQL-Server ist weg. |
|
CR_SERVER_LOST – Die Verbindung zum Server ging während der Anfrage verloren. |
|
CR_UNKNOWN_ERROR – Ein unbekannter Fehler trat auf. |
Für Anfragen an den Server mit z. B. INSERT, wo ja nichts zurückgegeben wird, reichen mysql_query() und mysql_real_query() aus. Allerdings bei Anfragen mittels SELECT, SHOW, DESCRIBE oder EXPLAIN, wo etwas zurückgegeben wird, benötigen Sie eine Funktion, um die Daten der SQL-Abfrage abzuholen. Hierfür müssen Sie die Funktionen mysql_store_result() oder mysql_use_result() aufrufen. Es ist allerdings auch möglich, die beiden Funktionen bei SQL-Anfragen aufzurufen, die keinen Wert zurückgeben. Damit können Sie z. B. feststellen, ob die Anfrage eine Ergebnismenge hatte, wenn Sie prüfen, ob mysql_store_result() 0 zurückgibt.
Beginnend mit mysql_store_result() – die Syntax:
#include <mysql.h>
MYSQL_RES *mysql_store_result(MYSQL *mysql) ;
Damit lesen Sie das gesamte Ergebnis einer SQL-Anfrage (meist mit mysql_real_query()) zum Client ein. Als Rückgabewert erhalten Sie eine Ergebnismenge vom Typ der MYSQ_RES-Struktur zurück. Wie Sie diese Ergebnismenge behandeln können, haben Sie ja bereits gelesen. Hat die Anfrage keine Ergebnismenge zurückgegeben, gibt mysql_store_result() NULL zurück (z. B. wenn die Anfrage ein INSERT-Statement war). NULL wird allerdings auch zurückgegeben, wenn das Lesen der Ergebnismenge fehlgeschlagen ist. Sie können überprüfen, ob es sich dabei um einen Fehler handelt, wenn mysql_error() keinen NULL-Zeiger zurückgibt und/oder mysql_errno() ungleich 0 zurückgab. Eine leere Ergebnismenge wird zurückgegeben, wenn keine Zeilen zurückgegeben werden. (Eine leere Ergebnismenge unterscheidet sich als Rückgabewert von einem NULL-Zeiger.)
Wenn Sie mittels mysql_store_result() ein Ergebnis erhalten haben, das kein NULL-Zeiger ist, können Sie mit der Funktion mysql_num_rows() herausfinden, wie viele Zeilen in der Ergebnismenge enthalten sind. Mit mysql_fetch_row() können Sie die einzelnen Zeilen der Ergebnismenge holen oder mit mysql_row_seek() und mysql_row_tell() die Zeilenposition (Zeilencursor) innerhalb der Zeile setzen oder abfragen (funktioniert auch mit mysql_data_seek()).
Die Behandlung des Erhalts der Ergebnismenge mit mysql_store_result() kann also ebenso gehandhabt werden, wie Sie dies schon in den Seiten zuvor mit der Behandlung der Mengen zeilenweise und spaltenweise gesehen und (hoffentlich auch) gemacht haben.
Sie müssen mysql_free_result() aufrufen, wenn Sie mit der Ergebnismenge fertig sind.
Natürlich kann es auch passieren, dass mysql_store_result() NULL zurückliefert, obwohl der Aufruf von mysql_real_query() erfolgreich war. Dies kann mehrere Ursachen haben. So war die Rückgabe der Ergebnismenge zu groß, und es konnte dafür kein Speicher mehr reserviert werden, oder die Daten konnten nicht gelesen werden. Und – der plausibelste Grund – die Anfrage gibt keine Daten zurück, wie dies bei INSERT, UPDATE oder DELETE der Fall ist.
Um zu überprüfen, ob das Statement eine leere Ergebnismenge zurückgegeben hat, sollten Sie mysql_field_count() aufrufen. Wenn mysql_field_count() 0 zurückgibt, ist die Ergebnismenge leer, und bei der Anfrage handelte es sich um ein SQL-Statement, das keinen Rückgabewert liefert (INSERT, UPDATE, DELETE).
Es empfiehlt sich außerdem, mit mysql_error() und mysql_errno() auf Fehler zu überprüfen. Folgende Fehler können dabei auftreten:
|
CR_COMMANDS_OUT_OF_SYNC – Befehle wurden nicht in der korrekten Reihenfolge ausgeführt. |
|
CR_OUT_OF_MEMORY – Kein Speicher mehr. |
|
CR_SERVER_GONE_ERROR – Der MySQL-Server ist weg. |
|
CR_SERVER_LOST – Die Verbindung zum Server ging während der Anfrage verloren. |
|
CR_UNKNOWN_ERROR – Ein unbekannter Fehler trat auf. |
Die Funktion mysql_use_result() initiiert einen Ergebnismengen-Abruf, aber liest die Ergebnismenge nicht in die Clientanwendung ein wie mysql_store_result().
#include <mysql.h>
MYSQL_RES *mysql_use_result(MYSQL *mysql);
Mit mysql_use_result() muss jede Zeile extra abgerufen werden, indem hierzu Aufrufe mit mysql_fetch_row() ausgeführt werden. Damit wird das Ergebnis einer Anfrage direkt vom Server geholt, ohne (wie bei mysql_store_result()) zuvor eine temporäre Tabelle oder einen lokalen Puffer zu belasten – was daher logischerweise schneller sein kann als mysql_store_result(). Der Clientanwendung wird nur Speicher für die aktuelle Zeile zugewiesen sowie ein Kommunikationspuffer, der bis zu max_allowed_packet Bytes groß werden kann.
Auf der anderen Seite sollten Sie mysql_use_result() nicht verwenden, wenn Sie viele Verarbeitungen für jede Zeile auf der Clientseite durchführen oder wenn die Ausgabe auf den Bildschirm geschickt wird, auf dem der Benutzer (STRG)+(S) (stop scroll) eingeben kann. Das bindet den Server und sorgt dafür, dass andere Threads irgendwelche Tabellen nicht aktualisieren können, von denen gerade Daten geholt werden.
Bei der Funktion mysql_use_result() müssen Sie außerdem mysql_fetch_row() ausführen, bis ein NULL-Wert zurückgegeben wird, da sonst die nicht geholten Zeilen als ein Teil der Ergebnismenge bei der nächsten Anfrage zurückgegeben werden. Wird dies nicht beachtet, bekommen Sie einen Fehler wie Commands out of sync; You can't run this command now zurück.
Da die Funktion mysql_use_result() jede Zeile extra aufruft, können Sie Funktionen wie mysql_data_seek(), mysql_row_seek(), mysql_row_tell(), mysql_num_rows() oder mysql_affected_rows() nicht bei einem Ergebnis verwenden. Des Weiteren darf keine Anfrage gesetzt werden, wenn mysql_use_result() nicht beendet ist. Wenn alle Zeilen geholt wurden, wird die Funktion mysql_num_row() aber dennoch die exakte Anzahl der geholten Zeilen zurückgeben. Sie müssen mysql_free_result() aufrufen, wenn Sie mit der Ergebnismenge fertig sind.
Die Fehler, die Sie mit mysql_error() und mysql_errno() abfragen können, sind dieselben wie bei der Funktion mysql_store_result().
Um herauszufinden, wie viele Zeilen von einem SQL-Statement wie UPDATE, DELETE oder INSERT betroffen waren, kann man direkt nach mysql_real_query() die Funktion
#include <mysql.h>
my_ulonglong mysql_affected_rows(MYSQL *mysql) ;
aufrufen. Bei SELECT-Statements funktioniert mysql_affectetd_rows() wie mysql_num_rows(). Ein Wert größer als 0 gibt die Anzahl der Zeilen, die vom Abruf betroffen waren. Bei 0 wurde kein Datensatz geändert, und bei -1 ist ein Fehler aufgetreten. So wird auch ein Fehler zurückgegeben, wenn Sie bei einem SELECT-Statement die Funktion mysql_affected_row() vor der Funktion mysql_store_result() aufrufen. Ein Beispiel einer solchen Abfrage könnte so aussehen:
char
str[BUF] = "UPDATE adressen SET plz=86316 WHERE ort='Friedberg'";
mysql_real_query(&mysql, str, strlen(str));
printf("%ld Adressen vom UPDATE betroffen",
(long) mysql_affected_rows(&mysql));
Bei der Verwendung eines REPLACE-Statements und für den Fall, dass ein Datensatz verändert wurde, gibt mysql_affected_row() 2 zurück. Dies liegt daran, dass erst eine neue Zeile eingefügt und dann die alte gelöscht wird.
Wenn Sie mit der Abarbeitung der Ergebnismenge von mysql_store_result(), mysql_use_result(), mysql_list_dbs(), mysql_list_fields(), mysql_list_tables() ... fertig sind, sollten Sie den Speicher wieder freigeben, um Memory Leaks zu vermeiden. Eine Ergebnismenge freigeben können Sie mit der Angabe der Ergebnismenge als Parameter mit der Funktion:
#include <mysql.h>
void mysql_free_result(MYSQL_RES *result) ;
12.5.12 Weitere Funktionen
Bevor Sie ein etwas umfangreicheres Beispiel sehen werden, folgen hier noch zu Referenzzwecken weitere API-Funktionen im Schnelldurchlauf, die allerdings im anschließenden Beispiel nicht verwendet werden.
Soll der Benutzer geändert wurden, kann die Funktion
#include <mysql.h>
my_bool mysql_change_user(MYSQL *mysql, const char *user,
const char *password, const char *db);
verwendet werden. Damit wird der Benutzer für die Verbindung mit dem MySQL-Handle geändert. Als weitere Parameter werden der Username, das Passwort und eventuell die Datenbank benötigt. Wenn Sie keine den Vorgaben entsprechende Datenbank haben wollen, können Sie hierfür auch NULL angeben. Kann der Benutzer nicht authentifiziert werden oder fehlen die entsprechenden Zugriffsrechte auf die Datenbank, schlägt die Funktion mysql_change_user() fehl. Bei einem Fehler wird ein Wert ungleich 0 zurückgegeben – bei Erfolg 0. Um zu ermitteln, welcher Fehler aufgetreten ist, empfiehlt es sich, die Funktionen mysql_error() und mysql_errno() zu verwenden. Die Fehler, die Sie dabei erhalten können, entsprechen denselben, die Sie bei der Funktion mysql_real_connect() erhalten könnten.
Hinweis Bei einer API-Version vor 3.23.3 ist diese Funktionen noch nicht implementiert.
|
Ein Codeausschnitt zur Anwendung sieht wie folgt aus:
if (mysql_change_user(&mysql, "nobody",
"x4i4d1k4",
"datenbank_3")) {
fprintf(stderr, "Änderung des Benutzers fehlgeschlagen. "
" Fehler: %s\n", mysql_error(&mysql));
}
Um eine korrekte SQL-Zeichenkette für ein SQL-Statement zu erzeugen, kann folgende Funktion verwendet werden:
#include <mysql.h>
unsigned int mysql_escape_string( MYSQL *mysql, char *nach,
const char *von,
unsigned int laenge );
unsigned int mysql_real_escape_string( MYSQL *mysql, char *nach,
const char *von,
unsigned int laenge );
Die Zeichenkette von wird in eine escape-SQL-Zeichenkette kodiert. Kodierte Zeichen sind NUL (ASCII 0), \n, \r, \, ', " und (STRG)+(Z); wobei die Funktion mysql_real_escape_string() der Funktion mysql_escape_string() vorzuziehen ist, da diese Funktion den aktuellen Zeichensatz, der verwendet wird, berücksichtigt. Bei mysql_escape_string() wird die aktuelle Zeichenstellung ignoriert. Das Ergebnis finden Sie als einen terminierten String in nach. Der String, auf den von zeigt, muss laenge Bytes lang sein. Sie müssen den nach-Puffer so zuweisen, dass er mindestens laenge*2+1 Bytes lang ist. Der Rückgabewert ist die Länge der kodierten Zeichenkette ohne das Nullzeichen am Ende.
Wenn Sie eine Kennung für eine AUTO_INCREMENT-Spalte benötigen, die durch eine vorherige Anfrage erzeugt wurde, dann kann die Funktion
#include <mysql.h>
my_ulonglong mysql_insert_id(MYSQL *mysql) ;
verwendet werden. Benutzen sollten Sie die Funktion unmittelbar nach einer INSERT-Anfrage für eine Tabelle, die den AUTO_INCREMENT-Wert erzeugt hat. mysql_insert_id() wird nach jedem INSERT- und UPDATE-Statement aktualisiert, die einen AUTO_INCREMENT-Wert erzeugen oder einen Spaltenwert auf LAST_INSERT_ID(ausdruck) setzen. Beachten Sie auch, dass der Wert der LAST_INSERT_ID()-Funktion immer den aktuellsten erzeugten AUTO_INCREMENT-Wert enthält und zwischen den Anfragen nicht zurückgesetzt wird, weil der Wert dieser Funktion beim Server erwartet wird. Zurückgegeben wird der Wert des AUTO_INCREMENT-Feldes, der durch die vorherige Anfrage verändert wurde – ansonsten 0, wenn es keine Anfrage gab oder wenn die Anfrage keinen AUTO_INCREMENT-Wert aktualisiert hat.
Wollen Sie einen bestimmten Thread mit einer bestimmten Prozess-ID beenden, können Sie folgende Funktion verwenden:
#include <mysql.h>
int mysql_kill(MYSQL *mysql, unsigned long pid) ;
Damit wird der Thread der aktuellen Verbindung mysql mit der Erkennung pid beendet. Konnte der Thread erfolgreich »getötet« werden, gibt diese Funktion 0, ansonsten bei einem Fehler ungleich 0 zurück.
Es besteht auch die Möglichkeit, das Verhalten einer Verbindung zu verändern. Mit der Funktion mysql_options() können Sie zusätzlich Optionen zu einer Verbindung setzen.
#include <mysql.h>
int mysql_options(MYSQL *mysql, enum mysql_option option,
const char *arg) ;
mysql_options() kann zwar mehrfach aufgerufen werden, um verschiedene Optionen zu setzen, allerdings muss dies nach mysql_init() und noch vor mysql_real_connect() (bzw. mysql_connect()) geschehen. Ist eine Verbindung erst hergestellt, gibt es keine Möglichkeit mehr, die laufende Verbindung mit den Optionen zu modifizieren. Die Funktion gibt 0 bei Erfolg und ungleich 0 zurück, wenn Sie eine unbekannte Option verwenden. Das option-Argument ist die Option, die Sie setzen wollen. Das arg-Argument ist der Wert für die Option. Wenn die Option eine Ganzzahl ist, sollte arg auf den Wert der Ganzzahl zeigen, ist die Option ein String, dann sollte arg auch auf einen String zeigen. Hier die möglichen Optionswerte mit den Argumenttypen und deren Bedeutungen:
Tabelle 12.8
Optionen und deren Argumenttypen für mysql_options()
Option
|
arg-Typ
|
Bedeutung
|
MYSQL_OPT_CONNECT_TIMEOUT
|
unsigned
int *
|
Verbindungszeitüberschreitung (Timeout) in Sekunden
|
MYSQL_OPT_COMPRESS
|
Unbenutzt
|
Das komprimierte Client-Server-Protokoll verwenden
|
MYSQL_OPT_NAMED_PIPE
|
Unbenutzt
|
Named Pipes benutzen, um sich mit einem MySQL-Server unter NT zu verbinden.
|
MYSQL_INIT_COMMAND
|
char *
|
Befehl, der beim Verbinden mit dem MySQL-Server ausgeführt werden soll. Wird beim erneuten Verbinden automatisch wieder ausgeführt.
|
MYSQL_READ_DEFAULT_FILE
|
char *
|
Optionen aus der benannten Optionsdatei anstelle von my.cnf einlesen.
|
MYSQL_READ_DEFAULT_GROUP
|
char *
|
Optionen aus der benannten Gruppe von my.cnf oder der Datei einlesen, die durch MYSQL_READ_DEFAULT_FILE angegeben wurde.
|
Beachten Sie, dass die Gruppe client immer gelesen wird, wenn Sie MYSQL_READ_DEFAULT_FILE oder MYSQL_READ_DEFAULT_GROUP benutzen. Die angegebene Gruppe in der Optionsdatei kann folgende Optionen enthalten:
|
connect_timeout – Zeitüberschreitung (Timeout) für die Verbindung in Sekunden. Unter Linux wird dieser Wert zusätzlich für die Wartezeit auf die erste Antwort vom Server benutzt. Beachten Sie, dass timeout durch connect_timeout ersetzt wurde. Dennoch wird timeout noch für eine Weile funktionieren. |
|
compress – Das komprimierte Client-Server-Protokoll benutzen |
|
database – Mit dieser Datenbank verbinden, wenn im Verbindungsbefehl keine Datenbank angegeben wurde. |
|
debug – Debug-Optionen |
|
host – Vorgabemäßiger Hostname |
|
init-commund – Befehl, der bei der Verbindung zum MySQL-Server ausgeführt wird. Wird automatisch beim erneuten Verbinden noch einmal ausgeführt. |
|
interactive-timeout – Dasselbe wie die Angabe von CLIENT_INTERACTIVE für mysql_real_connect() |
|
password – Vorgabemäßiges Passwort |
|
pipe – Named Pipes benutzen, um sich mit einem MySQL-Server unter NT zu verbinden. |
|
port – Vorgabemäßige Portnummer |
|
return-found-rows – Weist mysql_info() an, gefundene Zeilen anstelle von aktualisierten Zeilen zurückzugeben, wenn UPDATE benutzt wird. |
|
socket – Vorgabemäßige Socket-Nummer |
|
user – Vorgabemäßiger Benutzer |
Ein Code-Ausschnitt einer solchen Anwendung:
MYSQL my;
mysql_init(&my);
mysql_options(&my,MYSQL_OPT_COMPRESS,0);
mysql_options(&my,MYSQL_READ_DEFAULT_GROUP,"odbc");
if (mysql_real_connect(&my, "host",
"benutzer",
"passwort",
"datenbank"
,0,NULL,0) == 0 ) {
fprintf(stderr, "Fehler bei mysql_real_connect: %s\n",
mysql_error(&my));
exit(EXIT_FAILURE);
}
Im obigen Beispiel wird der Client angewiesen, das komprimierte Client-Server-Protokoll zu benutzen und zusätzliche Optionen aus dem odbc-Abschnitt in my.cnf zu lesen.
Um zu überprüfen, ob eine Verbindung zum Server noch steht oder nicht, können Sie folgende Funktion verwenden:
#include <mysql.h>
int mysql_ping(MYSQL *mysql) ;
Besteht keine Verbindung mehr zum Server, wird versucht, von Clientseite erneut eine Verbindung zum Server herzustellen. Dies ist z. B. nötig, wenn Ihre Clientanwendung lange Zeit im Leerlauf mit dem Server verbunden ist und der Server die Verbindung getrennt hat und Sie sich erneut verbinden wollen. Die Funktion gibt bei Erfolg 0, ansonsten, wenn der Server nicht mehr da ist oder ein Fehler auftrat, ungleich 0 zurück. Was genau passiert ist, sollten Sie am besten mit mysql_error() und mysql_errno() überprüfen. Folgende Fehler können dabei aufgetreten sein:
|
CR_COMMANDS_OUT_OF_SYNC – Befehle wurden nicht in der korrekten Reihenfolge ausgeführt. |
|
CR_SERVER_GONE_ERROR – Der MySQL-Server ist weg. |
|
CR_UNKNOWN_ERROR – Ein unbekannter Fehler trat auf. |
Wollen Sie für eine Verbindung eine vorgabemäßige Datenbank angeben, dann können Sie folgende Funktion verwenden:
#include <mysql.h>
int mysql_select_db(MYSQL *mysql, const char *db) ;
Damit wird die Datenbank, die Sie mit db angeben, zur aktuellen Datenbank der Verbindung mysql. Alle nachfolgenden Anfragen beziehen sich auf diese Datenbank. Die Funktion schlägt fehl, sofern der Benutzer keine Zugriffsrechte auf diese Datenbank hat. Die Funktion gibt bei Erfolg 0, ansonsten bei einem Fehler ungleich 0 zurück.
Den MySQL-Server herunterfahren können Sie, sofern entsprechende Berechtigungen vorhanden sind, mit der folgenden Funktion:
#include <mysql.h>
int mysql_shutdown(MYSQL *mysql) ;
Konnte der Server erfolgreich heruntergefahren werden, wird 0, ansonsten bei einem Fehler ungleich 0 zurückgegeben.
Benötigen Sie Informationen, ähnlich wie die der Ausgabe von mysqladmin status, können Sie folgende Funktion verwenden:
#include <mysql.h>
const char *mysql_stat(MYSQL *mysql);
Dabei erhalten Sie einen terminierten String zurück, der Informationen wie die Betriebszeit (Uptime) in Sekunden und die Anzahl laufender Threads, Anfragen (Questions), Neuladen (Reloads) und offene Tabellen ausgibt. Konnte der Status nicht erfragt werden oder trat sonst ein Fehler auf, wird NULL zurückgegeben.
Die Kennung (pid) eines aktuellen Threads können Sie mit der Funktion
#include <mysql.h>
unsigned long mysql_thread_id(MYSQL *mysql) ;
ermitteln. Diesen zurückgegebenen Wert können Sie anschließend z. B. verwenden, um den Thread mit der Funktion mysql_kill() zu beenden. Beachten Sie allerdings, wenn eine Verbindung beendet wird und Sie mit mysql_ping() eine neue Verbindung aufbauen, dass sich die Thread-ID ebenfalls verändert hat und Sie somit die Funktion mysql_thread_id() erneut aufrufen sollten, sofern Sie die Kennung benötigen.
Sofern Sie Probleme mit dem Server oder der Clientanwendung haben, können Sie mit der Funktion mysql_debug() ein DBUG_PUSH mit einem angegebenen String durchführen, z. B.:
mysql_debug("d:t:O,/tmp/client.trace");
Mit dieser Funktion wird die Debug-Bibliothek von Fred Fish benutzt. Voraussetzung dafür, dass Sie diese Funktion verwenden können, ist, dass Sie die Client-Bibliothek so kompilieren, dass diese das Debuggen unterstützt. Mit der Funktion mysql_dump_debug_info() weisen Sie den Server an, Debug-Informationen in die Log-Datei zu schreiben (entsprechende Zugriffsrechte vorausgesetzt!). Ich will allerdings auf die Debug-Geschichte nicht näher eingehen, da dies ein umfangreicheres Unterfangen ist und nicht Diskussionspunkt in diesem Buch ist.
12.5.13 Veraltete Funktionen
Zum Abschluss folgen hier noch einige Funktionen, die allerdings nicht mehr verwendet werden sollten, da sie als veraltet gelten. Damit ältere Clientanwendungen noch mit einer neueren API-Bibliothek übersetzt werden können, sind diese Funktionen noch vorhanden. Falls Sie eine ältere Clientanwendung verändern müssen und Sie auf eine dieser Funktionen treffen, wissen Sie wenigstens Bescheid, worum es sich handelt.
Mit der Funktion
#include <mysql.h>
int mysql_create_db(MYSQL *mysql, const char *db) ;
kann eine Datenbank, die mit dem Parameter db angegeben wird, erzeugt werden. Als Alternative wird empfohlen, die Funktion mysql_real_query() und das SQL-Statement CREATE DATABASES zu verwenden, z. B.:
const char buf[BUF] = "CREATE DATABASE datenbank";
mysql_real_query( my, buf, strlen(buf));
Ebenso sieht es mit der Funktion
#include <mysql.h>
int mysql_drop_db(MYSQL *mysql, const char *db) ;
aus. Damit wird eine Datenbank, die mit dem Parameter db angegeben wird, gelöscht. Auch hierzu sollte mysql_real_query() mit dem SQL-Statement DROP_DATABASES bevorzugt werden:
const char buf[BUF] = "DROP DATABASE datenbank";
mysql_real_query(my, buf, strlen(buf));
Mit der Funktion
#include <mysql.h>
my_bool mysql_eof(MYSQL_RES *result) ;
kann festgestellt werden, ob die letzte Zeile einer Ergebnismenge gelesen wurde oder nicht. Es wird allerdings empfohlen, die Funktionen mysql_error() und mysql_errno() zu verwenden, da diese Funktionen diesen Fehler ebenfalls überprüfen und auch mehr Auskunft zur Verfügung stellen als mysql_eof(), die nur einen booleschen Wert zurückgibt und keine Meldung darüber, warum der Fehler auftrat.
Um die Anforderung an den Server zu stellen, dass er die Berechtigungstabelle neu laden soll, kann die Funktion
#include <mysql.h>
int mysql_reload(MYSQL *mysql) ;
verwendet werden. Voraussetzung ist natürlich eine entsprechende Berechtigung dazu. Auch hierbei sei empfohlen, die Funktion mysql_real_query() mit dem SQL-Statement FLUSH_PRIVILEGS zu verwenden.
12.5.14 Neue Funktionen ab Version 4.1.x
Die Weiterentwicklung der MySQL C-API schläft nicht und wird munter vorangetrieben. Schon seit der Version 4.1.x ist wieder bereits eine Menge neuer Funktionen hinzugekommen – und ein Ende ist nicht in Sicht. Leider hat man bei einem Buch das Problem, je nach Verkaufszahlen, höchstens jedes Jahr ein Update (Neuauflage) zu machen. In der folgenden Tabelle finden Sie einen kurzen Überblick über neuere Funktionen ab Version 4.1.x:
Tabelle 12.9
Weitere neuere Funktionen ab MySQL C-API 4.1.x
Funktion
|
Bedeutung
|
mysql_sqlstate()
|
Gibt einen nullterminierten String mit einem SQLSTATE-Fehlercode für den letzten aufgetretenen Fehler zurück. Der Fehlercode beinhaltet fünf Zeichen. So bedeutet '00000', dass kein Fehler aufgetreten ist. Dieser Wert wird von ANSI SQL und ODBC vorgegeben. Für eine Liste der Fehlercodes sei auf die (englische) Dokumentation hingewiesen.
|
mysql_warning_count()
|
Gibt die Anzahl der Warnungen zurück, die bei einer Ausführung eines SQL-Statements erzeugt wurden.
|
mysql_commit()
|
Legt die aktuellen Transaktionen fest.
|
mysql_rollback()
|
Macht die letzte Transaktion rückgängig.
|
mysql_autocommit()
|
Kann gesetzt oder nicht gesetzt werden, damit eine Transaktion automatisch durchgeführt wird.
|
mysql_more_results()
|
Funktion gibt TRUE zurück, wenn eine SQL-Anfrage mehrere Resultate enthält. Falls dies der Fall ist, muss mysql_next_result() aufgerufen werden, um die Menge abzuholen.
|
mysql_next_result()
|
Wenn mehrere Ergebnismengen existieren, liest mysql_next_result() immer das Resultat der nächsten Anfrage und gibt den Status an die Anwendung zurück (0 = es sind noch mehr Resultate vorhanden; -1 = kein Resultat mehr vorhanden).
|
|