URL dieses Artikels:

zu Ausgabe: 2.2004
Heute schon (ab)gefragt?
MySQL API in C++ - eine komfortable Möglichkeit, MySQL-Daten in C++ zu verwenden
von Tobias Wassermann
Derjenige, der damals die Bezeichnung Paradox für die gleichnamige Datenbank ersann, war ziemlich gewitzt - denn Datenbanken sind schlicht paradox. Es scheint wie folgt: Je besser die Datenbank, desto komplizierter ist der Zugriff. Doch das wollen wir ändern.

Sicher, es gibt Schlimmeres, als die Zugriffe auf eine Datenbank. Besonders MySQL in Verbindung mit der MySQL C API gestaltet sich noch sehr human. Allerdings gibt es auch Erfreulicheres. Damit das deutlich wird, verinnerlichen wir uns doch einmal, was alles für einen Datenbankzugriff notwendig ist (zum Beispiel eine Abfrage mit Rückgabe - also ein SELECT): Da wäre der Verbindungsaufbau, das Senden der Abfrage an den Server, das Lesen des Ergebnisses und anschließend das Schließen der Verbindung. Das war jetzt freilich noch die triviale Version - denn das Lesen des Ergebnisses strotz beispielsweise nur vor einzelnen Arbeitsschritten, etwa der Ermittlung, ob überhaupt ein Ergebnis zurückgeliefert wurde, wie viele Spalten und Reihen das Ergebnis umfasst, das Lesen mittels mysql_fetch_row und mysql_fetch_field. Ein kleines Beispiel soll das ganze vom Codeaufwand her zeigen:

MYSQL mysql_con;
MYSQL_RES* mysql_result;
MYSQL_ROW mysql_row;
mysql_init(&mysql_con);
mysql_real_connect(&mysql_con, localhost, user, password, database, 0, NULL, 0);
mysql_query(&mysql_con, select Name from Personal where PersonalID=1);
mysql_result=mysql_store_result(&mysql_con);
mysql_row=mysql_fetch_row(mysql_result);
printf(Name: %s, mysql_row[0]);
mysql_free_result(&mysql_con);
mysql_close(&mysql_con);

Dieses Beispiel war nun einfach gehalten - hier wird nur eine einzige Spalte bei einer Rückgabe von einem Datensatz gelesen, von der komplett abwesenden Fehlerbehandlung einmal zu schweigen; um so umfangreicher die zu lesenden Datenmengen, desto komplexer wird der Code freilich. Daraus ist eines ganz klar erkennbar: So ein einziger Vorgang zum Lesen eines Abfrageergebnisses ist aufwändig. Handelt es sich um eine Anwendung, die nur wenig auf eine MySQL-Datenbank zugreift, so mag dies noch angehen. Doch spätestens bei größeren Applikationen, die häufig auf die Datenbank zugreifen, nimmt das Abenteuer seinen Lauf, und man wünscht sich doch, das ganze etwas einfacher handhaben zu können.

Die MySQL API ist geboren
Doch bevor man nun selbst etwas entwickelt, schaut man sich besser nach bestehendem um (ja, hier geht es auch um die alte, leidliche Geschichte von der nicht notwendigen Neuerfindung des Rades). Diese Abstrahierung der MySQL-Zugriffe ist nicht ganz so trivial, wie sie erscheint. Möchte man zum Beispiel auch eine einheitliche und nicht zu aufwändige Fehlerbehandlung und -ausgabe erreichen. Dafür gibt es nun die MySQL API. Sie wurde entwickelt, um Zugriffe auf MySQL zu vereinfachen, aber dennoch auch sehr komplexe Abfragen zu ermöglichen.


Doch was kann die API denn nun? Zum einen ist sie natürlich fähig, mit MySQL und Daten umzugehen - sie kann sowohl lesende Abfragen (SELECT) absetzen und verarbeiten als auch schreibende SQL-Statements (INSERT, UPDATE) bearbeiten. Zum anderen bietet diese API etwas, was man bisher wahrscheinlich nicht hatte: Eine automatisierte Fehlerbehandlung und -ausgabe, die auf Callbacks zurückgreift. Bisher wurde wohl jeder Fehler von Hand ausgelesen, dann wurde eventuell dazu noch von irgendwoher ein passender Fehlertext ermittelt und dann ein Fehler ausgegeben. Dazu später mehr.


Obendrein kann man mit Hilfe der MySQL API übersichtlich SQL-Statements bilden, ohne dass man sich - bei Statements mit flexiblen Werten aus Variablen - in Format-Strings von sprintf a la SELECT %s FROM %s INNER JOIN %s ON %s.%s=%s.%s WHERE %s=%u AND %s<%u verliert. Doch genug der Schwärmereien - ab jetzt geht es an die Fakten: Wie genau das nun alles funktioniert, gibt's jetzt mit Beispielen als Dreingabe.


Noch eins vorweg - damit auch jeder gleich mitmachen kann, findet man im Kasten Wie geht`s los? eine Beschreibung, wie man die API in sein C++-Projekt integriert.


<table width="450" border="0" cellspacing="0" cellpadding="1">
<tr bgcolor="#000000">
<td>
<table width="100%" border="0" cellspacing="0" cellpadding="4">
<tr bgcolor="#FFFFFF">
<td>

Wie geht's los?
Die MySQL API zu integrieren ist recht einfach.
Zum einen muss - wie im entsprechenden Kasten beschrieben - MSGINFO
integriert werden. Zusätzlich gibt es nur noch das zu tun:
  • Inkludieren Sie die MySQLAPI.h (Wichtig: Unbedingt die MySQLAPI.h aus dem Release-Verzeichnis der API verwenden!) in die Quelldateien, die mit der API arbeiten werden
  • Nehmen Sie die MySQLAPI.lib aus dem Release-Verzeichnis in Ihr Projekt auf
  • Legen Sie bei der Distribution der Anwendung die libMySQL.dll bei - zum Testen sollte diese im Projektverzeichnis liegen

Danach kann es ganz einfach mit der Nutzung der API losgehen. </td>
</tr>
</table></td>
</tr>
</table>

Zuallererst geht es an die Initialisierung. Die Deklaration dafür lautet UInt MySQLInit(Char *DBServer, Char *DBName, Char *DBUser, Char *DBPassword, MSGINFO *MsgInfo). MySQLInit initialisiert - wie der Name schon sagt - die API. Diese Funktion trifft vor allem einige Vorbereitungen, die für die spätere Arbeit der anderen Funktionen notwendig sind. Vor allem sorgt dieser Aufruft dafür, dass die Verbindungsinformationen für spätere Neukonnektierungen innerhalb für die API dauerhaft (bis zum MySQLDeInit) zur Verfügung stehen. Denn so müssen Sie nicht immer wieder vom Entwickler übergeben werden und die API kann sich so voll und ganz um die Datenbankverbindung kümmern, ohne dass die eigentliche Anwendung damit Scherereien hat. Zu den Parametern: Hierbei muss neben MsgInfo der MySQL-Server (DBServer), der MySQL-Benutzer (DBUser), das MySQL-Passwort (DBPassword) sowie die zu selektierende Datenbank (DBName) angegeben werden. Bis auf das Passwort darf keine der Variablen NULL sein. Hinter MsgInfo steht eine Struktur, die für die Fehlerbehandlung und vor allem automatische Fehlerausgabe sehr wichtig ist, mithilfe von MsgInfo wird die Ausgabe von MessageBoxes automatisiert, und der Programmierer muss nur noch die Rückgabewerte der MySQL API-Funktionen abfragen und daran entscheiden, wie seine Anwendung fortfährt. Er muss sich jedoch nicht damit rumplagen, welcher Fehler denn nun vom MySQL Server zurückgegeben worden ist. Damit hier nun nicht zu sehr vom Thema abgewichen wird, steht alles Wichtige zum Thema Fehlerbehandlung im Kasten MsgInfo - ganz einfach mitgeteilt. MySQLInit liefert bei erfolgreicher Initialisierung (d. h. der Speicher für die Verbindungsdaten konnte erfolgreich reserviert werden und die MySQL API konnte eine Verbindung zur Datenbank aufbauen) 1 zurück, bei Fehlern 0.


<table width="450" border="0" cellspacing="0" cellpadding="1">
<tr bgcolor="#000000">
<td>
<table width="100%" border="0" cellspacing="0" cellpadding="4">
<tr bgcolor="#FFFFFF">
<td>

MsgInfo - Fehler geschickt behandelt
Die MsgInfo-Library kann selbständig Fehler in einer MessageBox ausgeben. Der Programmierer kann zwischen verschiedenen Fehlertypen (und damit verschiedenen Ausgaben) wählen: Fehler, SysFehler, Info, SysInfo, Warnung, SysWarnung, Debug, SysDebug. Dabei wird der vom Entwickler übergebene Fehlertext ausgegeben (bei den Sys-Funktionen zusätzlich auch der Fehlertext, den GetLastError in Verbindung mit FormatMessage liefert). Optional kann auch noch die Fehlernummer mit ausgegeben werden.


Der Entwickler muss nur einmalig die CallBack-Funktion (die den Fehler ausgibt) und das Window-Handle angeben, jede Funktion, die dann die MSGINFO-Struktur übergeben bekommt kann so recht simpel einen Fehler oder einen Hinweis an den Benutzer weitergeben, ohne sich das Window-Handle suchen zu müssen (dies macht vor allem eine Trennung von GUI und Datenverarbeitung einfach).


In der Praxis bedeutet dies, dass Msg.cpp und Msg.h sowie MyTypes.h dazu in das aktuelle Projekt integriert und inkludiert werden müssen; zusätzlich muss nur noch die shlwapi.lib (Standard-Bibliothek) verlinkt sein (in VC++ über Projekteinstellungen unter dem Punkt Linker)- danach kann MSGINFO wie folgt definiert werden:

MSGINFO MsgInfo;
MsgInfo->Parameter=hWnd;
MsgInfo->MsgCallBackProc=MsgAusgabe;

Danach kann diese Struktur recht simpel weitergegeben werden (falls durch die Funktion erforderlich natürlich auch als Pointer). Diese Funktionalität wird mit Absicht direkt als Source in ein Projekt integriert - denn so kann man für jedes Projekt die MsgAusgabe-Funktion anpassen. In der Standardversion wird neben dem Fehlertext auch die Quelldatei mit der Codezeile in der der Fehler auftrat ausgegeben, damit die Suche nach einem evtl. vorhandenen Fehler erleichtert wird. Ein paar Beispiele zum Aufruf gibt es im Download der MySQL API.
</td>
</tr>
</table></td>
</tr>
</table>

Wenn später das Programm entladen wird oder einer der Parameter von MySQLInit geändert werden soll, so muss vorher MySQLDeInit() aufgerufen werden. So wird der reservierte Speicher freigegeben und eine eventuell noch bestehende Verbindung zur Datenbank beendet. Diese Funktion hat übrigens keinen Rückgabewert.


Munter geht's weiter - zu erst möchten wir einmal in die Datenbank schreiben. Dazu gibt es die Funktion UInt MySQLWrite(Char * querystring, UInt *LastID, MSGINFO *MsgInfo). Hinter querystring steckt freilich die auszuführende Abfrage, MsgInfo ist wohl auch mehr als bekannt. LastID soll ein Pointer auf einen UInt (unsigned int) sein, der Pointer soll aber nur bei einem INSERT INTO-Statement gesetzt sein, bei dem ein Auto Increment-Feld durch MySQL gesetzt wurde. LastID enthält dann die eingefügte ID. Handelt es sich weder um eine INSERT INTO-Abfrage noch um eine mit Auto Increment-Feld, sollte als zweiter Parameter NULL übergeben werden.

Lies mich - aber wie?
Beim Lesen der Daten aus der MySQL-Datenbank wird in zwei Funktionen unterschieden. Welche der beiden genutzt werden sollte hängt ganz von der Abfrage ab, die man an den MySQL Server absetzt bzw. was man mit dem Ergebnis machen möchte. Möchte man das Ergebnis einer komplexen Abfrage auslesen und mit diesen Daten in C++ verwenden, so wird MySQLRead_Array verwendet. Dabei versteht sich unter komplex ein SQL-Statement, das mehrere Datenzeilen und/oder mehrere Datenspalten zurückliefert. Liest man hingegen nur ein Feld und eine Zeile aus (etwa weil man eine ganz bestimmte ID ermitteln möchte, oder man nutzt eine SQL-Funktion a la SUM) kann man getrost MySQLQuery_String nutzen.


Das Ganze nun mal im Detail. Beginnen wir simpel - mit MySQLQuery_String. Der Aufruf sieht hier wie folgt aus: UInt MySQLQuery_String(Char *querystring, Char **Result, MSGINFO* MsgInfo). MsgInfo ist nun schon bekannt, diese Art der Fehlerbehandlung zieht sich durch die gesamte MySQL API. querystring ist schlichtweg das SQL-Statement, das an den MySQL Server gesendet werden soll. Das sollte unbedingt nur eine Abfrage pro Funktionsaufruf sein. Result ist ein Pointer auf einen Char *, dieser sollte in der aufrufenden Funktion nur definiert worden ein, es sollte allerdings noch keine Speicherreservierung vorgenommen worden sein, dies übernimmt die API dann (denn die aufrufende Funktion kann nur in den seltensten Fällen vorher wissen, wie viel Speicher für das Rückgabeergebnis notwendig ist. Die Speicherfreigabe sollte auch die API übernehmen, dazu weiter unten mehr (dafür gibt es freilich eine eigene Funktion). Zurück zu Result - wenn die Abfrage erfolgreich war, wird hier die Rückgabe des MySQL Servers abgelegt. Wird nur eine Spalte zurückgeliefert, so enthält Result diese 1:1. Werden hingegen zwei oder mehr Spalten zurückgegeben, ist dies auch kein Problem, dann werden die Spalten getrennt durch ein | in der Variable gespeichert. Allerdings gilt es eines zu berücksichtigen: MySQLQuery_String kann zwar mehrere Spalten zurückliefern, allerdings wird nur die erste Zeile des Abfrageergebnisses berücksichtigt, alles andere fällt unter den Tisch. Das ist durchaus Absicht - man könnte dies zwar lösen, doch diese Funktion ist extra dazu gedacht, um nur mal eben einen Wert aus einer Datenbank auslesen zu können. Vor dem abschließenden Beispiel noch eine kurze Bemerkung zum Rückgabewert: Diese Funktion liefert bei Erfolg 1 zurück, bei Misserfolg 0 (eine entsprechend detaillierte Fehlermeldung bekommt der Benutzer durch MsgInfo angezeigt). Nun noch die Funktion in der Praxis (im Beispiel wird MsgInfo der Funktion bereits übergeben, also nicht wundern, wenn MsgInfo aus dem Nichts kommt):

Char *Result;
if(MySQLInit(server, test_db, user1, password_user1, MsgInfo))
{
if(MySQLQuery_String(SELECT SUM(gehalt) FROM personal, &Result, MsgInfo))
{
printf(Gehälter im Monat: %s, Result);
MySQLFreeResultset(NULL, Result); // Reservierten Speicher freigeben
}
MySQLDeInit();
}

Dieses Beispiel setzt voraus, das die API innerhalb dieser Funktion initialisiert und deinitialisiert wird. Nutzt man die API auch noch außerhalb dieser Funktion und wird MySQLInit schon an anderer Stelle aufgerufen kann man freilich auf die Initialisierung und Deinitialisierung verzichten. Noch etwas wichtiges: Die Lesefunktionen (MySQLQuery_String und MySQLQuery_Array) liefern die Daten aus der MySQL-Datenbank generell als String zurück. Jedoch kann man einen String recht einfach mit beispielsweise itoa, itof und Konsorten recht einfach in den Datentyp wandeln, den man benötigt.


Nun geht es ein wenig komplexer einher: MySQLQuery_Array ist an der Reihe. Diese Funktion soll Abfragen abdecken, die mehrere Datenreihen und -spalten zurückliefern. Der Aufruf ist vergleichbar mit dem der Schwesterfunktion: UInt MySQLQuery_Array(Char *querystring, DBRESULT **Result, MSGINFO *MsgInfo). Das einzig neue ist die Struktur DBRESULT; diese beinhaltet die abgefragten Daten. Man könnte diese Struktur nun groß und breit erklären, ohne dass sich nach dieser Erklärung wohl jemand vorstellen kann, was denn nun eigentlich gemeint ist. Deswegen geht es ab in das kalte Wasser - hier ist die Struktur:

typedef struct _DBRESULT
{
UInt countRows;
UInt countFields;
ROWDATA ** Rows;
COLDATA ** Fields;
} DBRESULT;

Die Hälfte dessen, was da steht dürfte klar sein: countRows steht für die in der Struktur enthaltenen Datenzeilen und countFields für die Datenspalten (diese Angaben variieren selbstverständlich anhand der abgesetzten Abfrage). Nun gibt es einfach noch die beiden anderen Strukturen auf das Gemüt:

typedef struct _COLDATA
{
Char* text;
} COLDATA;
typedef struct _ROWDATA
{
Char** text;
} ROWDATA;

Mancher fragt sich nun, was das denn nun sei. COLDATA beinhaltet schlichtweg den Namen einer Spalte im Format Tabelle.Spalte bzw. Alias. Mit der Struktur ROWDATA wird es schon etwas komplizierter - hier enthält text die Daten aus einer Reihe in einer bestimmten Spalte. Das schaut jetzt noch komisch aus - deswegen aus der Praxis ein kurzes Beispiel: Es soll die Spalte (der Wert, nicht der Spaltenname) 0 aus der Zeile 1 ausgelesen werden. Das sieht dann wie folgt aus: Result->Rowdata[1]->text[0]. So hat man recht einfach und dennoch strukturiert Zugriff auf die Daten.


Die Spaltennamen befinden sich, wie bereits erwähnt in der Struktur COLDATA. Damit man sich nicht jedes Mal seinen gesuchten Tabellen- und Spaltennamen mit sprintf zusammensetzen muss, um ihn dann mit dem Inhalt von text in COLDATA per _stricmp zu vergleichen gibt es die Funktion Bool VergleicheTabellenSpalteMitName(Char* Tablename, Char* Colname, Char* Vergleichsname). Diese liefert True zurück, falls der Vergleichsname (der Wert aus COLDATA) mit dem Muster Tablename.Colname übereinstimmt. Dies kann man dann recht bequem in eine for-Schleife integrieren und so sicherstellen, dass man immer die Daten aus der richtigen Spalte ausliest. Bevor es nun zu einem praktischem Beispiel geht - noch die üblichen und eindringlichen Hinweise. DBResult sollte wieder nur definiert werden, den Speicher reserviert die API wie gewohnt selbst (Listing 1).



Listing 1

DBRESULT *Result;
UInt i, ColNo_Name, ColNo_Vorname, ColNo_Strasse, ColNo_PLZ, ColNo_Ort;
if(MySQLInit(server, test_db, user1, password_user1, MsgInfo))
{
if(MySQLQuery_Array(SELECT Name, Vorname, Strasse, PLZ, Ort FROM personal, &Result, MsgInfo))
{
for(i=0; i<Result->countFields; i++)
{
if(VergleicheTabellenSpalteMitName(personal,Name,Result->Fields[i]->text))
ColNo_Name=i;
else if(VergleicheTabellenSpalteMitName(personal,Vorname,Result->Fields[i]->text))
ColNo_Vorname=i;
else if(VergleicheTabellenSpalteMitName(personal,Strasse,Result->Fields[i]->text))
ColNo_Strasse=i;
else if(VergleicheTabellenSpalteMitName(personal,PLZ,Result->Fields[i]->text))
ColNo_PLZ=i;
else if(VergleicheTabellenSpalteMitName(personal,Ort,Result->Fields[i]->text))
ColNo_Ort=i;
}
for(i=0; i<Result->countRows; i++)
{
printf(%s%s, %s, %s %s, Result->Rows[i]->text[ColNo_Vorname], Result->Rows[i]->text[ColNo_Name], Result->Rows[i]->text[ColNo_Strasse], Result->Rows[i]->text[ColNo_PLZ], Result->Rows[i]->text[ColNo_Ort]);
}
MySQLFreeResultset(&Result, NULL); // Reservierten Speicher freigeben
}
MySQLDeInit();
}

Selbstverständlich muss man die Daten nicht immer mit printf ausgeben, sondern kann sie auch in eine eigene Datenstruktur zur weiteren Verarbeitung übernehmen.
Dann gibt es auch noch eine Funktion, die bereits in den Beispielen verwendet worden ist: MySQLFreeResultset(DBRESULT **Result, Char *StringResult). Damit lässt sich der durch MySQLQuery_Array und MySQLQuery_String reservierte Speicher freigeben. Für jeden Parameter kann auch NULL eingesetzt werden, sodass man mit MySQLFreeResultset(NULL, Result) nur ein Char * (resultierend aus MySQLQuery_String) freigegeben wird.


Jetzt geht es noch an das drum herum, das mit dieser API daher kommt. Da gibt es zum Beispiel das Problem der flexiblen SQL-Statements in C++. Wer nicht genau weiß, was gemeint ist, der führe sich folgendes harmlose SELECT-Statement zu Gemüte:

sprintf(buf, SELECT %s.%s, %s.%s, %s.%s, sum(%s.%s) FROM %s INNER JOIN %s ON %s.%s=%s.%s INNER JOIN %s on %s.%s=%s.%s WHERE %s.%s=%u GROUP BY %s.%s, Tbl_Bestellung, Col_Bestellung_Nr, Tbl_Bestellposten, Col_Bestellposten_ArtNr, Tbl_Bestellposten, Col_Bestellposten_Bezeichnung, Tbl_Lieferung, Col_Lieferung_Anzahl, Tbl_Bestellung, Tbl_Bestellposten, Tbl_Bestellung, Col_Bestellung_Nr, Tbl_Bestellposten, Col_Bestellposten_BestellungNr, Tbl_Lieferung, Tbl_Bestellposten, Col_Bestellposten_ArtNr, Tbl_Lieferung, Col_Lieferung_ArtNr, Tbl_Bestellposten, Col_Bestellposten_ArtNr, ArtikelNummer, Tbl_Bestellposten, Col_Bestellposten_ArtNr); 

Wer findet das noch lustig? Wahrscheinlich niemand. Dabei ist da noch eine recht einfache Abfrage, die schlichtweg alle Daten eines bestellten Artikels zusammenbringt, und schlichtweg ausgibt, wann es in welcher Bestellung für diesen Artikel einen Auftrag gab, und welche Anzahl hier bereits geliefert wurde. Für den Endbenutzer ist das die einfachste (und unter Umständen auch die wichtigste!) Frage der Welt: Wann habe ich von Artikel X wie viel erhalten? Für den Entwickler ist das nicht mehr einfach - sondern komplex. Aber halt! wird nun mancher laut rufen. Man muss doch nicht sämtliche Tabellen- und Spaltennamen in Variablen oder Konstanten auslagern. Sicher - man muss es nicht, und wenn man es nicht macht, wird natürlich die sprintf-Anweisung um einiges vereinfacht. Doch es dürfte stets besser sein, wenn man die Datenbankeinstellungen soweit wie möglich parametrisiert. Es ist immer möglich, dass sich die Tabellen- oder Spaltennamen einmal ändern müssen, und in diesem Fall muss man die Änderung bei dieser Parametrisierung nur an einer Stelle vornehmen (und ganz nebenbei kann man so z. B. dem Kunden über eine ini-Datei die Möglichkeit geben, die Tabellen- und Spaltenbezeichnung selbst festzulegen).

Elementares
Doch selbst wenn man die Namen der Spalten und Tabellen hart hinein coded kann so eine Abfrage - man denke sich nur einige WHERE-Bedingungen, die mit Variablen aus der C++-Anwendung zusammenhängen - sehr komplex und unübersichtlich werden. Und jedes Mal, wenn an einer solchen Abfrage etwas geändert werden soll, muss man sich erst in diese sprintf-Anweisung hineindenken, und bei komplexeren Format-Strings kann man hier leicht durcheinander kommen. Es fehlt schlicht die Möglichkeit, eine Abfrage (sei es nun ein SELECT, ein UPDATE oder ein INSERT) flexibel zusammensetzen zu können, und dabei dennoch einen recht guten Über- und vor allem sofortigen Einblick zu behalten. Die MySQL API wäre nicht die MySQL API, wenn sie da nicht auch etwas als Lösung bieten könnte: Dafür gibt es die Struktur QUERYSTRUCT sowie die dazugehörigen Funktionen ReserveElementList, AddElementToList, MergeQuery und FreeElementList. Klingt erst einmal alles seltsam. Doch mit einem Beispiel sollte alles etwas klarer werden:Durch diese Befehlsreihe wird ein SQL-Statement erzeugt, das einen Großteil seines Inhaltes aus Variablen und Konstanten bezieht. Diese Art der Zusammensetzung spart zwar nicht gerade an Codezeilen, doch ist es um einiges übersichtlicher als ein sprintf, bei dem fünf Mal %s hintereinander steht. Der einzige (derzeitige) Nachteil gegenüber sprintf: Es können nur Strings als Parameter übergeben werden. Doch was verbirgt sich nun hinter diesen Funktionen? ReserveElementList(QUERYSTRUCT *Elemente) und FreeElementList(QUERYSTRUCT *Elemente) reservieren Speicher für die Struktur und geben ihn wieder frei.


AddElementToList(QUERYSTRUCT *Elemente, Char *String, UInt NoLeadingSpace) fügt ein neues Element an. Man darf sich QUERYSTRUCT als eine Ansammlung von einzelnen Teilen (Elementen) vorstellen, die erst mit der Funktion MergeQuery(QUERYSTRUCT *Elemente, Char **Query) zu einem String zusammengesetzt werden. Der Parameter NoLeadingSpace ist dabei noch eine kleine Besonderheit: Wird hier 0 übergeben, findet sich in dem SQL-Statement später vor dem betroffenem Element ein Leerzeichen - steht hier 1 fällt dieses Leerzeichen weg.

Kleinigkeiten
Nun noch eine Spielerei: UInt MySQLCheckConnect(). Diese Funktion gibt schlicht 1 zurück, falls noch eine aktive Verbindung zum MySQL-Datenbankserver besteht. Dies ist im Zusammenhang mit Verwendung der API mehr eine Spielerei, denn falls keine Verbindung aktiv ist, baut die API vor jedem Zugriff diese Verbindung neu auf. MySQLCheckConnect dient also allenfalls der Feststellung, ob der Server persistente Verbindungen unterstützt oder ob hier nach einer zeitlichen Frist einfach alle ruhenden Verbindung gekappt werden.



Links und Literatur


© 2004 Software & Support Verlag GmbH. Vervielfältigung nur mit Genehmigung des Verlags. Fragen?