Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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