Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

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

   1  <?php
   2  /**
   3   * IBM DB2 Native Client driver.
   4   *
   5   * Originally DB2 drivers were dependent on an ODBC driver, and some installations
   6   * may still use that. To use an ODBC driver connection, use the odbc_db2
   7   * ADOdb driver. For Linux, you need the 'ibm_db2' PECL extension for PHP,
   8   * For Windows, you need to locate an appropriate version of the php_ibm_db2.dll,
   9   * as well as the IBM data server client software.
  10   * This is basically a full rewrite of the original driver, for information
  11   * about all the changes, see the update information on the ADOdb website
  12   * for version 5.21.0.
  13   *
  14   * @link http://pecl.php.net/package/ibm_db2 PECL Extension For DB2
  15   *
  16   * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
  17   *
  18   * @package ADOdb
  19   * @link https://adodb.org Project's web site and documentation
  20   * @link https://github.com/ADOdb/ADOdb Source code and issue tracker
  21   *
  22   * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause
  23   * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option,
  24   * any later version. This means you can use it in proprietary products.
  25   * See the LICENSE.md file distributed with this source code for details.
  26   * @license BSD-3-Clause
  27   * @license LGPL-2.1-or-later
  28   *
  29   * @copyright 2000-2013 John Lim
  30   * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
  31   * @author Mark Newnham
  32   */
  33  
  34  // security - hide paths
  35  if (!defined('ADODB_DIR')) die();
  36  
  37  define("_ADODB_DB2_LAYER", 2 );
  38  
  39  
  40  class ADODB_db2 extends ADOConnection {
  41  	 var $databaseType = "db2";
  42  	 var $fmtDate = "'Y-m-d'";
  43  	 var $concat_operator = '||';
  44  
  45  	 var $sysTime = 'CURRENT TIME';
  46  	 var $sysDate = 'CURRENT DATE';
  47  	 var $sysTimeStamp = 'CURRENT TIMESTAMP';
  48  
  49  	 var $fmtTimeStamp = "'Y-m-d H:i:s'";
  50  	 var $replaceQuote = "''"; // string to use to replace quotes
  51  	 var $dataProvider = "db2";
  52  	 var $hasAffectedRows = true;
  53  
  54  	 var $binmode = DB2_BINARY;
  55  
  56  	 /*
  57  	 * setting this to true will make array elements in FETCH_ASSOC
  58  	 * mode case-sensitive breaking backward-compat
  59  	 */
  60  	 var $useFetchArray = false;
  61  	 var $_bindInputArray = true;
  62  	 var $_genIDSQL = "VALUES NEXTVAL FOR %s";
  63  	 var $_genSeqSQL = "
  64  	 CREATE SEQUENCE %s START WITH %s
  65  	 NO MAXVALUE NO CYCLE INCREMENT BY 1 NO CACHE
  66  	 ";
  67  	 var $_dropSeqSQL = "DROP SEQUENCE %s";
  68  	 var $_autocommit = true;
  69  	 var $_lastAffectedRows = 0;
  70  	 var $hasInsertID = true;
  71  	 var $hasGenID    = true;
  72  
  73  	 /*
  74  	  * Character used to wrap column and table names for escaping special
  75  	  * characters in column and table names as well as forcing upper and
  76  	  * lower case
  77  	  */
  78  	 public $nameQuote = '"';
  79  
  80  	 /*
  81  	  * Holds information about the stored procedure request
  82  	  * currently being built
  83  	  */
  84  	 private $storedProcedureParameters = false;
  85  
  86  
  87  	function __construct() {}
  88  
  89  	protected function _insertID($table = '', $column = '')
  90  	 {
  91  	 	 return ADOConnection::GetOne('VALUES IDENTITY_VAL_LOCAL()');
  92  	 }
  93  
  94  	public function _connect($argDSN, $argUsername, $argPassword, $argDatabasename)
  95  	 {
  96  	 	 return $this->doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename);
  97  	 }
  98  
  99  	public function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename)
 100  	 {
 101  	 	 return $this->doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename,true);
 102  	 }
 103  
 104  	private function doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename, $persistent=false)
 105  	 {
 106  	 	 
 107  	 	 if (!function_exists('db2_connect')) {
 108  	 	 	 ADOConnection::outp("DB2 extension not installed.");
 109  	 	 	 return null;
 110  	 	 }
 111  
 112  	 	 $connectionParameters = $this->unpackParameters($argDSN,
 113  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $argUsername,
 114  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $argPassword,
 115  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $argDatabasename);
 116  
 117  	 	 if ($connectionParameters == null)
 118  	 	 {
 119  	 	 	 /*
 120  	 	 	  * Error thrown
 121  	 	 	  */
 122  	 	 	 return null;
 123  	 	 }
 124  
 125  	 	 $argDSN 	 	          = $connectionParameters['dsn'];
 126  	 	 $argUsername 	          = $connectionParameters['uid'];
 127  	 	 $argPassword 	          = $connectionParameters['pwd'];
 128  	 	 $argDatabasename         = $connectionParameters['database'];
 129  	 	 $useCataloguedConnection = $connectionParameters['catalogue'];
 130  
 131  	 	 if ($this->debug){
 132  	 	 	 if ($useCataloguedConnection){
 133  	 	 	 	 $connectMessage = "Catalogued connection using parameters: ";
 134  	 	 	 	 $connectMessage .= "DB=$argDatabasename / ";
 135  	 	 	 	 $connectMessage .= "UID=$argUsername / ";
 136  	 	 	 	 $connectMessage .= "PWD=$argPassword";
 137  	 	 	 }
 138  	 	 	 else
 139  	 	 	 {
 140  	 	 	 	 $connectMessage = "Uncatalogued connection using DSN: $argDSN";
 141  	 	 	 }
 142  	 	 	 ADOConnection::outp($connectMessage);
 143  	 	 }
 144  	 	 /*
 145  	 	  * This needs to be set before the connect().
 146  	 	  */
 147  	 	 ini_set('ibm_db2.binmode', $this->binmode);
 148  
 149  	 	 if ($persistent)
 150  	 	 	 $db2Function = 'db2_pconnect';
 151  	 	 else
 152  	 	 	 $db2Function = 'db2_connect';
 153  
 154  	 	 /*
 155  	 	 * We need to flatten out the connectionParameters
 156  	 	 */
 157  
 158  	 	 $db2Options = array();
 159  	 	 if ($this->connectionParameters)
 160  	 	 {
 161  	 	 	 foreach($this->connectionParameters as $p)
 162  	 	 	 	 foreach($p as $k=>$v)
 163  	 	 	 	 	 $db2Options[$k] = $v;
 164  	 	 }
 165  
 166  	 	 if ($useCataloguedConnection)
 167  	 	 	 $this->_connectionID = $db2Function($argDatabasename,
 168  	 	 	 	 	 	 	 	 	 	 	 	 $argUsername,
 169  	 	 	 	 	 	 	 	 	 	 	 	 $argPassword,
 170  	 	 	 	 	 	 	 	 	 	 	 	 $db2Options);
 171  	 	 else
 172  	 	 	 $this->_connectionID = $db2Function($argDSN,
 173  	 	 	 	 	 	 	 	 	 	 	 	 null,
 174  	 	 	 	 	 	 	 	 	 	 	 	 null,
 175  	 	 	 	 	 	 	 	 	 	 	 	 $db2Options);
 176  
 177  	 	 
 178  	 	 $this->_errorMsg = @db2_conn_errormsg();
 179  
 180  	 	 if ($this->_connectionID && $this->connectStmt)
 181  	 	 	 $this->execute($this->connectStmt);
 182  
 183  	 	 return $this->_connectionID != false;
 184  
 185  	 }
 186  
 187  	 /**
 188  	  * Validates and preprocesses the passed parameters for consistency
 189  	  *
 190  	  * @param	 string	 $argDSN	 	 	 	 Either DSN or database
 191  	  * @param	 string	 $argUsername	 	 User name or null
 192  	  * @param	 string	 $argPassword	 	 Password or null
 193  	  * @param	 string	 $argDatabasename	 Either DSN or database
 194  	  *
 195  	  * @return mixed  array if correct, null if not
 196  	  */
 197  	private function unpackParameters($argDSN, $argUsername, $argPassword, $argDatabasename)
 198  	 {
 199  
 200  	 	 
 201  	 	 $connectionParameters = array('dsn'=>'',
 202  	 	 	 	 	 	 	 	 	   'uid'=>'',
 203  	 	 	 	 	 	 	 	 	   'pwd'=>'',
 204  	 	 	 	 	 	 	 	 	   'database'=>'',
 205  	 	 	 	 	 	 	 	 	   'catalogue'=>true
 206  	 	 	 	 	 	 	 	 	   );
 207  
 208  	 	 /*
 209  	 	  * Uou can either connect to a catalogued connection
 210  	 	  * with a database name e.g. 'SAMPLE'
 211  	 	  * or an uncatalogued connection with a DSN like connection
 212  	 	  * DATABASE=database;HOSTNAME=hostname;PORT=port;PROTOCOL=TCPIP;UID=username;PWD=password;
 213  	 	  */
 214  
 215  	 	 if (!$argDSN && !$argDatabasename)
 216  	 	 {
 217  	 	 	 $errorMessage = 'Supply either catalogued or uncatalogued connection parameters';
 218  	 	 	 $this->_errorMsg = $errorMessage;
 219  	 	 	 if ($this->debug)
 220  	 	 	 	 ADOConnection::outp($errorMessage);
 221  	 	 	 return null;
 222  	 	 }
 223  
 224  	 	 $useCataloguedConnection = true;
 225  	 	 $schemaName 	 	 	  = '';
 226  
 227  	 	 if ($argDSN && $argDatabasename)
 228  	 	 {
 229  	 	 	 /*
 230  	 	 	  * If a catalogued connection if provided,
 231  	 	 	  * as well as user and password
 232  	 	 	  * that will take priority
 233  	 	 	  */
 234  	 	 	 if ($argUsername && $argPassword && !$this->isDsn($argDatabasename))
 235  	 	 	 {
 236  	 	 	 	 if ($this->debug){
 237  	 	 	 	 	 $errorMessage = 'Warning: Because you provided user,';
 238  	 	 	 	 	 $errorMessage.= 'password and database, DSN connection ';
 239  	 	 	 	 	 $errorMessage.= 'parameters were discarded';
 240  	 	 	 	 	 ADOConnection::outp($errorMessage);
 241  
 242  	 	 	 	 }
 243  	 	 	 	 $argDSN = '';
 244  	 	 	 }
 245  	 	 	 else if ($this->isDsn($argDSN) && $this->isDsn($argDatabasename))
 246  	 	 	 {
 247  	 	 	 	 $errorMessage = 'Supply uncatalogued connection parameters ';
 248  	 	 	 	 $errorMessage.= 'in either the database or DSN arguments, ';
 249  	 	 	 	 $errorMessage.= 'but not both';
 250  	 	 	 
 251  	 	 	 	 if ($this->debug)
 252  	 	 	 	 	 ADOConnection::outp($errorMessage);
 253  	 	 	 	 return null;
 254  	 	 	 }
 255  	 	 }
 256  
 257  	 	 if (!$this->isDsn($argDSN) && $this->isDsn($argDatabasename))
 258  	 	 {
 259  	 	 	 /*
 260  	 	 	  * Switch them around for next test
 261  	 	 	  */
 262  	 	 	 $temp           = $argDSN;
 263  	 	 	 $argDsn         = $argDatabasename;
 264  	 	 	 $argDatabasenME = $temp;
 265  	 	 }
 266  
 267  	 	 if ($this->isDsn($argDSN))
 268  	 	 {
 269  
 270  	 	 	 if (!preg_match('/uid=/i',$argDSN)
 271  	 	 	 ||  !preg_match('/pwd=/i',$argDSN))
 272  	 	 	 {
 273  	 	 	 	 $errorMessage = 'For uncatalogued connections, provide ';
 274  	 	 	 	 $errorMessage.= 'both UID and PWD in the connection string';
 275  	 	 	 	 
 276  	 	 	 	 if ($this->debug)
 277  	 	 	 	 	 ADOConnection::outp($errorMessage);
 278  	 	 	 	 return null;
 279  	 	 	 }
 280  
 281  	 	 	 if (preg_match('/database=/i',$argDSN))
 282  	 	 	 {
 283  	 	 	 	 if ($argDatabasename)
 284  	 	 	 	 {
 285  	 	 	 	 	 $argDatabasename = '';
 286  	 	 	 	 	 if ($this->debug)
 287  	 	 	 	 	 {
 288  	 	 	 	 	 	 $errorMessage = 'Warning: Because you provided ';
 289  	 	 	 	 	 	 $errorMessage.= 'database information in the DSN ';
 290  	 	 	 	 	 	 $errorMessage.= 'parameters, the supplied database ';
 291  	 	 	 	 	 	 $errorMessage.= 'name was discarded';
 292  	 	 	 	 	 	 ADOConnection::outp($errorMessage);
 293  	 	 	 	 	 }
 294  	 	 	 	 }
 295  	 	 	 	 $useCataloguedConnection = false;
 296  
 297  	 	 	 }
 298  	 	 	 elseif ($argDatabasename)
 299  	 	 	 {
 300  	 	 	 	 $this->database = $argDatabasename;
 301  	 	 	 	 $argDSN .= ';database=' . $argDatabasename;
 302  	 	 	 	 $argDatabasename = '';
 303  	 	 	 	 $useCataloguedConnection = false;
 304  
 305  	 	 	 }
 306  	 	 	 else
 307  	 	 	 {
 308  	 	 	 	 $errorMessage = 'Uncatalogued connection parameters ';
 309  	 	 	 	 $errorMessage.= 'must contain a database= argument';
 310  	 	 	 	 
 311  	 	 	 	 if ($this->debug)
 312  	 	 	 	 	 ADOConnection::outp($errorMessage);
 313  	 	 	 	 return null;
 314  	 	 	 }
 315  	 	 }
 316  
 317  	 	 if ($argDSN && !$argDatabasename && $useCataloguedConnection)
 318  	 	 {
 319  	 	 	 $argDatabasename = $argDSN;
 320  	 	 	 $argDSN          = '';
 321  	 	 }
 322  
 323  
 324  	 	 if ($useCataloguedConnection
 325  	 	 && (!$argDatabasename
 326  	 	 || !$argUsername
 327  	 	 || !$argPassword))
 328  	 	 {
 329  
 330  	 	 	 $errorMessage = 'For catalogued connections, provide ';
 331  	 	 	 $errorMessage.= 'database, username and password';
 332  	 	 	 $this->_errorMsg = $errorMessage;
 333  	 	 	 if ($this->debug)
 334  	 	 	 	 ADOConnection::outp($errorMessage);
 335  	 	 	 return null;
 336  
 337  	 	 }
 338  
 339  	 	 if ($argDatabasename)
 340  	 	 	 $this->database = $argDatabasename;
 341  	 	 elseif (!$this->database)
 342  	 	 	 $this->database = $this->getDatabasenameFromDsn($argDSN);
 343  
 344  
 345  	 	 $connectionParameters = array('dsn'=>$argDSN,
 346  	 	 	 	 	 	 	 	 	   'uid'=>$argUsername,
 347  	 	 	 	 	 	 	 	 	   'pwd'=>$argPassword,
 348  	 	 	 	 	 	 	 	 	   'database'=>$argDatabasename,
 349  	 	 	 	 	 	 	 	 	   'catalogue'=>$useCataloguedConnection
 350  	 	 	 	 	 	 	 	 	   );
 351  
 352  	 	 return $connectionParameters;
 353  
 354  	 }
 355  
 356  	 /**
 357  	   * Does the provided string look like a DSN
 358  	   *
 359  	   * @param	 string	 $dsnString
 360  	   *
 361  	   * @return bool
 362  	   */
 363  	private function isDsn($dsnString){
 364  	 	 $dsnArray = preg_split('/[;=]+/',$dsnString);
 365  	 	 if (count($dsnArray) > 2)
 366  	 	 	 return true;
 367  	 	 return false;
 368  	 }
 369  
 370  
 371  	 /**
 372  	   * Gets the database name from the DSN
 373  	   *
 374  	   * @param	 string	 $dsnString
 375  	   *
 376  	   * @return string
 377  	   */
 378  	private function getDatabasenameFromDsn($dsnString){
 379  
 380  	 	 $dsnArray = preg_split('/[;=]+/',$dsnString);
 381  	 	 $dbIndex  = array_search('database',$dsnArray);
 382  
 383  	 	 return $dsnArray[$dbIndex + 1];
 384  	 }
 385  
 386  
 387  	 /**
 388  	 * format and return date string in database timestamp format
 389  	 *
 390  	 * @param	 mixed	 $ts	 	 either a string or a unixtime
 391  	 * @param	 bool	 $isField	 discarded
 392  	 *
 393  	 * @return string
 394  	 */
 395  	function dbTimeStamp($ts,$isField=false)
 396  	 {
 397  	 	 if (empty($ts) && $ts !== 0) return 'null';
 398  	 	 if (is_string($ts)) $ts = ADORecordSet::unixTimeStamp($ts);
 399  	 	 return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'YYYY-MM-DD HH24:MI:SS')";
 400  	 }
 401  
 402  	 /**
 403  	 * Format date column in sql string given an input format that understands Y M D
 404  	 *
 405  	 * @param	 string	 $fmt
 406  	 * @param	 bool	 $col
 407  	 *
 408  	 * @return string
 409  	 */
 410  	function sqlDate($fmt, $col=false)
 411  	 {
 412  	 	 if (!$col) $col = $this->sysDate;
 413  
 414  	 	 /* use TO_CHAR() if $fmt is TO_CHAR() allowed fmt */
 415  	 	 if ($fmt== 'Y-m-d H:i:s')
 416  	 	 	 return 'TO_CHAR('.$col.", 'YYYY-MM-DD HH24:MI:SS')";
 417  
 418  	 	 $s = '';
 419  
 420  	 	 $len = strlen($fmt);
 421  	 	 for ($i=0; $i < $len; $i++) {
 422  	 	 	 if ($s) $s .= $this->concat_operator;
 423  	 	 	 $ch = $fmt[$i];
 424  	 	 	 switch($ch) {
 425  	 	 	 case 'Y':
 426  	 	 	 case 'y':
 427  	 	 	 	 if ($len==1) return "year($col)";
 428  	 	 	 	 $s .= "char(year($col))";
 429  	 	 	 	 break;
 430  	 	 	 case 'M':
 431  	 	 	 	 if ($len==1) return "monthname($col)";
 432  	 	 	 	 $s .= "substr(monthname($col),1,3)";
 433  	 	 	 	 break;
 434  	 	 	 case 'm':
 435  	 	 	 	 if ($len==1) return "month($col)";
 436  	 	 	 	 $s .= "right(digits(month($col)),2)";
 437  	 	 	 	 break;
 438  	 	 	 case 'D':
 439  	 	 	 case 'd':
 440  	 	 	 	 if ($len==1) return "day($col)";
 441  	 	 	 	 $s .= "right(digits(day($col)),2)";
 442  	 	 	 	 break;
 443  	 	 	 case 'H':
 444  	 	 	 case 'h':
 445  	 	 	 	 if ($len==1) return "hour($col)";
 446  	 	 	 	 if ($col != $this->sysDate) $s .= "right(digits(hour($col)),2)";
 447  	 	 	 	 else $s .= "''";
 448  	 	 	 	 break;
 449  	 	 	 case 'i':
 450  	 	 	 case 'I':
 451  	 	 	 	 if ($len==1) return "minute($col)";
 452  	 	 	 	 if ($col != $this->sysDate)
 453  	 	 	 	 	 $s .= "right(digits(minute($col)),2)";
 454  	 	 	 	 	 else $s .= "''";
 455  	 	 	 	 break;
 456  	 	 	 case 'S':
 457  	 	 	 case 's':
 458  	 	 	 	 if ($len==1) return "second($col)";
 459  	 	 	 	 if ($col != $this->sysDate)
 460  	 	 	 	 	 $s .= "right(digits(second($col)),2)";
 461  	 	 	 	 else $s .= "''";
 462  	 	 	 	 break;
 463  	 	 	 default:
 464  	 	 	 	 if ($ch == '\\') {
 465  	 	 	 	 	 $i++;
 466  	 	 	 	 	 $ch = substr($fmt,$i,1);
 467  	 	 	 	 }
 468  	 	 	 	 $s .= $this->qstr($ch);
 469  	 	 	 }
 470  	 	 }
 471  	 	 return $s;
 472  	 }
 473  
 474  
 475  	function serverInfo()
 476  	 {
 477  	 	 $sql = "SELECT service_level, fixpack_num
 478  	 	 	 	   FROM TABLE(sysproc.env_get_inst_info())
 479  	 	 	 	 	 AS INSTANCEINFO";
 480  	 	 $row = $this->GetRow($sql);
 481  
 482  
 483  	 	 if ($row) {
 484  	 	 	 $info['version'] = $row[0].':'.$row[1];
 485  	 	 	 $info['fixpack'] = $row[1];
 486  	 	 	 $info['description'] = '';
 487  	 	 } else {
 488  	 	 	 return ADOConnection::serverInfo();
 489  	 	 }
 490  
 491  	 	 return $info;
 492  	 }
 493  
 494  	function createSequence($seqname='adodbseq',$start=1)
 495  	 {
 496  	 	 if (empty($this->_genSeqSQL))
 497  	 	 	 return false;
 498  
 499  	 	 $ok = $this->execute(sprintf($this->_genSeqSQL,$seqname,$start));
 500  	 	 if (!$ok)
 501  	 	 	 return false;
 502  	 	 return true;
 503  	 }
 504  
 505  	function dropSequence($seqname='adodbseq')
 506  	 {
 507  	 	 if (empty($this->_dropSeqSQL)) return false;
 508  	 	 return $this->execute(sprintf($this->_dropSeqSQL,$seqname));
 509  	 }
 510  
 511  	function selectLimit($sql,$nrows=-1,$offset=-1,$inputArr=false,$secs2cache=0)
 512  	 {
 513  	 	 $nrows = (integer) $nrows;
 514  
 515  	 	 if ($offset <= 0)
 516  	 	 {
 517  	 	 	 if ($nrows >= 0)
 518  	 	 	 	 $sql .=  " FETCH FIRST $nrows ROWS ONLY ";
 519  
 520  	 	 	 $rs = $this->execute($sql,$inputArr);
 521  
 522  	 	 }
 523  	 	 else
 524  	 	 {
 525  	 	 	 if ($offset > 0 && $nrows < 0);
 526  
 527  	 	 	 else
 528  	 	 	 {
 529  	 	 	 	 $nrows += $offset;
 530  	 	 	 	 $sql .=  " FETCH FIRST $nrows ROWS ONLY ";
 531  	 	 	 }
 532  
 533  	 	 	 /*
 534  	 	 	  * DB2 has no native support for mid table offset
 535  	 	 	  */
 536  	 	 	 $rs = ADOConnection::selectLimit($sql,$nrows,$offset,$inputArr);
 537  
 538  	 	 }
 539  
 540  	 	 return $rs;
 541  	 }
 542  
 543  
 544  	function errorMsg()
 545  	 {
 546  	 	 if ($this->_errorMsg !== false)
 547  	 	 	 return $this->_errorMsg;
 548  
 549  	 	 if (empty($this->_connectionID))
 550  	 	 	 return @db2_conn_errormsg();
 551  
 552  	 	 return @db2_conn_errormsg($this->_connectionID);
 553  	 }
 554  
 555  	function errorNo()
 556  	 {
 557  
 558  	 	 if ($this->_errorCode !== false)
 559  	 	 	 return $this->_errorCode;
 560  
 561  
 562  	 	 if (empty($this->_connectionID))
 563  	 	 	 $e = @db2_conn_error();
 564  
 565  	 	 else
 566  	 	 	 $e = @db2_conn_error($this->_connectionID);
 567  
 568  	 	 return $e;
 569  	 }
 570  
 571  
 572  
 573  	function beginTrans()
 574  	 {
 575  	 	 if (!$this->hasTransactions)
 576  	 	 	 return false;
 577  	 	 if ($this->transOff)
 578  	 	 	 return true;
 579  
 580  	 	 $this->transCnt += 1;
 581  
 582  	 	 $this->_autocommit = false;
 583  
 584  	 	 return db2_autocommit($this->_connectionID,false);
 585  	 }
 586  
 587  	function CommitTrans($ok=true)
 588  	 {
 589  	 	 if ($this->transOff)
 590  	 	 	 return true;
 591  
 592  	 	 if (!$ok)
 593  	 	 	 return $this->RollbackTrans();
 594  
 595  	 	 if ($this->transCnt)
 596  	 	 	 $this->transCnt -= 1;
 597  
 598  	 	 $this->_autocommit = true;
 599  	 	 $ret = @db2_commit($this->_connectionID);
 600  	 	 @db2_autocommit($this->_connectionID,true);
 601  	 	 return $ret;
 602  	 }
 603  
 604  	function RollbackTrans()
 605  	 {
 606  	 	 if ($this->transOff) return true;
 607  	 	 if ($this->transCnt) $this->transCnt -= 1;
 608  	 	 $this->_autocommit = true;
 609  	 	 $ret = @db2_rollback($this->_connectionID);
 610  	 	 @db2_autocommit($this->_connectionID,true);
 611  	 	 return $ret;
 612  	 }
 613  
 614  	 /**
 615  	  * Return a list of Primary Keys for a specified table
 616  	  *
 617  	  * We don't use db2_statistics as the function does not seem to play
 618  	  * well with mixed case table names
 619  	  *
 620  	  * @param string   $table
 621  	  * @param bool     $primary    (optional) only return primary keys
 622  	  * @param bool     $owner      (optional) not used in this driver
 623  	  *
 624  	  * @return string[]    Array of indexes
 625  	  */
 626  	public function metaPrimaryKeys($table,$owner=false)
 627  	 {
 628  
 629  	 	 $primaryKeys = array();
 630  
 631  	 	 global $ADODB_FETCH_MODE;
 632  
 633  	 	 $schema = '';
 634  	 	 $this->_findschema($table,$schema);
 635  
 636  	 	 $table = $this->getTableCasedValue($table);
 637  
 638  	 	 $savem 	 	 	   = $ADODB_FETCH_MODE;
 639  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 640  	 	 $this->setFetchMode(ADODB_FETCH_NUM);
 641  
 642  
 643  	 	 $sql = "SELECT *
 644  	 	 	 	   FROM syscat.indexes
 645  	 	 	 	  WHERE tabname='$table'";
 646  
 647  	 	 $rows = $this->getAll($sql);
 648  
 649  	 	 $this->setFetchMode($savem);
 650  	 	 $ADODB_FETCH_MODE = $savem;
 651  
 652  	 	 if (empty($rows))
 653  	 	 	 return false;
 654  
 655  	 	 foreach ($rows as $r)
 656  	 	 {
 657  	 	 	 if ($r[7] != 'P')
 658  	 	 	 	 continue;
 659  
 660  	 	 	 $cols = explode('+',$r[6]);
 661  	 	 	 foreach ($cols as $colIndex=>$col)
 662  	 	 	 {
 663  	 	 	 	 if ($colIndex == 0)
 664  	 	 	 	 	 continue;
 665  	 	 	 	 $columnName = $this->getMetaCasedValue($col);
 666  	 	 	 	 $primaryKeys[] = $columnName;
 667  	 	 	 }
 668  	 	 	 break;
 669  	 	 }
 670  	 	 return $primaryKeys;
 671  	 }
 672  
 673  	 /**
 674  	  * Returns a list of Foreign Keys associated with a specific table.
 675  	  *
 676  	  * @param string $table
 677  	  * @param string $owner       discarded
 678  	  * @param bool   $upper       discarded
 679  	  * @param bool   $associative discarded
 680  	  *
 681  	  * @return string[]|false An array where keys are tables, and values are foreign keys;
 682  	  *                        false if no foreign keys could be found.
 683  	  */
 684  	public function metaForeignKeys($table, $owner = '', $upper = false, $associative = 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->database);
 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  	 	 $db2Options = array();
1574  	 	 /*
1575  	 	  * Use DB2 Internal case handling for best speed
1576  	 	  */
1577  	 	 switch(ADODB_ASSOC_CASE)
1578  	 	 {
1579  	 	 case ADODB_ASSOC_CASE_UPPER:
1580  	 	 	 $db2Options = array('db2_attr_case'=>DB2_CASE_UPPER);
1581  	 	 	 $setOption = @db2_set_option($this->_connectionID,$db2Options,1);
1582  	 	 	 break;
1583  
1584  	 	  case ADODB_ASSOC_CASE_LOWER:
1585  	 	 	 $db2Options = array('db2_attr_case'=>DB2_CASE_LOWER);
1586  	 	 	 $setOption = @db2_set_option($this->_connectionID,$db2Options,1);
1587  	 	 	 break;
1588  
1589  	 	 default:
1590  	 	 	 $db2Options = array('db2_attr_case'=>DB2_CASE_NATURAL);
1591  	 	 	 $setOption = @db2_set_option($this->_connectionID,$db2Options,1);
1592  	 	 }
1593  
1594  	 	 if ($inputarr)
1595  	 	 {
1596  	 	 	 if (is_array($sql))
1597  	 	 	 {
1598  	 	 	 	 $stmtid = $sql[1];
1599  	 	 	 }
1600  	 	 	 else
1601  	 	 	 {
1602  	 	 	 	 $stmtid = @db2_prepare($this->_connectionID,$sql);
1603  
1604  	 	 	 	 if ($stmtid == false)
1605  	 	 	 	 {
1606  	 	 	 	 	 $this->_errorMsg  = @db2_stmt_errormsg();
1607  	 	 	 	 	 $this->_errorCode = @db2_stmt_error();
1608  	 	 	 	 	 
1609  	 	 	 	 	 if ($this->debug)
1610  	 	 	 	 	 	 ADOConnection::outp($this->_errorMsg);
1611  	 	 	 	 	 
1612  	 	 	 	 	 return false;
1613  	 	 	 	 }
1614  	 	 	 }
1615  
1616  	 	 	 if (! @db2_execute($stmtid,$inputarr))
1617  	 	 	 {
1618  	 	 	 	 $this->_errorMsg = @db2_stmt_errormsg();
1619  	 	 	 	 $this->_errorCode = @db2_stmt_error();
1620  	 	 	 	 if ($this->debug)
1621  	 	 	 	 	 ADOConnection::outp($this->_errorMsg);
1622  	 	 	 	 return false;
1623  	 	 	 }
1624  
1625  	 	 }
1626  	 	 else if (is_array($sql))
1627  	 	 {
1628  
1629  	 	 	 /*
1630  	 	 	  * Either a prepared statement or a stored procedure
1631  	 	 	  */
1632  
1633  	 	 	 if (is_array($this->storedProcedureParameters)
1634  	 	 	 	 && is_resource($this->storedProcedureParameters['resource']
1635  	 	 	 ))
1636  	 	 	 	 /*
1637  	 	 	 	  * This is all handled in the separate method for
1638  	 	 	 	  * readability
1639  	 	 	 	  */
1640  	 	 	 	 return $this->executeStoredProcedure();
1641  
1642  	 	 	 /*
1643  	 	 	  * First, we prepare the statement
1644  	 	 	  */
1645  	 	 	 $stmtid = @db2_prepare($this->_connectionID,$sql[0]);
1646  	 	 	 if (!$stmtid){
1647  	 	 	 	 $this->_errorMsg = @db2_stmt_errormsg();
1648  	 	 	 	 $this->_errorCode = @db2_stmt_error();
1649  	 	 	 	 if ($this->debug)
1650  	 	 	 	 	 ADOConnection::outp("Prepare failed: " . $this->_errorMsg);
1651  
1652  	 	 	 	 return false;
1653  	 	 	 }
1654  	 	 	 /*
1655  	 	 	  * We next bind some input parameters
1656  	 	 	  */
1657  	 	 	 $ordinal = 1;
1658  	 	 	 foreach ($sql[1] as $psVar=>$psVal){
1659  	 	 	 	 ${$psVar} = $psVal;
1660  	 	 	 	 $ok = @db2_bind_param($stmtid, $ordinal, $psVar, DB2_PARAM_IN);
1661  	 	 	 	 if (!$ok)
1662  	 	 	 	 {
1663  	 	 	 	 	 $this->_errorMsg = @db2_stmt_errormsg();
1664  	 	 	 	 	 $this->_errorCode = @db2_stmt_error();
1665  	 	 	 	 	 if ($this->debug)
1666  	 	 	 	 	 	 ADOConnection::outp("Bind failed: " . $this->_errorMsg);
1667  	 	 	 	 	 return false;
1668  	 	 	 	 }
1669  	 	 	 }
1670  
1671  	 	 	 if (!@db2_execute($stmtid))
1672  	 	 	 {
1673  	 	 	 	 $this->_errorMsg = @db2_stmt_errormsg();
1674  	 	 	 	 $this->_errorCode = @db2_stmt_error();
1675  	 	 	 	 if ($this->debug)
1676  	 	 	 	 	 ADOConnection::outp($this->_errorMsg);
1677  	 	 	 	 return false;
1678  	 	 	 }
1679  
1680  	 	 	 return $stmtid;
1681  	 	 }
1682  	 	 else
1683  	 	 {
1684  
1685  	 	 	 $stmtid = @db2_exec($this->_connectionID,$sql);
1686  	 	 }
1687  	 	 $this->_lastAffectedRows = 0;
1688  	 	 if ($stmtid)
1689  	 	 {
1690  	 	 	 if (@db2_num_fields($stmtid) == 0)
1691  	 	 	 {
1692  	 	 	 	 $this->_lastAffectedRows = db2_num_rows($stmtid);
1693  	 	 	 	 $stmtid = true;
1694  	 	 	 }
1695  	 	 	 else
1696  	 	 	 {
1697  	 	 	 	 $this->_lastAffectedRows = 0;
1698  	 	 	 }
1699  
1700  	 	 	 $this->_errorMsg = '';
1701  	 	 	 $this->_errorCode = 0;
1702  
1703  	 	 }
1704  	 	 else
1705  	 	 {
1706  
1707  	 	 	 $this->_errorMsg = @db2_stmt_errormsg();
1708  	 	 	 $this->_errorCode = @db2_stmt_error();
1709  
1710  	 	 }
1711  	 	 return $stmtid;
1712  	 }
1713  
1714  	 /*
1715  	 	 Insert a null into the blob field of the table first.
1716  	 	 Then use UpdateBlob to store the blob.
1717  
1718  	 	 Usage:
1719  
1720  	 	 $conn->execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
1721  	 	 $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
1722  	 */
1723  	function updateBlob($table,$column,$val,$where,$blobtype='BLOB')
1724  	 {
1725  	 	 return $this->execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
1726  	 }
1727  
1728  	 // returns true or false
1729  	function _close()
1730  	 {
1731  	 	 $ret = @db2_close($this->_connectionID);
1732  	 	 $this->_connectionID = false;
1733  	 	 return $ret;
1734  	 }
1735  
1736  	function _affectedrows()
1737  	 {
1738  	 	 return $this->_lastAffectedRows;
1739  	 }
1740  
1741  	 /**
1742  	  * Gets a meta cased parameter
1743  	  *
1744  	  * Receives an input variable to be processed per the metaCasing
1745  	  * rule, and returns the same value, processed
1746  	  *
1747  	  * @param string $value
1748  	  *
1749  	  * @return string
1750  	  */
1751  	final public function getMetaCasedValue($value)
1752  	 {
1753  	 	 global $ADODB_ASSOC_CASE;
1754  
1755  	 	 switch($ADODB_ASSOC_CASE)
1756  	 	 {
1757  	 	 case ADODB_ASSOC_CASE_LOWER:
1758  	 	 	 $value = strtolower($value);
1759  	 	 	 break;
1760  	 	 case ADODB_ASSOC_CASE_UPPER:
1761  	 	 	 $value = strtoupper($value);
1762  	 	 	 break;
1763  	 	 }
1764  	 	 return $value;
1765  	 }
1766  
1767  
1768  	 const TABLECASE_LOWER    =  0;
1769  	 const TABLECASE_UPPER    =  1;
1770  	 const TABLECASE_DEFAULT  =  2;
1771  
1772  	 /**
1773  	  * Controls the casing of the table provided to the meta functions
1774  	  */
1775  	 private $tableCase = 2;
1776  
1777  	 /**
1778  	  * Sets the table case parameter
1779  	  *
1780  	  * @param int $caseOption
1781  	  * @return null
1782  	  */
1783  	final public function setTableCasing($caseOption)
1784  	 {
1785  	 	 $this->tableCase = $caseOption;
1786  	 }
1787  
1788  	 /**
1789  	  * Gets the table casing parameter
1790  	  *
1791  	  * @return int $caseOption
1792  	  */
1793  	final public function getTableCasing()
1794  	 {
1795  	 	 return $this->tableCase;
1796  	 }
1797  
1798  	 /**
1799  	  * Gets a table cased parameter
1800  	  *
1801  	  * Receives an input variable to be processed per the tableCasing
1802  	  * rule, and returns the same value, processed
1803  	  *
1804  	  * @param string $value
1805  	  *
1806  	  * @return string
1807  	  */
1808  	final public function getTableCasedValue($value)
1809  	 {
1810  	 	 switch($this->tableCase)
1811  	 	 {
1812  	 	 case self::TABLECASE_LOWER:
1813  	 	 	 $value = strtolower($value);
1814  	 	 	 break;
1815  	 	 case self::TABLECASE_UPPER:
1816  	 	 	 $value = strtoupper($value);
1817  	 	 	 break;
1818  	 	 }
1819  	 	 return $value;
1820  	 }
1821  
1822  }
1823  
1824  /*--------------------------------------------------------------------------------------
1825  	  Class Name: Recordset
1826  --------------------------------------------------------------------------------------*/
1827  
1828  class ADORecordSet_db2 extends ADORecordSet {
1829  
1830  	 var $bind = false;
1831  	 var $databaseType = "db2";
1832  	 var $dataProvider = "db2";
1833  	 var $useFetchArray;
1834  
1835  	function __construct($id,$mode=false)
1836  	 {
1837  	 	 if ($mode === false) {
1838  	 	 	 global $ADODB_FETCH_MODE;
1839  	 	 	 $mode = $ADODB_FETCH_MODE;
1840  	 	 }
1841  	 	 $this->fetchMode = $mode;
1842  
1843  	 	 $this->_queryID = $id;
1844  	 }
1845  
1846  
1847  	 // returns the field object
1848  	function fetchField($offset = 0)
1849  	 {
1850  	 	 $o	 	 	    = new ADOFieldObject();
1851  	 	 $o->name 	    = @db2_field_name($this->_queryID,$offset);
1852  	 	 $o->type 	    = @db2_field_type($this->_queryID,$offset);
1853  	 	 $o->max_length = @db2_field_width($this->_queryID,$offset);
1854  
1855  	 	 /*
1856  	 	 if (ADODB_ASSOC_CASE == 0)
1857  	 	 	 $o->name = strtolower($o->name);
1858  	 	 else if (ADODB_ASSOC_CASE == 1)
1859  	 	 	 $o->name = strtoupper($o->name);
1860  	 	 */
1861  	 	 return $o;
1862  	 }
1863  
1864  	 /* Use associative array to get fields array */
1865  	function fields($colname)
1866  	 {
1867  
1868  	 	 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
1869  	 	 	 return $this->fields[$colname];
1870  	 	 }
1871  
1872  	 	 if (!$this->bind) {
1873  	 	 	 $this->bind = array();
1874  	 	 	 for ($i=0; $i < $this->_numOfFields; $i++) {
1875  	 	 	 	 $o = $this->FetchField($i);
1876  	 	 	 	 $this->bind[strtoupper($o->name)] = $i;
1877  	 	 	 }
1878  	 	 }
1879  
1880  	 	  return $this->fields[$this->bind[strtoupper($colname)]];
1881  	 }
1882  
1883  
1884  	function _initrs()
1885  	 {
1886  	 	 global $ADODB_COUNTRECS;
1887  	 	 $this->_numOfRows = ($ADODB_COUNTRECS) ? @db2_num_rows($this->_queryID) : -1;
1888  
1889  	 	 $this->_numOfFields = @db2_num_fields($this->_queryID);
1890  
1891  	 	 // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0
1892  
1893  	 	 if ($this->_numOfRows == 0)
1894  	 	 	 $this->_numOfRows = -1;
1895  	 }
1896  
1897  	function _seek($row)
1898  	 {
1899  	 	 return false;
1900  	 }
1901  
1902  	function getArrayLimit($nrows,$offset=0)
1903  	 {
1904  	 	 if ($offset <= 0) {
1905  	 	 	 $rs = $this->GetArray($nrows);
1906  	 	 	 return $rs;
1907  	 	 }
1908  
1909  	 	 $this->Move($offset);
1910  
1911  
1912  	 	 $results = array();
1913  	 	 $cnt = 0;
1914  	 	 while (!$this->EOF && $nrows != $cnt) {
1915  	 	 	 $results[$cnt++] = $this->fields;
1916  	 	 	 $this->MoveNext();
1917  	 	 }
1918  
1919  	 	 return $results;
1920  	 }
1921  
1922  	function moveNext()
1923  	 {
1924  	 	 if ($this->EOF || $this->_numOfRows == 0)
1925  	 	 	 return false;
1926  
1927  	 	 $this->_currentRow++;
1928  
1929  	 	 $this->processCoreFetch();
1930  	 	 return $this->processMoveRecord();
1931  
1932  	 }
1933  
1934  	private function processCoreFetch()
1935  	 {
1936  	 	 switch ($this->fetchMode){
1937  	 	 case ADODB_FETCH_ASSOC:
1938  
1939  	 	 	 /*
1940  	 	 	  * Associative array
1941  	 	 	  */
1942  	 	 	 $this->fields = @db2_fetch_assoc($this->_queryID);
1943  	 	 	 break;
1944  
1945  	 	 case ADODB_FETCH_BOTH:
1946  	 	 	 /*
1947  	 	 	  * Fetch both numeric and Associative array
1948  	 	 	  */
1949  	 	 	 $this->fields = @db2_fetch_both($this->_queryID);
1950  	 	 	 break;
1951  	 	 default:
1952  	 	 	 /*
1953  	 	 	  * Numeric array
1954  	 	 	  */
1955  	 	 	 $this->fields = @db2_fetch_array($this->_queryID);
1956  	 	 	 break;
1957  	 	 }
1958  	 }
1959  
1960  	private function processMoveRecord()
1961  	 {
1962  	 	 if (!$this->fields){
1963  	 	 	 $this->EOF = true;
1964  	 	 	 return false;
1965  	 	 }
1966  
1967  	 	 return true;
1968  	 }
1969  
1970  	function _fetch()
1971  	 {
1972  	 	 $this->processCoreFetch();
1973  	 	 if ($this->fields)
1974  	 	 	 return true;
1975  
1976  	 	 $this->fields = false;
1977  	 	 return false;
1978  	 }
1979  
1980  	function _close()
1981  	 {
1982  	 	 $ok = @db2_free_result($this->_queryID);
1983  	 	 if (!$ok)
1984  	 	 {
1985  	 	 	 $this->connection->_errorMsg  = @db2_stmt_errormsg($this->_queryID);
1986  	 	 	 $this->connection->_errorCode = @db2_stmt_error();
1987  
1988  	 	 	 if ($this->debug)
1989  	 	 	 	 ADOConnection::outp($this->connection->_errorMsg);
1990  	 	 	 return false;
1991  	 	 }
1992  
1993  	 }
1994  
1995  }