PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Db/Adapter/Pdo/Oci.php

https://github.com/mfairchild365/zf2
PHP | 376 lines | 190 code | 25 blank | 161 comment | 20 complexity | 96819247e4c14c17d3d97f8422779281 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Db
  17. * @subpackage Adapter
  18. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. */
  21. /**
  22. * @namespace
  23. */
  24. namespace Zend\Db\Adapter\Pdo;
  25. use Zend\Db;
  26. use Zend\Db\Adapter;
  27. /**
  28. * Class for connecting to Oracle databases and performing common operations.
  29. *
  30. * @uses \Zend\Db\Db
  31. * @uses \Zend\Db\Adapter\Exception
  32. * @uses \Zend\Db\Adapter\Pdo\AbstractPdo
  33. * @category Zend
  34. * @package Zend_Db
  35. * @subpackage Adapter
  36. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  37. * @license http://framework.zend.com/license/new-bsd New BSD License
  38. */
  39. class Oci extends \Zend\Db\Adapter\AbstractPdoAdapter
  40. {
  41. /**
  42. * Pdo type.
  43. *
  44. * @var string
  45. */
  46. protected $_pdoType = 'oci';
  47. /**
  48. * Default class name for a DB statement.
  49. *
  50. * @var string
  51. */
  52. protected $_defaultStmtClass = 'Zend\Db\Statement\Pdo\Oci';
  53. /**
  54. * Keys are UPPERCASE SQL datatypes or the constants
  55. * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE.
  56. *
  57. * Values are:
  58. * 0 = 32-bit integer
  59. * 1 = 64-bit integer
  60. * 2 = float or decimal
  61. *
  62. * @var array Associative array of datatypes to values 0, 1, or 2.
  63. */
  64. protected $_numericDataTypes = array(
  65. Db\Db::INT_TYPE => Db\Db::INT_TYPE,
  66. Db\Db::BIGINT_TYPE => Db\Db::BIGINT_TYPE,
  67. Db\Db::FLOAT_TYPE => Db\Db::FLOAT_TYPE,
  68. 'BINARY_DOUBLE' => Db\Db::FLOAT_TYPE,
  69. 'BINARY_FLOAT' => Db\Db::FLOAT_TYPE,
  70. 'NUMBER' => Db\Db::FLOAT_TYPE
  71. );
  72. /**
  73. * Creates a Pdo DSN for the adapter from $this->_config settings.
  74. *
  75. * @return string
  76. */
  77. protected function _dsn()
  78. {
  79. // baseline of DSN parts
  80. $dsn = $this->_config;
  81. if (isset($dsn['host'])) {
  82. $tns = 'dbname=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)' .
  83. '(HOST=' . $dsn['host'] . ')';
  84. if (isset($dsn['port'])) {
  85. $tns .= '(PORT=' . $dsn['port'] . ')';
  86. } else {
  87. $tns .= '(PORT=1521)';
  88. }
  89. $tns .= '))(CONNECT_DATA=(SID=' . $dsn['dbname'] . ')))';
  90. } else {
  91. $tns = 'dbname=' . $dsn['dbname'];
  92. }
  93. if (isset($dsn['charset'])) {
  94. $tns .= ';charset=' . $dsn['charset'];
  95. }
  96. return $this->_pdoType . ':' . $tns;
  97. }
  98. /**
  99. * Quote a raw string.
  100. * Most Pdo drivers have an implementation for the quote() method,
  101. * but the Oracle Oci driver must use the same implementation as the
  102. * Zend_Db_Adapter_Abstract class.
  103. *
  104. * @param string $value Raw string
  105. * @return string Quoted string
  106. */
  107. protected function _quote($value)
  108. {
  109. if (is_int($value) || is_float($value)) {
  110. return $value;
  111. }
  112. $value = str_replace("'", "''", $value);
  113. return "'" . addcslashes($value, "\000\n\r\\\032") . "'";
  114. }
  115. /**
  116. * Quote a table identifier and alias.
  117. *
  118. * @param string|array|\Zend\Db\Expr $ident The identifier or expression.
  119. * @param string $alias An alias for the table.
  120. * @return string The quoted identifier and alias.
  121. */
  122. public function quoteTableAs($ident, $alias = null, $auto = false)
  123. {
  124. // Oracle doesn't allow the 'AS' keyword between the table identifier/expression and alias.
  125. return $this->_quoteIdentifierAs($ident, $alias, $auto, ' ');
  126. }
  127. /**
  128. * Returns a list of the tables in the database.
  129. *
  130. * @return array
  131. */
  132. public function listTables()
  133. {
  134. $data = $this->fetchCol('SELECT table_name FROM all_tables');
  135. return $data;
  136. }
  137. /**
  138. * Returns the column descriptions for a table.
  139. *
  140. * The return value is an associative array keyed by the column name,
  141. * as returned by the RDBMS.
  142. *
  143. * The value of each array element is an associative array
  144. * with the following keys:
  145. *
  146. * SCHEMA_NAME => string; name of schema
  147. * TABLE_NAME => string;
  148. * COLUMN_NAME => string; column name
  149. * COLUMN_POSITION => number; ordinal position of column in table
  150. * DATA_TYPE => string; SQL datatype name of column
  151. * DEFAULT => string; default expression of column, null if none
  152. * NULLABLE => boolean; true if column can have nulls
  153. * LENGTH => number; length of CHAR/VARCHAR
  154. * SCALE => number; scale of NUMERIC/DECIMAL
  155. * PRECISION => number; precision of NUMERIC/DECIMAL
  156. * UNSIGNED => boolean; unsigned property of an integer type
  157. * PRIMARY => boolean; true if column is part of the primary key
  158. * PRIMARY_POSITION => integer; position of column in primary key
  159. * IDENTITY => integer; true if column is auto-generated with unique values
  160. *
  161. * @todo Discover integer unsigned property.
  162. *
  163. * @param string $tableName
  164. * @param string $schemaName OPTIONAL
  165. * @return array
  166. */
  167. public function describeTable($tableName, $schemaName = null)
  168. {
  169. $version = $this->getServerVersion();
  170. if (($version === null) || version_compare($version, '9.0.0', '>=')) {
  171. $sql = "SELECT TC.TABLE_NAME, TC.OWNER, TC.COLUMN_NAME, TC.DATA_TYPE,
  172. TC.DATA_DEFAULT, TC.NULLABLE, TC.COLUMN_ID, TC.DATA_LENGTH,
  173. TC.DATA_SCALE, TC.DATA_PRECISION, C.CONSTRAINT_TYPE, CC.POSITION
  174. FROM ALL_TAB_COLUMNS TC
  175. LEFT JOIN (ALL_CONS_COLUMNS CC JOIN ALL_CONSTRAINTS C
  176. ON (CC.CONSTRAINT_NAME = C.CONSTRAINT_NAME AND CC.TABLE_NAME = C.TABLE_NAME AND CC.OWNER = C.OWNER AND C.CONSTRAINT_TYPE = 'P'))
  177. ON TC.TABLE_NAME = CC.TABLE_NAME AND TC.COLUMN_NAME = CC.COLUMN_NAME
  178. WHERE UPPER(TC.TABLE_NAME) = UPPER(:TBNAME)";
  179. $bind[':TBNAME'] = $tableName;
  180. if ($schemaName) {
  181. $sql .= ' AND UPPER(TC.OWNER) = UPPER(:SCNAME)';
  182. $bind[':SCNAME'] = $schemaName;
  183. }
  184. $sql .= ' ORDER BY TC.COLUMN_ID';
  185. } else {
  186. $subSql="SELECT AC.OWNER, AC.TABLE_NAME, ACC.COLUMN_NAME, AC.CONSTRAINT_TYPE, ACC.POSITION
  187. from ALL_CONSTRAINTS AC, ALL_CONS_COLUMNS ACC
  188. WHERE ACC.CONSTRAINT_NAME = AC.CONSTRAINT_NAME
  189. AND ACC.TABLE_NAME = AC.TABLE_NAME
  190. AND ACC.OWNER = AC.OWNER
  191. AND AC.CONSTRAINT_TYPE = 'P'
  192. AND UPPER(AC.TABLE_NAME) = UPPER(:TBNAME)";
  193. $bind[':TBNAME'] = $tableName;
  194. if ($schemaName) {
  195. $subSql .= ' AND UPPER(ACC.OWNER) = UPPER(:SCNAME)';
  196. $bind[':SCNAME'] = $schemaName;
  197. }
  198. $sql="SELECT TC.TABLE_NAME, TC.OWNER, TC.COLUMN_NAME, TC.DATA_TYPE,
  199. TC.DATA_DEFAULT, TC.NULLABLE, TC.COLUMN_ID, TC.DATA_LENGTH,
  200. TC.DATA_SCALE, TC.DATA_PRECISION, CC.CONSTRAINT_TYPE, CC.POSITION
  201. FROM ALL_TAB_COLUMNS TC, ($subSql) CC
  202. WHERE UPPER(TC.TABLE_NAME) = UPPER(:TBNAME)
  203. AND TC.OWNER = CC.OWNER(+) AND TC.TABLE_NAME = CC.TABLE_NAME(+) AND TC.COLUMN_NAME = CC.COLUMN_NAME(+)";
  204. if ($schemaName) {
  205. $sql .= ' AND UPPER(TC.OWNER) = UPPER(:SCNAME)';
  206. }
  207. $sql .= ' ORDER BY TC.COLUMN_ID';
  208. }
  209. $stmt = $this->query($sql, $bind);
  210. /**
  211. * Use FETCH_NUM so we are not dependent on the CASE attribute of the Pdo connection
  212. */
  213. $result = $stmt->fetchAll(Db\Db::FETCH_NUM);
  214. $table_name = 0;
  215. $owner = 1;
  216. $column_name = 2;
  217. $data_type = 3;
  218. $data_default = 4;
  219. $nullable = 5;
  220. $column_id = 6;
  221. $data_length = 7;
  222. $data_scale = 8;
  223. $data_precision = 9;
  224. $constraint_type = 10;
  225. $position = 11;
  226. $desc = array();
  227. foreach ($result as $key => $row) {
  228. list ($primary, $primaryPosition, $identity) = array(false, null, false);
  229. if ($row[$constraint_type] == 'P') {
  230. $primary = true;
  231. $primaryPosition = $row[$position];
  232. /**
  233. * Oracle does not support auto-increment keys.
  234. */
  235. $identity = false;
  236. }
  237. $desc[$this->foldCase($row[$column_name])] = array(
  238. 'SCHEMA_NAME' => $this->foldCase($row[$owner]),
  239. 'TABLE_NAME' => $this->foldCase($row[$table_name]),
  240. 'COLUMN_NAME' => $this->foldCase($row[$column_name]),
  241. 'COLUMN_POSITION' => $row[$column_id],
  242. 'DATA_TYPE' => $row[$data_type],
  243. 'DEFAULT' => $row[$data_default],
  244. 'NULLABLE' => (bool) ($row[$nullable] == 'Y'),
  245. 'LENGTH' => $row[$data_length],
  246. 'SCALE' => $row[$data_scale],
  247. 'PRECISION' => $row[$data_precision],
  248. 'UNSIGNED' => null, // @todo
  249. 'PRIMARY' => $primary,
  250. 'PRIMARY_POSITION' => $primaryPosition,
  251. 'IDENTITY' => $identity
  252. );
  253. }
  254. return $desc;
  255. }
  256. /**
  257. * Return the most recent value from the specified sequence in the database.
  258. * This is supported only on RDBMS brands that support sequences
  259. * (e.g. Oracle, PostgreSQL, Db2). Other RDBMS brands return null.
  260. *
  261. * @param string $sequenceName
  262. * @return integer
  263. */
  264. public function lastSequenceId($sequenceName)
  265. {
  266. $this->_connect();
  267. $value = $this->fetchOne('SELECT '.$this->quoteIdentifier($sequenceName, true).'.CURRVAL FROM dual');
  268. return $value;
  269. }
  270. /**
  271. * Generate a new value from the specified sequence in the database, and return it.
  272. * This is supported only on RDBMS brands that support sequences
  273. * (e.g. Oracle, PostgreSQL, Db2). Other RDBMS brands return null.
  274. *
  275. * @param string $sequenceName
  276. * @return integer
  277. */
  278. public function nextSequenceId($sequenceName)
  279. {
  280. $this->_connect();
  281. $value = $this->fetchOne('SELECT '.$this->quoteIdentifier($sequenceName, true).'.NEXTVAL FROM dual');
  282. return $value;
  283. }
  284. /**
  285. * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
  286. *
  287. * As a convention, on RDBMS brands that support sequences
  288. * (e.g. Oracle, PostgreSQL, Db2), this method forms the name of a sequence
  289. * from the arguments and returns the last id generated by that sequence.
  290. * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
  291. * returns the last value generated for such a column, and the table name
  292. * argument is disregarded.
  293. *
  294. * Oracle does not support IDENTITY columns, so if the sequence is not
  295. * specified, this method returns null.
  296. *
  297. * @param string $tableName OPTIONAL Name of table.
  298. * @param string $primaryKey OPTIONAL Name of primary key column.
  299. * @return string
  300. * @throws \Zend\Db\Adapter\Oracle\Exception
  301. */
  302. public function lastInsertId($tableName = null, $primaryKey = null)
  303. {
  304. if ($tableName !== null) {
  305. $sequenceName = $tableName;
  306. if ($primaryKey) {
  307. $sequenceName .= $this->foldCase("_$primaryKey");
  308. }
  309. $sequenceName .= $this->foldCase('_seq');
  310. return $this->lastSequenceId($sequenceName);
  311. }
  312. // No support for IDENTITY columns; return null
  313. return null;
  314. }
  315. /**
  316. * Adds an adapter-specific LIMIT clause to the SELECT statement.
  317. *
  318. * @param string $sql
  319. * @param integer $count
  320. * @param integer $offset
  321. * @throws \Zend\Db\Adapter\Exception
  322. * @return string
  323. */
  324. public function limit($sql, $count, $offset = 0)
  325. {
  326. $count = intval($count);
  327. if ($count <= 0) {
  328. throw new Adapter\Exception("LIMIT argument count=$count is not valid");
  329. }
  330. $offset = intval($offset);
  331. if ($offset < 0) {
  332. throw new Adapter\Exception("LIMIT argument offset=$offset is not valid");
  333. }
  334. /**
  335. * Oracle does not implement the LIMIT clause as some RDBMS do.
  336. * We have to simulate it with subqueries and ROWNUM.
  337. * Unfortunately because we use the column wildcard "*",
  338. * this puts an extra column into the query result set.
  339. */
  340. $limit_sql = "SELECT z2.*
  341. FROM (
  342. SELECT z1.*, ROWNUM AS \"zend_db_rownum\"
  343. FROM (
  344. " . $sql . "
  345. ) z1
  346. ) z2
  347. WHERE z2.\"zend_db_rownum\" BETWEEN " . ($offset+1) . " AND " . ($offset+$count);
  348. return $limit_sql;
  349. }
  350. }