Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   1  <?php
   2  /**
   3    @version   v5.21.0  2021-02-27
   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    Released under both BSD license and Lesser GPL library license.
   7    Whenever there is any discrepancy between the two licenses,
   8    the BSD license will take precedence.
   9  
  10    Set tabs to 4 for best viewing.
  11  
  12  * Driver for use with IBM DB2 Native Client
  13  *
  14  * Originally DB2 drivers were dependent on an ODBC driver, and some installations
  15  * may still use that. To use an ODBC driver connection, use the odbc_db2
  16  * ADOdb driver. For Linux, you need the 'ibm_db2' PECL extension for PHP,
  17  * For Windows, you need to locate an appropriate version of the php_ibm_db2.dll,
  18  * as well as the IBM data server client software.
  19  * This is basically a full rewrite of the original driver, for information 
  20  * about all the changes, see the update information on the ADOdb website 
  21  * for version 5.21.0 
  22  *
  23  * @link http://pecl.php.net/package/ibm_db2 Pecl Extension For DB2
  24  * @author Mark Newnham
  25  */
  26  
  27  // security - hide paths
  28  if (!defined('ADODB_DIR')) die();
  29  
  30    define("_ADODB_DB2_LAYER", 2 );
  31  
  32  /*--------------------------------------------------------------------
  33  ----------------------------------------------------------------------*/
  34  
  35  
  36  class ADODB_db2 extends ADOConnection {
  37  	 var $databaseType = "db2";
  38  	 var $fmtDate = "'Y-m-d'";
  39  	 var $concat_operator = '||';
  40  
  41  	 var $sysTime = 'CURRENT TIME';
  42  	 var $sysDate = 'CURRENT DATE';
  43  	 var $sysTimeStamp = 'CURRENT TIMESTAMP';
  44  
  45  	 var $fmtTimeStamp = "'Y-m-d H:i:s'";
  46  	 var $replaceQuote = "''"; // string to use to replace quotes
  47  	 var $dataProvider = "db2";
  48  	 var $hasAffectedRows = true;
  49  
  50  	 var $binmode = DB2_BINARY;
  51  
  52  	 /*
  53  	 * setting this to true will make array elements in FETCH_ASSOC 
  54  	 * mode case-sensitive breaking backward-compat
  55  	 */
  56  	 var $useFetchArray = false; 
  57  	 var $_bindInputArray = true;
  58  	 var $_genIDSQL = "VALUES NEXTVAL FOR %s";
  59  	 var $_genSeqSQL = "
  60  	 CREATE SEQUENCE %s START WITH %s 
  61  	 NO MAXVALUE NO CYCLE INCREMENT BY 1 NO CACHE
  62  	 ";
  63  	 var $_dropSeqSQL = "DROP SEQUENCE %s";
  64  	 var $_autocommit = true;
  65  	 var $_lastAffectedRows = 0;
  66  	 var $hasInsertID = true;
  67  	 var $hasGenID    = true;
  68  	 
  69  	 /* 
  70  	  * Character used to wrap column and table names for escaping special 
  71  	  * characters in column and table names as well as forcing upper and
  72  	  * lower case
  73  	  */
  74  	 public $nameQuote = '"';
  75  	 
  76  	 /*
  77  	  * Executed after successful connection
  78  	  */
  79  	 public $connectStmt = '';
  80  	 
  81  	 /*
  82  	  * Holds the current database name
  83  	  */
  84  	 private $databaseName = '';
  85  
  86  	 /*
  87  	  * Holds information about the stored procedure request
  88  	  * currently being built
  89  	  */
  90  	 private $storedProcedureParameters = false;
  91  	  
  92  	 
  93  	function __construct() {}
  94  
  95      function _insertid()
  96      {
  97          return ADOConnection::GetOne('VALUES IDENTITY_VAL_LOCAL()');
  98      }
  99  
 100  	public function _connect($argDSN, $argUsername, $argPassword, $argDatabasename)
 101  	 {
 102  	 	 return $this->doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename);
 103  	 }
 104  	 
 105  	public function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename)
 106  	 {
 107  	 	 return $this->doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename,true);
 108  	 }
 109  	 
 110  	private function doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename, $persistent=false)
 111  	 {
 112  
 113  	 	 if (!function_exists('db2_connect')) {
 114  	 	 	 ADOConnection::outp("DB2 extension not installed.");
 115  	 	 	 return null;
 116  	 	 }
 117  	 	 
 118  	 	 $connectionParameters = $this->unpackParameters($argDSN, 
 119  	 	 	 	 	 	 	 	 	 	 	 	 	     $argUsername, 
 120  	 	 	 	 	 	 	 	 	 	 	 	 	     $argPassword, 
 121  	 	 	 	 	 	 	 	 	 	 	 	 	     $argDatabasename);
 122  	 	 	 	 	 	 	 	 	 	 	 	 	  
 123  	 	 if ($connectionParameters == null)
 124  	 	 {
 125  	 	 	 /*
 126  	 	      * Error thrown
 127  	 	 	  */
 128  	 	 	 return null;
 129  	 	 }
 130  	 	 
 131  	 	 $argDSN 	 	          = $connectionParameters['dsn'];
 132  	 	 $argUsername 	          = $connectionParameters['uid'];
 133  	 	 $argPassword 	          = $connectionParameters['pwd'];
 134  	 	 $argDatabasename         = $connectionParameters['database'];
 135  	 	 $useCataloguedConnection = $connectionParameters['catalogue'];
 136  	 	 
 137  	 	 if ($this->debug){
 138  	 	 	 if ($useCataloguedConnection){
 139  	 	 	 	 $connectMessage = "Catalogued connection using parameters: ";
 140  	 	 	 	 $connectMessage .= "DB=$argDatabasename / ";
 141  	 	 	 	 $connectMessage .= "UID=$argUsername / ";
 142  	 	 	 	 $connectMessage .= "PWD=$argPassword";
 143  	 	 	 }
 144  	 	 	 else
 145  	 	     {
 146  	 	 	 	 $connectMessage = "Uncatalogued connection using DSN: $argDSN";
 147  	 	 	 }
 148  	 	 	 ADOConnection::outp($connectMessage);
 149  	 	 }	 
 150  	 	 /*
 151           * This needs to be set before the connect().
 152  	 	  */
 153  	 	 ini_set('ibm_db2.binmode', $this->binmode);
 154  
 155  	 	 if ($persistent)
 156  	 	 	 $db2Function = 'db2_pconnect';
 157  	 	 else
 158  	 	 	 $db2Function = 'db2_connect';
 159  	 	 
 160  	 	 /*
 161  	 	 * We need to flatten out the connectionParameters
 162  	 	 */
 163  
 164  	 	 $db2Options = array();
 165  	 	 if ($this->connectionParameters)
 166  	 	 {
 167  	 	 	 foreach($this->connectionParameters as $p)
 168  	 	 	 	 foreach($p as $k=>$v)
 169  	 	 	 	 	 $db2Options[$k] = $v;
 170  	 	 }
 171  	 	 
 172  	 	 if ($useCataloguedConnection)
 173  	 	 	 $this->_connectionID = $db2Function($argDatabasename,
 174  	 	 	 	 	 	 	 	 	 	 	     $argUsername,
 175  	 	 	 	 	 	 	 	 	 	 	     $argPassword,
 176  	 	 	 	 	 	 	 	 	 	 	     $db2Options);
 177  	 	 else
 178  	 	 	 $this->_connectionID = $db2Function($argDSN,
 179  	 	 	 	 	 	 	 	 	 	 	     null,
 180  	 	 	 	 	 	 	 	 	 	 	     null,
 181  	 	 	 	 	 	 	 	 	 	 	     $db2Options);
 182  	 	 
 183  	 	 $this->_errorMsg = @db2_conn_errormsg();
 184  	 	 
 185  	 	 if ($this->_connectionID && $this->connectStmt)
 186  	 	 	 $this->execute($this->connectStmt);
 187  
 188  	 	 return $this->_connectionID != false;
 189  	 	 
 190  	 }	 
 191  	 
 192  	 /**
 193  	  * Validates and preprocesses the passed parameters for consistency
 194  	  *
 195  	  * @param	 string	 $argDSN	 	 	 	 Either DSN or database
 196  	  * @param	 string	 $argUsername	 	 User name or null
 197  	  * @param	 string	 $argPassword	 	 Password or null
 198  	  * @param	 string	 $argDatabasename	 Either DSN or database
 199  	  *
 200  	  * @return mixed  array if correct, null if not
 201  	  */
 202  	private function unpackParameters($argDSN, $argUsername, $argPassword, $argDatabasename)
 203  	 {
 204  	 	 
 205  	 	 $connectionParameters = array('dsn'=>'',
 206  	 	 	 	 	 	 	 	 	   'uid'=>'',
 207  	 	 	 	 	 	 	 	 	   'pwd'=>'',
 208  	 	 	 	 	 	 	 	 	   'database'=>'',
 209  	 	 	 	 	 	 	 	 	   'catalogue'=>true
 210  	 	 	 	 	 	 	 	 	   );
 211  	 	 
 212  	 	 /*
 213  	 	  * Uou can either connect to a catalogued connection
 214           * with a database name e.g. 'SAMPLE'
 215           * or an uncatalogued connection with a DSN like connection
 216  	 	  * DATABASE=database;HOSTNAME=hostname;PORT=port;PROTOCOL=TCPIP;UID=username;PWD=password;
 217  	 	  */
 218  	 	 
 219  	 	 if (!$argDSN && !$argDatabasename)
 220  	 	 {
 221  	 	 	 $errorMessage = 'Supply either catalogued or uncatalogued connection parameters';
 222  	 	 	 $this->_errorMsg = $errorMessage;
 223  	 	 	 if ($this->debug)
 224  	 	 	 	 ADOConnection::outp($errorMessage);
 225  	 	 	 return null;
 226  	 	 }
 227  	 	 
 228  	 	 $useCataloguedConnection = true;
 229  	 	 $schemaName 	 	 	  = '';
 230  	 	 
 231  	 	 if ($argDSN && $argDatabasename)
 232  	 	 {
 233  	 	 	 /*
 234  	 	 	  * If a catalogued connection if provided, 
 235  	 	 	  * as well as user and password
 236  	 	 	  * that will take priority
 237  	 	 	  */
 238  	 	 	 if ($argUsername && $argPassword && !$this->isDsn($argDatabasename))
 239  	 	 	 {
 240  	 	 	 	 if ($this->debug){
 241  	 	 	 	 	 $errorMessage = 'Warning: Because you provided user,';
 242  	 	 	 	 	 $errorMessage.= 'password and database, DSN connection ';
 243  	 	 	 	 	 $errorMessage.= 'parameters were discarded';
 244  	 	 	 	 	 ADOConnection::outp($errorMessage);
 245  	 	 	 	 
 246  	 	 	 	 }
 247  	 	 	 	 $argDSN = '';
 248  	 	 	 }
 249  	 	 	 else if ($this->isDsn($argDSN) && $this->isDsn($argDatabasename))
 250  	 	 	 {
 251  	 	 	 	 $errorMessage = 'Supply uncatalogued connection parameters ';
 252  	 	 	 	 $errorMessage.= 'in either the database or DSN arguments, ';
 253  	 	 	 	 $errorMessage.= 'but not both';
 254  	 	 	 	 if ($this->debug)
 255  	 	 	 	 	 ADOConnection::outp($errorMessage);
 256  	 	 	 	 return null;
 257  	 	 	 }
 258  	 	 }
 259  	 	 	 	 
 260  	 	 if (!$this->isDsn($argDSN) && $this->isDsn($argDatabasename))
 261  	 	 {
 262  	 	 	 /*
 263  	 	 	  * Switch them around for next test
 264  	 	 	  */
 265  	 	 	 $temp           = $argDSN;
 266  	 	 	 $argDsn         = $argDatabasename;
 267  	 	 	 $argDatabasenME = $temp;
 268  	 	 }
 269  	 	 	 	 
 270  	 	 if ($this->isDsn($argDSN))
 271  	 	 {
 272  	 	 	 
 273  	 	 	 if (!preg_match('/uid=/i',$argDSN) 
 274  	 	 	 ||  !preg_match('/pwd=/i',$argDSN))
 275  	 	 	 {
 276  	 	 	 	 $errorMessage = 'For uncatalogued connections, provide ';
 277  	 	 	 	 $errorMessage.= 'both UID and PWD in the connection string';
 278  	 	 	 	 if ($this->debug)
 279  	 	 	 	 	 ADOConnection::outp($errorMessage);
 280  	 	 	 	 return null;
 281  	 	 	 }
 282  	 	 	 
 283  	 	 	 if (preg_match('/database=/i',$argDSN))
 284  	 	 	 {
 285  	 	 	 	 if ($argDatabasename)
 286  	 	 	     {
 287  	 	 	 	 	 $argDatabasename = '';
 288  	 	 	 	 	 if ($this->debug)
 289  	 	 	 	 	 {
 290  	 	 	 	 	 	 $errorMessage = 'Warning: Because you provided ';
 291  	 	 	 	 	 	 $errorMessage.= 'database information in the DSN ';
 292  	 	 	 	 	 	 $errorMessage.= 'parameters, the supplied database ';
 293  	 	 	 	 	 	 $errorMessage.= 'name was discarded';
 294  	 	 	 	 	 	 ADOConnection::outp($errorMessage);
 295  	 	 	 	 	 }
 296  	 	         }
 297  	 	 	 	 $useCataloguedConnection = false;
 298  	 	 	 	 
 299  	 	 	 } 
 300  	 	 	 elseif ($argDatabasename)
 301  	 	 	 {
 302  	 	 	 	 $this->databaseName = $argDatabasename;
 303  	 	 	 	 $argDSN .= ';database=' . $argDatabasename;
 304  	 	 	 	 $argDatabasename = '';
 305  	 	 	 	 $useCataloguedConnection = false;
 306  	 	 	 	 	 
 307  	 	 	 } 
 308  	 	 	 else 
 309  	 	 	 {
 310  	 	 	 	 $errorMessage = 'Uncatalogued connection parameters ';
 311  	 	 	 	 $errorMessage.= 'must contain a database= argument';
 312  	 	 	 	 if ($this->debug)
 313  	 	 	 	 	 ADOConnection::outp($errorMessage);
 314  	 	 	 	 return null;
 315  	 	 	 }
 316  	 	 }
 317  	 	 
 318  	 	 if ($argDSN && !$argDatabasename && $useCataloguedConnection)
 319  	 	 {
 320  	 	 	 $argDatabasename = $argDSN;
 321  	 	 	 $argDSN          = '';
 322  	 	 }
 323  	 	 
 324  
 325  	 	 if ($useCataloguedConnection 
 326  	 	 && (!$argDatabasename 
 327  	 	 || !$argUsername
 328  	 	 || !$argPassword))
 329  	 	 {
 330  	 	 	 	 	 
 331  	 	 	 $errorMessage = 'For catalogued connections, provide ';
 332  	 	 	 $errorMessage.= 'database, username and password';
 333  	 	 	 $this->_errorMsg = $errorMessage;
 334  	 	 	 if ($this->debug)
 335  	 	 	 	 ADOConnection::outp($errorMessage);
 336  	 	 	 return null;
 337  	 	 	 
 338  	 	 }
 339  	 	 
 340  	 	 if ($argDatabasename)
 341  	 	 	 $this->databaseName = $argDatabasename;
 342  	 	 elseif (!$this->databaseName)
 343  	 	 	 $this->databaseName = $this->getDatabasenameFromDsn($argDSN);
 344  	 
 345  	 	 
 346  	 	 $connectionParameters = array('dsn'=>$argDSN,
 347  	 	 	 	 	 	 	 	 	   'uid'=>$argUsername,
 348  	 	 	 	 	 	 	 	 	   'pwd'=>$argPassword,
 349  	 	 	 	 	 	 	 	 	   'database'=>$argDatabasename,
 350  	 	 	 	 	 	 	 	 	   'catalogue'=>$useCataloguedConnection
 351  	 	 	 	 	 	 	 	 	   );
 352  	 	 
 353  	 	 return $connectionParameters;
 354  	 	 
 355  	 }
 356  
 357  	 /**
 358  	   * Does the provided string look like a DSN
 359  	   *
 360  	   * @param	 string	 $dsnString
 361  	   *
 362  	   * @return bool
 363  	   */
 364  	private function isDsn($dsnString){
 365  	 	 $dsnArray = preg_split('/[;=]+/',$dsnString);
 366  	 	 if (count($dsnArray) > 2)
 367  	 	 	 return true;
 368  	 	 return false;
 369  	 }
 370  	 	 
 371  
 372  	 /**
 373  	   * Gets the database name from the DSN
 374  	   *
 375  	   * @param	 string	 $dsnString
 376  	   *
 377  	   * @return string
 378  	   */
 379  	private function getDatabasenameFromDsn($dsnString){
 380  	 	 
 381  	 	 $dsnArray = preg_split('/[;=]+/',$dsnString);
 382  	 	 $dbIndex  = array_search('database',$dsnArray);
 383  	 	 
 384  	 	 return $dsnArray[$dbIndex + 1];
 385  	 }	 
 386  	 
 387  	 	 
 388  	 /**
 389  	 * format and return date string in database timestamp format
 390  	 *
 391  	 * @param	 mixed	 $ts	 	 either a string or a unixtime
 392  	 * @param	 bool	 $isField	 discarded
 393  	 *
 394  	 * @return string
 395  	 */
 396  	function dbTimeStamp($ts,$isField=false)
 397  	 {
 398  	 	 if (empty($ts) && $ts !== 0) return 'null';
 399  	 	 if (is_string($ts)) $ts = ADORecordSet::unixTimeStamp($ts);
 400  	 	 return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'YYYY-MM-DD HH24:MI:SS')";
 401  	 }
 402  
 403  	 /**
 404  	 * Format date column in sql string given an input format that understands Y M D
 405  	 *
 406  	 * @param	 string	 $fmt
 407  	 * @param	 bool	 $col
 408  	 *
 409  	 * @return string
 410  	 */
 411  	function sqlDate($fmt, $col=false)
 412  	 {
 413  	 	 if (!$col) $col = $this->sysDate;
 414  
 415  	 	 /* use TO_CHAR() if $fmt is TO_CHAR() allowed fmt */
 416  	 	 if ($fmt== 'Y-m-d H:i:s')
 417  	 	 	 return 'TO_CHAR('.$col.", 'YYYY-MM-DD HH24:MI:SS')";
 418  
 419  	 	 $s = '';
 420  
 421  	 	 $len = strlen($fmt);
 422  	 	 for ($i=0; $i < $len; $i++) {
 423  	 	 	 if ($s) $s .= $this->concat_operator;
 424  	 	 	 $ch = $fmt[$i];
 425  	 	 	 switch($ch) {
 426  	 	 	 case 'Y':
 427  	 	 	 case 'y':
 428  	 	 	 	 if ($len==1) return "year($col)";
 429  	 	 	 	 $s .= "char(year($col))";
 430  	 	 	 	 break;
 431  	 	 	 case 'M':
 432  	 	 	 	 if ($len==1) return "monthname($col)";
 433  	 	 	 	 $s .= "substr(monthname($col),1,3)";
 434  	 	 	 	 break;
 435  	 	 	 case 'm':
 436  	 	 	 	 if ($len==1) return "month($col)";
 437  	 	 	 	 $s .= "right(digits(month($col)),2)";
 438  	 	 	 	 break;
 439  	 	 	 case 'D':
 440  	 	 	 case 'd':
 441  	 	 	 	 if ($len==1) return "day($col)";
 442  	 	 	 	 $s .= "right(digits(day($col)),2)";
 443  	 	 	 	 break;
 444  	 	 	 case 'H':
 445  	 	 	 case 'h':
 446  	 	 	 	 if ($len==1) return "hour($col)";
 447  	 	 	 	 if ($col != $this->sysDate) $s .= "right(digits(hour($col)),2)";
 448  	 	 	 	 else $s .= "''";
 449  	 	 	 	 break;
 450  	 	 	 case 'i':
 451  	 	 	 case 'I':
 452  	 	 	 	 if ($len==1) return "minute($col)";
 453  	 	 	 	 if ($col != $this->sysDate)
 454  	 	 	 	 	 $s .= "right(digits(minute($col)),2)";
 455  	 	 	 	 	 else $s .= "''";
 456  	 	 	 	 break;
 457  	 	 	 case 'S':
 458  	 	 	 case 's':
 459  	 	 	 	 if ($len==1) return "second($col)";
 460  	 	 	 	 if ($col != $this->sysDate)
 461  	 	 	 	 	 $s .= "right(digits(second($col)),2)";
 462  	 	 	 	 else $s .= "''";
 463  	 	 	 	 break;
 464  	 	 	 default:
 465  	 	 	 	 if ($ch == '\\') {
 466  	 	 	 	 	 $i++;
 467  	 	 	 	 	 $ch = substr($fmt,$i,1);
 468  	 	 	 	 }
 469  	 	 	 	 $s .= $this->qstr($ch);
 470  	 	 	 }
 471  	 	 }
 472  	 	 return $s;
 473  	 }
 474  
 475  
 476  	function serverInfo()
 477  	 {
 478  	 	 $sql = "SELECT service_level, fixpack_num 
 479  	 	 	 	   FROM TABLE(sysproc.env_get_inst_info())
 480  	 	 	 	 	 AS INSTANCEINFO";
 481  	 	 $row = $this->GetRow($sql);
 482  
 483  
 484  	 	 if ($row) {
 485  	 	 	 $info['version'] = $row[0].':'.$row[1];
 486  	 	 	 $info['fixpack'] = $row[1];
 487  	 	 	 $info['description'] = '';
 488  	 	 } else {
 489  	 	 	 return ADOConnection::serverInfo();
 490  	 	 }
 491  
 492  	 	 return $info;
 493  	 }
 494  
 495  	function createSequence($seqname='adodbseq',$start=1)
 496  	 {
 497  	 	 if (empty($this->_genSeqSQL)) 
 498  	 	 	 return false;
 499  	 	 
 500  	 	 $ok = $this->execute(sprintf($this->_genSeqSQL,$seqname,$start));
 501  	 	 if (!$ok) 
 502  	 	 	 return false;
 503  	 	 return true;
 504  	 }
 505  
 506  	function dropSequence($seqname='adodbseq')
 507  	 {
 508  	 	 if (empty($this->_dropSeqSQL)) return false;
 509  	 	 return $this->execute(sprintf($this->_dropSeqSQL,$seqname));
 510  	 }
 511  
 512  	function selectLimit($sql,$nrows=-1,$offset=-1,$inputArr=false,$secs2cache=0)
 513  	 {
 514  	 	 $nrows = (integer) $nrows;
 515  	 	 
 516  	 	 if ($offset <= 0)
 517  	 	 {
 518  	 	 	 if ($nrows >= 0) 
 519  	 	 	 	 $sql .=  " FETCH FIRST $nrows ROWS ONLY ";
 520  	 	 	 
 521  	 	 	 $rs = $this->execute($sql,$inputArr);
 522  	 	 	 
 523  	 	 } 
 524  	 	 else
 525  	 	 {
 526  	 	 	 if ($offset > 0 && $nrows < 0);
 527  	 	 	 
 528  	 	 	 else 
 529  	 	 	 {
 530  	 	 	 	 $nrows += $offset;
 531  	 	 	 	 $sql .=  " FETCH FIRST $nrows ROWS ONLY ";
 532  	 	 	 }
 533  	 	 	 
 534  	 	 	 /*
 535  	 	 	  * DB2 has no native support for mid table offset
 536  	 	 	  */
 537  	 	 	 $rs = ADOConnection::selectLimit($sql,$nrows,$offset,$inputArr);
 538  	 	 
 539  	 	 }
 540  	 	 
 541  	 	 return $rs;
 542  	 }
 543  
 544  	 
 545  	function errorMsg()
 546  	 {
 547  	 	 if ($this->_errorMsg !== false) 
 548  	 	 	 return $this->_errorMsg;
 549  	 	 
 550  	 	 if (empty($this->_connectionID)) 
 551  	 	 	 return @db2_conn_errormsg();
 552  	 	 
 553  	 	 return @db2_conn_errormsg($this->_connectionID);
 554  	 }
 555  
 556  	function errorNo()
 557  	 {
 558  
 559  	 	 if ($this->_errorCode !== false) 
 560  	 	 	 return $this->_errorCode;
 561  	 	 
 562  
 563  	 	 if (empty($this->_connectionID)) 
 564  	 	 	 $e = @db2_conn_error();
 565  	 	 
 566  	 	 else 
 567  	 	 	 $e = @db2_conn_error($this->_connectionID);
 568  	 	 
 569  	 	 return $e;
 570  	 }
 571  
 572  
 573  
 574  	function beginTrans()
 575  	 {
 576  	 	 if (!$this->hasTransactions) 
 577  	 	 	 return false;
 578  	 	 if ($this->transOff) 
 579  	 	 	 return true;
 580  	 	 
 581  	 	 $this->transCnt += 1;
 582  	 	 
 583  	 	 $this->_autocommit = false;
 584  	 	 
 585  	 	 return db2_autocommit($this->_connectionID,false);
 586  	 }
 587  
 588  	function CommitTrans($ok=true)
 589  	 {
 590  	 	 if ($this->transOff)
 591  	 	 	 return true;
 592  	 	 
 593  	 	 if (!$ok) 
 594  	 	 	 return $this->RollbackTrans();
 595  	 	 
 596  	 	 if ($this->transCnt)
 597  	 	 	 $this->transCnt -= 1;
 598  	 	 
 599  	 	 $this->_autocommit = true;
 600  	 	 $ret = @db2_commit($this->_connectionID);
 601  	 	 @db2_autocommit($this->_connectionID,true);
 602  	 	 return $ret;
 603  	 }
 604  
 605  	function RollbackTrans()
 606  	 {
 607  	 	 if ($this->transOff) return true;
 608  	 	 if ($this->transCnt) $this->transCnt -= 1;
 609  	 	 $this->_autocommit = true;
 610  	 	 $ret = @db2_rollback($this->_connectionID);
 611  	 	 @db2_autocommit($this->_connectionID,true);
 612  	 	 return $ret;
 613  	 }
 614  
 615  	 /**
 616        * Return a list of Primary Keys for a specified table
 617  	   *
 618  	   * We don't use db2_statistics as the function does not seem to play
 619  	   * well with mixed case table names
 620        *
 621        * @param string   $table
 622        * @param bool     $primary    (optional) only return primary keys
 623        * @param bool     $owner      (optional) not used in this driver
 624        *
 625        * @return string[]    Array of indexes
 626        */
 627  	public function metaPrimaryKeys($table,$owner=false)
 628  	 {
 629  	     
 630  	 	 $primaryKeys = array();
 631  	 	 
 632  	 	 global $ADODB_FETCH_MODE;
 633  
 634  	 	 $schema = '';
 635  	 	 $this->_findschema($table,$schema);
 636  	 	 
 637  	 	 $table = $this->getTableCasedValue($table);
 638  
 639  	 	 $savem 	 	 	   = $ADODB_FETCH_MODE;
 640  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 641  	 	 $this->setFetchMode(ADODB_FETCH_NUM);
 642  
 643  
 644          $sql = "SELECT * 
 645  	 	 	 	   FROM syscat.indexes
 646  	 	 	 	  WHERE tabname='$table'";
 647  	 	  
 648  	 	 $rows = $this->getAll($sql);
 649  	 	 
 650  	 	 $this->setFetchMode($savem);
 651  	 	 $ADODB_FETCH_MODE = $savem;
 652  
 653  	 	 if (empty($rows))
 654  	 	 	 return false;
 655          
 656  	 	 foreach ($rows as $r)
 657  	 	 {
 658  	 	 	 if ($r[7] != 'P')
 659  	 	 	 	 continue;
 660  	 	 	 
 661  	 	 	 $cols = explode('+',$r[6]);
 662  	 	 	 foreach ($cols as $colIndex=>$col)
 663  	 	 	 {
 664  	 	 	 	 if ($colIndex == 0)
 665  	 	 	 	 	 continue;
 666  	 	 	 	 $columnName = $this->getMetaCasedValue($col);
 667  	 	 	 	 $primaryKeys[] = $columnName;
 668  	 	 	 }
 669  	 	 	 break;
 670  	 	 }
 671  	 	 return $primaryKeys;
 672  	 }
 673  
 674  	 /**
 675  	  * returns assoc array where keys are tables, and values are foreign keys
 676  	  *
 677  	  * @param	 string	 $table
 678  	  * @param	 string	 $owner	 	 [optional][discarded]
 679  	  * @param	 bool	 $upper	 	 [optional][discarded]
 680  	  * @param	 bool	 $associative[optional][discarded]
 681  	  *
 682  	  * @return	 mixed[]	 	 	 Array of foreign key information
 683  	  */
 684  	public function metaForeignKeys($table, $owner = FALSE, $upper = FALSE, $asociative = FALSE )
 685  	 {
 686  	 	 
 687  	 	 global $ADODB_FETCH_MODE;
 688  
 689  	 	 $schema = '';
 690  	 	 $this->_findschema($table,$schema);
 691  
 692  	 	 $savem = $ADODB_FETCH_MODE;
 693  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 694  	 	 
 695  	 	 $this->setFetchMode(ADODB_FETCH_NUM);
 696  	 	 
 697  	 	 $sql = "SELECT SUBSTR(tabname,1,20) table_name,
 698  	 	 	 	 	    SUBSTR(constname,1,20) fk_name,
 699  	 	 	 	 	    SUBSTR(REFTABNAME,1,12) parent_table,
 700  	 	 	 	 	    SUBSTR(refkeyname,1,20) pk_orig_table,
 701  	 	 	 	 	    fk_colnames 
 702  	 	 	 	  FROM syscat.references 
 703  	 	 	 	 WHERE tabname = '$table'";
 704  	 	 
 705  	 	 $results = $this->getAll($sql);
 706  	 	 
 707  	 	 $ADODB_FETCH_MODE = $savem;
 708  	 	 $this->setFetchMode($savem);
 709  
 710  	 	 if (empty($results))
 711  	 	 	 return false;
 712  	 	 
 713  	 	 $foreignKeys = array();
 714  	 	 
 715  	 	 foreach ($results as $r)
 716  	 	 {
 717  	 	 	 $parentTable = trim($this->getMetaCasedValue($r[2]));
 718  	 	 	 $keyName     = trim($this->getMetaCasedValue($r[1]));
 719  	 	 	 $foreignKeys[$parentTable] = $keyName;
 720  	 	 }
 721  	 	 
 722  	 	 return $foreignKeys;
 723  	 }
 724  
 725  	 /**
 726  	   * Returns a list of tables
 727  	   *
 728  	   * @param string	 $ttype (optional)
 729  	   * @param	 string	 $schema	 (optional)
 730  	   * @param	 string	 $mask	 (optional)
 731  	   *
 732  	   * @return array
 733  	   */
 734  	public function metaTables($ttype=false,$schema=false,$mask=false)
 735  	 {
 736  	 	 
 737  	 	 global $ADODB_FETCH_MODE;
 738  
 739  	 	 $savem 	 	 	   = $ADODB_FETCH_MODE;
 740  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 741  	 	 
 742  	 	 /*
 743  	 	 * Values for TABLE_TYPE
 744  	 	 * ---------------------------
 745  	 	 * ALIAS, HIERARCHY TABLE, INOPERATIVE VIEW, NICKNAME, 
 746  	 	 * MATERIALIZED QUERY TABLE, SYSTEM TABLE, TABLE, 
 747  	 	 * TYPED TABLE, TYPED VIEW, and VIEW
 748  	 	 *
 749  	 	 * If $ttype passed as '', match 'TABLE' and 'VIEW'
 750  	 	 * If $ttype passed as 'T' it is assumed to be 'TABLE'
 751  	 	 * if $ttype passed as 'V' it is assumed to be 'VIEW'
 752  	 	 */
 753  	 	 $ttype = strtoupper($ttype);
 754  	 	 if ($ttype) {
 755  	 	 	 /*
 756  	 	 	  * @todo We could do valid type checking or array type
 757  	 	 	  */	 	 	 
 758  	 	 	  if ($ttype == 'V')
 759  	 	 	 	 $ttype = 'VIEW';
 760  	 	 	 if ($ttype == 'T')
 761  	 	 	 	 $ttype = 'TABLE';
 762  	 	 }
 763  	 	 	 
 764  	 	 if (!$schema)
 765  	 	 	 $schema = '%';
 766  	 	 
 767  	 	 if (!$mask)
 768  	 	 	 $mask = '%';
 769  	 	 
 770  	 	 $qid = @db2_tables($this->_connectionID,NULL,$schema,$mask,$ttype);
 771  
 772  	 	 $rs = new ADORecordSet_db2($qid);
 773  	 	 
 774  	 	 $ADODB_FETCH_MODE = $savem;
 775  	 	 
 776  	 	 if (!$rs)
 777  	 	 	 return false;
 778  	 	 
 779  	 	 $arr = $rs->getArray();
 780  	 	 
 781  	 	 $rs->Close();
 782  	 	 
 783  	 	 $tableList = array();
 784  
 785  	 	 /*
 786  	 	 * Array items
 787  	 	 * ---------------------------------
 788  	 	 * 0 TABLE_CAT	 The catalog that contains the table. 
 789  	 	 *	 	 	 	 The value is NULL if this table does not have catalogs.
 790  	 	 * 1 TABLE_SCHEM	 Name of the schema that contains the table.
 791  	 	 * 2 TABLE_NAME	 Name of the table.
 792  	 	 * 3 TABLE_TYPE	 Table type identifier for the table.
 793  	 	 * 4 REMARKS	 	 Description of the table.
 794  	 	 */
 795  	 	 
 796  	 	 for ($i=0; $i < sizeof($arr); $i++) 
 797  	 	 {
 798  	 	 	 
 799  	 	 	 $tableRow = $arr[$i];
 800  	 	 	 $tableName = $tableRow[2];
 801  	 	 	 $tableType = $tableRow[3];
 802  	 	 	 
 803  	 	 	 if (!$tableName) 
 804  	 	 	 	 continue;
 805  	 	 	 
 806  	 	 	 if ($ttype == '' && (strcmp($tableType,'TABLE') <> 0 && strcmp($tableType,'VIEW') <> 0))
 807  	 	 	 	 continue;
 808  	 	 	 
 809  	 	 	 /*
 810  	 	 	  * Set metacasing if required
 811  	 	 	  */
 812  	 	 	 $tableName = $this->getMetaCasedValue($tableName);
 813  	 	 	 
 814  	 	 	 /*
 815  	 	 	  * If we requested a schema, we prepend the schema 
 816  	 	 	    name to the table name
 817  	 	 	  */
 818  	 	 	 if (strcmp($schema,'%') <> 0)
 819  	 	 	 	 $tableName = $schema . '.' . $tableName;
 820  	 	 	 
 821  	 	 	 $tableList[] = $tableName;
 822  	 	 	 
 823  	 	 }
 824  	 	 return $tableList;
 825  	 }
 826      
 827      /**
 828        * Return a list of indexes for a specified table
 829  	   *
 830  	   * We don't use db2_statistics as the function does not seem to play
 831  	   * well with mixed case table names
 832        *
 833        * @param string   $table
 834        * @param bool     $primary    (optional) only return primary keys
 835        * @param bool     $owner      (optional) not used in this driver
 836        *
 837        * @return string[]    Array of indexes
 838        */
 839      public function metaIndexes($table, $primary = false, $owner = false) {
 840          
 841  	 	 global $ADODB_FETCH_MODE;
 842  
 843  	 	  /* Array(
 844  	 	  *   [name_of_index] => Array(
 845  	 	  *     [unique] => true or false
 846  	 	  *     [columns] => Array(
 847  	 	  *       [0] => firstcol
 848  	 	  *       [1] => nextcol
 849           *       [2] => etc........
 850  	 	  *     )
 851  	 	  *   )
 852  	 	  * )
 853  	 	  */
 854  	 	 $indices 	 	 = array();
 855  	 	 $primaryKeyName = '';
 856  	 	 
 857  	 	 $table = $this->getTableCasedValue($table);
 858  
 859  	 	 
 860  	 	 $savem 	 	 	   = $ADODB_FETCH_MODE;
 861  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 862  	 	 $this->setFetchMode(ADODB_FETCH_NUM);
 863  
 864          $sql = "SELECT * 
 865  	 	 	 	   FROM syscat.indexes
 866  	 	 	 	  WHERE tabname='$table'";
 867  	 	  
 868  	 	 $rows = $this->getAll($sql);
 869  	 	 
 870  	 	 $this->setFetchMode($savem);
 871  	 	 $ADODB_FETCH_MODE = $savem;
 872  	 	 
 873  	 	 if (empty($rows))
 874  	 	 	 return false;
 875  
 876          foreach ($rows as $r)
 877  	 	 {
 878  	 	 	 
 879  	 	 	 $primaryIndex = $r[7] == 'P'?1:0;
 880  	 	 	 if (!$primary)
 881  	 	 	 	 /*
 882  	 	 	      * Primary key not requested, ignore that one
 883  	 	 	 	  */
 884  	 	 	 	 if ($r[7] == 'P')
 885  	 	 	 	 	 continue;
 886  	 	 	 	 
 887  	 	 	 $indexName = $this->getMetaCasedValue($r[1]);
 888  	 	 	 if (!isset($indices[$indexName]))
 889  	 	 	 {
 890  	 	 	 	 $unique = ($r[7] == 'U')?1:0;
 891  	 	 	 	 $indices[$indexName] = array('unique'=>$unique,
 892  	 	 	 	 	 	 	 	 	 	 	  'primary'=>$primaryIndex,
 893  	 	 	 	 	 	 	 	 	 	      'columns'=>array()
 894  	 	 	 	 	 	 	 	 	 	 );
 895  	 	 	 }
 896  	 	 	 $cols = explode('+',$r[6]);
 897  	 	 	 foreach ($cols as $colIndex=>$col)
 898  	 	 	 {
 899  	 	 	 	 if ($colIndex == 0)
 900  	 	 	 	 	 continue;
 901  	 	 	 	 $columnName = $this->getMetaCasedValue($col);
 902  	 	 	 	 $indices[$indexName]['columns'][] = $columnName;
 903  	 	 	 }
 904  	 	 	 
 905  	 	 }
 906          
 907  	 	 return $indices;
 908         
 909      }
 910  	 
 911  	 /**
 912  	  * List procedures or functions in an array.
 913  	  *
 914       * We interrogate syscat.routines instead of calling the PHP 
 915  	  * function procedures because ADOdb requires the type of procedure
 916  	  * this is not available in the php function
 917  	  *
 918  	  * @param	 string $procedureNamePattern (optional)
 919  	  * @param	 string $catalog	 	 	 	  (optional)
 920  	  * @param	 string $schemaPattern	 	  (optional)
 921  	  
 922  	  * @return array of procedures on current database.
 923  	  *
 924  	  */
 925  	public function metaProcedures($procedureNamePattern = null, $catalog  = null, $schemaPattern  = null) {
 926  	 	 
 927  	 	 
 928  	 	 global $ADODB_FETCH_MODE;
 929  	 	 	 	 
 930  	 	 $metaProcedures = array();
 931  	 	 $procedureSQL   = '';
 932  	 	 $catalogSQL     = '';
 933  	 	 $schemaSQL      = '';
 934  	 	 
 935  	 	 $savem 	 	 	   = $ADODB_FETCH_MODE;
 936  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 937  	 	 
 938  	 	 if ($procedureNamePattern)
 939  	 	 	 $procedureSQL = "AND ROUTINENAME LIKE " . strtoupper($this->qstr($procedureNamePattern));
 940  	 	 
 941  	 	 if ($catalog)
 942  	 	 	 $catalogSQL = "AND OWNER=" . strtoupper($this->qstr($catalog));
 943  	 	 
 944  	 	 if ($schemaPattern)
 945  	 	 	 $schemaSQL = "AND ROUTINESCHEMA LIKE {$this->qstr($schemaPattern)}";
 946  	 	 
 947  	 	 	 	 
 948  	 	 $fields = "
 949  	 	 ROUTINENAME,
 950  	 	 CASE ROUTINETYPE
 951  	 	 	  WHEN 'P' THEN 'PROCEDURE'
 952  	 	 	  WHEN 'F' THEN 'FUNCTION'
 953  	 	 	  ELSE 'METHOD'
 954  	 	 	  END AS ROUTINETYPE_NAME,
 955  	 	 ROUTINESCHEMA,
 956  	 	 REMARKS";
 957  	 	 
 958  	 	 $SQL = "SELECT $fields
 959  	 	 	       FROM syscat.routines 
 960  	 	 	 	  WHERE OWNER IS NOT NULL
 961  	 	 	 	   $procedureSQL
 962  	 	 	 	   $catalogSQL
 963  	 	 	 	   $schemaSQL
 964  	 	 	 	 ORDER BY ROUTINENAME
 965  	 	 	 	 ";
 966  	 	 
 967  	 	 $result = $this->execute($SQL);
 968  	 	 
 969  	 	 $ADODB_FETCH_MODE = $savem;
 970  	 	 
 971  	 	 if (!$result)
 972  	 	 	 return false;
 973  	 	 
 974  	 	 while ($r = $result->fetchRow()){
 975  	 	 	 $procedureName = $this->getMetaCasedValue($r[0]);
 976  	 	 	 $schemaName    = $this->getMetaCasedValue($r[2]);
 977  	 	 	 $metaProcedures[$procedureName] = array('type'=> $r[1],
 978  	 	 	 	 	 	 	 	 	 	 	 	    'catalog' => '',
 979  	 	 	 	 	 	 	 	 	 	 	 	    'schema'  => $schemaName,
 980  	 	 	 	 	 	 	 	 	 	 	 	    'remarks' => $r[3]
 981  	 	 	 	 	 	 	 	 	 	 	 	     );
 982  	 	 }
 983  	 	 
 984  	 	 return $metaProcedures;
 985  	 	 
 986  	 }
 987  	 
 988  	 /**
 989  	   * Lists databases. Because instances are independent, we only know about
 990  	   * the current database name
 991  	   *
 992  	   * @return string[]
 993  	   */
 994  	public function metaDatabases(){
 995  	 	 
 996  	 	 $dbName = $this->getMetaCasedValue($this->databaseName);
 997  	 	 
 998  	 	 return (array)$dbName;
 999  	 	 
1000  	 }
1001  	 
1002      
1003     
1004  
1005  /*
1006  See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2datetime_data_type_changes.asp
1007  / SQL data type codes /
1008  #define	 SQL_UNKNOWN_TYPE	 0
1009  #define SQL_CHAR	 	 	 1
1010  #define SQL_NUMERIC	 	  2
1011  #define SQL_DECIMAL	 	  3
1012  #define SQL_INTEGER	 	  4
1013  #define SQL_SMALLINT	 	 5
1014  #define SQL_FLOAT	 	    6
1015  #define SQL_REAL	 	 	 7
1016  #define SQL_DOUBLE	 	   8
1017  #if (DB2VER >= 0x0300)
1018  #define SQL_DATETIME	 	 9
1019  #endif
1020  #define SQL_VARCHAR	 	 12
1021  
1022  
1023  / One-parameter shortcuts for date/time data types /
1024  #if (DB2VER >= 0x0300)
1025  #define SQL_TYPE_DATE	   91
1026  #define SQL_TYPE_TIME	   92
1027  #define SQL_TYPE_TIMESTAMP 93
1028  
1029  #define SQL_UNICODE                             (-95)
1030  #define SQL_UNICODE_VARCHAR                     (-96)
1031  #define SQL_UNICODE_LONGVARCHAR                 (-97)
1032  */
1033  	function DB2Types($t)
1034  	 {
1035  	 	 switch ((integer)$t) {
1036  	 	 case 1:
1037  	 	 case 12:
1038  	 	 case 0:
1039  	 	 case -95:
1040  	 	 case -96:
1041  	 	 	 return 'C';
1042  	 	 case -97:
1043  	 	 case -1: //text
1044  	 	 	 return 'X';
1045  	 	 case -4: //image
1046  	 	 	 return 'B';
1047  
1048  	 	 case 9:
1049  	 	 case 91:
1050  	 	 	 return 'D';
1051  
1052  	 	 case 10:
1053  	 	 case 11:
1054  	 	 case 92:
1055  	 	 case 93:
1056  	 	 	 return 'T';
1057  
1058  	 	 case 4:
1059  	 	 case 5:
1060  	 	 case -6:
1061  	 	 	 return 'I';
1062  
1063  	 	 case -11: // uniqidentifier
1064  	 	 	 return 'R';
1065  	 	 case -7: //bit
1066  	 	 	 return 'L';
1067  
1068  	 	 default:
1069  	 	 	 return 'N';
1070  	 	 }
1071  	 }
1072  
1073  	public function metaColumns($table, $normalize=true)
1074  	 {
1075  	 	 global $ADODB_FETCH_MODE;
1076  	 	 
1077  	 	 $savem = $ADODB_FETCH_MODE;
1078  
1079  	 	 $schema = '%';
1080  	 	 $this->_findschema($table,$schema);
1081  	 	 $table = $this->getTableCasedValue($table);
1082         	 $colname = "%";
1083  	     $qid = db2_columns($this->_connectionID, null, $schema, $table, $colname);
1084  	 	 if (empty($qid))
1085  	 	 {	 	 	 
1086  	 	 	 if ($this->debug)
1087  	 	 	 {
1088  	 	 	 	 $errorMessage = @db2_conn_errormsg($this->_connectionID);
1089  	 	 	 	 ADOConnection::outp($errorMessage);
1090  	 	 	 }
1091  	 	 	 return false;
1092  	 	 }
1093  
1094  	 	 $rs = new ADORecordSet_db2($qid);
1095  
1096  	 	 if (!$rs) 
1097  	 	 	 return false;
1098  	 	 
1099  	 	 $rs->_fetch();
1100  
1101  	 	 $retarr = array();
1102  
1103  	 	 /*
1104  	 	 $rs->fields indices
1105  	 	 0 TABLE_QUALIFIER
1106  	 	 1 TABLE_SCHEM
1107  	 	 2 TABLE_NAME
1108  	 	 3 COLUMN_NAME
1109  	 	 4 DATA_TYPE
1110  	 	 5 TYPE_NAME
1111  	 	 6 PRECISION
1112  	 	 7 LENGTH
1113  	 	 8 SCALE
1114  	 	 9 RADIX
1115  	 	 10 NULLABLE
1116  	 	 11 REMARKS
1117          12 Column Default
1118          13 SQL Data Type
1119          14 SQL DateTime SubType
1120          15 Max length in Octets
1121          16 Ordinal Position
1122          17 Is NULLABLE
1123  	 	 */
1124  	 	 while (!$rs->EOF) 
1125  	 	 {
1126  	 	 	 if ($rs->fields[2] == $table) 
1127  	 	 	 {
1128  	 	 	 	 
1129  	 	 	 	 $fld       = new ADOFieldObject();
1130  	 	 	 	 $fld->name = $rs->fields[3];
1131  	 	 	 	 $fld->type = $this->DB2Types($rs->fields[4]);
1132  
1133  	 	 	 	 // ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp
1134  	 	 	 	 // access uses precision to store length for char/varchar
1135  	 	 	 	 
1136  	 	 	 	 if ($fld->type == 'C' or $fld->type == 'X') {
1137  	 	 	 	 	 if ($rs->fields[4] <= -95) // UNICODE
1138  	 	 	 	 	 	 $fld->max_length = $rs->fields[7]/2;
1139  	 	 	 	 	 else
1140  	 	 	 	 	 	 $fld->max_length = $rs->fields[7];
1141  	 	 	 	 } else
1142  	 	 	 	 	 $fld->max_length = $rs->fields[7];
1143  	 	 	 	 
1144  	 	 	 	 $fld->not_null         = !empty($rs->fields[10]);
1145  	 	 	 	 $fld->scale            = $rs->fields[8];
1146  	 	 	 	 $fld->primary_key      = false;
1147  	 	 	 	 
1148  	 	 	 	 //$columnName = $this->getMetaCasedValue($fld->name);
1149  	 	 	 	 $columnName = strtoupper($fld->name);
1150  	 	 	 	 $retarr[$columnName] = $fld;
1151  	 	 	 
1152  	 	 	 } 
1153  	 	 	 else if (sizeof($retarr)>0)
1154  	 	 	 	 break;
1155  	 	 	 
1156  	 	 	 $rs->MoveNext();
1157  	 	 
1158  	 	 }
1159  	 	 
1160  	 	 $rs->Close();
1161  	 	 if (empty($retarr)) 
1162  	 	 	 $retarr = false;
1163  
1164  	     /*
1165  	 	  * Now we find out if the column is part of a primary key
1166  	 	  */
1167  	 	 
1168  	 	 $qid = @db2_primary_keys($this->_connectionID, "", $schema, $table);
1169  	 	 if (empty($qid)) 
1170  	 	 	 return false;
1171  
1172  	 	 $rs = new ADORecordSet_db2($qid);
1173  
1174  	 	 if (!$rs)
1175  	 	 {	 
1176  	 	 	 $ADODB_FETCH_MODE = $savem;
1177  	 	 	 return $retarr;
1178  	 	 }	 
1179  	 	 $rs->_fetch();
1180  
1181  	 	 /*
1182  	 	 $rs->fields indices
1183  	 	 0 TABLE_CAT
1184  	 	 1 TABLE_SCHEM
1185  	 	 2 TABLE_NAME
1186  	 	 3 COLUMN_NAME
1187  	 	 4 KEY_SEQ
1188  	 	 5 PK_NAME
1189  	 	 */
1190  	 	 while (!$rs->EOF) {
1191  	 	 	 if (strtoupper(trim($rs->fields[2])) == $table 
1192  	 	 	 && (!$schema || strtoupper($rs->fields[1]) == $schema))
1193  	 	 	 {
1194  	 	 	 	 $retarr[strtoupper($rs->fields[3])]->primary_key = true;
1195  	 	 	 } 
1196  	 	 	 else if (sizeof($retarr)>0)
1197  	 	 	 	 break;
1198  	 	 	 
1199  	 	 	 $rs->MoveNext();
1200  	 	 }
1201  	 	 $rs->Close();
1202  
1203  	 	 $ADODB_FETCH_MODE = $savem;
1204  
1205  	 	 if (empty($retarr)) 
1206  	 	 	 return false;
1207  	 	 
1208  	 	 /*
1209  	 	 * If the fetch mode is numeric, return as numeric array
1210  	 	 */
1211  	 	 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM)
1212  	 	 	 $retarr = array_values($retarr);
1213  	 	 
1214  	 	 return $retarr;
1215  	 }
1216  
1217  	 /**
1218  	   * In this version if prepareSp, we just check to make sure
1219  	   * that the name of the stored procedure is correct
1220  	   * If true, we returns an array
1221  	   * else false
1222  	   *
1223  	   * @param	 string	 $procedureName
1224  	   * @param	 mixed   $parameters (not used in db2 connections)
1225  	   * @return mixed[]
1226  	   */
1227  	function prepareSp($procedureName,$parameters=false) {
1228  	 	 
1229  	 	 global $ADODB_FETCH_MODE;
1230  	 	 
1231  	 	 $this->storedProcedureParameters = array('name'=>'',
1232  	 	 	 	 	 	 	 	 	 	 	 	  'resource'=>false,
1233  	 	 	 	 	 	 	 	 	 	 	 	  'in'=>array(),
1234  	 	 	 	 	 	 	 	 	 	 	 	  'out'=>array(),
1235  	 	 	 	 	 	 	 	 	 	 	 	  'index'=>array(),
1236  	 	 	 	 	 	 	 	 	 	 	 	  'parameters'=>array(),
1237  	 	 	 	 	 	 	 	 	 	 	 	  'keyvalue' => array());
1238  	 	 
1239  	 	 //$procedureName = strtoupper($procedureName);
1240  	 	 //$procedureName = $this->getTableCasedValue($procedureName);
1241  
1242  	 	 $savem = $ADODB_FETCH_MODE;
1243  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
1244  	 	 
1245  	 	 $qid = db2_procedures($this->_connectionID, NULL , '%' , $procedureName );
1246  	 	 	 	 
1247  	 	 $ADODB_FETCH_MODE = $savem;
1248  	 	 
1249  	 	 if (!$qid)
1250  	 	 {
1251  	 	 	 if ($this->debug)
1252  	 	 	 	 ADOConnection::outp(sprintf('No Procedure of name %s available',$procedureName));
1253  	 	 	 return false;
1254  	 	 }
1255  	 	 
1256  	 	 
1257  	 	 	 	 
1258  	 	 $this->storedProcedureParameters['name'] = $procedureName;
1259  	 	 /*
1260  	 	  * Now we know we have a valid procedure name, lets see if it requires 
1261  	 	  * parameters
1262  	 	  */
1263  	 	 $savem = $ADODB_FETCH_MODE;
1264  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
1265  	 	 
1266  	 	 $qid = db2_procedure_columns($this->_connectionID, NULL , '%' , $procedureName , NULL );
1267  	 	 
1268  	 	 $ADODB_FETCH_MODE = $savem;
1269  
1270  	 	 if (!$qid)
1271  	 	 {
1272  	 	 	 if ($this->debug)
1273  	 	 	 	 ADOConnection::outp(sprintf('No columns of name %s available',$procedureName));
1274  	 	 	 return false;
1275  	 	 }
1276  	 	 $rs = new ADORecordSet_db2($qid);
1277  	 	 if (!$rs) 
1278  	 	 	 return false;
1279  	 	 
1280  	 	 $preparedStatement = 'CALL %s(%s)';
1281  	 	 $parameterMarkers = array();
1282  	 	 while (!$rs->EOF)
1283  	 	 {
1284  	 	 	 $parameterName = $rs->fields[3];
1285  	 	 	 if ($parameterName == '')
1286  	 	 	 {
1287  	 	 	 	 $rs->moveNext();
1288  	 	 	 	 continue;
1289  	 	 	 }
1290  	 	 	 $parameterType = $rs->fields[4];
1291  	 	 	 $ordinalPosition = $rs->fields[17];
1292  	 	 	 switch($parameterType)
1293  	 	 	 {
1294  	 	 	 case DB2_PARAM_IN:
1295  	 	 	 case DB2_PARAM_INOUT:
1296  	 	 	     $this->storedProcedureParameters['in'][$parameterName] = '';
1297  	 	 	 	 break;
1298  	 	 	 case DB2_PARAM_INOUT:
1299  	 	 	 case DB2_PARAM_OUT:
1300  	 	 	     $this->storedProcedureParameters['out'][$parameterName] = '';
1301  	 	 	 	 break;	 
1302  	 	 	 }
1303  	 	 	 $this->storedProcedureParameters['index'][$parameterName] = $ordinalPosition;
1304  	 	 	 $this->storedProcedureParameters['parameters'][$ordinalPosition] = $rs->fields;
1305  	 	 	 $rs->moveNext();
1306  
1307  	 	 }
1308  	 	 $parameterCount = count($this->storedProcedureParameters['index']);
1309  	 	 $parameterMarkers = array_fill(0,$parameterCount,'?');
1310  	 	 
1311  	 	 /*
1312  	 	  * We now know how many parameters to bind to the stored procedure
1313  	 	  */
1314  	 	 $parameterList = implode(',',$parameterMarkers);
1315  	 	 
1316  	 	 $sql = sprintf($preparedStatement,$procedureName,$parameterList);
1317  	 	 
1318  	 	 $spResource = @db2_prepare($this->_connectionID,$sql);
1319  	 	 
1320  	 	 if (!$spResource)
1321  	 	 {
1322  	 	 	 $errorMessage = @db2_conn_errormsg($this->_connectionID);
1323  	 	 	 $this->_errorMsg = $errorMessage;
1324  	 	 	 
1325  	 	 	 if ($this->debug)
1326  	 	 	 	 ADOConnection::outp($errorMessage);
1327  	 	 	 
1328  	 	 	 return false;
1329  	 	 }
1330  	 	 
1331  	 	 $this->storedProcedureParameters['resource'] = $spResource;
1332  	 	 
1333  	 	 if ($this->debug)
1334  	 	 {
1335  	 	 	 
1336  	 	 	 ADOConnection::outp('The following parameters will be used in the SP call');
1337  	 	 	 ADOConnection::outp(print_r($this->storedProcedureParameters));
1338  	 	 }
1339  	 	 /*
1340  	 	  * We now have a stored parameter resource 
1341  	 	  * to bind to. The spResource and sql that is returned are
1342  	 	  * not usable, its for dummy compatibility. Everything
1343  	 	  * will be handled by the storedProcedureParameters 
1344  	 	  * array
1345  	 	  */
1346  	 	 return array($sql,$spResource);
1347  	 	 
1348  	 }
1349  	 
1350  	private function storedProcedureParameter(&$stmt, 
1351  	 	 	 	 	 	 	 	 	 	 	   &$var, 
1352  	 	 	 	 	 	 	 	 	 	 	   $name, 
1353  	 	 	 	 	 	 	 	 	 	 	   $isOutput=false, 
1354  	 	 	 	 	 	 	 	 	 	 	   $maxLen=4000, 
1355  	 	 	 	 	 	 	 	 	 	 	   $type=false)
1356  	 {
1357  	 	 
1358  	 	 	 	 
1359  	 	 $name = strtoupper($name);
1360  	 	 
1361  	 	 /*
1362  	 	  * Must exist in the list of parameter names for the type
1363  	 	  */
1364  	 	 if ($isOutput 
1365  	 	 && !isset( $this->storedProcedureParameters['out'][$name]))
1366  	 	 {
1367  	 	 	 $errorMessage = sprintf('%s is not a valid OUT parameter name',$name);
1368  	 	 	 
1369  	 	 	 $this->_errorMsg = $errorMessage;
1370  	 	 	 if ($this->debug)
1371  	 	 	 	 ADOConnection::outp($errorMessage);
1372  	 	 	 return false;
1373  	 	 }
1374  	 	 
1375  	 	 if (!$isOutput 
1376  	 	 && !isset( $this->storedProcedureParameters['in'][$name]))
1377  	 	 {
1378  	 	 	 $errorMessage = sprintf('%s is not a valid IN parameter name',$name);
1379  	 	 	 
1380  	 	 	 $this->_errorMsg = $errorMessage;
1381  	 	 	 if ($this->debug)
1382  	 	 	 	 ADOConnection::outp($errorMessage);
1383  	 	 	 return false;
1384  	 	 }
1385  	 	 
1386  	 	 /*
1387  	 	  * We will use these values to bind to when we execute
1388  	 	  * the query
1389  	 	  */
1390  	 	 $this->storedProcedureParameters['keyvalue'][$name] = &$var;
1391  	 	 
1392  	 	 return true;
1393  	 	 
1394  	 }
1395  	 
1396  	 /**
1397  	 * Executes a prepared stored procedure. 
1398  	 * 
1399  	 * The function uses the previously accumulated information and
1400  	 * resources in the $storedProcedureParameters array
1401  	 *
1402  	 * @return mixed	 The statement id if successful, or false
1403  	 */
1404  	private function executeStoredProcedure()
1405  	 {
1406  	 
1407  	 	 /*
1408  	 	  * Get the previously built resource
1409  	 	  */
1410  	 	 $stmtid = $this->storedProcedureParameters['resource'];
1411  	 	 	 	 	 
1412  	 	 /*
1413  	 	  * Bind our variables to the DB2 procedure
1414  	 	  */
1415  	 	 foreach ($this->storedProcedureParameters['keyvalue'] as $spName=>$spValue){
1416  	 	 	 
1417  	 	 	 /*
1418  	 	 	  * Get the ordinal position, required for binding
1419  	 	 	  */
1420  	 	 	 $ordinalPosition = $this->storedProcedureParameters['index'][$spName];
1421  	 	 
1422  	 	 	 /*
1423  	 	 	  * Get the db2 column dictionary for the parameter
1424  	 	 	  */
1425  	 	 	 $columnDictionary = $this->storedProcedureParameters['parameters'][$ordinalPosition];
1426  	 	 	 $parameterType    = $columnDictionary[4];
1427  	 	 	 $dataType         = $columnDictionary[5];
1428  	 	 	 $precision        = $columnDictionary[10];
1429  	 	 	 $scale        	   = $columnDictionary[9];
1430  	 	 	 
1431  	 	 	 $ok = @db2_bind_param ($this->storedProcedureParameters['resource'], 
1432  	 	 	 	 	 	 	 	   $ordinalPosition , 
1433  	 	 	 	 	 	 	 	   $spName,
1434  	 	 	 	 	 	 	 	   $parameterType,
1435  	 	 	 	 	 	 	 	   $dataType,
1436  	 	 	 	 	 	 	 	   $precision,
1437  	 	 	 	 	 	 	 	   $scale
1438  	 	 	 	 	 	 	 	   );
1439  	 	 	 
1440  	 	 	 if (!$ok)
1441  	 	 	 {
1442  	 	 	 	 $this->_errorMsg  = @db2_stmt_errormsg();
1443  	 	 	 	 $this->_errorCode = @db2_stmt_error();
1444  
1445  	 	 	 	 if ($this->debug) 
1446  	 	 	 	 	 ADOConnection::outp($this->_errorMsg);	 
1447  	 	 	 	 return false;
1448  	 	 	 }
1449  	 	 	 
1450  	 	 	 if ($this->debug)
1451  	 	 	 	 ADOConnection::outp("Correctly Bound parameter $spName to procedure");	 
1452  	 	 	 
1453  	 	 	 /*
1454  	 	 	  * Build a variable in the current environment that matches 
1455  	 	 	  * the parameter name
1456  	 	 	  */
1457  	 	 	 ${$spName} = $spValue;
1458  	 	 	 
1459  	 	 }
1460  	 	 
1461  	 	 /*
1462  	 	  * All bound, execute
1463  	 	  */
1464  	 	 	 	 	 
1465  	 	 if (!@db2_execute($stmtid)) 
1466  	 	 {
1467  	 	 	 $this->_errorMsg = @db2_stmt_errormsg();
1468  	 	 	 $this->_errorCode = @db2_stmt_error();
1469  	 	 	 
1470  	 	 	 if ($this->debug) 
1471  	 	 	 	 ADOConnection::outp($this->_errorMsg);	 
1472  	 	 	 return false;
1473  	 	 }
1474  	 	 
1475  	 	 /*
1476  	 	  * We now take the changed parameters back into the
1477  	 	  * stored procedures array where we can query them later
1478  	 	  * Remember that $spValue was passed in by reference, so we
1479  	 	  * can access the value in the variable that was originally
1480  	 	  * passed to inParameter or outParameter
1481  	 	  */
1482  	 	 foreach ($this->storedProcedureParameters['keyvalue'] as $spName=>$spValue)
1483  	 	 {
1484  	 	 	 /*
1485  	 	 	  * We make it available to the environment
1486  	 	 	  */
1487  	 	 	 $spValue = ${$spName};
1488  	 	 	 $this->storedProcedureParameters['keyvalue'][$spName] = $spValue;
1489  	 	 }
1490  	 	 
1491  	 	 return $stmtid;
1492  	 }
1493  	 
1494  	 /**
1495  	 *
1496      * Accepts an input or output parameter to bind to either a stored
1497      * or prepared statements. For DB2, this should not be called as an
1498  	 * API. always wrap with inParameter and outParameter
1499      *
1500  	 * @param mixed[] $stmt 	 	 Statement returned by Prepare() or PrepareSP().
1501  	 * @param mixed   $var 	 	 PHP variable to bind to. Can set to null (for isNull support).
1502  	 * @param string  $name 	 	 Name of stored procedure variable name to bind to.
1503  	 * @param int	  $isOutput 	 optional) Indicates direction of parameter 
1504  	 * 	 	 	 	 	 	 	 0/false=IN  1=OUT  2= IN/OUT
1505  	 *	 	 	 	 	 	 	 This is ignored for Stored Procedures
1506  	 * @param int	 $maxLen	 	 (optional)Holds an maximum length of the variable.
1507  	 *	 	 	 	 	 	 	 This is ignored for Stored Procedures
1508  	 * @param int	 $type 	 	 (optional) The data type of $var.
1509  	 *	 	 	 	 	 	 	 This is ignored for Stored Procedures
1510      *
1511  	 * @return bool	 	 	 	 Success of the operation
1512  	 */
1513  	public function parameter(&$stmt, &$var, $name, $isOutput=false, $maxLen=4000, $type=false)
1514  	 {
1515  	 	 
1516  	 	 /*
1517  	 	  * If the $stmt is the name of a stored procedure we are
1518  	 	  * setting up, we will process it one way, otherwise
1519  	 	  * we assume we are setting up a prepared statement
1520  	 	 */
1521  	 	 if (is_array($stmt))
1522  	 	 {
1523  	 	 	 if ($this->debug)
1524  	 	 	 	 ADOConnection::outp("Adding parameter to stored procedure");
1525  	 	 	 if ($stmt[1] == $this->storedProcedureParameters['resource'])
1526  	 	 	 	 return $this->storedProcedureParameter($stmt[1], 
1527  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $var, 
1528  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $name, 
1529  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $isOutput, 
1530  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $maxLen,
1531  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $type);
1532  	 	 	 	 	 	 	 	 	 	 	 	 
1533  	 	 }
1534  	 	 
1535  	 	 /*
1536  	 	  * We are going to add a parameter to a prepared statement
1537  	 	  */
1538  	 	 if ($this->debug)
1539  	 	 	 ADOConnection::outp("Adding parameter to prepared statement");
1540  	 }
1541  	 
1542  	 
1543  	 /**
1544  	 * Prepares a prepared SQL statement, not used for stored procedures
1545  	 *
1546  	 * @param string	 $sql
1547  	 * 
1548  	 * @return mixed
1549  	 */
1550  	function prepare($sql)
1551  	 {
1552  	 	 
1553  	 	 if (! $this->_bindInputArray) return $sql; // no binding
1554  	 	 
1555  	 	 $stmt = @db2_prepare($this->_connectionID,$sql);
1556  	 	 if (!$stmt) {
1557  	 	 	 // we don't know whether db2 driver is parsing prepared stmts, so just return sql
1558  	 	 	 return $sql;
1559  	 	 }
1560  	 	 return array($sql,$stmt,false);
1561  	 }
1562  
1563  	 /**
1564   	 * Executes a query 
1565  	 * 
1566  	 * @param	 mixed $sql
1567  	 * @param	 mixed $inputarr	 An optional array of parameters
1568  	 * 
1569  	 * @return mixed	 	 	 	 either the queryID or false
1570  	 */
1571  	function _query(&$sql,$inputarr=false)
1572  	 {
1573          
1574  	 	 $this->_error = '';
1575  
1576  	 	 $db2Options = array();
1577  	 	 /*
1578           * Use DB2 Internal case handling for best speed
1579           */
1580  	 	 switch(ADODB_ASSOC_CASE)
1581  	 	 {
1582          case ADODB_ASSOC_CASE_UPPER:
1583              $db2Options = array('db2_attr_case'=>DB2_CASE_UPPER);
1584              $setOption = @db2_set_option($this->_connectionID,$db2Options,1);
1585              break;
1586           
1587  	 	  case ADODB_ASSOC_CASE_LOWER:
1588              $db2Options = array('db2_attr_case'=>DB2_CASE_LOWER);
1589              $setOption = @db2_set_option($this->_connectionID,$db2Options,1);
1590              break;
1591          
1592  	 	 default:
1593              $db2Options = array('db2_attr_case'=>DB2_CASE_NATURAL);
1594              $setOption = @db2_set_option($this->_connectionID,$db2Options,1);
1595          }
1596              
1597  	 	 $db2Options = array('db2_attr_case'=>DB2_CASE_LOWER);
1598          $setOption = @db2_set_option($this->_connectionID,$db2Options,1);	 
1599  	 	 
1600          if ($inputarr)
1601  	 	 {
1602  	 	 	 if (is_array($sql))
1603  	 	 	 {
1604  	 	 	 	 $stmtid = $sql[1];
1605  	 	 	 } 
1606  	 	 	 else
1607  	 	 	 {
1608  	 	 	 	 $stmtid = @db2_prepare($this->_connectionID,$sql);
1609  
1610  	 	 	 	 if ($stmtid == false)
1611  	 	 	 	 {
1612                      $this->_errorMsg  = @db2_stmt_errormsg();
1613                      $this->_errorCode = @db2_stmt_error();
1614  
1615                      if ($this->debug)
1616                          ADOConnection::outp($this->_errorMsg);
1617  
1618  	 	 	 	 	 return false;
1619  	 	 	 	 }
1620  	 	 	 }
1621  
1622  	 	 	 if (! @db2_execute($stmtid,$inputarr))
1623  	 	 	 {
1624  	 	 	 	 $this->_errorMsg = @db2_stmt_errormsg();
1625  	 	 	 	 $this->_errorCode = @db2_stmt_error();
1626  	 	 	 	 if ($this->debug) 
1627  	 	 	 	 	 ADOConnection::outp($this->_errorMsg);	 
1628  	 	 	 	 return false;
1629  	 	 	 }
1630  
1631  	 	 } 
1632  	 	 else if (is_array($sql))
1633  	 	 {
1634  	 	 	 
1635  	 	 	 /*
1636  	 	 	  * Either a prepared statement or a stored procedure
1637  	 	 	  */
1638  	 	 	 
1639  	 	 	 if (is_array($this->storedProcedureParameters)
1640  	 	 	 	 && is_resource($this->storedProcedureParameters['resource']
1641  	 	 	 )) 
1642  	 	 	 	 /*
1643  	 	 	 	  * This is all handled in the separate method for
1644  	 	 	 	  * readability
1645  	 	 	 	  */
1646  	 	 	 	 return $this->executeStoredProcedure();
1647  	 	 	 	 
1648  	 	 	 /*
1649  	 	 	  * First, we prepare the statement
1650  	 	 	  */
1651  	 	 	 $stmtid = @db2_prepare($this->_connectionID,$sql[0]);
1652  	 	 	 if (!$stmtid){
1653  	 	 	 	 $this->_errorMsg = @db2_stmt_errormsg();
1654  	 	 	 	 $this->_errorCode = @db2_stmt_error();
1655  	 	 	 	 if ($this->debug)
1656  	 	 	 	 	 ADOConnection::outp("Prepare failed: " . $this->_errorMsg);
1657  
1658  	 	 	 	 return false;
1659  	 	 	 }
1660  	 	 	 /*
1661  	 	 	  * We next bind some input parameters
1662  	 	 	  */
1663  	 	 	 $ordinal = 1;
1664  	 	 	 foreach ($sql[1] as $psVar=>$psVal){
1665  	 	 	 	 ${$psVar} = $psVal;
1666  	 	 	 	 $ok = @db2_bind_param($stmtid, $ordinal, $psVar, DB2_PARAM_IN);
1667  	 	 	 	 if (!$ok)
1668  	 	 	 	 {
1669  	 	 	 	 	 $this->_errorMsg = @db2_stmt_errormsg();
1670  	 	 	 	 	 $this->_errorCode = @db2_stmt_error();
1671  	 	 	 	 	 if ($this->debug)
1672  	 	 	 	 	 	 ADOConnection::outp("Bind failed: " . $this->_errorMsg);
1673  	 	 	 	 	 return false;
1674  	 	 	 	 }
1675  	 	 	 }
1676  	 	 	 
1677  	 	 	 if (!@db2_execute($stmtid)) 
1678  	 	 	 {
1679  	 	 	 	 $this->_errorMsg = @db2_stmt_errormsg();
1680  	 	 	 	 $this->_errorCode = @db2_stmt_error();
1681  	 	 	 	 if ($this->debug) 
1682  	 	 	 	 	 ADOConnection::outp($this->_errorMsg);	 
1683  	 	 	 	 return false;
1684  	 	 	 }
1685  	 	 	 
1686  	 	 	 return $stmtid;
1687  	 	 }
1688  	 	 else
1689  	 	 {
1690  
1691  	 	 	 $stmtid = @db2_exec($this->_connectionID,$sql);
1692          }
1693  	 	 $this->_lastAffectedRows = 0;
1694  	 	 if ($stmtid)
1695  	 	 {
1696  	 	 	 if (@db2_num_fields($stmtid) == 0)
1697  	 	 	 {
1698  	 	 	 	 $this->_lastAffectedRows = db2_num_rows($stmtid);
1699  	 	 	 	 $stmtid = true;
1700  	 	 	 } 
1701  	 	 	 else 
1702  	 	 	 {
1703  	 	 	 	 $this->_lastAffectedRows = 0;
1704  	 	 	 }
1705  
1706  	 	 	 $this->_errorMsg = '';
1707  	 	 	 $this->_errorCode = 0;
1708  	 	 	 
1709  	 	 }
1710  	 	 else
1711  	 	 {
1712  	 	 	 
1713  	 	 	 $this->_errorMsg = @db2_stmt_errormsg();
1714  	 	 	 $this->_errorCode = @db2_stmt_error();
1715  	 	 
1716  	 	 }
1717  	 	 return $stmtid;
1718  	 }
1719  
1720  	 /*
1721  	 	 Insert a null into the blob field of the table first.
1722  	 	 Then use UpdateBlob to store the blob.
1723  
1724  	 	 Usage:
1725  
1726  	 	 $conn->execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
1727  	 	 $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
1728  	 */
1729  	function updateBlob($table,$column,$val,$where,$blobtype='BLOB')
1730  	 {
1731  	 	 return $this->execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
1732  	 }
1733  
1734  	 // returns true or false
1735  	function _close()
1736  	 {
1737  	 	 $ret = @db2_close($this->_connectionID);
1738  	 	 $this->_connectionID = false;
1739  	 	 return $ret;
1740  	 }
1741  
1742  	function _affectedrows()
1743  	 {
1744  	 	 return $this->_lastAffectedRows;
1745  	 }
1746  	 
1747  	 /**
1748  	   * Gets a meta cased parameter 
1749  	   *
1750  	   * Receives an input variable to be processed per the metaCasing
1751  	   * rule, and returns the same value, processed
1752  	   *
1753  	   * @param string $value
1754  	   *
1755  	   * @return string
1756  	   */
1757  	final public function getMetaCasedValue($value)
1758  	 {
1759  	 	 global $ADODB_ASSOC_CASE;
1760  	 	 
1761  	 	 switch($ADODB_ASSOC_CASE)
1762  	 	 {
1763  	 	 case ADODB_ASSOC_CASE_LOWER:
1764  	 	 	 $value = strtolower($value);
1765  	 	 	 break;
1766  	 	 case ADODB_ASSOC_CASE_UPPER:
1767  	 	 	 $value = strtoupper($value);
1768  	 	 	 break;
1769  	 	 }
1770  	 	 return $value;
1771  	 }
1772  	 
1773  	 
1774  	 const TABLECASE_LOWER    =  0;
1775      const TABLECASE_UPPER    =  1;
1776      const TABLECASE_DEFAULT  =  2;
1777  	 
1778  	 /*
1779  	  * Controls the casing of the table provided to the meta functions
1780  	  */
1781  	 private $tableCase = 2;
1782  	 
1783  	 /**
1784  	   * Sets the table case parameter 
1785  	   *
1786  	   * @param int $caseOption
1787  	   * @return null
1788  	   */
1789  	final public function setTableCasing($caseOption)
1790  	 {
1791  	 	 $this->tableCase = $caseOption;
1792  	 }
1793  	 
1794  	 /**
1795  	   * Gets the table casing parameter 
1796  	   *
1797  	   * @return int $caseOption
1798  	   */
1799  	final public function getTableCasing()
1800  	 {
1801  	 	 return $this->tableCase;
1802  	 }
1803  	 
1804  	 /**
1805  	   * Gets a table cased parameter 
1806  	   *
1807  	   * Receives an input variable to be processed per the tableCasing
1808  	   * rule, and returns the same value, processed
1809  	   *
1810  	   * @param string $value
1811  	   *
1812  	   * @return string
1813  	   */
1814  	final public function getTableCasedValue($value)
1815  	 {
1816  	 	 switch($this->tableCase)
1817  	 	 {
1818  	 	 case self::TABLECASE_LOWER:
1819  	 	 	 $value = strtolower($value);
1820  	 	 	 break;
1821  	 	 case self::TABLECASE_UPPER:
1822  	 	 	 $value = strtoupper($value);
1823  	 	 	 break;
1824  	 	 }
1825  	 	 return $value;
1826  	 }
1827  
1828  }
1829  
1830  /*--------------------------------------------------------------------------------------
1831  	  Class Name: Recordset
1832  --------------------------------------------------------------------------------------*/
1833  
1834  class ADORecordSet_db2 extends ADORecordSet {
1835  
1836  	 var $bind = false;
1837  	 var $databaseType = "db2";
1838  	 var $dataProvider = "db2";
1839  	 var $useFetchArray;
1840  
1841  	function __construct($id,$mode=false)
1842  	 {
1843  	 	 if ($mode === false) {
1844  	 	 	 global $ADODB_FETCH_MODE;
1845  	 	 	 $mode = $ADODB_FETCH_MODE;
1846  	 	 }
1847  	 	 $this->fetchMode = $mode;
1848  
1849  	 	 $this->_queryID = $id;
1850  	 }
1851  
1852  
1853  	 // returns the field object
1854  	function fetchField($offset = 0)
1855  	 {
1856  	 	 $o	 	 	    = new ADOFieldObject();
1857  	 	 $o->name 	    = @db2_field_name($this->_queryID,$offset);
1858  	 	 $o->type 	    = @db2_field_type($this->_queryID,$offset);
1859  	 	 $o->max_length = @db2_field_width($this->_queryID,$offset);
1860  	 	 
1861  	 	 /*
1862  	 	 if (ADODB_ASSOC_CASE == 0) 
1863  	 	 	 $o->name = strtolower($o->name);
1864  	 	 else if (ADODB_ASSOC_CASE == 1) 
1865  	 	 	 $o->name = strtoupper($o->name);
1866  	 	 */
1867  	 	 return $o;
1868  	 }
1869  
1870  	 /* Use associative array to get fields array */
1871  	function fields($colname)
1872  	 {
1873  	 	 
1874  	 	 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
1875  	 	 	 return $this->fields[$colname];
1876          }
1877  	 	 
1878  	 	 if (!$this->bind) {
1879  	 	 	 $this->bind = array();
1880  	 	 	 for ($i=0; $i < $this->_numOfFields; $i++) {
1881  	 	 	 	 $o = $this->FetchField($i);
1882  	 	 	 	 $this->bind[strtoupper($o->name)] = $i;
1883  	 	 	 }
1884  	 	 }
1885  
1886  	 	  return $this->fields[$this->bind[strtoupper($colname)]];
1887  	 }
1888  
1889  
1890  	function _initrs()
1891  	 {
1892  	 	 global $ADODB_COUNTRECS;
1893  	 	 $this->_numOfRows = ($ADODB_COUNTRECS) ? @db2_num_rows($this->_queryID) : -1;
1894  	 	 
1895  	 	 $this->_numOfFields = @db2_num_fields($this->_queryID);
1896  	 	 
1897  	 	 // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0
1898  	 	 
1899  	 	 if ($this->_numOfRows == 0) 
1900  	 	 	 $this->_numOfRows = -1;
1901  	 }
1902  
1903  	function _seek($row)
1904  	 {
1905  	 	 return false;
1906  	 }
1907  
1908  	function getArrayLimit($nrows,$offset=0)
1909  	 {
1910  	 	 if ($offset <= 0) {
1911  	 	 	 $rs = $this->GetArray($nrows);
1912  	 	 	 return $rs;
1913  	 	 }
1914  	 	 
1915  	 	 $this->Move($offset);
1916  
1917  
1918  	 	 $results = array();
1919  	 	 $cnt = 0;
1920  	 	 while (!$this->EOF && $nrows != $cnt) {
1921  	 	 	 $results[$cnt++] = $this->fields;
1922  	 	 	 $this->MoveNext();
1923  	 	 }
1924  	 	 
1925  	 	 return $results;
1926  	 }
1927  
1928  	function moveNext()
1929  	 {
1930  	 	 if ($this->EOF || $this->_numOfRows == 0)
1931  	 	 	 return false;
1932  	 	 
1933  	 	 $this->_currentRow++;
1934  	 	 	 
1935  	 	 $this->processCoreFetch();
1936  	 	 return $this->processMoveRecord();
1937  	 	 
1938  	 }
1939      
1940      private function processCoreFetch()
1941      {
1942          switch ($this->fetchMode){
1943  	 	 case ADODB_FETCH_ASSOC:
1944  	 	 	 
1945  	 	 	 /*
1946  	 	 	  * Associative array
1947  	 	 	  */
1948  	 	 	 $this->fields = @db2_fetch_assoc($this->_queryID);
1949  	 	 	 break;
1950  	 	 	 
1951  	 	 case ADODB_FETCH_BOTH:
1952  	 	 	 /*
1953  	 	 	  * Fetch both numeric and Associative array
1954  	 	 	  */
1955  	 	 	 $this->fields = @db2_fetch_both($this->_queryID);
1956  	 	 	 break;
1957  	 	 default:
1958  	 	 	 /*
1959  	 	 	  * Numeric array
1960  	 	 	  */
1961  	 	 	 $this->fields = @db2_fetch_array($this->_queryID);
1962  	 	 	 break;
1963  	 	 }
1964      }
1965  
1966  	private function processMoveRecord()
1967      {
1968  	 	 if (!$this->fields){
1969  	 	 	 $this->EOF = true;
1970  	 	 	 return false;
1971  	 	 }
1972  	 	    
1973  	 	 return true;
1974  	 }
1975      
1976      function _fetch()
1977  	 {
1978          $this->processCoreFetch();
1979          if ($this->fields) 
1980  	 	 	 return true;
1981  	 	 
1982  	 	 $this->fields = false;
1983  	 	 return false;
1984  	 }
1985  
1986  	function _close()
1987  	 {
1988  	 	 $ok = @db2_free_result($this->_queryID);
1989  	 	 if (!$ok)
1990  	 	 {
1991  	 	 	 $this->_errorMsg  = @db2_stmt_errormsg($this->_queryId);
1992  	 	 	 $this->_errorCode = @db2_stmt_error();
1993  
1994  	 	 	 if ($this->debug) 
1995  	 	 	 	 ADOConnection::outp($this->_errorMsg);	 
1996  	 	 	 return false;
1997  	 	 }
1998  	 	 
1999  	 }
2000  
2001  }