The Easiest Way to Save and Share Code Snippets on the web

Oracle Datasource CakePHP 2.0

php | by: odin88

last edit: Jan, 9th 2012 | jump to bottom

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