00001 <?php
00027 require_once 'Zend/Db.php';
00028
00032 require_once 'Zend/Db/Adapter/Abstract.php';
00033
00037 require_once 'Zend/Db/Statement/Db2.php';
00038
00039
00046 class Zend_Db_Adapter_Db2 extends Zend_Db_Adapter_Abstract
00047 {
00065 protected $_config = array(
00066 'dbname' => null,
00067 'username' => null,
00068 'password' => null,
00069 'host' => 'localhost',
00070 'port' => '50000',
00071 'protocol' => 'TCPIP',
00072 'persistent' => false,
00073 'os' => null,
00074 'schema' => null
00075 );
00076
00082 protected $_execute_mode = DB2_AUTOCOMMIT_ON;
00083
00089 protected $_defaultStmtClass = 'Zend_Db_Statement_Db2';
00090 protected $_isI5 = false;
00091
00103 protected $_numericDataTypes = array(
00104 Zend_Db::INT_TYPE => Zend_Db::INT_TYPE,
00105 Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
00106 Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
00107 'INTEGER' => Zend_Db::INT_TYPE,
00108 'SMALLINT' => Zend_Db::INT_TYPE,
00109 'BIGINT' => Zend_Db::BIGINT_TYPE,
00110 'DECIMAL' => Zend_Db::FLOAT_TYPE,
00111 'NUMERIC' => Zend_Db::FLOAT_TYPE
00112 );
00113
00119 protected function _connect()
00120 {
00121 if (is_resource($this->_connection)) {
00122
00123 return;
00124 }
00125
00126 if (!extension_loaded('ibm_db2')) {
00130 require_once 'Zend/Db/Adapter/Db2/Exception.php';
00131 throw new Zend_Db_Adapter_Db2_Exception('The IBM DB2 extension is required for this adapter but the extension is not loaded');
00132 }
00133
00134 $this->_determineI5();
00135 if ($this->_config['persistent']) {
00136
00137 $conn_func_name = 'db2_pconnect';
00138 } else {
00139
00140 $conn_func_name = 'db2_connect';
00141 }
00142
00143 if (!isset($this->_config['driver_options']['autocommit'])) {
00144
00145 $this->_config['driver_options']['autocommit'] = &$this->_execute_mode;
00146 }
00147
00148 if (isset($this->_config['options'][Zend_Db::CASE_FOLDING])) {
00149 $caseAttrMap = array(
00150 Zend_Db::CASE_NATURAL => DB2_CASE_NATURAL,
00151 Zend_Db::CASE_UPPER => DB2_CASE_UPPER,
00152 Zend_Db::CASE_LOWER => DB2_CASE_LOWER
00153 );
00154 $this->_config['driver_options']['DB2_ATTR_CASE'] = $caseAttrMap[$this->_config['options'][Zend_Db::CASE_FOLDING]];
00155 }
00156
00157 if ($this->_config['host'] !== 'localhost' && !$this->_isI5) {
00158
00159 $dbname = 'DRIVER={IBM DB2 ODBC DRIVER}' .
00160 ';DATABASE=' . $this->_config['dbname'] .
00161 ';HOSTNAME=' . $this->_config['host'] .
00162 ';PORT=' . $this->_config['port'] .
00163 ';PROTOCOL=' . $this->_config['protocol'] .
00164 ';UID=' . $this->_config['username'] .
00165 ';PWD=' . $this->_config['password'] .';';
00166 $this->_connection = $conn_func_name(
00167 $dbname,
00168 null,
00169 null,
00170 $this->_config['driver_options']
00171 );
00172 } else {
00173
00174 $this->_connection = $conn_func_name(
00175 $this->_config['dbname'],
00176 $this->_config['username'],
00177 $this->_config['password'],
00178 $this->_config['driver_options']
00179 );
00180 }
00181
00182
00183 if (!$this->_connection) {
00187 require_once 'Zend/Db/Adapter/Db2/Exception.php';
00188 throw new Zend_Db_Adapter_Db2_Exception(db2_conn_errormsg(), db2_conn_error());
00189 }
00190 }
00191
00197 public function isConnected()
00198 {
00199 return ((bool) (is_resource($this->_connection)
00200 && get_resource_type($this->_connection) == 'DB2 Connection'));
00201 }
00202
00208 public function closeConnection()
00209 {
00210 if ($this->isConnected()) {
00211 db2_close($this->_connection);
00212 }
00213 $this->_connection = null;
00214 }
00215
00222 public function prepare($sql)
00223 {
00224 $this->_connect();
00225 $stmtClass = $this->_defaultStmtClass;
00226 if (!class_exists($stmtClass)) {
00227 require_once 'Zend/Loader.php';
00228 Zend_Loader::loadClass($stmtClass);
00229 }
00230 $stmt = new $stmtClass($this, $sql);
00231 $stmt->setFetchMode($this->_fetchMode);
00232 return $stmt;
00233 }
00234
00240 public function _getExecuteMode()
00241 {
00242 return $this->_execute_mode;
00243 }
00244
00249 public function _setExecuteMode($mode)
00250 {
00251 switch ($mode) {
00252 case DB2_AUTOCOMMIT_OFF:
00253 case DB2_AUTOCOMMIT_ON:
00254 $this->_execute_mode = $mode;
00255 db2_autocommit($this->_connection, $mode);
00256 break;
00257 default:
00261 require_once 'Zend/Db/Adapter/Db2/Exception.php';
00262 throw new Zend_Db_Adapter_Db2_Exception("execution mode not supported");
00263 break;
00264 }
00265 }
00266
00273 protected function _quote($value)
00274 {
00275 if (is_int($value) || is_float($value)) {
00276 return $value;
00277 }
00283 if (function_exists('db2_escape_string')) {
00284 return "'" . db2_escape_string($value) . "'";
00285 }
00286 return parent::_quote($value);
00287 }
00288
00292 public function getQuoteIdentifierSymbol()
00293 {
00294 $this->_connect();
00295 $info = db2_server_info($this->_connection);
00296 if ($info) {
00297 $identQuote = $info->IDENTIFIER_QUOTE_CHAR;
00298 } else {
00299
00300 if ($this->_isI5) {
00301 $identQuote ="'";
00302 }
00303 }
00304 return $identQuote;
00305 }
00306
00312 public function listTables($schema = null)
00313 {
00314 $this->_connect();
00315
00316 if ($schema === null && $this->_config['schema'] != null) {
00317 $schema = $this->_config['schema'];
00318 }
00319
00320 $tables = array();
00321
00322 if (!$this->_isI5) {
00323 if ($schema) {
00324 $stmt = db2_tables($this->_connection, null, $schema);
00325 } else {
00326 $stmt = db2_tables($this->_connection);
00327 }
00328 while ($row = db2_fetch_assoc($stmt)) {
00329 $tables[] = $row['TABLE_NAME'];
00330 }
00331 } else {
00332 $tables = $this->_i5listTables($schema);
00333 }
00334
00335 return $tables;
00336 }
00337
00338
00368 public function describeTable($tableName, $schemaName = null)
00369 {
00370
00371 $this->_connect();
00372
00373 if ($schemaName === null && $this->_config['schema'] != null) {
00374 $schemaName = $this->_config['schema'];
00375 }
00376
00377 if (!$this->_isI5) {
00378
00379 $sql = "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno,
00380 c.typename, c.default, c.nulls, c.length, c.scale,
00381 c.identity, tc.type AS tabconsttype, k.colseq
00382 FROM syscat.columns c
00383 LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc
00384 ON (k.tabschema = tc.tabschema
00385 AND k.tabname = tc.tabname
00386 AND tc.type = 'P'))
00387 ON (c.tabschema = k.tabschema
00388 AND c.tabname = k.tabname
00389 AND c.colname = k.colname)
00390 WHERE "
00391 . $this->quoteInto('UPPER(c.tabname) = UPPER(?)', $tableName);
00392
00393 if ($schemaName) {
00394 $sql .= $this->quoteInto(' AND UPPER(c.tabschema) = UPPER(?)', $schemaName);
00395 }
00396
00397 $sql .= " ORDER BY c.colno";
00398
00399 } else {
00400
00401
00402 $sql = "SELECT DISTINCT C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.ORDINAL_POSITION,
00403 C.DATA_TYPE, C.COLUMN_DEFAULT, C.NULLS ,C.LENGTH, C.SCALE, LEFT(C.IDENTITY,1),
00404 LEFT(tc.TYPE, 1) AS tabconsttype, k.COLSEQ
00405 FROM QSYS2.SYSCOLUMNS C
00406 LEFT JOIN (QSYS2.syskeycst k JOIN QSYS2.SYSCST tc
00407 ON (k.TABLE_SCHEMA = tc.TABLE_SCHEMA
00408 AND k.TABLE_NAME = tc.TABLE_NAME
00409 AND LEFT(tc.type,1) = 'P'))
00410 ON (C.TABLE_SCHEMA = k.TABLE_SCHEMA
00411 AND C.TABLE_NAME = k.TABLE_NAME
00412 AND C.COLUMN_NAME = k.COLUMN_NAME)
00413 WHERE "
00414 . $this->quoteInto('UPPER(C.TABLE_NAME) = UPPER(?)', $tableName);
00415
00416 if ($schemaName) {
00417 $sql .= $this->quoteInto(' AND UPPER(C.TABLE_SCHEMA) = UPPER(?)', $schemaName);
00418 }
00419
00420 $sql .= " ORDER BY C.ORDINAL_POSITION FOR FETCH ONLY";
00421 }
00422
00423 $desc = array();
00424 $stmt = $this->query($sql);
00425
00429 $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
00430
00435 $tabschema = 0;
00436 $tabname = 1;
00437 $colname = 2;
00438 $colno = 3;
00439 $typename = 4;
00440 $default = 5;
00441 $nulls = 6;
00442 $length = 7;
00443 $scale = 8;
00444 $identityCol = 9;
00445 $tabconstType = 10;
00446 $colseq = 11;
00447
00448 foreach ($result as $key => $row) {
00449 list ($primary, $primaryPosition, $identity) = array(false, null, false);
00450 if ($row[$tabconstType] == 'P') {
00451 $primary = true;
00452 $primaryPosition = $row[$colseq];
00453 }
00458 if ($row[$identityCol] == 'Y') {
00459 $identity = true;
00460 }
00461
00462
00463 $desc[$this->foldCase($row[$colname])] = array(
00464 'SCHEMA_NAME' => $this->foldCase($row[$tabschema]),
00465 'TABLE_NAME' => $this->foldCase($row[$tabname]),
00466 'COLUMN_NAME' => $this->foldCase($row[$colname]),
00467 'COLUMN_POSITION' => (!$this->_isI5) ? $row[$colno]+1 : $row[$colno],
00468 'DATA_TYPE' => $row[$typename],
00469 'DEFAULT' => $row[$default],
00470 'NULLABLE' => (bool) ($row[$nulls] == 'Y'),
00471 'LENGTH' => $row[$length],
00472 'SCALE' => $row[$scale],
00473 'PRECISION' => ($row[$typename] == 'DECIMAL' ? $row[$length] : 0),
00474 'UNSIGNED' => false,
00475 'PRIMARY' => $primary,
00476 'PRIMARY_POSITION' => $primaryPosition,
00477 'IDENTITY' => $identity
00478 );
00479 }
00480
00481 return $desc;
00482 }
00483
00492 public function lastSequenceId($sequenceName)
00493 {
00494 $this->_connect();
00495
00496 if (!$this->_isI5) {
00497 $quotedSequenceName = $this->quoteIdentifier($sequenceName, true);
00498 $sql = 'SELECT PREVVAL FOR ' . $quotedSequenceName . ' AS VAL FROM SYSIBM.SYSDUMMY1';
00499 } else {
00500 $quotedSequenceName = $sequenceName;
00501 $sql = 'SELECT PREVVAL FOR ' . $this->quoteIdentifier($sequenceName, true) . ' AS VAL FROM QSYS2.QSQPTABL';
00502 }
00503
00504 $value = $this->fetchOne($sql);
00505 return (string) $value;
00506 }
00507
00516 public function nextSequenceId($sequenceName)
00517 {
00518 $this->_connect();
00519 $sql = 'SELECT NEXTVAL FOR '.$this->quoteIdentifier($sequenceName, true).' AS VAL FROM SYSIBM.SYSDUMMY1';
00520 $value = $this->fetchOne($sql);
00521 return (string) $value;
00522 }
00523
00543 public function lastInsertId($tableName = null, $primaryKey = null, $idType = null)
00544 {
00545 $this->_connect();
00546
00547 if ($this->_isI5) {
00548 return (string) $this->_i5LastInsertId($tableName, $idType);
00549 }
00550
00551 if ($tableName !== null) {
00552 $sequenceName = $tableName;
00553 if ($primaryKey) {
00554 $sequenceName .= "_$primaryKey";
00555 }
00556 $sequenceName .= '_seq';
00557 return $this->lastSequenceId($sequenceName);
00558 }
00559
00560 $sql = 'SELECT IDENTITY_VAL_LOCAL() AS VAL FROM SYSIBM.SYSDUMMY1';
00561 $value = $this->fetchOne($sql);
00562 return (string) $value;
00563 }
00564
00570 protected function _beginTransaction()
00571 {
00572 $this->_setExecuteMode(DB2_AUTOCOMMIT_OFF);
00573 }
00574
00580 protected function _commit()
00581 {
00582 if (!db2_commit($this->_connection)) {
00586 require_once 'Zend/Db/Adapter/Db2/Exception.php';
00587 throw new Zend_Db_Adapter_Db2_Exception(
00588 db2_conn_errormsg($this->_connection),
00589 db2_conn_error($this->_connection));
00590 }
00591
00592 $this->_setExecuteMode(DB2_AUTOCOMMIT_ON);
00593 }
00594
00600 protected function _rollBack()
00601 {
00602 if (!db2_rollback($this->_connection)) {
00606 require_once 'Zend/Db/Adapter/Db2/Exception.php';
00607 throw new Zend_Db_Adapter_Db2_Exception(
00608 db2_conn_errormsg($this->_connection),
00609 db2_conn_error($this->_connection));
00610 }
00611 $this->_setExecuteMode(DB2_AUTOCOMMIT_ON);
00612 }
00613
00621 public function setFetchMode($mode)
00622 {
00623 switch ($mode) {
00624 case Zend_Db::FETCH_NUM:
00625 case Zend_Db::FETCH_ASSOC:
00626 case Zend_Db::FETCH_BOTH:
00627 case Zend_Db::FETCH_OBJ:
00628 $this->_fetchMode = $mode;
00629 break;
00630 case Zend_Db::FETCH_BOUND:
00634 require_once 'Zend/Db/Adapter/Db2/Exception.php';
00635 throw new Zend_Db_Adapter_Db2_Exception('FETCH_BOUND is not supported yet');
00636 break;
00637 default:
00641 require_once 'Zend/Db/Adapter/Db2/Exception.php';
00642 throw new Zend_Db_Adapter_Db2_Exception("Invalid fetch mode '$mode' specified");
00643 break;
00644 }
00645 }
00646
00655 public function limit($sql, $count, $offset = 0)
00656 {
00657 $count = intval($count);
00658 if ($count <= 0) {
00662 require_once 'Zend/Db/Adapter/Db2/Exception.php';
00663 throw new Zend_Db_Adapter_Db2_Exception("LIMIT argument count=$count is not valid");
00664 }
00665
00666 $offset = intval($offset);
00667 if ($offset < 0) {
00671 require_once 'Zend/Db/Adapter/Db2/Exception.php';
00672 throw new Zend_Db_Adapter_Db2_Exception("LIMIT argument offset=$offset is not valid");
00673 }
00674
00675 if ($offset == 0) {
00676 $limit_sql = $sql . " FETCH FIRST $count ROWS ONLY";
00677 return $limit_sql;
00678 }
00679
00686 $limit_sql = "SELECT z2.*
00687 FROM (
00688 SELECT ROW_NUMBER() OVER() AS \"ZEND_DB_ROWNUM\", z1.*
00689 FROM (
00690 " . $sql . "
00691 ) z1
00692 ) z2
00693 WHERE z2.zend_db_rownum BETWEEN " . ($offset+1) . " AND " . ($offset+$count);
00694 return $limit_sql;
00695 }
00696
00703 public function supportsParameters($type)
00704 {
00705 if ($type == 'positional') {
00706 return true;
00707 }
00708
00709
00710 return false;
00711 }
00712
00718 public function getServerVersion()
00719 {
00720 $this->_connect();
00721 $server_info = db2_server_info($this->_connection);
00722 if ($server_info !== false) {
00723 $version = $server_info->DBMS_VER;
00724 if ($this->_isI5) {
00725 $version = (int) substr($version, 0, 2) . '.' . (int) substr($version, 2, 2) . '.' . (int) substr($version, 4);
00726 }
00727 return $version;
00728 } else {
00729 return null;
00730 }
00731 }
00732
00738 public function isI5()
00739 {
00740 if ($this->_isI5 === null) {
00741 $this->_determineI5();
00742 }
00743
00744 return (bool) $this->_isI5;
00745 }
00746
00753 protected function _determineI5()
00754 {
00755
00756 $this->_isI5 = (php_uname('s') == 'OS400') ? true : false;
00757
00758
00759 if (isset($this->_config['os'])){
00760 if (strtolower($this->_config['os']) === 'i5') {
00761 $this->_isI5 = true;
00762 } else {
00763
00764 $this->_isI5 = false;
00765 }
00766 }
00767
00768 }
00769
00778 protected function _i5listTables($schema = null)
00779 {
00780
00781 $tables = array();
00782 if ($schema) {
00783 $tablesStatement = db2_tables($this->_connection, null, $schema);
00784 while ($rowTables = db2_fetch_assoc($tablesStatement) ) {
00785 if ($rowTables['TABLE_NAME'] !== null) {
00786 $tables[] = $rowTables['TABLE_NAME'];
00787 }
00788 }
00789 } else {
00790 $schemaStatement = db2_tables($this->_connection);
00791 while ($schema = db2_fetch_assoc($schemaStatement)) {
00792 if ($schema['TABLE_SCHEM'] !== null) {
00793
00794 $tablesStatement = db2_tables($this->_connection, NULL, $schema['TABLE_SCHEM']);
00795 if (is_resource($tablesStatement)) {
00796 while ($rowTables = db2_fetch_assoc($tablesStatement) ) {
00797 if ($rowTables['TABLE_NAME'] !== null) {
00798 $tables[] = $rowTables['TABLE_NAME'];
00799 }
00800 }
00801 }
00802 }
00803 }
00804 }
00805
00806 return $tables;
00807 }
00808
00809 protected function _i5LastInsertId($objectName = null, $idType = null)
00810 {
00811
00812 if ($objectName === null) {
00813 $sql = 'SELECT IDENTITY_VAL_LOCAL() AS VAL FROM QSYS2.QSQPTABL';
00814 $value = $this->fetchOne($sql);
00815 return $value;
00816 }
00817
00818 if (strtoupper($idType) === 'S'){
00819
00820 $sequenceName = $objectName;
00821 return $this->lastSequenceId($sequenceName);
00822 }
00823
00824
00825
00826 $tableName = $objectName;
00827 return $this->fetchOne('SELECT IDENTITY_VAL_LOCAL() from ' . $this->quoteIdentifier($tableName));
00828 }
00829
00830 }
00831
00832