00001 <?php
00011 class ORABlob {
00012 var $mData;
00013
00014 function __construct( $data ) {
00015 $this->mData = $data;
00016 }
00017
00018 function getData() {
00019 return $this->mData;
00020 }
00021 }
00022
00029 class ORAResult {
00030 private $rows;
00031 private $cursor;
00032 private $stmt;
00033 private $nrows;
00034
00035 private $unique;
00036 private function array_unique_md( $array_in ) {
00037 $array_out = array();
00038 $array_hashes = array();
00039
00040 foreach ( $array_in as $key => $item ) {
00041 $hash = md5( serialize( $item ) );
00042 if ( !isset( $array_hashes[$hash] ) ) {
00043 $array_hashes[$hash] = $hash;
00044 $array_out[] = $item;
00045 }
00046 }
00047
00048 return $array_out;
00049 }
00050
00051 function __construct( &$db, $stmt, $unique = false ) {
00052 $this->db =& $db;
00053
00054 if ( ( $this->nrows = oci_fetch_all( $stmt, $this->rows, 0, - 1, OCI_FETCHSTATEMENT_BY_ROW | OCI_NUM ) ) === false ) {
00055 $e = oci_error( $stmt );
00056 $db->reportQueryError( $e['message'], $e['code'], '', __FUNCTION__ );
00057 return;
00058 }
00059
00060 if ( $unique ) {
00061 $this->rows = $this->array_unique_md( $this->rows );
00062 $this->nrows = count( $this->rows );
00063 }
00064
00065 $this->cursor = 0;
00066 $this->stmt = $stmt;
00067 }
00068
00069 public function free() {
00070 oci_free_statement( $this->stmt );
00071 }
00072
00073 public function seek( $row ) {
00074 $this->cursor = min( $row, $this->nrows );
00075 }
00076
00077 public function numRows() {
00078 return $this->nrows;
00079 }
00080
00081 public function numFields() {
00082 return oci_num_fields( $this->stmt );
00083 }
00084
00085 public function fetchObject() {
00086 if ( $this->cursor >= $this->nrows ) {
00087 return false;
00088 }
00089 $row = $this->rows[$this->cursor++];
00090 $ret = new stdClass();
00091 foreach ( $row as $k => $v ) {
00092 $lc = strtolower( oci_field_name( $this->stmt, $k + 1 ) );
00093 $ret->$lc = $v;
00094 }
00095
00096 return $ret;
00097 }
00098
00099 public function fetchRow() {
00100 if ( $this->cursor >= $this->nrows ) {
00101 return false;
00102 }
00103
00104 $row = $this->rows[$this->cursor++];
00105 $ret = array();
00106 foreach ( $row as $k => $v ) {
00107 $lc = strtolower( oci_field_name( $this->stmt, $k + 1 ) );
00108 $ret[$lc] = $v;
00109 $ret[$k] = $v;
00110 }
00111 return $ret;
00112 }
00113 }
00114
00119 class ORAField {
00120 private $name, $tablename, $default, $max_length, $nullable,
00121 $is_pk, $is_unique, $is_multiple, $is_key, $type;
00122
00123 function __construct( $info ) {
00124 $this->name = $info['column_name'];
00125 $this->tablename = $info['table_name'];
00126 $this->default = $info['data_default'];
00127 $this->max_length = $info['data_length'];
00128 $this->nullable = $info['not_null'];
00129 $this->is_pk = isset( $info['prim'] ) && $info['prim'] == 1 ? 1 : 0;
00130 $this->is_unique = isset( $info['uniq'] ) && $info['uniq'] == 1 ? 1 : 0;
00131 $this->is_multiple = isset( $info['nonuniq'] ) && $info['nonuniq'] == 1 ? 1 : 0;
00132 $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
00133 $this->type = $info['data_type'];
00134 }
00135
00136 function name() {
00137 return $this->name;
00138 }
00139
00140 function tableName() {
00141 return $this->tablename;
00142 }
00143
00144 function defaultValue() {
00145 return $this->default;
00146 }
00147
00148 function maxLength() {
00149 return $this->max_length;
00150 }
00151
00152 function nullable() {
00153 return $this->nullable;
00154 }
00155
00156 function isKey() {
00157 return $this->is_key;
00158 }
00159
00160 function isMultipleKey() {
00161 return $this->is_multiple;
00162 }
00163
00164 function type() {
00165 return $this->type;
00166 }
00167 }
00168
00172 class DatabaseOracle extends DatabaseBase {
00173 var $mInsertId = null;
00174 var $mLastResult = null;
00175 var $numeric_version = null;
00176 var $lastResult = null;
00177 var $cursor = 0;
00178 var $mAffectedRows;
00179
00180 var $ignore_DUP_VAL_ON_INDEX = false;
00181 var $sequenceData = null;
00182
00183 var $defaultCharset = 'AL32UTF8';
00184
00185 var $mFieldInfoCache = array();
00186
00187 function __construct( $server = false, $user = false, $password = false, $dbName = false,
00188 $failFunction = false, $flags = 0, $tablePrefix = 'get from global' )
00189 {
00190 $tablePrefix = $tablePrefix == 'get from global' ? $tablePrefix : strtoupper( $tablePrefix );
00191 parent::__construct( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix );
00192 wfRunHooks( 'DatabaseOraclePostInit', array( &$this ) );
00193 }
00194
00195 function getType() {
00196 return 'oracle';
00197 }
00198
00199 function cascadingDeletes() {
00200 return true;
00201 }
00202 function cleanupTriggers() {
00203 return true;
00204 }
00205 function strictIPs() {
00206 return true;
00207 }
00208 function realTimestamps() {
00209 return true;
00210 }
00211 function implicitGroupby() {
00212 return false;
00213 }
00214 function implicitOrderby() {
00215 return false;
00216 }
00217 function searchableIPs() {
00218 return true;
00219 }
00220
00221 static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0 )
00222 {
00223 return new DatabaseOracle( $server, $user, $password, $dbName, $failFunction, $flags );
00224 }
00225
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->mServer = $server;
00237 $this->mUser = $user;
00238 $this->mPassword = $password;
00239 $this->mDBname = $dbName;
00240
00241 if ( !strlen( $user ) ) { # e.g. the class is being loaded
00242 return;
00243 }
00244
00245 $session_mode = $this->mFlags & DBO_SYSDBA ? OCI_SYSDBA : OCI_DEFAULT;
00246 if ( $this->mFlags & DBO_DEFAULT ) {
00247 $this->mConn = oci_new_connect( $user, $password, $dbName, $this->defaultCharset, $session_mode );
00248 } else {
00249 $this->mConn = oci_connect( $user, $password, $dbName, $this->defaultCharset, $session_mode );
00250 }
00251
00252 if ( $this->mConn == false ) {
00253 wfDebug( "DB connection error\n" );
00254 wfDebug( "Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n" );
00255 wfDebug( $this->lastError() . "\n" );
00256 return false;
00257 }
00258
00259 $this->mOpened = true;
00260
00261 # removed putenv calls because they interfere with the system globaly
00262 $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
00263 $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
00264 return $this->mConn;
00265 }
00266
00271 function close() {
00272 $this->mOpened = false;
00273 if ( $this->mConn ) {
00274 return oci_close( $this->mConn );
00275 } else {
00276 return true;
00277 }
00278 }
00279
00280 function execFlags() {
00281 return $this->mTrxLevel ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS;
00282 }
00283
00284 function doQuery( $sql ) {
00285 wfDebug( "SQL: [$sql]\n" );
00286 if ( !mb_check_encoding( $sql ) ) {
00287 throw new MWException( "SQL encoding is invalid\n$sql" );
00288 }
00289
00290
00291
00292 if ( !defined( 'MEDIAWIKI_INSTALL' ) ) {
00293 $sql = preg_replace( '/ as /i', ' ', $sql );
00294 }
00295
00296
00297 $union_unique = ( preg_match( '/\/\* UNION_UNIQUE \*\/ /', $sql ) != 0 );
00298
00299
00300 $explain_id = date( 'dmYHis' );
00301
00302 $sql = preg_replace( '/^EXPLAIN /', 'EXPLAIN PLAN SET STATEMENT_ID = \'' . $explain_id . '\' FOR', $sql, 1, $explain_count );
00303
00304
00305 wfSuppressWarnings();
00306
00307 if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) {
00308 $e = oci_error( $this->mConn );
00309 $this->reportQueryError( $e['message'], $e['code'], $sql, __FUNCTION__ );
00310 return false;
00311 }
00312
00313 if ( oci_execute( $stmt, $this->execFlags() ) == false ) {
00314 $e = oci_error( $stmt );
00315 if ( !$this->ignore_DUP_VAL_ON_INDEX || $e['code'] != '1' ) {
00316 $this->reportQueryError( $e['message'], $e['code'], $sql, __FUNCTION__ );
00317 return false;
00318 }
00319 }
00320
00321 wfRestoreWarnings();
00322
00323 if ( $explain_count > 0 ) {
00324 return $this->doQuery( 'SELECT id, cardinality "ROWS" FROM plan_table WHERE statement_id = \'' . $explain_id . '\'' );
00325 } elseif ( oci_statement_type( $stmt ) == 'SELECT' ) {
00326 return new ORAResult( $this, $stmt, $union_unique );
00327 } else {
00328 $this->mAffectedRows = oci_num_rows( $stmt );
00329 return true;
00330 }
00331 }
00332
00333 function queryIgnore( $sql, $fname = '' ) {
00334 return $this->query( $sql, $fname, true );
00335 }
00336
00337 function freeResult( $res ) {
00338 if ( $res instanceof ORAResult ) {
00339 $res->free();
00340 } else {
00341 $res->result->free();
00342 }
00343 }
00344
00345 function fetchObject( $res ) {
00346 if ( $res instanceof ORAResult ) {
00347 return $res->numRows();
00348 } else {
00349 return $res->result->fetchObject();
00350 }
00351 }
00352
00353 function fetchRow( $res ) {
00354 if ( $res instanceof ORAResult ) {
00355 return $res->fetchRow();
00356 } else {
00357 return $res->result->fetchRow();
00358 }
00359 }
00360
00361 function numRows( $res ) {
00362 if ( $res instanceof ORAResult ) {
00363 return $res->numRows();
00364 } else {
00365 return $res->result->numRows();
00366 }
00367 }
00368
00369 function numFields( $res ) {
00370 if ( $res instanceof ORAResult ) {
00371 return $res->numFields();
00372 } else {
00373 return $res->result->numFields();
00374 }
00375 }
00376
00377 function fieldName( $stmt, $n ) {
00378 return oci_field_name( $stmt, $n );
00379 }
00380
00384 function insertId() {
00385 return $this->mInsertId;
00386 }
00387
00388 function dataSeek( $res, $row ) {
00389 if ( $res instanceof ORAResult ) {
00390 $res->seek( $row );
00391 } else {
00392 $res->result->seek( $row );
00393 }
00394 }
00395
00396 function lastError() {
00397 if ( $this->mConn === false ) {
00398 $e = oci_error();
00399 } else {
00400 $e = oci_error( $this->mConn );
00401 }
00402 return $e['message'];
00403 }
00404
00405 function lastErrno() {
00406 if ( $this->mConn === false ) {
00407 $e = oci_error();
00408 } else {
00409 $e = oci_error( $this->mConn );
00410 }
00411 return $e['code'];
00412 }
00413
00414 function affectedRows() {
00415 return $this->mAffectedRows;
00416 }
00417
00422 function indexInfo( $table, $index, $fname = 'DatabaseOracle::indexExists' ) {
00423 return false;
00424 }
00425
00426 function indexUnique( $table, $index, $fname = 'DatabaseOracle::indexUnique' ) {
00427 return false;
00428 }
00429
00430 function insert( $table, $a, $fname = 'DatabaseOracle::insert', $options = array() ) {
00431 if ( !count( $a ) ) {
00432 return true;
00433 }
00434
00435 if ( !is_array( $options ) ) {
00436 $options = array( $options );
00437 }
00438
00439 if ( in_array( 'IGNORE', $options ) ) {
00440 $this->ignore_DUP_VAL_ON_INDEX = true;
00441 }
00442
00443 if ( !is_array( reset( $a ) ) ) {
00444 $a = array( $a );
00445 }
00446
00447 foreach ( $a as &$row ) {
00448 $this->insertOneRow( $table, $row, $fname );
00449 }
00450 $retVal = true;
00451
00452 if ( in_array( 'IGNORE', $options ) ) {
00453 $this->ignore_DUP_VAL_ON_INDEX = false;
00454 }
00455
00456 return $retVal;
00457 }
00458
00459 private function insertOneRow( $table, $row, $fname ) {
00460 global $wgLang;
00461
00462 $table = $this->tableName( $table );
00463
00464 $sql = "INSERT INTO " . $table . " (" . join( ',', array_keys( $row ) ) . ')';
00465 $sql .= " VALUES (";
00466
00467
00468 $first = true;
00469 foreach ( $row as $col => $val ) {
00470 if ( $first ) {
00471 $sql .= $val !== null ? ':' . $col : 'NULL';
00472 } else {
00473 $sql .= $val !== null ? ', :' . $col : ', NULL';
00474 }
00475
00476 $first = false;
00477 }
00478 $sql .= ')';
00479
00480 $stmt = oci_parse( $this->mConn, $sql );
00481 foreach ( $row as $col => &$val ) {
00482 $col_info = $this->fieldInfoMulti( $table, $col );
00483 $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
00484
00485 if ( $val === null ) {
00486
00487 } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) {
00488 if ( is_object( $val ) ) {
00489 $val = $val->getData();
00490 }
00491
00492 if ( preg_match( '/^timestamp.*/i', $col_type ) == 1 && strtolower( $val ) == 'infinity' ) {
00493 $val = '31-12-2030 12:00:00.000000';
00494 }
00495
00496 $val = ( $wgLang != null ) ? $wgLang->checkTitleEncoding( $val ) : $val;
00497 if ( oci_bind_by_name( $stmt, ":$col", $val ) === false ) {
00498 $this->reportQueryError( $this->lastErrno(), $this->lastError(), $sql, __METHOD__ );
00499 return false;
00500 }
00501 } else {
00502 if ( ( $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB ) ) === false ) {
00503 $e = oci_error( $stmt );
00504 throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] );
00505 }
00506
00507 if ( $col_type == 'BLOB' ) {
00508 $lob[$col]->writeTemporary( $val );
00509 oci_bind_by_name( $stmt, ":$col", $lob[$col], - 1, SQLT_BLOB );
00510 } else {
00511 $lob[$col]->writeTemporary( $val );
00512 oci_bind_by_name( $stmt, ":$col", $lob[$col], - 1, OCI_B_CLOB );
00513 }
00514 }
00515 }
00516
00517 wfSuppressWarnings();
00518
00519 if ( oci_execute( $stmt, OCI_DEFAULT ) === false ) {
00520 $e = oci_error( $stmt );
00521
00522 if ( !$this->ignore_DUP_VAL_ON_INDEX || $e['code'] != '1' ) {
00523 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
00524 return false;
00525 } else {
00526 $this->mAffectedRows = oci_num_rows( $stmt );
00527 }
00528 } else {
00529 $this->mAffectedRows = oci_num_rows( $stmt );
00530 }
00531
00532 wfRestoreWarnings();
00533
00534 if ( isset( $lob ) ) {
00535 foreach ( $lob as $lob_i => $lob_v ) {
00536 $lob_v->free();
00537 }
00538 }
00539
00540 if ( !$this->mTrxLevel ) {
00541 oci_commit( $this->mConn );
00542 }
00543
00544 oci_free_statement( $stmt );
00545 }
00546
00547 function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = 'DatabaseOracle::insertSelect',
00548 $insertOptions = array(), $selectOptions = array() )
00549 {
00550 $destTable = $this->tableName( $destTable );
00551 if ( !is_array( $selectOptions ) ) {
00552 $selectOptions = array( $selectOptions );
00553 }
00554 list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
00555 if ( is_array( $srcTable ) ) {
00556 $srcTable = implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) );
00557 } else {
00558 $srcTable = $this->tableName( $srcTable );
00559 }
00560
00561 if ( ( $sequenceData = $this->getSequenceData( $destTable ) ) !== false &&
00562 !isset( $varMap[$sequenceData['column']] ) )
00563 $varMap[$sequenceData['column']] = 'GET_SEQUENCE_VALUE(\'' . $sequenceData['sequence'] . '\')';
00564
00565 // count-alias subselect fields to avoid abigious definition errors
00566 $i = 0;
00567 foreach ( $varMap as $key => &$val ) {
00568 $val = $val . ' field' . ( $i++ );
00569 }
00570
00571 $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
00572 " SELECT $startOpts " . implode( ',', $varMap ) .
00573 " FROM $srcTable $useIndex ";
00574 if ( $conds != '*' ) {
00575 $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
00576 }
00577 $sql .= " $tailOpts";
00578
00579 if ( in_array( 'IGNORE', $insertOptions ) ) {
00580 $this->ignore_DUP_VAL_ON_INDEX = true;
00581 }
00582
00583 $retval = $this->query( $sql, $fname );
00584
00585 if ( in_array( 'IGNORE', $insertOptions ) ) {
00586 $this->ignore_DUP_VAL_ON_INDEX = false;
00587 }
00588
00589 return $retval;
00590 }
00591
00592 function tableName( $name ) {
00593 global $wgSharedDB, $wgSharedPrefix, $wgSharedTables;
00594 /*
00595 Replace reserved words with better ones
00596 Using uppercase because that's the only way Oracle can handle
00597 quoted tablenames
00598 */
00599 switch( $name ) {
00600 case 'user':
00601 $name = 'MWUSER';
00602 break;
00603 case 'text':
00604 $name = 'PAGECONTENT';
00605 break;
00606 }
00607
00608
00609
00610
00611
00612 if ( $name[0] == '"' && substr( $name, - 1, 1 ) == '"' ) {
00613 return $name;
00614 }
00615 if ( preg_match( '/(^|\s)(DISTINCT|JOIN|ON|AS)(\s|$)/i', $name ) !== 0 ) {
00616 return $name;
00617 }
00618 $dbDetails = array_reverse( explode( '.', $name, 2 ) );
00619 if ( isset( $dbDetails[1] ) ) {
00620 @list( $table, $database ) = $dbDetails;
00621 } else {
00622 @list( $table ) = $dbDetails;
00623 }
00624
00625 $prefix = $this->mTablePrefix;
00626
00627 if ( isset( $database ) ) {
00628 $table = ( $table[0] == '`' ? $table : "`{$table}`" );
00629 }
00630
00631 if ( !isset( $database ) && isset( $wgSharedDB ) && $table[0] != '"'
00632 && isset( $wgSharedTables )
00633 && is_array( $wgSharedTables )
00634 && in_array( $table, $wgSharedTables )
00635 ) {
00636 $database = $wgSharedDB;
00637 $prefix = isset( $wgSharedPrefix ) ? $wgSharedPrefix : $prefix;
00638 }
00639
00640 if ( isset( $database ) ) {
00641 $database = ( $database[0] == '"' ? $database : "\"{$database}\"" );
00642 }
00643 $table = ( $table[0] == '"' ? $table : "\"{$prefix}{$table}\"" );
00644
00645 $tableName = ( isset( $database ) ? "{$database}.{$table}" : "{$table}" );
00646
00647 return strtoupper( $tableName );
00648 }
00649
00653 function nextSequenceValue( $seqName ) {
00654 $res = $this->query( "SELECT $seqName.nextval FROM dual" );
00655 $row = $this->fetchRow( $res );
00656 $this->mInsertId = $row[0];
00657 $this->freeResult( $res );
00658 return $this->mInsertId;
00659 }
00660
00664 private function getSequenceData( $table ) {
00665 if ( $this->sequenceData == null ) {
00666 $result = $this->query( "SELECT lower(us.sequence_name), lower(utc.table_name), lower(utc.column_name) from user_sequences us, user_tab_columns utc where us.sequence_name = utc.table_name||'_'||utc.column_name||'_SEQ'" );
00667
00668 while ( ( $row = $result->fetchRow() ) !== false ) {
00669 $this->sequenceData[$this->tableName( $row[1] )] = array(
00670 'sequence' => $row[0],
00671 'column' => $row[2]
00672 );
00673 }
00674 }
00675
00676 return ( isset( $this->sequenceData[$table] ) ) ? $this->sequenceData[$table] : false;
00677 }
00678
00679 # REPLACE query wrapper
00680 # Oracle simulates this with a DELETE followed by INSERT
00681 # $row is the row to insert, an associative array
00682 # $uniqueIndexes is an array of indexes. Each element may be either a
00683 # field name or an array of field names
00684 #
00685 # It may be more efficient to leave off unique indexes which are unlikely to collide.
00686 # However if you do this, you run the risk of encountering errors which wouldn't have
00687 # occurred in MySQL
00688 function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabaseOracle::replace' ) {
00689 $table = $this->tableName( $table );
00690
00691 if ( count( $rows ) == 0 ) {
00692 return;
00693 }
00694
00695 # Single row case
00696 if ( !is_array( reset( $rows ) ) ) {
00697 $rows = array( $rows );
00698 }
00699
00700 $sequenceData = $this->getSequenceData( $table );
00701
00702 foreach ( $rows as $row ) {
00703 # Delete rows which collide
00704 if ( $uniqueIndexes ) {
00705 $condsDelete = array();
00706 foreach ( $uniqueIndexes as $index )
00707 $condsDelete[$index] = $row[$index];
00708 if (count($condsDelete) > 0) {
00709 $this->delete( $table, $condsDelete, $fname );
00710 }
00711 }
00712
00713 if ( $sequenceData !== false && !isset( $row[$sequenceData['column']] ) ) {
00714 $row[$sequenceData['column']] = $this->nextSequenceValue( $sequenceData['sequence'] );
00715 }
00716
00717 # Now insert the row
00718 $this->insert( $table, $row, $fname );
00719 }
00720 }
00721
00722 # DELETE where the condition is a join
00723 function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "DatabaseOracle::deleteJoin" ) {
00724 if ( !$conds ) {
00725 throw new DBUnexpectedError( $this, 'DatabaseOracle::deleteJoin() called with empty $conds' );
00726 }
00727
00728 $delTable = $this->tableName( $delTable );
00729 $joinTable = $this->tableName( $joinTable );
00730 $sql = "DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable ";
00731 if ( $conds != '*' ) {
00732 $sql .= 'WHERE ' . $this->makeList( $conds, LIST_AND );
00733 }
00734 $sql .= ')';
00735
00736 $this->query( $sql, $fname );
00737 }
00738
00739 # Returns the size of a text field, or -1 for "unlimited"
00740 function textFieldSize( $table, $field ) {
00741 $fieldInfoData = $this->fieldInfo( $table, $field);
00742 if ( $fieldInfoData->type == "varchar" ) {
00743 $size = $row->size - 4;
00744 } else {
00745 $size = $row->size;
00746 }
00747 return $size;
00748 }
00749
00750 function limitResult( $sql, $limit, $offset = false ) {
00751 if ( $offset === false ) {
00752 $offset = 0;
00753 }
00754 return "SELECT * FROM ($sql) WHERE rownum >= (1 + $offset) AND rownum < (1 + $limit + $offset)";
00755 }
00756
00757
00758 function unionQueries( $sqls, $all ) {
00759 $glue = ' UNION ALL ';
00760 return 'SELECT * ' . ( $all ? '':'/* UNION_UNIQUE */ ' ) . 'FROM (' . implode( $glue, $sqls ) . ')' ;
00761 }
00762
00763 function wasDeadlock() {
00764 return $this->lastErrno() == 'OCI-00060';
00765 }
00766
00767
00768 function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabaseOracle::duplicateTableStructure' ) {
00769 $temporary = $temporary ? 'TRUE' : 'FALSE';
00770 $oldName = trim(strtoupper($oldName), '"');
00771 $oldParts = explode('_', $oldName);
00772
00773 $newName = trim(strtoupper($newName), '"');
00774 $newParts = explode('_', $newName);
00775
00776 $oldPrefix = '';
00777 $newPrefix = '';
00778 for ($i = count($oldParts)-1; $i >= 0; $i--) {
00779 if ($oldParts[$i] != $newParts[$i]) {
00780 $oldPrefix = implode('_', $oldParts).'_';
00781 $newPrefix = implode('_', $newParts).'_';
00782 break;
00783 }
00784 unset($oldParts[$i]);
00785 unset($newParts[$i]);
00786 }
00787
00788 $tabName = substr($oldName, strlen($oldPrefix));
00789
00790 return $this->query( 'BEGIN DUPLICATE_TABLE(\'' . $tabName . '\', \'' . $oldPrefix . '\', \''.$newPrefix.'\', ' . $temporary . '); END;', $fname );
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 function getSoftwareLink() {
00822 return '[http:
00823 }
00824
00828 function getServerVersion() {
00829 return oci_server_version( $this->mConn );
00830 }
00831
00835 function tableExists( $table ) {
00836 $SQL = "SELECT 1 FROM user_tables WHERE table_name='$table'";
00837 $res = $this->doQuery( $SQL );
00838 if ( $res ) {
00839 $count = $res->numRows();
00840 $res->free();
00841 } else {
00842 $count = 0;
00843 }
00844 return $count;
00845 }
00846
00856 private function fieldInfoMulti( $table, $field ) {
00857 $tableWhere = '';
00858 $field = strtoupper($field);
00859 if (is_array($table)) {
00860 $table = array_map( array( &$this, 'tableName' ), $table );
00861 $tableWhere = 'IN (';
00862 foreach($table as &$singleTable) {
00863 $singleTable = strtoupper(trim( $singleTable, '"' ));
00864 if (isset($this->mFieldInfoCache["$singleTable.$field"])) {
00865 return $this->mFieldInfoCache["$singleTable.$field"];
00866 }
00867 $tableWhere .= '\''.$singleTable.'\',';
00868 }
00869 $tableWhere = rtrim($tableWhere, ',').')';
00870 } else {
00871 $table = strtoupper(trim( $this->tableName($table), '"' ));
00872 if (isset($this->mFieldInfoCache["$table.$field"])) {
00873 return $this->mFieldInfoCache["$table.$field"];
00874 }
00875 $tableWhere = '= \''.$table.'\'';
00876 }
00877
00878 $fieldInfoStmt = oci_parse( $this->mConn, 'SELECT * FROM wiki_field_info_full WHERE table_name '.$tableWhere.' and column_name = \''.$field.'\'' );
00879 if ( oci_execute( $fieldInfoStmt, OCI_DEFAULT ) === false ) {
00880 $e = oci_error( $fieldInfoStmt );
00881 $this->reportQueryError( $e['message'], $e['code'], 'fieldInfo QUERY', __METHOD__ );
00882 return false;
00883 }
00884 $res = new ORAResult( $this, $fieldInfoStmt );
00885 if ($res->numRows() == 0 ) {
00886 if (is_array($table)) {
00887 foreach($table as &$singleTable) {
00888 $this->mFieldInfoCache["$singleTable.$field"] = false;
00889 }
00890 } else {
00891 $this->mFieldInfoCache["$table.$field"] = false;
00892 }
00893 } else {
00894 $fieldInfoTemp = new ORAField( $res->fetchRow() );
00895 $table = $fieldInfoTemp->tableName();
00896 $this->mFieldInfoCache["$table.$field"] = $fieldInfoTemp;
00897 return $fieldInfoTemp;
00898 }
00899 }
00900
00901 function fieldInfo( $table, $field ) {
00902 if ( is_array( $table ) ) {
00903 throw new DBUnexpectedError( $this, 'Database::fieldInfo called with table array!' );
00904 }
00905 return $this->fieldInfoMulti ($table, $field);
00906 }
00907
00908 function fieldExists( $table, $field, $fname = 'DatabaseOracle::fieldExists' ) {
00909 return (bool)$this->fieldInfo( $table, $field, $fname );
00910 }
00911
00912 function begin( $fname = '' ) {
00913 $this->mTrxLevel = 1;
00914 }
00915
00916 function immediateCommit( $fname = '' ) {
00917 return true;
00918 }
00919
00920 function commit( $fname = '' ) {
00921 oci_commit( $this->mConn );
00922 $this->mTrxLevel = 0;
00923 }
00924
00925 /* Not even sure why this is used in the main codebase... */
00926 function limitResultForUpdate( $sql, $num ) {
00927 return $sql;
00928 }
00929
00930 /* defines must comply with ^define\s*([^\s=]*)\s*=\s?'\{\$([^\}]*)\}'; */
00931 function sourceStream( $fp, $lineCallback = false, $resultCallback = false ) {
00932 $cmd = '';
00933 $done = false;
00934 $dollarquote = false;
00935
00936 $replacements = array();
00937
00938 while ( ! feof( $fp ) ) {
00939 if ( $lineCallback ) {
00940 call_user_func( $lineCallback );
00941 }
00942 $line = trim( fgets( $fp, 1024 ) );
00943 $sl = strlen( $line ) - 1;
00944
00945 if ( $sl < 0 ) {
00946 continue;
00947 }
00948 if ( '-' == $line { 0 } && '-' == $line { 1 } ) {
00949 continue;
00950 }
00951
00952 // Allow dollar quoting for function declarations
00953 if ( substr( $line, 0, 8 ) == '/*$mw$*/' ) {
00954 if ( $dollarquote ) {
00955 $dollarquote = false;
00956 $done = true;
00957 } else {
00958 $dollarquote = true;
00959 }
00960 } elseif ( !$dollarquote ) {
00961 if ( ';' == $line { $sl } && ( $sl < 2 || ';' != $line { $sl - 1 } ) ) {
00962 $done = true;
00963 $line = substr( $line, 0, $sl );
00964 }
00965 }
00966
00967 if ( $cmd != '' ) {
00968 $cmd .= ' ';
00969 }
00970 $cmd .= "$line\n";
00971
00972 if ( $done ) {
00973 $cmd = str_replace( ';;', ";", $cmd );
00974 if ( strtolower( substr( $cmd, 0, 6 ) ) == 'define' ) {
00975 if ( preg_match( '/^define\s*([^\s=]*)\s*=\s*\'\{\$([^\}]*)\}\'/', $cmd, $defines ) ) {
00976 $replacements[$defines[2]] = $defines[1];
00977 }
00978 } else {
00979 foreach ( $replacements as $mwVar => $scVar ) {
00980 $cmd = str_replace( '&' . $scVar . '.', '{$' . $mwVar . '}', $cmd );
00981 }
00982
00983 $cmd = $this->replaceVars( $cmd );
00984 $res = $this->query( $cmd, __METHOD__ );
00985 if ( $resultCallback ) {
00986 call_user_func( $resultCallback, $res, $this );
00987 }
00988
00989 if ( false === $res ) {
00990 $err = $this->lastError();
00991 return "Query \"{$cmd}\" failed with error code \"$err\".\n";
00992 }
00993 }
00994
00995 $cmd = '';
00996 $done = false;
00997 }
00998 }
00999 return true;
01000 }
01001
01002 function setup_database() {
01003 global $wgVersion, $wgDBmwschema, $wgDBts2schema, $wgDBport, $wgDBuser;
01004
01005 $res = $this->sourceFile( "../maintenance/ora/tables.sql" );
01006 if ($res === true) {
01007 print " done.</li>\n";
01008 } else {
01009 print " <b>FAILED</b></li>\n";
01010 dieout( htmlspecialchars( $res ) );
01011 }
01012
01013
01014 echo "<li>Populating interwiki table</li>\n";
01015 $f = fopen( "../maintenance/interwiki.sql", 'r' );
01016 if ( $f == false ) {
01017 dieout( "Could not find the interwiki.sql file" );
01018 }
01019
01020
01021 $SQL = "INSERT INTO ".$this->tableName('interwiki')." (iw_prefix,iw_url,iw_local) VALUES ";
01022 while ( !feof( $f ) ) {
01023 $line = fgets( $f, 1024 );
01024 $matches = array();
01025 if ( !preg_match( '/^\s*(\(.+?),(\d)\)/', $line, $matches ) ) {
01026 continue;
01027 }
01028 $this->query( "$SQL $matches[1],$matches[2])" );
01029 }
01030
01031 echo "<li>Table interwiki successfully populated</li>\n";
01032 }
01033
01034 function strencode( $s ) {
01035 return str_replace( "'", "''", $s );
01036 }
01037
01038 function addQuotes( $s ) {
01039 global $wgLang;
01040 if ( isset( $wgLang->mLoaded ) && $wgLang->mLoaded ) {
01041 $s = $wgLang->checkTitleEncoding( $s );
01042 }
01043 return "'" . $this->strencode( $s ) . "'";
01044 }
01045
01046 function quote_ident( $s ) {
01047 return $s;
01048 }
01049
01050 function selectRow( $table, $vars, $conds, $fname = 'DatabaseOracle::selectRow', $options = array(), $join_conds = array() ) {
01051 global $wgLang;
01052
01053 $conds2 = array();
01054 $conds = ($conds != null && !is_array($conds)) ? array($conds) : $conds;
01055 foreach ( $conds as $col => $val ) {
01056 $col_info = $this->fieldInfoMulti( $table, $col );
01057 $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
01058 if ( $col_type == 'CLOB' ) {
01059 $conds2['TO_CHAR(' . $col . ')'] = $wgLang->checkTitleEncoding( $val );
01060 } elseif ( $col_type == 'VARCHAR2' && !mb_check_encoding( $val ) ) {
01061 $conds2[$col] = $wgLang->checkTitleEncoding( $val );
01062 } else {
01063 $conds2[$col] = $val;
01064 }
01065 }
01066
01067 return parent::selectRow( $table, $vars, $conds2, $fname, $options, $join_conds );
01068 }
01069
01080 function makeSelectOptions( $options ) {
01081 $preLimitTail = $postLimitTail = '';
01082 $startOpts = '';
01083
01084 $noKeyOptions = array();
01085 foreach ( $options as $key => $option ) {
01086 if ( is_numeric( $key ) ) {
01087 $noKeyOptions[$option] = true;
01088 }
01089 }
01090
01091 if ( isset( $options['GROUP BY'] ) ) {
01092 $preLimitTail .= " GROUP BY {$options['GROUP BY']}";
01093 }
01094 if ( isset( $options['ORDER BY'] ) ) {
01095 $preLimitTail .= " ORDER BY {$options['ORDER BY']}";
01096 }
01097
01098 # if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $tailOpts .= ' FOR UPDATE';
01099 # if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $tailOpts .= ' LOCK IN SHARE MODE';
01100 if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) {
01101 $startOpts .= 'DISTINCT';
01102 }
01103
01104 if ( isset( $options['USE INDEX'] ) && ! is_array( $options['USE INDEX'] ) ) {
01105 $useIndex = $this->useIndexClause( $options['USE INDEX'] );
01106 } else {
01107 $useIndex = '';
01108 }
01109
01110 return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
01111 }
01112
01113 public function delete( $table, $conds, $fname = 'DatabaseOracle::delete' ) {
01114 global $wgLang;
01115
01116 if ( $wgLang != null ) {
01117 $conds2 = array();
01118 $conds = ($conds != null && !is_array($conds)) ? array($conds) : $conds;
01119 foreach ( $conds as $col => $val ) {
01120 $col_info = $this->fieldInfoMulti( $table, $col );
01121 $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
01122 if ( $col_type == 'CLOB' ) {
01123 $conds2['TO_CHAR(' . $col . ')'] = $wgLang->checkTitleEncoding( $val );
01124 } else {
01125 if ( is_array( $val ) ) {
01126 $conds2[$col] = $val;
01127 foreach ( $conds2[$col] as &$val2 ) {
01128 $val2 = $wgLang->checkTitleEncoding( $val2 );
01129 }
01130 } else {
01131 $conds2[$col] = $wgLang->checkTitleEncoding( $val );
01132 }
01133 }
01134 }
01135
01136 return parent::delete( $table, $conds2, $fname );
01137 } else {
01138 return parent::delete( $table, $conds, $fname );
01139 }
01140 }
01141
01142 function bitNot( $field ) {
01143
01144 return 'BITNOT(' . $bitField . ')';
01145 }
01146
01147 function bitAnd( $fieldLeft, $fieldRight ) {
01148 return 'BITAND(' . $fieldLeft . ', ' . $fieldRight . ')';
01149 }
01150
01151 function bitOr( $fieldLeft, $fieldRight ) {
01152 return 'BITOR(' . $fieldLeft . ', ' . $fieldRight . ')';
01153 }
01154
01160 public function getLag() {
01161 # Not implemented for Oracle
01162 return 0;
01163 }
01164
01165 function setFakeSlaveLag( $lag ) { }
01166 function setFakeMaster( $enabled = true ) { }
01167
01168 function getDBname() {
01169 return $this->mDBname;
01170 }
01171
01172 function getServer() {
01173 return $this->mServer;
01174 }
01175
01176 public function replaceVars( $ins ) {
01177 $varnames = array( 'wgDBprefix' );
01178 if ( $this->mFlags & DBO_SYSDBA ) {
01179 $varnames[] = 'wgDBOracleDefTS';
01180 $varnames[] = 'wgDBOracleTempTS';
01181 }
01182
01183
01184 foreach ( $varnames as $var ) {
01185 if ( isset( $GLOBALS[$var] ) ) {
01186 $val = addslashes( $GLOBALS[$var] );
01187 $ins = str_replace( '{$' . $var . '}', $val, $ins );
01188 $ins = str_replace( '/*$' . $var . '*/`', '`' . $val, $ins );
01189 $ins = str_replace( '/*$' . $var . '*/', $val, $ins );
01190 }
01191 }
01192
01193 return parent::replaceVars( $ins );
01194 }
01195
01196 public function getSearchEngine() {
01197 return 'SearchOracle';
01198 }
01199 }