File indexing completed on 2024-06-16 05:29:58

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Db
0017  * @subpackage Adapter
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  *
0022  */
0023 
0024 /**
0025  * @see Zend_Db
0026  */
0027 // require_once 'Zend/Db.php';
0028 
0029 /**
0030  * @see Zend_Db_Adapter_Abstract
0031  */
0032 // require_once 'Zend/Db/Adapter/Abstract.php';
0033 
0034 /**
0035  * @see Zend_Db_Statement_Db2
0036  */
0037 // require_once 'Zend/Db/Statement/Db2.php';
0038 
0039 
0040 /**
0041  * @package    Zend_Db
0042  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0043  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0044  */
0045 
0046 class Zend_Db_Adapter_Db2 extends Zend_Db_Adapter_Abstract
0047 {
0048     /**
0049      * User-provided configuration.
0050      *
0051      * Basic keys are:
0052      *
0053      * username   => (string)  Connect to the database as this username.
0054      * password   => (string)  Password associated with the username.
0055      * host       => (string)  What host to connect to (default 127.0.0.1)
0056      * dbname     => (string)  The name of the database to user
0057      * protocol   => (string)  Protocol to use, defaults to "TCPIP"
0058      * port       => (integer) Port number to use for TCP/IP if protocol is "TCPIP"
0059      * persistent => (boolean) Set TRUE to use a persistent connection (db2_pconnect)
0060      * os         => (string)  This should be set to 'i5' if the db is on an os400/i5
0061      * schema     => (string)  The default schema the connection should use
0062      *
0063      * @var array
0064      */
0065     protected $_config = array(
0066         'dbname'       => null,
0067         'username'     => null,
0068         'password'     => null,
0069         'host'         => 'localhost',
0070         'port'         => '50000',
0071         'protocol'     => 'TCPIP',
0072         'persistent'   => false,
0073         'os'           => null,
0074         'schema'       => null
0075     );
0076 
0077     /**
0078      * Execution mode
0079      *
0080      * @var int execution flag (DB2_AUTOCOMMIT_ON or DB2_AUTOCOMMIT_OFF)
0081      */
0082     protected $_execute_mode = DB2_AUTOCOMMIT_ON;
0083 
0084     /**
0085      * Default class name for a DB statement.
0086      *
0087      * @var string
0088      */
0089     protected $_defaultStmtClass = 'Zend_Db_Statement_Db2';
0090     protected $_isI5 = false;
0091 
0092     /**
0093      * Keys are UPPERCASE SQL datatypes or the constants
0094      * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE.
0095      *
0096      * Values are:
0097      * 0 = 32-bit integer
0098      * 1 = 64-bit integer
0099      * 2 = float or decimal
0100      *
0101      * @var array Associative array of datatypes to values 0, 1, or 2.
0102      */
0103     protected $_numericDataTypes = array(
0104         Zend_Db::INT_TYPE    => Zend_Db::INT_TYPE,
0105         Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
0106         Zend_Db::FLOAT_TYPE  => Zend_Db::FLOAT_TYPE,
0107         'INTEGER'            => Zend_Db::INT_TYPE,
0108         'SMALLINT'           => Zend_Db::INT_TYPE,
0109         'BIGINT'             => Zend_Db::BIGINT_TYPE,
0110         'DECIMAL'            => Zend_Db::FLOAT_TYPE,
0111         'NUMERIC'            => Zend_Db::FLOAT_TYPE
0112     );
0113 
0114     /**
0115      * Creates a connection resource.
0116      *
0117      * @return void
0118      */
0119     protected function _connect()
0120     {
0121         if (is_resource($this->_connection)) {
0122             // connection already exists
0123             return;
0124         }
0125 
0126         if (!extension_loaded('ibm_db2')) {
0127             /**
0128              * @see Zend_Db_Adapter_Db2_Exception
0129              */
0130             // require_once 'Zend/Db/Adapter/Db2/Exception.php';
0131             throw new Zend_Db_Adapter_Db2_Exception('The IBM DB2 extension is required for this adapter but the extension is not loaded');
0132         }
0133 
0134         $this->_determineI5();
0135         if ($this->_config['persistent']) {
0136             // use persistent connection
0137             $conn_func_name = 'db2_pconnect';
0138         } else {
0139             // use "normal" connection
0140             $conn_func_name = 'db2_connect';
0141         }
0142 
0143         if (!isset($this->_config['driver_options']['autocommit'])) {
0144             // set execution mode
0145             $this->_config['driver_options']['autocommit'] = &$this->_execute_mode;
0146         }
0147 
0148         if (isset($this->_config['options'][Zend_Db::CASE_FOLDING])) {
0149             $caseAttrMap = array(
0150                 Zend_Db::CASE_NATURAL => DB2_CASE_NATURAL,
0151                 Zend_Db::CASE_UPPER   => DB2_CASE_UPPER,
0152                 Zend_Db::CASE_LOWER   => DB2_CASE_LOWER
0153             );
0154             $this->_config['driver_options']['DB2_ATTR_CASE'] = $caseAttrMap[$this->_config['options'][Zend_Db::CASE_FOLDING]];
0155         }
0156 
0157         if ($this->_isI5 && isset($this->_config['driver_options']['i5_naming'])) {
0158             if ($this->_config['driver_options']['i5_naming']) {
0159                 $this->_config['driver_options']['i5_naming'] = DB2_I5_NAMING_ON;
0160             } else {
0161                 $this->_config['driver_options']['i5_naming'] = DB2_I5_NAMING_OFF;
0162             }
0163         }
0164 
0165         if ($this->_config['host'] !== 'localhost' && !$this->_isI5) {
0166             // if the host isn't localhost, use extended connection params
0167             $dbname = 'DRIVER={IBM DB2 ODBC DRIVER}' .
0168                      ';DATABASE=' . $this->_config['dbname'] .
0169                      ';HOSTNAME=' . $this->_config['host'] .
0170                      ';PORT='     . $this->_config['port'] .
0171                      ';PROTOCOL=' . $this->_config['protocol'] .
0172                      ';UID='      . $this->_config['username'] .
0173                      ';PWD='      . $this->_config['password'] .';';
0174             $this->_connection = $conn_func_name(
0175                 $dbname,
0176                 null,
0177                 null,
0178                 $this->_config['driver_options']
0179             );
0180         } else {
0181             // host is localhost, so use standard connection params
0182             $this->_connection = $conn_func_name(
0183                 $this->_config['dbname'],
0184                 $this->_config['username'],
0185                 $this->_config['password'],
0186                 $this->_config['driver_options']
0187             );
0188         }
0189 
0190         // check the connection
0191         if (!$this->_connection) {
0192             /**
0193              * @see Zend_Db_Adapter_Db2_Exception
0194              */
0195             // require_once 'Zend/Db/Adapter/Db2/Exception.php';
0196             throw new Zend_Db_Adapter_Db2_Exception(db2_conn_errormsg(), db2_conn_error());
0197         }
0198     }
0199 
0200     /**
0201      * Test if a connection is active
0202      *
0203      * @return boolean
0204      */
0205     public function isConnected()
0206     {
0207         return ((bool) (is_resource($this->_connection)
0208                      && get_resource_type($this->_connection) == 'DB2 Connection'));
0209     }
0210 
0211     /**
0212      * Force the connection to close.
0213      *
0214      * @return void
0215      */
0216     public function closeConnection()
0217     {
0218         if ($this->isConnected()) {
0219             db2_close($this->_connection);
0220         }
0221         $this->_connection = null;
0222     }
0223 
0224     /**
0225      * Returns an SQL statement for preparation.
0226      *
0227      * @param string $sql The SQL statement with placeholders.
0228      * @return Zend_Db_Statement_Db2
0229      */
0230     public function prepare($sql)
0231     {
0232         $this->_connect();
0233         $stmtClass = $this->_defaultStmtClass;
0234         if (!class_exists($stmtClass)) {
0235             // require_once 'Zend/Loader.php';
0236             Zend_Loader::loadClass($stmtClass);
0237         }
0238         $stmt = new $stmtClass($this, $sql);
0239         $stmt->setFetchMode($this->_fetchMode);
0240         return $stmt;
0241     }
0242 
0243     /**
0244      * Gets the execution mode
0245      *
0246      * @return int the execution mode (DB2_AUTOCOMMIT_ON or DB2_AUTOCOMMIT_OFF)
0247      */
0248     public function _getExecuteMode()
0249     {
0250         return $this->_execute_mode;
0251     }
0252 
0253     /**
0254      * @param integer $mode
0255      * @return void
0256      */
0257     public function _setExecuteMode($mode)
0258     {
0259         switch ($mode) {
0260             case DB2_AUTOCOMMIT_OFF:
0261             case DB2_AUTOCOMMIT_ON:
0262                 $this->_execute_mode = $mode;
0263                 db2_autocommit($this->_connection, $mode);
0264                 break;
0265             default:
0266                 /**
0267                  * @see Zend_Db_Adapter_Db2_Exception
0268                  */
0269                 // require_once 'Zend/Db/Adapter/Db2/Exception.php';
0270                 throw new Zend_Db_Adapter_Db2_Exception("execution mode not supported");
0271                 break;
0272         }
0273     }
0274 
0275     /**
0276      * Quote a raw string.
0277      *
0278      * @param string $value     Raw string
0279      * @return string           Quoted string
0280      */
0281     protected function _quote($value)
0282     {
0283         if (is_int($value) || is_float($value)) {
0284             return $value;
0285         }
0286         /**
0287          * Use db2_escape_string() if it is present in the IBM DB2 extension.
0288          * But some supported versions of PHP do not include this function,
0289          * so fall back to default quoting in the parent class.
0290          */
0291         if (function_exists('db2_escape_string')) {
0292             return "'" . db2_escape_string($value) . "'";
0293         }
0294         return parent::_quote($value);
0295     }
0296 
0297     /**
0298      * @return string
0299      */
0300     public function getQuoteIdentifierSymbol()
0301     {
0302         $this->_connect();
0303         $info = db2_server_info($this->_connection);
0304         if ($info) {
0305             $identQuote = $info->IDENTIFIER_QUOTE_CHAR;
0306         } else {
0307             // db2_server_info() does not return result on some i5 OS version
0308             if ($this->_isI5) {
0309                 $identQuote ="'";
0310             }
0311         }
0312         return $identQuote;
0313     }
0314 
0315     /**
0316      * Returns a list of the tables in the database.
0317      * @param string $schema OPTIONAL
0318      * @return array
0319      */
0320     public function listTables($schema = null)
0321     {
0322         $this->_connect();
0323 
0324         if ($schema === null && $this->_config['schema'] != null) {
0325             $schema = $this->_config['schema'];
0326         }
0327 
0328         $tables = array();
0329 
0330         if (!$this->_isI5) {
0331             if ($schema) {
0332                 $stmt = db2_tables($this->_connection, null, $schema);
0333             } else {
0334                 $stmt = db2_tables($this->_connection);
0335             }
0336             while ($row = db2_fetch_assoc($stmt)) {
0337                 $tables[] = $row['TABLE_NAME'];
0338             }
0339         } else {
0340             $tables = $this->_i5listTables($schema);
0341         }
0342 
0343         return $tables;
0344     }
0345 
0346 
0347     /**
0348      * Returns the column descriptions for a table.
0349      *
0350      * The return value is an associative array keyed by the column name,
0351      * as returned by the RDBMS.
0352      *
0353      * The value of each array element is an associative array
0354      * with the following keys:
0355      *
0356      * SCHEMA_NAME      => string; name of database or schema
0357      * TABLE_NAME       => string;
0358      * COLUMN_NAME      => string; column name
0359      * COLUMN_POSITION  => number; ordinal position of column in table
0360      * DATA_TYPE        => string; SQL datatype name of column
0361      * DEFAULT          => string; default expression of column, null if none
0362      * NULLABLE         => boolean; true if column can have nulls
0363      * LENGTH           => number; length of CHAR/VARCHAR
0364      * SCALE            => number; scale of NUMERIC/DECIMAL
0365      * PRECISION        => number; precision of NUMERIC/DECIMAL
0366      * UNSIGNED         => boolean; unsigned property of an integer type
0367      *                     DB2 not supports UNSIGNED integer.
0368      * PRIMARY          => boolean; true if column is part of the primary key
0369      * PRIMARY_POSITION => integer; position of column in primary key
0370      * IDENTITY         => integer; true if column is auto-generated with unique values
0371      *
0372      * @param string $tableName
0373      * @param string $schemaName OPTIONAL
0374      * @return array
0375      */
0376     public function describeTable($tableName, $schemaName = null)
0377     {
0378         // Ensure the connection is made so that _isI5 is set
0379         $this->_connect();
0380 
0381         if ($schemaName === null && $this->_config['schema'] != null) {
0382             $schemaName = $this->_config['schema'];
0383         }
0384 
0385         if (!$this->_isI5) {
0386 
0387             $sql = "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno,
0388                 c.typename, c.default, c.nulls, c.length, c.scale,
0389                 c.identity, tc.type AS tabconsttype, k.colseq
0390                 FROM syscat.columns c
0391                 LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc
0392                 ON (k.tabschema = tc.tabschema
0393                     AND k.tabname = tc.tabname
0394                     AND tc.type = 'P'))
0395                 ON (c.tabschema = k.tabschema
0396                     AND c.tabname = k.tabname
0397                     AND c.colname = k.colname)
0398                 WHERE "
0399                 . $this->quoteInto('UPPER(c.tabname) = UPPER(?)', $tableName);
0400 
0401             if ($schemaName) {
0402                $sql .= $this->quoteInto(' AND UPPER(c.tabschema) = UPPER(?)', $schemaName);
0403             }
0404 
0405             $sql .= " ORDER BY c.colno";
0406 
0407         } else {
0408 
0409             // DB2 On I5 specific query
0410             $sql = "SELECT DISTINCT C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.ORDINAL_POSITION,
0411                 C.DATA_TYPE, C.COLUMN_DEFAULT, C.NULLS ,C.LENGTH, C.SCALE, LEFT(C.IDENTITY,1),
0412                 LEFT(tc.TYPE, 1) AS tabconsttype, k.COLSEQ
0413                 FROM QSYS2.SYSCOLUMNS C
0414                 LEFT JOIN (QSYS2.syskeycst k JOIN QSYS2.SYSCST tc
0415                     ON (k.TABLE_SCHEMA = tc.TABLE_SCHEMA
0416                       AND k.TABLE_NAME = tc.TABLE_NAME
0417                       AND LEFT(tc.type,1) = 'P'))
0418                     ON (C.TABLE_SCHEMA = k.TABLE_SCHEMA
0419                        AND C.TABLE_NAME = k.TABLE_NAME
0420                        AND C.COLUMN_NAME = k.COLUMN_NAME)
0421                 WHERE "
0422                 . $this->quoteInto('UPPER(C.TABLE_NAME) = UPPER(?)', $tableName);
0423 
0424             if ($schemaName) {
0425                 $sql .= $this->quoteInto(' AND UPPER(C.TABLE_SCHEMA) = UPPER(?)', $schemaName);
0426             }
0427 
0428             $sql .= " ORDER BY C.ORDINAL_POSITION FOR FETCH ONLY";
0429         }
0430 
0431         $desc = array();
0432         $stmt = $this->query($sql);
0433 
0434         /**
0435          * To avoid case issues, fetch using FETCH_NUM
0436          */
0437         $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
0438 
0439         /**
0440          * The ordering of columns is defined by the query so we can map
0441          * to variables to improve readability
0442          */
0443         $tabschema      = 0;
0444         $tabname        = 1;
0445         $colname        = 2;
0446         $colno          = 3;
0447         $typename       = 4;
0448         $default        = 5;
0449         $nulls          = 6;
0450         $length         = 7;
0451         $scale          = 8;
0452         $identityCol    = 9;
0453         $tabconstType   = 10;
0454         $colseq         = 11;
0455 
0456         foreach ($result as $key => $row) {
0457             list ($primary, $primaryPosition, $identity) = array(false, null, false);
0458             if ($row[$tabconstType] == 'P') {
0459                 $primary = true;
0460                 $primaryPosition = $row[$colseq];
0461             }
0462             /**
0463              * In IBM DB2, an column can be IDENTITY
0464              * even if it is not part of the PRIMARY KEY.
0465              */
0466             if ($row[$identityCol] == 'Y') {
0467                 $identity = true;
0468             }
0469 
0470             // only colname needs to be case adjusted
0471             $desc[$this->foldCase($row[$colname])] = array(
0472                 'SCHEMA_NAME'      => $this->foldCase($row[$tabschema]),
0473                 'TABLE_NAME'       => $this->foldCase($row[$tabname]),
0474                 'COLUMN_NAME'      => $this->foldCase($row[$colname]),
0475                 'COLUMN_POSITION'  => (!$this->_isI5) ? $row[$colno]+1 : $row[$colno],
0476                 'DATA_TYPE'        => $row[$typename],
0477                 'DEFAULT'          => $row[$default],
0478                 'NULLABLE'         => (bool) ($row[$nulls] == 'Y'),
0479                 'LENGTH'           => $row[$length],
0480                 'SCALE'            => $row[$scale],
0481                 'PRECISION'        => ($row[$typename] == 'DECIMAL' ? $row[$length] : 0),
0482                 'UNSIGNED'         => false,
0483                 'PRIMARY'          => $primary,
0484                 'PRIMARY_POSITION' => $primaryPosition,
0485                 'IDENTITY'         => $identity
0486             );
0487         }
0488 
0489         return $desc;
0490     }
0491 
0492     /**
0493      * Return the most recent value from the specified sequence in the database.
0494      * This is supported only on RDBMS brands that support sequences
0495      * (e.g. Oracle, PostgreSQL, DB2).  Other RDBMS brands return null.
0496      *
0497      * @param string $sequenceName
0498      * @return string
0499      */
0500     public function lastSequenceId($sequenceName)
0501     {
0502         $this->_connect();
0503 
0504         if (!$this->_isI5) {
0505             $quotedSequenceName = $this->quoteIdentifier($sequenceName, true);
0506             $sql = 'SELECT PREVVAL FOR ' . $quotedSequenceName . ' AS VAL FROM SYSIBM.SYSDUMMY1';
0507         } else {
0508             $quotedSequenceName = $sequenceName;
0509             $sql = 'SELECT PREVVAL FOR ' . $this->quoteIdentifier($sequenceName, true) . ' AS VAL FROM QSYS2.QSQPTABL';
0510         }
0511 
0512         $value = $this->fetchOne($sql);
0513         return (string) $value;
0514     }
0515 
0516     /**
0517      * Generate a new value from the specified sequence in the database, and return it.
0518      * This is supported only on RDBMS brands that support sequences
0519      * (e.g. Oracle, PostgreSQL, DB2).  Other RDBMS brands return null.
0520      *
0521      * @param string $sequenceName
0522      * @return string
0523      */
0524     public function nextSequenceId($sequenceName)
0525     {
0526         $this->_connect();
0527         $sql = 'SELECT NEXTVAL FOR '.$this->quoteIdentifier($sequenceName, true).' AS VAL FROM SYSIBM.SYSDUMMY1';
0528         $value = $this->fetchOne($sql);
0529         return (string) $value;
0530     }
0531 
0532     /**
0533      * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
0534      *
0535      * As a convention, on RDBMS brands that support sequences
0536      * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
0537      * from the arguments and returns the last id generated by that sequence.
0538      * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
0539      * returns the last value generated for such a column, and the table name
0540      * argument is disregarded.
0541      *
0542      * The IDENTITY_VAL_LOCAL() function gives the last generated identity value
0543      * in the current process, even if it was for a GENERATED column.
0544      *
0545      * @param string $tableName OPTIONAL
0546      * @param string $primaryKey OPTIONAL
0547      * @param string $idType OPTIONAL used for i5 platform to define sequence/idenity unique value
0548      * @return string
0549      */
0550 
0551     public function lastInsertId($tableName = null, $primaryKey = null, $idType = null)
0552     {
0553         $this->_connect();
0554 
0555         if ($this->_isI5) {
0556             return (string) $this->_i5LastInsertId($tableName, $idType);
0557         }
0558 
0559         if ($tableName !== null) {
0560             $sequenceName = $tableName;
0561             if ($primaryKey) {
0562                 $sequenceName .= "_$primaryKey";
0563             }
0564             $sequenceName .= '_seq';
0565             return $this->lastSequenceId($sequenceName);
0566         }
0567 
0568         $sql = 'SELECT IDENTITY_VAL_LOCAL() AS VAL FROM SYSIBM.SYSDUMMY1';
0569         $value = $this->fetchOne($sql);
0570         return (string) $value;
0571     }
0572 
0573     /**
0574      * Begin a transaction.
0575      *
0576      * @return void
0577      */
0578     protected function _beginTransaction()
0579     {
0580         $this->_setExecuteMode(DB2_AUTOCOMMIT_OFF);
0581     }
0582 
0583     /**
0584      * Commit a transaction.
0585      *
0586      * @return void
0587      */
0588     protected function _commit()
0589     {
0590         if (!db2_commit($this->_connection)) {
0591             /**
0592              * @see Zend_Db_Adapter_Db2_Exception
0593              */
0594             // require_once 'Zend/Db/Adapter/Db2/Exception.php';
0595             throw new Zend_Db_Adapter_Db2_Exception(
0596                 db2_conn_errormsg($this->_connection),
0597                 db2_conn_error($this->_connection));
0598         }
0599 
0600         $this->_setExecuteMode(DB2_AUTOCOMMIT_ON);
0601     }
0602 
0603     /**
0604      * Rollback a transaction.
0605      *
0606      * @return void
0607      */
0608     protected function _rollBack()
0609     {
0610         if (!db2_rollback($this->_connection)) {
0611             /**
0612              * @see Zend_Db_Adapter_Db2_Exception
0613              */
0614             // require_once 'Zend/Db/Adapter/Db2/Exception.php';
0615             throw new Zend_Db_Adapter_Db2_Exception(
0616                 db2_conn_errormsg($this->_connection),
0617                 db2_conn_error($this->_connection));
0618         }
0619         $this->_setExecuteMode(DB2_AUTOCOMMIT_ON);
0620     }
0621 
0622     /**
0623      * Set the fetch mode.
0624      *
0625      * @param integer $mode
0626      * @return void
0627      * @throws Zend_Db_Adapter_Db2_Exception
0628      */
0629     public function setFetchMode($mode)
0630     {
0631         switch ($mode) {
0632             case Zend_Db::FETCH_NUM:   // seq array
0633             case Zend_Db::FETCH_ASSOC: // assoc array
0634             case Zend_Db::FETCH_BOTH:  // seq+assoc array
0635             case Zend_Db::FETCH_OBJ:   // object
0636                 $this->_fetchMode = $mode;
0637                 break;
0638             case Zend_Db::FETCH_BOUND:   // bound to PHP variable
0639                 /**
0640                  * @see Zend_Db_Adapter_Db2_Exception
0641                  */
0642                 // require_once 'Zend/Db/Adapter/Db2/Exception.php';
0643                 throw new Zend_Db_Adapter_Db2_Exception('FETCH_BOUND is not supported yet');
0644                 break;
0645             default:
0646                 /**
0647                  * @see Zend_Db_Adapter_Db2_Exception
0648                  */
0649                 // require_once 'Zend/Db/Adapter/Db2/Exception.php';
0650                 throw new Zend_Db_Adapter_Db2_Exception("Invalid fetch mode '$mode' specified");
0651                 break;
0652         }
0653     }
0654 
0655     /**
0656      * Adds an adapter-specific LIMIT clause to the SELECT statement.
0657      *
0658      * @param string $sql
0659      * @param integer $count
0660      * @param integer $offset OPTIONAL
0661      * @return string
0662      */
0663     public function limit($sql, $count, $offset = 0)
0664     {
0665         $count = intval($count);
0666         if ($count <= 0) {
0667             /**
0668              * @see Zend_Db_Adapter_Db2_Exception
0669              */
0670             // require_once 'Zend/Db/Adapter/Db2/Exception.php';
0671             throw new Zend_Db_Adapter_Db2_Exception("LIMIT argument count=$count is not valid");
0672         }
0673 
0674         $offset = intval($offset);
0675         if ($offset < 0) {
0676             /**
0677              * @see Zend_Db_Adapter_Db2_Exception
0678              */
0679             // require_once 'Zend/Db/Adapter/Db2/Exception.php';
0680             throw new Zend_Db_Adapter_Db2_Exception("LIMIT argument offset=$offset is not valid");
0681         }
0682 
0683         if ($offset == 0) {
0684             $limit_sql = $sql . " FETCH FIRST $count ROWS ONLY";
0685             return $limit_sql;
0686         }
0687 
0688         /**
0689          * DB2 does not implement the LIMIT clause as some RDBMS do.
0690          * We have to simulate it with subqueries and ROWNUM.
0691          * Unfortunately because we use the column wildcard "*",
0692          * this puts an extra column into the query result set.
0693          */
0694         $limit_sql = "SELECT z2.*
0695             FROM (
0696                 SELECT ROW_NUMBER() OVER() AS \"ZEND_DB_ROWNUM\", z1.*
0697                 FROM (
0698                     " . $sql . "
0699                 ) z1
0700             ) z2
0701             WHERE z2.zend_db_rownum BETWEEN " . ($offset+1) . " AND " . ($offset+$count);
0702         return $limit_sql;
0703     }
0704 
0705     /**
0706      * Check if the adapter supports real SQL parameters.
0707      *
0708      * @param string $type 'positional' or 'named'
0709      * @return bool
0710      */
0711     public function supportsParameters($type)
0712     {
0713         if ($type == 'positional') {
0714             return true;
0715         }
0716 
0717         // if its 'named' or anything else
0718         return false;
0719     }
0720 
0721     /**
0722      * Retrieve server version in PHP style
0723      *
0724      * @return string
0725      */
0726     public function getServerVersion()
0727     {
0728         $this->_connect();
0729         $server_info = db2_server_info($this->_connection);
0730         if ($server_info !== false) {
0731             $version = $server_info->DBMS_VER;
0732             if ($this->_isI5) {
0733                 $version = (int) substr($version, 0, 2) . '.' . (int) substr($version, 2, 2) . '.' . (int) substr($version, 4);
0734             }
0735             return $version;
0736         } else {
0737             return null;
0738         }
0739     }
0740 
0741     /**
0742      * Return whether or not this is running on i5
0743      *
0744      * @return bool
0745      */
0746     public function isI5()
0747     {
0748         if ($this->_isI5 === null) {
0749             $this->_determineI5();
0750         }
0751 
0752         return (bool) $this->_isI5;
0753     }
0754 
0755     /**
0756      * Check the connection parameters according to verify
0757      * type of used OS
0758      *
0759      *  @return void
0760      */
0761     protected function _determineI5()
0762     {
0763         // first us the compiled flag.
0764         $this->_isI5 = (php_uname('s') == 'OS400') ? true : false;
0765 
0766         // if this is set, then us it
0767         if (isset($this->_config['os'])){
0768             if (strtolower($this->_config['os']) === 'i5') {
0769                 $this->_isI5 = true;
0770             } else {
0771                 // any other value passed in, its null
0772                 $this->_isI5 = false;
0773             }
0774         }
0775 
0776     }
0777 
0778     /**
0779      * Db2 On I5 specific method
0780      *
0781      * Returns a list of the tables in the database .
0782      * Used only for DB2/400.
0783      *
0784      * @return array
0785      */
0786     protected function _i5listTables($schema = null)
0787     {
0788         //list of i5 libraries.
0789         $tables = array();
0790         if ($schema) {
0791             $tablesStatement = db2_tables($this->_connection, null, $schema);
0792             while ($rowTables = db2_fetch_assoc($tablesStatement) ) {
0793                 if ($rowTables['TABLE_NAME'] !== null) {
0794                     $tables[] = $rowTables['TABLE_NAME'];
0795                 }
0796             }
0797         } else {
0798             $schemaStatement = db2_tables($this->_connection);
0799             while ($schema = db2_fetch_assoc($schemaStatement)) {
0800                 if ($schema['TABLE_SCHEM'] !== null) {
0801                     // list of the tables which belongs to the selected library
0802                     $tablesStatement = db2_tables($this->_connection, NULL, $schema['TABLE_SCHEM']);
0803                     if (is_resource($tablesStatement)) {
0804                         while ($rowTables = db2_fetch_assoc($tablesStatement) ) {
0805                             if ($rowTables['TABLE_NAME'] !== null) {
0806                                 $tables[] = $rowTables['TABLE_NAME'];
0807                             }
0808                         }
0809                     }
0810                 }
0811             }
0812         }
0813 
0814         return $tables;
0815     }
0816 
0817     protected function _i5LastInsertId($objectName = null, $idType = null)
0818     {
0819 
0820         if ($objectName === null) {
0821             $sql = 'SELECT IDENTITY_VAL_LOCAL() AS VAL FROM QSYS2.QSQPTABL';
0822             $value = $this->fetchOne($sql);
0823             return $value;
0824         }
0825 
0826         if (strtoupper($idType) === 'S'){
0827             //check i5_lib option
0828             $sequenceName = $objectName;
0829             return $this->lastSequenceId($sequenceName);
0830         }
0831 
0832             //returns last identity value for the specified table
0833         //if (strtoupper($idType) === 'I') {
0834         $tableName = $objectName;
0835         return $this->fetchOne('SELECT IDENTITY_VAL_LOCAL() from ' . $this->quoteIdentifier($tableName));
0836     }
0837 
0838 }
0839 
0840