Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   1  <?php
   2  /**
   3    @version   v5.20.16  12-Jan-2020
   4    @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
   5    @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
   6  
   7    This is a version of the ADODB driver for DB2.  It uses the 'ibm_db2' PECL extension
   8    for PHP (http://pecl.php.net/package/ibm_db2), which in turn requires DB2 V8.2.2 or
   9    higher.
  10  
  11    Originally tested with PHP 5.1.1 and Apache 2.0.55 on Windows XP SP2.
  12    More recently tested with PHP 5.1.2 and Apache 2.0.55 on Windows XP SP2.
  13  
  14    This file was ported from "adodb-odbc.inc.php" by Larry Menard, "larry.menard#rogers.com".
  15    I ripped out what I believed to be a lot of redundant or obsolete code, but there are
  16    probably still some remnants of the ODBC support in this file; I'm relying on reviewers
  17    of this code to point out any other things that can be removed.
  18  */
  19  
  20  // security - hide paths
  21  if (!defined('ADODB_DIR')) die();
  22  
  23    define("_ADODB_DB2_LAYER", 2 );
  24  
  25  /*--------------------------------------------------------------------------------------
  26  --------------------------------------------------------------------------------------*/
  27  
  28  
  29  
  30  
  31  
  32  class ADODB_db2 extends ADOConnection {
  33  	 var $databaseType = "db2";
  34  	 var $fmtDate = "'Y-m-d'";
  35  	 var $concat_operator = '||';
  36  
  37  	 var $sysTime = 'CURRENT TIME';
  38  	 var $sysDate = 'CURRENT DATE';
  39  	 var $sysTimeStamp = 'CURRENT TIMESTAMP';
  40  
  41  	 var $fmtTimeStamp = "'Y-m-d H:i:s'";
  42  	 var $replaceQuote = "''"; // string to use to replace quotes
  43  	 var $dataProvider = "db2";
  44  	 var $hasAffectedRows = true;
  45  
  46  	 var $binmode = DB2_BINARY;
  47  
  48  	 var $useFetchArray = false; // setting this to true will make array elements in FETCH_ASSOC mode case-sensitive
  49  	 	 	 	 	 	 	 	 // breaking backward-compat
  50  	 var $_bindInputArray = false;
  51  	 var $_genIDSQL = "VALUES NEXTVAL FOR %s";
  52  	 var $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s NO MAXVALUE NO CYCLE";
  53  	 var $_dropSeqSQL = "DROP SEQUENCE %s";
  54  	 var $_autocommit = true;
  55  	 var $_haserrorfunctions = true;
  56  	 var $_lastAffectedRows = 0;
  57  	 var $uCaseTables = true; // for meta* functions, uppercase table names
  58  	 var $hasInsertID = true;
  59  
  60  
  61      function _insertid()
  62      {
  63          return ADOConnection::GetOne('VALUES IDENTITY_VAL_LOCAL()');
  64      }
  65  
  66  	function __construct()
  67  	 {
  68  	 	 $this->_haserrorfunctions = ADODB_PHPVER >= 0x4050;
  69  	 }
  70  
  71  	 	 // returns true or false
  72  	function _connect($argDSN, $argUsername, $argPassword, $argDatabasename)
  73  	 {
  74  	 	 if (!function_exists('db2_connect')) {
  75  	 	 	 ADOConnection::outp("Warning: The old ODBC based DB2 driver has been renamed 'odbc_db2'. This ADOdb driver calls PHP's native db2 extension which is not installed.");
  76  	 	 	 return null;
  77  	 	 }
  78  	 	 // This needs to be set before the connect().
  79  	 	 // Replaces the odbc_binmode() call that was in Execute()
  80  	 	 ini_set('ibm_db2.binmode', $this->binmode);
  81  
  82  	 	 if ($argDatabasename && empty($argDSN)) {
  83  
  84  	 	 	 if (stripos($argDatabasename,'UID=') && stripos($argDatabasename,'PWD=')) $this->_connectionID = db2_connect($argDatabasename,null,null);
  85  	 	 	 else $this->_connectionID = db2_connect($argDatabasename,$argUsername,$argPassword);
  86  	 	 } else {
  87  	 	 	 if ($argDatabasename) $schema = $argDatabasename;
  88  	 	 	 if (stripos($argDSN,'UID=') && stripos($argDSN,'PWD=')) $this->_connectionID = db2_connect($argDSN,null,null);
  89  	 	 	 else $this->_connectionID = db2_connect($argDSN,$argUsername,$argPassword);
  90  	 	 }
  91  
  92  	 	 // For db2_connect(), there is an optional 4th arg.  If present, it must be
  93  	 	 // an array of valid options.  So far, we don't use them.
  94  
  95  	 	 $this->_errorMsg = @db2_conn_errormsg();
  96  	 	 if (isset($this->connectStmt)) $this->Execute($this->connectStmt);
  97  
  98  	 	 if ($this->_connectionID && isset($schema)) $this->Execute("SET SCHEMA=$schema");
  99  	 	 return $this->_connectionID != false;
 100  	 }
 101  
 102  	 // returns true or false
 103  	function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename)
 104  	 {
 105  	 	 if (!function_exists('db2_connect')) return null;
 106  
 107  	 	 // This needs to be set before the connect().
 108  	 	 // Replaces the odbc_binmode() call that was in Execute()
 109  	 	 ini_set('ibm_db2.binmode', $this->binmode);
 110  
 111  	 	 $this->_errorMsg = '';
 112  
 113  	 	 if ($argDatabasename && empty($argDSN)) {
 114  
 115  	 	 	 if (stripos($argDatabasename,'UID=') && stripos($argDatabasename,'PWD=')) $this->_connectionID = db2_pconnect($argDatabasename,null,null);
 116  	 	 	 else $this->_connectionID = db2_pconnect($argDatabasename,$argUsername,$argPassword);
 117  	 	 } else {
 118  	 	 	 if ($argDatabasename) $schema = $argDatabasename;
 119  	 	 	 if (stripos($argDSN,'UID=') && stripos($argDSN,'PWD=')) $this->_connectionID = db2_pconnect($argDSN,null,null);
 120  	 	 	 else $this->_connectionID = db2_pconnect($argDSN,$argUsername,$argPassword);
 121  	 	 }
 122  
 123  	 	 $this->_errorMsg = @db2_conn_errormsg();
 124  	 	 if ($this->_connectionID && $this->autoRollback) @db2_rollback($this->_connectionID);
 125  	 	 if (isset($this->connectStmt)) $this->Execute($this->connectStmt);
 126  
 127  	 	 if ($this->_connectionID && isset($schema)) $this->Execute("SET SCHEMA=$schema");
 128  	 	 return $this->_connectionID != false;
 129  	 }
 130  
 131  	 // format and return date string in database timestamp format
 132  	function DBTimeStamp($ts, $isfld = false)
 133  	 {
 134  	 	 if (empty($ts) && $ts !== 0) return 'null';
 135  	 	 if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts);
 136  	 	 return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'YYYY-MM-DD HH24:MI:SS')";
 137  	 }
 138  
 139  	 // Format date column in sql string given an input format that understands Y M D
 140  	function SQLDate($fmt, $col=false)
 141  	 {
 142  	 // use right() and replace() ?
 143  	 	 if (!$col) $col = $this->sysDate;
 144  
 145  	 	 /* use TO_CHAR() if $fmt is TO_CHAR() allowed fmt */
 146  	 	 if ($fmt== 'Y-m-d H:i:s')
 147  	 	 	 return 'TO_CHAR('.$col.", 'YYYY-MM-DD HH24:MI:SS')";
 148  
 149  	 	 $s = '';
 150  
 151  	 	 $len = strlen($fmt);
 152  	 	 for ($i=0; $i < $len; $i++) {
 153  	 	 	 if ($s) $s .= $this->concat_operator;
 154  	 	 	 $ch = $fmt[$i];
 155  	 	 	 switch($ch) {
 156  	 	 	 case 'Y':
 157  	 	 	 case 'y':
 158  	 	 	 	 if ($len==1) return "year($col)";
 159  	 	 	 	 $s .= "char(year($col))";
 160  	 	 	 	 break;
 161  	 	 	 case 'M':
 162  	 	 	 	 if ($len==1) return "monthname($col)";
 163  	 	 	 	 $s .= "substr(monthname($col),1,3)";
 164  	 	 	 	 break;
 165  	 	 	 case 'm':
 166  	 	 	 	 if ($len==1) return "month($col)";
 167  	 	 	 	 $s .= "right(digits(month($col)),2)";
 168  	 	 	 	 break;
 169  	 	 	 case 'D':
 170  	 	 	 case 'd':
 171  	 	 	 	 if ($len==1) return "day($col)";
 172  	 	 	 	 $s .= "right(digits(day($col)),2)";
 173  	 	 	 	 break;
 174  	 	 	 case 'H':
 175  	 	 	 case 'h':
 176  	 	 	 	 if ($len==1) return "hour($col)";
 177  	 	 	 	 if ($col != $this->sysDate) $s .= "right(digits(hour($col)),2)";
 178  	 	 	 	 else $s .= "''";
 179  	 	 	 	 break;
 180  	 	 	 case 'i':
 181  	 	 	 case 'I':
 182  	 	 	 	 if ($len==1) return "minute($col)";
 183  	 	 	 	 if ($col != $this->sysDate)
 184  	 	 	 	 	 $s .= "right(digits(minute($col)),2)";
 185  	 	 	 	 	 else $s .= "''";
 186  	 	 	 	 break;
 187  	 	 	 case 'S':
 188  	 	 	 case 's':
 189  	 	 	 	 if ($len==1) return "second($col)";
 190  	 	 	 	 if ($col != $this->sysDate)
 191  	 	 	 	 	 $s .= "right(digits(second($col)),2)";
 192  	 	 	 	 else $s .= "''";
 193  	 	 	 	 break;
 194  	 	 	 default:
 195  	 	 	 	 if ($ch == '\\') {
 196  	 	 	 	 	 $i++;
 197  	 	 	 	 	 $ch = substr($fmt,$i,1);
 198  	 	 	 	 }
 199  	 	 	 	 $s .= $this->qstr($ch);
 200  	 	 	 }
 201  	 	 }
 202  	 	 return $s;
 203  	 }
 204  
 205  
 206  	function ServerInfo()
 207  	 {
 208  	 	 $row = $this->GetRow("SELECT service_level, fixpack_num FROM TABLE(sysproc.env_get_inst_info())
 209  	 	 	 as INSTANCEINFO");
 210  
 211  
 212  	 	 if ($row) {
 213  	 	 	 $info['version'] = $row[0].':'.$row[1];
 214  	 	 	 $info['fixpack'] = $row[1];
 215  	 	 	 $info['description'] = '';
 216  	 	 } else {
 217  	 	 	 return ADOConnection::ServerInfo();
 218  	 	 }
 219  
 220  	 	 return $info;
 221  	 }
 222  
 223  	function CreateSequence($seqname='adodbseq',$start=1)
 224  	 {
 225  	 	 if (empty($this->_genSeqSQL)) return false;
 226  	 	 $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$start));
 227  	 	 if (!$ok) return false;
 228  	 	 return true;
 229  	 }
 230  
 231  	function DropSequence($seqname = 'adodbseq')
 232  	 {
 233  	 	 if (empty($this->_dropSeqSQL)) return false;
 234  	 	 return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
 235  	 }
 236  
 237  	function SelectLimit($sql, $nrows = -1, $offset = -1, $inputArr = false, $secs2cache = 0)
 238  	 {
 239  	 	 $nrows = (int) $nrows;
 240  	 	 $offset = (int) $offset;
 241  	 	 if ($offset <= 0) {
 242  	 	 // could also use " OPTIMIZE FOR $nrows ROWS "
 243  	 	 	 if ($nrows >= 0) $sql .=  " FETCH FIRST $nrows ROWS ONLY ";
 244  	 	 	 $rs = $this->Execute($sql,$inputArr);
 245  	 	 } else {
 246  	 	 	 if ($offset > 0 && $nrows < 0);
 247  	 	 	 else {
 248  	 	 	 	 $nrows += $offset;
 249  	 	 	 	 $sql .=  " FETCH FIRST $nrows ROWS ONLY ";
 250  	 	 	 }
 251  	 	 	 $rs = ADOConnection::SelectLimit($sql,-1,$offset,$inputArr);
 252  	 	 }
 253  
 254  	 	 return $rs;
 255  	 }
 256  
 257  	 /*
 258  	 	 This algorithm is not very efficient, but works even if table locking
 259  	 	 is not available.
 260  
 261  	 	 Will return false if unable to generate an ID after $MAXLOOPS attempts.
 262  	 */
 263  	function GenID($seq='adodbseq',$start=1)
 264  	 {
 265  	 	 // if you have to modify the parameter below, your database is overloaded,
 266  	 	 // or you need to implement generation of id's yourself!
 267  	 	 	 	 $num = $this->GetOne("VALUES NEXTVAL FOR $seq");
 268  	 	 	 	 return $num;
 269  	 }
 270  
 271  
 272  	function ErrorMsg()
 273  	 {
 274  	 	 if ($this->_haserrorfunctions) {
 275  	 	 	 if ($this->_errorMsg !== false) return $this->_errorMsg;
 276  	 	 	 if (empty($this->_connectionID)) return @db2_conn_errormsg();
 277  	 	 	 return @db2_conn_errormsg($this->_connectionID);
 278  	 	 } else return ADOConnection::ErrorMsg();
 279  	 }
 280  
 281  	function ErrorNo()
 282  	 {
 283  
 284  	 	 if ($this->_haserrorfunctions) {
 285  	 	 	 if ($this->_errorCode !== false) {
 286  	 	 	 	 // bug in 4.0.6, error number can be corrupted string (should be 6 digits)
 287  	 	 	 	 return (strlen($this->_errorCode)<=2) ? 0 : $this->_errorCode;
 288  	 	 	 }
 289  
 290  	 	 	 if (empty($this->_connectionID)) $e = @db2_conn_error();
 291  	 	 	 else $e = @db2_conn_error($this->_connectionID);
 292  
 293  	 	 	  // bug in 4.0.6, error number can be corrupted string (should be 6 digits)
 294  	 	 	  // so we check and patch
 295  	 	 	 if (strlen($e)<=2) return 0;
 296  	 	 	 return $e;
 297  	 	 } else return ADOConnection::ErrorNo();
 298  	 }
 299  
 300  
 301  
 302  	function BeginTrans()
 303  	 {
 304  	 	 if (!$this->hasTransactions) return false;
 305  	 	 if ($this->transOff) return true;
 306  	 	 $this->transCnt += 1;
 307  	 	 $this->_autocommit = false;
 308  	 	 return db2_autocommit($this->_connectionID,false);
 309  	 }
 310  
 311  	function CommitTrans($ok=true)
 312  	 {
 313  	 	 if ($this->transOff) return true;
 314  	 	 if (!$ok) return $this->RollbackTrans();
 315  	 	 if ($this->transCnt) $this->transCnt -= 1;
 316  	 	 $this->_autocommit = true;
 317  	 	 $ret = db2_commit($this->_connectionID);
 318  	 	 db2_autocommit($this->_connectionID,true);
 319  	 	 return $ret;
 320  	 }
 321  
 322  	function RollbackTrans()
 323  	 {
 324  	 	 if ($this->transOff) return true;
 325  	 	 if ($this->transCnt) $this->transCnt -= 1;
 326  	 	 $this->_autocommit = true;
 327  	 	 $ret = db2_rollback($this->_connectionID);
 328  	 	 db2_autocommit($this->_connectionID,true);
 329  	 	 return $ret;
 330  	 }
 331  
 332  	function MetaPrimaryKeys($table, $owner = false)
 333  	 {
 334  	 global $ADODB_FETCH_MODE;
 335  
 336  	 	 if ($this->uCaseTables) $table = strtoupper($table);
 337  	 	 $schema = '';
 338  	 	 $this->_findschema($table,$schema);
 339  
 340  	 	 $savem = $ADODB_FETCH_MODE;
 341  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 342  	 	 $qid = @db2_primarykeys($this->_connectionID,'',$schema,$table);
 343  
 344  	 	 if (!$qid) {
 345  	 	 	 $ADODB_FETCH_MODE = $savem;
 346  	 	 	 return false;
 347  	 	 }
 348  	 	 $rs = new ADORecordSet_db2($qid);
 349  	 	 $ADODB_FETCH_MODE = $savem;
 350  
 351  	 	 if (!$rs) return false;
 352  
 353  	 	 $arr = $rs->GetArray();
 354  	 	 $rs->Close();
 355  	 	 $arr2 = array();
 356  	 	 for ($i=0; $i < sizeof($arr); $i++) {
 357  	 	 	 if ($arr[$i][3]) $arr2[] = $arr[$i][3];
 358  	 	 }
 359  	 	 return $arr2;
 360  	 }
 361  
 362  	function MetaForeignKeys($table, $owner = FALSE, $upper = FALSE, $asociative = FALSE )
 363  	 {
 364  	 global $ADODB_FETCH_MODE;
 365  
 366  	 	 if ($this->uCaseTables) $table = strtoupper($table);
 367  	 	 $schema = '';
 368  	 	 $this->_findschema($table,$schema);
 369  
 370  	 	 $savem = $ADODB_FETCH_MODE;
 371  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 372  	 	 $qid = @db2_foreign_keys($this->_connectionID,'',$schema,$table);
 373  	 	 if (!$qid) {
 374  	 	 	 $ADODB_FETCH_MODE = $savem;
 375  	 	 	 return false;
 376  	 	 }
 377  	 	 $rs = new ADORecordSet_db2($qid);
 378  
 379  	 	 $ADODB_FETCH_MODE = $savem;
 380  	 	 /*
 381  	 	 $rs->fields indices
 382  	 	 0 PKTABLE_CAT
 383  	 	 1 PKTABLE_SCHEM
 384  	 	 2 PKTABLE_NAME
 385  	 	 3 PKCOLUMN_NAME
 386  	 	 4 FKTABLE_CAT
 387  	 	 5 FKTABLE_SCHEM
 388  	 	 6 FKTABLE_NAME
 389  	 	 7 FKCOLUMN_NAME
 390  	 	 */
 391  	 	 if (!$rs) return false;
 392  
 393  	 	 $foreign_keys = array();
 394  	 	 while (!$rs->EOF) {
 395  	 	 	 if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) {
 396  	 	 	 	 if (!is_array($foreign_keys[$rs->fields[5].'.'.$rs->fields[6]]))
 397  	 	 	 	 	 $foreign_keys[$rs->fields[5].'.'.$rs->fields[6]] = array();
 398  	 	 	 	 $foreign_keys[$rs->fields[5].'.'.$rs->fields[6]][$rs->fields[7]] = $rs->fields[3];
 399  	 	 	 }
 400  	 	 	 $rs->MoveNext();
 401  	 	 }
 402  
 403  	 	 $rs->Close();
 404  	 	 return $foreign_key;
 405  	 }
 406  
 407  
 408  	function MetaTables($ttype = false, $schema = false, $mask = false)
 409  	 {
 410  	 global $ADODB_FETCH_MODE;
 411  
 412  	 	 $savem = $ADODB_FETCH_MODE;
 413  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 414  	 	 $qid = db2_tables($this->_connectionID);
 415  
 416  	 	 $rs = new ADORecordSet_db2($qid);
 417  
 418  	 	 $ADODB_FETCH_MODE = $savem;
 419  	 	 if (!$rs) {
 420  	 	 	 $false = false;
 421  	 	 	 return $false;
 422  	 	 }
 423  
 424  	 	 $arr = $rs->GetArray();
 425  	 	 $rs->Close();
 426  	 	 $arr2 = array();
 427  
 428  	 	 if ($ttype) {
 429  	 	 	 $isview = strncmp($ttype,'V',1) === 0;
 430  	 	 }
 431  	 	 for ($i=0; $i < sizeof($arr); $i++) {
 432  	 	 	 if (!$arr[$i][2]) continue;
 433  	 	 	 $type = $arr[$i][3];
 434  	 	 	 $owner = $arr[$i][1];
 435  	 	 	 $schemaval = ($schema) ? $arr[$i][1].'.' : '';
 436  	 	 	 if ($ttype) {
 437  	 	 	 	 if ($isview) {
 438  	 	 	 	 	 if (strncmp($type,'V',1) === 0) $arr2[] = $schemaval.$arr[$i][2];
 439  	 	 	 	 } else if (strncmp($owner,'SYS',3) !== 0) $arr2[] = $schemaval.$arr[$i][2];
 440  	 	 	 } else if (strncmp($owner,'SYS',3) !== 0) $arr2[] = $schemaval.$arr[$i][2];
 441  	 	 }
 442  	 	 return $arr2;
 443  	 }
 444  
 445  /*
 446  See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2datetime_data_type_changes.asp
 447  / SQL data type codes /
 448  #define	 SQL_UNKNOWN_TYPE	 0
 449  #define SQL_CHAR	 	 	 1
 450  #define SQL_NUMERIC	 	  2
 451  #define SQL_DECIMAL	 	  3
 452  #define SQL_INTEGER	 	  4
 453  #define SQL_SMALLINT	 	 5
 454  #define SQL_FLOAT	 	    6
 455  #define SQL_REAL	 	 	 7
 456  #define SQL_DOUBLE	 	   8
 457  #if (DB2VER >= 0x0300)
 458  #define SQL_DATETIME	 	 9
 459  #endif
 460  #define SQL_VARCHAR	 	 12
 461  
 462  
 463  / One-parameter shortcuts for date/time data types /
 464  #if (DB2VER >= 0x0300)
 465  #define SQL_TYPE_DATE	   91
 466  #define SQL_TYPE_TIME	   92
 467  #define SQL_TYPE_TIMESTAMP 93
 468  
 469  #define SQL_UNICODE                             (-95)
 470  #define SQL_UNICODE_VARCHAR                     (-96)
 471  #define SQL_UNICODE_LONGVARCHAR                 (-97)
 472  */
 473  	function DB2Types($t)
 474  	 {
 475  	 	 switch ((integer)$t) {
 476  	 	 case 1:
 477  	 	 case 12:
 478  	 	 case 0:
 479  	 	 case -95:
 480  	 	 case -96:
 481  	 	 	 return 'C';
 482  	 	 case -97:
 483  	 	 case -1: //text
 484  	 	 	 return 'X';
 485  	 	 case -4: //image
 486  	 	 	 return 'B';
 487  
 488  	 	 case 9:
 489  	 	 case 91:
 490  	 	 	 return 'D';
 491  
 492  	 	 case 10:
 493  	 	 case 11:
 494  	 	 case 92:
 495  	 	 case 93:
 496  	 	 	 return 'T';
 497  
 498  	 	 case 4:
 499  	 	 case 5:
 500  	 	 case -6:
 501  	 	 	 return 'I';
 502  
 503  	 	 case -11: // uniqidentifier
 504  	 	 	 return 'R';
 505  	 	 case -7: //bit
 506  	 	 	 return 'L';
 507  
 508  	 	 default:
 509  	 	 	 return 'N';
 510  	 	 }
 511  	 }
 512  
 513  	function MetaColumns($table, $normalize=true)
 514  	 {
 515  	 global $ADODB_FETCH_MODE;
 516  
 517  	 	 $false = false;
 518  	 	 if ($this->uCaseTables) $table = strtoupper($table);
 519  	 	 $schema = '';
 520  	 	 $this->_findschema($table,$schema);
 521  
 522  	 	 $savem = $ADODB_FETCH_MODE;
 523  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 524  
 525          	 $colname = "%";
 526  	         $qid = db2_columns($this->_connectionID, "", $schema, $table, $colname);
 527  	 	 if (empty($qid)) return $false;
 528  
 529  	 	 $rs = new ADORecordSet_db2($qid);
 530  	 	 $ADODB_FETCH_MODE = $savem;
 531  
 532  	 	 if (!$rs) return $false;
 533  	 	 $rs->_fetch();
 534  
 535  	 	 $retarr = array();
 536  
 537  	 	 /*
 538  	 	 $rs->fields indices
 539  	 	 0 TABLE_QUALIFIER
 540  	 	 1 TABLE_SCHEM
 541  	 	 2 TABLE_NAME
 542  	 	 3 COLUMN_NAME
 543  	 	 4 DATA_TYPE
 544  	 	 5 TYPE_NAME
 545  	 	 6 PRECISION
 546  	 	 7 LENGTH
 547  	 	 8 SCALE
 548  	 	 9 RADIX
 549  	 	 10 NULLABLE
 550  	 	 11 REMARKS
 551  	 	 */
 552  	 	 while (!$rs->EOF) {
 553  	 	 	 if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) {
 554  	 	 	 	 $fld = new ADOFieldObject();
 555  	 	 	 	 $fld->name = $rs->fields[3];
 556  	 	 	 	 $fld->type = $this->DB2Types($rs->fields[4]);
 557  
 558  	 	 	 	 // ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp
 559  	 	 	 	 // access uses precision to store length for char/varchar
 560  	 	 	 	 if ($fld->type == 'C' or $fld->type == 'X') {
 561  	 	 	 	 	 if ($rs->fields[4] <= -95) // UNICODE
 562  	 	 	 	 	 	 $fld->max_length = $rs->fields[7]/2;
 563  	 	 	 	 	 else
 564  	 	 	 	 	 	 $fld->max_length = $rs->fields[7];
 565  	 	 	 	 } else
 566  	 	 	 	 	 $fld->max_length = $rs->fields[7];
 567  	 	 	 	 $fld->not_null = !empty($rs->fields[10]);
 568  	 	 	 	 $fld->scale = $rs->fields[8];
 569  	 	 	 	 $fld->primary_key = false;
 570  	 	 	 	 $retarr[strtoupper($fld->name)] = $fld;
 571  	 	 	 } else if (sizeof($retarr)>0)
 572  	 	 	 	 break;
 573  	 	 	 $rs->MoveNext();
 574  	 	 }
 575  	 	 $rs->Close();
 576  	 	 if (empty($retarr)) $retarr = false;
 577  
 578  	       $qid = db2_primary_keys($this->_connectionID, "", $schema, $table);
 579  	 	 if (empty($qid)) return $false;
 580  
 581  	 	 $rs = new ADORecordSet_db2($qid);
 582  	 	 $ADODB_FETCH_MODE = $savem;
 583  
 584  	 	 if (!$rs) return $retarr;
 585  	 	 $rs->_fetch();
 586  
 587  	 	 /*
 588  	 	 $rs->fields indices
 589  	 	 0 TABLE_CAT
 590  	 	 1 TABLE_SCHEM
 591  	 	 2 TABLE_NAME
 592  	 	 3 COLUMN_NAME
 593  	 	 4 KEY_SEQ
 594  	 	 5 PK_NAME
 595  	 	 */
 596  	 	 while (!$rs->EOF) {
 597  	 	 	 if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) {
 598  	 	 	 	 $retarr[strtoupper($rs->fields[3])]->primary_key = true;
 599  	 	 	 } else if (sizeof($retarr)>0)
 600  	 	 	 	 break;
 601  	 	 	 $rs->MoveNext();
 602  	 	 }
 603  	 	 $rs->Close();
 604  
 605  	 	 if (empty($retarr)) $retarr = false;
 606  	 	 return $retarr;
 607  	 }
 608  
 609  
 610  	function Prepare($sql)
 611  	 {
 612  	 	 if (! $this->_bindInputArray) return $sql; // no binding
 613  	 	 $stmt = db2_prepare($this->_connectionID,$sql);
 614  	 	 if (!$stmt) {
 615  	 	 	 // we don't know whether db2 driver is parsing prepared stmts, so just return sql
 616  	 	 	 return $sql;
 617  	 	 }
 618  	 	 return array($sql,$stmt,false);
 619  	 }
 620  
 621  	 /* returns queryID or false */
 622  	function _query($sql,$inputarr=false)
 623  	 {
 624  	 	 $last_php_error = $this->resetLastError();
 625  	 	 $this->_errorMsg = '';
 626  
 627  	 	 if ($inputarr) {
 628  	 	 	 if (is_array($sql)) {
 629  	 	 	 	 $stmtid = $sql[1];
 630  	 	 	 } else {
 631  	 	 	 	 $stmtid = db2_prepare($this->_connectionID,$sql);
 632  
 633  	 	 	 	 if ($stmtid == false) {
 634  	 	 	 	 	 $this->_errorMsg = $this->getChangedErrorMsg($last_php_error);
 635  	 	 	 	 	 return false;
 636  	 	 	 	 }
 637  	 	 	 }
 638  
 639  	 	 	 if (! db2_execute($stmtid,$inputarr)) {
 640  	 	 	 	 if ($this->_haserrorfunctions) {
 641  	 	 	 	 	 $this->_errorMsg = db2_stmt_errormsg();
 642  	 	 	 	 	 $this->_errorCode = db2_stmt_error();
 643  	 	 	 	 }
 644  	 	 	 	 return false;
 645  	 	 	 }
 646  
 647  	 	 } else if (is_array($sql)) {
 648  	 	 	 $stmtid = $sql[1];
 649  	 	 	 if (!db2_execute($stmtid)) {
 650  	 	 	 	 if ($this->_haserrorfunctions) {
 651  	 	 	 	 	 $this->_errorMsg = db2_stmt_errormsg();
 652  	 	 	 	 	 $this->_errorCode = db2_stmt_error();
 653  	 	 	 	 }
 654  	 	 	 	 return false;
 655  	 	 	 }
 656  	 	 } else
 657  	 	 	 $stmtid = @db2_exec($this->_connectionID,$sql);
 658  
 659  	 	 $this->_lastAffectedRows = 0;
 660  	 	 if ($stmtid) {
 661  	 	 	 if (@db2_num_fields($stmtid) == 0) {
 662  	 	 	 	 $this->_lastAffectedRows = db2_num_rows($stmtid);
 663  	 	 	 	 $stmtid = true;
 664  	 	 	 } else {
 665  	 	 	 	 $this->_lastAffectedRows = 0;
 666  	 	 	 }
 667  
 668  	 	 	 if ($this->_haserrorfunctions) {
 669  	 	 	 	 $this->_errorMsg = '';
 670  	 	 	 	 $this->_errorCode = 0;
 671  	 	 	 } else {
 672  	 	 	 	 $this->_errorMsg = $this->getChangedErrorMsg($last_php_error);
 673  	 	 	 }
 674  	 	 } else {
 675  	 	 	 if ($this->_haserrorfunctions) {
 676  	 	 	 	 $this->_errorMsg = db2_stmt_errormsg();
 677  	 	 	 	 $this->_errorCode = db2_stmt_error();
 678  	 	 	 } else {
 679  	 	 	 	 $this->_errorMsg = $this->getChangedErrorMsg($last_php_error);
 680  	 	 	 }
 681  	 	 }
 682  	 	 return $stmtid;
 683  	 }
 684  
 685  	 /*
 686  	 	 Insert a null into the blob field of the table first.
 687  	 	 Then use UpdateBlob to store the blob.
 688  
 689  	 	 Usage:
 690  
 691  	 	 $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
 692  	 	 $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
 693  	 */
 694  	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
 695  	 {
 696  	 	 return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
 697  	 }
 698  
 699  	 // returns true or false
 700  	function _close()
 701  	 {
 702  	 	 $ret = @db2_close($this->_connectionID);
 703  	 	 $this->_connectionID = false;
 704  	 	 return $ret;
 705  	 }
 706  
 707  	function _affectedrows()
 708  	 {
 709  	 	 return $this->_lastAffectedRows;
 710  	 }
 711  
 712  }
 713  
 714  /*--------------------------------------------------------------------------------------
 715  	  Class Name: Recordset
 716  --------------------------------------------------------------------------------------*/
 717  
 718  class ADORecordSet_db2 extends ADORecordSet {
 719  
 720  	 var $bind = false;
 721  	 var $databaseType = "db2";
 722  	 var $dataProvider = "db2";
 723  	 var $useFetchArray;
 724  
 725  	function __construct($id,$mode=false)
 726  	 {
 727  	 	 if ($mode === false) {
 728  	 	 	 global $ADODB_FETCH_MODE;
 729  	 	 	 $mode = $ADODB_FETCH_MODE;
 730  	 	 }
 731  	 	 $this->fetchMode = $mode;
 732  
 733  	 	 $this->_queryID = $id;
 734  	 }
 735  
 736  
 737  	 // returns the field object
 738  	function FetchField($offset = -1)
 739  	 {
 740  	 	 $o= new ADOFieldObject();
 741  	 	 $o->name = @db2_field_name($this->_queryID,$offset);
 742  	 	 $o->type = @db2_field_type($this->_queryID,$offset);
 743  	 	 $o->max_length = db2_field_width($this->_queryID,$offset);
 744  	 	 if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name);
 745  	 	 else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name);
 746  	 	 return $o;
 747  	 }
 748  
 749  	 /* Use associative array to get fields array */
 750  	function Fields($colname)
 751  	 {
 752  	 	 if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname];
 753  	 	 if (!$this->bind) {
 754  	 	 	 $this->bind = array();
 755  	 	 	 for ($i=0; $i < $this->_numOfFields; $i++) {
 756  	 	 	 	 $o = $this->FetchField($i);
 757  	 	 	 	 $this->bind[strtoupper($o->name)] = $i;
 758  	 	 	 }
 759  	 	 }
 760  
 761  	 	  return $this->fields[$this->bind[strtoupper($colname)]];
 762  	 }
 763  
 764  
 765  	function _initrs()
 766  	 {
 767  	 global $ADODB_COUNTRECS;
 768  	 	 $this->_numOfRows = ($ADODB_COUNTRECS) ? @db2_num_rows($this->_queryID) : -1;
 769  	 	 $this->_numOfFields = @db2_num_fields($this->_queryID);
 770  	 	 // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0
 771  	 	 if ($this->_numOfRows == 0) $this->_numOfRows = -1;
 772  	 }
 773  
 774  	function _seek($row)
 775  	 {
 776  	 	 return false;
 777  	 }
 778  
 779  	 // speed up SelectLimit() by switching to ADODB_FETCH_NUM as ADODB_FETCH_ASSOC is emulated
 780  	function GetArrayLimit($nrows,$offset=-1)
 781  	 {
 782  	 	 if ($offset <= 0) {
 783  	 	 	 $rs = $this->GetArray($nrows);
 784  	 	 	 return $rs;
 785  	 	 }
 786  	 	 $savem = $this->fetchMode;
 787  	 	 $this->fetchMode = ADODB_FETCH_NUM;
 788  	 	 $this->Move($offset);
 789  	 	 $this->fetchMode = $savem;
 790  
 791  	 	 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
 792  	 	 	 $this->fields = $this->GetRowAssoc();
 793  	 	 }
 794  
 795  	 	 $results = array();
 796  	 	 $cnt = 0;
 797  	 	 while (!$this->EOF && $nrows != $cnt) {
 798  	 	 	 $results[$cnt++] = $this->fields;
 799  	 	 	 $this->MoveNext();
 800  	 	 }
 801  
 802  	 	 return $results;
 803  	 }
 804  
 805  
 806  	function MoveNext()
 807  	 {
 808  	 	 if ($this->_numOfRows != 0 && !$this->EOF) {
 809  	 	 	 $this->_currentRow++;
 810  
 811  	 	 	 $this->fields = @db2_fetch_array($this->_queryID);
 812  	 	 	 if ($this->fields) {
 813  	 	 	 	 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
 814  	 	 	 	 	 $this->fields = $this->GetRowAssoc();
 815  	 	 	 	 }
 816  	 	 	 	 return true;
 817  	 	 	 }
 818  	 	 }
 819  	 	 $this->fields = false;
 820  	 	 $this->EOF = true;
 821  	 	 return false;
 822  	 }
 823  
 824  	function _fetch()
 825  	 {
 826  
 827  	 	 $this->fields = db2_fetch_array($this->_queryID);
 828  	 	 if ($this->fields) {
 829  	 	 	 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
 830  	 	 	 	 $this->fields = $this->GetRowAssoc();
 831  	 	 	 }
 832  	 	 	 return true;
 833  	 	 }
 834  	 	 $this->fields = false;
 835  	 	 return false;
 836  	 }
 837  
 838  	function _close()
 839  	 {
 840  	 	 return @db2_free_result($this->_queryID);
 841  	 }
 842  
 843  }