File indexing completed on 2025-01-26 05:24:56
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_Adapter_Pdo_Abstract 0026 */ 0027 // require_once 'Zend/Db/Adapter/Pdo/Abstract.php'; 0028 0029 0030 /** 0031 * Class for connecting to Oracle databases and performing common operations. 0032 * 0033 * @category Zend 0034 * @package Zend_Db 0035 * @subpackage Adapter 0036 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0037 * @license http://framework.zend.com/license/new-bsd New BSD License 0038 */ 0039 class Zend_Db_Adapter_Pdo_Oci extends Zend_Db_Adapter_Pdo_Abstract 0040 { 0041 0042 /** 0043 * PDO type. 0044 * 0045 * @var string 0046 */ 0047 protected $_pdoType = 'oci'; 0048 0049 /** 0050 * Default class name for a DB statement. 0051 * 0052 * @var string 0053 */ 0054 protected $_defaultStmtClass = 'Zend_Db_Statement_Pdo_Oci'; 0055 0056 /** 0057 * Keys are UPPERCASE SQL datatypes or the constants 0058 * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. 0059 * 0060 * Values are: 0061 * 0 = 32-bit integer 0062 * 1 = 64-bit integer 0063 * 2 = float or decimal 0064 * 0065 * @var array Associative array of datatypes to values 0, 1, or 2. 0066 */ 0067 protected $_numericDataTypes = array( 0068 Zend_Db::INT_TYPE => Zend_Db::INT_TYPE, 0069 Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE, 0070 Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE, 0071 'BINARY_DOUBLE' => Zend_Db::FLOAT_TYPE, 0072 'BINARY_FLOAT' => Zend_Db::FLOAT_TYPE, 0073 'NUMBER' => Zend_Db::FLOAT_TYPE 0074 ); 0075 0076 /** 0077 * Creates a PDO DSN for the adapter from $this->_config settings. 0078 * 0079 * @return string 0080 */ 0081 protected function _dsn() 0082 { 0083 // baseline of DSN parts 0084 $dsn = $this->_config; 0085 0086 if (isset($dsn['host'])) { 0087 $tns = 'dbname=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)' . 0088 '(HOST=' . $dsn['host'] . ')'; 0089 0090 if (isset($dsn['port'])) { 0091 $tns .= '(PORT=' . $dsn['port'] . ')'; 0092 } else { 0093 $tns .= '(PORT=1521)'; 0094 } 0095 0096 $tns .= '))(CONNECT_DATA=(SID=' . $dsn['dbname'] . ')))'; 0097 } else { 0098 $tns = 'dbname=' . $dsn['dbname']; 0099 } 0100 0101 if (isset($dsn['charset'])) { 0102 $tns .= ';charset=' . $dsn['charset']; 0103 } 0104 0105 return $this->_pdoType . ':' . $tns; 0106 } 0107 0108 /** 0109 * Quote a raw string. 0110 * Most PDO drivers have an implementation for the quote() method, 0111 * but the Oracle OCI driver must use the same implementation as the 0112 * Zend_Db_Adapter_Abstract class. 0113 * 0114 * @param string $value Raw string 0115 * @return string Quoted string 0116 */ 0117 protected function _quote($value) 0118 { 0119 if (is_int($value) || is_float($value)) { 0120 return $value; 0121 } 0122 $value = str_replace("'", "''", $value); 0123 return "'" . addcslashes($value, "\000\n\r\\\032") . "'"; 0124 } 0125 0126 /** 0127 * Quote a table identifier and alias. 0128 * 0129 * @param string|array|Zend_Db_Expr $ident The identifier or expression. 0130 * @param string $alias An alias for the table. 0131 * @return string The quoted identifier and alias. 0132 */ 0133 public function quoteTableAs($ident, $alias = null, $auto = false) 0134 { 0135 // Oracle doesn't allow the 'AS' keyword between the table identifier/expression and alias. 0136 return $this->_quoteIdentifierAs($ident, $alias, $auto, ' '); 0137 } 0138 0139 /** 0140 * Returns a list of the tables in the database. 0141 * 0142 * @return array 0143 */ 0144 public function listTables() 0145 { 0146 $data = $this->fetchCol('SELECT table_name FROM all_tables'); 0147 return $data; 0148 } 0149 0150 /** 0151 * Returns the column descriptions for a table. 0152 * 0153 * The return value is an associative array keyed by the column name, 0154 * as returned by the RDBMS. 0155 * 0156 * The value of each array element is an associative array 0157 * with the following keys: 0158 * 0159 * SCHEMA_NAME => string; name of schema 0160 * TABLE_NAME => string; 0161 * COLUMN_NAME => string; column name 0162 * COLUMN_POSITION => number; ordinal position of column in table 0163 * DATA_TYPE => string; SQL datatype name of column 0164 * DEFAULT => string; default expression of column, null if none 0165 * NULLABLE => boolean; true if column can have nulls 0166 * LENGTH => number; length of CHAR/VARCHAR 0167 * SCALE => number; scale of NUMERIC/DECIMAL 0168 * PRECISION => number; precision of NUMERIC/DECIMAL 0169 * UNSIGNED => boolean; unsigned property of an integer type 0170 * PRIMARY => boolean; true if column is part of the primary key 0171 * PRIMARY_POSITION => integer; position of column in primary key 0172 * IDENTITY => integer; true if column is auto-generated with unique values 0173 * 0174 * @todo Discover integer unsigned property. 0175 * 0176 * @param string $tableName 0177 * @param string $schemaName OPTIONAL 0178 * @return array 0179 */ 0180 public function describeTable($tableName, $schemaName = null) 0181 { 0182 $version = $this->getServerVersion(); 0183 if (($version === null) || version_compare($version, '9.0.0', '>=')) { 0184 $sql = "SELECT TC.TABLE_NAME, TC.OWNER, TC.COLUMN_NAME, TC.DATA_TYPE, 0185 TC.DATA_DEFAULT, TC.NULLABLE, TC.COLUMN_ID, TC.DATA_LENGTH, 0186 TC.DATA_SCALE, TC.DATA_PRECISION, C.CONSTRAINT_TYPE, CC.POSITION 0187 FROM ALL_TAB_COLUMNS TC 0188 LEFT JOIN (ALL_CONS_COLUMNS CC JOIN ALL_CONSTRAINTS C 0189 ON (CC.CONSTRAINT_NAME = C.CONSTRAINT_NAME AND CC.TABLE_NAME = C.TABLE_NAME AND CC.OWNER = C.OWNER AND C.CONSTRAINT_TYPE = 'P')) 0190 ON TC.TABLE_NAME = CC.TABLE_NAME AND TC.COLUMN_NAME = CC.COLUMN_NAME 0191 WHERE UPPER(TC.TABLE_NAME) = UPPER(:TBNAME)"; 0192 $bind[':TBNAME'] = $tableName; 0193 if ($schemaName) { 0194 $sql .= ' AND UPPER(TC.OWNER) = UPPER(:SCNAME)'; 0195 $bind[':SCNAME'] = $schemaName; 0196 } 0197 $sql .= ' ORDER BY TC.COLUMN_ID'; 0198 } else { 0199 $subSql="SELECT AC.OWNER, AC.TABLE_NAME, ACC.COLUMN_NAME, AC.CONSTRAINT_TYPE, ACC.POSITION 0200 from ALL_CONSTRAINTS AC, ALL_CONS_COLUMNS ACC 0201 WHERE ACC.CONSTRAINT_NAME = AC.CONSTRAINT_NAME 0202 AND ACC.TABLE_NAME = AC.TABLE_NAME 0203 AND ACC.OWNER = AC.OWNER 0204 AND AC.CONSTRAINT_TYPE = 'P' 0205 AND UPPER(AC.TABLE_NAME) = UPPER(:TBNAME)"; 0206 $bind[':TBNAME'] = $tableName; 0207 if ($schemaName) { 0208 $subSql .= ' AND UPPER(ACC.OWNER) = UPPER(:SCNAME)'; 0209 $bind[':SCNAME'] = $schemaName; 0210 } 0211 $sql="SELECT TC.TABLE_NAME, TC.OWNER, TC.COLUMN_NAME, TC.DATA_TYPE, 0212 TC.DATA_DEFAULT, TC.NULLABLE, TC.COLUMN_ID, TC.DATA_LENGTH, 0213 TC.DATA_SCALE, TC.DATA_PRECISION, CC.CONSTRAINT_TYPE, CC.POSITION 0214 FROM ALL_TAB_COLUMNS TC, ($subSql) CC 0215 WHERE UPPER(TC.TABLE_NAME) = UPPER(:TBNAME) 0216 AND TC.OWNER = CC.OWNER(+) AND TC.TABLE_NAME = CC.TABLE_NAME(+) AND TC.COLUMN_NAME = CC.COLUMN_NAME(+)"; 0217 if ($schemaName) { 0218 $sql .= ' AND UPPER(TC.OWNER) = UPPER(:SCNAME)'; 0219 } 0220 $sql .= ' ORDER BY TC.COLUMN_ID'; 0221 } 0222 0223 $stmt = $this->query($sql, $bind); 0224 0225 /** 0226 * Use FETCH_NUM so we are not dependent on the CASE attribute of the PDO connection 0227 */ 0228 $result = $stmt->fetchAll(Zend_Db::FETCH_NUM); 0229 0230 $table_name = 0; 0231 $owner = 1; 0232 $column_name = 2; 0233 $data_type = 3; 0234 $data_default = 4; 0235 $nullable = 5; 0236 $column_id = 6; 0237 $data_length = 7; 0238 $data_scale = 8; 0239 $data_precision = 9; 0240 $constraint_type = 10; 0241 $position = 11; 0242 0243 $desc = array(); 0244 foreach ($result as $key => $row) { 0245 list ($primary, $primaryPosition, $identity) = array(false, null, false); 0246 if ($row[$constraint_type] == 'P') { 0247 $primary = true; 0248 $primaryPosition = $row[$position]; 0249 /** 0250 * Oracle does not support auto-increment keys. 0251 */ 0252 $identity = false; 0253 } 0254 $desc[$this->foldCase($row[$column_name])] = array( 0255 'SCHEMA_NAME' => $this->foldCase($row[$owner]), 0256 'TABLE_NAME' => $this->foldCase($row[$table_name]), 0257 'COLUMN_NAME' => $this->foldCase($row[$column_name]), 0258 'COLUMN_POSITION' => $row[$column_id], 0259 'DATA_TYPE' => $row[$data_type], 0260 'DEFAULT' => $row[$data_default], 0261 'NULLABLE' => (bool) ($row[$nullable] == 'Y'), 0262 'LENGTH' => $row[$data_length], 0263 'SCALE' => $row[$data_scale], 0264 'PRECISION' => $row[$data_precision], 0265 'UNSIGNED' => null, // @todo 0266 'PRIMARY' => $primary, 0267 'PRIMARY_POSITION' => $primaryPosition, 0268 'IDENTITY' => $identity 0269 ); 0270 } 0271 return $desc; 0272 } 0273 0274 /** 0275 * Return the most recent value from the specified sequence in the database. 0276 * This is supported only on RDBMS brands that support sequences 0277 * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null. 0278 * 0279 * @param string $sequenceName 0280 * @return integer 0281 */ 0282 public function lastSequenceId($sequenceName) 0283 { 0284 $this->_connect(); 0285 $value = $this->fetchOne('SELECT '.$this->quoteIdentifier($sequenceName, true).'.CURRVAL FROM dual'); 0286 return $value; 0287 } 0288 0289 /** 0290 * Generate a new value from the specified sequence in the database, and return it. 0291 * This is supported only on RDBMS brands that support sequences 0292 * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null. 0293 * 0294 * @param string $sequenceName 0295 * @return integer 0296 */ 0297 public function nextSequenceId($sequenceName) 0298 { 0299 $this->_connect(); 0300 $value = $this->fetchOne('SELECT '.$this->quoteIdentifier($sequenceName, true).'.NEXTVAL FROM dual'); 0301 return $value; 0302 } 0303 0304 /** 0305 * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column. 0306 * 0307 * As a convention, on RDBMS brands that support sequences 0308 * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence 0309 * from the arguments and returns the last id generated by that sequence. 0310 * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method 0311 * returns the last value generated for such a column, and the table name 0312 * argument is disregarded. 0313 * 0314 * Oracle does not support IDENTITY columns, so if the sequence is not 0315 * specified, this method returns null. 0316 * 0317 * @param string $tableName OPTIONAL Name of table. 0318 * @param string $primaryKey OPTIONAL Name of primary key column. 0319 * @return string 0320 * @throws Zend_Db_Adapter_Oracle_Exception 0321 */ 0322 public function lastInsertId($tableName = null, $primaryKey = null) 0323 { 0324 if ($tableName !== null) { 0325 $sequenceName = $tableName; 0326 if ($primaryKey) { 0327 $sequenceName .= $this->foldCase("_$primaryKey"); 0328 } 0329 $sequenceName .= $this->foldCase('_seq'); 0330 return $this->lastSequenceId($sequenceName); 0331 } 0332 // No support for IDENTITY columns; return null 0333 return null; 0334 } 0335 0336 /** 0337 * Adds an adapter-specific LIMIT clause to the SELECT statement. 0338 * 0339 * @param string $sql 0340 * @param integer $count 0341 * @param integer $offset 0342 * @throws Zend_Db_Adapter_Exception 0343 * @return string 0344 */ 0345 public function limit($sql, $count, $offset = 0) 0346 { 0347 $count = intval($count); 0348 if ($count <= 0) { 0349 /** @see Zend_Db_Adapter_Exception */ 0350 // require_once 'Zend/Db/Adapter/Exception.php'; 0351 throw new Zend_Db_Adapter_Exception("LIMIT argument count=$count is not valid"); 0352 } 0353 0354 $offset = intval($offset); 0355 if ($offset < 0) { 0356 /** @see Zend_Db_Adapter_Exception */ 0357 // require_once 'Zend/Db/Adapter/Exception.php'; 0358 throw new Zend_Db_Adapter_Exception("LIMIT argument offset=$offset is not valid"); 0359 } 0360 0361 /** 0362 * Oracle does not implement the LIMIT clause as some RDBMS do. 0363 * We have to simulate it with subqueries and ROWNUM. 0364 * Unfortunately because we use the column wildcard "*", 0365 * this puts an extra column into the query result set. 0366 */ 0367 $limit_sql = "SELECT z2.* 0368 FROM ( 0369 SELECT z1.*, ROWNUM AS \"zend_db_rownum\" 0370 FROM ( 0371 " . $sql . " 0372 ) z1 0373 ) z2 0374 WHERE z2.\"zend_db_rownum\" BETWEEN " . ($offset+1) . " AND " . ($offset+$count); 0375 return $limit_sql; 0376 } 0377 0378 }