See Release Notes
Long Term Support Release
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body