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 private $db;
00035
00036 function __construct(&$db, $stmt) {
00037 $this->db =& $db;
00038 if (($this->nrows = oci_fetch_all($stmt, $this->rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW | OCI_NUM)) === false) {
00039 $e = oci_error($stmt);
00040 $db->reportQueryError($e['message'], $e['code'], '', __FUNCTION__);
00041 return;
00042 }
00043
00044 $this->cursor = 0;
00045 $this->stmt = $stmt;
00046 }
00047
00048 function free() {
00049 oci_free_statement($this->stmt);
00050 }
00051
00052 function seek($row) {
00053 $this->cursor = min($row, $this->nrows);
00054 }
00055
00056 function numRows() {
00057 return $this->nrows;
00058 }
00059
00060 function numFields() {
00061 return oci_num_fields($this->stmt);
00062 }
00063
00064 function fetchObject() {
00065 if ($this->cursor >= $this->nrows)
00066 return false;
00067
00068 $row = $this->rows[$this->cursor++];
00069 $ret = new stdClass();
00070 foreach ($row as $k => $v) {
00071 $lc = strtolower(oci_field_name($this->stmt, $k + 1));
00072 $ret->$lc = $v;
00073 }
00074
00075 return $ret;
00076 }
00077
00078 function fetchAssoc() {
00079 if ($this->cursor >= $this->nrows)
00080 return false;
00081
00082 $row = $this->rows[$this->cursor++];
00083 $ret = array();
00084 foreach ($row as $k => $v) {
00085 $lc = strtolower(oci_field_name($this->stmt, $k + 1));
00086 $ret[$lc] = $v;
00087 $ret[$k] = $v;
00088 }
00089 return $ret;
00090 }
00091 }
00092
00096 class DatabaseOracle extends Database {
00097 var $mInsertId = NULL;
00098 var $mLastResult = NULL;
00099 var $numeric_version = NULL;
00100 var $lastResult = null;
00101 var $cursor = 0;
00102 var $mAffectedRows;
00103
00104 function DatabaseOracle($server = false, $user = false, $password = false, $dbName = false,
00105 $failFunction = false, $flags = 0 )
00106 {
00107
00108 global $wgOut;
00109 # Can't get a reference if it hasn't been set yet
00110 if ( !isset( $wgOut ) ) {
00111 $wgOut = NULL;
00112 }
00113 $this->mOut =& $wgOut;
00114 $this->mFailFunction = $failFunction;
00115 $this->mFlags = $flags;
00116 $this->open( $server, $user, $password, $dbName);
00117
00118 }
00119
00120 function cascadingDeletes() {
00121 return true;
00122 }
00123 function cleanupTriggers() {
00124 return true;
00125 }
00126 function strictIPs() {
00127 return true;
00128 }
00129 function realTimestamps() {
00130 return true;
00131 }
00132 function implicitGroupby() {
00133 return false;
00134 }
00135 function implicitOrderby() {
00136 return false;
00137 }
00138 function searchableIPs() {
00139 return true;
00140 }
00141
00142 static function newFromParams( $server = false, $user = false, $password = false, $dbName = false,
00143 $failFunction = false, $flags = 0)
00144 {
00145 return new DatabaseOracle( $server, $user, $password, $dbName, $failFunction, $flags );
00146 }
00147
00152 function open( $server, $user, $password, $dbName ) {
00153 if ( !function_exists( 'oci_connect' ) ) {
00154 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" );
00155 }
00156
00157 # Needed for proper UTF-8 functionality
00158 putenv("NLS_LANG=AMERICAN_AMERICA.AL32UTF8");
00159
00160 $this->close();
00161 $this->mServer = $server;
00162 $this->mUser = $user;
00163 $this->mPassword = $password;
00164 $this->mDBname = $dbName;
00165
00166 if (!strlen($user)) { ## e.g. the class is being loaded
00167 return;
00168 }
00169
00170 error_reporting( E_ALL );
00171 $this->mConn = oci_connect($user, $password, $dbName);
00172
00173 if ($this->mConn == false) {
00174 wfDebug("DB connection error\n");
00175 wfDebug("Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n");
00176 wfDebug($this->lastError()."\n");
00177 return false;
00178 }
00179
00180 $this->mOpened = true;
00181 return $this->mConn;
00182 }
00183
00188 function close() {
00189 $this->mOpened = false;
00190 if ( $this->mConn ) {
00191 return oci_close( $this->mConn );
00192 } else {
00193 return true;
00194 }
00195 }
00196
00197 function execFlags() {
00198 return $this->mTrxLevel ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS;
00199 }
00200
00201 function doQuery($sql) {
00202 wfDebug("SQL: [$sql]\n");
00203 if (!mb_check_encoding($sql)) {
00204 throw new MWException("SQL encoding is invalid");
00205 }
00206
00207 if (($this->mLastResult = $stmt = oci_parse($this->mConn, $sql)) === false) {
00208 $e = oci_error($this->mConn);
00209 $this->reportQueryError($e['message'], $e['code'], $sql, __FUNCTION__);
00210 }
00211
00212 if (oci_execute($stmt, $this->execFlags()) == false) {
00213 $e = oci_error($stmt);
00214 $this->reportQueryError($e['message'], $e['code'], $sql, __FUNCTION__);
00215 }
00216 if (oci_statement_type($stmt) == "SELECT")
00217 return new ORAResult($this, $stmt);
00218 else {
00219 $this->mAffectedRows = oci_num_rows($stmt);
00220 return true;
00221 }
00222 }
00223
00224 function queryIgnore($sql, $fname = '') {
00225 return $this->query($sql, $fname, true);
00226 }
00227
00228 function freeResult($res) {
00229 $res->free();
00230 }
00231
00232 function fetchObject($res) {
00233 return $res->fetchObject();
00234 }
00235
00236 function fetchRow($res) {
00237 return $res->fetchAssoc();
00238 }
00239
00240 function numRows($res) {
00241 return $res->numRows();
00242 }
00243
00244 function numFields($res) {
00245 return $res->numFields();
00246 }
00247
00248 function fieldName($stmt, $n) {
00249 return pg_field_name($stmt, $n);
00250 }
00251
00255 function insertId() {
00256 return $this->mInsertId;
00257 }
00258
00259 function dataSeek($res, $row) {
00260 $res->seek($row);
00261 }
00262
00263 function lastError() {
00264 if ($this->mConn === false)
00265 $e = oci_error();
00266 else
00267 $e = oci_error($this->mConn);
00268 return $e['message'];
00269 }
00270
00271 function lastErrno() {
00272 if ($this->mConn === false)
00273 $e = oci_error();
00274 else
00275 $e = oci_error($this->mConn);
00276 return $e['code'];
00277 }
00278
00279 function affectedRows() {
00280 return $this->mAffectedRows;
00281 }
00282
00287 function indexInfo( $table, $index, $fname = 'Database::indexExists' ) {
00288 return false;
00289 }
00290
00291 function indexUnique ($table, $index, $fname = 'Database::indexUnique' ) {
00292 return false;
00293 }
00294
00295 function insert( $table, $a, $fname = 'Database::insert', $options = array() ) {
00296 if (!is_array($options))
00297 $options = array($options);
00298
00299 #if (in_array('IGNORE', $options))
00300 # $oldIgnore = $this->ignoreErrors(true);
00301
00302 # IGNORE is performed using single-row inserts, ignoring errors in each
00303 # FIXME: need some way to distiguish between key collision and other types of error
00304
00305 if (!is_array(reset($a))) {
00306 $a = array($a);
00307 }
00308 foreach ($a as $row) {
00309 $this->insertOneRow($table, $row, $fname);
00310 }
00311
00312 $retVal = true;
00313
00314
00315
00316
00317 return $retVal;
00318 }
00319
00320 function insertOneRow($table, $row, $fname) {
00321
00322 $sql = "INSERT INTO " . $this->tableName($table) . " (" . join(',', array_keys($row)) . ')';
00323 $sql .= " VALUES (";
00324
00325
00326 $first = true;
00327 $returning = '';
00328 foreach ($row as $col => $val) {
00329 if (is_object($val)) {
00330 $what = "EMPTY_BLOB()";
00331 assert($returning === '');
00332 $returning = " RETURNING $col INTO :bval";
00333 $blobcol = $col;
00334 } else
00335 $what = ":$col";
00336
00337 if ($first)
00338 $sql .= "$what";
00339 else
00340 $sql.= ", $what";
00341 $first = false;
00342 }
00343 $sql .= ") $returning";
00344
00345 $stmt = oci_parse($this->mConn, $sql);
00346 foreach ($row as $col => $val) {
00347 if (!is_object($val)) {
00348 if (oci_bind_by_name($stmt, ":$col", $row[$col]) === false)
00349 $this->reportQueryError($this->lastErrno(), $this->lastError(), $sql, __METHOD__);
00350 }
00351 }
00352
00353 if (($bval = oci_new_descriptor($this->mConn, OCI_D_LOB)) === false) {
00354 $e = oci_error($stmt);
00355 throw new DBUnexpectedError($this, "Cannot create LOB descriptor: " . $e['message']);
00356 }
00357
00358 if (strlen($returning))
00359 oci_bind_by_name($stmt, ":bval", $bval, -1, SQLT_BLOB);
00360
00361 if (oci_execute($stmt, OCI_DEFAULT) === false) {
00362 $e = oci_error($stmt);
00363 $this->reportQueryError($e['message'], $e['code'], $sql, __METHOD__);
00364 }
00365 if (strlen($returning)) {
00366 $bval->save($row[$blobcol]->getData());
00367 $bval->free();
00368 }
00369 if (!$this->mTrxLevel)
00370 oci_commit($this->mConn);
00371
00372 oci_free_statement($stmt);
00373 }
00374
00375 function tableName( $name ) {
00376 # Replace reserved words with better ones
00377 switch( $name ) {
00378 case 'user':
00379 return 'mwuser';
00380 case 'text':
00381 return 'pagecontent';
00382 default:
00383 return $name;
00384 }
00385 }
00386
00390 function nextSequenceValue($seqName) {
00391 $res = $this->query("SELECT $seqName.nextval FROM dual");
00392 $row = $this->fetchRow($res);
00393 $this->mInsertId = $row[0];
00394 $this->freeResult($res);
00395 return $this->mInsertId;
00396 }
00397
00401 function useIndexClause($index) {
00402 return '';
00403 }
00404
00405 # REPLACE query wrapper
00406 # Oracle simulates this with a DELETE followed by INSERT
00407 # $row is the row to insert, an associative array
00408 # $uniqueIndexes is an array of indexes. Each element may be either a
00409 # field name or an array of field names
00410 #
00411 # It may be more efficient to leave off unique indexes which are unlikely to collide.
00412 # However if you do this, you run the risk of encountering errors which wouldn't have
00413 # occurred in MySQL
00414 function replace( $table, $uniqueIndexes, $rows, $fname = 'Database::replace' ) {
00415 $table = $this->tableName($table);
00416
00417 if (count($rows)==0) {
00418 return;
00419 }
00420
00421 # Single row case
00422 if (!is_array(reset($rows))) {
00423 $rows = array($rows);
00424 }
00425
00426 foreach( $rows as $row ) {
00427 # Delete rows which collide
00428 if ( $uniqueIndexes ) {
00429 $sql = "DELETE FROM $table WHERE ";
00430 $first = true;
00431 foreach ( $uniqueIndexes as $index ) {
00432 if ( $first ) {
00433 $first = false;
00434 $sql .= "(";
00435 } else {
00436 $sql .= ') OR (';
00437 }
00438 if ( is_array( $index ) ) {
00439 $first2 = true;
00440 foreach ( $index as $col ) {
00441 if ( $first2 ) {
00442 $first2 = false;
00443 } else {
00444 $sql .= ' AND ';
00445 }
00446 $sql .= $col.'=' . $this->addQuotes( $row[$col] );
00447 }
00448 } else {
00449 $sql .= $index.'=' . $this->addQuotes( $row[$index] );
00450 }
00451 }
00452 $sql .= ')';
00453 $this->query( $sql, $fname );
00454 }
00455
00456 # Now insert the row
00457 $sql = "INSERT INTO $table (" . $this->makeList( array_keys( $row ), LIST_NAMES ) .') VALUES (' .
00458 $this->makeList( $row, LIST_COMMA ) . ')';
00459 $this->query($sql, $fname);
00460 }
00461 }
00462
00463 # DELETE where the condition is a join
00464 function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin" ) {
00465 if ( !$conds ) {
00466 throw new DBUnexpectedError($this, 'Database::deleteJoin() called with empty $conds' );
00467 }
00468
00469 $delTable = $this->tableName( $delTable );
00470 $joinTable = $this->tableName( $joinTable );
00471 $sql = "DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable ";
00472 if ( $conds != '*' ) {
00473 $sql .= 'WHERE ' . $this->makeList( $conds, LIST_AND );
00474 }
00475 $sql .= ')';
00476
00477 $this->query( $sql, $fname );
00478 }
00479
00480 # Returns the size of a text field, or -1 for "unlimited"
00481 function textFieldSize( $table, $field ) {
00482 $table = $this->tableName( $table );
00483 $sql = "SELECT t.typname as ftype,a.atttypmod as size
00484 FROM pg_class c, pg_attribute a, pg_type t
00485 WHERE relname='$table' AND a.attrelid=c.oid AND
00486 a.atttypid=t.oid and a.attname='$field'";
00487 $res =$this->query($sql);
00488 $row=$this->fetchObject($res);
00489 if ($row->ftype=="varchar") {
00490 $size=$row->size-4;
00491 } else {
00492 $size=$row->size;
00493 }
00494 $this->freeResult( $res );
00495 return $size;
00496 }
00497
00498 function lowPriorityOption() {
00499 return '';
00500 }
00501
00502 function limitResult($sql, $limit, $offset) {
00503 if ($offset === false)
00504 $offset = 0;
00505 return "SELECT * FROM ($sql) WHERE rownum >= (1 + $offset) AND rownum < 1 + $limit + $offset";
00506 }
00507
00517 function conditional( $cond, $trueVal, $falseVal ) {
00518 return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
00519 }
00520
00521 function wasDeadlock() {
00522 return $this->lastErrno() == 'OCI-00060';
00523 }
00524
00525 function timestamp($ts = 0) {
00526 return wfTimestamp(TS_ORACLE, $ts);
00527 }
00528
00532 function aggregateValue ($valuedata,$valuename='value') {
00533 return $valuedata;
00534 }
00535
00536 function reportQueryError($error, $errno, $sql, $fname, $tempIgnore = false) {
00537 # Ignore errors during error handling to avoid infinite
00538 # recursion
00539 $ignore = $this->ignoreErrors(true);
00540 ++$this->mErrorCount;
00541
00542 if ($ignore || $tempIgnore) {
00543 echo "error ignored! query = [$sql]\n";
00544 wfDebug("SQL ERROR (ignored): $error\n");
00545 $this->ignoreErrors( $ignore );
00546 }
00547 else {
00548 echo "error!\n";
00549 $message = "A database error has occurred\n" .
00550 "Query: $sql\n" .
00551 "Function: $fname\n" .
00552 "Error: $errno $error\n";
00553 throw new DBUnexpectedError($this, $message);
00554 }
00555 }
00556
00560 function getSoftwareLink() {
00561 return "[http://www.oracle.com/ Oracle]";
00562 }
00563
00567 function getServerVersion() {
00568 return oci_server_version($this->mConn);
00569 }
00570
00574 function tableExists($table) {
00575 $etable= $this->addQuotes($table);
00576 $SQL = "SELECT 1 FROM user_tables WHERE table_name='$etable'";
00577 $res = $this->query($SQL);
00578 $count = $res ? oci_num_rows($res) : 0;
00579 if ($res)
00580 $this->freeResult($res);
00581 return $count;
00582 }
00583
00587 function fieldExists( $table, $field ) {
00588 return true;
00589 }
00590
00591 function fieldInfo( $table, $field ) {
00592 return false;
00593 }
00594
00595 function begin( $fname = '' ) {
00596 $this->mTrxLevel = 1;
00597 }
00598 function immediateCommit( $fname = '' ) {
00599 return true;
00600 }
00601 function commit( $fname = '' ) {
00602 oci_commit($this->mConn);
00603 $this->mTrxLevel = 0;
00604 }
00605
00606
00607 function limitResultForUpdate($sql, $num) {
00608 return $sql;
00609 }
00610
00611 function strencode($s) {
00612 return str_replace("'", "''", $s);
00613 }
00614
00615 function encodeBlob($b) {
00616 return new ORABlob($b);
00617 }
00618 function decodeBlob($b) {
00619 return $b;
00620 }
00621
00622 function addQuotes( $s ) {
00623 global $wgLang;
00624 $s = $wgLang->checkTitleEncoding($s);
00625 return "'" . $this->strencode($s) . "'";
00626 }
00627
00628 function quote_ident( $s ) {
00629 return $s;
00630 }
00631
00632
00633 function selectDB( $db ) {
00634 return true;
00635 }
00636
00647 function makeSelectOptions( $options ) {
00648 $preLimitTail = $postLimitTail = '';
00649 $startOpts = '';
00650
00651 $noKeyOptions = array();
00652 foreach ( $options as $key => $option ) {
00653 if ( is_numeric( $key ) ) {
00654 $noKeyOptions[$option] = true;
00655 }
00656 }
00657
00658 if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY {$options['GROUP BY']}";
00659 if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY {$options['ORDER BY']}";
00660
00661 if (isset($options['LIMIT'])) {
00662
00663
00664
00665 }
00666
00667 #if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $tailOpts .= ' FOR UPDATE';
00668 #if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $tailOpts .= ' LOCK IN SHARE MODE';
00669 if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
00670
00671 if ( isset( $options['USE INDEX'] ) && ! is_array( $options['USE INDEX'] ) ) {
00672 $useIndex = $this->useIndexClause( $options['USE INDEX'] );
00673 } else {
00674 $useIndex = '';
00675 }
00676
00677 return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
00678 }
00679
00680 public function setTimeout( $timeout ) {
00681
00682 }
00683
00684 function ping() {
00685 wfDebug( "Function ping() not written for DatabaseOracle.php yet");
00686 return true;
00687 }
00688
00694 public function getLag() {
00695 # Not implemented for Oracle
00696 return 0;
00697 }
00698
00699 function setFakeSlaveLag( $lag ) {}
00700 function setFakeMaster( $enabled = true ) {}
00701
00702 function getDBname() {
00703 return $this->mDBname;
00704 }
00705
00706 function getServer() {
00707 return $this->mServer;
00708 }
00709
00713 public function lock( $lockName, $method ) {
00714 return true;
00715 }
00716 public function unlock( $lockName, $method ) {
00717 return true;
00718 }
00719
00720 public function getSearchEngine() {
00721 return "SearchOracle";
00722 }
00723
00724 }