Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

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