dbo_oracle.php

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

Generated on Sun Nov 22 00:30:53 2009 for CakePHP 1.2.x.x (v1.2.4.8284) by doxygen 1.4.7