[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/db/ -> DatabaseOracle.php (source)

   1  <?php
   2  /**
   3   * This is the Oracle database abstraction layer.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   * @ingroup Database
  22   */
  23  
  24  /**
  25   * The oci8 extension is fairly weak and doesn't support oci_num_rows, among
  26   * other things. We use a wrapper class to handle that and other
  27   * Oracle-specific bits, like converting column names back to lowercase.
  28   * @ingroup Database
  29   */
  30  class ORAResult {
  31      private $rows;
  32      private $cursor;
  33      private $nrows;
  34  
  35      private $columns = array();
  36  
  37  	private function array_unique_md( $array_in ) {
  38          $array_out = array();
  39          $array_hashes = array();
  40  
  41          foreach ( $array_in as $item ) {
  42              $hash = md5( serialize( $item ) );
  43              if ( !isset( $array_hashes[$hash] ) ) {
  44                  $array_hashes[$hash] = $hash;
  45                  $array_out[] = $item;
  46              }
  47          }
  48  
  49          return $array_out;
  50      }
  51  
  52      /**
  53       * @param DatabaseBase $db
  54       * @param resource $stmt A valid OCI statement identifier
  55       * @param bool $unique
  56       */
  57  	function __construct( &$db, $stmt, $unique = false ) {
  58          $this->db =& $db;
  59  
  60          $this->nrows = oci_fetch_all( $stmt, $this->rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW | OCI_NUM );
  61          if ( $this->nrows === false ) {
  62              $e = oci_error( $stmt );
  63              $db->reportQueryError( $e['message'], $e['code'], '', __METHOD__ );
  64              $this->free();
  65  
  66              return;
  67          }
  68  
  69          if ( $unique ) {
  70              $this->rows = $this->array_unique_md( $this->rows );
  71              $this->nrows = count( $this->rows );
  72          }
  73  
  74          if ( $this->nrows > 0 ) {
  75              foreach ( $this->rows[0] as $k => $v ) {
  76                  $this->columns[$k] = strtolower( oci_field_name( $stmt, $k + 1 ) );
  77              }
  78          }
  79  
  80          $this->cursor = 0;
  81          oci_free_statement( $stmt );
  82      }
  83  
  84  	public function free() {
  85          unset( $this->db );
  86      }
  87  
  88  	public function seek( $row ) {
  89          $this->cursor = min( $row, $this->nrows );
  90      }
  91  
  92  	public function numRows() {
  93          return $this->nrows;
  94      }
  95  
  96  	public function numFields() {
  97          return count( $this->columns );
  98      }
  99  
 100  	public function fetchObject() {
 101          if ( $this->cursor >= $this->nrows ) {
 102              return false;
 103          }
 104          $row = $this->rows[$this->cursor++];
 105          $ret = new stdClass();
 106          foreach ( $row as $k => $v ) {
 107              $lc = $this->columns[$k];
 108              $ret->$lc = $v;
 109          }
 110  
 111          return $ret;
 112      }
 113  
 114  	public function fetchRow() {
 115          if ( $this->cursor >= $this->nrows ) {
 116              return false;
 117          }
 118  
 119          $row = $this->rows[$this->cursor++];
 120          $ret = array();
 121          foreach ( $row as $k => $v ) {
 122              $lc = $this->columns[$k];
 123              $ret[$lc] = $v;
 124              $ret[$k] = $v;
 125          }
 126  
 127          return $ret;
 128      }
 129  }
 130  
 131  /**
 132   * Utility class.
 133   * @ingroup Database
 134   */
 135  class ORAField implements Field {
 136      private $name, $tablename, $default, $max_length, $nullable,
 137          $is_pk, $is_unique, $is_multiple, $is_key, $type;
 138  
 139  	function __construct( $info ) {
 140          $this->name = $info['column_name'];
 141          $this->tablename = $info['table_name'];
 142          $this->default = $info['data_default'];
 143          $this->max_length = $info['data_length'];
 144          $this->nullable = $info['not_null'];
 145          $this->is_pk = isset( $info['prim'] ) && $info['prim'] == 1 ? 1 : 0;
 146          $this->is_unique = isset( $info['uniq'] ) && $info['uniq'] == 1 ? 1 : 0;
 147          $this->is_multiple = isset( $info['nonuniq'] ) && $info['nonuniq'] == 1 ? 1 : 0;
 148          $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
 149          $this->type = $info['data_type'];
 150      }
 151  
 152  	function name() {
 153          return $this->name;
 154      }
 155  
 156  	function tableName() {
 157          return $this->tablename;
 158      }
 159  
 160  	function defaultValue() {
 161          return $this->default;
 162      }
 163  
 164  	function maxLength() {
 165          return $this->max_length;
 166      }
 167  
 168  	function isNullable() {
 169          return $this->nullable;
 170      }
 171  
 172  	function isKey() {
 173          return $this->is_key;
 174      }
 175  
 176  	function isMultipleKey() {
 177          return $this->is_multiple;
 178      }
 179  
 180  	function type() {
 181          return $this->type;
 182      }
 183  }
 184  
 185  /**
 186   * @ingroup Database
 187   */
 188  class DatabaseOracle extends DatabaseBase {
 189      /** @var resource */
 190      protected $mLastResult = null;
 191  
 192      /** @var int The number of rows affected as an integer */
 193      protected $mAffectedRows;
 194  
 195      /** @var int */
 196      private $mInsertId = null;
 197  
 198      /** @var bool */
 199      private $ignoreDupValOnIndex = false;
 200  
 201      /** @var bool|array */
 202      private $sequenceData = null;
 203  
 204      /** @var string Character set for Oracle database */
 205      private $defaultCharset = 'AL32UTF8';
 206  
 207      /** @var array */
 208      private $mFieldInfoCache = array();
 209  
 210  	function __construct( $p = null ) {
 211          global $wgDBprefix;
 212  
 213          if ( !is_array( $p ) ) { // legacy calling pattern
 214              wfDeprecated( __METHOD__ . " method called without parameter array.", "1.22" );
 215              $args = func_get_args();
 216              $p = array(
 217                  'host' => isset( $args[0] ) ? $args[0] : false,
 218                  'user' => isset( $args[1] ) ? $args[1] : false,
 219                  'password' => isset( $args[2] ) ? $args[2] : false,
 220                  'dbname' => isset( $args[3] ) ? $args[3] : false,
 221                  'flags' => isset( $args[4] ) ? $args[4] : 0,
 222                  'tablePrefix' => isset( $args[5] ) ? $args[5] : 'get from global',
 223                  'schema' => 'get from global',
 224                  'foreign' => isset( $args[6] ) ? $args[6] : false
 225              );
 226          }
 227          if ( $p['tablePrefix'] == 'get from global' ) {
 228              $p['tablePrefix'] = $wgDBprefix;
 229          }
 230          $p['tablePrefix'] = strtoupper( $p['tablePrefix'] );
 231          parent::__construct( $p );
 232          wfRunHooks( 'DatabaseOraclePostInit', array( $this ) );
 233      }
 234  
 235  	function __destruct() {
 236          if ( $this->mOpened ) {
 237              wfSuppressWarnings();
 238              $this->close();
 239              wfRestoreWarnings();
 240          }
 241      }
 242  
 243  	function getType() {
 244          return 'oracle';
 245      }
 246  
 247  	function cascadingDeletes() {
 248          return true;
 249      }
 250  
 251  	function cleanupTriggers() {
 252          return true;
 253      }
 254  
 255  	function strictIPs() {
 256          return true;
 257      }
 258  
 259  	function realTimestamps() {
 260          return true;
 261      }
 262  
 263  	function implicitGroupby() {
 264          return false;
 265      }
 266  
 267  	function implicitOrderby() {
 268          return false;
 269      }
 270  
 271  	function searchableIPs() {
 272          return true;
 273      }
 274  
 275      /**
 276       * Usually aborts on failure
 277       * @param string $server
 278       * @param string $user
 279       * @param string $password
 280       * @param string $dbName
 281       * @throws DBConnectionError
 282       * @return DatabaseBase|null
 283       */
 284  	function open( $server, $user, $password, $dbName ) {
 285          global $wgDBOracleDRCP;
 286          if ( !function_exists( 'oci_connect' ) ) {
 287              throw new DBConnectionError(
 288                  $this,
 289                  "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n " .
 290                      "(Note: if you recently installed PHP, you may need to restart your webserver\n " .
 291                      "and database)\n" );
 292          }
 293  
 294          $this->close();
 295          $this->mUser = $user;
 296          $this->mPassword = $password;
 297          // changed internal variables functions
 298          // mServer now holds the TNS endpoint
 299          // mDBname is schema name if different from username
 300          if ( !$server ) {
 301              // backward compatibillity (server used to be null and TNS was supplied in dbname)
 302              $this->mServer = $dbName;
 303              $this->mDBname = $user;
 304          } else {
 305              $this->mServer = $server;
 306              if ( !$dbName ) {
 307                  $this->mDBname = $user;
 308              } else {
 309                  $this->mDBname = $dbName;
 310              }
 311          }
 312  
 313          if ( !strlen( $user ) ) { # e.g. the class is being loaded
 314              return null;
 315          }
 316  
 317          if ( $wgDBOracleDRCP ) {
 318              $this->setFlag( DBO_PERSISTENT );
 319          }
 320  
 321          $session_mode = $this->mFlags & DBO_SYSDBA ? OCI_SYSDBA : OCI_DEFAULT;
 322  
 323          wfSuppressWarnings();
 324          if ( $this->mFlags & DBO_PERSISTENT ) {
 325              $this->mConn = oci_pconnect(
 326                  $this->mUser,
 327                  $this->mPassword,
 328                  $this->mServer,
 329                  $this->defaultCharset,
 330                  $session_mode
 331              );
 332          } elseif ( $this->mFlags & DBO_DEFAULT ) {
 333              $this->mConn = oci_new_connect(
 334                  $this->mUser,
 335                  $this->mPassword,
 336                  $this->mServer,
 337                  $this->defaultCharset,
 338                  $session_mode
 339              );
 340          } else {
 341              $this->mConn = oci_connect(
 342                  $this->mUser,
 343                  $this->mPassword,
 344                  $this->mServer,
 345                  $this->defaultCharset,
 346                  $session_mode
 347              );
 348          }
 349          wfRestoreWarnings();
 350  
 351          if ( $this->mUser != $this->mDBname ) {
 352              //change current schema in session
 353              $this->selectDB( $this->mDBname );
 354          }
 355  
 356          if ( !$this->mConn ) {
 357              throw new DBConnectionError( $this, $this->lastError() );
 358          }
 359  
 360          $this->mOpened = true;
 361  
 362          # removed putenv calls because they interfere with the system globaly
 363          $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
 364          $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
 365          $this->doQuery( 'ALTER SESSION SET NLS_NUMERIC_CHARACTERS=\'.,\'' );
 366  
 367          return $this->mConn;
 368      }
 369  
 370      /**
 371       * Closes a database connection, if it is open
 372       * Returns success, true if already closed
 373       * @return bool
 374       */
 375  	protected function closeConnection() {
 376          return oci_close( $this->mConn );
 377      }
 378  
 379  	function execFlags() {
 380          return $this->mTrxLevel ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS;
 381      }
 382  
 383  	protected function doQuery( $sql ) {
 384          wfDebug( "SQL: [$sql]\n" );
 385          if ( !StringUtils::isUtf8( $sql ) ) {
 386              throw new MWException( "SQL encoding is invalid\n$sql" );
 387          }
 388  
 389          // handle some oracle specifics
 390          // remove AS column/table/subquery namings
 391          if ( !$this->getFlag( DBO_DDLMODE ) ) {
 392              $sql = preg_replace( '/ as /i', ' ', $sql );
 393          }
 394  
 395          // Oracle has issues with UNION clause if the statement includes LOB fields
 396          // So we do a UNION ALL and then filter the results array with array_unique
 397          $union_unique = ( preg_match( '/\/\* UNION_UNIQUE \*\/ /', $sql ) != 0 );
 398          // EXPLAIN syntax in Oracle is EXPLAIN PLAN FOR and it return nothing
 399          // you have to select data from plan table after explain
 400          $explain_id = MWTimestamp::getLocalInstance()->format( 'dmYHis' );
 401  
 402          $sql = preg_replace(
 403              '/^EXPLAIN /',
 404              'EXPLAIN PLAN SET STATEMENT_ID = \'' . $explain_id . '\' FOR',
 405              $sql,
 406              1,
 407              $explain_count
 408          );
 409  
 410          wfSuppressWarnings();
 411  
 412          if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) {
 413              $e = oci_error( $this->mConn );
 414              $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
 415  
 416              return false;
 417          }
 418  
 419          if ( !oci_execute( $stmt, $this->execFlags() ) ) {
 420              $e = oci_error( $stmt );
 421              if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) {
 422                  $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
 423  
 424                  return false;
 425              }
 426          }
 427  
 428          wfRestoreWarnings();
 429  
 430          if ( $explain_count > 0 ) {
 431              return $this->doQuery( 'SELECT id, cardinality "ROWS" FROM plan_table ' .
 432                  'WHERE statement_id = \'' . $explain_id . '\'' );
 433          } elseif ( oci_statement_type( $stmt ) == 'SELECT' ) {
 434              return new ORAResult( $this, $stmt, $union_unique );
 435          } else {
 436              $this->mAffectedRows = oci_num_rows( $stmt );
 437  
 438              return true;
 439          }
 440      }
 441  
 442  	function queryIgnore( $sql, $fname = '' ) {
 443          return $this->query( $sql, $fname, true );
 444      }
 445  
 446      /**
 447       * Frees resources associated with the LOB descriptor
 448       * @param ResultWrapper|resource $res
 449       */
 450  	function freeResult( $res ) {
 451          if ( $res instanceof ResultWrapper ) {
 452              $res = $res->result;
 453          }
 454  
 455          $res->free();
 456      }
 457  
 458      /**
 459       * @param ResultWrapper|stdClass $res
 460       * @return mixed
 461       */
 462  	function fetchObject( $res ) {
 463          if ( $res instanceof ResultWrapper ) {
 464              $res = $res->result;
 465          }
 466  
 467          return $res->fetchObject();
 468      }
 469  
 470  	function fetchRow( $res ) {
 471          if ( $res instanceof ResultWrapper ) {
 472              $res = $res->result;
 473          }
 474  
 475          return $res->fetchRow();
 476      }
 477  
 478  	function numRows( $res ) {
 479          if ( $res instanceof ResultWrapper ) {
 480              $res = $res->result;
 481          }
 482  
 483          return $res->numRows();
 484      }
 485  
 486  	function numFields( $res ) {
 487          if ( $res instanceof ResultWrapper ) {
 488              $res = $res->result;
 489          }
 490  
 491          return $res->numFields();
 492      }
 493  
 494  	function fieldName( $stmt, $n ) {
 495          return oci_field_name( $stmt, $n );
 496      }
 497  
 498      /**
 499       * This must be called after nextSequenceVal
 500       * @return null|int
 501       */
 502  	function insertId() {
 503          return $this->mInsertId;
 504      }
 505  
 506      /**
 507       * @param mixed $res
 508       * @param int $row
 509       */
 510  	function dataSeek( $res, $row ) {
 511          if ( $res instanceof ORAResult ) {
 512              $res->seek( $row );
 513          } else {
 514              $res->result->seek( $row );
 515          }
 516      }
 517  
 518  	function lastError() {
 519          if ( $this->mConn === false ) {
 520              $e = oci_error();
 521          } else {
 522              $e = oci_error( $this->mConn );
 523          }
 524  
 525          return $e['message'];
 526      }
 527  
 528  	function lastErrno() {
 529          if ( $this->mConn === false ) {
 530              $e = oci_error();
 531          } else {
 532              $e = oci_error( $this->mConn );
 533          }
 534  
 535          return $e['code'];
 536      }
 537  
 538  	function affectedRows() {
 539          return $this->mAffectedRows;
 540      }
 541  
 542      /**
 543       * Returns information about an index
 544       * If errors are explicitly ignored, returns NULL on failure
 545       * @param string $table
 546       * @param string $index
 547       * @param string $fname
 548       * @return bool
 549       */
 550  	function indexInfo( $table, $index, $fname = __METHOD__ ) {
 551          return false;
 552      }
 553  
 554  	function indexUnique( $table, $index, $fname = __METHOD__ ) {
 555          return false;
 556      }
 557  
 558  	function insert( $table, $a, $fname = __METHOD__, $options = array() ) {
 559          if ( !count( $a ) ) {
 560              return true;
 561          }
 562  
 563          if ( !is_array( $options ) ) {
 564              $options = array( $options );
 565          }
 566  
 567          if ( in_array( 'IGNORE', $options ) ) {
 568              $this->ignoreDupValOnIndex = true;
 569          }
 570  
 571          if ( !is_array( reset( $a ) ) ) {
 572              $a = array( $a );
 573          }
 574  
 575          foreach ( $a as &$row ) {
 576              $this->insertOneRow( $table, $row, $fname );
 577          }
 578          $retVal = true;
 579  
 580          if ( in_array( 'IGNORE', $options ) ) {
 581              $this->ignoreDupValOnIndex = false;
 582          }
 583  
 584          return $retVal;
 585      }
 586  
 587  	private function fieldBindStatement( $table, $col, &$val, $includeCol = false ) {
 588          $col_info = $this->fieldInfoMulti( $table, $col );
 589          $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
 590  
 591          $bind = '';
 592          if ( is_numeric( $col ) ) {
 593              $bind = $val;
 594              $val = null;
 595  
 596              return $bind;
 597          } elseif ( $includeCol ) {
 598              $bind = "$col = ";
 599          }
 600  
 601          if ( $val == '' && $val !== 0 && $col_type != 'BLOB' && $col_type != 'CLOB' ) {
 602              $val = null;
 603          }
 604  
 605          if ( $val === 'NULL' ) {
 606              $val = null;
 607          }
 608  
 609          if ( $val === null ) {
 610              if ( $col_info != false && $col_info->isNullable() == 0 && $col_info->defaultValue() != null ) {
 611                  $bind .= 'DEFAULT';
 612              } else {
 613                  $bind .= 'NULL';
 614              }
 615          } else {
 616              $bind .= ':' . $col;
 617          }
 618  
 619          return $bind;
 620      }
 621  
 622      /**
 623       * @param string $table
 624       * @param array $row
 625       * @param string $fname
 626       * @return bool
 627       * @throws DBUnexpectedError
 628       */
 629  	private function insertOneRow( $table, $row, $fname ) {
 630          global $wgContLang;
 631  
 632          $table = $this->tableName( $table );
 633          // "INSERT INTO tables (a, b, c)"
 634          $sql = "INSERT INTO " . $table . " (" . join( ',', array_keys( $row ) ) . ')';
 635          $sql .= " VALUES (";
 636  
 637          // for each value, append ":key"
 638          $first = true;
 639          foreach ( $row as $col => &$val ) {
 640              if ( !$first ) {
 641                  $sql .= ', ';
 642              } else {
 643                  $first = false;
 644              }
 645              if ( $this->isQuotedIdentifier( $val ) ) {
 646                  $sql .= $this->removeIdentifierQuotes( $val );
 647                  unset( $row[$col] );
 648              } else {
 649                  $sql .= $this->fieldBindStatement( $table, $col, $val );
 650              }
 651          }
 652          $sql .= ')';
 653  
 654          if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) {
 655              $e = oci_error( $this->mConn );
 656              $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
 657  
 658              return false;
 659          }
 660          foreach ( $row as $col => &$val ) {
 661              $col_info = $this->fieldInfoMulti( $table, $col );
 662              $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
 663  
 664              if ( $val === null ) {
 665                  // do nothing ... null was inserted in statement creation
 666              } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) {
 667                  if ( is_object( $val ) ) {
 668                      $val = $val->fetch();
 669                  }
 670  
 671                  // backward compatibility
 672                  if ( preg_match( '/^timestamp.*/i', $col_type ) == 1 && strtolower( $val ) == 'infinity' ) {
 673                      $val = $this->getInfinity();
 674                  }
 675  
 676                  $val = ( $wgContLang != null ) ? $wgContLang->checkTitleEncoding( $val ) : $val;
 677                  if ( oci_bind_by_name( $stmt, ":$col", $val, -1, SQLT_CHR ) === false ) {
 678                      $e = oci_error( $stmt );
 679                      $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
 680  
 681                      return false;
 682                  }
 683              } else {
 684                  /** @var OCI_Lob[] $lob */
 685                  if ( ( $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB ) ) === false ) {
 686                      $e = oci_error( $stmt );
 687                      throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] );
 688                  }
 689  
 690                  if ( is_object( $val ) ) {
 691                      $val = $val->fetch();
 692                  }
 693  
 694                  if ( $col_type == 'BLOB' ) {
 695                      $lob[$col]->writeTemporary( $val, OCI_TEMP_BLOB );
 696                      oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_BLOB );
 697                  } else {
 698                      $lob[$col]->writeTemporary( $val, OCI_TEMP_CLOB );
 699                      oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_CLOB );
 700                  }
 701              }
 702          }
 703  
 704          wfSuppressWarnings();
 705  
 706          if ( oci_execute( $stmt, $this->execFlags() ) === false ) {
 707              $e = oci_error( $stmt );
 708              if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) {
 709                  $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
 710  
 711                  return false;
 712              } else {
 713                  $this->mAffectedRows = oci_num_rows( $stmt );
 714              }
 715          } else {
 716              $this->mAffectedRows = oci_num_rows( $stmt );
 717          }
 718  
 719          wfRestoreWarnings();
 720  
 721          if ( isset( $lob ) ) {
 722              foreach ( $lob as $lob_v ) {
 723                  $lob_v->free();
 724              }
 725          }
 726  
 727          if ( !$this->mTrxLevel ) {
 728              oci_commit( $this->mConn );
 729          }
 730  
 731          return oci_free_statement( $stmt );
 732      }
 733  
 734  	function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
 735          $insertOptions = array(), $selectOptions = array()
 736      ) {
 737          $destTable = $this->tableName( $destTable );
 738          if ( !is_array( $selectOptions ) ) {
 739              $selectOptions = array( $selectOptions );
 740          }
 741          list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
 742          if ( is_array( $srcTable ) ) {
 743              $srcTable = implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) );
 744          } else {
 745              $srcTable = $this->tableName( $srcTable );
 746          }
 747  
 748          if ( ( $sequenceData = $this->getSequenceData( $destTable ) ) !== false &&
 749              !isset( $varMap[$sequenceData['column']] )
 750          ) {
 751              $varMap[$sequenceData['column']] = 'GET_SEQUENCE_VALUE(\'' . $sequenceData['sequence'] . '\')';
 752          }
 753  
 754          // count-alias subselect fields to avoid abigious definition errors
 755          $i = 0;
 756          foreach ( $varMap as &$val ) {
 757              $val = $val . ' field' . ( $i++ );
 758          }
 759  
 760          $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
 761              " SELECT $startOpts " . implode( ',', $varMap ) .
 762              " FROM $srcTable $useIndex ";
 763          if ( $conds != '*' ) {
 764              $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
 765          }
 766          $sql .= " $tailOpts";
 767  
 768          if ( in_array( 'IGNORE', $insertOptions ) ) {
 769              $this->ignoreDupValOnIndex = true;
 770          }
 771  
 772          $retval = $this->query( $sql, $fname );
 773  
 774          if ( in_array( 'IGNORE', $insertOptions ) ) {
 775              $this->ignoreDupValOnIndex = false;
 776          }
 777  
 778          return $retval;
 779      }
 780  
 781  	public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
 782          $fname = __METHOD__
 783      ) {
 784          if ( !count( $rows ) ) {
 785              return true; // nothing to do
 786          }
 787  
 788          if ( !is_array( reset( $rows ) ) ) {
 789              $rows = array( $rows );
 790          }
 791  
 792          $sequenceData = $this->getSequenceData( $table );
 793          if ( $sequenceData !== false ) {
 794              // add sequence column to each list of columns, when not set
 795              foreach ( $rows as &$row ) {
 796                  if ( !isset( $row[$sequenceData['column']] ) ) {
 797                      $row[$sequenceData['column']] =
 798                          $this->addIdentifierQuotes( 'GET_SEQUENCE_VALUE(\'' .
 799                              $sequenceData['sequence'] . '\')' );
 800                  }
 801              }
 802          }
 803  
 804          return parent::upsert( $table, $rows, $uniqueIndexes, $set, $fname );
 805      }
 806  
 807  	function tableName( $name, $format = 'quoted' ) {
 808          /*
 809          Replace reserved words with better ones
 810          Using uppercase because that's the only way Oracle can handle
 811          quoted tablenames
 812          */
 813          switch ( $name ) {
 814              case 'user':
 815                  $name = 'MWUSER';
 816                  break;
 817              case 'text':
 818                  $name = 'PAGECONTENT';
 819                  break;
 820          }
 821  
 822          return strtoupper( parent::tableName( $name, $format ) );
 823      }
 824  
 825  	function tableNameInternal( $name ) {
 826          $name = $this->tableName( $name );
 827  
 828          return preg_replace( '/.*\.(.*)/', '$1', $name );
 829      }
 830  
 831      /**
 832       * Return the next in a sequence, save the value for retrieval via insertId()
 833       *
 834       * @param string $seqName
 835       * @return null|int
 836       */
 837  	function nextSequenceValue( $seqName ) {
 838          $res = $this->query( "SELECT $seqName.nextval FROM dual" );
 839          $row = $this->fetchRow( $res );
 840          $this->mInsertId = $row[0];
 841  
 842          return $this->mInsertId;
 843      }
 844  
 845      /**
 846       * Return sequence_name if table has a sequence
 847       *
 848       * @param string $table
 849       * @return bool
 850       */
 851  	private function getSequenceData( $table ) {
 852          if ( $this->sequenceData == null ) {
 853              $result = $this->doQuery( "SELECT lower(asq.sequence_name),
 854                  lower(atc.table_name),
 855                  lower(atc.column_name)
 856              FROM all_sequences asq, all_tab_columns atc
 857              WHERE decode(
 858                      atc.table_name,
 859                      '{$this->mTablePrefix}MWUSER',
 860                      '{$this->mTablePrefix}USER',
 861                      atc.table_name
 862                  ) || '_' ||
 863                  atc.column_name || '_SEQ' = '{$this->mTablePrefix}' || asq.sequence_name
 864                  AND asq.sequence_owner = upper('{$this->mDBname}')
 865                  AND atc.owner = upper('{$this->mDBname}')" );
 866  
 867              while ( ( $row = $result->fetchRow() ) !== false ) {
 868                  $this->sequenceData[$row[1]] = array(
 869                      'sequence' => $row[0],
 870                      'column' => $row[2]
 871                  );
 872              }
 873          }
 874          $table = strtolower( $this->removeIdentifierQuotes( $this->tableName( $table ) ) );
 875  
 876          return ( isset( $this->sequenceData[$table] ) ) ? $this->sequenceData[$table] : false;
 877      }
 878  
 879      /**
 880       * Returns the size of a text field, or -1 for "unlimited"
 881       *
 882       * @param string $table
 883       * @param string $field
 884       * @return mixed
 885       */
 886  	function textFieldSize( $table, $field ) {
 887          $fieldInfoData = $this->fieldInfo( $table, $field );
 888  
 889          return $fieldInfoData->maxLength();
 890      }
 891  
 892  	function limitResult( $sql, $limit, $offset = false ) {
 893          if ( $offset === false ) {
 894              $offset = 0;
 895          }
 896  
 897          return "SELECT * FROM ($sql) WHERE rownum >= (1 + $offset) AND rownum < (1 + $limit + $offset)";
 898      }
 899  
 900  	function encodeBlob( $b ) {
 901          return new Blob( $b );
 902      }
 903  
 904  	function decodeBlob( $b ) {
 905          if ( $b instanceof Blob ) {
 906              $b = $b->fetch();
 907          }
 908  
 909          return $b;
 910      }
 911  
 912  	function unionQueries( $sqls, $all ) {
 913          $glue = ' UNION ALL ';
 914  
 915          return 'SELECT * ' . ( $all ? '' : '/* UNION_UNIQUE */ ' ) .
 916              'FROM (' . implode( $glue, $sqls ) . ')';
 917      }
 918  
 919  	function wasDeadlock() {
 920          return $this->lastErrno() == 'OCI-00060';
 921      }
 922  
 923  	function duplicateTableStructure( $oldName, $newName, $temporary = false,
 924          $fname = __METHOD__
 925      ) {
 926          $temporary = $temporary ? 'TRUE' : 'FALSE';
 927  
 928          $newName = strtoupper( $newName );
 929          $oldName = strtoupper( $oldName );
 930  
 931          $tabName = substr( $newName, strlen( $this->mTablePrefix ) );
 932          $oldPrefix = substr( $oldName, 0, strlen( $oldName ) - strlen( $tabName ) );
 933          $newPrefix = strtoupper( $this->mTablePrefix );
 934  
 935          return $this->doQuery( "BEGIN DUPLICATE_TABLE( '$tabName', " .
 936              "'$oldPrefix', '$newPrefix', $temporary ); END;" );
 937      }
 938  
 939  	function listTables( $prefix = null, $fname = __METHOD__ ) {
 940          $listWhere = '';
 941          if ( !empty( $prefix ) ) {
 942              $listWhere = ' AND table_name LIKE \'' . strtoupper( $prefix ) . '%\'';
 943          }
 944  
 945          $owner = strtoupper( $this->mDBname );
 946          $result = $this->doQuery( "SELECT table_name FROM all_tables " .
 947              "WHERE owner='$owner' AND table_name NOT LIKE '%!_IDX\$_' ESCAPE '!' $listWhere" );
 948  
 949          // dirty code ... i know
 950          $endArray = array();
 951          $endArray[] = strtoupper( $prefix . 'MWUSER' );
 952          $endArray[] = strtoupper( $prefix . 'PAGE' );
 953          $endArray[] = strtoupper( $prefix . 'IMAGE' );
 954          $fixedOrderTabs = $endArray;
 955          while ( ( $row = $result->fetchRow() ) !== false ) {
 956              if ( !in_array( $row['table_name'], $fixedOrderTabs ) ) {
 957                  $endArray[] = $row['table_name'];
 958              }
 959          }
 960  
 961          return $endArray;
 962      }
 963  
 964  	public function dropTable( $tableName, $fName = __METHOD__ ) {
 965          $tableName = $this->tableName( $tableName );
 966          if ( !$this->tableExists( $tableName ) ) {
 967              return false;
 968          }
 969  
 970          return $this->doQuery( "DROP TABLE $tableName CASCADE CONSTRAINTS PURGE" );
 971      }
 972  
 973  	function timestamp( $ts = 0 ) {
 974          return wfTimestamp( TS_ORACLE, $ts );
 975      }
 976  
 977      /**
 978       * Return aggregated value function call
 979       *
 980       * @param array $valuedata
 981       * @param string $valuename
 982       * @return mixed
 983       */
 984  	public function aggregateValue( $valuedata, $valuename = 'value' ) {
 985          return $valuedata;
 986      }
 987  
 988  	function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
 989          # Ignore errors during error handling to avoid infinite
 990          # recursion
 991          $ignore = $this->ignoreErrors( true );
 992          ++$this->mErrorCount;
 993  
 994          if ( $ignore || $tempIgnore ) {
 995              wfDebug( "SQL ERROR (ignored): $error\n" );
 996              $this->ignoreErrors( $ignore );
 997          } else {
 998              throw new DBQueryError( $this, $error, $errno, $sql, $fname );
 999          }
1000      }
1001  
1002      /**
1003       * @return string Wikitext of a link to the server software's web site
1004       */
1005  	public function getSoftwareLink() {
1006          return '[{{int:version-db-oracle-url}} Oracle]';
1007      }
1008  
1009      /**
1010       * @return string Version information from the database
1011       */
1012  	function getServerVersion() {
1013          //better version number, fallback on driver
1014          $rset = $this->doQuery(
1015              'SELECT version FROM product_component_version ' .
1016                  'WHERE UPPER(product) LIKE \'ORACLE DATABASE%\''
1017          );
1018          if ( !( $row = $rset->fetchRow() ) ) {
1019              return oci_server_version( $this->mConn );
1020          }
1021  
1022          return $row['version'];
1023      }
1024  
1025      /**
1026       * Query whether a given index exists
1027       * @param string $table
1028       * @param string $index
1029       * @param string $fname
1030       * @return bool
1031       */
1032  	function indexExists( $table, $index, $fname = __METHOD__ ) {
1033          $table = $this->tableName( $table );
1034          $table = strtoupper( $this->removeIdentifierQuotes( $table ) );
1035          $index = strtoupper( $index );
1036          $owner = strtoupper( $this->mDBname );
1037          $sql = "SELECT 1 FROM all_indexes WHERE owner='$owner' AND index_name='{$table}_{$index}'";
1038          $res = $this->doQuery( $sql );
1039          if ( $res ) {
1040              $count = $res->numRows();
1041              $res->free();
1042          } else {
1043              $count = 0;
1044          }
1045  
1046          return $count != 0;
1047      }
1048  
1049      /**
1050       * Query whether a given table exists (in the given schema, or the default mw one if not given)
1051       * @param string $table
1052       * @param string $fname
1053       * @return bool
1054       */
1055  	function tableExists( $table, $fname = __METHOD__ ) {
1056          $table = $this->tableName( $table );
1057          $table = $this->addQuotes( strtoupper( $this->removeIdentifierQuotes( $table ) ) );
1058          $owner = $this->addQuotes( strtoupper( $this->mDBname ) );
1059          $sql = "SELECT 1 FROM all_tables WHERE owner=$owner AND table_name=$table";
1060          $res = $this->doQuery( $sql );
1061          if ( $res && $res->numRows() > 0 ) {
1062              $exists = true;
1063          } else {
1064              $exists = false;
1065          }
1066  
1067          $res->free();
1068  
1069          return $exists;
1070      }
1071  
1072      /**
1073       * Function translates mysql_fetch_field() functionality on ORACLE.
1074       * Caching is present for reducing query time.
1075       * For internal calls. Use fieldInfo for normal usage.
1076       * Returns false if the field doesn't exist
1077       *
1078       * @param array|string $table
1079       * @param string $field
1080       * @return ORAField|ORAResult
1081       */
1082  	private function fieldInfoMulti( $table, $field ) {
1083          $field = strtoupper( $field );
1084          if ( is_array( $table ) ) {
1085              $table = array_map( array( &$this, 'tableNameInternal' ), $table );
1086              $tableWhere = 'IN (';
1087              foreach ( $table as &$singleTable ) {
1088                  $singleTable = $this->removeIdentifierQuotes( $singleTable );
1089                  if ( isset( $this->mFieldInfoCache["$singleTable.$field"] ) ) {
1090                      return $this->mFieldInfoCache["$singleTable.$field"];
1091                  }
1092                  $tableWhere .= '\'' . $singleTable . '\',';
1093              }
1094              $tableWhere = rtrim( $tableWhere, ',' ) . ')';
1095          } else {
1096              $table = $this->removeIdentifierQuotes( $this->tableNameInternal( $table ) );
1097              if ( isset( $this->mFieldInfoCache["$table.$field"] ) ) {
1098                  return $this->mFieldInfoCache["$table.$field"];
1099              }
1100              $tableWhere = '= \'' . $table . '\'';
1101          }
1102  
1103          $fieldInfoStmt = oci_parse(
1104              $this->mConn,
1105              'SELECT * FROM wiki_field_info_full WHERE table_name ' .
1106                  $tableWhere . ' and column_name = \'' . $field . '\''
1107          );
1108          if ( oci_execute( $fieldInfoStmt, $this->execFlags() ) === false ) {
1109              $e = oci_error( $fieldInfoStmt );
1110              $this->reportQueryError( $e['message'], $e['code'], 'fieldInfo QUERY', __METHOD__ );
1111  
1112              return false;
1113          }
1114          $res = new ORAResult( $this, $fieldInfoStmt );
1115          if ( $res->numRows() == 0 ) {
1116              if ( is_array( $table ) ) {
1117                  foreach ( $table as &$singleTable ) {
1118                      $this->mFieldInfoCache["$singleTable.$field"] = false;
1119                  }
1120              } else {
1121                  $this->mFieldInfoCache["$table.$field"] = false;
1122              }
1123              $fieldInfoTemp = null;
1124          } else {
1125              $fieldInfoTemp = new ORAField( $res->fetchRow() );
1126              $table = $fieldInfoTemp->tableName();
1127              $this->mFieldInfoCache["$table.$field"] = $fieldInfoTemp;
1128          }
1129          $res->free();
1130  
1131          return $fieldInfoTemp;
1132      }
1133  
1134      /**
1135       * @throws DBUnexpectedError
1136       * @param string $table
1137       * @param string $field
1138       * @return ORAField
1139       */
1140  	function fieldInfo( $table, $field ) {
1141          if ( is_array( $table ) ) {
1142              throw new DBUnexpectedError( $this, 'DatabaseOracle::fieldInfo called with table array!' );
1143          }
1144  
1145          return $this->fieldInfoMulti( $table, $field );
1146      }
1147  
1148  	protected function doBegin( $fname = __METHOD__ ) {
1149          $this->mTrxLevel = 1;
1150          $this->doQuery( 'SET CONSTRAINTS ALL DEFERRED' );
1151      }
1152  
1153  	protected function doCommit( $fname = __METHOD__ ) {
1154          if ( $this->mTrxLevel ) {
1155              $ret = oci_commit( $this->mConn );
1156              if ( !$ret ) {
1157                  throw new DBUnexpectedError( $this, $this->lastError() );
1158              }
1159              $this->mTrxLevel = 0;
1160              $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' );
1161          }
1162      }
1163  
1164  	protected function doRollback( $fname = __METHOD__ ) {
1165          if ( $this->mTrxLevel ) {
1166              oci_rollback( $this->mConn );
1167              $this->mTrxLevel = 0;
1168              $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' );
1169          }
1170      }
1171  
1172      /**
1173       * defines must comply with ^define\s*([^\s=]*)\s*=\s?'\{\$([^\}]*)\}';
1174       *
1175       * @param resource $fp
1176       * @param bool|string $lineCallback
1177       * @param bool|callable $resultCallback
1178       * @param string $fname
1179       * @param bool|callable $inputCallback
1180       * @return bool|string
1181       */
1182  	function sourceStream( $fp, $lineCallback = false, $resultCallback = false,
1183          $fname = __METHOD__, $inputCallback = false ) {
1184          $cmd = '';
1185          $done = false;
1186          $dollarquote = false;
1187  
1188          $replacements = array();
1189  
1190          while ( !feof( $fp ) ) {
1191              if ( $lineCallback ) {
1192                  call_user_func( $lineCallback );
1193              }
1194              $line = trim( fgets( $fp, 1024 ) );
1195              $sl = strlen( $line ) - 1;
1196  
1197              if ( $sl < 0 ) {
1198                  continue;
1199              }
1200              if ( '-' == $line[0] && '-' == $line[1] ) {
1201                  continue;
1202              }
1203  
1204              // Allow dollar quoting for function declarations
1205              if ( substr( $line, 0, 8 ) == '/*$mw$*/' ) {
1206                  if ( $dollarquote ) {
1207                      $dollarquote = false;
1208                      $line = str_replace( '/*$mw$*/', '', $line ); // remove dollarquotes
1209                      $done = true;
1210                  } else {
1211                      $dollarquote = true;
1212                  }
1213              } elseif ( !$dollarquote ) {
1214                  if ( ';' == $line[$sl] && ( $sl < 2 || ';' != $line[$sl - 1] ) ) {
1215                      $done = true;
1216                      $line = substr( $line, 0, $sl );
1217                  }
1218              }
1219  
1220              if ( $cmd != '' ) {
1221                  $cmd .= ' ';
1222              }
1223              $cmd .= "$line\n";
1224  
1225              if ( $done ) {
1226                  $cmd = str_replace( ';;', ";", $cmd );
1227                  if ( strtolower( substr( $cmd, 0, 6 ) ) == 'define' ) {
1228                      if ( preg_match( '/^define\s*([^\s=]*)\s*=\s*\'\{\$([^\}]*)\}\'/', $cmd, $defines ) ) {
1229                          $replacements[$defines[2]] = $defines[1];
1230                      }
1231                  } else {
1232                      foreach ( $replacements as $mwVar => $scVar ) {
1233                          $cmd = str_replace( '&' . $scVar . '.', '`{$' . $mwVar . '}`', $cmd );
1234                      }
1235  
1236                      $cmd = $this->replaceVars( $cmd );
1237                      if ( $inputCallback ) {
1238                          call_user_func( $inputCallback, $cmd );
1239                      }
1240                      $res = $this->doQuery( $cmd );
1241                      if ( $resultCallback ) {
1242                          call_user_func( $resultCallback, $res, $this );
1243                      }
1244  
1245                      if ( false === $res ) {
1246                          $err = $this->lastError();
1247  
1248                          return "Query \"{$cmd}\" failed with error code \"$err\".\n";
1249                      }
1250                  }
1251  
1252                  $cmd = '';
1253                  $done = false;
1254              }
1255          }
1256  
1257          return true;
1258      }
1259  
1260  	function selectDB( $db ) {
1261          $this->mDBname = $db;
1262          if ( $db == null || $db == $this->mUser ) {
1263              return true;
1264          }
1265          $sql = 'ALTER SESSION SET CURRENT_SCHEMA=' . strtoupper( $db );
1266          $stmt = oci_parse( $this->mConn, $sql );
1267          wfSuppressWarnings();
1268          $success = oci_execute( $stmt );
1269          wfRestoreWarnings();
1270          if ( !$success ) {
1271              $e = oci_error( $stmt );
1272              if ( $e['code'] != '1435' ) {
1273                  $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
1274              }
1275  
1276              return false;
1277          }
1278  
1279          return true;
1280      }
1281  
1282  	function strencode( $s ) {
1283          return str_replace( "'", "''", $s );
1284      }
1285  
1286  	function addQuotes( $s ) {
1287          global $wgContLang;
1288          if ( isset( $wgContLang->mLoaded ) && $wgContLang->mLoaded ) {
1289              $s = $wgContLang->checkTitleEncoding( $s );
1290          }
1291  
1292          return "'" . $this->strencode( $s ) . "'";
1293      }
1294  
1295  	public function addIdentifierQuotes( $s ) {
1296          if ( !$this->getFlag( DBO_DDLMODE ) ) {
1297              $s = '/*Q*/' . $s;
1298          }
1299  
1300          return $s;
1301      }
1302  
1303  	public function removeIdentifierQuotes( $s ) {
1304          return strpos( $s, '/*Q*/' ) === false ? $s : substr( $s, 5 );
1305      }
1306  
1307  	public function isQuotedIdentifier( $s ) {
1308          return strpos( $s, '/*Q*/' ) !== false;
1309      }
1310  
1311  	private function wrapFieldForWhere( $table, &$col, &$val ) {
1312          global $wgContLang;
1313  
1314          $col_info = $this->fieldInfoMulti( $table, $col );
1315          $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
1316          if ( $col_type == 'CLOB' ) {
1317              $col = 'TO_CHAR(' . $col . ')';
1318              $val = $wgContLang->checkTitleEncoding( $val );
1319          } elseif ( $col_type == 'VARCHAR2' ) {
1320              $val = $wgContLang->checkTitleEncoding( $val );
1321          }
1322      }
1323  
1324  	private function wrapConditionsForWhere( $table, $conds, $parentCol = null ) {
1325          $conds2 = array();
1326          foreach ( $conds as $col => $val ) {
1327              if ( is_array( $val ) ) {
1328                  $conds2[$col] = $this->wrapConditionsForWhere( $table, $val, $col );
1329              } else {
1330                  if ( is_numeric( $col ) && $parentCol != null ) {
1331                      $this->wrapFieldForWhere( $table, $parentCol, $val );
1332                  } else {
1333                      $this->wrapFieldForWhere( $table, $col, $val );
1334                  }
1335                  $conds2[$col] = $val;
1336              }
1337          }
1338  
1339          return $conds2;
1340      }
1341  
1342  	function selectRow( $table, $vars, $conds, $fname = __METHOD__,
1343          $options = array(), $join_conds = array()
1344      ) {
1345          if ( is_array( $conds ) ) {
1346              $conds = $this->wrapConditionsForWhere( $table, $conds );
1347          }
1348  
1349          return parent::selectRow( $table, $vars, $conds, $fname, $options, $join_conds );
1350      }
1351  
1352      /**
1353       * Returns an optional USE INDEX clause to go after the table, and a
1354       * string to go at the end of the query
1355       *
1356       * @param array $options An associative array of options to be turned into
1357       *   an SQL query, valid keys are listed in the function.
1358       * @return array
1359       */
1360  	function makeSelectOptions( $options ) {
1361          $preLimitTail = $postLimitTail = '';
1362          $startOpts = '';
1363  
1364          $noKeyOptions = array();
1365          foreach ( $options as $key => $option ) {
1366              if ( is_numeric( $key ) ) {
1367                  $noKeyOptions[$option] = true;
1368              }
1369          }
1370  
1371          $preLimitTail .= $this->makeGroupByWithHaving( $options );
1372  
1373          $preLimitTail .= $this->makeOrderBy( $options );
1374  
1375          if ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
1376              $postLimitTail .= ' FOR UPDATE';
1377          }
1378  
1379          if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) {
1380              $startOpts .= 'DISTINCT';
1381          }
1382  
1383          if ( isset( $options['USE INDEX'] ) && !is_array( $options['USE INDEX'] ) ) {
1384              $useIndex = $this->useIndexClause( $options['USE INDEX'] );
1385          } else {
1386              $useIndex = '';
1387          }
1388  
1389          return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
1390      }
1391  
1392  	public function delete( $table, $conds, $fname = __METHOD__ ) {
1393          if ( is_array( $conds ) ) {
1394              $conds = $this->wrapConditionsForWhere( $table, $conds );
1395          }
1396          // a hack for deleting pages, users and images (which have non-nullable FKs)
1397          // all deletions on these tables have transactions so final failure rollbacks these updates
1398          $table = $this->tableName( $table );
1399          if ( $table == $this->tableName( 'user' ) ) {
1400              $this->update( 'archive', array( 'ar_user' => 0 ),
1401                  array( 'ar_user' => $conds['user_id'] ), $fname );
1402              $this->update( 'ipblocks', array( 'ipb_user' => 0 ),
1403                  array( 'ipb_user' => $conds['user_id'] ), $fname );
1404              $this->update( 'image', array( 'img_user' => 0 ),
1405                  array( 'img_user' => $conds['user_id'] ), $fname );
1406              $this->update( 'oldimage', array( 'oi_user' => 0 ),
1407                  array( 'oi_user' => $conds['user_id'] ), $fname );
1408              $this->update( 'filearchive', array( 'fa_deleted_user' => 0 ),
1409                  array( 'fa_deleted_user' => $conds['user_id'] ), $fname );
1410              $this->update( 'filearchive', array( 'fa_user' => 0 ),
1411                  array( 'fa_user' => $conds['user_id'] ), $fname );
1412              $this->update( 'uploadstash', array( 'us_user' => 0 ),
1413                  array( 'us_user' => $conds['user_id'] ), $fname );
1414              $this->update( 'recentchanges', array( 'rc_user' => 0 ),
1415                  array( 'rc_user' => $conds['user_id'] ), $fname );
1416              $this->update( 'logging', array( 'log_user' => 0 ),
1417                  array( 'log_user' => $conds['user_id'] ), $fname );
1418          } elseif ( $table == $this->tableName( 'image' ) ) {
1419              $this->update( 'oldimage', array( 'oi_name' => 0 ),
1420                  array( 'oi_name' => $conds['img_name'] ), $fname );
1421          }
1422  
1423          return parent::delete( $table, $conds, $fname );
1424      }
1425  
1426      /**
1427       * @param string $table
1428       * @param array $values
1429       * @param array $conds
1430       * @param string $fname
1431       * @param array $options
1432       * @return bool
1433       * @throws DBUnexpectedError
1434       */
1435  	function update( $table, $values, $conds, $fname = __METHOD__, $options = array() ) {
1436          global $wgContLang;
1437  
1438          $table = $this->tableName( $table );
1439          $opts = $this->makeUpdateOptions( $options );
1440          $sql = "UPDATE $opts $table SET ";
1441  
1442          $first = true;
1443          foreach ( $values as $col => &$val ) {
1444              $sqlSet = $this->fieldBindStatement( $table, $col, $val, true );
1445  
1446              if ( !$first ) {
1447                  $sqlSet = ', ' . $sqlSet;
1448              } else {
1449                  $first = false;
1450              }
1451              $sql .= $sqlSet;
1452          }
1453  
1454          if ( $conds !== array() && $conds !== '*' ) {
1455              $conds = $this->wrapConditionsForWhere( $table, $conds );
1456              $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
1457          }
1458  
1459          if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) {
1460              $e = oci_error( $this->mConn );
1461              $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
1462  
1463              return false;
1464          }
1465          foreach ( $values as $col => &$val ) {
1466              $col_info = $this->fieldInfoMulti( $table, $col );
1467              $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
1468  
1469              if ( $val === null ) {
1470                  // do nothing ... null was inserted in statement creation
1471              } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) {
1472                  if ( is_object( $val ) ) {
1473                      $val = $val->getData();
1474                  }
1475  
1476                  if ( preg_match( '/^timestamp.*/i', $col_type ) == 1 && strtolower( $val ) == 'infinity' ) {
1477                      $val = '31-12-2030 12:00:00.000000';
1478                  }
1479  
1480                  $val = ( $wgContLang != null ) ? $wgContLang->checkTitleEncoding( $val ) : $val;
1481                  if ( oci_bind_by_name( $stmt, ":$col", $val ) === false ) {
1482                      $e = oci_error( $stmt );
1483                      $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
1484  
1485                      return false;
1486                  }
1487              } else {
1488                  /** @var OCI_Lob[] $lob */
1489                  if ( ( $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB ) ) === false ) {
1490                      $e = oci_error( $stmt );
1491                      throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] );
1492                  }
1493  
1494                  if ( is_object( $val ) ) {
1495                      $val = $val->getData();
1496                  }
1497  
1498                  if ( $col_type == 'BLOB' ) {
1499                      $lob[$col]->writeTemporary( $val );
1500                      oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, SQLT_BLOB );
1501                  } else {
1502                      $lob[$col]->writeTemporary( $val );
1503                      oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_CLOB );
1504                  }
1505              }
1506          }
1507  
1508          wfSuppressWarnings();
1509  
1510          if ( oci_execute( $stmt, $this->execFlags() ) === false ) {
1511              $e = oci_error( $stmt );
1512              if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) {
1513                  $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
1514  
1515                  return false;
1516              } else {
1517                  $this->mAffectedRows = oci_num_rows( $stmt );
1518              }
1519          } else {
1520              $this->mAffectedRows = oci_num_rows( $stmt );
1521          }
1522  
1523          wfRestoreWarnings();
1524  
1525          if ( isset( $lob ) ) {
1526              foreach ( $lob as $lob_v ) {
1527                  $lob_v->free();
1528              }
1529          }
1530  
1531          if ( !$this->mTrxLevel ) {
1532              oci_commit( $this->mConn );
1533          }
1534  
1535          return oci_free_statement( $stmt );
1536      }
1537  
1538  	function bitNot( $field ) {
1539          // expecting bit-fields smaller than 4bytes
1540          return 'BITNOT(' . $field . ')';
1541      }
1542  
1543  	function bitAnd( $fieldLeft, $fieldRight ) {
1544          return 'BITAND(' . $fieldLeft . ', ' . $fieldRight . ')';
1545      }
1546  
1547  	function bitOr( $fieldLeft, $fieldRight ) {
1548          return 'BITOR(' . $fieldLeft . ', ' . $fieldRight . ')';
1549      }
1550  
1551  	function getDBname() {
1552          return $this->mDBname;
1553      }
1554  
1555  	function getServer() {
1556          return $this->mServer;
1557      }
1558  
1559  	public function buildGroupConcatField(
1560          $delim, $table, $field, $conds = '', $join_conds = array()
1561      ) {
1562          $fld = "LISTAGG($field," . $this->addQuotes( $delim ) . ") WITHIN GROUP (ORDER BY $field)";
1563  
1564          return '(' . $this->selectSQLText( $table, $fld, $conds, null, array(), $join_conds ) . ')';
1565      }
1566  
1567  	public function getSearchEngine() {
1568          return 'SearchOracle';
1569      }
1570  
1571  	public function getInfinity() {
1572          return '31-12-2030 12:00:00.000000';
1573      }
1574  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1