PageRenderTime 52ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/cake/libs/model/datasources/dbo/dbo_oracle.php

http://github.com/Datawalke/Coordino
PHP | 1159 lines | 990 code | 26 blank | 143 comment | 13 complexity | 483bee8e68c53555124eac30320cbc3d MD5 | raw file
  1. <?php
  2. /**
  3. * Oracle layer for DBO.
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @package cake
  16. * @subpackage cake.cake.libs.model.datasources.dbo
  17. * @since CakePHP v 1.2.0.4041
  18. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  19. */
  20. /**
  21. * Oracle layer for DBO.
  22. *
  23. * Long description for class
  24. *
  25. * @package cake
  26. * @subpackage cake.cake.libs.model.datasources.dbo
  27. */
  28. class DboOracle extends DboSource {
  29. /**
  30. * Configuration options
  31. *
  32. * @var array
  33. * @access public
  34. */
  35. var $config = array();
  36. /**
  37. * Alias
  38. *
  39. * @var string
  40. */
  41. var $alias = '';
  42. /**
  43. * Sequence names as introspected from the database
  44. */
  45. var $_sequences = array();
  46. /**
  47. * Transaction in progress flag
  48. *
  49. * @var boolean
  50. */
  51. var $__transactionStarted = false;
  52. /**
  53. * Column definitions
  54. *
  55. * @var array
  56. * @access public
  57. */
  58. var $columns = array(
  59. 'primary_key' => array('name' => ''),
  60. 'string' => array('name' => 'varchar2', 'limit' => '255'),
  61. 'text' => array('name' => 'varchar2'),
  62. 'integer' => array('name' => 'number'),
  63. 'float' => array('name' => 'float'),
  64. 'datetime' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'),
  65. 'timestamp' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'),
  66. 'time' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'),
  67. 'date' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'),
  68. 'binary' => array('name' => 'bytea'),
  69. 'boolean' => array('name' => 'boolean'),
  70. 'number' => array('name' => 'number'),
  71. 'inet' => array('name' => 'inet'));
  72. /**
  73. * Connection object
  74. *
  75. * @var mixed
  76. * @access protected
  77. */
  78. var $connection;
  79. /**
  80. * Query limit
  81. *
  82. * @var int
  83. * @access protected
  84. */
  85. var $_limit = -1;
  86. /**
  87. * Query offset
  88. *
  89. * @var int
  90. * @access protected
  91. */
  92. var $_offset = 0;
  93. /**
  94. * Enter description here...
  95. *
  96. * @var unknown_type
  97. * @access protected
  98. */
  99. var $_map;
  100. /**
  101. * Current Row
  102. *
  103. * @var mixed
  104. * @access protected
  105. */
  106. var $_currentRow;
  107. /**
  108. * Number of rows
  109. *
  110. * @var int
  111. * @access protected
  112. */
  113. var $_numRows;
  114. /**
  115. * Query results
  116. *
  117. * @var mixed
  118. * @access protected
  119. */
  120. var $_results;
  121. /**
  122. * Last error issued by oci extension
  123. *
  124. * @var unknown_type
  125. */
  126. var $_error;
  127. /**
  128. * Base configuration settings for MySQL driver
  129. *
  130. * @var array
  131. */
  132. var $_baseConfig = array(
  133. 'persistent' => true,
  134. 'host' => 'localhost',
  135. 'login' => 'system',
  136. 'password' => '',
  137. 'database' => 'cake',
  138. 'nls_sort' => '',
  139. 'nls_sort' => ''
  140. );
  141. /**
  142. * Table-sequence map
  143. *
  144. * @var unknown_type
  145. */
  146. var $_sequenceMap = array();
  147. /**
  148. * Connects to the database using options in the given configuration array.
  149. *
  150. * @return boolean True if the database could be connected, else false
  151. * @access public
  152. */
  153. function connect() {
  154. $config = $this->config;
  155. $this->connected = false;
  156. $config['charset'] = !empty($config['charset']) ? $config['charset'] : null;
  157. if (!$config['persistent']) {
  158. $this->connection = ocilogon($config['login'], $config['password'], $config['database'], $config['charset']);
  159. } else {
  160. $this->connection = ociplogon($config['login'], $config['password'], $config['database'], $config['charset']);
  161. }
  162. if ($this->connection) {
  163. $this->connected = true;
  164. if (!empty($config['nls_sort'])) {
  165. $this->execute('ALTER SESSION SET NLS_SORT='.$config['nls_sort']);
  166. }
  167. if (!empty($config['nls_comp'])) {
  168. $this->execute('ALTER SESSION SET NLS_COMP='.$config['nls_comp']);
  169. }
  170. $this->execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'");
  171. } else {
  172. $this->connected = false;
  173. $this->_setError();
  174. return false;
  175. }
  176. return $this->connected;
  177. }
  178. /**
  179. * Keeps track of the most recent Oracle error
  180. *
  181. */
  182. function _setError($source = null, $clear = false) {
  183. if ($source) {
  184. $e = ocierror($source);
  185. } else {
  186. $e = ocierror();
  187. }
  188. $this->_error = $e['message'];
  189. if ($clear) {
  190. $this->_error = null;
  191. }
  192. }
  193. /**
  194. * Sets the encoding language of the session
  195. *
  196. * @param string $lang language constant
  197. * @return bool
  198. */
  199. function setEncoding($lang) {
  200. if (!$this->execute('ALTER SESSION SET NLS_LANGUAGE='.$lang)) {
  201. return false;
  202. }
  203. return true;
  204. }
  205. /**
  206. * Gets the current encoding language
  207. *
  208. * @return string language constant
  209. */
  210. function getEncoding() {
  211. $sql = 'SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER=\'NLS_LANGUAGE\'';
  212. if (!$this->execute($sql)) {
  213. return false;
  214. }
  215. if (!$row = $this->fetchRow()) {
  216. return false;
  217. }
  218. return $row[0]['VALUE'];
  219. }
  220. /**
  221. * Disconnects from database.
  222. *
  223. * @return boolean True if the database could be disconnected, else false
  224. * @access public
  225. */
  226. function disconnect() {
  227. if ($this->connection) {
  228. $this->connected = !ocilogoff($this->connection);
  229. return !$this->connected;
  230. }
  231. }
  232. /**
  233. * Scrape the incoming SQL to create the association map. This is an extremely
  234. * experimental method that creates the association maps since Oracle will not tell us.
  235. *
  236. * @param string $sql
  237. * @return false if sql is nor a SELECT
  238. * @access protected
  239. */
  240. function _scrapeSQL($sql) {
  241. $sql = str_replace("\"", '', $sql);
  242. $preFrom = preg_split('/\bFROM\b/', $sql);
  243. $preFrom = $preFrom[0];
  244. $find = array('SELECT');
  245. $replace = array('');
  246. $fieldList = trim(str_replace($find, $replace, $preFrom));
  247. $fields = preg_split('/,\s+/', $fieldList);//explode(', ', $fieldList);
  248. $lastTableName = '';
  249. foreach($fields as $key => $value) {
  250. if ($value != 'COUNT(*) AS count') {
  251. if (preg_match('/\s+(\w+(\.\w+)*)$/', $value, $matches)) {
  252. $fields[$key] = $matches[1];
  253. if (preg_match('/^(\w+\.)/', $value, $matches)) {
  254. $fields[$key] = $matches[1] . $fields[$key];
  255. $lastTableName = $matches[1];
  256. }
  257. }
  258. /*
  259. if (preg_match('/(([[:alnum:]_]+)\.[[:alnum:]_]+)(\s+AS\s+(\w+))?$/i', $value, $matches)) {
  260. $fields[$key] = isset($matches[4]) ? $matches[2] . '.' . $matches[4] : $matches[1];
  261. }
  262. */
  263. }
  264. }
  265. $this->_map = array();
  266. foreach($fields as $f) {
  267. $e = explode('.', $f);
  268. if (count($e) > 1) {
  269. $table = $e[0];
  270. $field = strtolower($e[1]);
  271. } else {
  272. $table = 0;
  273. $field = $e[0];
  274. }
  275. $this->_map[] = array($table, $field);
  276. }
  277. }
  278. /**
  279. * Modify a SQL query to limit (and offset) the result set
  280. *
  281. * @param integer $limit Maximum number of rows to return
  282. * @param integer $offset Row to begin returning
  283. * @return modified SQL Query
  284. * @access public
  285. */
  286. function limit($limit = -1, $offset = 0) {
  287. $this->_limit = (int) $limit;
  288. $this->_offset = (int) $offset;
  289. }
  290. /**
  291. * Returns number of rows in previous resultset. If no previous resultset exists,
  292. * this returns false.
  293. *
  294. * @return integer Number of rows in resultset
  295. * @access public
  296. */
  297. function lastNumRows() {
  298. return $this->_numRows;
  299. }
  300. /**
  301. * Executes given SQL statement. This is an overloaded method.
  302. *
  303. * @param string $sql SQL statement
  304. * @return resource Result resource identifier or null
  305. * @access protected
  306. */
  307. function _execute($sql) {
  308. $this->_statementId = @ociparse($this->connection, $sql);
  309. if (!$this->_statementId) {
  310. $this->_setError($this->connection);
  311. return false;
  312. }
  313. if ($this->__transactionStarted) {
  314. $mode = OCI_DEFAULT;
  315. } else {
  316. $mode = OCI_COMMIT_ON_SUCCESS;
  317. }
  318. if (!@ociexecute($this->_statementId, $mode)) {
  319. $this->_setError($this->_statementId);
  320. return false;
  321. }
  322. $this->_setError(null, true);
  323. switch(ocistatementtype($this->_statementId)) {
  324. case 'DESCRIBE':
  325. case 'SELECT':
  326. $this->_scrapeSQL($sql);
  327. break;
  328. default:
  329. return $this->_statementId;
  330. break;
  331. }
  332. if ($this->_limit >= 1) {
  333. ocisetprefetch($this->_statementId, $this->_limit);
  334. } else {
  335. ocisetprefetch($this->_statementId, 3000);
  336. }
  337. $this->_numRows = ocifetchstatement($this->_statementId, $this->_results, $this->_offset, $this->_limit, OCI_NUM | OCI_FETCHSTATEMENT_BY_ROW);
  338. $this->_currentRow = 0;
  339. $this->limit();
  340. return $this->_statementId;
  341. }
  342. /**
  343. * Fetch result row
  344. *
  345. * @return array
  346. * @access public
  347. */
  348. function fetchRow() {
  349. if ($this->_currentRow >= $this->_numRows) {
  350. ocifreestatement($this->_statementId);
  351. $this->_map = null;
  352. $this->_results = null;
  353. $this->_currentRow = null;
  354. $this->_numRows = null;
  355. return false;
  356. }
  357. $resultRow = array();
  358. foreach($this->_results[$this->_currentRow] as $index => $field) {
  359. list($table, $column) = $this->_map[$index];
  360. if (strpos($column, ' count')) {
  361. $resultRow[0]['count'] = $field;
  362. } else {
  363. $resultRow[$table][$column] = $this->_results[$this->_currentRow][$index];
  364. }
  365. }
  366. $this->_currentRow++;
  367. return $resultRow;
  368. }
  369. /**
  370. * Fetches the next row from the current result set
  371. *
  372. * @return unknown
  373. */
  374. function fetchResult() {
  375. return $this->fetchRow();
  376. }
  377. /**
  378. * Checks to see if a named sequence exists
  379. *
  380. * @param string $sequence
  381. * @return bool
  382. * @access public
  383. */
  384. function sequenceExists($sequence) {
  385. $sql = "SELECT SEQUENCE_NAME FROM USER_SEQUENCES WHERE SEQUENCE_NAME = '$sequence'";
  386. if (!$this->execute($sql)) {
  387. return false;
  388. }
  389. return $this->fetchRow();
  390. }
  391. /**
  392. * Creates a database sequence
  393. *
  394. * @param string $sequence
  395. * @return bool
  396. * @access public
  397. */
  398. function createSequence($sequence) {
  399. $sql = "CREATE SEQUENCE $sequence";
  400. return $this->execute($sql);
  401. }
  402. /**
  403. * Create trigger
  404. *
  405. * @param string $table
  406. * @return mixed
  407. * @access public
  408. */
  409. function createTrigger($table) {
  410. $sql = "CREATE OR REPLACE TRIGGER pk_$table" . "_trigger BEFORE INSERT ON $table FOR EACH ROW BEGIN SELECT pk_$table.NEXTVAL INTO :NEW.ID FROM DUAL; END;";
  411. return $this->execute($sql);
  412. }
  413. /**
  414. * Returns an array of tables in the database. If there are no tables, an error is
  415. * raised and the application exits.
  416. *
  417. * @return array tablenames in the database
  418. * @access public
  419. */
  420. function listSources() {
  421. $cache = parent::listSources();
  422. if ($cache != null) {
  423. return $cache;
  424. }
  425. $sql = 'SELECT view_name AS name FROM user_views UNION SELECT table_name AS name FROM user_tables';
  426. if (!$this->execute($sql)) {
  427. return false;
  428. }
  429. $sources = array();
  430. while($r = $this->fetchRow()) {
  431. $sources[] = strtolower($r[0]['name']);
  432. }
  433. parent::listSources($sources);
  434. return $sources;
  435. }
  436. /**
  437. * Returns an array of the fields in given table name.
  438. *
  439. * @param object instance of a model to inspect
  440. * @return array Fields in table. Keys are name and type
  441. * @access public
  442. */
  443. function describe(&$model) {
  444. $table = $this->fullTableName($model, false);
  445. if (!empty($model->sequence)) {
  446. $this->_sequenceMap[$table] = $model->sequence;
  447. } elseif (!empty($model->table)) {
  448. $this->_sequenceMap[$table] = $model->table . '_seq';
  449. }
  450. $cache = parent::describe($model);
  451. if ($cache != null) {
  452. return $cache;
  453. }
  454. $sql = 'SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH FROM user_tab_columns WHERE table_name = \'';
  455. $sql .= strtoupper($this->fullTableName($model)) . '\'';
  456. if (!$this->execute($sql)) {
  457. return false;
  458. }
  459. $fields = array();
  460. for ($i = 0; $row = $this->fetchRow(); $i++) {
  461. $fields[strtolower($row[0]['COLUMN_NAME'])] = array(
  462. 'type'=> $this->column($row[0]['DATA_TYPE']),
  463. 'length'=> $row[0]['DATA_LENGTH']
  464. );
  465. }
  466. $this->__cacheDescription($this->fullTableName($model, false), $fields);
  467. return $fields;
  468. }
  469. /**
  470. * Deletes all the records in a table and drops all associated auto-increment sequences.
  471. * Using DELETE instead of TRUNCATE because it causes locking problems.
  472. *
  473. * @param mixed $table A string or model class representing the table to be truncated
  474. * @param integer $reset If -1, sequences are dropped, if 0 (default), sequences are reset,
  475. * and if 1, sequences are not modified
  476. * @return boolean SQL TRUNCATE TABLE statement, false if not applicable.
  477. * @access public
  478. *
  479. */
  480. function truncate($table, $reset = 0) {
  481. if (empty($this->_sequences)) {
  482. $sql = "SELECT sequence_name FROM all_sequences";
  483. $this->execute($sql);
  484. while ($row = $this->fetchRow()) {
  485. $this->_sequences[] = strtolower($row[0]['sequence_name']);
  486. }
  487. }
  488. $this->execute('DELETE FROM ' . $this->fullTableName($table));
  489. if (!isset($this->_sequenceMap[$table]) || !in_array($this->_sequenceMap[$table], $this->_sequences)) {
  490. return true;
  491. }
  492. if ($reset === 0) {
  493. $this->execute("SELECT {$this->_sequenceMap[$table]}.nextval FROM dual");
  494. $row = $this->fetchRow();
  495. $currval = $row[$this->_sequenceMap[$table]]['nextval'];
  496. $this->execute("SELECT min_value FROM all_sequences WHERE sequence_name = '{$this->_sequenceMap[$table]}'");
  497. $row = $this->fetchRow();
  498. $min_value = $row[0]['min_value'];
  499. if ($min_value == 1) $min_value = 0;
  500. $offset = -($currval - $min_value);
  501. $this->execute("ALTER SEQUENCE {$this->_sequenceMap[$table]} INCREMENT BY $offset MINVALUE $min_value");
  502. $this->execute("SELECT {$this->_sequenceMap[$table]}.nextval FROM dual");
  503. $this->execute("ALTER SEQUENCE {$this->_sequenceMap[$table]} INCREMENT BY 1");
  504. } else {
  505. //$this->execute("DROP SEQUENCE {$this->_sequenceMap[$table]}");
  506. }
  507. return true;
  508. }
  509. /**
  510. * Enables, disables, and lists table constraints
  511. *
  512. * Note: This method could have been written using a subselect for each table,
  513. * however the effort Oracle expends to run the constraint introspection is very high.
  514. * Therefore, this method caches the result once and loops through the arrays to find
  515. * what it needs. It reduced my query time by 50%. YMMV.
  516. *
  517. * @param string $action
  518. * @param string $table
  519. * @return mixed boolean true or array of constraints
  520. */
  521. function constraint($action, $table) {
  522. if (empty($table)) {
  523. trigger_error(__('Must specify table to operate on constraints', true));
  524. }
  525. $table = strtoupper($table);
  526. if (empty($this->_keyConstraints)) {
  527. $sql = "SELECT
  528. table_name,
  529. c.constraint_name
  530. FROM all_cons_columns cc
  531. LEFT JOIN all_indexes i ON (cc.constraint_name = i.index_name)
  532. LEFT JOIN all_constraints c ON(c.constraint_name = cc.constraint_name)";
  533. $this->execute($sql);
  534. while ($row = $this->fetchRow()) {
  535. $this->_keyConstraints[] = array($row[0]['table_name'], $row['c']['constraint_name']);
  536. }
  537. }
  538. $relatedKeys = array();
  539. foreach ($this->_keyConstraints as $c) {
  540. if ($c[0] == $table) {
  541. $relatedKeys[] = $c[1];
  542. }
  543. }
  544. if (empty($this->_constraints)) {
  545. $sql = "SELECT
  546. table_name,
  547. constraint_name,
  548. r_constraint_name
  549. FROM
  550. all_constraints";
  551. $this->execute($sql);
  552. while ($row = $this->fetchRow()) {
  553. $this->_constraints[] = $row[0];
  554. }
  555. }
  556. $constraints = array();
  557. foreach ($this->_constraints as $c) {
  558. if (in_array($c['r_constraint_name'], $relatedKeys)) {
  559. $constraints[] = array($c['table_name'], $c['constraint_name']);
  560. }
  561. }
  562. foreach ($constraints as $c) {
  563. list($table, $constraint) = $c;
  564. switch ($action) {
  565. case 'enable':
  566. $this->execute("ALTER TABLE $table ENABLE CONSTRAINT $constraint");
  567. break;
  568. case 'disable':
  569. $this->execute("ALTER TABLE $table DISABLE CONSTRAINT $constraint");
  570. break;
  571. case 'list':
  572. return $constraints;
  573. break;
  574. default:
  575. trigger_error(__('DboOracle::constraint() accepts only enable, disable, or list', true));
  576. }
  577. }
  578. return true;
  579. }
  580. /**
  581. * Returns an array of the indexes in given table name.
  582. *
  583. * @param string $model Name of model to inspect
  584. * @return array Fields in table. Keys are column and unique
  585. */
  586. function index($model) {
  587. $index = array();
  588. $table = $this->fullTableName($model, false);
  589. if ($table) {
  590. $indexes = $this->query('SELECT
  591. cc.table_name,
  592. cc.column_name,
  593. cc.constraint_name,
  594. c.constraint_type,
  595. i.index_name,
  596. i.uniqueness
  597. FROM all_cons_columns cc
  598. LEFT JOIN all_indexes i ON(cc.constraint_name = i.index_name)
  599. LEFT JOIN all_constraints c ON(c.constraint_name = cc.constraint_name)
  600. WHERE cc.table_name = \'' . strtoupper($table) .'\'');
  601. foreach ($indexes as $i => $idx) {
  602. if ($idx['c']['constraint_type'] == 'P') {
  603. $key = 'PRIMARY';
  604. } else {
  605. continue;
  606. }
  607. if (!isset($index[$key])) {
  608. $index[$key]['column'] = strtolower($idx['cc']['column_name']);
  609. $index[$key]['unique'] = intval($idx['i']['uniqueness'] == 'UNIQUE');
  610. } else {
  611. if (!is_array($index[$key]['column'])) {
  612. $col[] = $index[$key]['column'];
  613. }
  614. $col[] = strtolower($idx['cc']['column_name']);
  615. $index[$key]['column'] = $col;
  616. }
  617. }
  618. }
  619. return $index;
  620. }
  621. /**
  622. * Generate a Oracle Alter Table syntax for the given Schema comparison
  623. *
  624. * @param unknown_type $schema
  625. * @return unknown
  626. */
  627. function alterSchema($compare, $table = null) {
  628. if (!is_array($compare)) {
  629. return false;
  630. }
  631. $out = '';
  632. $colList = array();
  633. foreach($compare as $curTable => $types) {
  634. if (!$table || $table == $curTable) {
  635. $out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
  636. foreach($types as $type => $column) {
  637. switch($type) {
  638. case 'add':
  639. foreach($column as $field => $col) {
  640. $col['name'] = $field;
  641. $alter = 'ADD '.$this->buildColumn($col);
  642. if (isset($col['after'])) {
  643. $alter .= ' AFTER '. $this->name($col['after']);
  644. }
  645. $colList[] = $alter;
  646. }
  647. break;
  648. case 'drop':
  649. foreach($column as $field => $col) {
  650. $col['name'] = $field;
  651. $colList[] = 'DROP '.$this->name($field);
  652. }
  653. break;
  654. case 'change':
  655. foreach($column as $field => $col) {
  656. if (!isset($col['name'])) {
  657. $col['name'] = $field;
  658. }
  659. $colList[] = 'CHANGE '. $this->name($field).' '.$this->buildColumn($col);
  660. }
  661. break;
  662. }
  663. }
  664. $out .= "\t" . implode(",\n\t", $colList) . ";\n\n";
  665. }
  666. }
  667. return $out;
  668. }
  669. /**
  670. * This method should quote Oracle identifiers. Well it doesn't.
  671. * It would break all scaffolding and all of Cake's default assumptions.
  672. *
  673. * @param unknown_type $var
  674. * @return unknown
  675. * @access public
  676. */
  677. function name($name) {
  678. if (strpos($name, '.') !== false && strpos($name, '"') === false) {
  679. list($model, $field) = explode('.', $name);
  680. if ($field[0] == "_") {
  681. $name = "$model.\"$field\"";
  682. }
  683. } else {
  684. if ($name[0] == "_") {
  685. $name = "\"$name\"";
  686. }
  687. }
  688. return $name;
  689. }
  690. /**
  691. * Begin a transaction
  692. *
  693. * @param unknown_type $model
  694. * @return boolean True on success, false on fail
  695. * (i.e. if the database/model does not support transactions).
  696. */
  697. function begin() {
  698. $this->__transactionStarted = true;
  699. return true;
  700. }
  701. /**
  702. * Rollback a transaction
  703. *
  704. * @param unknown_type $model
  705. * @return boolean True on success, false on fail
  706. * (i.e. if the database/model does not support transactions,
  707. * or a transaction has not started).
  708. */
  709. function rollback() {
  710. return ocirollback($this->connection);
  711. }
  712. /**
  713. * Commit a transaction
  714. *
  715. * @param unknown_type $model
  716. * @return boolean True on success, false on fail
  717. * (i.e. if the database/model does not support transactions,
  718. * or a transaction has not started).
  719. */
  720. function commit() {
  721. $this->__transactionStarted = false;
  722. return ocicommit($this->connection);
  723. }
  724. /**
  725. * Converts database-layer column types to basic types
  726. *
  727. * @param string $real Real database-layer column type (i.e. "varchar(255)")
  728. * @return string Abstract column type (i.e. "string")
  729. * @access public
  730. */
  731. function column($real) {
  732. if (is_array($real)) {
  733. $col = $real['name'];
  734. if (isset($real['limit'])) {
  735. $col .= '('.$real['limit'].')';
  736. }
  737. return $col;
  738. } else {
  739. $real = strtolower($real);
  740. }
  741. $col = str_replace(')', '', $real);
  742. $limit = null;
  743. if (strpos($col, '(') !== false) {
  744. list($col, $limit) = explode('(', $col);
  745. }
  746. if (in_array($col, array('date', 'timestamp'))) {
  747. return $col;
  748. }
  749. if (strpos($col, 'number') !== false) {
  750. return 'integer';
  751. }
  752. if (strpos($col, 'integer') !== false) {
  753. return 'integer';
  754. }
  755. if (strpos($col, 'char') !== false) {
  756. return 'string';
  757. }
  758. if (strpos($col, 'text') !== false) {
  759. return 'text';
  760. }
  761. if (strpos($col, 'blob') !== false) {
  762. return 'binary';
  763. }
  764. if (in_array($col, array('float', 'double', 'decimal'))) {
  765. return 'float';
  766. }
  767. if ($col == 'boolean') {
  768. return $col;
  769. }
  770. return 'text';
  771. }
  772. /**
  773. * Returns a quoted and escaped string of $data for use in an SQL statement.
  774. *
  775. * @param string $data String to be prepared for use in an SQL statement
  776. * @return string Quoted and escaped
  777. * @access public
  778. */
  779. function value($data, $column = null, $safe = false) {
  780. $parent = parent::value($data, $column, $safe);
  781. if ($parent != null) {
  782. return $parent;
  783. }
  784. if ($data === null) {
  785. return 'NULL';
  786. }
  787. if ($data === '') {
  788. return "''";
  789. }
  790. switch($column) {
  791. case 'date':
  792. $data = date('Y-m-d H:i:s', strtotime($data));
  793. $data = "TO_DATE('$data', 'YYYY-MM-DD HH24:MI:SS')";
  794. break;
  795. case 'integer' :
  796. case 'float' :
  797. case null :
  798. if (is_numeric($data)) {
  799. break;
  800. }
  801. default:
  802. $data = str_replace("'", "''", $data);
  803. $data = "'$data'";
  804. break;
  805. }
  806. return $data;
  807. }
  808. /**
  809. * Returns the ID generated from the previous INSERT operation.
  810. *
  811. * @param string
  812. * @return integer
  813. * @access public
  814. */
  815. function lastInsertId($source) {
  816. $sequence = $this->_sequenceMap[$source];
  817. $sql = "SELECT $sequence.currval FROM dual";
  818. if (!$this->execute($sql)) {
  819. return false;
  820. }
  821. while($row = $this->fetchRow()) {
  822. return $row[$sequence]['currval'];
  823. }
  824. return false;
  825. }
  826. /**
  827. * Returns a formatted error message from previous database operation.
  828. *
  829. * @return string Error message with error number
  830. * @access public
  831. */
  832. function lastError() {
  833. return $this->_error;
  834. }
  835. /**
  836. * Returns number of affected rows in previous database operation. If no previous operation exists, this returns false.
  837. *
  838. * @return int Number of affected rows
  839. * @access public
  840. */
  841. function lastAffected() {
  842. return $this->_statementId ? ocirowcount($this->_statementId): false;
  843. }
  844. /**
  845. * Renders a final SQL statement by putting together the component parts in the correct order
  846. *
  847. * @param string $type
  848. * @param array $data
  849. * @return string
  850. */
  851. function renderStatement($type, $data) {
  852. extract($data);
  853. $aliases = null;
  854. switch (strtolower($type)) {
  855. case 'select':
  856. return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order} {$limit}";
  857. break;
  858. case 'create':
  859. return "INSERT INTO {$table} ({$fields}) VALUES ({$values})";
  860. break;
  861. case 'update':
  862. if (!empty($alias)) {
  863. $aliases = "{$this->alias}{$alias} ";
  864. }
  865. return "UPDATE {$table} {$aliases}SET {$fields} {$conditions}";
  866. break;
  867. case 'delete':
  868. if (!empty($alias)) {
  869. $aliases = "{$this->alias}{$alias} ";
  870. }
  871. return "DELETE FROM {$table} {$aliases}{$conditions}";
  872. break;
  873. case 'schema':
  874. foreach (array('columns', 'indexes') as $var) {
  875. if (is_array(${$var})) {
  876. ${$var} = "\t" . implode(",\n\t", array_filter(${$var}));
  877. }
  878. }
  879. if (trim($indexes) != '') {
  880. $columns .= ',';
  881. }
  882. return "CREATE TABLE {$table} (\n{$columns}{$indexes})";
  883. break;
  884. case 'alter':
  885. break;
  886. }
  887. }
  888. /**
  889. * Enter description here...
  890. *
  891. * @param Model $model
  892. * @param unknown_type $linkModel
  893. * @param string $type Association type
  894. * @param unknown_type $association
  895. * @param unknown_type $assocData
  896. * @param unknown_type $queryData
  897. * @param unknown_type $external
  898. * @param unknown_type $resultSet
  899. * @param integer $recursive Number of levels of association
  900. * @param array $stack
  901. */
  902. function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) {
  903. if ($query = $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet)) {
  904. if (!isset($resultSet) || !is_array($resultSet)) {
  905. if (Configure::read() > 0) {
  906. echo '<div style = "font: Verdana bold 12px; color: #FF0000">' . sprintf(__('SQL Error in model %s:', true), $model->alias) . ' ';
  907. if (isset($this->error) && $this->error != null) {
  908. echo $this->error;
  909. }
  910. echo '</div>';
  911. }
  912. return null;
  913. }
  914. $count = count($resultSet);
  915. if ($type === 'hasMany' && (!isset($assocData['limit']) || empty($assocData['limit']))) {
  916. $ins = $fetch = array();
  917. for ($i = 0; $i < $count; $i++) {
  918. if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
  919. $ins[] = $in;
  920. }
  921. }
  922. if (!empty($ins)) {
  923. $fetch = array();
  924. $ins = array_chunk($ins, 1000);
  925. foreach ($ins as $i) {
  926. $q = str_replace('{$__cakeID__$}', implode(', ', $i), $query);
  927. $q = str_replace('= (', 'IN (', $q);
  928. $res = $this->fetchAll($q, $model->cacheQueries, $model->alias);
  929. $fetch = array_merge($fetch, $res);
  930. }
  931. }
  932. if (!empty($fetch) && is_array($fetch)) {
  933. if ($recursive > 0) {
  934. foreach ($linkModel->__associations as $type1) {
  935. foreach ($linkModel->{$type1} as $assoc1 => $assocData1) {
  936. $deepModel =& $linkModel->{$assoc1};
  937. $tmpStack = $stack;
  938. $tmpStack[] = $assoc1;
  939. if ($linkModel->useDbConfig === $deepModel->useDbConfig) {
  940. $db =& $this;
  941. } else {
  942. $db =& ConnectionManager::getDataSource($deepModel->useDbConfig);
  943. }
  944. $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack);
  945. }
  946. }
  947. }
  948. }
  949. return $this->__mergeHasMany($resultSet, $fetch, $association, $model, $linkModel, $recursive);
  950. } elseif ($type === 'hasAndBelongsToMany') {
  951. $ins = $fetch = array();
  952. for ($i = 0; $i < $count; $i++) {
  953. if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
  954. $ins[] = $in;
  955. }
  956. }
  957. $foreignKey = $model->hasAndBelongsToMany[$association]['foreignKey'];
  958. $joinKeys = array($foreignKey, $model->hasAndBelongsToMany[$association]['associationForeignKey']);
  959. list($with, $habtmFields) = $model->joinModel($model->hasAndBelongsToMany[$association]['with'], $joinKeys);
  960. $habtmFieldsCount = count($habtmFields);
  961. if (!empty($ins)) {
  962. $fetch = array();
  963. $ins = array_chunk($ins, 1000);
  964. foreach ($ins as $i) {
  965. $q = str_replace('{$__cakeID__$}', '(' .implode(', ', $i) .')', $query);
  966. $q = str_replace('= (', 'IN (', $q);
  967. $q = str_replace(' WHERE 1 = 1', '', $q);
  968. $q = $this->insertQueryData($q, null, $association, $assocData, $model, $linkModel, $stack);
  969. if ($q != false) {
  970. $res = $this->fetchAll($q, $model->cacheQueries, $model->alias);
  971. $fetch = array_merge($fetch, $res);
  972. }
  973. }
  974. }
  975. }
  976. for ($i = 0; $i < $count; $i++) {
  977. $row =& $resultSet[$i];
  978. if ($type !== 'hasAndBelongsToMany') {
  979. $q = $this->insertQueryData($query, $resultSet[$i], $association, $assocData, $model, $linkModel, $stack);
  980. if ($q != false) {
  981. $fetch = $this->fetchAll($q, $model->cacheQueries, $model->alias);
  982. } else {
  983. $fetch = null;
  984. }
  985. }
  986. if (!empty($fetch) && is_array($fetch)) {
  987. if ($recursive > 0) {
  988. foreach ($linkModel->__associations as $type1) {
  989. foreach ($linkModel->{$type1} as $assoc1 => $assocData1) {
  990. $deepModel =& $linkModel->{$assoc1};
  991. if (($type1 === 'belongsTo') || ($deepModel->alias === $model->alias && $type === 'belongsTo') || ($deepModel->alias != $model->alias)) {
  992. $tmpStack = $stack;
  993. $tmpStack[] = $assoc1;
  994. if ($linkModel->useDbConfig == $deepModel->useDbConfig) {
  995. $db =& $this;
  996. } else {
  997. $db =& ConnectionManager::getDataSource($deepModel->useDbConfig);
  998. }
  999. $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack);
  1000. }
  1001. }
  1002. }
  1003. }
  1004. if ($type == 'hasAndBelongsToMany') {
  1005. $merge = array();
  1006. foreach($fetch as $j => $data) {
  1007. if (isset($data[$with]) && $data[$with][$foreignKey] === $row[$model->alias][$model->primaryKey]) {
  1008. if ($habtmFieldsCount > 2) {
  1009. $merge[] = $data;
  1010. } else {
  1011. $merge[] = Set::diff($data, array($with => $data[$with]));
  1012. }
  1013. }
  1014. }
  1015. if (empty($merge) && !isset($row[$association])) {
  1016. $row[$association] = $merge;
  1017. } else {
  1018. $this->__mergeAssociation($resultSet[$i], $merge, $association, $type);
  1019. }
  1020. } else {
  1021. $this->__mergeAssociation($resultSet[$i], $fetch, $association, $type);
  1022. }
  1023. $resultSet[$i][$association] = $linkModel->afterfind($resultSet[$i][$association]);
  1024. } else {
  1025. $tempArray[0][$association] = false;
  1026. $this->__mergeAssociation($resultSet[$i], $tempArray, $association, $type);
  1027. }
  1028. }
  1029. }
  1030. }
  1031. /**
  1032. * Generate a "drop table" statement for the given Schema object
  1033. *
  1034. * @param object $schema An instance of a subclass of CakeSchema
  1035. * @param string $table Optional. If specified only the table name given will be generated.
  1036. * Otherwise, all tables defined in the schema are generated.
  1037. * @return string
  1038. */
  1039. function dropSchema($schema, $table = null) {
  1040. if (!is_a($schema, 'CakeSchema')) {
  1041. trigger_error(__('Invalid schema object', true), E_USER_WARNING);
  1042. return null;
  1043. }
  1044. $out = '';
  1045. foreach ($schema->tables as $curTable => $columns) {
  1046. if (!$table || $table == $curTable) {
  1047. $out .= 'DROP TABLE ' . $this->fullTableName($curTable) . "\n";
  1048. }
  1049. }
  1050. return $out;
  1051. }
  1052. }