2007-03-11 04:00:43 +00:00
< ? php
/**
* This is the Oracle database abstraction layer .
2007-04-20 08:55:14 +00:00
* @ addtogroup Database
2007-03-11 04:00:43 +00:00
*/
class ORABlob {
var $mData ;
function __construct ( $data ) {
$this -> mData = $data ;
}
function getData () {
return $this -> mData ;
}
2007-04-04 05:22:37 +00:00
}
2007-03-11 04:00:43 +00:00
2007-04-04 05:22:37 +00:00
/**
2007-03-11 04:00:43 +00:00
* The oci8 extension is fairly weak and doesn ' t support oci_num_rows , among
* other things . We use a wrapper class to handle that and other
* Oracle - specific bits , like converting column names back to lowercase .
2007-04-20 08:55:14 +00:00
* @ addtogroup Database
2007-03-11 04:00:43 +00:00
*/
class ORAResult {
private $rows ;
private $cursor ;
private $stmt ;
private $nrows ;
private $db ;
function __construct ( & $db , $stmt ) {
$this -> db =& $db ;
if (( $this -> nrows = oci_fetch_all ( $stmt , $this -> rows , 0 , - 1 , OCI_FETCHSTATEMENT_BY_ROW | OCI_NUM )) === false ) {
$e = oci_error ( $stmt );
2007-03-30 19:35:24 +00:00
$db -> reportQueryError ( $e [ 'message' ], $e [ 'code' ], '' , __FUNCTION__ );
2007-03-11 04:00:43 +00:00
return ;
}
$this -> cursor = 0 ;
$this -> stmt = $stmt ;
}
function free () {
oci_free_statement ( $this -> stmt );
}
function seek ( $row ) {
$this -> cursor = min ( $row , $this -> nrows );
}
function numRows () {
return $this -> nrows ;
}
function numFields () {
return oci_num_fields ( $this -> stmt );
}
function fetchObject () {
if ( $this -> cursor >= $this -> nrows )
return false ;
$row = $this -> rows [ $this -> cursor ++ ];
$ret = new stdClass ();
foreach ( $row as $k => $v ) {
$lc = strtolower ( oci_field_name ( $this -> stmt , $k + 1 ));
$ret -> $lc = $v ;
}
return $ret ;
}
function fetchAssoc () {
if ( $this -> cursor >= $this -> nrows )
return false ;
$row = $this -> rows [ $this -> cursor ++ ];
$ret = array ();
foreach ( $row as $k => $v ) {
$lc = strtolower ( oci_field_name ( $this -> stmt , $k + 1 ));
$ret [ $lc ] = $v ;
$ret [ $k ] = $v ;
}
return $ret ;
}
2007-04-20 08:55:14 +00:00
}
2007-03-11 04:00:43 +00:00
2007-04-20 08:55:14 +00:00
/**
* @ addtogroup Database
*/
2007-03-11 04:00:43 +00:00
class DatabaseOracle extends Database {
var $mInsertId = NULL ;
var $mLastResult = NULL ;
var $numeric_version = NULL ;
var $lastResult = null ;
var $cursor = 0 ;
var $mAffectedRows ;
function DatabaseOracle ( $server = false , $user = false , $password = false , $dbName = false ,
$failFunction = false , $flags = 0 )
{
global $wgOut ;
# Can't get a reference if it hasn't been set yet
if ( ! isset ( $wgOut ) ) {
$wgOut = NULL ;
}
$this -> mOut =& $wgOut ;
$this -> mFailFunction = $failFunction ;
$this -> mFlags = $flags ;
$this -> open ( $server , $user , $password , $dbName );
}
2007-03-21 18:19:35 +00:00
function cascadingDeletes () {
return true ;
}
function cleanupTriggers () {
return true ;
}
function strictIPs () {
return true ;
}
2007-03-11 04:00:43 +00:00
function realTimestamps () {
return true ;
}
function implicitGroupby () {
return false ;
}
function searchableIPs () {
return true ;
}
static function newFromParams ( $server = false , $user = false , $password = false , $dbName = false ,
$failFunction = false , $flags = 0 )
{
return new DatabaseOracle ( $server , $user , $password , $dbName , $failFunction , $flags );
}
/**
* Usually aborts on failure
* If the failFunction is set to a non - zero integer , returns success
*/
function open ( $server , $user , $password , $dbName ) {
if ( ! function_exists ( 'oci_connect' ) ) {
throw new DBConnectionError ( $this , " Oracle functions missing, have you compiled PHP with the --with-oci8 option? \n (Note: if you recently installed PHP, you may need to restart your webserver and database) \n " );
}
2007-03-11 19:19:49 +00:00
# Needed for proper UTF-8 functionality
putenv ( " NLS_LANG=AMERICAN_AMERICA.AL32UTF8 " );
2007-03-11 04:00:43 +00:00
$this -> close ();
$this -> mServer = $server ;
$this -> mUser = $user ;
$this -> mPassword = $password ;
$this -> mDBname = $dbName ;
if ( ! strlen ( $user )) { ## e.g. the class is being loaded
return ;
}
error_reporting ( E_ALL );
$this -> mConn = oci_connect ( $user , $password , $dbName );
if ( $this -> mConn == false ) {
wfDebug ( " DB connection error \n " );
wfDebug ( " Server: $server , Database: $dbName , User: $user , Password: " . substr ( $password , 0 , 3 ) . " ... \n " );
wfDebug ( $this -> lastError () . " \n " );
return false ;
}
$this -> mOpened = true ;
return $this -> mConn ;
}
/**
* Closes a database connection , if it is open
* Returns success , true if already closed
*/
function close () {
$this -> mOpened = false ;
if ( $this -> mConn ) {
return oci_close ( $this -> mConn );
} else {
return true ;
}
}
function execFlags () {
return $this -> mTrxLevel ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS ;
}
function doQuery ( $sql ) {
wfDebug ( " SQL: [ $sql ] \n " );
2007-03-30 19:35:24 +00:00
if ( ! mb_check_encoding ( $sql )) {
throw new MWException ( " SQL encoding is invalid " );
}
if (( $this -> mLastResult = $stmt = oci_parse ( $this -> mConn , $sql )) === false ) {
$e = oci_error ( $this -> mConn );
$this -> reportQueryError ( $e [ 'message' ], $e [ 'code' ], $sql , __FUNCTION__ );
}
2007-03-11 04:00:43 +00:00
if ( oci_execute ( $stmt , $this -> execFlags ()) == false ) {
$e = oci_error ( $stmt );
2007-03-30 19:35:24 +00:00
$this -> reportQueryError ( $e [ 'message' ], $e [ 'code' ], $sql , __FUNCTION__ );
2007-03-11 04:00:43 +00:00
}
if ( oci_statement_type ( $stmt ) == " SELECT " )
return new ORAResult ( $this , $stmt );
else {
$this -> mAffectedRows = oci_num_rows ( $stmt );
return true ;
}
}
function queryIgnore ( $sql , $fname = '' ) {
return $this -> query ( $sql , $fname , true );
}
function freeResult ( $res ) {
$res -> free ();
}
function fetchObject ( $res ) {
return $res -> fetchObject ();
}
function fetchRow ( $res ) {
return $res -> fetchAssoc ();
}
function numRows ( $res ) {
return $res -> numRows ();
}
function numFields ( $res ) {
return $res -> numFields ();
}
function fieldName ( $stmt , $n ) {
return pg_field_name ( $stmt , $n );
}
/**
* This must be called after nextSequenceVal
*/
function insertId () {
return $this -> mInsertId ;
}
function dataSeek ( $res , $row ) {
$res -> seek ( $row );
}
function lastError () {
if ( $this -> mConn === false )
$e = oci_error ();
else
$e = oci_error ( $this -> mConn );
return $e [ 'message' ];
}
function lastErrno () {
if ( $this -> mConn === false )
$e = oci_error ();
else
$e = oci_error ( $this -> mConn );
return $e [ 'code' ];
}
function affectedRows () {
return $this -> mAffectedRows ;
}
/**
* Returns information about an index
* If errors are explicitly ignored , returns NULL on failure
*/
function indexInfo ( $table , $index , $fname = 'Database::indexExists' ) {
return false ;
}
function indexUnique ( $table , $index , $fname = 'Database::indexUnique' ) {
return false ;
}
function insert ( $table , $a , $fname = 'Database::insert' , $options = array () ) {
if ( ! is_array ( $options ))
$options = array ( $options );
2007-03-30 19:35:24 +00:00
#if (in_array('IGNORE', $options))
# $oldIgnore = $this->ignoreErrors(true);
2007-03-11 04:00:43 +00:00
# IGNORE is performed using single-row inserts, ignoring errors in each
# FIXME: need some way to distiguish between key collision and other types of error
//$oldIgnore = $this->ignoreErrors(true);
if ( ! is_array ( reset ( $a ))) {
$a = array ( $a );
}
foreach ( $a as $row ) {
$this -> insertOneRow ( $table , $row , $fname );
}
//$this->ignoreErrors($oldIgnore);
$retVal = true ;
2007-03-30 19:35:24 +00:00
//if (in_array('IGNORE', $options))
// $this->ignoreErrors($oldIgnore);
2007-03-11 04:00:43 +00:00
return $retVal ;
}
function insertOneRow ( $table , $row , $fname ) {
// "INSERT INTO tables (a, b, c)"
$sql = " INSERT INTO " . $this -> tableName ( $table ) . " ( " . join ( ',' , array_keys ( $row )) . ')' ;
$sql .= " VALUES ( " ;
// for each value, append ":key"
$first = true ;
2007-03-11 21:44:38 +00:00
$returning = '' ;
2007-03-11 04:00:43 +00:00
foreach ( $row as $col => $val ) {
2007-03-11 21:44:38 +00:00
if ( is_object ( $val )) {
2007-03-11 04:00:43 +00:00
$what = " EMPTY_BLOB() " ;
2007-03-11 21:44:38 +00:00
assert ( $returning === '' );
$returning = " RETURNING $col INTO :bval " ;
$blobcol = $col ;
} else
2007-03-11 04:00:43 +00:00
$what = " : $col " ;
if ( $first )
$sql .= " $what " ;
else
$sql .= " , $what " ;
$first = false ;
}
2007-03-11 21:44:38 +00:00
$sql .= " ) $returning " ;
2007-03-11 04:00:43 +00:00
$stmt = oci_parse ( $this -> mConn , $sql );
foreach ( $row as $col => $val ) {
if ( ! is_object ( $val )) {
if ( oci_bind_by_name ( $stmt , " : $col " , $row [ $col ]) === false )
$this -> reportQueryError ( $this -> lastErrno (), $this -> lastError (), $sql , __METHOD__ );
}
}
2007-03-28 18:26:28 +00:00
if (( $bval = oci_new_descriptor ( $this -> mConn , OCI_D_LOB )) === false ) {
$e = oci_error ( $stmt );
throw new DBUnexpectedError ( $this , " Cannot create LOB descriptor: " . $e [ 'message' ]);
}
2007-03-11 21:44:38 +00:00
if ( strlen ( $returning ))
oci_bind_by_name ( $stmt , " :bval " , $bval , - 1 , SQLT_BLOB );
if ( oci_execute ( $stmt , OCI_DEFAULT ) === false ) {
2007-03-11 04:00:43 +00:00
$e = oci_error ( $stmt );
$this -> reportQueryError ( $e [ 'message' ], $e [ 'code' ], $sql , __METHOD__ );
}
2007-03-11 21:44:38 +00:00
if ( strlen ( $returning )) {
$bval -> save ( $row [ $blobcol ] -> getData ());
$bval -> free ();
}
if ( ! $this -> mTrxLevel )
oci_commit ( $this -> mConn );
2007-03-11 04:00:43 +00:00
oci_free_statement ( $stmt );
}
function tableName ( $name ) {
# Replace reserved words with better ones
switch ( $name ) {
case 'user' :
return 'mwuser' ;
case 'text' :
return 'pagecontent' ;
default :
return $name ;
}
}
/**
* Return the next in a sequence , save the value for retrieval via insertId ()
*/
function nextSequenceValue ( $seqName ) {
$res = $this -> query ( " SELECT $seqName .nextval FROM dual " );
$row = $this -> fetchRow ( $res );
$this -> mInsertId = $row [ 0 ];
$this -> freeResult ( $res );
return $this -> mInsertId ;
}
/**
2007-04-20 08:55:14 +00:00
* Oracle does not have a " USE INDEX " clause , so return an empty string
2007-03-11 04:00:43 +00:00
*/
function useIndexClause ( $index ) {
return '' ;
}
# REPLACE query wrapper
# Oracle simulates this with a DELETE followed by INSERT
# $row is the row to insert, an associative array
# $uniqueIndexes is an array of indexes. Each element may be either a
# field name or an array of field names
#
# It may be more efficient to leave off unique indexes which are unlikely to collide.
# However if you do this, you run the risk of encountering errors which wouldn't have
# occurred in MySQL
function replace ( $table , $uniqueIndexes , $rows , $fname = 'Database::replace' ) {
$table = $this -> tableName ( $table );
if ( count ( $rows ) == 0 ) {
return ;
}
# Single row case
if ( ! is_array ( reset ( $rows ))) {
$rows = array ( $rows );
}
foreach ( $rows as $row ) {
# Delete rows which collide
if ( $uniqueIndexes ) {
$sql = " DELETE FROM $table WHERE " ;
$first = true ;
foreach ( $uniqueIndexes as $index ) {
if ( $first ) {
$first = false ;
$sql .= " ( " ;
} else {
$sql .= ') OR (' ;
}
if ( is_array ( $index ) ) {
$first2 = true ;
foreach ( $index as $col ) {
if ( $first2 ) {
$first2 = false ;
} else {
$sql .= ' AND ' ;
}
$sql .= $col . '=' . $this -> addQuotes ( $row [ $col ] );
}
} else {
$sql .= $index . '=' . $this -> addQuotes ( $row [ $index ] );
}
}
$sql .= ')' ;
$this -> query ( $sql , $fname );
}
# Now insert the row
$sql = " INSERT INTO $table ( " . $this -> makeList ( array_keys ( $row ), LIST_NAMES ) . ') VALUES (' .
$this -> makeList ( $row , LIST_COMMA ) . ')' ;
$this -> query ( $sql , $fname );
}
}
# DELETE where the condition is a join
function deleteJoin ( $delTable , $joinTable , $delVar , $joinVar , $conds , $fname = " Database::deleteJoin " ) {
if ( ! $conds ) {
throw new DBUnexpectedError ( $this , 'Database::deleteJoin() called with empty $conds' );
}
$delTable = $this -> tableName ( $delTable );
$joinTable = $this -> tableName ( $joinTable );
$sql = " DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable " ;
if ( $conds != '*' ) {
$sql .= 'WHERE ' . $this -> makeList ( $conds , LIST_AND );
}
$sql .= ')' ;
$this -> query ( $sql , $fname );
}
# Returns the size of a text field, or -1 for "unlimited"
function textFieldSize ( $table , $field ) {
$table = $this -> tableName ( $table );
$sql = " SELECT t.typname as ftype,a.atttypmod as size
FROM pg_class c , pg_attribute a , pg_type t
WHERE relname = '$table' AND a . attrelid = c . oid AND
a . atttypid = t . oid and a . attname = '$field' " ;
$res = $this -> query ( $sql );
$row = $this -> fetchObject ( $res );
if ( $row -> ftype == " varchar " ) {
$size = $row -> size - 4 ;
} else {
$size = $row -> size ;
}
$this -> freeResult ( $res );
return $size ;
}
function lowPriorityOption () {
return '' ;
}
function limitResult ( $sql , $limit , $offset ) {
if ( $offset === false )
$offset = 0 ;
return " SELECT * FROM ( $sql ) WHERE rownum >= (1 + $offset ) AND rownum < 1 + $limit + $offset " ;
}
/**
* Returns an SQL expression for a simple conditional .
* Uses CASE on Oracle
*
* @ param string $cond SQL expression which will result in a boolean value
* @ param string $trueVal SQL expression to return if true
* @ param string $falseVal SQL expression to return if false
* @ return string SQL fragment
*/
function conditional ( $cond , $trueVal , $falseVal ) {
return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) " ;
}
function wasDeadlock () {
return $this -> lastErrno () == 'OCI-00060' ;
}
function timestamp ( $ts = 0 ) {
return wfTimestamp ( TS_ORACLE , $ts );
}
/**
* Return aggregated value function call
*/
function aggregateValue ( $valuedata , $valuename = 'value' ) {
return $valuedata ;
}
function reportQueryError ( $error , $errno , $sql , $fname , $tempIgnore = false ) {
2007-03-30 19:35:24 +00:00
# Ignore errors during error handling to avoid infinite
# recursion
2007-03-11 04:00:43 +00:00
$ignore = $this -> ignoreErrors ( true );
++ $this -> mErrorCount ;
if ( $ignore || $tempIgnore ) {
2007-03-30 19:35:24 +00:00
echo " error ignored! query = [ $sql ] \n " ;
2007-03-11 04:00:43 +00:00
wfDebug ( " SQL ERROR (ignored): $error\n " );
$this -> ignoreErrors ( $ignore );
}
else {
2007-03-30 19:35:24 +00:00
echo " error! \n " ;
2007-03-11 04:00:43 +00:00
$message = " A database error has occurred \n " .
" Query: $sql\n " .
" Function: $fname\n " .
" Error: $errno $error\n " ;
throw new DBUnexpectedError ( $this , $message );
}
}
/**
* @ return string wikitext of a link to the server software ' s web site
*/
function getSoftwareLink () {
return " [http://www.oracle.com/ Oracle] " ;
}
/**
* @ return string Version information from the database
*/
function getServerVersion () {
return oci_server_version ( $this -> mConn );
}
/**
* Query whether a given table exists ( in the given schema , or the default mw one if not given )
*/
function tableExists ( $table ) {
$etable = $this -> addQuotes ( $table );
$SQL = " SELECT 1 FROM user_tables WHERE table_name=' $etable ' " ;
$res = $this -> query ( $SQL );
$count = $res ? oci_num_rows ( $res ) : 0 ;
if ( $res )
$this -> freeResult ( $res );
return $count ;
}
/**
* Query whether a given column exists in the mediawiki schema
*/
function fieldExists ( $table , $field ) {
return true ; // XXX
}
function fieldInfo ( $table , $field ) {
return false ; // XXX
}
function begin ( $fname = '' ) {
$this -> mTrxLevel = 1 ;
}
function immediateCommit ( $fname = '' ) {
return true ;
}
function commit ( $fname = '' ) {
oci_commit ( $this -> mConn );
$this -> mTrxLevel = 0 ;
}
/* Not even sure why this is used in the main codebase... */
function limitResultForUpdate ( $sql , $num ) {
return $sql ;
}
function strencode ( $s ) {
return str_replace ( " ' " , " '' " , $s );
}
function encodeBlob ( $b ) {
return new ORABlob ( $b );
}
function decodeBlob ( $b ) {
return $b ; //return $b->load();
}
function addQuotes ( $s ) {
2007-03-30 19:35:24 +00:00
global $wgLang ;
$s = $wgLang -> checkTitleEncoding ( $s );
2007-03-11 04:00:43 +00:00
return " ' " . $this -> strencode ( $s ) . " ' " ;
}
function quote_ident ( $s ) {
return $s ;
}
/* For now, does nothing */
function selectDB ( $db ) {
return true ;
}
/**
* Returns an optional USE INDEX clause to go after the table , and a
* string to go at the end of the query
*
* @ private
*
* @ param array $options an associative array of options to be turned into
* an SQL query , valid keys are listed in the function .
* @ return array
*/
function makeSelectOptions ( $options ) {
2007-03-11 15:49:27 +00:00
$preLimitTail = $postLimitTail = '' ;
2007-03-11 04:00:43 +00:00
$startOpts = '' ;
$noKeyOptions = array ();
foreach ( $options as $key => $option ) {
if ( is_numeric ( $key ) ) {
$noKeyOptions [ $option ] = true ;
}
}
2007-03-11 15:49:27 +00:00
if ( isset ( $options [ 'GROUP BY' ] ) ) $preLimitTail .= " GROUP BY { $options [ 'GROUP BY' ] } " ;
if ( isset ( $options [ 'ORDER BY' ] ) ) $preLimitTail .= " ORDER BY { $options [ 'ORDER BY' ] } " ;
2007-03-11 04:00:43 +00:00
if ( isset ( $options [ 'LIMIT' ])) {
// $tailOpts .= $this->limitResult('', $options['LIMIT'],
// isset($options['OFFSET']) ? $options['OFFSET']
// : false);
}
2007-03-11 15:49:27 +00:00
#if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $tailOpts .= ' FOR UPDATE';
#if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $tailOpts .= ' LOCK IN SHARE MODE';
2007-03-11 04:00:43 +00:00
if ( isset ( $noKeyOptions [ 'DISTINCT' ] ) && isset ( $noKeyOptions [ 'DISTINCTROW' ] ) ) $startOpts .= 'DISTINCT' ;
if ( isset ( $options [ 'USE INDEX' ] ) && ! is_array ( $options [ 'USE INDEX' ] ) ) {
$useIndex = $this -> useIndexClause ( $options [ 'USE INDEX' ] );
} else {
$useIndex = '' ;
}
2007-03-11 15:49:27 +00:00
return array ( $startOpts , $useIndex , $preLimitTail , $postLimitTail );
2007-03-11 04:00:43 +00:00
}
public function setTimeout ( $timeout ) {
2007-04-18 16:37:39 +00:00
// @todo fixme no-op
2007-03-11 04:00:43 +00:00
}
function ping () {
2007-06-04 21:43:02 +00:00
wfDebug ( " Function ping() not written for DatabaseOracle.php yet " );
2007-03-11 04:00:43 +00:00
return true ;
}
2007-06-04 21:43:02 +00:00
/**
* How lagged is this slave ?
*
* @ return int
*/
public function getLag () {
# Not implemented for Oracle
return 0 ;
}
2007-03-11 04:00:43 +00:00
} // end DatabaseOracle class
?>