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