MediaWiki
REL1_22
|
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