MediaWiki  REL1_19
DatabaseOracle.php
Go to the documentation of this file.
00001 <?php
00015 class ORAResult {
00016         private $rows;
00017         private $cursor;
00018         private $nrows;
00019 
00020         private $columns = array();
00021 
00022         private function array_unique_md( $array_in ) {
00023                 $array_out = array();
00024                 $array_hashes = array();
00025 
00026                 foreach ( $array_in as $item ) {
00027                         $hash = md5( serialize( $item ) );
00028                         if ( !isset( $array_hashes[$hash] ) ) {
00029                                 $array_hashes[$hash] = $hash;
00030                                 $array_out[] = $item;
00031                         }
00032                 }
00033 
00034                 return $array_out;
00035         }
00036 
00042         function __construct( &$db, $stmt, $unique = false ) {
00043                 $this->db =& $db;
00044 
00045                 if ( ( $this->nrows = oci_fetch_all( $stmt, $this->rows, 0, - 1, OCI_FETCHSTATEMENT_BY_ROW | OCI_NUM ) ) === false ) {
00046                         $e = oci_error( $stmt );
00047                         $db->reportQueryError( $e['message'], $e['code'], '', __METHOD__ );
00048                         $this->free();
00049                         return;
00050                 }
00051 
00052                 if ( $unique ) {
00053                         $this->rows = $this->array_unique_md( $this->rows );
00054                         $this->nrows = count( $this->rows );
00055                 }
00056 
00057                 if ($this->nrows > 0) {
00058                         foreach ( $this->rows[0] as $k => $v ) {
00059                                 $this->columns[$k] = strtolower( oci_field_name( $stmt, $k + 1 ) );
00060                         }
00061                 }
00062 
00063                 $this->cursor = 0;
00064                 oci_free_statement( $stmt );
00065         }
00066 
00067         public function free() {
00068                 unset($this->db);
00069         }
00070 
00071         public function seek( $row ) {
00072                 $this->cursor = min( $row, $this->nrows );
00073         }
00074 
00075         public function numRows() {
00076                 return $this->nrows;
00077         }
00078 
00079         public function numFields() {
00080                 return count($this->columns);
00081         }
00082 
00083         public function fetchObject() {
00084                 if ( $this->cursor >= $this->nrows ) {
00085                         return false;
00086                 }
00087                 $row = $this->rows[$this->cursor++];
00088                 $ret = new stdClass();
00089                 foreach ( $row as $k => $v ) {
00090                         $lc = $this->columns[$k];
00091                         $ret->$lc = $v;
00092                 }
00093 
00094                 return $ret;
00095         }
00096 
00097         public function fetchRow() {
00098                 if ( $this->cursor >= $this->nrows ) {
00099                         return false;
00100                 }
00101 
00102                 $row = $this->rows[$this->cursor++];
00103                 $ret = array();
00104                 foreach ( $row as $k => $v ) {
00105                         $lc = $this->columns[$k];
00106                         $ret[$lc] = $v;
00107                         $ret[$k] = $v;
00108                 }
00109                 return $ret;
00110         }
00111 }
00112 
00117 class ORAField implements Field {
00118         private $name, $tablename, $default, $max_length, $nullable,
00119                 $is_pk, $is_unique, $is_multiple, $is_key, $type;
00120 
00121         function __construct( $info ) {
00122                 $this->name = $info['column_name'];
00123                 $this->tablename = $info['table_name'];
00124                 $this->default = $info['data_default'];
00125                 $this->max_length = $info['data_length'];
00126                 $this->nullable = $info['not_null'];
00127                 $this->is_pk = isset( $info['prim'] ) && $info['prim'] == 1 ? 1 : 0;
00128                 $this->is_unique = isset( $info['uniq'] ) && $info['uniq'] == 1 ? 1 : 0;
00129                 $this->is_multiple = isset( $info['nonuniq'] ) && $info['nonuniq'] == 1 ? 1 : 0;
00130                 $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
00131                 $this->type = $info['data_type'];
00132         }
00133 
00134         function name() {
00135                 return $this->name;
00136         }
00137 
00138         function tableName() {
00139                 return $this->tablename;
00140         }
00141 
00142         function defaultValue() {
00143                 return $this->default;
00144         }
00145 
00146         function maxLength() {
00147                 return $this->max_length;
00148         }
00149 
00150         function isNullable() {
00151                 return $this->nullable;
00152         }
00153 
00154         function isKey() {
00155                 return $this->is_key;
00156         }
00157 
00158         function isMultipleKey() {
00159                 return $this->is_multiple;
00160         }
00161 
00162         function type() {
00163                 return $this->type;
00164         }
00165 }
00166 
00170 class DatabaseOracle extends DatabaseBase {
00171         var $mInsertId = null;
00172         var $mLastResult = null;
00173         var $lastResult = null;
00174         var $cursor = 0;
00175         var $mAffectedRows;
00176 
00177         var $ignore_DUP_VAL_ON_INDEX = false;
00178         var $sequenceData = null;
00179 
00180         var $defaultCharset = 'AL32UTF8';
00181 
00182         var $mFieldInfoCache = array();
00183 
00184         function __construct( $server = false, $user = false, $password = false, $dbName = false,
00185                 $flags = 0, $tablePrefix = 'get from global' )
00186         {
00187                 global $wgDBprefix;
00188                 $tablePrefix = $tablePrefix == 'get from global' ? strtoupper( $wgDBprefix ) : strtoupper( $tablePrefix );
00189                 parent::__construct( $server, $user, $password, $dbName, $flags, $tablePrefix );
00190                 wfRunHooks( 'DatabaseOraclePostInit', array( $this ) );
00191         }
00192 
00193         function __destruct() {
00194                 if ($this->mOpened) {
00195                         wfSuppressWarnings();
00196                         $this->close();
00197                         wfRestoreWarnings();
00198                 }
00199         }
00200 
00201         function getType() {
00202                 return 'oracle';
00203         }
00204 
00205         function cascadingDeletes() {
00206                 return true;
00207         }
00208         function cleanupTriggers() {
00209                 return true;
00210         }
00211         function strictIPs() {
00212                 return true;
00213         }
00214         function realTimestamps() {
00215                 return true;
00216         }
00217         function implicitGroupby() {
00218                 return false;
00219         }
00220         function implicitOrderby() {
00221                 return false;
00222         }
00223         function searchableIPs() {
00224                 return true;
00225         }
00226 
00230         function open( $server, $user, $password, $dbName ) {
00231                 if ( !function_exists( 'oci_connect' ) ) {
00232                         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" );
00233                 }
00234 
00235                 $this->close();
00236                 $this->mUser = $user;
00237                 $this->mPassword = $password;
00238                 // changed internal variables functions
00239                 // mServer now holds the TNS endpoint
00240                 // mDBname is schema name if different from username
00241                 if ( !$server ) {
00242                         // backward compatibillity (server used to be null and TNS was supplied in dbname)
00243                         $this->mServer = $dbName;
00244                         $this->mDBname = $user;
00245                 } else {
00246                         $this->mServer = $server;
00247                         if ( !$dbName ) {
00248                                 $this->mDBname = $user;
00249                         } else {
00250                                 $this->mDBname = $dbName;
00251                         }
00252                 }
00253 
00254                 if ( !strlen( $user ) ) { # e.g. the class is being loaded
00255                         return;
00256                 }
00257 
00258                 $session_mode = $this->mFlags & DBO_SYSDBA ? OCI_SYSDBA : OCI_DEFAULT;
00259                 wfSuppressWarnings();
00260                 if ( $this->mFlags & DBO_DEFAULT ) {
00261                         $this->mConn = oci_new_connect( $this->mUser, $this->mPassword, $this->mServer, $this->defaultCharset, $session_mode );
00262                 } else {
00263                         $this->mConn = oci_connect( $this->mUser, $this->mPassword, $this->mServer, $this->defaultCharset, $session_mode );
00264                 }
00265                 wfRestoreWarnings();
00266 
00267                 if ( $this->mUser != $this->mDBname ) {
00268                         //change current schema in session
00269                         $this->selectDB( $this->mDBname );
00270                 }
00271 
00272                 if ( !$this->mConn ) {
00273                         throw new DBConnectionError( $this, $this->lastError() );
00274                 }
00275 
00276                 $this->mOpened = true;
00277 
00278                 # removed putenv calls because they interfere with the system globaly
00279                 $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
00280                 $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
00281                 $this->doQuery( 'ALTER SESSION SET NLS_NUMERIC_CHARACTERS=\'.,\'' );
00282                 return $this->mConn;
00283         }
00284 
00289         function close() {
00290                 $this->mOpened = false;
00291                 if ( $this->mConn ) {
00292                         if ( $this->mTrxLevel ) {
00293                                 $this->commit();
00294                         }
00295                         return oci_close( $this->mConn );
00296                 } else {
00297                         return true;
00298                 }
00299         }
00300 
00301         function execFlags() {
00302                 return $this->mTrxLevel ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS;
00303         }
00304 
00305         protected function doQuery( $sql ) {
00306                 wfDebug( "SQL: [$sql]\n" );
00307                 if ( !mb_check_encoding( $sql ) ) {
00308                         throw new MWException( "SQL encoding is invalid\n$sql" );
00309                 }
00310 
00311                 // handle some oracle specifics
00312                 // remove AS column/table/subquery namings
00313                 if( !$this->getFlag( DBO_DDLMODE ) ) {
00314                         $sql = preg_replace( '/ as /i', ' ', $sql );
00315                 }
00316 
00317                 // Oracle has issues with UNION clause if the statement includes LOB fields
00318                 // So we do a UNION ALL and then filter the results array with array_unique
00319                 $union_unique = ( preg_match( '/\/\* UNION_UNIQUE \*\/ /', $sql ) != 0 );
00320                 // EXPLAIN syntax in Oracle is EXPLAIN PLAN FOR and it return nothing
00321                 // you have to select data from plan table after explain
00322                 $explain_id = date( 'dmYHis' );
00323 
00324                 $sql = preg_replace( '/^EXPLAIN /', 'EXPLAIN PLAN SET STATEMENT_ID = \'' . $explain_id . '\' FOR', $sql, 1, $explain_count );
00325 
00326                 wfSuppressWarnings();
00327 
00328                 if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) {
00329                         $e = oci_error( $this->mConn );
00330                         $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
00331                         return false;
00332                 }
00333 
00334                 if ( !oci_execute( $stmt, $this->execFlags() ) ) {
00335                         $e = oci_error( $stmt );
00336                         if ( !$this->ignore_DUP_VAL_ON_INDEX || $e['code'] != '1' ) {
00337                                 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
00338                                 return false;
00339                         }
00340                 }
00341 
00342                 wfRestoreWarnings();
00343 
00344                 if ( $explain_count > 0 ) {
00345                         return $this->doQuery( 'SELECT id, cardinality "ROWS" FROM plan_table WHERE statement_id = \'' . $explain_id . '\'' );
00346                 } elseif ( oci_statement_type( $stmt ) == 'SELECT' ) {
00347                         return new ORAResult( $this, $stmt, $union_unique );
00348                 } else {
00349                         $this->mAffectedRows = oci_num_rows( $stmt );
00350                         return true;
00351                 }
00352         }
00353 
00354         function queryIgnore( $sql, $fname = '' ) {
00355                 return $this->query( $sql, $fname, true );
00356         }
00357 
00358         function freeResult( $res ) {
00359                 if ( $res instanceof ResultWrapper ) {
00360                         $res = $res->result;
00361                 }
00362 
00363                 $res->free();
00364         }
00365 
00366         function fetchObject( $res ) {
00367                 if ( $res instanceof ResultWrapper ) {
00368                         $res = $res->result;
00369                 }
00370 
00371                 return $res->fetchObject();
00372         }
00373 
00374         function fetchRow( $res ) {
00375                 if ( $res instanceof ResultWrapper ) {
00376                         $res = $res->result;
00377                 }
00378 
00379                 return $res->fetchRow();
00380         }
00381 
00382         function numRows( $res ) {
00383                 if ( $res instanceof ResultWrapper ) {
00384                         $res = $res->result;
00385                 }
00386 
00387                 return $res->numRows();
00388         }
00389 
00390         function numFields( $res ) {
00391                 if ( $res instanceof ResultWrapper ) {
00392                         $res = $res->result;
00393                 }
00394 
00395                 return $res->numFields();
00396         }
00397 
00398         function fieldName( $stmt, $n ) {
00399                 return oci_field_name( $stmt, $n );
00400         }
00401 
00405         function insertId() {
00406                 return $this->mInsertId;
00407         }
00408 
00409         function dataSeek( $res, $row ) {
00410                 if ( $res instanceof ORAResult ) {
00411                         $res->seek( $row );
00412                 } else {
00413                         $res->result->seek( $row );
00414                 }
00415         }
00416 
00417         function lastError() {
00418                 if ( $this->mConn === false ) {
00419                         $e = oci_error();
00420                 } else {
00421                         $e = oci_error( $this->mConn );
00422                 }
00423                 return $e['message'];
00424         }
00425 
00426         function lastErrno() {
00427                 if ( $this->mConn === false ) {
00428                         $e = oci_error();
00429                 } else {
00430                         $e = oci_error( $this->mConn );
00431                 }
00432                 return $e['code'];
00433         }
00434 
00435         function affectedRows() {
00436                 return $this->mAffectedRows;
00437         }
00438 
00443         function indexInfo( $table, $index, $fname = 'DatabaseOracle::indexExists' ) {
00444                 return false;
00445         }
00446 
00447         function indexUnique( $table, $index, $fname = 'DatabaseOracle::indexUnique' ) {
00448                 return false;
00449         }
00450 
00451         function insert( $table, $a, $fname = 'DatabaseOracle::insert', $options = array() ) {
00452                 if ( !count( $a ) ) {
00453                         return true;
00454                 }
00455 
00456                 if ( !is_array( $options ) ) {
00457                         $options = array( $options );
00458                 }
00459 
00460                 if ( in_array( 'IGNORE', $options ) ) {
00461                         $this->ignore_DUP_VAL_ON_INDEX = true;
00462                 }
00463 
00464                 if ( !is_array( reset( $a ) ) ) {
00465                         $a = array( $a );
00466                 }
00467 
00468                 foreach ( $a as &$row ) {
00469                         $this->insertOneRow( $table, $row, $fname );
00470                 }
00471                 $retVal = true;
00472 
00473                 if ( in_array( 'IGNORE', $options ) ) {
00474                         $this->ignore_DUP_VAL_ON_INDEX = false;
00475                 }
00476 
00477                 return $retVal;
00478         }
00479 
00480         private function fieldBindStatement ( $table, $col, &$val, $includeCol = false ) {
00481                 $col_info = $this->fieldInfoMulti( $table, $col );
00482                 $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
00483 
00484                 $bind = '';
00485                 if ( is_numeric( $col ) ) {
00486                         $bind = $val;
00487                         $val = null;
00488                         return $bind;
00489                 } elseif ( $includeCol ) {
00490                         $bind = "$col = ";
00491                 }
00492 
00493                 if ( $val == '' && $val !== 0 && $col_type != 'BLOB' && $col_type != 'CLOB' ) {
00494                         $val = null;
00495                 }
00496 
00497                 if ( $val === 'NULL' ) {
00498                         $val = null;
00499                 }
00500 
00501                 if ( $val === null ) {
00502                         if ( $col_info != false && $col_info->isNullable() == 0 && $col_info->defaultValue() != null ) {
00503                                 $bind .= 'DEFAULT';
00504                         } else {
00505                                 $bind .= 'NULL';
00506                         }
00507                 } else {
00508                         $bind .= ':' . $col;
00509                 }
00510 
00511                 return $bind;
00512         }
00513 
00514         private function insertOneRow( $table, $row, $fname ) {
00515                 global $wgContLang;
00516 
00517                 $table = $this->tableName( $table );
00518                 // "INSERT INTO tables (a, b, c)"
00519                 $sql = "INSERT INTO " . $table . " (" . join( ',', array_keys( $row ) ) . ')';
00520                 $sql .= " VALUES (";
00521 
00522                 // for each value, append ":key"
00523                 $first = true;
00524                 foreach ( $row as $col => &$val ) {
00525                         if ( !$first ) {
00526                                 $sql .= ', ';
00527                         } else {
00528                                 $first = false;
00529                         }
00530 
00531                         $sql .= $this->fieldBindStatement( $table, $col, $val );
00532                 }
00533                 $sql .= ')';
00534 
00535                 if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) {
00536                         $e = oci_error( $this->mConn );
00537                         $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
00538                         return false;
00539                 }
00540                 foreach ( $row as $col => &$val ) {
00541                         $col_info = $this->fieldInfoMulti( $table, $col );
00542                         $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
00543 
00544                         if ( $val === null ) {
00545                                 // do nothing ... null was inserted in statement creation
00546                         } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) {
00547                                 if ( is_object( $val ) ) {
00548                                         $val = $val->fetch();
00549                                 }
00550 
00551                                 // backward compatibility
00552                                 if ( preg_match( '/^timestamp.*/i', $col_type ) == 1 && strtolower( $val ) == 'infinity' ) {
00553                                         $val = $this->getInfinity();
00554                                 }
00555 
00556                                 $val = ( $wgContLang != null ) ? $wgContLang->checkTitleEncoding( $val ) : $val;
00557                                 if ( oci_bind_by_name( $stmt, ":$col", $val, -1, SQLT_CHR ) === false ) {
00558                                         $e = oci_error( $stmt );
00559                                         $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
00560                                         return false;
00561                                 }
00562                         } else {
00563                                 if ( ( $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB ) ) === false ) {
00564                                         $e = oci_error( $stmt );
00565                                         throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] );
00566                                 }
00567 
00568                                 if ( is_object( $val ) ) {
00569                                         $val = $val->fetch();
00570                                 }
00571 
00572                                 if ( $col_type == 'BLOB' ) {
00573                                         $lob[$col]->writeTemporary( $val, OCI_TEMP_BLOB );
00574                                         oci_bind_by_name( $stmt, ":$col", $lob[$col], - 1, OCI_B_BLOB );
00575                                 } else {
00576                                         $lob[$col]->writeTemporary( $val, OCI_TEMP_CLOB );
00577                                         oci_bind_by_name( $stmt, ":$col", $lob[$col], - 1, OCI_B_CLOB );
00578                                 }
00579                         }
00580                 }
00581 
00582                 wfSuppressWarnings();
00583 
00584                 if ( oci_execute( $stmt, $this->execFlags() ) === false ) {
00585                         $e = oci_error( $stmt );
00586                         if ( !$this->ignore_DUP_VAL_ON_INDEX || $e['code'] != '1' ) {
00587                                 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
00588                                 return false;
00589                         } else {
00590                                 $this->mAffectedRows = oci_num_rows( $stmt );
00591                         }
00592                 } else {
00593                         $this->mAffectedRows = oci_num_rows( $stmt );
00594                 }
00595 
00596                 wfRestoreWarnings();
00597 
00598                 if ( isset( $lob ) ) {
00599                         foreach ( $lob as $lob_v ) {
00600                                 $lob_v->free();
00601                         }
00602                 }
00603 
00604                 if ( !$this->mTrxLevel ) {
00605                         oci_commit( $this->mConn );
00606                 }
00607 
00608                 oci_free_statement( $stmt );
00609         }
00610 
00611         function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = 'DatabaseOracle::insertSelect',
00612                 $insertOptions = array(), $selectOptions = array() )
00613         {
00614                 $destTable = $this->tableName( $destTable );
00615                 if ( !is_array( $selectOptions ) ) {
00616                         $selectOptions = array( $selectOptions );
00617                 }
00618                 list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
00619                 if ( is_array( $srcTable ) ) {
00620                         $srcTable =  implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) );
00621                 } else {
00622                         $srcTable = $this->tableName( $srcTable );
00623                 }
00624 
00625                 if ( ( $sequenceData = $this->getSequenceData( $destTable ) ) !== false &&
00626                                 !isset( $varMap[$sequenceData['column']] ) )
00627                 {
00628                         $varMap[$sequenceData['column']] = 'GET_SEQUENCE_VALUE(\'' . $sequenceData['sequence'] . '\')';
00629                 }
00630 
00631                 // count-alias subselect fields to avoid abigious definition errors
00632                 $i = 0;
00633                 foreach ( $varMap as &$val ) {
00634                         $val = $val . ' field' . ( $i++ );
00635                 }
00636 
00637                 $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
00638                         " SELECT $startOpts " . implode( ',', $varMap ) .
00639                         " FROM $srcTable $useIndex ";
00640                 if ( $conds != '*' ) {
00641                         $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
00642                 }
00643                 $sql .= " $tailOpts";
00644 
00645                 if ( in_array( 'IGNORE', $insertOptions ) ) {
00646                         $this->ignore_DUP_VAL_ON_INDEX = true;
00647                 }
00648 
00649                 $retval = $this->query( $sql, $fname );
00650 
00651                 if ( in_array( 'IGNORE', $insertOptions ) ) {
00652                         $this->ignore_DUP_VAL_ON_INDEX = false;
00653                 }
00654 
00655                 return $retval;
00656         }
00657 
00658         function tableName( $name, $format = 'quoted' ) {
00659                 /*
00660                 Replace reserved words with better ones
00661                 Using uppercase because that's the only way Oracle can handle
00662                 quoted tablenames
00663                 */
00664                 switch( $name ) {
00665                         case 'user':
00666                                 $name = 'MWUSER';
00667                                 break;
00668                         case 'text':
00669                                 $name = 'PAGECONTENT';
00670                                 break;
00671                 }
00672 
00673                 return parent::tableName( strtoupper( $name ), $format );
00674         }
00675 
00676         function tableNameInternal( $name ) {
00677                 $name = $this->tableName( $name );
00678                 return preg_replace( '/.*\.(.*)/', '$1', $name);
00679         }
00683         function nextSequenceValue( $seqName ) {
00684                 $res = $this->query( "SELECT $seqName.nextval FROM dual" );
00685                 $row = $this->fetchRow( $res );
00686                 $this->mInsertId = $row[0];
00687                 return $this->mInsertId;
00688         }
00689 
00693         private function getSequenceData( $table ) {
00694                 if ( $this->sequenceData == null ) {
00695                         $result = $this->doQuery( "SELECT lower(asq.sequence_name),
00696                                    lower(atc.table_name),
00697                                    lower(atc.column_name)
00698                           FROM all_sequences asq, all_tab_columns atc
00699                          WHERE decode(atc.table_name, '{$this->mTablePrefix}MWUSER', '{$this->mTablePrefix}USER', atc.table_name) || '_' ||
00700                                    atc.column_name || '_SEQ' = '{$this->mTablePrefix}' || asq.sequence_name
00701                            AND asq.sequence_owner = upper('{$this->mDBname}')
00702                            AND atc.owner = upper('{$this->mDBname}')" );
00703 
00704                         while ( ( $row = $result->fetchRow() ) !== false ) {
00705                                 $this->sequenceData[$row[1]] = array(
00706                                         'sequence' => $row[0],
00707                                         'column' => $row[2]
00708                                 );
00709                         }
00710                 }
00711                 $table = strtolower( $this->removeIdentifierQuotes( $this->tableName( $table ) ) );
00712                 return ( isset( $this->sequenceData[$table] ) ) ? $this->sequenceData[$table] : false;
00713         }
00714 
00715         # Returns the size of a text field, or -1 for "unlimited"
00716         function textFieldSize( $table, $field ) {
00717                 $fieldInfoData = $this->fieldInfo( $table, $field );
00718                 return $fieldInfoData->maxLength();
00719         }
00720 
00721         function limitResult( $sql, $limit, $offset = false ) {
00722                 if ( $offset === false ) {
00723                         $offset = 0;
00724                 }
00725                 return "SELECT * FROM ($sql) WHERE rownum >= (1 + $offset) AND rownum < (1 + $limit + $offset)";
00726         }
00727 
00728         function encodeBlob( $b ) {
00729                 return new Blob( $b );
00730         }
00731 
00732         function decodeBlob( $b ) {
00733                 if ( $b instanceof Blob ) {
00734                         $b = $b->fetch();
00735                 }
00736                 return $b;
00737         }
00738 
00739         function unionQueries( $sqls, $all ) {
00740                 $glue = ' UNION ALL ';
00741                 return 'SELECT * ' . ( $all ? '':'/* UNION_UNIQUE */ ' ) . 'FROM (' . implode( $glue, $sqls ) . ')' ;
00742         }
00743 
00744         function wasDeadlock() {
00745                 return $this->lastErrno() == 'OCI-00060';
00746         }
00747 
00748         function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabaseOracle::duplicateTableStructure' ) {
00749                 $temporary = $temporary ? 'TRUE' : 'FALSE';
00750 
00751                 $newName = strtoupper( $newName );
00752                 $oldName = strtoupper( $oldName );
00753 
00754                 $tabName = substr( $newName, strlen( $this->mTablePrefix ) );
00755                 $oldPrefix = substr( $oldName, 0, strlen( $oldName ) - strlen( $tabName ) );
00756                 $newPrefix = strtoupper( $this->mTablePrefix );
00757 
00758                 return $this->doQuery( "BEGIN DUPLICATE_TABLE( '$tabName', '$oldPrefix', '$newPrefix', $temporary ); END;" );
00759         }
00760 
00761         function listTables( $prefix = null, $fname = 'DatabaseOracle::listTables' ) {
00762                 $listWhere = '';
00763                 if (!empty($prefix)) {
00764                         $listWhere = ' AND table_name LIKE \''.strtoupper($prefix).'%\'';
00765                 }
00766 
00767                 $owner = strtoupper( $this->mDBname );
00768                 $result = $this->doQuery( "SELECT table_name FROM all_tables WHERE owner='$owner' AND table_name NOT LIKE '%!_IDX\$_' ESCAPE '!' $listWhere" );
00769 
00770                 // dirty code ... i know
00771                 $endArray = array();
00772                 $endArray[] = strtoupper($prefix.'MWUSER');
00773                 $endArray[] = strtoupper($prefix.'PAGE');
00774                 $endArray[] = strtoupper($prefix.'IMAGE');
00775                 $fixedOrderTabs = $endArray;
00776                 while (($row = $result->fetchRow()) !== false) {
00777                         if (!in_array($row['table_name'], $fixedOrderTabs))
00778                                 $endArray[] = $row['table_name'];
00779                 }
00780 
00781                 return $endArray;
00782         }
00783 
00784         public function dropTable( $tableName, $fName = 'DatabaseOracle::dropTable' ) {
00785                 $tableName = $this->tableName($tableName);
00786                 if( !$this->tableExists( $tableName ) ) {
00787                         return false;
00788                 }
00789 
00790                 return $this->doQuery( "DROP TABLE $tableName CASCADE CONSTRAINTS PURGE" );
00791         }
00792 
00793         function timestamp( $ts = 0 ) {
00794                 return wfTimestamp( TS_ORACLE, $ts );
00795         }
00796 
00800         function aggregateValue ( $valuedata, $valuename = 'value' ) {
00801                 return $valuedata;
00802         }
00803 
00804         function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
00805                 # Ignore errors during error handling to avoid infinite
00806                 # recursion
00807                 $ignore = $this->ignoreErrors( true );
00808                 ++$this->mErrorCount;
00809 
00810                 if ( $ignore || $tempIgnore ) {
00811                         wfDebug( "SQL ERROR (ignored): $error\n" );
00812                         $this->ignoreErrors( $ignore );
00813                 } else {
00814                         throw new DBQueryError( $this, $error, $errno, $sql, $fname );
00815                 }
00816         }
00817 
00821         public static function getSoftwareLink() {
00822                 return '[http://www.oracle.com/ Oracle]';
00823         }
00824 
00828         function getServerVersion() {
00829                 //better version number, fallback on driver
00830                 $rset = $this->doQuery( 'SELECT version FROM product_component_version WHERE UPPER(product) LIKE \'ORACLE DATABASE%\'' );
00831                 if ( !( $row =  $rset->fetchRow() ) ) {
00832                         return oci_server_version( $this->mConn );
00833                 }
00834                 return $row['version'];
00835         }
00836 
00840         function indexExists( $table, $index, $fname = 'DatabaseOracle::indexExists' ) {
00841                 $table = $this->tableName( $table );
00842                 $table = strtoupper( $this->removeIdentifierQuotes( $table ) );
00843                 $index = strtoupper( $index );
00844                 $owner = strtoupper( $this->mDBname );
00845                 $SQL = "SELECT 1 FROM all_indexes WHERE owner='$owner' AND index_name='{$table}_{$index}'";
00846                 $res = $this->doQuery( $SQL );
00847                 if ( $res ) {
00848                         $count = $res->numRows();
00849                         $res->free();
00850                 } else {
00851                         $count = 0;
00852                 }
00853                 return $count != 0;
00854         }
00855 
00859         function tableExists( $table, $fname = __METHOD__ ) {
00860                 $table = $this->tableName( $table );
00861                 $table = $this->addQuotes( strtoupper( $this->removeIdentifierQuotes( $table ) ) );
00862                 $owner = $this->addQuotes( strtoupper( $this->mDBname ) );
00863                 $SQL = "SELECT 1 FROM all_tables WHERE owner=$owner AND table_name=$table";
00864                 $res = $this->doQuery( $SQL );
00865                 if ( $res ) {
00866                         $count = $res->numRows();
00867                         $res->free();
00868                 } else {
00869                         $count = 0;
00870                 }
00871                 return $count;
00872         }
00873 
00884         private function fieldInfoMulti( $table, $field ) {
00885                 $field = strtoupper( $field );
00886                 if ( is_array( $table ) ) {
00887                         $table = array_map( array( &$this, 'tableNameInternal' ), $table );
00888                         $tableWhere = 'IN (';
00889                         foreach( $table as &$singleTable ) {
00890                                 $singleTable = $this->removeIdentifierQuotes($singleTable);
00891                                 if ( isset( $this->mFieldInfoCache["$singleTable.$field"] ) ) {
00892                                         return $this->mFieldInfoCache["$singleTable.$field"];
00893                                 }
00894                                 $tableWhere .= '\'' . $singleTable . '\',';
00895                         }
00896                         $tableWhere = rtrim( $tableWhere, ',' ) . ')';
00897                 } else {
00898                         $table = $this->removeIdentifierQuotes(  $this->tableNameInternal( $table ) );
00899                         if ( isset( $this->mFieldInfoCache["$table.$field"] ) ) {
00900                                 return $this->mFieldInfoCache["$table.$field"];
00901                         }
00902                         $tableWhere = '= \''.$table.'\'';
00903                 }
00904 
00905                 $fieldInfoStmt = oci_parse( $this->mConn, 'SELECT * FROM wiki_field_info_full WHERE table_name '.$tableWhere.' and column_name = \''.$field.'\'' );
00906                 if ( oci_execute( $fieldInfoStmt, $this->execFlags() ) === false ) {
00907                         $e = oci_error( $fieldInfoStmt );
00908                         $this->reportQueryError( $e['message'], $e['code'], 'fieldInfo QUERY', __METHOD__ );
00909                         return false;
00910                 }
00911                 $res = new ORAResult( $this, $fieldInfoStmt );
00912                 if ( $res->numRows() == 0 ) {
00913                         if ( is_array( $table ) ) {
00914                                 foreach( $table as &$singleTable ) {
00915                                         $this->mFieldInfoCache["$singleTable.$field"] = false;
00916                                 }
00917                         } else {
00918                                 $this->mFieldInfoCache["$table.$field"] = false;
00919                         }
00920                         $fieldInfoTemp = null;
00921                 } else {
00922                         $fieldInfoTemp = new ORAField( $res->fetchRow() );
00923                         $table = $fieldInfoTemp->tableName();
00924                         $this->mFieldInfoCache["$table.$field"] = $fieldInfoTemp;
00925                 }
00926                 $res->free();
00927                 return $fieldInfoTemp;
00928         }
00929 
00936         function fieldInfo( $table, $field ) {
00937                 if ( is_array( $table ) ) {
00938                         throw new DBUnexpectedError( $this, 'DatabaseOracle::fieldInfo called with table array!' );
00939                 }
00940                 return $this->fieldInfoMulti ($table, $field);
00941         }
00942 
00943         function begin( $fname = 'DatabaseOracle::begin' ) {
00944                 $this->mTrxLevel = 1;
00945                 $this->doQuery( 'SET CONSTRAINTS ALL DEFERRED' );
00946         }
00947 
00948         function commit( $fname = 'DatabaseOracle::commit' ) {
00949                 if ( $this->mTrxLevel ) {
00950                         $ret = oci_commit( $this->mConn );
00951                         if ( !$ret ) {
00952                                 throw new DBUnexpectedError( $this, $this->lastError() );
00953                         }
00954                         $this->mTrxLevel = 0;
00955                         $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' );
00956                 }
00957         }
00958 
00959         function rollback( $fname = 'DatabaseOracle::rollback' ) {
00960                 if ( $this->mTrxLevel ) {
00961                         oci_rollback( $this->mConn );
00962                         $this->mTrxLevel = 0;
00963                         $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' );
00964                 }
00965         }
00966 
00967         /* Not even sure why this is used in the main codebase... */
00968         function limitResultForUpdate( $sql, $num ) {
00969                 return $sql;
00970         }
00971 
00972         /* defines must comply with ^define\s*([^\s=]*)\s*=\s?'\{\$([^\}]*)\}'; */
00973         function sourceStream( $fp, $lineCallback = false, $resultCallback = false,
00974                 $fname = 'DatabaseOracle::sourceStream', $inputCallback = false ) {
00975                 $cmd = '';
00976                 $done = false;
00977                 $dollarquote = false;
00978 
00979                 $replacements = array();
00980 
00981                 while ( ! feof( $fp ) ) {
00982                         if ( $lineCallback ) {
00983                                 call_user_func( $lineCallback );
00984                         }
00985                         $line = trim( fgets( $fp, 1024 ) );
00986                         $sl = strlen( $line ) - 1;
00987 
00988                         if ( $sl < 0 ) {
00989                                 continue;
00990                         }
00991                         if ( '-' == $line { 0 } && '-' == $line { 1 } ) {
00992                                 continue;
00993                         }
00994 
00995                         // Allow dollar quoting for function declarations
00996                         if ( substr( $line, 0, 8 ) == '/*$mw$*/' ) {
00997                                 if ( $dollarquote ) {
00998                                         $dollarquote = false;
00999                                         $line = str_replace( '/*$mw$*/', '', $line ); // remove dollarquotes
01000                                         $done = true;
01001                                 } else {
01002                                         $dollarquote = true;
01003                                 }
01004                         } elseif ( !$dollarquote ) {
01005                                 if ( ';' == $line { $sl } && ( $sl < 2 || ';' != $line { $sl - 1 } ) ) {
01006                                         $done = true;
01007                                         $line = substr( $line, 0, $sl );
01008                                 }
01009                         }
01010 
01011                         if ( $cmd != '' ) {
01012                                 $cmd .= ' ';
01013                         }
01014                         $cmd .= "$line\n";
01015 
01016                         if ( $done ) {
01017                                 $cmd = str_replace( ';;', ";", $cmd );
01018                                 if ( strtolower( substr( $cmd, 0, 6 ) ) == 'define' ) {
01019                                         if ( preg_match( '/^define\s*([^\s=]*)\s*=\s*\'\{\$([^\}]*)\}\'/', $cmd, $defines ) ) {
01020                                                 $replacements[$defines[2]] = $defines[1];
01021                                         }
01022                                 } else {
01023                                         foreach ( $replacements as $mwVar => $scVar ) {
01024                                                 $cmd = str_replace( '&' . $scVar . '.', '`{$' . $mwVar . '}`', $cmd );
01025                                         }
01026 
01027                                         $cmd = $this->replaceVars( $cmd );
01028                                         if ( $inputCallback ) {
01029                                                 call_user_func( $inputCallback, $cmd );
01030                                         }
01031                                         $res = $this->doQuery( $cmd );
01032                                         if ( $resultCallback ) {
01033                                                 call_user_func( $resultCallback, $res, $this );
01034                                         }
01035 
01036                                         if ( false === $res ) {
01037                                                 $err = $this->lastError();
01038                                                 return "Query \"{$cmd}\" failed with error code \"$err\".\n";
01039                                         }
01040                                 }
01041 
01042                                 $cmd = '';
01043                                 $done = false;
01044                         }
01045                 }
01046                 return true;
01047         }
01048 
01049         function selectDB( $db ) {
01050                 $this->mDBname = $db;
01051                 if ( $db == null || $db == $this->mUser ) {
01052                         return true;
01053                 }
01054                 $sql = 'ALTER SESSION SET CURRENT_SCHEMA=' . strtoupper($db);
01055                 $stmt = oci_parse( $this->mConn, $sql );
01056                 wfSuppressWarnings();
01057                 $success = oci_execute( $stmt );
01058                 wfRestoreWarnings();
01059                 if ( !$success ) {
01060                         $e = oci_error( $stmt );
01061                         if ( $e['code'] != '1435' ) {
01062                                 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
01063                         }
01064                         return false;
01065                 }
01066                 return true;
01067         }
01068 
01069         function strencode( $s ) {
01070                 return str_replace( "'", "''", $s );
01071         }
01072 
01073         function addQuotes( $s ) {
01074                 global $wgContLang;
01075                 if ( isset( $wgContLang->mLoaded ) && $wgContLang->mLoaded ) {
01076                         $s = $wgContLang->checkTitleEncoding( $s );
01077                 }
01078                 return "'" . $this->strencode( $s ) . "'";
01079         }
01080 
01081         public function addIdentifierQuotes( $s ) {
01082                 if ( !$this->getFlag( DBO_DDLMODE ) ) {
01083                         $s = '/*Q*/' . $s;
01084                 }
01085                 return $s;
01086         }
01087 
01088         public function removeIdentifierQuotes( $s ) {
01089                 return strpos($s, '/*Q*/') === FALSE ? $s : substr($s, 5);
01090         }
01091 
01092         public function isQuotedIdentifier( $s ) {
01093                 return strpos($s, '/*Q*/') !== FALSE;
01094         }
01095 
01096         private function wrapFieldForWhere( $table, &$col, &$val ) {
01097                 global $wgContLang;
01098 
01099                 $col_info = $this->fieldInfoMulti( $table, $col );
01100                 $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
01101                 if ( $col_type == 'CLOB' ) {
01102                         $col = 'TO_CHAR(' . $col . ')';
01103                         $val = $wgContLang->checkTitleEncoding( $val );
01104                 } elseif ( $col_type == 'VARCHAR2' && !mb_check_encoding( $val ) ) {
01105                         $val = $wgContLang->checkTitleEncoding( $val );
01106                 }
01107         }
01108 
01109         private function wrapConditionsForWhere ( $table, $conds, $parentCol = null ) {
01110                 $conds2 = array();
01111                 foreach ( $conds as $col => $val ) {
01112                         if ( is_array( $val ) ) {
01113                                 $conds2[$col] = $this->wrapConditionsForWhere ( $table, $val, $col );
01114                         } else {
01115                                 if ( is_numeric( $col ) && $parentCol != null ) {
01116                                         $this->wrapFieldForWhere ( $table, $parentCol, $val );
01117                                 } else {
01118                                         $this->wrapFieldForWhere ( $table, $col, $val );
01119                                 }
01120                                 $conds2[$col] = $val;
01121                         }
01122                 }
01123                 return $conds2;
01124         }
01125 
01126         function selectRow( $table, $vars, $conds, $fname = 'DatabaseOracle::selectRow', $options = array(), $join_conds = array() ) {
01127                 if ( is_array($conds) ) {
01128                         $conds = $this->wrapConditionsForWhere( $table, $conds );
01129                 }
01130                 return parent::selectRow( $table, $vars, $conds, $fname, $options, $join_conds );
01131         }
01132 
01143         function makeSelectOptions( $options ) {
01144                 $preLimitTail = $postLimitTail = '';
01145                 $startOpts = '';
01146 
01147                 $noKeyOptions = array();
01148                 foreach ( $options as $key => $option ) {
01149                         if ( is_numeric( $key ) ) {
01150                                 $noKeyOptions[$option] = true;
01151                         }
01152                 }
01153 
01154                 if ( isset( $options['GROUP BY'] ) ) {
01155                         $preLimitTail .= " GROUP BY {$options['GROUP BY']}";
01156                 }
01157                 if ( isset( $options['ORDER BY'] ) ) {
01158                         $preLimitTail .= " ORDER BY {$options['ORDER BY']}";
01159                 }
01160 
01161                 # if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $tailOpts .= ' FOR UPDATE';
01162                 # if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $tailOpts .= ' LOCK IN SHARE MODE';
01163                 if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) {
01164                         $startOpts .= 'DISTINCT';
01165                 }
01166 
01167                 if ( isset( $options['USE INDEX'] ) && ! is_array( $options['USE INDEX'] ) ) {
01168                         $useIndex = $this->useIndexClause( $options['USE INDEX'] );
01169                 } else {
01170                         $useIndex = '';
01171                 }
01172 
01173                 return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
01174         }
01175 
01176         public function delete( $table, $conds, $fname = 'DatabaseOracle::delete' ) {
01177                 if ( is_array($conds) ) {
01178                         $conds = $this->wrapConditionsForWhere( $table, $conds );
01179                 }
01180                 // a hack for deleting pages, users and images (which have non-nullable FKs)
01181                 // all deletions on these tables have transactions so final failure rollbacks these updates
01182                 $table = $this->tableName( $table );
01183                 if ( $table == $this->tableName( 'user' )  ) {
01184                                 $this->update( 'archive', array( 'ar_user' => 0 ), array( 'ar_user' => $conds['user_id'] ), $fname );
01185                                 $this->update( 'ipblocks', array( 'ipb_user' => 0 ), array( 'ipb_user' => $conds['user_id'] ), $fname );
01186                                 $this->update( 'image', array( 'img_user' => 0 ), array( 'img_user' => $conds['user_id'] ), $fname );
01187                                 $this->update( 'oldimage', array( 'oi_user' => 0 ), array( 'oi_user' => $conds['user_id'] ), $fname );
01188                                 $this->update( 'filearchive', array( 'fa_deleted_user' => 0 ), array( 'fa_deleted_user' => $conds['user_id'] ), $fname );
01189                                 $this->update( 'filearchive', array( 'fa_user' => 0 ), array( 'fa_user' => $conds['user_id'] ), $fname );
01190                                 $this->update( 'uploadstash', array( 'us_user' => 0 ), array( 'us_user' => $conds['user_id'] ), $fname );
01191                                 $this->update( 'recentchanges', array( 'rc_user' => 0 ), array( 'rc_user' => $conds['user_id'] ), $fname );
01192                                 $this->update( 'logging', array( 'log_user' => 0 ), array( 'log_user' => $conds['user_id'] ), $fname );
01193                 } elseif ( $table == $this->tableName( 'image' )  ) {
01194                                 $this->update( 'oldimage', array( 'oi_name' => 0 ), array( 'oi_name' => $conds['img_name'] ), $fname );
01195                 }
01196                 return parent::delete( $table, $conds, $fname );
01197         }
01198 
01199         function update( $table, $values, $conds, $fname = 'DatabaseOracle::update', $options = array() ) {
01200                 global $wgContLang;
01201 
01202                 $table = $this->tableName( $table );
01203                 $opts = $this->makeUpdateOptions( $options );
01204                 $sql = "UPDATE $opts $table SET ";
01205 
01206                 $first = true;
01207                 foreach ( $values as $col => &$val ) {
01208                         $sqlSet = $this->fieldBindStatement( $table, $col, $val, true );
01209 
01210                         if ( !$first ) {
01211                                 $sqlSet = ', ' . $sqlSet;
01212                         } else {
01213                                 $first = false;
01214                         }
01215                         $sql .= $sqlSet;
01216                 }
01217 
01218                 if ( $conds !== array() && $conds !== '*' ) {
01219                         $conds = $this->wrapConditionsForWhere( $table, $conds );
01220                         $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
01221                 }
01222 
01223                 if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) {
01224                         $e = oci_error( $this->mConn );
01225                         $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
01226                         return false;
01227                 }
01228                 foreach ( $values as $col => &$val ) {
01229                         $col_info = $this->fieldInfoMulti( $table, $col );
01230                         $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
01231 
01232                         if ( $val === null ) {
01233                                 // do nothing ... null was inserted in statement creation
01234                         } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) {
01235                                 if ( is_object( $val ) ) {
01236                                         $val = $val->getData();
01237                                 }
01238 
01239                                 if ( preg_match( '/^timestamp.*/i', $col_type ) == 1 && strtolower( $val ) == 'infinity' ) {
01240                                         $val = '31-12-2030 12:00:00.000000';
01241                                 }
01242 
01243                                 $val = ( $wgContLang != null ) ? $wgContLang->checkTitleEncoding( $val ) : $val;
01244                                 if ( oci_bind_by_name( $stmt, ":$col", $val ) === false ) {
01245                                         $e = oci_error( $stmt );
01246                                         $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
01247                                         return false;
01248                                 }
01249                         } else {
01250                                 if ( ( $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB ) ) === false ) {
01251                                         $e = oci_error( $stmt );
01252                                         throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] );
01253                                 }
01254 
01255                                 if ( $col_type == 'BLOB' ) {
01256                                         $lob[$col]->writeTemporary( $val );
01257                                         oci_bind_by_name( $stmt, ":$col", $lob[$col], - 1, SQLT_BLOB );
01258                                 } else {
01259                                         $lob[$col]->writeTemporary( $val );
01260                                         oci_bind_by_name( $stmt, ":$col", $lob[$col], - 1, OCI_B_CLOB );
01261                                 }
01262                         }
01263                 }
01264 
01265                 wfSuppressWarnings();
01266 
01267                 if ( oci_execute( $stmt, $this->execFlags() ) === false ) {
01268                         $e = oci_error( $stmt );
01269                         if ( !$this->ignore_DUP_VAL_ON_INDEX || $e['code'] != '1' ) {
01270                                 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
01271                                 return false;
01272                         } else {
01273                                 $this->mAffectedRows = oci_num_rows( $stmt );
01274                         }
01275                 } else {
01276                         $this->mAffectedRows = oci_num_rows( $stmt );
01277                 }
01278 
01279                 wfRestoreWarnings();
01280 
01281                 if ( isset( $lob ) ) {
01282                         foreach ( $lob as $lob_v ) {
01283                                 $lob_v->free();
01284                         }
01285                 }
01286 
01287                 if ( !$this->mTrxLevel ) {
01288                         oci_commit( $this->mConn );
01289                 }
01290 
01291                 oci_free_statement( $stmt );
01292         }
01293 
01294         function bitNot( $field ) {
01295                 // expecting bit-fields smaller than 4bytes
01296                 return 'BITNOT(' . $field . ')';
01297         }
01298 
01299         function bitAnd( $fieldLeft, $fieldRight ) {
01300                 return 'BITAND(' . $fieldLeft . ', ' . $fieldRight . ')';
01301         }
01302 
01303         function bitOr( $fieldLeft, $fieldRight ) {
01304                 return 'BITOR(' . $fieldLeft . ', ' . $fieldRight . ')';
01305         }
01306 
01307         function setFakeMaster( $enabled = true ) {
01308         }
01309 
01310         function getDBname() {
01311                 return $this->mDBname;
01312         }
01313 
01314         function getServer() {
01315                 return $this->mServer;
01316         }
01317 
01318         public function getSearchEngine() {
01319                 return 'SearchOracle';
01320         }
01321 
01322         public function getInfinity() {
01323                 return '31-12-2030 12:00:00.000000';
01324         }
01325 
01326 } // end DatabaseOracle class