Bevor wir aber richtig loslegen, liste ich schnell noch die Komponenten auf, mit denen im Laufe des folgenden Artikels gearbeitet wird:
- Windows 2000
- Apache 1.3.8
- DB2/NT 8.1
- PHP 4.3.2
- MS-Visual C 6.0
ODBC und der DB2/CLI
PHP nutzt die ODBC-Schnittstelle von DB2 und damit das DB2/CLI-Interface. Voraussetzung für alles folgende ist dementsprechend ein funktionierender DB2-Client auf dem PC oder Server, wo auch PHP installiert ist. Die Installation des DB2-Client soll aber nicht Thema dieses Artikels sein. Wir setzen voraus, dass alles notwendige vorhanden ist, also auch der Zugriff auf die DB2 Beispieldatenbank SAMPLE. Im Zweifelsfalle wenden Sie sich bitte an den bereits häufiger zitierten DBA Ihres Vertrauens, der Ihnen sicher gerne weiter hilft.First Contact oder Hello World
Um eine Datenbankverbindung aufzubauen, benötigen wir die ODBC-Extension von PHP. Diese muss in der PHP.INI nicht extra installiert werden, sondern ist schon per Default aktiviert.hello_world.php:<?php$hDB = odbc_connect('SAMPLE','xx','yy');if (empty($hDB)) {echo "Verbindungsaufbau misslungen..";die();} else {echo "Verbindungsaufbau ok!";odbc_close($hDB);}?>
Listing 1
GetDB2Version.php: (Ausschnitt)[..]/*** SQL-Abfrage aufbauen und ausführen*/echo "<HTML>\n";echo "<HEAD></HEAD>\n";echo "<BODY>\n";echo "<TABLE border='1' >\n";$sQuery = "SELECT * FROM sysibm.sysversionsFOR READ ONLY";// Query ausführen$hResult = odbc_exec($hDB,$sQuery);if ($hResult) {// anzahl Spalten ermitteln$nCol = odbc_num_fields($hResult);// Tabellen Header aufbauenecho "<TR>\n";for ($i=1;$i<=$nCol;$i++) {$sFieldName = odbc_field_name($hResult,$i);echo "<TH>".$sFieldName."</TD>";}echo "</TR>\n";// Alle Datenzeilen einlesenwhile (odbc_fetch_row($hResult)) {// Tabellen Datenzeile aufbauenecho "<TR>\n";for ($i=1;$i<=$nCol;$i++) {$sFieldValue = odbc_result($hResult,$i);echo "<TD>".$sFieldValue."</TD>";}echo "</TR>\n";}[..]
DB2CLI.INI anpassen
Alternativ zur obigen Variante FOR READ ONLY, lässt sich das geschilderte Problem auch mit einer globalen Einstellung in der DB2CLI.INI beheben. Bei der Installation muss dazu die Unterstützung (Optimierung) für Microsoft Visual Basic aktiviert werden. Dabei werden einige Parameter in der DB2CLI.INI gesetzt (Abb. 3). Der wesentliche Parameter ist PATCH2=6, der den Moveable Cursor deaktiviert.DB2CLI.INI (..Ausschnitt..):[SAMPLE]UID=twPATCH2=6PATCH1=1024LOBMAXCOLUMNSIZE=1048575LONGDATACOMPAT=1
Cursortyp SQL_CUR_USE_ODBC
Die dritte und vielleicht eleganteste Variante ist der Einsatz der Option SQL_CUR_USE_ODBC beim Aufbauen der Datenbankverbindung./*** Definition des ODBC Cursortyp*/$hDB = odbc_connect('SAMPLE','xx','yy',SQL_CUR_USE_ODBC);
- SQL_CUR_USE_IF_NEEDED
- SQL_CUR_USE_ODBC
- SQL_CUR_USE_DRIVER
- SQL_CUR_DEFAULT
Stored Procedure - warum ?
Stored Procedures (SP) sind kleine Programme, die in C oder Java geschrieben sind und komplett innerhalb der Datenbank abgelegt (gebunden) sind. SP sind klein und schnell in der Ausführung, da das Prepare - also das Prüfen des SQL-Befehles - nur einmal (während der CREATE PROCEDURE) erfolgt. SPs ermöglichen es, ganze Teile der Geschäftslogik in der Datenbank abzulegen. Knifflige Routinen müssen nur einmal geschrieben werden und andere Programme können einfach mit einem CALL gehaltserhoehung_in_prozent (25) darauf zugreifen. Die Themen COM/DCOM und der aktuelle Hype mit den Web Services lässt grüßen. Auch dort wird versucht, komplexe Abläufe hinter allgemein zugänglichen Schnittstellen (APIs) zu verstecken.Stored Procedure Language SQL
Seit der DB2 Version 7.1 hat IBM auch ein Herz für diejenigen Datenbankentwickler, die zwar perfekt in SQL, aber deren Kenntnisse in C oder JAVA eher ausbaufähig sind. IBM hat als weitere SP-Sprache SQL eingeführt und nennt das ganze nun SQL PL (SQL Procedural Language). Dieses Entgegenkommen wäre sicherlich nicht ohne den kollegialen Druck von Oracle (PL/SQL) und Microsoft (T-SQL) möglich gewesen. Wie dem auch sei, seit der Version 7 kennt DB2 nun SQL als SP-Sprache, und damit sind wir auch beim zentralen Thema angelangt [4].LOGINLOG.DDL:CREATE TABLE tw.loginlog (username CHAR(32) NOT NULL,message VARCHAR(256),createtime timestamp NOT NULL)
CREATE PROCEDURE - Hürden
Vor dem (Programm-)Vergnügen steht meist die Installation, so auch hier, aber zuerst schauen wir uns unsere erste SP mit dem Namen LOGINLOG_SP genauer an.LOGINLOG_SP.DDL:CREATE PROCEDURE loginlog_sp (IN p_sMsg CHAR(32))SPECIFIC loginlog_spLANGUAGE SQLBEGININSERT INTO tw.loginlog(username, message, createtime)VALUES(USER, p_sMsg, CURRENT TIMESTAMP);END!
SQL7032N Die SQL-Prozedur "LOGINLOG_SP" wurde nicht erstellt.Die Diagnosedatei ist "P0124965.log".[...]
DB2_SQLROUTINE_COMPILER_PATH
DB2-Umgebungsvariablen werden am besten mit dem Programm DB2SET.EXE auf Kommandoebene (MS-DOS-Eingabeaufforderung) gesetzt (Abb. 4). Eine DB2-Umgebungsvariable hat nichts mit den sonstigen Windowsvariablen der Art Environment zu tun, sondern wird extra in der Registry verwaltet.
Jetzt aber...
Wenn wir nun versuchen unsere SQL-Procedure LOGINLOG_SP zu erzeugen, meldet sich DB2 - nach einigem Gerumpel auf der Platte - netterweise mit DB20000I Der Befehl SQL wurde erfolgreich ausgeführt. Das bedeutet, wir können unsere SP jetzt ausprobieren. Damit andere Benutzer das neue Paket auch nutzen können, müssen noch die EXECUTE-Rechte auf dem Paket vergeben werden. Bleibt noch das Problem, auf welchen Namen das neue Paket denn eigentlich hört. Denn einfach nurGRANT EXECUTE ON PACKAGE tw.loginlog_sp TO PUBLIC
GRANT EXECUTE ON PACKAGE TW.P3022352 TO PUBLICGRANT EXECUTE ON PROCEDURE TW.LOGINLOG_SP(CHAR()) TO PUBLIC
DB2 COMMAND CENTER:CALL tw.loginlog_sp('Es klappt...')-> Rückgabestatus = 0
DB2 COMMAND CENTER:SELECT * FROM tw.loginlogUSERNAME MESSAGE CREATETIME-------- ------------- ---------------------TW Es klappt... 2004-03-20-13.03.16.9870001 Satz/Sätze ausgewählt.
CallDB2SP.php: (Ausschnitt)/**** DB2 Stored Procedure aufrufen*/$sQuery = "CALL tw.loginlog_sp('PHP kann es auch...')";// Befehl ausführenodbc_exec($hDB,$sQuery);odbc_commit($hDB);[...]
One way street ?
In den Newsgroups kann man immer wieder mal die Frage lesen, wie man denn an die Rückgabewerte einer Stored Procedure käme? Lösungen findet man eher selten, deshalb probieren wir es einfach selber aus:LOGINLOG_COUNT_SP.DDL:CREATE PROCEDURE loginlog_count_sp(OUT p_nCount INT)SPECIFIC loginlog_count_spLANGUAGE SQLBEGINSELECT COUNT (*) INTO p_nCountFROM tw.loginlog;END!
CALL tw.loginlog_count_sp(?)Wert der Ausgabeparameter-------------------------Parametername: P_NCOUNTParameterwert: 2Rückgabestatus = 0
Listing 2
CallDB2CountSP.php: (Ausschnitt)[...]/**** DB2 Stored Procedure aufrufen*/$sQuery = "CALL tw.loginlog_count_sp('?')";// Prepare/Execute wegen HOST Variablen (?)$stmt = odbc_prepare($hDB,$sQuery);if ($stmt) {$nCount = 0;$aParam = array();$aParam['count'] = 0;if (odbc_execute($stmt,$aParam)) { // Line 33// Alle Datenzeilen einlesenwhile (odbc_fetch_row($stmt)) {[...]Warning: SQL error: [IBM][CLI Driver][DB2/NT] SQL0469N Der Parametermodus (IN, OUT oder INOUT) ist für einen Parameter in der Prozedur "LOGINLOG_COUNT_SP" mit dem spezifischen Namen "LOGINLOG_COUNT_SP" (Parameternummer "0", Name "P_NCOUNT") ungültig. SQLSTATE=42886 , SQL state 42886 in SQLExecute in ..\calldb2countsp.php on line 33
|
Flinte ins Korn werfen?
Was tun? Warten bis IBM ihren DB2/ODBC-Treiber verbessert oder sich einen Workaround überlegen. Zweites entspricht doch eher unserem PHP-Magazin-Image. Denn SQL PL bietet eine RESULT SETS-Eigenschaft, die wir uns zu Nutzen machen können [6].GETRESULTSET_SP.DDL:CREATE PROCEDURE GetResultSet()SPECIFIC GetResultSetRESULT SETS 1LANGUAGE SQLBEGIN-- definiere cursor mit ReturnDECLARE c1 CURSOR WITH RETURN FORSELECT count (*) as Anzahl FROM tw.loginlog;-- stellt ein Result-Set zur VerfügungOPEN c1;END!
GetResultSet.php: (Ausschnitt)[...]/**** DB2 Stored Procedure aufrufen*/$sQuery = "CALL tw.GetResultSet()";// CALL ausführen$hResult = odbc_exec($hDB,$sQuery);if ($hResult) {// anzahl Spalten ermitteln$nCol = odbc_num_fields($hResult);[...]
Quintessenz
Die hier gezeigten SQL PL-Beispiele sind zwar recht einfach gehalten, zeigen aber doch einen gangbaren Weg auf, damit PHP auch in dieser Liga mitspielen kann. Die Parameterübergabe ist möglich. Zudem haben wir die Tür genügend weit aufgestoßen, um die dahinter liegenden Möglichkeiten mit dem DB2-Feature SQL PL zu erkennen und schätzen zu lernen. Ab der kommenden DB2 Version 9 wird die etwas hakelige Geschichte mit dem C-Compiler abgelöst werden, dann soll direkt ein entsprechender Bytecode erzeugt werden. Lassen wir uns einfach überraschen.Thomas Wiedmann ist Anwendungs- und Datenbankentwickler für individuelle Softwarelösungen, Autor des Buches DB2 sowie IBM Certified DB2 Administrator. Wenn er nicht gerade Musik hört, liest oder fotografiert, beschäftigt er sich mit Datenbanken und Web-Applikationen. Sie können seine Webseite unter http://www.twiedmann.de einsehen.
Literatur und Links
- [1] PHP Homepage: www.php.net/
- [2] WikiPedia: de.wikipedia.org/wiki/php
- [3] Heiko Müller, Mit den Großen spielen. PHP-Magazin 04.2003, Seite 68ff
- [4] Paul Yip: DB2 SQL Procedural Language for Linux, Unix, and Windows. IBM Press 2003. ISBN 0-13-100772-6
- [5] IBM Web-Seite: www-1.ibm.com/support/docview.wss?uid=swg21053313
- [6] IBM Web-Seite: www-106.ibm.com/developerworks/db2/library/techarticle/0301liu/0301liu.html



