397 lines
9.9 KiB
PHP
397 lines
9.9 KiB
PHP
<?php
|
|
|
|
/**
|
|
* $Id: OCI8Connection.php,v 1.18 2005/10/17 19:03:51 dlawson_mi Exp $
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* This software consists of voluntary contributions made by many individuals
|
|
* and is licensed under the LGPL. For more information please see
|
|
* <http://creole.phpdb.org>.
|
|
*/
|
|
|
|
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 <david@giffin.org>
|
|
* @author Hans Lellelid <hans@xmpl.org>
|
|
* @author Stig Bakken <ssb@fast.no>
|
|
* @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' ];
|
|
$port = $dsninfo[ 'port' ];
|
|
$db = $dsninfo[ 'database' ];
|
|
|
|
$connect_function = ( $persistent )
|
|
? 'oci_pconnect'
|
|
: 'oci_connect';
|
|
$encoding = !empty($dsninfo['encoding']) ? $dsninfo['encoding'] : null;
|
|
|
|
if ( $hostspec && $port )
|
|
{
|
|
$hostspec .= ':' . $port;
|
|
}
|
|
|
|
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:
|
|
* <pre>
|
|
* SELECT B.* FROM (
|
|
* SELECT A.*, rownum as TORQUE$ROWNUM FROM (
|
|
* query
|
|
* ) A
|
|
* ) B WHERE B.TORQUE$ROWNUM > offset AND B.TORQUE$ROWNUM
|
|
* <= offset + limit
|
|
* </pre>
|
|
*
|
|
* @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 );
|
|
}
|
|
}
|