. */ require_once 'creole/Connection.php'; require_once 'creole/common/ConnectionCommon.php'; include_once 'creole/drivers/oracle/OCI8ResultSet.php'; /** * Oracle implementation of Connection. * * @author David Giffin * @author Hans Lellelid * @author Stig Bakken * @author Lukas Smith * @version $Revision: 1.18 $ * @package creole.drivers.oracle */ class OCI8Connection extends ConnectionCommon implements Connection { protected $lastStmt = null; /** * Auto commit mode for oci_execute * @var int */ protected $execMode = OCI_COMMIT_ON_SUCCESS; /** * Connect to a database and log in as the specified user. * * @param array $dsn The data source hash. * @param int $flags Any connection flags. * @access public * @throws SQLException * @return void */ function connect( $dsninfo, $flags = 0 ) { if ( !extension_loaded( 'oci8' ) ) { throw new SQLException( 'oci8 extension not loaded' ); } $this->dsn = $dsninfo; $this->flags = $flags; $persistent = ( $flags & Creole::PERSISTENT === Creole::PERSISTENT ); $user = $dsninfo[ 'username' ]; $pw = $dsninfo[ 'password' ]; $hostspec = $dsninfo[ 'hostspec' ]; $db = $dsninfo[ 'database' ]; $connect_function = ( $persistent ) ? 'oci_pconnect' : 'oci_connect'; $encoding = !empty($dsninfo['encoding']) ? $dsninfo['encoding'] : null; @ini_set( 'track_errors', true ); if ( $db && $hostspec && $user && $pw ) { $conn = @$connect_function( $user, $pw, "//$hostspec/$db", $encoding); } elseif ( $hostspec && $user && $pw ) { $conn = @$connect_function( $user, $pw, $hostspec, $encoding ); } elseif ( $user || $pw ) { $conn = @$connect_function( $user, $pw, null, $encoding ); } else { $conn = false; } @ini_restore( 'track_errors' ); if ( $conn == false ) { $error = oci_error(); $error = ( is_array( $error ) ) ? $error[ 'message' ] : null; throw new SQLException( 'connect failed', $error ); } $this->dblink = $conn; //connected ok, need to set a few environment settings //please note, if this is changed, the function setTimestamp and setDate in OCI8PreparedStatement.php //must be changed to match $sql = "ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"; $this->executeQuery($sql); } /** * @see Connection::disconnect() */ function close() { $ret = @oci_close( $this->dblink ); $this->dblink = null; return $ret; } /** * @see Connection::executeQuery() */ function executeQuery( $sql, $fetchmode = null ) { $this->lastQuery = $sql; // $result = @oci_parse( $this->dblink, $sql ); $result = oci_parse( $this->dblink, $sql ); if ( ! $result ) { throw new SQLException( 'Unable to prepare query' , $this->nativeError() , $sql ); } $success = oci_execute( $result, $this->execMode ); if ( ! $success ) { throw new SQLException( 'Unable to execute query' , $this->nativeError( $result ) , $sql ); } return new OCI8ResultSet( $this, $result, $fetchmode ); } /** * @see Connection::simpleUpdate() */ function executeUpdate( $sql ) { $this->lastQuery = $sql; $statement = oci_parse( $this->dblink, $sql ); if ( ! $statement ) { throw new SQLException( 'Unable to prepare update' , $this->nativeError() , $sql ); } $success = oci_execute( $statement, $this->execMode ); if ( ! $success ) { throw new SQLException( 'Unable to execute update' , $this->nativeError( $statement ) , $sql ); } $this->lastStmt = $statement; return oci_num_rows( $statement ); } /** * Start a database transaction. * @throws SQLException * @return void */ protected function beginTrans() { $this->execMode = OCI_DEFAULT; } /** * Commit the current transaction. * @throws SQLException * @return void */ protected function commitTrans() { $result = oci_commit( $this->dblink ); if ( ! $result ) { throw new SQLException( 'Unable to commit transaction' , $this->nativeError() ); } $this->execMode = OCI_COMMIT_ON_SUCCESS; } /** * Roll back ( undo ) the current transaction. * @throws SQLException * @return void */ protected function rollbackTrans() { $result = oci_rollback( $this->dblink ); if ( ! $result ) { throw new SQLException( 'Unable to rollback transaction' , $this->nativeError() ); } $this->execMode = OCI_COMMIT_ON_SUCCESS; } /** * Gets the number of rows affected by the data manipulation * query. * * @return int Number of rows affected by the last query. * @todo -cOCI8Connection Figure out whether getUpdateCount() should throw exception on error or just return 0. */ function getUpdateCount() { if ( ! $this->lastStmt ) { return 0; } $result = oci_num_rows( $this->lastStmt ); if ( $result === false ) { throw new SQLException( 'Update count failed' , $this->nativeError( $this->lastStmt ) ); } return $result; } /** * Build Oracle-style query with limit or offset. * If the original SQL is in variable: query then the requlting * SQL looks like this: *
    * SELECT B.* FROM ( 
    *          SELECT A.*, rownum as TORQUE$ROWNUM FROM ( 
    *                  query
    *           ) A
    *      ) B WHERE B.TORQUE$ROWNUM > offset AND B.TORQUE$ROWNUM
    *     <= offset + limit
    * 
* * @param string &$sql the query * @param int $offset * @param int $limit * @return void ( $sql parameter is currently manipulated directly ) */ public function applyLimit( &$sql, $offset, $limit ) { $sql = 'SELECT B.* FROM ( ' . 'SELECT A.*, rownum AS CREOLE$ROWNUM FROM ( ' . $sql . ' ) A ' . ' ) B WHERE '; if ( $offset > 0 ) { $sql .= ' B.CREOLE$ROWNUM > ' . $offset; if ( $limit > 0 ) { $sql .= ' AND B.CREOLE$ROWNUM <= ' . ( $offset + $limit ); } } else { $sql .= ' B.CREOLE$ROWNUM <= ' . $limit; } } /** * Get the native Oracle Error Message as a string. * * @param string $msg The Internal Error Message * @param mixed $errno The Oracle Error resource */ public function nativeError( $result = null ) { if ( $result !== null ) { $error = oci_error( $result ); } else { $error = oci_error( $this->dblink ); } return $error[ 'code' ] . ': ' . $error[ 'message' ]; } /** * @see Connection::getDatabaseInfo() */ public function getDatabaseInfo() { require_once 'creole/drivers/oracle/metadata/OCI8DatabaseInfo.php'; return new OCI8DatabaseInfo( $this ); } /** * @see Connection::getIdGenerator() */ public function getIdGenerator() { require_once 'creole/drivers/oracle/OCI8IdGenerator.php'; return new OCI8IdGenerator( $this ); } /** * Oracle supports native prepared statements, but the oci_parse call * is actually called by the OCI8PreparedStatement class because * some additional SQL processing may be necessary ( e.g. to apply limit ). * @see OCI8PreparedStatement::executeQuery() * @see OCI8PreparedStatement::executeUpdate() * @see Connection::prepareStatement() */ public function prepareStatement( $sql ) { require_once 'creole/drivers/oracle/OCI8PreparedStatement.php'; return new OCI8PreparedStatement( $this, $sql ); } /** * @see Connection::prepareCall() */ public function prepareCall( $sql ) { throw new SQLException( 'Oracle driver does not yet support stored procedures using CallableStatement.' ); } /** * @see Connection::createStatement() */ public function createStatement() { require_once 'creole/drivers/oracle/OCI8Statement.php'; return new OCI8Statement( $this ); } }