oci8_driver.php

Go to the documentation of this file.
00001 <?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
00002 /**
00003  * CodeIgniter
00004  *
00005  * An open source application development framework for PHP 4.3.2 or newer
00006  *
00007  * @package             CodeIgniter
00008  * @author              ExpressionEngine Dev Team
00009  * @copyright   Copyright (c) 2006, EllisLab, Inc.
00010  * @license             http://codeigniter.com/user_guide/license.html
00011  * @link                http://codeigniter.com
00012  * @since               Version 1.0
00013  * @filesource
00014  */
00015 
00016 // ------------------------------------------------------------------------
00017 
00018 /**
00019  * oci8 Database Adapter Class
00020  *
00021  * Note: _DB is an extender class that the app controller
00022  * creates dynamically based on whether the active record
00023  * class is being used or not.
00024  *
00025  * @package             CodeIgniter
00026  * @subpackage  Drivers
00027  * @category    Database
00028  * @author              ExpressionEngine Dev Team
00029  * @link                http://codeigniter.com/user_guide/database/
00030  */
00031 
00032 /**
00033  * oci8 Database Adapter Class
00034  *
00035  * This is a modification of the DB_driver class to
00036  * permit access to oracle databases
00037  *
00038  * NOTE: this uses the PHP 4 oci methods
00039  *
00040  * @author        Kelly McArdle
00041  *
00042  */
00043 
00044 class CI_DB_oci8_driver extends CI_DB {
00045 
00046         /**
00047          * The syntax to count rows is slightly different across different
00048          * database engines, so this string appears in each driver and is
00049          * used for the count_all() and count_all_results() functions.
00050          */
00051         var $_count_string = "SELECT COUNT(1) AS ";
00052         var $_random_keyword = ' ASC'; // not currently supported
00053 
00054         // Set "auto commit" by default
00055         var $_commit = OCI_COMMIT_ON_SUCCESS;
00056 
00057         // need to track statement id and cursor id
00058         var $stmt_id;
00059         var $curs_id;
00060 
00061         // if we use a limit, we will add a field that will
00062         // throw off num_fields later
00063         var $limit_used;
00064 
00065         /**
00066          * Non-persistent database connection
00067          *
00068          * @access  private called by the base class
00069          * @return  resource
00070          */
00071         function db_connect()
00072         {
00073                 return @ocilogon($this->username, $this->password, $this->hostname);
00074         }
00075 
00076         // --------------------------------------------------------------------
00077 
00078         /**
00079          * Persistent database connection
00080          *
00081          * @access  private called by the base class
00082          * @return  resource
00083          */
00084         function db_pconnect()
00085         {
00086                 return @ociplogon($this->username, $this->password, $this->hostname);
00087         }
00088 
00089         // --------------------------------------------------------------------
00090 
00091         /**
00092          * Select the database
00093          *
00094          * @access  private called by the base class
00095          * @return  resource
00096          */
00097         function db_select()
00098         {
00099                 return TRUE;
00100         }
00101 
00102         // --------------------------------------------------------------------
00103 
00104         /**
00105          * Set client character set
00106          *
00107          * @access      public
00108          * @param       string
00109          * @param       string
00110          * @return      resource
00111          */
00112         function db_set_charset($charset, $collation)
00113         {
00114                 // TODO - add support if needed
00115                 return TRUE;
00116         }
00117 
00118         // --------------------------------------------------------------------
00119         
00120         /**
00121          * Version number query string
00122          *
00123          * @access  public
00124          * @return  string
00125          */
00126         function _version()
00127         {
00128                 return ociserverversion($this->conn_id);
00129         }
00130 
00131         // --------------------------------------------------------------------
00132 
00133         /**
00134          * Execute the query
00135          *
00136          * @access  private called by the base class
00137          * @param   string  an SQL query
00138          * @return  resource
00139          */
00140         function _execute($sql)
00141         {
00142                 // oracle must parse the query before it is run. All of the actions with
00143                 // the query are based on the statement id returned by ociparse
00144                 $this->_set_stmt_id($sql);
00145                 ocisetprefetch($this->stmt_id, 1000);
00146                 return @ociexecute($this->stmt_id, $this->_commit);
00147         }
00148 
00149         /**
00150          * Generate a statement ID
00151          *
00152          * @access  private
00153          * @param   string  an SQL query
00154          * @return  none
00155          */
00156         function _set_stmt_id($sql)
00157         {
00158                 if ( ! is_resource($this->stmt_id))
00159                 {
00160                         $this->stmt_id = ociparse($this->conn_id, $this->_prep_query($sql));
00161                 }
00162         }
00163 
00164         // --------------------------------------------------------------------
00165 
00166         /**
00167          * Prep the query
00168          *
00169          * If needed, each database adapter can prep the query string
00170          *
00171          * @access  private called by execute()
00172          * @param   string  an SQL query
00173          * @return  string
00174          */
00175         function _prep_query($sql)
00176         {
00177                 return $sql;
00178         }
00179 
00180         // --------------------------------------------------------------------
00181 
00182         /**
00183          * getCursor.  Returns a cursor from the datbase
00184          *
00185          * @access  public
00186          * @return  cursor id
00187          */
00188         function get_cursor()
00189         {
00190                 $this->curs_id = ocinewcursor($this->conn_id);
00191                 return $this->curs_id;
00192         }
00193 
00194         // --------------------------------------------------------------------
00195 
00196         /**
00197          * Stored Procedure.  Executes a stored procedure
00198          *
00199          * @access  public
00200          * @param   package      package stored procedure is in
00201          * @param   procedure   stored procedure to execute
00202          * @param   params        array of parameters
00203          * @return  array
00204          *
00205          * params array keys
00206          *
00207          * KEY    OPTIONAL      NOTES
00208          * name  no               the name of the parameter should be in :<param_name> format
00209          * value        no                the value of the parameter.  If this is an OUT or IN OUT parameter,
00210          *                                        this should be a reference to a variable
00211          * type  yes             the type of the parameter
00212          * length   yes          the max size of the parameter
00213          */
00214         function stored_procedure($package, $procedure, $params)
00215         {
00216                 if ($package == '' OR $procedure == '' OR ! is_array($params))
00217                 {
00218                         if ($this->db_debug)
00219                         {
00220                                 log_message('error', 'Invalid query: '.$package.'.'.$procedure);
00221                                 return $this->display_error('db_invalid_query');
00222                         }
00223                         return FALSE;
00224                 }
00225                 
00226                 // build the query string
00227                 $sql = "begin $package.$procedure(";
00228 
00229                 $have_cursor = FALSE;
00230                 foreach($params as $param)
00231                 {
00232                         $sql .= $param['name'] . ",";
00233                         
00234                         if (array_key_exists('type', $param) && ($param['type'] == OCI_B_CURSOR))
00235                         {
00236                                 $have_cursor = TRUE;
00237                         }
00238                 }
00239                 $sql = trim($sql, ",") . "); end;";
00240                                 
00241                 $this->stmt_id = FALSE;
00242                 $this->_set_stmt_id($sql);
00243                 $this->_bind_params($params);
00244                 $this->query($sql, FALSE, $have_cursor);
00245         }
00246         
00247         // --------------------------------------------------------------------
00248 
00249         /**
00250          * Bind parameters
00251          *
00252          * @access  private
00253          * @return  none
00254          */
00255         function _bind_params($params)
00256         {
00257                 if ( ! is_array($params) OR ! is_resource($this->stmt_id))
00258                 {
00259                         return;
00260                 }
00261                 
00262                 foreach ($params as $param)
00263                 {
00264                         foreach (array('name', 'value', 'type', 'length') as $val)
00265                         {
00266                                 if ( ! isset($param[$val]))
00267                                 {
00268                                         $param[$val] = '';
00269                                 }
00270                         }
00271 
00272                         ocibindbyname($this->stmt_id, $param['name'], $param['value'], $param['length'], $param['type']);
00273                 }
00274         }
00275 
00276         // --------------------------------------------------------------------
00277 
00278         /**
00279          * Begin Transaction
00280          *
00281          * @access      public
00282          * @return      bool            
00283          */     
00284         function trans_begin($test_mode = FALSE)
00285         {
00286                 if ( ! $this->trans_enabled)
00287                 {
00288                         return TRUE;
00289                 }
00290                 
00291                 // When transactions are nested we only begin/commit/rollback the outermost ones
00292                 if ($this->_trans_depth > 0)
00293                 {
00294                         return TRUE;
00295                 }
00296                 
00297                 // Reset the transaction failure flag.
00298                 // If the $test_mode flag is set to TRUE transactions will be rolled back
00299                 // even if the queries produce a successful result.
00300                 $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
00301                 
00302                 $this->_commit = OCI_DEFAULT;
00303                 return TRUE;
00304         }
00305 
00306         // --------------------------------------------------------------------
00307 
00308         /**
00309          * Commit Transaction
00310          *
00311          * @access      public
00312          * @return      bool            
00313          */     
00314         function trans_commit()
00315         {
00316                 if ( ! $this->trans_enabled)
00317                 {
00318                         return TRUE;
00319                 }
00320 
00321                 // When transactions are nested we only begin/commit/rollback the outermost ones
00322                 if ($this->_trans_depth > 0)
00323                 {
00324                         return TRUE;
00325                 }
00326 
00327                 $ret = OCIcommit($this->conn_id);
00328                 $this->_commit = OCI_COMMIT_ON_SUCCESS;
00329                 return $ret;
00330         }
00331 
00332         // --------------------------------------------------------------------
00333 
00334         /**
00335          * Rollback Transaction
00336          *
00337          * @access      public
00338          * @return      bool            
00339          */     
00340         function trans_rollback()
00341         {
00342                 if ( ! $this->trans_enabled)
00343                 {
00344                         return TRUE;
00345                 }
00346 
00347                 // When transactions are nested we only begin/commit/rollback the outermost ones
00348                 if ($this->_trans_depth > 0)
00349                 {
00350                         return TRUE;
00351                 }
00352 
00353                 $ret = OCIrollback($this->conn_id);
00354                 $this->_commit = OCI_COMMIT_ON_SUCCESS;
00355                 return $ret;
00356         }
00357 
00358         // --------------------------------------------------------------------
00359 
00360         /**
00361          * Escape String
00362          *
00363          * @access  public
00364          * @param   string
00365          * @return  string
00366          */
00367         function escape_str($str)
00368         {
00369                 return $str;
00370         }
00371 
00372         // --------------------------------------------------------------------
00373 
00374         /**
00375          * Affected Rows
00376          *
00377          * @access  public
00378          * @return  integer
00379          */
00380         function affected_rows()
00381         {
00382                 return @ocirowcount($this->stmt_id);
00383         }
00384 
00385         // --------------------------------------------------------------------
00386 
00387         /**
00388          * Insert ID
00389          *
00390          * @access  public
00391          * @return  integer
00392          */
00393         function insert_id()
00394         {
00395                 // not supported in oracle
00396                 return $this->display_error('db_unsupported_function');
00397         }
00398 
00399         // --------------------------------------------------------------------
00400 
00401         /**
00402          * "Count All" query
00403          *
00404          * Generates a platform-specific query string that counts all records in
00405          * the specified database
00406          *
00407          * @access  public
00408          * @param   string
00409          * @return  string
00410          */
00411         function count_all($table = '')
00412         {
00413                 if ($table == '')
00414                         return '0';
00415 
00416                 $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows'). " FROM " . $this->_protect_identifiers($this->dbprefix.$table));
00417 
00418                 if ($query == FALSE)
00419                         {
00420                         return 0;
00421                         }
00422 
00423                 $row = $query->row();
00424                 return $row->NUMROWS;
00425         }
00426 
00427         // --------------------------------------------------------------------
00428 
00429         /**
00430          * Show table query
00431          *
00432          * Generates a platform-specific query string so that the table names can be fetched
00433          *
00434          * @access  private
00435          * @param       boolean
00436          * @return  string
00437          */
00438         function _list_tables($prefix_limit = FALSE)
00439         {
00440                 $sql = "SELECT TABLE_NAME FROM ALL_TABLES";
00441 
00442                 if ($prefix_limit !== FALSE AND $this->dbprefix != '')
00443                 {
00444                         $sql .= " WHERE TABLE_NAME LIKE '".$this->dbprefix."%'";
00445                 }
00446                 
00447                 return $sql;
00448         }
00449 
00450         // --------------------------------------------------------------------
00451 
00452         /**
00453          * Show column query
00454          *
00455          * Generates a platform-specific query string so that the column names can be fetched
00456          *
00457          * @access  public
00458          * @param   string  the table name
00459          * @return  string
00460          */
00461         function _list_columns($table = '')
00462         {
00463                 return "SELECT COLUMN_NAME FROM all_tab_columns WHERE table_name = '$table'";
00464         }
00465 
00466         // --------------------------------------------------------------------
00467 
00468         /**
00469          * Field data query
00470          *
00471          * Generates a platform-specific query so that the column data can be retrieved
00472          *
00473          * @access  public
00474          * @param   string  the table name
00475          * @return  object
00476          */
00477         function _field_data($table)
00478         {
00479                 return "SELECT * FROM ".$this->_escape_table($table)." where rownum = 1";
00480         }
00481 
00482         // --------------------------------------------------------------------
00483 
00484         /**
00485          * The error message string
00486          *
00487          * @access  private
00488          * @return  string
00489          */
00490         function _error_message()
00491         {
00492                 $error = ocierror($this->conn_id);
00493                 return $error['message'];
00494         }
00495 
00496         // --------------------------------------------------------------------
00497 
00498         /**
00499          * The error message number
00500          *
00501          * @access  private
00502          * @return  integer
00503          */
00504         function _error_number()
00505         {
00506                 $error = ocierror($this->conn_id);
00507                 return $error['code'];
00508         }
00509 
00510         // --------------------------------------------------------------------
00511 
00512         /**
00513          * Escape Table Name
00514          *
00515          * This function adds backticks if the table name has a period
00516          * in it. Some DBs will get cranky unless periods are escaped
00517          *
00518          * @access  private
00519          * @param   string  the table name
00520          * @return  string
00521          */
00522         function _escape_table($table)
00523         {
00524                 if (strpos($table, '.') !== FALSE)
00525                 {
00526                         $table = '"' . str_replace('.', '"."', $table) . '"';
00527                 }
00528 
00529                 return $table;
00530         }
00531 
00532         // --------------------------------------------------------------------
00533 
00534         /**
00535          * Protect Identifiers
00536          *
00537          * This function adds backticks if appropriate based on db type
00538          *
00539          * @access      private
00540          * @param       mixed   the item to escape
00541          * @param       boolean only affect the first word
00542          * @return      mixed   the item with backticks
00543          */
00544         function _protect_identifiers($item, $first_word_only = FALSE)
00545         {
00546                 if (is_array($item))
00547                 {
00548                         $escaped_array = array();
00549 
00550                         foreach($item as $k=>$v)
00551                         {
00552                                 $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v, $first_word_only);
00553                         }
00554 
00555                         return $escaped_array;
00556                 }       
00557 
00558                 // This function may get "item1 item2" as a string, and so
00559                 // we may need ""item1" "item2"" and not ""item1 item2""
00560                 if (ctype_alnum($item) === FALSE)
00561                 {
00562                         if (strpos($item, '.') !== FALSE)
00563                         {
00564                                 $aliased_tables = implode(".",$this->ar_aliased_tables).'.';
00565                                 $table_name =  substr($item, 0, strpos($item, '.')+1);
00566                                 $item = (strpos($aliased_tables, $table_name) !== FALSE) ? $item = $item : $this->dbprefix.$item;
00567                         }
00568 
00569                         // This function may get "field >= 1", and need it to return ""field" >= 1"
00570                         $lbound = ($first_word_only === TRUE) ? '' : '|\s|\(';
00571 
00572                         $item = preg_replace('/(^'.$lbound.')([\w\d\-\_]+?)(\s|\)|$)/iS', '$1"$2"$3', $item);
00573                 }
00574                 else
00575                 {
00576                         return "\"{$item}\"";
00577                 }
00578 
00579                 $exceptions = array('AS', '/', '-', '%', '+', '*', 'OR', 'IS');
00580                 
00581                 foreach ($exceptions as $exception)
00582                 {
00583                 
00584                         if (stristr($item, " \"{$exception}\" ") !== FALSE)
00585                         {
00586                                 $item = preg_replace('/ "('.preg_quote($exception).')" /i', ' $1 ', $item);
00587                         }
00588                 }
00589                 return $item;
00590         }
00591                         
00592         // --------------------------------------------------------------------
00593 
00594         /**
00595          * From Tables
00596          *
00597          * This function implicitly groups FROM tables so there is no confusion
00598          * about operator precedence in harmony with SQL standards
00599          *
00600          * @access      public
00601          * @param       type
00602          * @return      type
00603          */
00604         function _from_tables($tables)
00605         {
00606                 if ( ! is_array($tables))
00607                 {
00608                         $tables = array($tables);
00609                 }
00610                 
00611                 return implode(', ', $tables);
00612         }
00613 
00614         // --------------------------------------------------------------------
00615         
00616         /**
00617          * Insert statement
00618          *
00619          * Generates a platform-specific insert string from the supplied data
00620          *
00621          * @access  public
00622          * @param   string  the table name
00623          * @param   array   the insert keys
00624          * @param   array   the insert values
00625          * @return  string
00626          */
00627         function _insert($table, $keys, $values)
00628         {
00629         return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
00630         }
00631 
00632         // --------------------------------------------------------------------
00633 
00634         /**
00635          * Update statement
00636          *
00637          * Generates a platform-specific update string from the supplied data
00638          *
00639          * @access      public
00640          * @param       string  the table name
00641          * @param       array   the update data
00642          * @param       array   the where clause
00643          * @param       array   the orderby clause
00644          * @param       array   the limit clause
00645          * @return      string
00646          */
00647         function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
00648         {
00649                 foreach($values as $key => $val)
00650                 {
00651                         $valstr[] = $key." = ".$val;
00652                 }
00653                 
00654                 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
00655                 
00656                 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
00657         
00658                 $sql = "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr);
00659                 $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
00660                 $sql .= $orderby.$limit;
00661                 
00662                 return $sql;
00663         }
00664 
00665         // --------------------------------------------------------------------
00666 
00667         /**
00668          * Truncate statement
00669          *
00670          * Generates a platform-specific truncate string from the supplied data
00671          * If the database does not support the truncate() command
00672          * This function maps to "DELETE FROM table"
00673          *
00674          * @access      public
00675          * @param       string  the table name
00676          * @return      string
00677          */     
00678         function _truncate($table)
00679         {
00680                 return "TRUNCATE TABLE ".$this->_escape_table($table);
00681         }
00682         
00683         // --------------------------------------------------------------------
00684 
00685         /**
00686          * Delete statement
00687          *
00688          * Generates a platform-specific delete string from the supplied data
00689          *
00690          * @access      public
00691          * @param       string  the table name
00692          * @param       array   the where clause
00693          * @param       string  the limit clause
00694          * @return      string
00695          */     
00696         function _delete($table, $where = array(), $like = array(), $limit = FALSE)
00697         {
00698                 $conditions = '';
00699 
00700                 if (count($where) > 0 OR count($like) > 0)
00701                 {
00702                         $conditions = "\nWHERE ";
00703                         $conditions .= implode("\n", $this->ar_where);
00704 
00705                         if (count($where) > 0 && count($like) > 0)
00706                         {
00707                                 $conditions .= " AND ";
00708                         }
00709                         $conditions .= implode("\n", $like);
00710                 }
00711 
00712                 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
00713         
00714                 return "DELETE FROM ".$table.$conditions.$limit;
00715         }
00716 
00717         // --------------------------------------------------------------------
00718 
00719         /**
00720          * Limit string
00721          *
00722          * Generates a platform-specific LIMIT clause
00723          *
00724          * @access  public
00725          * @param   string  the sql query string
00726          * @param   integer the number of rows to limit the query to
00727          * @param   integer the offset value
00728          * @return  string
00729          */
00730         function _limit($sql, $limit, $offset)
00731         {
00732                 $limit = $offset + $limit;
00733                 $newsql = "SELECT * FROM (select inner_query.*, rownum rnum FROM ($sql) inner_query WHERE rownum < $limit)";
00734 
00735                 if ($offset != 0)
00736                 {
00737                         $newsql .= " WHERE rnum >= $offset";
00738                 }
00739 
00740                 // remember that we used limits
00741                 $this->limit_used = TRUE;
00742 
00743                 return $newsql;
00744         }       
00745 
00746         // --------------------------------------------------------------------
00747 
00748         /**
00749          * Close DB Connection
00750          *
00751          * @access  public
00752          * @param   resource
00753          * @return  void
00754          */
00755         function _close($conn_id)
00756         {
00757                 @ocilogoff($conn_id);
00758         }
00759 
00760 
00761 }
00762 
00763 
00764 
00765 /* End of file oci8_driver.php */
00766 /* Location: ./system/database/drivers/oci8/oci8_driver.php */