Documentation TYPO3 par Ameos |
00001 <?php 00002 /* 00003 V4.80 8 Mar 2006 (c) 2006 John Lim (jlim@natsoft.com.my). All rights reserved. 00004 00005 This is a version of the ADODB driver for DB2. It uses the 'ibm_db2' PECL extension for PHP 00006 (http://pecl.php.net/package/ibm_db2), which in turn requires DB2 V8.2.2. 00007 00008 Tested with PHP 5.1.1 and Apache 2.0.55 on Windows XP SP2. 00009 00010 This file was ported from "adodb-odbc.inc.php" by Larry Menard, "larry.menard@rogers.com". 00011 I ripped out what I believed to be a lot of redundant or obsolete code, but there are 00012 probably still some remnants of the ODBC support in this file; I'm relying on reviewers 00013 of this code to point out any other things that can be removed. 00014 */ 00015 00016 // security - hide paths 00017 if (!defined('ADODB_DIR')) die(); 00018 00019 define("_ADODB_DB2_LAYER", 2 ); 00020 00021 /*-------------------------------------------------------------------------------------- 00022 --------------------------------------------------------------------------------------*/ 00023 00024 00025 class ADODB_db2 extends ADOConnection { 00026 var $databaseType = "db2"; 00027 var $fmtDate = "'Y-m-d'"; 00028 var $fmtTimeStamp = "'Y-m-d, h:i:sA'"; 00029 var $replaceQuote = "''"; // string to use to replace quotes 00030 var $dataProvider = "db2"; 00031 var $hasAffectedRows = true; 00032 00033 var $binmode = DB2_BINARY; 00034 00035 var $useFetchArray = false; // setting this to true will make array elements in FETCH_ASSOC mode case-sensitive 00036 // breaking backward-compat 00037 var $_bindInputArray = false; 00038 var $_genSeqSQL = "create table %s (id integer)"; 00039 var $_autocommit = true; 00040 var $_haserrorfunctions = true; 00041 var $_lastAffectedRows = 0; 00042 var $uCaseTables = true; // for meta* functions, uppercase table names 00043 00044 function ADODB_db2() 00045 { 00046 $this->_haserrorfunctions = ADODB_PHPVER >= 0x4050; 00047 } 00048 00049 // returns true or false 00050 function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) 00051 { 00052 global $php_errormsg; 00053 00054 if (!function_exists('db2_connect')) { 00055 ADOConnection::outp("Warning: The old ODBC based DB2 driver has been renamed 'odbc_db2'. This ADOdb driver calls PHP's native db2 extension."); 00056 return null; 00057 } 00058 // This needs to be set before the connect(). 00059 // Replaces the odbc_binmode() call that was in Execute() 00060 ini_set('ibm_db2.binmode', $this->binmode); 00061 00062 if ($argDatabasename) { 00063 $this->_connectionID = db2_connect($argDatabasename,$argUsername,$argPassword); 00064 } else { 00065 $this->_connectionID = db2_connect($argDSN,$argUsername,$argPassword); 00066 } 00067 if (isset($php_errormsg)) $php_errormsg = ''; 00068 00069 // For db2_connect(), there is an optional 4th arg. If present, it must be 00070 // an array of valid options. So far, we don't use them. 00071 00072 $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; 00073 if (isset($this->connectStmt)) $this->Execute($this->connectStmt); 00074 00075 return $this->_connectionID != false; 00076 } 00077 00078 // returns true or false 00079 function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) 00080 { 00081 global $php_errormsg; 00082 00083 if (!function_exists('db2_connect')) return null; 00084 00085 // This needs to be set before the connect(). 00086 // Replaces the odbc_binmode() call that was in Execute() 00087 ini_set('ibm_db2.binmode', $this->binmode); 00088 00089 if (isset($php_errormsg)) $php_errormsg = ''; 00090 $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; 00091 00092 if ($argDatabasename) { 00093 $this->_connectionID = db2_pconnect($argDatabasename,$argUsername,$argPassword); 00094 } else { 00095 $this->_connectionID = db2_pconnect($argDSN,$argUsername,$argPassword); 00096 } 00097 if (isset($php_errormsg)) $php_errormsg = ''; 00098 00099 $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; 00100 if ($this->_connectionID && $this->autoRollback) @db2_rollback($this->_connectionID); 00101 if (isset($this->connectStmt)) $this->Execute($this->connectStmt); 00102 00103 return $this->_connectionID != false; 00104 } 00105 00106 00107 function ServerInfo() 00108 { 00109 00110 if (!empty($this->host) && ADODB_PHPVER >= 0x4300) { 00111 $dsn = strtoupper($this->host); 00112 $first = true; 00113 $found = false; 00114 00115 if (!function_exists('db2_data_source')) return false; 00116 00117 while(true) { 00118 00119 $rez = @db2_data_source($this->_connectionID, 00120 $first ? SQL_FETCH_FIRST : SQL_FETCH_NEXT); 00121 $first = false; 00122 if (!is_array($rez)) break; 00123 if (strtoupper($rez['server']) == $dsn) { 00124 $found = true; 00125 break; 00126 } 00127 } 00128 if (!$found) return ADOConnection::ServerInfo(); 00129 if (!isset($rez['version'])) $rez['version'] = ''; 00130 return $rez; 00131 } else { 00132 return ADOConnection::ServerInfo(); 00133 } 00134 } 00135 00136 00137 function CreateSequence($seqname='adodbseq',$start=1) 00138 { 00139 if (empty($this->_genSeqSQL)) return false; 00140 $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); 00141 if (!$ok) return false; 00142 $start -= 1; 00143 return $this->Execute("insert into $seqname values($start)"); 00144 } 00145 00146 var $_dropSeqSQL = 'drop table %s'; 00147 function DropSequence($seqname) 00148 { 00149 if (empty($this->_dropSeqSQL)) return false; 00150 return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); 00151 } 00152 00153 /* 00154 This algorithm is not very efficient, but works even if table locking 00155 is not available. 00156 00157 Will return false if unable to generate an ID after $MAXLOOPS attempts. 00158 */ 00159 function GenID($seq='adodbseq',$start=1) 00160 { 00161 // if you have to modify the parameter below, your database is overloaded, 00162 // or you need to implement generation of id's yourself! 00163 $MAXLOOPS = 100; 00164 while (--$MAXLOOPS>=0) { 00165 $num = $this->GetOne("select id from $seq"); 00166 if ($num === false) { 00167 $this->Execute(sprintf($this->_genSeqSQL ,$seq)); 00168 $start -= 1; 00169 $num = '0'; 00170 $ok = $this->Execute("insert into $seq values($start)"); 00171 if (!$ok) return false; 00172 } 00173 $this->Execute("update $seq set id=id+1 where id=$num"); 00174 00175 if ($this->affected_rows() > 0) { 00176 $num += 1; 00177 $this->genID = $num; 00178 return $num; 00179 } 00180 } 00181 if ($fn = $this->raiseErrorFn) { 00182 $fn($this->databaseType,'GENID',-32000,"Unable to generate unique id after $MAXLOOPS attempts",$seq,$num); 00183 } 00184 return false; 00185 } 00186 00187 00188 function ErrorMsg() 00189 { 00190 if ($this->_haserrorfunctions) { 00191 if ($this->_errorMsg !== false) return $this->_errorMsg; 00192 if (empty($this->_connectionID)) return @db2_errormsg(); 00193 return @db2_errormsg($this->_connectionID); 00194 } else return ADOConnection::ErrorMsg(); 00195 } 00196 00197 function ErrorNo() 00198 { 00199 00200 if ($this->_haserrorfunctions) { 00201 if ($this->_errorCode !== false) { 00202 // bug in 4.0.6, error number can be corrupted string (should be 6 digits) 00203 return (strlen($this->_errorCode)<=2) ? 0 : $this->_errorCode; 00204 } 00205 00206 if (empty($this->_connectionID)) $e = @db2_error(); 00207 else $e = @db2_error($this->_connectionID); 00208 00209 // bug in 4.0.6, error number can be corrupted string (should be 6 digits) 00210 // so we check and patch 00211 if (strlen($e)<=2) return 0; 00212 return $e; 00213 } else return ADOConnection::ErrorNo(); 00214 } 00215 00216 00217 00218 function BeginTrans() 00219 { 00220 if (!$this->hasTransactions) return false; 00221 if ($this->transOff) return true; 00222 $this->transCnt += 1; 00223 $this->_autocommit = false; 00224 return db2_autocommit($this->_connectionID,false); 00225 } 00226 00227 function CommitTrans($ok=true) 00228 { 00229 if ($this->transOff) return true; 00230 if (!$ok) return $this->RollbackTrans(); 00231 if ($this->transCnt) $this->transCnt -= 1; 00232 $this->_autocommit = true; 00233 $ret = db2_commit($this->_connectionID); 00234 db2_autocommit($this->_connectionID,true); 00235 return $ret; 00236 } 00237 00238 function RollbackTrans() 00239 { 00240 if ($this->transOff) return true; 00241 if ($this->transCnt) $this->transCnt -= 1; 00242 $this->_autocommit = true; 00243 $ret = db2_rollback($this->_connectionID); 00244 db2_autocommit($this->_connectionID,true); 00245 return $ret; 00246 } 00247 00248 function MetaPrimaryKeys($table) 00249 { 00250 global $ADODB_FETCH_MODE; 00251 00252 if ($this->uCaseTables) $table = strtoupper($table); 00253 $schema = ''; 00254 $this->_findschema($table,$schema); 00255 00256 $savem = $ADODB_FETCH_MODE; 00257 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 00258 $qid = @db2_primarykeys($this->_connectionID,'',$schema,$table); 00259 00260 if (!$qid) { 00261 $ADODB_FETCH_MODE = $savem; 00262 return false; 00263 } 00264 $rs = new ADORecordSet_db2($qid); 00265 $ADODB_FETCH_MODE = $savem; 00266 00267 if (!$rs) return false; 00268 00269 $arr =& $rs->GetArray(); 00270 $rs->Close(); 00271 $arr2 = array(); 00272 for ($i=0; $i < sizeof($arr); $i++) { 00273 if ($arr[$i][3]) $arr2[] = $arr[$i][3]; 00274 } 00275 return $arr2; 00276 } 00277 00278 00279 00280 function &MetaTables($ttype=false) 00281 { 00282 global $ADODB_FETCH_MODE; 00283 00284 $savem = $ADODB_FETCH_MODE; 00285 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 00286 $qid = db2_tables($this->_connectionID); 00287 00288 $rs = new ADORecordSet_db2($qid); 00289 00290 $ADODB_FETCH_MODE = $savem; 00291 if (!$rs) { 00292 $false = false; 00293 return $false; 00294 } 00295 00296 $arr =& $rs->GetArray(); 00297 00298 $rs->Close(); 00299 $arr2 = array(); 00300 00301 if ($ttype) { 00302 $isview = strncmp($ttype,'V',1) === 0; 00303 } 00304 for ($i=0; $i < sizeof($arr); $i++) { 00305 if (!$arr[$i][2]) continue; 00306 $type = $arr[$i][3]; 00307 if ($ttype) { 00308 if ($isview) { 00309 if (strncmp($type,'V',1) === 0) $arr2[] = $arr[$i][2]; 00310 } else if (strncmp($type,'SYS',3) !== 0) $arr2[] = $arr[$i][2]; 00311 } else if (strncmp($type,'SYS',3) !== 0) $arr2[] = $arr[$i][2]; 00312 } 00313 return $arr2; 00314 } 00315 00316 /* 00317 See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2datetime_data_type_changes.asp 00318 / SQL data type codes / 00319 #define SQL_UNKNOWN_TYPE 0 00320 #define SQL_CHAR 1 00321 #define SQL_NUMERIC 2 00322 #define SQL_DECIMAL 3 00323 #define SQL_INTEGER 4 00324 #define SQL_SMALLINT 5 00325 #define SQL_FLOAT 6 00326 #define SQL_REAL 7 00327 #define SQL_DOUBLE 8 00328 #if (DB2VER >= 0x0300) 00329 #define SQL_DATETIME 9 00330 #endif 00331 #define SQL_VARCHAR 12 00332 00333 00334 / One-parameter shortcuts for date/time data types / 00335 #if (DB2VER >= 0x0300) 00336 #define SQL_TYPE_DATE 91 00337 #define SQL_TYPE_TIME 92 00338 #define SQL_TYPE_TIMESTAMP 93 00339 00340 #define SQL_UNICODE (-95) 00341 #define SQL_UNICODE_VARCHAR (-96) 00342 #define SQL_UNICODE_LONGVARCHAR (-97) 00343 */ 00344 function DB2Types($t) 00345 { 00346 switch ((integer)$t) { 00347 case 1: 00348 case 12: 00349 case 0: 00350 case -95: 00351 case -96: 00352 return 'C'; 00353 case -97: 00354 case -1: //text 00355 return 'X'; 00356 case -4: //image 00357 return 'B'; 00358 00359 case 9: 00360 case 91: 00361 return 'D'; 00362 00363 case 10: 00364 case 11: 00365 case 92: 00366 case 93: 00367 return 'T'; 00368 00369 case 4: 00370 case 5: 00371 case -6: 00372 return 'I'; 00373 00374 case -11: // uniqidentifier 00375 return 'R'; 00376 case -7: //bit 00377 return 'L'; 00378 00379 default: 00380 return 'N'; 00381 } 00382 } 00383 00384 function &MetaColumns($table) 00385 { 00386 global $ADODB_FETCH_MODE; 00387 00388 $false = false; 00389 if ($this->uCaseTables) $table = strtoupper($table); 00390 $schema = ''; 00391 $this->_findschema($table,$schema); 00392 00393 $savem = $ADODB_FETCH_MODE; 00394 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 00395 00396 $colname = "%"; 00397 $qid = db2_columns($this->_connectionID, "", $schema, $table, $colname); 00398 if (empty($qid)) return $false; 00399 00400 $rs =& new ADORecordSet_db2($qid); 00401 $ADODB_FETCH_MODE = $savem; 00402 00403 if (!$rs) return $false; 00404 $rs->_fetch(); 00405 00406 $retarr = array(); 00407 00408 /* 00409 $rs->fields indices 00410 0 TABLE_QUALIFIER 00411 1 TABLE_SCHEM 00412 2 TABLE_NAME 00413 3 COLUMN_NAME 00414 4 DATA_TYPE 00415 5 TYPE_NAME 00416 6 PRECISION 00417 7 LENGTH 00418 8 SCALE 00419 9 RADIX 00420 10 NULLABLE 00421 11 REMARKS 00422 */ 00423 while (!$rs->EOF) { 00424 if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) { 00425 $fld = new ADOFieldObject(); 00426 $fld->name = $rs->fields[3]; 00427 $fld->type = $this->DB2Types($rs->fields[4]); 00428 00429 // ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp 00430 // access uses precision to store length for char/varchar 00431 if ($fld->type == 'C' or $fld->type == 'X') { 00432 if ($rs->fields[4] <= -95) // UNICODE 00433 $fld->max_length = $rs->fields[7]/2; 00434 else 00435 $fld->max_length = $rs->fields[7]; 00436 } else 00437 $fld->max_length = $rs->fields[7]; 00438 $fld->not_null = !empty($rs->fields[10]); 00439 $fld->scale = $rs->fields[8]; 00440 $retarr[strtoupper($fld->name)] = $fld; 00441 } else if (sizeof($retarr)>0) 00442 break; 00443 $rs->MoveNext(); 00444 } 00445 $rs->Close(); //-- crashes 4.03pl1 -- why? 00446 00447 if (empty($retarr)) $retarr = false; 00448 return $retarr; 00449 } 00450 00451 function Prepare($sql) 00452 { 00453 if (! $this->_bindInputArray) return $sql; // no binding 00454 $stmt = db2_prepare($this->_connectionID,$sql); 00455 if (!$stmt) { 00456 // we don't know whether db2 driver is parsing prepared stmts, so just return sql 00457 return $sql; 00458 } 00459 return array($sql,$stmt,false); 00460 } 00461 00462 /* returns queryID or false */ 00463 function _query($sql,$inputarr=false) 00464 { 00465 GLOBAL $php_errormsg; 00466 if (isset($php_errormsg)) $php_errormsg = ''; 00467 $this->_error = ''; 00468 00469 if ($inputarr) { 00470 if (is_array($sql)) { 00471 $stmtid = $sql[1]; 00472 } else { 00473 $stmtid = db2_prepare($this->_connectionID,$sql); 00474 00475 if ($stmtid == false) { 00476 $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; 00477 return false; 00478 } 00479 } 00480 00481 if (! db2_execute($stmtid,$inputarr)) { 00482 if ($this->_haserrorfunctions) { 00483 $this->_errorMsg = db2_errormsg(); 00484 $this->_errorCode = db2_error(); 00485 } 00486 return false; 00487 } 00488 00489 } else if (is_array($sql)) { 00490 $stmtid = $sql[1]; 00491 if (!db2_execute($stmtid)) { 00492 if ($this->_haserrorfunctions) { 00493 $this->_errorMsg = db2_errormsg(); 00494 $this->_errorCode = db2_error(); 00495 } 00496 return false; 00497 } 00498 } else 00499 $stmtid = db2_exec($this->_connectionID,$sql); 00500 00501 $this->_lastAffectedRows = 0; 00502 if ($stmtid) { 00503 if (@db2_num_fields($stmtid) == 0) { 00504 $this->_lastAffectedRows = db2_num_rows($stmtid); 00505 $stmtid = true; 00506 } else { 00507 $this->_lastAffectedRows = 0; 00508 } 00509 00510 if ($this->_haserrorfunctions) { 00511 $this->_errorMsg = ''; 00512 $this->_errorCode = 0; 00513 } else 00514 $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; 00515 } else { 00516 if ($this->_haserrorfunctions) { 00517 $this->_errorMsg = db2_stmt_errormsg(); 00518 $this->_errorCode = db2_stmt_error(); 00519 } else 00520 $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; 00521 } 00522 return $stmtid; 00523 } 00524 00525 /* 00526 Insert a null into the blob field of the table first. 00527 Then use UpdateBlob to store the blob. 00528 00529 Usage: 00530 00531 $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); 00532 $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); 00533 */ 00534 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') 00535 { 00536 return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false; 00537 } 00538 00539 // returns true or false 00540 function _close() 00541 { 00542 $ret = @db2_close($this->_connectionID); 00543 $this->_connectionID = false; 00544 return $ret; 00545 } 00546 00547 function _affectedrows() 00548 { 00549 return $this->_lastAffectedRows; 00550 } 00551 00552 } 00553 00554 /*-------------------------------------------------------------------------------------- 00555 Class Name: Recordset 00556 --------------------------------------------------------------------------------------*/ 00557 00558 class ADORecordSet_db2 extends ADORecordSet { 00559 00560 var $bind = false; 00561 var $databaseType = "db2"; 00562 var $dataProvider = "db2"; 00563 var $useFetchArray; 00564 00565 function ADORecordSet_db2($id,$mode=false) 00566 { 00567 if ($mode === false) { 00568 global $ADODB_FETCH_MODE; 00569 $mode = $ADODB_FETCH_MODE; 00570 } 00571 $this->fetchMode = $mode; 00572 00573 $this->_queryID = $id; 00574 } 00575 00576 00577 // returns the field object 00578 function &FetchField($fieldOffset = -1) 00579 { 00580 00581 $off=$fieldOffset+1; // offsets begin at 1 00582 00583 $o= new ADOFieldObject(); 00584 $o->name = @db2_field_name($this->_queryID,$off); 00585 $o->type = @db2_field_type($this->_queryID,$off); 00586 $o->max_length = db2_field_width($this->_queryID,$off); 00587 if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name); 00588 else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name); 00589 return $o; 00590 } 00591 00592 /* Use associative array to get fields array */ 00593 function Fields($colname) 00594 { 00595 if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname]; 00596 if (!$this->bind) { 00597 $this->bind = array(); 00598 for ($i=0; $i < $this->_numOfFields; $i++) { 00599 $o = $this->FetchField($i); 00600 $this->bind[strtoupper($o->name)] = $i; 00601 } 00602 } 00603 00604 return $this->fields[$this->bind[strtoupper($colname)]]; 00605 } 00606 00607 00608 function _initrs() 00609 { 00610 global $ADODB_COUNTRECS; 00611 $this->_numOfRows = ($ADODB_COUNTRECS) ? @db2_num_rows($this->_queryID) : -1; 00612 $this->_numOfFields = @db2_num_fields($this->_queryID); 00613 // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0 00614 if ($this->_numOfRows == 0) $this->_numOfRows = -1; 00615 } 00616 00617 function _seek($row) 00618 { 00619 return false; 00620 } 00621 00622 // speed up SelectLimit() by switching to ADODB_FETCH_NUM as ADODB_FETCH_ASSOC is emulated 00623 function &GetArrayLimit($nrows,$offset=-1) 00624 { 00625 if ($offset <= 0) { 00626 $rs =& $this->GetArray($nrows); 00627 return $rs; 00628 } 00629 $savem = $this->fetchMode; 00630 $this->fetchMode = ADODB_FETCH_NUM; 00631 $this->Move($offset); 00632 $this->fetchMode = $savem; 00633 00634 if ($this->fetchMode & ADODB_FETCH_ASSOC) { 00635 $this->fields =& $this->GetRowAssoc(ADODB_ASSOC_CASE); 00636 } 00637 00638 $results = array(); 00639 $cnt = 0; 00640 while (!$this->EOF && $nrows != $cnt) { 00641 $results[$cnt++] = $this->fields; 00642 $this->MoveNext(); 00643 } 00644 00645 return $results; 00646 } 00647 00648 00649 function MoveNext() 00650 { 00651 if ($this->_numOfRows != 0 && !$this->EOF) { 00652 $this->_currentRow++; 00653 00654 $this->fields = @db2_fetch_array($this->_queryID); 00655 if ($this->fields) { 00656 if ($this->fetchMode & ADODB_FETCH_ASSOC) { 00657 $this->fields =& $this->GetRowAssoc(ADODB_ASSOC_CASE); 00658 } 00659 return true; 00660 } 00661 } 00662 $this->fields = false; 00663 $this->EOF = true; 00664 return false; 00665 } 00666 00667 function _fetch() 00668 { 00669 00670 $this->fields = db2_fetch_array($this->_queryID); 00671 if ($this->fields) { 00672 if ($this->fetchMode & ADODB_FETCH_ASSOC) { 00673 $this->fields =& $this->GetRowAssoc(ADODB_ASSOC_CASE); 00674 } 00675 return true; 00676 } 00677 $this->fields = false; 00678 return false; 00679 } 00680 00681 function _close() 00682 { 00683 return @db2_free_result($this->_queryID); 00684 } 00685 00686 } 00687 ?>