dbo_oracle.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: dbo__oracle_8php-source.html 580 2008-07-01 14:45:49Z gwoo $ */
00003 /**
00004  * Oracle layer for DBO.
00005  *
00006  * Long description for file
00007  *
00008  * PHP versions 4 and 5
00009  *
00010  * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
00011  * Copyright 2005-2008, Cake Software Foundation, Inc.
00012  *                              1785 E. Sahara Avenue, Suite 490-204
00013  *                              Las Vegas, Nevada 89104
00014  *
00015  * Licensed under The MIT License
00016  * Redistributions of files must retain the above copyright notice.
00017  *
00018  * @filesource
00019  * @copyright       Copyright 2005-2008, Cake Software Foundation, Inc.
00020  * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00021  * @package         cake
00022  * @subpackage      cake.cake.libs.model.datasources.dbo
00023  * @since           CakePHP v 1.2.0.4041
00024  * @version         $Revision: 580 $
00025  * @modifiedby      $LastChangedBy: gwoo $
00026  * @lastmodified    $Date: 2008-07-01 09:45:49 -0500 (Tue, 01 Jul 2008) $
00027  * @license         http://www.opensource.org/licenses/mit-license.php The MIT License
00028  */
00029 /**
00030  * Short description for class.
00031  *
00032  * Long description for class
00033  *
00034  * @package     cake
00035  * @subpackage  cake.cake.libs.model.datasources.dbo
00036  */
00037 class DboOracle extends DboSource {
00038 /**
00039  * Enter description here...
00040  *
00041  * @var unknown_type
00042  * @access public
00043  */
00044     var $config;
00045 /**
00046  * Enter description here...
00047  *
00048  * @var unknown_type
00049  */
00050     var $alias = '';
00051 /**
00052  * Sequence names as introspected from the database
00053  */
00054     var $_sequences = array();
00055 /**
00056  * Transaction in progress flag
00057  *
00058  * @var boolean
00059  */
00060     var $__transactionStarted = false;
00061 
00062 /**
00063  * Enter description here...
00064  *
00065  * @var unknown_type
00066  * @access public
00067  */
00068     var $columns = array(
00069         'primary_key' => array('name' => 'number NOT NULL'),
00070         'string' => array('name' => 'varchar2', 'limit' => '255'),
00071         'text' => array('name' => 'varchar2'),
00072         'integer' => array('name' => 'numeric'),
00073         'float' => array('name' => 'float'),
00074         'datetime' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'),
00075         'timestamp' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'),
00076         'time' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'),
00077         'date' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'),
00078         'binary' => array('name' => 'bytea'),
00079         'boolean' => array('name' => 'boolean'),
00080         'number' => array('name' => 'numeric'),
00081         'inet' => array('name' => 'inet'));
00082 /**
00083  * Enter description here...
00084  *
00085  * @var unknown_type
00086  * @access protected
00087  */
00088     var $connection;
00089 /**
00090  * Enter description here...
00091  *
00092  * @var unknown_type
00093  * @access protected
00094  */
00095     var $_limit = -1;
00096 /**
00097  * Enter description here...
00098  *
00099  * @var unknown_type
00100  * @access protected
00101  */
00102     var $_offset = 0;
00103 /**
00104  * Enter description here...
00105  *
00106  * @var unknown_type
00107  * @access protected
00108  */
00109     var $_map;
00110 /**
00111  * Enter description here...
00112  *
00113  * @var unknown_type
00114  * @access protected
00115  */
00116     var $_currentRow;
00117 /**
00118  * Enter description here...
00119  *
00120  * @var unknown_type
00121  * @access protected
00122  */
00123     var $_numRows;
00124 /**
00125  * Enter description here...
00126  *
00127  * @var unknown_type
00128  * @access protected
00129  */
00130     var $_results;
00131 
00132 /**
00133  * Last error issued by oci extension
00134  *
00135  * @var unknown_type
00136  */
00137     var $_error;
00138 
00139 /**
00140  * Base configuration settings for MySQL driver
00141  *
00142  * @var array
00143  */
00144     var $_baseConfig = array(
00145         'persistent' => true,
00146         'host' => 'localhost',
00147         'login' => 'system',
00148         'password' => '',
00149         'database' => 'cake',
00150         'nls_sort' => '',
00151         'nls_sort' => ''
00152     );
00153 
00154 /**
00155  * Table-sequence map
00156  *
00157  * @var unknown_type
00158  */
00159     var $_sequenceMap = array();
00160 /**
00161  * Connects to the database using options in the given configuration array.
00162  *
00163  * @return boolean True if the database could be connected, else false
00164  * @access public
00165  */
00166     function connect() {
00167         $config = $this->config;
00168         $this->connected = false;
00169         $config['charset'] = !empty($config['charset']) ? $config['charset'] : null;
00170 
00171         if ($this->config['persistent']) {
00172             $connect = 'ociplogon';
00173         } else {
00174             $connect = 'ocilogon';
00175         }
00176         $this->connection = @$connect($config['login'], $config['password'], $config['database'], $config['charset']);
00177 
00178         if ($this->connection) {
00179             $this->connected = true;
00180             if (!empty($config['nls_sort'])) {
00181                 $this->execute('ALTER SESSION SET NLS_SORT='.$config['nls_sort']);
00182             }
00183 
00184             if (!empty($config['nls_comp'])) {
00185                 $this->execute('ALTER SESSION SET NLS_COMP='.$config['nls_comp']);
00186             }
00187             $this->execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'");
00188         } else {
00189             $this->connected = false;
00190             $this->_setError();
00191             return false;
00192         }
00193         return $this->connected;
00194     }
00195 
00196     /**
00197      * Keeps track of the most recent Oracle error
00198      *
00199      */
00200     function _setError($source = null, $clear = false) {
00201         if ($source) {
00202             $e = ocierror($source);
00203         } else {
00204             $e = ocierror();
00205         }
00206         $this->_error = $e['message'];
00207         if ($clear) {
00208             $this->_error = null;
00209         }
00210     }
00211 /**
00212  * Sets the encoding language of the session
00213  *
00214  * @param string $lang language constant
00215  * @return bool
00216  */
00217     function setEncoding($lang) {
00218         if (!$this->execute('ALTER SESSION SET NLS_LANGUAGE='.$lang)) {
00219             return false;
00220         }
00221         return true;
00222     }
00223 /**
00224  * Gets the current encoding language
00225  *
00226  * @return string language constant
00227  */
00228     function getEncoding() {
00229         $sql = 'SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER=\'NLS_LANGUAGE\'';
00230         if (!$this->execute($sql)) {
00231             return false;
00232         }
00233 
00234         if (!$row = $this->fetchRow()) {
00235             return false;
00236         }
00237         return $row[0]['VALUE'];
00238     }
00239 /**
00240  * Disconnects from database.
00241  *
00242  * @return boolean True if the database could be disconnected, else false
00243  * @access public
00244  */
00245     function disconnect() {
00246         if ($this->connection) {
00247             $this->connected = !ocilogoff($this->connection);
00248             return !$this->connected;
00249         }
00250     }
00251 /**
00252  * Scrape the incoming SQL to create the association map. This is an extremely
00253  * experimental method that creates the association maps since Oracle will not tell us.
00254  *
00255  * @param string $sql
00256  * @return false if sql is nor a SELECT
00257  * @access protected
00258  */
00259     function _scrapeSQL($sql) {
00260         $sql = str_replace("\"", '', $sql);
00261         $preFrom = preg_split('/\bFROM\b/', $sql);
00262         $preFrom = $preFrom[0];
00263         $find = array('SELECT');
00264         $replace = array('');
00265         $fieldList = trim(str_replace($find, $replace, $preFrom));
00266         $fields = preg_split('/,\s+/', $fieldList);//explode(', ', $fieldList);
00267         $lastTableName  = '';
00268 
00269         foreach($fields as $key => $value) {
00270             if ($value != 'COUNT(*) AS count') {
00271                 if (preg_match('/\s+(\w+(\.\w+)*)$/', $value, $matches)) {
00272                     $fields[$key]   = $matches[1];
00273 
00274                     if (preg_match('/^(\w+\.)/', $value, $matches)) {
00275                         $fields[$key]   = $matches[1] . $fields[$key];
00276                         $lastTableName  = $matches[1];
00277                     }
00278                 }
00279                 /*
00280                 if (preg_match('/(([[:alnum:]_]+)\.[[:alnum:]_]+)(\s+AS\s+(\w+))?$/i', $value, $matches)) {
00281                     $fields[$key]   = isset($matches[4]) ? $matches[2] . '.' . $matches[4] : $matches[1];
00282                 }
00283                 */
00284             }
00285         }
00286         $this->_map = array();
00287 
00288         foreach($fields as $f) {
00289             $e = explode('.', $f);
00290             if (count($e) > 1) {
00291                 $table = $e[0];
00292                 $field = strtolower($e[1]);
00293             } else {
00294                 $table = 0;
00295                 $field = $e[0];
00296             }
00297             $this->_map[] = array($table, $field);
00298         }
00299     }
00300 /**
00301  * Modify a SQL query to limit (and offset) the result set
00302  *
00303  * @param integer $limit Maximum number of rows to return
00304  * @param integer $offset Row to begin returning
00305  * @return modified SQL Query
00306  * @access public
00307  */
00308     function limit($limit = -1, $offset = 0) {
00309         $this->_limit = (int) $limit;
00310         $this->_offset = (int) $offset;
00311     }
00312 /**
00313  * Returns number of rows in previous resultset. If no previous resultset exists,
00314  * this returns false.
00315  *
00316  * @return integer Number of rows in resultset
00317  * @access public
00318  */
00319     function lastNumRows() {
00320         return $this->_numRows;
00321     }
00322 /**
00323  * Executes given SQL statement. This is an overloaded method.
00324  *
00325  * @param string $sql SQL statement
00326  * @return resource Result resource identifier or null
00327  * @access protected
00328  */
00329     function _execute($sql) {
00330         $this->_statementId = @ociparse($this->connection, $sql);
00331         if (!$this->_statementId) {
00332             $this->_setError($this->connection);
00333             return false;
00334         }
00335 
00336         if ($this->__transactionStarted) {
00337             $mode = OCI_DEFAULT;
00338         } else {
00339             $mode = OCI_COMMIT_ON_SUCCESS;
00340         }
00341 
00342         if (!@ociexecute($this->_statementId, $mode)) {
00343             $this->_setError($this->_statementId);
00344             return false;
00345         }
00346         
00347         $this->_setError(null, true);
00348 
00349         switch(ocistatementtype($this->_statementId)) {
00350             case 'DESCRIBE':
00351             case 'SELECT':
00352                 $this->_scrapeSQL($sql);
00353             break;
00354             default:
00355                 return $this->_statementId;
00356             break;
00357         }
00358 
00359         if ($this->_limit >= 1) {
00360             ocisetprefetch($this->_statementId, $this->_limit);
00361         } else {
00362             ocisetprefetch($this->_statementId, 3000);
00363         }
00364         $this->_numRows = ocifetchstatement($this->_statementId, $this->_results, $this->_offset, $this->_limit, OCI_NUM | OCI_FETCHSTATEMENT_BY_ROW);
00365         $this->_currentRow = 0;
00366         $this->limit();
00367         return $this->_statementId;
00368     }
00369 /**
00370  * Enter description here...
00371  *
00372  * @return unknown
00373  * @access public
00374  */
00375     function fetchRow() {
00376         if ($this->_currentRow >= $this->_numRows) {
00377             ocifreestatement($this->_statementId);
00378             $this->_map = null;
00379             $this->_results = null;
00380             $this->_currentRow = null;
00381             $this->_numRows = null;
00382             return false;
00383         }
00384         $resultRow = array();
00385 
00386         foreach($this->_results[$this->_currentRow] as $index => $field) {
00387             list($table, $column) = $this->_map[$index];
00388 
00389             if (strpos($column, ' count')) {
00390                 $resultRow[0]['count'] = $field;
00391             } else {
00392                 $resultRow[$table][$column] = $this->_results[$this->_currentRow][$index];
00393             }
00394         }
00395         $this->_currentRow++;
00396         return $resultRow;
00397     }
00398 /**
00399  * Checks to see if a named sequence exists
00400  *
00401  * @param string $sequence
00402  * @return bool
00403  * @access public
00404  */
00405     function sequenceExists($sequence) {
00406         $sql = "SELECT SEQUENCE_NAME FROM USER_SEQUENCES WHERE SEQUENCE_NAME = '$sequence'";
00407         if (!$this->execute($sql)) {
00408             return false;
00409         }
00410         return $this->fetchRow();
00411     }
00412 /**
00413  * Creates a database sequence
00414  *
00415  * @param string $sequence
00416  * @return bool
00417  * @access public
00418  */
00419     function createSequence($sequence) {
00420         $sql = "CREATE SEQUENCE $sequence";
00421         return $this->execute($sql);
00422     }
00423 /**
00424  * Enter description here...
00425  *
00426  * @param unknown_type $table
00427  * @return unknown
00428  * @access public
00429  */
00430     function createTrigger($table) {
00431         $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;";
00432         return $this->execute($sql);
00433     }
00434 /**
00435  * Returns an array of tables in the database. If there are no tables, an error is
00436  * raised and the application exits.
00437  *
00438  * @return array tablenames in the database
00439  * @access public
00440  */
00441     function listSources() {
00442         $cache = parent::listSources();
00443         if ($cache != null) {
00444             return $cache;
00445         }
00446         $sql = 'SELECT view_name AS name FROM all_views UNION SELECT table_name AS name FROM all_tables';
00447 
00448         if (!$this->execute($sql)) {
00449             return false;
00450         }
00451         $sources = array();
00452 
00453         while($r = $this->fetchRow()) {
00454             $sources[] = strtolower($r[0]['name']);
00455         }
00456         parent::listSources($sources);
00457         return $sources;
00458     }
00459 /**
00460  * Returns an array of the fields in given table name.
00461  *
00462  * @param object instance of a model to inspect
00463  * @return array Fields in table. Keys are name and type
00464  * @access public
00465  */
00466     function describe(&$model) {
00467 
00468         if (!empty($model->sequence)) {
00469             $this->_sequenceMap[$model->table] = $model->sequence;
00470         } elseif (!empty($model->table)) {
00471             $this->_sequenceMap[$model->table] = $model->table . '_seq';
00472         } 
00473 
00474         $cache = parent::describe($model);
00475 
00476         if ($cache != null) {
00477             return $cache;
00478         }
00479         $sql = 'SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH FROM all_tab_columns WHERE table_name = \'';
00480         $sql .= strtoupper($this->fullTableName($model)) . '\'';
00481 
00482         if (!$this->execute($sql)) {
00483             return false;
00484         }
00485         $fields = array();
00486 
00487         for ($i = 0; $row = $this->fetchRow(); $i++) {
00488             $fields[strtolower($row[0]['COLUMN_NAME'])] = array(
00489                 'type'=> $this->column($row[0]['DATA_TYPE']),
00490                 'length'=> $row[0]['DATA_LENGTH']
00491             );
00492         }
00493         $this->__cacheDescription($this->fullTableName($model, false), $fields);
00494 
00495         return $fields;
00496     }
00497 /**
00498  * Deletes all the records in a table and drops all associated auto-increment sequences.
00499  * Using DELETE instead of TRUNCATE because it causes locking problems.
00500  *
00501  * @param mixed $table A string or model class representing the table to be truncated
00502  * @param integer $reset If -1, sequences are dropped, if 0 (default), sequences are reset,
00503  *                      and if 1, sequences are not modified
00504  * @return boolean  SQL TRUNCATE TABLE statement, false if not applicable.
00505  * @access public
00506  *
00507  */
00508     function truncate($table, $reset = 0) {
00509 
00510         if (empty($this->_sequences)) {
00511             $sql = "SELECT sequence_name FROM all_sequences";
00512             $this->execute($sql);
00513             while ($row = $this->fetchRow()) {
00514                 $this->_sequences[] = strtolower($row[0]['sequence_name']);
00515             }
00516         }
00517 
00518         $this->execute('DELETE FROM ' . $this->fullTableName($table));
00519         if (!isset($this->_sequenceMap[$table]) || !in_array($this->_sequenceMap[$table], $this->_sequences)) {
00520             return true;
00521         }
00522         if ($reset === 0) {
00523             $this->execute("SELECT {$this->_sequenceMap[$table]}.nextval FROM dual");
00524             $row = $this->fetchRow();
00525             $currval = $row[$this->_sequenceMap[$table]]['nextval'];
00526 
00527             $this->execute("SELECT min_value FROM all_sequences WHERE sequence_name = '{$this->_sequenceMap[$table]}'");
00528             $row = $this->fetchRow();
00529             $min_value = $row[0]['min_value'];
00530 
00531             if ($min_value == 1) $min_value = 0;
00532             $offset = -($currval - $min_value);
00533 
00534             $this->execute("ALTER SEQUENCE {$this->_sequenceMap[$table]} INCREMENT BY $offset MINVALUE $min_value");
00535             $this->execute("SELECT {$this->_sequenceMap[$table]}.nextval FROM dual");
00536             $this->execute("ALTER SEQUENCE {$this->_sequenceMap[$table]} INCREMENT BY 1");
00537         } else {
00538             //$this->execute("DROP SEQUENCE {$this->_sequenceMap[$table]}");
00539         }
00540         return true;
00541     }
00542 /**
00543  * Enables, disables, and lists table constraints
00544  *
00545  * Note: This method could have been written using a subselect for each table,
00546  * however the effort Oracle expends to run the constraint introspection is very high.
00547  * Therefore, this method caches the result once and loops through the arrays to find
00548  * what it needs. It reduced my query time by 50%. YMMV.
00549  *
00550  * @param string $action
00551  * @param string $table
00552  * @return mixed boolean true or array of constraints
00553  */
00554     function constraint($action, $table) {
00555         if (empty($table)) {
00556             trigger_error(__('Must specify table to operate on constraints'));
00557         }
00558 
00559         $table = strtoupper($table);
00560 
00561         if (empty($this->_keyConstraints)) {
00562             $sql = "SELECT
00563                       table_name,
00564                       c.constraint_name
00565                     FROM all_cons_columns cc
00566                     LEFT JOIN all_indexes i ON (cc.constraint_name = i.index_name)
00567                     LEFT JOIN all_constraints c ON(c.constraint_name = cc.constraint_name)";
00568             $this->execute($sql);
00569             while ($row = $this->fetchRow()) {
00570                 $this->_keyConstraints[] = array($row[0]['table_name'], $row['c']['constraint_name']);
00571             }
00572         }
00573 
00574         $relatedKeys = array();
00575         foreach ($this->_keyConstraints as $c) {
00576             if ($c[0] == $table) {
00577                 $relatedKeys[] = $c[1];
00578             }
00579         }
00580 
00581         if (empty($this->_constraints)) {
00582             $sql = "SELECT
00583                       table_name,
00584                       constraint_name,
00585                       r_constraint_name
00586                     FROM
00587                       all_constraints";
00588             $this->execute($sql);
00589             while ($row = $this->fetchRow()) {
00590                 $this->_constraints[] = $row[0];
00591             }
00592         }
00593 
00594         $constraints = array();
00595         foreach ($this->_constraints as $c) {
00596             if (in_array($c['r_constraint_name'], $relatedKeys)) {
00597                 $constraints[] = array($c['table_name'], $c['constraint_name']);
00598             }
00599         }
00600 
00601         foreach ($constraints as $c) {
00602             list($table, $constraint) = $c;
00603             switch ($action) {
00604                 case 'enable':
00605                     $this->execute("ALTER TABLE $table ENABLE CONSTRAINT $constraint");
00606                     break;
00607                 case 'disable':
00608                     $this->execute("ALTER TABLE $table DISABLE CONSTRAINT $constraint");
00609                     break;
00610                 case 'list':
00611                     return $constraints;
00612                     break;
00613                 default:
00614                     trigger_error(__('DboOracle::constraint() accepts only enable, disable, or list'));
00615             }
00616         }
00617         return true;
00618     }
00619 
00620 /**
00621  * Returns an array of the indexes in given table name.
00622  *
00623  * @param string $model Name of model to inspect
00624  * @return array Fields in table. Keys are column and unique
00625  */
00626     function index($model) {
00627         $index = array();
00628         $table = $this->fullTableName($model, false);
00629         if($table) {
00630             $indexes = $this->query('SELECT
00631               cc.table_name,
00632               cc.column_name,
00633               cc.constraint_name,
00634               c.constraint_type,
00635               i.index_name,
00636               i.uniqueness
00637             FROM all_cons_columns cc
00638             LEFT JOIN all_indexes i ON(cc.constraint_name = i.index_name)
00639             LEFT JOIN all_constraints c ON(c.constraint_name = cc.constraint_name)
00640             WHERE cc.table_name = \'' . strtoupper($table) .'\'');
00641             foreach ($indexes as $i => $idx) {
00642                 if ($idx['c']['constraint_type'] == 'P') {
00643                     $key = 'PRIMARY';
00644                 } else {
00645                     continue;
00646                 }
00647                 if(!isset($index[$key])) {
00648                     $index[$key]['column'] = strtolower($idx['cc']['column_name']);
00649                     $index[$key]['unique'] = ife($idx['i']['uniqueness'] == 'UNIQUE', 1, 0);
00650                 } else {
00651                     if(!is_array($index[$key]['column'])) {
00652                         $col[] = $index[$key]['column'];
00653                     }
00654                     $col[] = strtolower($idx['cc']['column_name']);
00655                     $index[$key]['column'] = $col;
00656                 }
00657             }
00658         }
00659         return $index;
00660     }
00661 
00662 /**
00663  * Generate a Oracle Alter Table syntax for the given Schema comparison
00664  *
00665  * @param unknown_type $schema
00666  * @return unknown
00667  */
00668     function alterSchema($compare, $table = null) {
00669         if(!is_array($compare)) {
00670             return false;
00671         }
00672         $out = '';
00673         $colList = array();
00674         foreach($compare as $curTable => $types) {
00675             if (!$table || $table == $curTable) {
00676                 $out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
00677                 foreach($types as $type => $column) {
00678                     switch($type) {
00679                         case 'add':
00680                             foreach($column as $field => $col) {
00681                                 $col['name'] = $field;
00682                                 $alter = 'ADD '.$this->buildColumn($col);
00683                                 if(isset($col['after'])) {
00684                                     $alter .= ' AFTER '. $this->name($col['after']);
00685                                 }
00686                                 $colList[] = $alter;
00687                             }
00688                         break;
00689                         case 'drop':
00690                             foreach($column as $field => $col) {
00691                                 $col['name'] = $field;
00692                                 $colList[] = 'DROP '.$this->name($field);
00693                             }
00694                         break;
00695                         case 'change':
00696                             foreach($column as $field => $col) {
00697                                 if(!isset($col['name'])) {
00698                                     $col['name'] = $field;
00699                                 }
00700                                 $colList[] = 'CHANGE '. $this->name($field).' '.$this->buildColumn($col);
00701                             }
00702                         break;
00703                     }
00704                 }
00705                 $out .= "\t" . join(",\n\t", $colList) . ";\n\n";
00706             }
00707         }
00708         return $out;
00709     }
00710 
00711 /**
00712  * This method should quote Oracle identifiers. Well it doesn't.
00713  * It would break all scaffolding and all of Cake's default assumptions.
00714  *
00715  * @param unknown_type $var
00716  * @return unknown
00717  * @access public
00718  */
00719     function name($name) {
00720         if (strpos($name, '.') !== false && strpos($name, '"') === false) {
00721             list($model, $field) = explode('.', $name);
00722             if ($field[0] == "_") {
00723                 $name = "$model.\"$field\"";
00724             }
00725         } else {
00726             if ($name[0] == "_") {
00727                 $name = "\"$name\"";
00728             }
00729         }
00730         return $name;
00731     }
00732 /**
00733  * Begin a transaction
00734  *
00735  * @param unknown_type $model
00736  * @return boolean True on success, false on fail
00737  * (i.e. if the database/model does not support transactions).
00738  */
00739     function begin() {
00740         $this->__transactionStarted = true;
00741         return true;
00742     }
00743 /**
00744  * Rollback a transaction
00745  *
00746  * @param unknown_type $model
00747  * @return boolean True on success, false on fail
00748  * (i.e. if the database/model does not support transactions,
00749  * or a transaction has not started).
00750  */
00751     function rollback() {
00752         return ocirollback($this->connection);
00753     }
00754 /**
00755  * Commit a transaction
00756  *
00757  * @param unknown_type $model
00758  * @return boolean True on success, false on fail
00759  * (i.e. if the database/model does not support transactions,
00760  * or a transaction has not started).
00761  */
00762     function commit() {
00763         $this->__transactionStarted = false;
00764         return ocicommit($this->connection);
00765     }
00766 /**
00767  * Converts database-layer column types to basic types
00768  *
00769  * @param string $real Real database-layer column type (i.e. "varchar(255)")
00770  * @return string Abstract column type (i.e. "string")
00771  * @access public
00772  */
00773     function column($real) {
00774         if (is_array($real)) {
00775             $col = $real['name'];
00776 
00777             if (isset($real['limit'])) {
00778                 $col .= '('.$real['limit'].')';
00779             }
00780             return $col;
00781         } else {
00782             $real = strtolower($real);
00783         }
00784         $col = str_replace(')', '', $real);
00785         $limit = null;
00786         if (strpos($col, '(') !== false) {
00787             list($col, $limit) = explode('(', $col);
00788         }
00789 
00790         if (in_array($col, array('date', 'timestamp'))) {
00791             return $col;
00792         }
00793         if (strpos($col, 'number') !== false) {
00794             return 'integer';
00795         }
00796         if (strpos($col, 'integer') !== false) {
00797             return 'integer';
00798         }
00799         if (strpos($col, 'char') !== false) {
00800             return 'string';
00801         }
00802         if (strpos($col, 'text') !== false) {
00803             return 'text';
00804         }
00805         if (strpos($col, 'blob') !== false) {
00806             return 'binary';
00807         }
00808         if (in_array($col, array('float', 'double', 'decimal'))) {
00809             return 'float';
00810         }
00811         if ($col == 'boolean') {
00812             return $col;
00813         }
00814         return 'text';
00815     }
00816 /**
00817  * Returns a quoted and escaped string of $data for use in an SQL statement.
00818  *
00819  * @param string $data String to be prepared for use in an SQL statement
00820  * @return string Quoted and escaped
00821  * @access public
00822  */
00823     function value($data, $column = null, $safe = false) {
00824         $parent = parent::value($data, $column, $safe);
00825 
00826         if ($parent != null) {
00827             return $parent;
00828         }
00829 
00830         if ($data === null) {
00831             return 'NULL';
00832         }
00833 
00834         if ($data === '') {
00835             return  "''";
00836         }
00837 
00838         switch($column) {
00839             case 'date':
00840                 $data = date('Y-m-d H:i:s', strtotime($data));
00841                 $data = "TO_DATE('$data', 'YYYY-MM-DD HH24:MI:SS')";
00842             break;
00843             case 'integer' :
00844             case 'float' :
00845             case null :
00846                 if (is_numeric($data)) {
00847                     break;
00848                 }
00849             default:
00850                 $data = str_replace("'", "''", $data);
00851                 $data = "'$data'";
00852             break;
00853         }
00854         return $data;
00855     }
00856 /**
00857  * Returns the ID generated from the previous INSERT operation.
00858  *
00859  * @param string
00860  * @return integer
00861  * @access public
00862  */
00863     function lastInsertId($source) {
00864         $sequence = $this->_sequenceMap[$source];
00865         $sql = "SELECT $sequence.currval FROM dual";
00866 
00867         if (!$this->execute($sql)) {
00868             return false;
00869         }
00870 
00871         while($row = $this->fetchRow()) {
00872             return $row[$sequence]['currval'];
00873         }
00874         return false;
00875     }
00876 /**
00877  * Returns a formatted error message from previous database operation.
00878  *
00879  * @return string Error message with error number
00880  * @access public
00881  */
00882     function lastError() {
00883         return $this->_error;
00884     }
00885 /**
00886  * Returns number of affected rows in previous database operation. If no previous operation exists, this returns false.
00887  *
00888  * @return int Number of affected rows
00889  * @access public
00890  */
00891     function lastAffected() {
00892         return $this->_statementId ? ocirowcount($this->_statementId): false;
00893     }
00894 /**
00895  * Renders a final SQL statement by putting together the component parts in the correct order
00896  *
00897  * @param string $type
00898  * @param array $data
00899  * @return string
00900  */
00901     function renderStatement($type, $data) {
00902         extract($data);
00903         $aliases = null;
00904 
00905         switch (strtolower($type)) {
00906             case 'select':
00907                 return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$order} {$limit}";
00908             break;
00909             case 'update':
00910                 if (!empty($alias)) {
00911                     $aliases = "{$this->alias}{$alias} ";
00912                 }
00913                 return "UPDATE {$table} {$aliases}SET {$fields} {$conditions}";
00914             break;
00915             case 'delete':
00916                 if (!empty($alias)) {
00917                     $aliases = "{$this->alias}{$alias} ";
00918                 }
00919                 return "DELETE FROM {$table} {$aliases}{$conditions}";
00920             break;
00921         }
00922     }
00923 
00924 /**
00925  * Enter description here...
00926  *
00927  * @param Model $model
00928  * @param unknown_type $linkModel
00929  * @param string $type Association type
00930  * @param unknown_type $association
00931  * @param unknown_type $assocData
00932  * @param unknown_type $queryData
00933  * @param unknown_type $external
00934  * @param unknown_type $resultSet
00935  * @param integer $recursive Number of levels of association
00936  * @param array $stack
00937  */
00938     function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) {
00939 
00940         if ($query = $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet)) {
00941 
00942             if (!isset($resultSet) || !is_array($resultSet)) {
00943                 if (Configure::read() > 0) {
00944                     e('<div style = "font: Verdana bold 12px; color: #FF0000">' . sprintf(__('SQL Error in model %s:', true), $model->alias) . ' ');
00945                     if (isset($this->error) && $this->error != null) {
00946                         e($this->error);
00947                     }
00948                     e('</div>');
00949                 }
00950                 return null;
00951             }
00952             $count = count($resultSet);
00953 
00954             if ($type === 'hasMany' && (!isset($assocData['limit']) || empty($assocData['limit']))) {
00955                 $ins = $fetch = array();
00956                 for ($i = 0; $i < $count; $i++) {
00957                     if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
00958                         $ins[] = $in;
00959                     }
00960                 }
00961 
00962                 if (!empty($ins)) {
00963                     $fetch = array();
00964                     $ins = array_chunk($ins, 1000);
00965                     foreach ($ins as $i) {
00966                         $q = str_replace('{$__cakeID__$}', join(', ', $i), $query);
00967                         $res = $this->fetchAll($q, $model->cacheQueries, $model->alias);
00968                         $fetch = array_merge($fetch, $res);
00969                     }
00970                 }
00971 
00972                 if (!empty($fetch) && is_array($fetch)) {
00973                     if ($recursive > 0) {
00974 
00975                         foreach ($linkModel->__associations as $type1) {
00976                             foreach ($linkModel->{$type1} as $assoc1 => $assocData1) {
00977                                 $deepModel =& $linkModel->{$assoc1};
00978                                 $tmpStack = $stack;
00979                                 $tmpStack[] = $assoc1;
00980 
00981                                 if ($linkModel->useDbConfig === $deepModel->useDbConfig) {
00982                                     $db =& $this;
00983                                 } else {
00984                                     $db =& ConnectionManager::getDataSource($deepModel->useDbConfig);
00985                                 }
00986                                 $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack);
00987                             }
00988                         }
00989                     }
00990                 }
00991                 return $this->__mergeHasMany($resultSet, $fetch, $association, $model, $linkModel, $recursive);
00992             } elseif ($type === 'hasAndBelongsToMany') {
00993                 $ins = $fetch = array();
00994                 for ($i = 0; $i < $count; $i++) {
00995                     if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
00996                         $ins[] = $in;
00997                     }
00998                 }
00999 
01000                 $foreignKey = $model->hasAndBelongsToMany[$association]['foreignKey'];
01001                 $joinKeys = array($foreignKey, $model->hasAndBelongsToMany[$association]['associationForeignKey']);
01002                 list($with, $habtmFields) = $model->joinModel($model->hasAndBelongsToMany[$association]['with'], $joinKeys);
01003                 $habtmFieldsCount = count($habtmFields);
01004 
01005                 if (!empty($ins)) {
01006                     $fetch = array();
01007                     $ins = array_chunk($ins, 1000);
01008                     foreach ($ins as $i) {
01009                         $q = str_replace('{$__cakeID__$}', '(' .join(', ', $i) .')', $query);
01010                         $q = str_replace('=  (', 'IN (', $q);
01011                         $q = str_replace('  WHERE 1 = 1', '', $q);
01012 
01013                         $q = $this->insertQueryData($q, null, $association, $assocData, $model, $linkModel, $stack);
01014                         if ($q != false) {
01015                             $res = $this->fetchAll($q, $model->cacheQueries, $model->alias);
01016                             $fetch = array_merge($fetch, $res);
01017                         }
01018                     }
01019                 }
01020             }
01021 
01022             for ($i = 0; $i < $count; $i++) {
01023                 $row =& $resultSet[$i];
01024 
01025                 if ($type !== 'hasAndBelongsToMany') {
01026                     $q = $this->insertQueryData($query, $resultSet[$i], $association, $assocData, $model, $linkModel, $stack);
01027                     if ($q != false) {
01028                         $fetch = $this->fetchAll($q, $model->cacheQueries, $model->alias);
01029                     } else {
01030                         $fetch = null;
01031                     }
01032                 }
01033 
01034                 if (!empty($fetch) && is_array($fetch)) {
01035                     if ($recursive > 0) {
01036 
01037                         foreach ($linkModel->__associations as $type1) {
01038                             foreach ($linkModel->{$type1} as $assoc1 => $assocData1) {
01039 
01040                                 $deepModel =& $linkModel->{$assoc1};
01041                                 if (($type1 === 'belongsTo') || ($deepModel->alias === $model->alias && $type === 'belongsTo') || ($deepModel->alias != $model->alias)) {
01042                                     $tmpStack = $stack;
01043                                     $tmpStack[] = $assoc1;
01044                                     if ($linkModel->useDbConfig == $deepModel->useDbConfig) {
01045                                         $db =& $this;
01046                                     } else {
01047                                         $db =& ConnectionManager::getDataSource($deepModel->useDbConfig);
01048                                     }
01049                                     $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack);
01050                                 }
01051                             }
01052                         }
01053                     }
01054                     if ($type == 'hasAndBelongsToMany') {
01055                         $merge = array();
01056                         foreach($fetch as $j => $data) {
01057                             if (isset($data[$with]) && $data[$with][$foreignKey] === $row[$model->alias][$model->primaryKey]) {
01058                                 if ($habtmFieldsCount > 2) {
01059                                     $merge[] = $data;
01060                                 } else {
01061                                     $merge[] = Set::diff($data, array($with => $data[$with]));
01062                                 }
01063                             }
01064                         }
01065                         if (empty($merge) && !isset($row[$association])) {
01066                             $row[$association] = $merge;
01067                         } else {
01068                             $this->__mergeAssociation($resultSet[$i], $merge, $association, $type);
01069                         }
01070                     } else {
01071                         $this->__mergeAssociation($resultSet[$i], $fetch, $association, $type);
01072                     }
01073                     $resultSet[$i][$association] = $linkModel->afterfind($resultSet[$i][$association]);
01074 
01075                 } else {
01076                     $tempArray[0][$association] = false;
01077                     $this->__mergeAssociation($resultSet[$i], $tempArray, $association, $type);
01078                 }
01079             }
01080         }
01081     }
01082 
01083 }
01084 
01085 ?>