MediaWiki PHP Cross Reference Collaborative Wikis

Source: /includes/db/Database.php - 3956 lines - 116266 bytes - Summary - Text - Print

   1  <?php
   2  /**
   3   * @defgroup Database Database
   4   *
   5   * This file deals with database interface functions
   6   * and query specifics/optimisations.
   7   *
   8   * This program is free software; you can redistribute it and/or modify
   9   * it under the terms of the GNU General Public License as published by
  10   * the Free Software Foundation; either version 2 of the License, or
  11   * (at your option) any later version.
  12   *
  13   * This program is distributed in the hope that it will be useful,
  14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16   * GNU General Public License for more details.
  17   *
  18   * You should have received a copy of the GNU General Public License along
  19   * with this program; if not, write to the Free Software Foundation, Inc.,
  20   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21   * http://www.gnu.org/copyleft/gpl.html
  22   *
  23   * @file
  24   * @ingroup Database
  25   */
  26  
  27  /**
  28   * Base interface for all DBMS-specific code. At a bare minimum, all of the
  29   * following must be implemented to support MediaWiki
  30   *
  31   * @file
  32   * @ingroup Database
  33   */
  34  interface DatabaseType {
  35      /**
  36       * Get the type of the DBMS, as it appears in $wgDBtype.
  37       *
  38       * @return string
  39       */
  40  	function getType();
  41  
  42      /**
  43       * Open a connection to the database. Usually aborts on failure
  44       *
  45       * @param string $server database server host
  46       * @param string $user database user name
  47       * @param string $password database user password
  48       * @param string $dbName database name
  49       * @return bool
  50       * @throws DBConnectionError
  51       */
  52  	function open( $server, $user, $password, $dbName );
  53  
  54      /**
  55       * Fetch the next row from the given result object, in object form.
  56       * Fields can be retrieved with $row->fieldname, with fields acting like
  57       * member variables.
  58       * If no more rows are available, false is returned.
  59       *
  60       * @param $res ResultWrapper|object as returned from DatabaseBase::query(), etc.
  61       * @return object|bool
  62       * @throws DBUnexpectedError Thrown if the database returns an error
  63       */
  64  	function fetchObject( $res );
  65  
  66      /**
  67       * Fetch the next row from the given result object, in associative array
  68       * form.  Fields are retrieved with $row['fieldname'].
  69       * If no more rows are available, false is returned.
  70       *
  71       * @param $res ResultWrapper result object as returned from DatabaseBase::query(), etc.
  72       * @return array|bool
  73       * @throws DBUnexpectedError Thrown if the database returns an error
  74       */
  75  	function fetchRow( $res );
  76  
  77      /**
  78       * Get the number of rows in a result object
  79       *
  80       * @param $res Mixed: A SQL result
  81       * @return int
  82       */
  83  	function numRows( $res );
  84  
  85      /**
  86       * Get the number of fields in a result object
  87       * @see http://www.php.net/mysql_num_fields
  88       *
  89       * @param $res Mixed: A SQL result
  90       * @return int
  91       */
  92  	function numFields( $res );
  93  
  94      /**
  95       * Get a field name in a result object
  96       * @see http://www.php.net/mysql_field_name
  97       *
  98       * @param $res Mixed: A SQL result
  99       * @param $n Integer
 100       * @return string
 101       */
 102  	function fieldName( $res, $n );
 103  
 104      /**
 105       * Get the inserted value of an auto-increment row
 106       *
 107       * The value inserted should be fetched from nextSequenceValue()
 108       *
 109       * Example:
 110       * $id = $dbw->nextSequenceValue( 'page_page_id_seq' );
 111       * $dbw->insert( 'page', array( 'page_id' => $id ) );
 112       * $id = $dbw->insertId();
 113       *
 114       * @return int
 115       */
 116  	function insertId();
 117  
 118      /**
 119       * Change the position of the cursor in a result object
 120       * @see http://www.php.net/mysql_data_seek
 121       *
 122       * @param $res Mixed: A SQL result
 123       * @param $row Mixed: Either MySQL row or ResultWrapper
 124       */
 125  	function dataSeek( $res, $row );
 126  
 127      /**
 128       * Get the last error number
 129       * @see http://www.php.net/mysql_errno
 130       *
 131       * @return int
 132       */
 133  	function lastErrno();
 134  
 135      /**
 136       * Get a description of the last error
 137       * @see http://www.php.net/mysql_error
 138       *
 139       * @return string
 140       */
 141  	function lastError();
 142  
 143      /**
 144       * mysql_fetch_field() wrapper
 145       * Returns false if the field doesn't exist
 146       *
 147       * @param string $table table name
 148       * @param string $field field name
 149       *
 150       * @return Field
 151       */
 152  	function fieldInfo( $table, $field );
 153  
 154      /**
 155       * Get information about an index into an object
 156       * @param string $table Table name
 157       * @param string $index Index name
 158       * @param string $fname Calling function name
 159       * @return Mixed: Database-specific index description class or false if the index does not exist
 160       */
 161  	function indexInfo( $table, $index, $fname = __METHOD__ );
 162  
 163      /**
 164       * Get the number of rows affected by the last write query
 165       * @see http://www.php.net/mysql_affected_rows
 166       *
 167       * @return int
 168       */
 169  	function affectedRows();
 170  
 171      /**
 172       * Wrapper for addslashes()
 173       *
 174       * @param string $s to be slashed.
 175       * @return string: slashed string.
 176       */
 177  	function strencode( $s );
 178  
 179      /**
 180       * Returns a wikitext link to the DB's website, e.g.,
 181       *     return "[http://www.mysql.com/ MySQL]";
 182       * Should at least contain plain text, if for some reason
 183       * your database has no website.
 184       *
 185       * @return string: wikitext of a link to the server software's web site
 186       */
 187  	function getSoftwareLink();
 188  
 189      /**
 190       * A string describing the current software version, like from
 191       * mysql_get_server_info().
 192       *
 193       * @return string: Version information from the database server.
 194       */
 195  	function getServerVersion();
 196  
 197      /**
 198       * A string describing the current software version, and possibly
 199       * other details in a user-friendly way.  Will be listed on Special:Version, etc.
 200       * Use getServerVersion() to get machine-friendly information.
 201       *
 202       * @return string: Version information from the database server
 203       */
 204  	function getServerInfo();
 205  }
 206  
 207  /**
 208   * Interface for classes that implement or wrap DatabaseBase
 209   * @ingroup Database
 210   */
 211  interface IDatabase {}
 212  
 213  /**
 214   * Database abstraction object
 215   * @ingroup Database
 216   */
 217  abstract class DatabaseBase implements IDatabase, DatabaseType {
 218      /** Number of times to re-try an operation in case of deadlock */
 219      const DEADLOCK_TRIES = 4;
 220      /** Minimum time to wait before retry, in microseconds */
 221      const DEADLOCK_DELAY_MIN = 500000;
 222      /** Maximum time to wait before retry */
 223      const DEADLOCK_DELAY_MAX = 1500000;
 224  
 225  # ------------------------------------------------------------------------------
 226  # Variables
 227  # ------------------------------------------------------------------------------
 228  
 229      protected $mLastQuery = '';
 230      protected $mDoneWrites = false;
 231      protected $mPHPError = false;
 232  
 233      protected $mServer, $mUser, $mPassword, $mDBname;
 234  
 235      protected $mConn = null;
 236      protected $mOpened = false;
 237  
 238      /** @var callable[] */
 239      protected $mTrxIdleCallbacks = array();
 240      /** @var callable[] */
 241      protected $mTrxPreCommitCallbacks = array();
 242  
 243      protected $mTablePrefix;
 244      protected $mFlags;
 245      protected $mForeign;
 246      protected $mTrxLevel = 0;
 247      protected $mErrorCount = 0;
 248      protected $mLBInfo = array();
 249      protected $mFakeSlaveLag = null, $mFakeMaster = false;
 250      protected $mDefaultBigSelects = null;
 251      protected $mSchemaVars = false;
 252  
 253      protected $preparedArgs;
 254  
 255      protected $htmlErrors;
 256  
 257      protected $delimiter = ';';
 258  
 259      /**
 260       * Remembers the function name given for starting the most recent transaction via begin().
 261       * Used to provide additional context for error reporting.
 262       *
 263       * @var String
 264       * @see DatabaseBase::mTrxLevel
 265       */
 266      private $mTrxFname = null;
 267  
 268      /**
 269       * Record if possible write queries were done in the last transaction started
 270       *
 271       * @var Bool
 272       * @see DatabaseBase::mTrxLevel
 273       */
 274      private $mTrxDoneWrites = false;
 275  
 276      /**
 277       * Record if the current transaction was started implicitly due to DBO_TRX being set.
 278       *
 279       * @var Bool
 280       * @see DatabaseBase::mTrxLevel
 281       */
 282      private $mTrxAutomatic = false;
 283  
 284      /**
 285       * @since 1.21
 286       * @var file handle for upgrade
 287       */
 288      protected $fileHandle = null;
 289  
 290      /**
 291       * @since 1.22
 292       * @var Process cache of VIEWs names in the database
 293       */
 294      protected $allViews = null;
 295  
 296  # ------------------------------------------------------------------------------
 297  # Accessors
 298  # ------------------------------------------------------------------------------
 299      # These optionally set a variable and return the previous state
 300  
 301      /**
 302       * A string describing the current software version, and possibly
 303       * other details in a user-friendly way.  Will be listed on Special:Version, etc.
 304       * Use getServerVersion() to get machine-friendly information.
 305       *
 306       * @return string: Version information from the database server
 307       */
 308  	public function getServerInfo() {
 309          return $this->getServerVersion();
 310      }
 311  
 312      /**
 313       * @return string: command delimiter used by this database engine
 314       */
 315  	public function getDelimiter() {
 316          return $this->delimiter;
 317      }
 318  
 319      /**
 320       * Boolean, controls output of large amounts of debug information.
 321       * @param $debug bool|null
 322       *   - true to enable debugging
 323       *   - false to disable debugging
 324       *   - omitted or null to do nothing
 325       *
 326       * @return bool|null previous value of the flag
 327       */
 328  	public function debug( $debug = null ) {
 329          return wfSetBit( $this->mFlags, DBO_DEBUG, $debug );
 330      }
 331  
 332      /**
 333       * Turns buffering of SQL result sets on (true) or off (false). Default is
 334       * "on".
 335       *
 336       * Unbuffered queries are very troublesome in MySQL:
 337       *
 338       *   - If another query is executed while the first query is being read
 339       *     out, the first query is killed. This means you can't call normal
 340       *     MediaWiki functions while you are reading an unbuffered query result
 341       *     from a normal wfGetDB() connection.
 342       *
 343       *   - Unbuffered queries cause the MySQL server to use large amounts of
 344       *     memory and to hold broad locks which block other queries.
 345       *
 346       * If you want to limit client-side memory, it's almost always better to
 347       * split up queries into batches using a LIMIT clause than to switch off
 348       * buffering.
 349       *
 350       * @param $buffer null|bool
 351       *
 352       * @return null|bool The previous value of the flag
 353       */
 354  	public function bufferResults( $buffer = null ) {
 355          if ( is_null( $buffer ) ) {
 356              return !(bool)( $this->mFlags & DBO_NOBUFFER );
 357          } else {
 358              return !wfSetBit( $this->mFlags, DBO_NOBUFFER, !$buffer );
 359          }
 360      }
 361  
 362      /**
 363       * Turns on (false) or off (true) the automatic generation and sending
 364       * of a "we're sorry, but there has been a database error" page on
 365       * database errors. Default is on (false). When turned off, the
 366       * code should use lastErrno() and lastError() to handle the
 367       * situation as appropriate.
 368       *
 369       * Do not use this function outside of the Database classes.
 370       *
 371       * @param $ignoreErrors bool|null
 372       *
 373       * @return bool The previous value of the flag.
 374       */
 375  	public function ignoreErrors( $ignoreErrors = null ) {
 376          return wfSetBit( $this->mFlags, DBO_IGNORE, $ignoreErrors );
 377      }
 378  
 379      /**
 380       * Gets or sets the current transaction level.
 381       *
 382       * Historically, transactions were allowed to be "nested". This is no
 383       * longer supported, so this function really only returns a boolean.
 384       *
 385       * @param int $level An integer (0 or 1), or omitted to leave it unchanged.
 386       * @return int The previous value
 387       */
 388  	public function trxLevel( $level = null ) {
 389          return wfSetVar( $this->mTrxLevel, $level );
 390      }
 391  
 392      /**
 393       * Get/set the number of errors logged. Only useful when errors are ignored
 394       * @param int $count The count to set, or omitted to leave it unchanged.
 395       * @return int The error count
 396       */
 397  	public function errorCount( $count = null ) {
 398          return wfSetVar( $this->mErrorCount, $count );
 399      }
 400  
 401      /**
 402       * Get/set the table prefix.
 403       * @param string $prefix The table prefix to set, or omitted to leave it unchanged.
 404       * @return string The previous table prefix.
 405       */
 406  	public function tablePrefix( $prefix = null ) {
 407          return wfSetVar( $this->mTablePrefix, $prefix );
 408      }
 409  
 410      /**
 411       * Set the filehandle to copy write statements to.
 412       *
 413       * @param $fh filehandle
 414       */
 415  	public function setFileHandle( $fh ) {
 416          $this->fileHandle = $fh;
 417      }
 418  
 419      /**
 420       * Get properties passed down from the server info array of the load
 421       * balancer.
 422       *
 423       * @param string $name The entry of the info array to get, or null to get the
 424       *   whole array
 425       *
 426       * @return LoadBalancer|null
 427       */
 428  	public function getLBInfo( $name = null ) {
 429          if ( is_null( $name ) ) {
 430              return $this->mLBInfo;
 431          } else {
 432              if ( array_key_exists( $name, $this->mLBInfo ) ) {
 433                  return $this->mLBInfo[$name];
 434              } else {
 435                  return null;
 436              }
 437          }
 438      }
 439  
 440      /**
 441       * Set the LB info array, or a member of it. If called with one parameter,
 442       * the LB info array is set to that parameter. If it is called with two
 443       * parameters, the member with the given name is set to the given value.
 444       *
 445       * @param $name
 446       * @param $value
 447       */
 448  	public function setLBInfo( $name, $value = null ) {
 449          if ( is_null( $value ) ) {
 450              $this->mLBInfo = $name;
 451          } else {
 452              $this->mLBInfo[$name] = $value;
 453          }
 454      }
 455  
 456      /**
 457       * Set lag time in seconds for a fake slave
 458       *
 459       * @param $lag int
 460       */
 461  	public function setFakeSlaveLag( $lag ) {
 462          $this->mFakeSlaveLag = $lag;
 463      }
 464  
 465      /**
 466       * Make this connection a fake master
 467       *
 468       * @param $enabled bool
 469       */
 470  	public function setFakeMaster( $enabled = true ) {
 471          $this->mFakeMaster = $enabled;
 472      }
 473  
 474      /**
 475       * Returns true if this database supports (and uses) cascading deletes
 476       *
 477       * @return bool
 478       */
 479  	public function cascadingDeletes() {
 480          return false;
 481      }
 482  
 483      /**
 484       * Returns true if this database supports (and uses) triggers (e.g. on the page table)
 485       *
 486       * @return bool
 487       */
 488  	public function cleanupTriggers() {
 489          return false;
 490      }
 491  
 492      /**
 493       * Returns true if this database is strict about what can be put into an IP field.
 494       * Specifically, it uses a NULL value instead of an empty string.
 495       *
 496       * @return bool
 497       */
 498  	public function strictIPs() {
 499          return false;
 500      }
 501  
 502      /**
 503       * Returns true if this database uses timestamps rather than integers
 504       *
 505       * @return bool
 506       */
 507  	public function realTimestamps() {
 508          return false;
 509      }
 510  
 511      /**
 512       * Returns true if this database does an implicit sort when doing GROUP BY
 513       *
 514       * @return bool
 515       */
 516  	public function implicitGroupby() {
 517          return true;
 518      }
 519  
 520      /**
 521       * Returns true if this database does an implicit order by when the column has an index
 522       * For example: SELECT page_title FROM page LIMIT 1
 523       *
 524       * @return bool
 525       */
 526  	public function implicitOrderby() {
 527          return true;
 528      }
 529  
 530      /**
 531       * Returns true if this database can do a native search on IP columns
 532       * e.g. this works as expected: .. WHERE rc_ip = '127.42.12.102/32';
 533       *
 534       * @return bool
 535       */
 536  	public function searchableIPs() {
 537          return false;
 538      }
 539  
 540      /**
 541       * Returns true if this database can use functional indexes
 542       *
 543       * @return bool
 544       */
 545  	public function functionalIndexes() {
 546          return false;
 547      }
 548  
 549      /**
 550       * Return the last query that went through DatabaseBase::query()
 551       * @return String
 552       */
 553  	public function lastQuery() {
 554          return $this->mLastQuery;
 555      }
 556  
 557      /**
 558       * Returns true if the connection may have been used for write queries.
 559       * Should return true if unsure.
 560       *
 561       * @return bool
 562       */
 563  	public function doneWrites() {
 564          return $this->mDoneWrites;
 565      }
 566  
 567      /**
 568       * Returns true if there is a transaction open with possible write
 569       * queries or transaction pre-commit/idle callbacks waiting on it to finish.
 570       *
 571       * @return bool
 572       */
 573  	public function writesOrCallbacksPending() {
 574          return $this->mTrxLevel && (
 575              $this->mTrxDoneWrites || $this->mTrxIdleCallbacks || $this->mTrxPreCommitCallbacks
 576          );
 577      }
 578  
 579      /**
 580       * Is a connection to the database open?
 581       * @return Boolean
 582       */
 583  	public function isOpen() {
 584          return $this->mOpened;
 585      }
 586  
 587      /**
 588       * Set a flag for this connection
 589       *
 590       * @param $flag Integer: DBO_* constants from Defines.php:
 591       *   - DBO_DEBUG: output some debug info (same as debug())
 592       *   - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
 593       *   - DBO_TRX: automatically start transactions
 594       *   - DBO_DEFAULT: automatically sets DBO_TRX if not in command line mode
 595       *       and removes it in command line mode
 596       *   - DBO_PERSISTENT: use persistant database connection
 597       */
 598  	public function setFlag( $flag ) {
 599          global $wgDebugDBTransactions;
 600          $this->mFlags |= $flag;
 601          if ( ( $flag & DBO_TRX ) & $wgDebugDBTransactions ) {
 602              wfDebug( "Implicit transactions are now  disabled.\n" );
 603          }
 604      }
 605  
 606      /**
 607       * Clear a flag for this connection
 608       *
 609       * @param $flag: same as setFlag()'s $flag param
 610       */
 611  	public function clearFlag( $flag ) {
 612          global $wgDebugDBTransactions;
 613          $this->mFlags &= ~$flag;
 614          if ( ( $flag & DBO_TRX ) && $wgDebugDBTransactions ) {
 615              wfDebug( "Implicit transactions are now disabled.\n" );
 616          }
 617      }
 618  
 619      /**
 620       * Returns a boolean whether the flag $flag is set for this connection
 621       *
 622       * @param $flag: same as setFlag()'s $flag param
 623       * @return Boolean
 624       */
 625  	public function getFlag( $flag ) {
 626          return !!( $this->mFlags & $flag );
 627      }
 628  
 629      /**
 630       * General read-only accessor
 631       *
 632       * @param $name string
 633       *
 634       * @return string
 635       */
 636  	public function getProperty( $name ) {
 637          return $this->$name;
 638      }
 639  
 640      /**
 641       * @return string
 642       */
 643  	public function getWikiID() {
 644          if ( $this->mTablePrefix ) {
 645              return "{$this->mDBname}-{$this->mTablePrefix}";
 646          } else {
 647              return $this->mDBname;
 648          }
 649      }
 650  
 651      /**
 652       * Return a path to the DBMS-specific schema file, otherwise default to tables.sql
 653       *
 654       * @return string
 655       */
 656  	public function getSchemaPath() {
 657          global $IP;
 658          if ( file_exists( "$IP/maintenance/" . $this->getType() . "/tables.sql" ) ) {
 659              return "$IP/maintenance/" . $this->getType() . "/tables.sql";
 660          } else {
 661              return "$IP/maintenance/tables.sql";
 662          }
 663      }
 664  
 665  # ------------------------------------------------------------------------------
 666  # Other functions
 667  # ------------------------------------------------------------------------------
 668  
 669      /**
 670       * Constructor.
 671       *
 672       * FIXME: It is possible to construct a Database object with no associated
 673       * connection object, by specifying no parameters to __construct(). This
 674       * feature is deprecated and should be removed.
 675       *
 676       * FIXME: The long list of formal parameters here is not really appropriate
 677       * for MySQL, and not at all appropriate for any other DBMS. It should be
 678       * replaced by named parameters as in DatabaseBase::factory().
 679       *
 680       * DatabaseBase subclasses should not be constructed directly in external
 681       * code. DatabaseBase::factory() should be used instead.
 682       *
 683       * @param string $server database server host
 684       * @param string $user database user name
 685       * @param string $password database user password
 686       * @param string $dbName database name
 687       * @param $flags
 688       * @param string $tablePrefix database table prefixes. By default use the prefix gave in LocalSettings.php
 689       * @param bool $foreign disable some operations specific to local databases
 690       */
 691  	function __construct( $server = false, $user = false, $password = false, $dbName = false,
 692          $flags = 0, $tablePrefix = 'get from global', $foreign = false
 693      ) {
 694          global $wgDBprefix, $wgCommandLineMode, $wgDebugDBTransactions;
 695  
 696          $this->mFlags = $flags;
 697  
 698          if ( $this->mFlags & DBO_DEFAULT ) {
 699              if ( $wgCommandLineMode ) {
 700                  $this->mFlags &= ~DBO_TRX;
 701                  if ( $wgDebugDBTransactions ) {
 702                      wfDebug( "Implicit transaction open disabled.\n" );
 703                  }
 704              } else {
 705                  $this->mFlags |= DBO_TRX;
 706                  if ( $wgDebugDBTransactions ) {
 707                      wfDebug( "Implicit transaction open enabled.\n" );
 708                  }
 709              }
 710          }
 711  
 712          /** Get the default table prefix*/
 713          if ( $tablePrefix == 'get from global' ) {
 714              $this->mTablePrefix = $wgDBprefix;
 715          } else {
 716              $this->mTablePrefix = $tablePrefix;
 717          }
 718  
 719          $this->mForeign = $foreign;
 720  
 721          if ( $user ) {
 722              $this->open( $server, $user, $password, $dbName );
 723          }
 724      }
 725  
 726      /**
 727       * Called by serialize. Throw an exception when DB connection is serialized.
 728       * This causes problems on some database engines because the connection is
 729       * not restored on unserialize.
 730       */
 731  	public function __sleep() {
 732          throw new MWException( 'Database serialization may cause problems, since the connection is not restored on wakeup.' );
 733      }
 734  
 735      /**
 736       * Given a DB type, construct the name of the appropriate child class of
 737       * DatabaseBase. This is designed to replace all of the manual stuff like:
 738       *    $class = 'Database' . ucfirst( strtolower( $dbType ) );
 739       * as well as validate against the canonical list of DB types we have
 740       *
 741       * This factory function is mostly useful for when you need to connect to a
 742       * database other than the MediaWiki default (such as for external auth,
 743       * an extension, et cetera). Do not use this to connect to the MediaWiki
 744       * database. Example uses in core:
 745       * @see LoadBalancer::reallyOpenConnection()
 746       * @see ForeignDBRepo::getMasterDB()
 747       * @see WebInstaller_DBConnect::execute()
 748       *
 749       * @since 1.18
 750       *
 751       * @param string $dbType A possible DB type
 752       * @param array $p An array of options to pass to the constructor.
 753       *    Valid options are: host, user, password, dbname, flags, tablePrefix, driver
 754       * @return DatabaseBase subclass or null
 755       */
 756  	final public static function factory( $dbType, $p = array() ) {
 757          $canonicalDBTypes = array(
 758              'mysql'    => array( 'mysqli', 'mysql' ),
 759              'postgres' => array(),
 760              'sqlite'   => array(),
 761              'oracle'   => array(),
 762              'mssql'    => array(),
 763          );
 764  
 765          $driver = false;
 766          $dbType = strtolower( $dbType );
 767          if ( isset( $canonicalDBTypes[$dbType] ) && $canonicalDBTypes[$dbType] ) {
 768              $possibleDrivers = $canonicalDBTypes[$dbType];
 769              if ( !empty( $p['driver'] ) ) {
 770                  if ( in_array( $p['driver'], $possibleDrivers ) ) {
 771                      $driver = $p['driver'];
 772                  } else {
 773                      throw new MWException( __METHOD__ .
 774                          " cannot construct Database with type '$dbType' and driver '{$p['driver']}'" );
 775                  }
 776              } else {
 777                  foreach ( $possibleDrivers as $posDriver ) {
 778                      if ( extension_loaded( $posDriver ) ) {
 779                          $driver = $posDriver;
 780                          break;
 781                      }
 782                  }
 783              }
 784          } else {
 785              $driver = $dbType;
 786          }
 787          if ( $driver === false ) {
 788              throw new MWException( __METHOD__ .
 789                  " no viable database extension found for type '$dbType'" );
 790          }
 791  
 792          $class = 'Database' . ucfirst( $driver );
 793          if ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) {
 794              return new $class(
 795                  isset( $p['host'] ) ? $p['host'] : false,
 796                  isset( $p['user'] ) ? $p['user'] : false,
 797                  isset( $p['password'] ) ? $p['password'] : false,
 798                  isset( $p['dbname'] ) ? $p['dbname'] : false,
 799                  isset( $p['flags'] ) ? $p['flags'] : 0,
 800                  isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : 'get from global',
 801                  isset( $p['foreign'] ) ? $p['foreign'] : false
 802              );
 803          } else {
 804              return null;
 805          }
 806      }
 807  
 808  	protected function installErrorHandler() {
 809          $this->mPHPError = false;
 810          $this->htmlErrors = ini_set( 'html_errors', '0' );
 811          set_error_handler( array( $this, 'connectionErrorHandler' ) );
 812      }
 813  
 814      /**
 815       * @return bool|string
 816       */
 817  	protected function restoreErrorHandler() {
 818          restore_error_handler();
 819          if ( $this->htmlErrors !== false ) {
 820              ini_set( 'html_errors', $this->htmlErrors );
 821          }
 822          if ( $this->mPHPError ) {
 823              $error = preg_replace( '!\[<a.*</a>\]!', '', $this->mPHPError );
 824              $error = preg_replace( '!^.*?:\s?(.*)$!', '$1', $error );
 825              return $error;
 826          } else {
 827              return false;
 828          }
 829      }
 830  
 831      /**
 832       * @param $errno
 833       * @param $errstr
 834       * @access private
 835       */
 836  	public function connectionErrorHandler( $errno, $errstr ) {
 837          $this->mPHPError = $errstr;
 838      }
 839  
 840      /**
 841       * Closes a database connection.
 842       * if it is open : commits any open transactions
 843       *
 844       * @throws MWException
 845       * @return Bool operation success. true if already closed.
 846       */
 847  	public function close() {
 848          if ( count( $this->mTrxIdleCallbacks ) ) { // sanity
 849              throw new MWException( "Transaction idle callbacks still pending." );
 850          }
 851          $this->mOpened = false;
 852          if ( $this->mConn ) {
 853              if ( $this->trxLevel() ) {
 854                  if ( !$this->mTrxAutomatic ) {
 855                      wfWarn( "Transaction still in progress (from {$this->mTrxFname}), " .
 856                          " performing implicit commit before closing connection!" );
 857                  }
 858  
 859                  $this->commit( __METHOD__, 'flush' );
 860              }
 861  
 862              $ret = $this->closeConnection();
 863              $this->mConn = false;
 864              return $ret;
 865          } else {
 866              return true;
 867          }
 868      }
 869  
 870      /**
 871       * Closes underlying database connection
 872       * @since 1.20
 873       * @return bool: Whether connection was closed successfully
 874       */
 875      abstract protected function closeConnection();
 876  
 877      /**
 878       * @param string $error fallback error message, used if none is given by DB
 879       * @throws DBConnectionError
 880       */
 881  	function reportConnectionError( $error = 'Unknown error' ) {
 882          $myError = $this->lastError();
 883          if ( $myError ) {
 884              $error = $myError;
 885          }
 886  
 887          # New method
 888          throw new DBConnectionError( $this, $error );
 889      }
 890  
 891      /**
 892       * The DBMS-dependent part of query()
 893       *
 894       * @param  $sql String: SQL query.
 895       * @return ResultWrapper Result object to feed to fetchObject, fetchRow, ...; or false on failure
 896       */
 897      abstract protected function doQuery( $sql );
 898  
 899      /**
 900       * Determine whether a query writes to the DB.
 901       * Should return true if unsure.
 902       *
 903       * @param $sql string
 904       *
 905       * @return bool
 906       */
 907  	public function isWriteQuery( $sql ) {
 908          return !preg_match( '/^(?:SELECT|BEGIN|ROLLBACK|COMMIT|SET|SHOW|EXPLAIN|\(SELECT)\b/i', $sql );
 909      }
 910  
 911      /**
 912       * Run an SQL query and return the result. Normally throws a DBQueryError
 913       * on failure. If errors are ignored, returns false instead.
 914       *
 915       * In new code, the query wrappers select(), insert(), update(), delete(),
 916       * etc. should be used where possible, since they give much better DBMS
 917       * independence and automatically quote or validate user input in a variety
 918       * of contexts. This function is generally only useful for queries which are
 919       * explicitly DBMS-dependent and are unsupported by the query wrappers, such
 920       * as CREATE TABLE.
 921       *
 922       * However, the query wrappers themselves should call this function.
 923       *
 924       * @param  $sql        String: SQL query
 925       * @param  $fname      String: Name of the calling function, for profiling/SHOW PROCESSLIST
 926       *     comment (you can use __METHOD__ or add some extra info)
 927       * @param  $tempIgnore Boolean:   Whether to avoid throwing an exception on errors...
 928       *     maybe best to catch the exception instead?
 929       * @throws MWException
 930       * @return boolean|ResultWrapper. true for a successful write query, ResultWrapper object
 931       *     for a successful read query, or false on failure if $tempIgnore set
 932       */
 933  	public function query( $sql, $fname = __METHOD__, $tempIgnore = false ) {
 934          global $wgUser, $wgDebugDBTransactions;
 935  
 936          $this->mLastQuery = $sql;
 937          if ( !$this->mDoneWrites && $this->isWriteQuery( $sql ) ) {
 938              # Set a flag indicating that writes have been done
 939              wfDebug( __METHOD__ . ": Writes done: $sql\n" );
 940              $this->mDoneWrites = true;
 941          }
 942  
 943          # Add a comment for easy SHOW PROCESSLIST interpretation
 944          if ( is_object( $wgUser ) && $wgUser->isItemLoaded( 'name' ) ) {
 945              $userName = $wgUser->getName();
 946              if ( mb_strlen( $userName ) > 15 ) {
 947                  $userName = mb_substr( $userName, 0, 15 ) . '...';
 948              }
 949              $userName = str_replace( '/', '', $userName );
 950          } else {
 951              $userName = '';
 952          }
 953  
 954          // Add trace comment to the begin of the sql string, right after the operator.
 955          // Or, for one-word queries (like "BEGIN" or COMMIT") add it to the end (bug 42598)
 956          $commentedSql = preg_replace( '/\s|$/', " /* $fname $userName */ ", $sql, 1 );
 957  
 958          # If DBO_TRX is set, start a transaction
 959          if ( ( $this->mFlags & DBO_TRX ) && !$this->mTrxLevel &&
 960              $sql != 'BEGIN' && $sql != 'COMMIT' && $sql != 'ROLLBACK' )
 961          {
 962              # Avoid establishing transactions for SHOW and SET statements too -
 963              # that would delay transaction initializations to once connection
 964              # is really used by application
 965              $sqlstart = substr( $sql, 0, 10 ); // very much worth it, benchmark certified(tm)
 966              if ( strpos( $sqlstart, "SHOW " ) !== 0 && strpos( $sqlstart, "SET " ) !== 0 ) {
 967                  if ( $wgDebugDBTransactions ) {
 968                      wfDebug( "Implicit transaction start.\n" );
 969                  }
 970                  $this->begin( __METHOD__ . " ($fname)" );
 971                  $this->mTrxAutomatic = true;
 972              }
 973          }
 974  
 975          # Keep track of whether the transaction has write queries pending
 976          if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $this->isWriteQuery( $sql ) ) {
 977              $this->mTrxDoneWrites = true;
 978              Profiler::instance()->transactionWritingIn( $this->mServer, $this->mDBname );
 979          }
 980  
 981          $isMaster = !is_null( $this->getLBInfo( 'master' ) );
 982          if ( !Profiler::instance()->isStub() ) {
 983              # generalizeSQL will probably cut down the query to reasonable
 984              # logging size most of the time. The substr is really just a sanity check.
 985              if ( $isMaster ) {
 986                  $queryProf = 'query-m: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
 987                  $totalProf = 'DatabaseBase::query-master';
 988              } else {
 989                  $queryProf = 'query: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
 990                  $totalProf = 'DatabaseBase::query';
 991              }
 992              wfProfileIn( $totalProf );
 993              wfProfileIn( $queryProf );
 994          }
 995  
 996          if ( $this->debug() ) {
 997              static $cnt = 0;
 998  
 999              $cnt++;
1000              $sqlx = substr( $commentedSql, 0, 500 );
1001              $sqlx = strtr( $sqlx, "\t\n", '  ' );
1002  
1003              $master = $isMaster ? 'master' : 'slave';
1004              wfDebug( "Query {$this->mDBname} ($cnt) ($master): $sqlx\n" );
1005          }
1006  
1007          $queryId = MWDebug::query( $sql, $fname, $isMaster );
1008  
1009          # Do the query and handle errors
1010          $ret = $this->doQuery( $commentedSql );
1011  
1012          MWDebug::queryTime( $queryId );
1013  
1014          # Try reconnecting if the connection was lost
1015          if ( false === $ret && $this->wasErrorReissuable() ) {
1016              # Transaction is gone, like it or not
1017              $this->mTrxLevel = 0;
1018              $this->mTrxIdleCallbacks = array(); // cancel
1019              $this->mTrxPreCommitCallbacks = array(); // cancel
1020              wfDebug( "Connection lost, reconnecting...\n" );
1021  
1022              if ( $this->ping() ) {
1023                  wfDebug( "Reconnected\n" );
1024                  $sqlx = substr( $commentedSql, 0, 500 );
1025                  $sqlx = strtr( $sqlx, "\t\n", '  ' );
1026                  global $wgRequestTime;
1027                  $elapsed = round( microtime( true ) - $wgRequestTime, 3 );
1028                  if ( $elapsed < 300 ) {
1029                      # Not a database error to lose a transaction after a minute or two
1030                      wfLogDBError( "Connection lost and reconnected after {$elapsed}s, query: $sqlx\n" );
1031                  }
1032                  $ret = $this->doQuery( $commentedSql );
1033              } else {
1034                  wfDebug( "Failed\n" );
1035              }
1036          }
1037  
1038          if ( false === $ret ) {
1039              $this->reportQueryError( $this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore );
1040          }
1041  
1042          if ( !Profiler::instance()->isStub() ) {
1043              wfProfileOut( $queryProf );
1044              wfProfileOut( $totalProf );
1045          }
1046  
1047          return $this->resultObject( $ret );
1048      }
1049  
1050      /**
1051       * Report a query error. Log the error, and if neither the object ignore
1052       * flag nor the $tempIgnore flag is set, throw a DBQueryError.
1053       *
1054       * @param $error String
1055       * @param $errno Integer
1056       * @param $sql String
1057       * @param $fname String
1058       * @param $tempIgnore Boolean
1059       * @throws DBQueryError
1060       */
1061  	public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
1062          # Ignore errors during error handling to avoid infinite recursion
1063          $ignore = $this->ignoreErrors( true );
1064          ++$this->mErrorCount;
1065  
1066          if ( $ignore || $tempIgnore ) {
1067              wfDebug( "SQL ERROR (ignored): $error\n" );
1068              $this->ignoreErrors( $ignore );
1069          } else {
1070              $sql1line = str_replace( "\n", "\\n", $sql );
1071              wfLogDBError( "$fname\t{$this->mServer}\t$errno\t$error\t$sql1line\n" );
1072              wfDebug( "SQL ERROR: " . $error . "\n" );
1073              throw new DBQueryError( $this, $error, $errno, $sql, $fname );
1074          }
1075      }
1076  
1077      /**
1078       * Intended to be compatible with the PEAR::DB wrapper functions.
1079       * http://pear.php.net/manual/en/package.database.db.intro-execute.php
1080       *
1081       * ? = scalar value, quoted as necessary
1082       * ! = raw SQL bit (a function for instance)
1083       * & = filename; reads the file and inserts as a blob
1084       *     (we don't use this though...)
1085       *
1086       * @param $sql string
1087       * @param $func string
1088       *
1089       * @return array
1090       */
1091  	protected function prepare( $sql, $func = 'DatabaseBase::prepare' ) {
1092          /* MySQL doesn't support prepared statements (yet), so just
1093             pack up the query for reference. We'll manually replace
1094             the bits later. */
1095          return array( 'query' => $sql, 'func' => $func );
1096      }
1097  
1098      /**
1099       * Free a prepared query, generated by prepare().
1100       * @param $prepared
1101       */
1102  	protected function freePrepared( $prepared ) {
1103          /* No-op by default */
1104      }
1105  
1106      /**
1107       * Execute a prepared query with the various arguments
1108       * @param string $prepared the prepared sql
1109       * @param $args Mixed: Either an array here, or put scalars as varargs
1110       *
1111       * @return ResultWrapper
1112       */
1113  	public function execute( $prepared, $args = null ) {
1114          if ( !is_array( $args ) ) {
1115              # Pull the var args
1116              $args = func_get_args();
1117              array_shift( $args );
1118          }
1119  
1120          $sql = $this->fillPrepared( $prepared['query'], $args );
1121  
1122          return $this->query( $sql, $prepared['func'] );
1123      }
1124  
1125      /**
1126       * For faking prepared SQL statements on DBs that don't support it directly.
1127       *
1128       * @param string $preparedQuery a 'preparable' SQL statement
1129       * @param array $args of arguments to fill it with
1130       * @return string executable SQL
1131       */
1132  	public function fillPrepared( $preparedQuery, $args ) {
1133          reset( $args );
1134          $this->preparedArgs =& $args;
1135  
1136          return preg_replace_callback( '/(\\\\[?!&]|[?!&])/',
1137              array( &$this, 'fillPreparedArg' ), $preparedQuery );
1138      }
1139  
1140      /**
1141       * preg_callback func for fillPrepared()
1142       * The arguments should be in $this->preparedArgs and must not be touched
1143       * while we're doing this.
1144       *
1145       * @param $matches Array
1146       * @throws DBUnexpectedError
1147       * @return String
1148       */
1149  	protected function fillPreparedArg( $matches ) {
1150          switch ( $matches[1] ) {
1151              case '\\?':
1152                  return '?';
1153              case '\\!':
1154                  return '!';
1155              case '\\&':
1156                  return '&';
1157          }
1158  
1159          list( /* $n */, $arg ) = each( $this->preparedArgs );
1160  
1161          switch ( $matches[1] ) {
1162              case '?':
1163                  return $this->addQuotes( $arg );
1164              case '!':
1165                  return $arg;
1166              case '&':
1167                  # return $this->addQuotes( file_get_contents( $arg ) );
1168                  throw new DBUnexpectedError( $this, '& mode is not implemented. If it\'s really needed, uncomment the line above.' );
1169              default:
1170                  throw new DBUnexpectedError( $this, 'Received invalid match. This should never happen!' );
1171          }
1172      }
1173  
1174      /**
1175       * Free a result object returned by query() or select(). It's usually not
1176       * necessary to call this, just use unset() or let the variable holding
1177       * the result object go out of scope.
1178       *
1179       * @param $res Mixed: A SQL result
1180       */
1181  	public function freeResult( $res ) {
1182      }
1183  
1184      /**
1185       * A SELECT wrapper which returns a single field from a single result row.
1186       *
1187       * Usually throws a DBQueryError on failure. If errors are explicitly
1188       * ignored, returns false on failure.
1189       *
1190       * If no result rows are returned from the query, false is returned.
1191       *
1192       * @param string|array $table Table name. See DatabaseBase::select() for details.
1193       * @param string $var The field name to select. This must be a valid SQL
1194       *   fragment: do not use unvalidated user input.
1195       * @param string|array $cond The condition array. See DatabaseBase::select() for details.
1196       * @param string $fname The function name of the caller.
1197       * @param string|array $options The query options. See DatabaseBase::select() for details.
1198       *
1199       * @return bool|mixed The value from the field, or false on failure.
1200       */
1201  	public function selectField( $table, $var, $cond = '', $fname = __METHOD__,
1202          $options = array()
1203      ) {
1204          if ( !is_array( $options ) ) {
1205              $options = array( $options );
1206          }
1207  
1208          $options['LIMIT'] = 1;
1209  
1210          $res = $this->select( $table, $var, $cond, $fname, $options );
1211  
1212          if ( $res === false || !$this->numRows( $res ) ) {
1213              return false;
1214          }
1215  
1216          $row = $this->fetchRow( $res );
1217  
1218          if ( $row !== false ) {
1219              return reset( $row );
1220          } else {
1221              return false;
1222          }
1223      }
1224  
1225      /**
1226       * Returns an optional USE INDEX clause to go after the table, and a
1227       * string to go at the end of the query.
1228       *
1229       * @param array $options associative array of options to be turned into
1230       *              an SQL query, valid keys are listed in the function.
1231       * @return Array
1232       * @see DatabaseBase::select()
1233       */
1234  	public function makeSelectOptions( $options ) {
1235          $preLimitTail = $postLimitTail = '';
1236          $startOpts = '';
1237  
1238          $noKeyOptions = array();
1239  
1240          foreach ( $options as $key => $option ) {
1241              if ( is_numeric( $key ) ) {
1242                  $noKeyOptions[$option] = true;
1243              }
1244          }
1245  
1246          $preLimitTail .= $this->makeGroupByWithHaving( $options );
1247  
1248          $preLimitTail .= $this->makeOrderBy( $options );
1249  
1250          // if (isset($options['LIMIT'])) {
1251          //    $tailOpts .= $this->limitResult('', $options['LIMIT'],
1252          //        isset($options['OFFSET']) ? $options['OFFSET']
1253          //        : false);
1254          // }
1255  
1256          if ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
1257              $postLimitTail .= ' FOR UPDATE';
1258          }
1259  
1260          if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) {
1261              $postLimitTail .= ' LOCK IN SHARE MODE';
1262          }
1263  
1264          if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) {
1265              $startOpts .= 'DISTINCT';
1266          }
1267  
1268          # Various MySQL extensions
1269          if ( isset( $noKeyOptions['STRAIGHT_JOIN'] ) ) {
1270              $startOpts .= ' /*! STRAIGHT_JOIN */';
1271          }
1272  
1273          if ( isset( $noKeyOptions['HIGH_PRIORITY'] ) ) {
1274              $startOpts .= ' HIGH_PRIORITY';
1275          }
1276  
1277          if ( isset( $noKeyOptions['SQL_BIG_RESULT'] ) ) {
1278              $startOpts .= ' SQL_BIG_RESULT';
1279          }
1280  
1281          if ( isset( $noKeyOptions['SQL_BUFFER_RESULT'] ) ) {
1282              $startOpts .= ' SQL_BUFFER_RESULT';
1283          }
1284  
1285          if ( isset( $noKeyOptions['SQL_SMALL_RESULT'] ) ) {
1286              $startOpts .= ' SQL_SMALL_RESULT';
1287          }
1288  
1289          if ( isset( $noKeyOptions['SQL_CALC_FOUND_ROWS'] ) ) {
1290              $startOpts .= ' SQL_CALC_FOUND_ROWS';
1291          }
1292  
1293          if ( isset( $noKeyOptions['SQL_CACHE'] ) ) {
1294              $startOpts .= ' SQL_CACHE';
1295          }
1296  
1297          if ( isset( $noKeyOptions['SQL_NO_CACHE'] ) ) {
1298              $startOpts .= ' SQL_NO_CACHE';
1299          }
1300  
1301          if ( isset( $options['USE INDEX'] ) && is_string( $options['USE INDEX'] ) ) {
1302              $useIndex = $this->useIndexClause( $options['USE INDEX'] );
1303          } else {
1304              $useIndex = '';
1305          }
1306  
1307          return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
1308      }
1309  
1310      /**
1311       * Returns an optional GROUP BY with an optional HAVING
1312       *
1313       * @param array $options associative array of options
1314       * @return string
1315       * @see DatabaseBase::select()
1316       * @since 1.21
1317       */
1318  	public function makeGroupByWithHaving( $options ) {
1319          $sql = '';
1320          if ( isset( $options['GROUP BY'] ) ) {
1321              $gb = is_array( $options['GROUP BY'] )
1322                  ? implode( ',', $options['GROUP BY'] )
1323                  : $options['GROUP BY'];
1324              $sql .= ' GROUP BY ' . $gb;
1325          }
1326          if ( isset( $options['HAVING'] ) ) {
1327              $having = is_array( $options['HAVING'] )
1328                  ? $this->makeList( $options['HAVING'], LIST_AND )
1329                  : $options['HAVING'];
1330              $sql .= ' HAVING ' . $having;
1331          }
1332          return $sql;
1333      }
1334  
1335      /**
1336       * Returns an optional ORDER BY
1337       *
1338       * @param array $options associative array of options
1339       * @return string
1340       * @see DatabaseBase::select()
1341       * @since 1.21
1342       */
1343  	public function makeOrderBy( $options ) {
1344          if ( isset( $options['ORDER BY'] ) ) {
1345              $ob = is_array( $options['ORDER BY'] )
1346                  ? implode( ',', $options['ORDER BY'] )
1347                  : $options['ORDER BY'];
1348              return ' ORDER BY ' . $ob;
1349          }
1350          return '';
1351      }
1352  
1353      /**
1354       * Execute a SELECT query constructed using the various parameters provided.
1355       * See below for full details of the parameters.
1356       *
1357       * @param string|array $table Table name
1358       * @param string|array $vars Field names
1359       * @param string|array $conds Conditions
1360       * @param string $fname Caller function name
1361       * @param array $options Query options
1362       * @param $join_conds Array Join conditions
1363       *
1364       * @param $table string|array
1365       *
1366       * May be either an array of table names, or a single string holding a table
1367       * name. If an array is given, table aliases can be specified, for example:
1368       *
1369       *    array( 'a' => 'user' )
1370       *
1371       * This includes the user table in the query, with the alias "a" available
1372       * for use in field names (e.g. a.user_name).
1373       *
1374       * All of the table names given here are automatically run through
1375       * DatabaseBase::tableName(), which causes the table prefix (if any) to be
1376       * added, and various other table name mappings to be performed.
1377       *
1378       *
1379       * @param $vars string|array
1380       *
1381       * May be either a field name or an array of field names. The field names
1382       * can be complete fragments of SQL, for direct inclusion into the SELECT
1383       * query. If an array is given, field aliases can be specified, for example:
1384       *
1385       *   array( 'maxrev' => 'MAX(rev_id)' )
1386       *
1387       * This includes an expression with the alias "maxrev" in the query.
1388       *
1389       * If an expression is given, care must be taken to ensure that it is
1390       * DBMS-independent.
1391       *
1392       *
1393       * @param $conds string|array
1394       *
1395       * May be either a string containing a single condition, or an array of
1396       * conditions. If an array is given, the conditions constructed from each
1397       * element are combined with AND.
1398       *
1399       * Array elements may take one of two forms:
1400       *
1401       *   - Elements with a numeric key are interpreted as raw SQL fragments.
1402       *   - Elements with a string key are interpreted as equality conditions,
1403       *     where the key is the field name.
1404       *     - If the value of such an array element is a scalar (such as a
1405       *       string), it will be treated as data and thus quoted appropriately.
1406       *       If it is null, an IS NULL clause will be added.
1407       *     - If the value is an array, an IN(...) clause will be constructed,
1408       *       such that the field name may match any of the elements in the
1409       *       array. The elements of the array will be quoted.
1410       *
1411       * Note that expressions are often DBMS-dependent in their syntax.
1412       * DBMS-independent wrappers are provided for constructing several types of
1413       * expression commonly used in condition queries. See:
1414       *    - DatabaseBase::buildLike()
1415       *    - DatabaseBase::conditional()
1416       *
1417       *
1418       * @param $options string|array
1419       *
1420       * Optional: Array of query options. Boolean options are specified by
1421       * including them in the array as a string value with a numeric key, for
1422       * example:
1423       *
1424       *    array( 'FOR UPDATE' )
1425       *
1426       * The supported options are:
1427       *
1428       *   - OFFSET: Skip this many rows at the start of the result set. OFFSET
1429       *     with LIMIT can theoretically be used for paging through a result set,
1430       *     but this is discouraged in MediaWiki for performance reasons.
1431       *
1432       *   - LIMIT: Integer: return at most this many rows. The rows are sorted
1433       *     and then the first rows are taken until the limit is reached. LIMIT
1434       *     is applied to a result set after OFFSET.
1435       *
1436       *   - FOR UPDATE: Boolean: lock the returned rows so that they can't be
1437       *     changed until the next COMMIT.
1438       *
1439       *   - DISTINCT: Boolean: return only unique result rows.
1440       *
1441       *   - GROUP BY: May be either an SQL fragment string naming a field or
1442       *     expression to group by, or an array of such SQL fragments.
1443       *
1444       *   - HAVING: May be either an string containing a HAVING clause or an array of
1445       *     conditions building the HAVING clause. If an array is given, the conditions
1446       *     constructed from each element are combined with AND.
1447       *
1448       *   - ORDER BY: May be either an SQL fragment giving a field name or
1449       *     expression to order by, or an array of such SQL fragments.
1450       *
1451       *   - USE INDEX: This may be either a string giving the index name to use
1452       *     for the query, or an array. If it is an associative array, each key
1453       *     gives the table name (or alias), each value gives the index name to
1454       *     use for that table. All strings are SQL fragments and so should be
1455       *     validated by the caller.
1456       *
1457       *   - EXPLAIN: In MySQL, this causes an EXPLAIN SELECT query to be run,
1458       *     instead of SELECT.
1459       *
1460       * And also the following boolean MySQL extensions, see the MySQL manual
1461       * for documentation:
1462       *
1463       *    - LOCK IN SHARE MODE
1464       *    - STRAIGHT_JOIN
1465       *    - HIGH_PRIORITY
1466       *    - SQL_BIG_RESULT
1467       *    - SQL_BUFFER_RESULT
1468       *    - SQL_SMALL_RESULT
1469       *    - SQL_CALC_FOUND_ROWS
1470       *    - SQL_CACHE
1471       *    - SQL_NO_CACHE
1472       *
1473       *
1474       * @param $join_conds string|array
1475       *
1476       * Optional associative array of table-specific join conditions. In the
1477       * most common case, this is unnecessary, since the join condition can be
1478       * in $conds. However, it is useful for doing a LEFT JOIN.
1479       *
1480       * The key of the array contains the table name or alias. The value is an
1481       * array with two elements, numbered 0 and 1. The first gives the type of
1482       * join, the second is an SQL fragment giving the join condition for that
1483       * table. For example:
1484       *
1485       *    array( 'page' => array( 'LEFT JOIN', 'page_latest=rev_id' ) )
1486       *
1487       * @return ResultWrapper. If the query returned no rows, a ResultWrapper
1488       *   with no rows in it will be returned. If there was a query error, a
1489       *   DBQueryError exception will be thrown, except if the "ignore errors"
1490       *   option was set, in which case false will be returned.
1491       */
1492  	public function select( $table, $vars, $conds = '', $fname = __METHOD__,
1493          $options = array(), $join_conds = array() ) {
1494          $sql = $this->selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
1495  
1496          return $this->query( $sql, $fname );
1497      }
1498  
1499      /**
1500       * The equivalent of DatabaseBase::select() except that the constructed SQL
1501       * is returned, instead of being immediately executed. This can be useful for
1502       * doing UNION queries, where the SQL text of each query is needed. In general,
1503       * however, callers outside of Database classes should just use select().
1504       *
1505       * @param string|array $table Table name
1506       * @param string|array $vars Field names
1507       * @param string|array $conds Conditions
1508       * @param string $fname Caller function name
1509       * @param string|array $options Query options
1510       * @param $join_conds string|array Join conditions
1511       *
1512       * @return string SQL query string.
1513       * @see DatabaseBase::select()
1514       */
1515  	public function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__,
1516          $options = array(), $join_conds = array() )
1517      {
1518          if ( is_array( $vars ) ) {
1519              $vars = implode( ',', $this->fieldNamesWithAlias( $vars ) );
1520          }
1521  
1522          $options = (array)$options;
1523          $useIndexes = ( isset( $options['USE INDEX'] ) && is_array( $options['USE INDEX'] ) )
1524              ? $options['USE INDEX']
1525              : array();
1526  
1527          if ( is_array( $table ) ) {
1528              $from = ' FROM ' .
1529                  $this->tableNamesWithUseIndexOrJOIN( $table, $useIndexes, $join_conds );
1530          } elseif ( $table != '' ) {
1531              if ( $table[0] == ' ' ) {
1532                  $from = ' FROM ' . $table;
1533              } else {
1534                  $from = ' FROM ' .
1535                      $this->tableNamesWithUseIndexOrJOIN( array( $table ), $useIndexes, array() );
1536              }
1537          } else {
1538              $from = '';
1539          }
1540  
1541          list( $startOpts, $useIndex, $preLimitTail, $postLimitTail ) =
1542              $this->makeSelectOptions( $options );
1543  
1544          if ( !empty( $conds ) ) {
1545              if ( is_array( $conds ) ) {
1546                  $conds = $this->makeList( $conds, LIST_AND );
1547              }
1548              $sql = "SELECT $startOpts $vars $from $useIndex WHERE $conds $preLimitTail";
1549          } else {
1550              $sql = "SELECT $startOpts $vars $from $useIndex $preLimitTail";
1551          }
1552  
1553          if ( isset( $options['LIMIT'] ) ) {
1554              $sql = $this->limitResult( $sql, $options['LIMIT'],
1555                  isset( $options['OFFSET'] ) ? $options['OFFSET'] : false );
1556          }
1557          $sql = "$sql $postLimitTail";
1558  
1559          if ( isset( $options['EXPLAIN'] ) ) {
1560              $sql = 'EXPLAIN ' . $sql;
1561          }
1562  
1563          return $sql;
1564      }
1565  
1566      /**
1567       * Single row SELECT wrapper. Equivalent to DatabaseBase::select(), except
1568       * that a single row object is returned. If the query returns no rows,
1569       * false is returned.
1570       *
1571       * @param string|array $table Table name
1572       * @param string|array $vars Field names
1573       * @param array $conds Conditions
1574       * @param string $fname Caller function name
1575       * @param string|array $options Query options
1576       * @param $join_conds array|string Join conditions
1577       *
1578       * @return object|bool
1579       */
1580  	public function selectRow( $table, $vars, $conds, $fname = __METHOD__,
1581          $options = array(), $join_conds = array() )
1582      {
1583          $options = (array)$options;
1584          $options['LIMIT'] = 1;
1585          $res = $this->select( $table, $vars, $conds, $fname, $options, $join_conds );
1586  
1587          if ( $res === false ) {
1588              return false;
1589          }
1590  
1591          if ( !$this->numRows( $res ) ) {
1592              return false;
1593          }
1594  
1595          $obj = $this->fetchObject( $res );
1596  
1597          return $obj;
1598      }
1599  
1600      /**
1601       * Estimate rows in dataset.
1602       *
1603       * MySQL allows you to estimate the number of rows that would be returned
1604       * by a SELECT query, using EXPLAIN SELECT. The estimate is provided using
1605       * index cardinality statistics, and is notoriously inaccurate, especially
1606       * when large numbers of rows have recently been added or deleted.
1607       *
1608       * For DBMSs that don't support fast result size estimation, this function
1609       * will actually perform the SELECT COUNT(*).
1610       *
1611       * Takes the same arguments as DatabaseBase::select().
1612       *
1613       * @param string $table table name
1614       * @param array|string $vars : unused
1615       * @param array|string $conds : filters on the table
1616       * @param string $fname function name for profiling
1617       * @param array $options options for select
1618       * @return Integer: row count
1619       */
1620  	public function estimateRowCount( $table, $vars = '*', $conds = '',
1621          $fname = __METHOD__, $options = array() )
1622      {
1623          $rows = 0;
1624          $res = $this->select( $table, array( 'rowcount' => 'COUNT(*)' ), $conds, $fname, $options );
1625  
1626          if ( $res ) {
1627              $row = $this->fetchRow( $res );
1628              $rows = ( isset( $row['rowcount'] ) ) ? $row['rowcount'] : 0;
1629          }
1630  
1631          return $rows;
1632      }
1633  
1634      /**
1635       * Removes most variables from an SQL query and replaces them with X or N for numbers.
1636       * It's only slightly flawed. Don't use for anything important.
1637       *
1638       * @param string $sql A SQL Query
1639       *
1640       * @return string
1641       */
1642  	static function generalizeSQL( $sql ) {
1643          # This does the same as the regexp below would do, but in such a way
1644          # as to avoid crashing php on some large strings.
1645          # $sql = preg_replace( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql );
1646  
1647          $sql = str_replace( "\\\\", '', $sql );
1648          $sql = str_replace( "\\'", '', $sql );
1649          $sql = str_replace( "\\\"", '', $sql );
1650          $sql = preg_replace( "/'.*'/s", "'X'", $sql );
1651          $sql = preg_replace( '/".*"/s', "'X'", $sql );
1652  
1653          # All newlines, tabs, etc replaced by single space
1654          $sql = preg_replace( '/\s+/', ' ', $sql );
1655  
1656          # All numbers => N
1657          $sql = preg_replace( '/-?\d+(,-?\d+)+/s', 'N,...,N', $sql );
1658          $sql = preg_replace( '/-?\d+/s', 'N', $sql );
1659  
1660          return $sql;
1661      }
1662  
1663      /**
1664       * Determines whether a field exists in a table
1665       *
1666       * @param string $table table name
1667       * @param string $field filed to check on that table
1668       * @param string $fname calling function name (optional)
1669       * @return Boolean: whether $table has filed $field
1670       */
1671  	public function fieldExists( $table, $field, $fname = __METHOD__ ) {
1672          $info = $this->fieldInfo( $table, $field );
1673  
1674          return (bool)$info;
1675      }
1676  
1677      /**
1678       * Determines whether an index exists
1679       * Usually throws a DBQueryError on failure
1680       * If errors are explicitly ignored, returns NULL on failure
1681       *
1682       * @param $table
1683       * @param $index
1684       * @param $fname string
1685       *
1686       * @return bool|null
1687       */
1688  	public function indexExists( $table, $index, $fname = __METHOD__ ) {
1689          if ( !$this->tableExists( $table ) ) {
1690              return null;
1691          }
1692  
1693          $info = $this->indexInfo( $table, $index, $fname );
1694          if ( is_null( $info ) ) {
1695              return null;
1696          } else {
1697              return $info !== false;
1698          }
1699      }
1700  
1701      /**
1702       * Query whether a given table exists
1703       *
1704       * @param $table string
1705       * @param $fname string
1706       *
1707       * @return bool
1708       */
1709  	public function tableExists( $table, $fname = __METHOD__ ) {
1710          $table = $this->tableName( $table );
1711          $old = $this->ignoreErrors( true );
1712          $res = $this->query( "SELECT 1 FROM $table LIMIT 1", $fname );
1713          $this->ignoreErrors( $old );
1714  
1715          return (bool)$res;
1716      }
1717  
1718      /**
1719       * mysql_field_type() wrapper
1720       * @param $res
1721       * @param $index
1722       * @return string
1723       */
1724  	public function fieldType( $res, $index ) {
1725          if ( $res instanceof ResultWrapper ) {
1726              $res = $res->result;
1727          }
1728  
1729          return mysql_field_type( $res, $index );
1730      }
1731  
1732      /**
1733       * Determines if a given index is unique
1734       *
1735       * @param $table string
1736       * @param $index string
1737       *
1738       * @return bool
1739       */
1740  	public function indexUnique( $table, $index ) {
1741          $indexInfo = $this->indexInfo( $table, $index );
1742  
1743          if ( !$indexInfo ) {
1744              return null;
1745          }
1746  
1747          return !$indexInfo[0]->Non_unique;
1748      }
1749  
1750      /**
1751       * Helper for DatabaseBase::insert().
1752       *
1753       * @param $options array
1754       * @return string
1755       */
1756  	protected function makeInsertOptions( $options ) {
1757          return implode( ' ', $options );
1758      }
1759  
1760      /**
1761       * INSERT wrapper, inserts an array into a table.
1762       *
1763       * $a may be either:
1764       *
1765       *   - A single associative array. The array keys are the field names, and
1766       *     the values are the values to insert. The values are treated as data
1767       *     and will be quoted appropriately. If NULL is inserted, this will be
1768       *     converted to a database NULL.
1769       *   - An array with numeric keys, holding a list of associative arrays.
1770       *     This causes a multi-row INSERT on DBMSs that support it. The keys in
1771       *     each subarray must be identical to each other, and in the same order.
1772       *
1773       * Usually throws a DBQueryError on failure. If errors are explicitly ignored,
1774       * returns success.
1775       *
1776       * $options is an array of options, with boolean options encoded as values
1777       * with numeric keys, in the same style as $options in
1778       * DatabaseBase::select(). Supported options are:
1779       *
1780       *   - IGNORE: Boolean: if present, duplicate key errors are ignored, and
1781       *     any rows which cause duplicate key errors are not inserted. It's
1782       *     possible to determine how many rows were successfully inserted using
1783       *     DatabaseBase::affectedRows().
1784       *
1785       * @param $table   String Table name. This will be passed through
1786       *                 DatabaseBase::tableName().
1787       * @param $a       Array of rows to insert
1788       * @param $fname   String Calling function name (use __METHOD__) for logs/profiling
1789       * @param array $options of options
1790       *
1791       * @return bool
1792       */
1793  	public function insert( $table, $a, $fname = __METHOD__, $options = array() ) {
1794          # No rows to insert, easy just return now
1795          if ( !count( $a ) ) {
1796              return true;
1797          }
1798  
1799          $table = $this->tableName( $table );
1800  
1801          if ( !is_array( $options ) ) {
1802              $options = array( $options );
1803          }
1804  
1805          $fh = null;
1806          if ( isset( $options['fileHandle'] ) ) {
1807              $fh = $options['fileHandle'];
1808          }
1809          $options = $this->makeInsertOptions( $options );
1810  
1811          if ( isset( $a[0] ) && is_array( $a[0] ) ) {
1812              $multi = true;
1813              $keys = array_keys( $a[0] );
1814          } else {
1815              $multi = false;
1816              $keys = array_keys( $a );
1817          }
1818  
1819          $sql = 'INSERT ' . $options .
1820              " INTO $table (" . implode( ',', $keys ) . ') VALUES ';
1821  
1822          if ( $multi ) {
1823              $first = true;
1824              foreach ( $a as $row ) {
1825                  if ( $first ) {
1826                      $first = false;
1827                  } else {
1828                      $sql .= ',';
1829                  }
1830                  $sql .= '(' . $this->makeList( $row ) . ')';
1831              }
1832          } else {
1833              $sql .= '(' . $this->makeList( $a ) . ')';
1834          }
1835  
1836          if ( $fh !== null && false === fwrite( $fh, $sql ) ) {
1837              return false;
1838          } elseif ( $fh !== null ) {
1839              return true;
1840          }
1841  
1842          return (bool)$this->query( $sql, $fname );
1843      }
1844  
1845      /**
1846       * Make UPDATE options for the DatabaseBase::update function
1847       *
1848       * @param array $options The options passed to DatabaseBase::update
1849       * @return string
1850       */
1851  	protected function makeUpdateOptions( $options ) {
1852          if ( !is_array( $options ) ) {
1853              $options = array( $options );
1854          }
1855  
1856          $opts = array();
1857  
1858          if ( in_array( 'LOW_PRIORITY', $options ) ) {
1859              $opts[] = $this->lowPriorityOption();
1860          }
1861  
1862          if ( in_array( 'IGNORE', $options ) ) {
1863              $opts[] = 'IGNORE';
1864          }
1865  
1866          return implode( ' ', $opts );
1867      }
1868  
1869      /**
1870       * UPDATE wrapper. Takes a condition array and a SET array.
1871       *
1872       * @param $table  String name of the table to UPDATE. This will be passed through
1873       *                DatabaseBase::tableName().
1874       *
1875       * @param array $values  An array of values to SET. For each array element,
1876       *                the key gives the field name, and the value gives the data
1877       *                to set that field to. The data will be quoted by
1878       *                DatabaseBase::addQuotes().
1879       *
1880       * @param $conds  Array:  An array of conditions (WHERE). See
1881       *                DatabaseBase::select() for the details of the format of
1882       *                condition arrays. Use '*' to update all rows.
1883       *
1884       * @param $fname  String: The function name of the caller (from __METHOD__),
1885       *                for logging and profiling.
1886       *
1887       * @param array $options An array of UPDATE options, can be:
1888       *                   - IGNORE: Ignore unique key conflicts
1889       *                   - LOW_PRIORITY: MySQL-specific, see MySQL manual.
1890       * @return Boolean
1891       */
1892  	function update( $table, $values, $conds, $fname = __METHOD__, $options = array() ) {
1893          $table = $this->tableName( $table );
1894          $opts = $this->makeUpdateOptions( $options );
1895          $sql = "UPDATE $opts $table SET " . $this->makeList( $values, LIST_SET );
1896  
1897          if ( $conds !== array() && $conds !== '*' ) {
1898              $sql .= " WHERE " . $this->makeList( $conds, LIST_AND );
1899          }
1900  
1901          return $this->query( $sql, $fname );
1902      }
1903  
1904      /**
1905       * Makes an encoded list of strings from an array
1906       * @param array $a containing the data
1907       * @param int $mode Constant
1908       *      - LIST_COMMA:          comma separated, no field names
1909       *      - LIST_AND:            ANDed WHERE clause (without the WHERE). See
1910       *        the documentation for $conds in DatabaseBase::select().
1911       *      - LIST_OR:             ORed WHERE clause (without the WHERE)
1912       *      - LIST_SET:            comma separated with field names, like a SET clause
1913       *      - LIST_NAMES:          comma separated field names
1914       *
1915       * @throws MWException|DBUnexpectedError
1916       * @return string
1917       */
1918  	public function makeList( $a, $mode = LIST_COMMA ) {
1919          if ( !is_array( $a ) ) {
1920              throw new DBUnexpectedError( $this, 'DatabaseBase::makeList called with incorrect parameters' );
1921          }
1922  
1923          $first = true;
1924          $list = '';
1925  
1926          foreach ( $a as $field => $value ) {
1927              if ( !$first ) {
1928                  if ( $mode == LIST_AND ) {
1929                      $list .= ' AND ';
1930                  } elseif ( $mode == LIST_OR ) {
1931                      $list .= ' OR ';
1932                  } else {
1933                      $list .= ',';
1934                  }
1935              } else {
1936                  $first = false;
1937              }
1938  
1939              if ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_numeric( $field ) ) {
1940                  $list .= "($value)";
1941              } elseif ( ( $mode == LIST_SET ) && is_numeric( $field ) ) {
1942                  $list .= "$value";
1943              } elseif ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_array( $value ) ) {
1944                  if ( count( $value ) == 0 ) {
1945                      throw new MWException( __METHOD__ . ": empty input for field $field" );
1946                  } elseif ( count( $value ) == 1 ) {
1947                      // Special-case single values, as IN isn't terribly efficient
1948                      // Don't necessarily assume the single key is 0; we don't
1949                      // enforce linear numeric ordering on other arrays here.
1950                      $value = array_values( $value );
1951                      $list .= $field . " = " . $this->addQuotes( $value[0] );
1952                  } else {
1953                      $list .= $field . " IN (" . $this->makeList( $value ) . ") ";
1954                  }
1955              } elseif ( $value === null ) {
1956                  if ( $mode == LIST_AND || $mode == LIST_OR ) {
1957                      $list .= "$field IS ";
1958                  } elseif ( $mode == LIST_SET ) {
1959                      $list .= "$field = ";
1960                  }
1961                  $list .= 'NULL';
1962              } else {
1963                  if ( $mode == LIST_AND || $mode == LIST_OR || $mode == LIST_SET ) {
1964                      $list .= "$field = ";
1965                  }
1966                  $list .= $mode == LIST_NAMES ? $value : $this->addQuotes( $value );
1967              }
1968          }
1969  
1970          return $list;
1971      }
1972  
1973      /**
1974       * Build a partial where clause from a 2-d array such as used for LinkBatch.
1975       * The keys on each level may be either integers or strings.
1976       *
1977       * @param array $data organized as 2-d
1978       *              array(baseKeyVal => array(subKeyVal => [ignored], ...), ...)
1979       * @param string $baseKey field name to match the base-level keys to (eg 'pl_namespace')
1980       * @param string $subKey field name to match the sub-level keys to (eg 'pl_title')
1981       * @return Mixed: string SQL fragment, or false if no items in array.
1982       */
1983  	public function makeWhereFrom2d( $data, $baseKey, $subKey ) {
1984          $conds = array();
1985  
1986          foreach ( $data as $base => $sub ) {
1987              if ( count( $sub ) ) {
1988                  $conds[] = $this->makeList(
1989                      array( $baseKey => $base, $subKey => array_keys( $sub ) ),
1990                      LIST_AND );
1991              }
1992          }
1993  
1994          if ( $conds ) {
1995              return $this->makeList( $conds, LIST_OR );
1996          } else {
1997              // Nothing to search for...
1998              return false;
1999          }
2000      }
2001  
2002      /**
2003       * Return aggregated value alias
2004       *
2005       * @param $valuedata
2006       * @param $valuename string
2007       *
2008       * @return string
2009       */
2010  	public function aggregateValue( $valuedata, $valuename = 'value' ) {
2011          return $valuename;
2012      }
2013  
2014      /**
2015       * @param $field
2016       * @return string
2017       */
2018  	public function bitNot( $field ) {
2019          return "(~$field)";
2020      }
2021  
2022      /**
2023       * @param $fieldLeft
2024       * @param $fieldRight
2025       * @return string
2026       */
2027  	public function bitAnd( $fieldLeft, $fieldRight ) {
2028          return "($fieldLeft & $fieldRight)";
2029      }
2030  
2031      /**
2032       * @param  $fieldLeft
2033       * @param  $fieldRight
2034       * @return string
2035       */
2036  	public function bitOr( $fieldLeft, $fieldRight ) {
2037          return "($fieldLeft | $fieldRight)";
2038      }
2039  
2040      /**
2041       * Build a concatenation list to feed into a SQL query
2042       * @param array $stringList list of raw SQL expressions; caller is responsible for any quoting
2043       * @return String
2044       */
2045  	public function buildConcat( $stringList ) {
2046          return 'CONCAT(' . implode( ',', $stringList ) . ')';
2047      }
2048  
2049      /**
2050       * Change the current database
2051       *
2052       * @todo Explain what exactly will fail if this is not overridden.
2053       *
2054       * @param $db
2055       *
2056       * @return bool Success or failure
2057       */
2058  	public function selectDB( $db ) {
2059          # Stub.  Shouldn't cause serious problems if it's not overridden, but
2060          # if your database engine supports a concept similar to MySQL's
2061          # databases you may as well.
2062          $this->mDBname = $db;
2063          return true;
2064      }
2065  
2066      /**
2067       * Get the current DB name
2068       */
2069  	public function getDBname() {
2070          return $this->mDBname;
2071      }
2072  
2073      /**
2074       * Get the server hostname or IP address
2075       */
2076  	public function getServer() {
2077          return $this->mServer;
2078      }
2079  
2080      /**
2081       * Format a table name ready for use in constructing an SQL query
2082       *
2083       * This does two important things: it quotes the table names to clean them up,
2084       * and it adds a table prefix if only given a table name with no quotes.
2085       *
2086       * All functions of this object which require a table name call this function
2087       * themselves. Pass the canonical name to such functions. This is only needed
2088       * when calling query() directly.
2089       *
2090       * @param string $name database table name
2091       * @param string $format One of:
2092       *   quoted - Automatically pass the table name through addIdentifierQuotes()
2093       *            so that it can be used in a query.
2094       *   raw - Do not add identifier quotes to the table name
2095       * @return String: full database name
2096       */
2097  	public function tableName( $name, $format = 'quoted' ) {
2098          global $wgSharedDB, $wgSharedPrefix, $wgSharedTables;
2099          # Skip the entire process when we have a string quoted on both ends.
2100          # Note that we check the end so that we will still quote any use of
2101          # use of `database`.table. But won't break things if someone wants
2102          # to query a database table with a dot in the name.
2103          if ( $this->isQuotedIdentifier( $name ) ) {
2104              return $name;
2105          }
2106  
2107          # Lets test for any bits of text that should never show up in a table
2108          # name. Basically anything like JOIN or ON which are actually part of
2109          # SQL queries, but may end up inside of the table value to combine
2110          # sql. Such as how the API is doing.
2111          # Note that we use a whitespace test rather than a \b test to avoid
2112          # any remote case where a word like on may be inside of a table name
2113          # surrounded by symbols which may be considered word breaks.
2114          if ( preg_match( '/(^|\s)(DISTINCT|JOIN|ON|AS)(\s|$)/i', $name ) !== 0 ) {
2115              return $name;
2116          }
2117  
2118          # Split database and table into proper variables.
2119          # We reverse the explode so that database.table and table both output
2120          # the correct table.
2121          $dbDetails = explode( '.', $name, 2 );
2122          if ( count( $dbDetails ) == 2 ) {
2123              list( $database, $table ) = $dbDetails;
2124              # We don't want any prefix added in this case
2125              $prefix = '';
2126          } else {
2127              list( $table ) = $dbDetails;
2128              if ( $wgSharedDB !== null # We have a shared database
2129                  && $this->mForeign == false # We're not working on a foreign database
2130                  && !$this->isQuotedIdentifier( $table ) # Paranoia check to prevent shared tables listing '`table`'
2131                  && in_array( $table, $wgSharedTables ) # A shared table is selected
2132              ) {
2133                  $database = $wgSharedDB;
2134                  $prefix = $wgSharedPrefix === null ? $this->mTablePrefix : $wgSharedPrefix;
2135              } else {
2136                  $database = null;
2137                  $prefix = $this->mTablePrefix; # Default prefix
2138              }
2139          }
2140  
2141          # Quote $table and apply the prefix if not quoted.
2142          $tableName = "{$prefix}{$table}";
2143          if ( $format == 'quoted' && !$this->isQuotedIdentifier( $tableName ) ) {
2144              $tableName = $this->addIdentifierQuotes( $tableName );
2145          }
2146  
2147          # Quote $database and merge it with the table name if needed
2148          if ( $database !== null ) {
2149              if ( $format == 'quoted' && !$this->isQuotedIdentifier( $database ) ) {
2150                  $database = $this->addIdentifierQuotes( $database );
2151              }
2152              $tableName = $database . '.' . $tableName;
2153          }
2154  
2155          return $tableName;
2156      }
2157  
2158      /**
2159       * Fetch a number of table names into an array
2160       * This is handy when you need to construct SQL for joins
2161       *
2162       * Example:
2163       * extract( $dbr->tableNames( 'user', 'watchlist' ) );
2164       * $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user
2165       *         WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
2166       *
2167       * @return array
2168       */
2169  	public function tableNames() {
2170          $inArray = func_get_args();
2171          $retVal = array();
2172  
2173          foreach ( $inArray as $name ) {
2174              $retVal[$name] = $this->tableName( $name );
2175          }
2176  
2177          return $retVal;
2178      }
2179  
2180      /**
2181       * Fetch a number of table names into an zero-indexed numerical array
2182       * This is handy when you need to construct SQL for joins
2183       *
2184       * Example:
2185       * list( $user, $watchlist ) = $dbr->tableNamesN( 'user', 'watchlist' );
2186       * $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user
2187       *         WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
2188       *
2189       * @return array
2190       */
2191  	public function tableNamesN() {
2192          $inArray = func_get_args();
2193          $retVal = array();
2194  
2195          foreach ( $inArray as $name ) {
2196              $retVal[] = $this->tableName( $name );
2197          }
2198  
2199          return $retVal;
2200      }
2201  
2202      /**
2203       * Get an aliased table name
2204       * e.g. tableName AS newTableName
2205       *
2206       * @param string $name Table name, see tableName()
2207       * @param string|bool $alias Alias (optional)
2208       * @return string SQL name for aliased table. Will not alias a table to its own name
2209       */
2210  	public function tableNameWithAlias( $name, $alias = false ) {
2211          if ( !$alias || $alias == $name ) {
2212              return $this->tableName( $name );
2213          } else {
2214              return $this->tableName( $name ) . ' ' . $this->addIdentifierQuotes( $alias );
2215          }
2216      }
2217  
2218      /**
2219       * Gets an array of aliased table names
2220       *
2221       * @param $tables array( [alias] => table )
2222       * @return array of strings, see tableNameWithAlias()
2223       */
2224  	public function tableNamesWithAlias( $tables ) {
2225          $retval = array();
2226          foreach ( $tables as $alias => $table ) {
2227              if ( is_numeric( $alias ) ) {
2228                  $alias = $table;
2229              }
2230              $retval[] = $this->tableNameWithAlias( $table, $alias );
2231          }
2232          return $retval;
2233      }
2234  
2235      /**
2236       * Get an aliased field name
2237       * e.g. fieldName AS newFieldName
2238       *
2239       * @param string $name Field name
2240       * @param string|bool $alias Alias (optional)
2241       * @return string SQL name for aliased field. Will not alias a field to its own name
2242       */
2243  	public function fieldNameWithAlias( $name, $alias = false ) {
2244          if ( !$alias || (string)$alias === (string)$name ) {
2245              return $name;
2246          } else {
2247              return $name . ' AS ' . $alias; //PostgreSQL needs AS
2248          }
2249      }
2250  
2251      /**
2252       * Gets an array of aliased field names
2253       *
2254       * @param $fields array( [alias] => field )
2255       * @return array of strings, see fieldNameWithAlias()
2256       */
2257  	public function fieldNamesWithAlias( $fields ) {
2258          $retval = array();
2259          foreach ( $fields as $alias => $field ) {
2260              if ( is_numeric( $alias ) ) {
2261                  $alias = $field;
2262              }
2263              $retval[] = $this->fieldNameWithAlias( $field, $alias );
2264          }
2265          return $retval;
2266      }
2267  
2268      /**
2269       * Get the aliased table name clause for a FROM clause
2270       * which might have a JOIN and/or USE INDEX clause
2271       *
2272       * @param array $tables ( [alias] => table )
2273       * @param $use_index array Same as for select()
2274       * @param $join_conds array Same as for select()
2275       * @return string
2276       */
2277  	protected function tableNamesWithUseIndexOrJOIN(
2278          $tables, $use_index = array(), $join_conds = array()
2279      ) {
2280          $ret = array();
2281          $retJOIN = array();
2282          $use_index = (array)$use_index;
2283          $join_conds = (array)$join_conds;
2284  
2285          foreach ( $tables as $alias => $table ) {
2286              if ( !is_string( $alias ) ) {
2287                  // No alias? Set it equal to the table name
2288                  $alias = $table;
2289              }
2290              // Is there a JOIN clause for this table?
2291              if ( isset( $join_conds[$alias] ) ) {
2292                  list( $joinType, $conds ) = $join_conds[$alias];
2293                  $tableClause = $joinType;
2294                  $tableClause .= ' ' . $this->tableNameWithAlias( $table, $alias );
2295                  if ( isset( $use_index[$alias] ) ) { // has USE INDEX?
2296                      $use = $this->useIndexClause( implode( ',', (array)$use_index[$alias] ) );
2297                      if ( $use != '' ) {
2298                          $tableClause .= ' ' . $use;
2299                      }
2300                  }
2301                  $on = $this->makeList( (array)$conds, LIST_AND );
2302                  if ( $on != '' ) {
2303                      $tableClause .= ' ON (' . $on . ')';
2304                  }
2305  
2306                  $retJOIN[] = $tableClause;
2307              // Is there an INDEX clause for this table?
2308              } elseif ( isset( $use_index[$alias] ) ) {
2309                  $tableClause = $this->tableNameWithAlias( $table, $alias );
2310                  $tableClause .= ' ' . $this->useIndexClause(
2311                      implode( ',', (array)$use_index[$alias] ) );
2312  
2313                  $ret[] = $tableClause;
2314              } else {
2315                  $tableClause = $this->tableNameWithAlias( $table, $alias );
2316  
2317                  $ret[] = $tableClause;
2318              }
2319          }
2320  
2321          // We can't separate explicit JOIN clauses with ',', use ' ' for those
2322          $implicitJoins = !empty( $ret ) ? implode( ',', $ret ) : "";
2323          $explicitJoins = !empty( $retJOIN ) ? implode( ' ', $retJOIN ) : "";
2324  
2325          // Compile our final table clause
2326          return implode( ' ', array( $implicitJoins, $explicitJoins ) );
2327      }
2328  
2329      /**
2330       * Get the name of an index in a given table
2331       *
2332       * @param $index
2333       *
2334       * @return string
2335       */
2336  	protected function indexName( $index ) {
2337          // Backwards-compatibility hack
2338          $renamed = array(
2339              'ar_usertext_timestamp' => 'usertext_timestamp',
2340              'un_user_id' => 'user_id',
2341              'un_user_ip' => 'user_ip',
2342          );
2343  
2344          if ( isset( $renamed[$index] ) ) {
2345              return $renamed[$index];
2346          } else {
2347              return $index;
2348          }
2349      }
2350  
2351      /**
2352       * Adds quotes and backslashes.
2353       *
2354       * @param $s string
2355       *
2356       * @return string
2357       */
2358  	public function addQuotes( $s ) {
2359          if ( $s === null ) {
2360              return 'NULL';
2361          } else {
2362              # This will also quote numeric values. This should be harmless,
2363              # and protects against weird problems that occur when they really
2364              # _are_ strings such as article titles and string->number->string
2365              # conversion is not 1:1.
2366              return "'" . $this->strencode( $s ) . "'";
2367          }
2368      }
2369  
2370      /**
2371       * Quotes an identifier using `backticks` or "double quotes" depending on the database type.
2372       * MySQL uses `backticks` while basically everything else uses double quotes.
2373       * Since MySQL is the odd one out here the double quotes are our generic
2374       * and we implement backticks in DatabaseMysql.
2375       *
2376       * @param $s string
2377       *
2378       * @return string
2379       */
2380  	public function addIdentifierQuotes( $s ) {
2381          return '"' . str_replace( '"', '""', $s ) . '"';
2382      }
2383  
2384      /**
2385       * Returns if the given identifier looks quoted or not according to
2386       * the database convention for quoting identifiers .
2387       *
2388       * @param $name string
2389       *
2390       * @return boolean
2391       */
2392  	public function isQuotedIdentifier( $name ) {
2393          return $name[0] == '"' && substr( $name, -1, 1 ) == '"';
2394      }
2395  
2396      /**
2397       * @param $s string
2398       * @return string
2399       */
2400  	protected function escapeLikeInternal( $s ) {
2401          $s = str_replace( '\\', '\\\\', $s );
2402          $s = $this->strencode( $s );
2403          $s = str_replace( array( '%', '_' ), array( '\%', '\_' ), $s );
2404  
2405          return $s;
2406      }
2407  
2408      /**
2409       * LIKE statement wrapper, receives a variable-length argument list with parts of pattern to match
2410       * containing either string literals that will be escaped or tokens returned by anyChar() or anyString().
2411       * Alternatively, the function could be provided with an array of aforementioned parameters.
2412       *
2413       * Example: $dbr->buildLike( 'My_page_title/', $dbr->anyString() ) returns a LIKE clause that searches
2414       * for subpages of 'My page title'.
2415       * Alternatively: $pattern = array( 'My_page_title/', $dbr->anyString() ); $query .= $dbr->buildLike( $pattern );
2416       *
2417       * @since 1.16
2418       * @return String: fully built LIKE statement
2419       */
2420  	public function buildLike() {
2421          $params = func_get_args();
2422  
2423          if ( count( $params ) > 0 && is_array( $params[0] ) ) {
2424              $params = $params[0];
2425          }
2426  
2427          $s = '';
2428  
2429          foreach ( $params as $value ) {
2430              if ( $value instanceof LikeMatch ) {
2431                  $s .= $value->toString();
2432              } else {
2433                  $s .= $this->escapeLikeInternal( $value );
2434              }
2435          }
2436  
2437          return " LIKE '" . $s . "' ";
2438      }
2439  
2440      /**
2441       * Returns a token for buildLike() that denotes a '_' to be used in a LIKE query
2442       *
2443       * @return LikeMatch
2444       */
2445  	public function anyChar() {
2446          return new LikeMatch( '_' );
2447      }
2448  
2449      /**
2450       * Returns a token for buildLike() that denotes a '%' to be used in a LIKE query
2451       *
2452       * @return LikeMatch
2453       */
2454  	public function anyString() {
2455          return new LikeMatch( '%' );
2456      }
2457  
2458      /**
2459       * Returns an appropriately quoted sequence value for inserting a new row.
2460       * MySQL has autoincrement fields, so this is just NULL. But the PostgreSQL
2461       * subclass will return an integer, and save the value for insertId()
2462       *
2463       * Any implementation of this function should *not* involve reusing
2464       * sequence numbers created for rolled-back transactions.
2465       * See http://bugs.mysql.com/bug.php?id=30767 for details.
2466       * @param $seqName string
2467       * @return null
2468       */
2469  	public function nextSequenceValue( $seqName ) {
2470          return null;
2471      }
2472  
2473      /**
2474       * USE INDEX clause.  Unlikely to be useful for anything but MySQL.  This
2475       * is only needed because a) MySQL must be as efficient as possible due to
2476       * its use on Wikipedia, and b) MySQL 4.0 is kind of dumb sometimes about
2477       * which index to pick.  Anyway, other databases might have different
2478       * indexes on a given table.  So don't bother overriding this unless you're
2479       * MySQL.
2480       * @param $index
2481       * @return string
2482       */
2483  	public function useIndexClause( $index ) {
2484          return '';
2485      }
2486  
2487      /**
2488       * REPLACE query wrapper.
2489       *
2490       * REPLACE is a very handy MySQL extension, which functions like an INSERT
2491       * except that when there is a duplicate key error, the old row is deleted
2492       * and the new row is inserted in its place.
2493       *
2494       * We simulate this with standard SQL with a DELETE followed by INSERT. To
2495       * perform the delete, we need to know what the unique indexes are so that
2496       * we know how to find the conflicting rows.
2497       *
2498       * It may be more efficient to leave off unique indexes which are unlikely
2499       * to collide. However if you do this, you run the risk of encountering
2500       * errors which wouldn't have occurred in MySQL.
2501       *
2502       * @param string $table The table to replace the row(s) in.
2503       * @param array $rows Can be either a single row to insert, or multiple rows,
2504       *    in the same format as for DatabaseBase::insert()
2505       * @param array $uniqueIndexes is an array of indexes. Each element may be either
2506       *    a field name or an array of field names
2507       * @param string $fname Calling function name (use __METHOD__) for logs/profiling
2508       */
2509  	public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
2510          $quotedTable = $this->tableName( $table );
2511  
2512          if ( count( $rows ) == 0 ) {
2513              return;
2514          }
2515  
2516          # Single row case
2517          if ( !is_array( reset( $rows ) ) ) {
2518              $rows = array( $rows );
2519          }
2520  
2521          foreach ( $rows as $row ) {
2522              # Delete rows which collide
2523              if ( $uniqueIndexes ) {
2524                  $sql = "DELETE FROM $quotedTable WHERE ";
2525                  $first = true;
2526                  foreach ( $uniqueIndexes as $index ) {
2527                      if ( $first ) {
2528                          $first = false;
2529                          $sql .= '( ';
2530                      } else {
2531                          $sql .= ' ) OR ( ';
2532                      }
2533                      if ( is_array( $index ) ) {
2534                          $first2 = true;
2535                          foreach ( $index as $col ) {
2536                              if ( $first2 ) {
2537                                  $first2 = false;
2538                              } else {
2539                                  $sql .= ' AND ';
2540                              }
2541                              $sql .= $col . '=' . $this->addQuotes( $row[$col] );
2542                          }
2543                      } else {
2544                          $sql .= $index . '=' . $this->addQuotes( $row[$index] );
2545                      }
2546                  }
2547                  $sql .= ' )';
2548                  $this->query( $sql, $fname );
2549              }
2550  
2551              # Now insert the row
2552              $this->insert( $table, $row, $fname );
2553          }
2554      }
2555  
2556      /**
2557       * REPLACE query wrapper for MySQL and SQLite, which have a native REPLACE
2558       * statement.
2559       *
2560       * @param string $table Table name
2561       * @param array $rows Rows to insert
2562       * @param string $fname Caller function name
2563       *
2564       * @return ResultWrapper
2565       */
2566  	protected function nativeReplace( $table, $rows, $fname ) {
2567          $table = $this->tableName( $table );
2568  
2569          # Single row case
2570          if ( !is_array( reset( $rows ) ) ) {
2571              $rows = array( $rows );
2572          }
2573  
2574          $sql = "REPLACE INTO $table (" . implode( ',', array_keys( $rows[0] ) ) . ') VALUES ';
2575          $first = true;
2576  
2577          foreach ( $rows as $row ) {
2578              if ( $first ) {
2579                  $first = false;
2580              } else {
2581                  $sql .= ',';
2582              }
2583  
2584              $sql .= '(' . $this->makeList( $row ) . ')';
2585          }
2586  
2587          return $this->query( $sql, $fname );
2588      }
2589  
2590      /**
2591       * INSERT ON DUPLICATE KEY UPDATE wrapper, upserts an array into a table.
2592       *
2593       * This updates any conflicting rows (according to the unique indexes) using
2594       * the provided SET clause and inserts any remaining (non-conflicted) rows.
2595       *
2596       * $rows may be either:
2597       *   - A single associative array. The array keys are the field names, and
2598       *     the values are the values to insert. The values are treated as data
2599       *     and will be quoted appropriately. If NULL is inserted, this will be
2600       *     converted to a database NULL.
2601       *   - An array with numeric keys, holding a list of associative arrays.
2602       *     This causes a multi-row INSERT on DBMSs that support it. The keys in
2603       *     each subarray must be identical to each other, and in the same order.
2604       *
2605       * It may be more efficient to leave off unique indexes which are unlikely
2606       * to collide. However if you do this, you run the risk of encountering
2607       * errors which wouldn't have occurred in MySQL.
2608       *
2609       * Usually throws a DBQueryError on failure. If errors are explicitly ignored,
2610       * returns success.
2611       *
2612       * @param string $table Table name. This will be passed through DatabaseBase::tableName().
2613       * @param array $rows A single row or list of rows to insert
2614       * @param array $uniqueIndexes List of single field names or field name tuples
2615       * @param array $set An array of values to SET. For each array element,
2616       *                the key gives the field name, and the value gives the data
2617       *                to set that field to. The data will be quoted by
2618       *                DatabaseBase::addQuotes().
2619       * @param string $fname Calling function name (use __METHOD__) for logs/profiling
2620       * @param array $options of options
2621       *
2622       * @return bool
2623       * @since 1.22
2624       */
2625  	public function upsert(
2626          $table, array $rows, array $uniqueIndexes, array $set, $fname = __METHOD__
2627      ) {
2628          if ( !count( $rows ) ) {
2629              return true; // nothing to do
2630          }
2631          $rows = is_array( reset( $rows ) ) ? $rows : array( $rows );
2632  
2633          if ( count( $uniqueIndexes ) ) {
2634              $clauses = array(); // list WHERE clauses that each identify a single row
2635              foreach ( $rows as $row ) {
2636                  foreach ( $uniqueIndexes as $index ) {
2637                      $index = is_array( $index ) ? $index : array( $index ); // columns
2638                      $rowKey = array(); // unique key to this row
2639                      foreach ( $index as $column ) {
2640                          $rowKey[$column] = $row[$column];
2641                      }
2642                      $clauses[] = $this->makeList( $rowKey, LIST_AND );
2643                  }
2644              }
2645              $where = array( $this->makeList( $clauses, LIST_OR ) );
2646          } else {
2647              $where = false;
2648          }
2649  
2650          $useTrx = !$this->mTrxLevel;
2651          if ( $useTrx ) {
2652              $this->begin( $fname );
2653          }
2654          try {
2655              # Update any existing conflicting row(s)
2656              if ( $where !== false ) {
2657                  $ok = $this->update( $table, $set, $where, $fname );
2658              } else {
2659                  $ok = true;
2660              }
2661              # Now insert any non-conflicting row(s)
2662              $ok = $this->insert( $table, $rows, $fname, array( 'IGNORE' ) ) && $ok;
2663          } catch ( Exception $e ) {
2664              if ( $useTrx ) {
2665                  $this->rollback( $fname );
2666              }
2667              throw $e;
2668          }
2669          if ( $useTrx ) {
2670              $this->commit( $fname );
2671          }
2672  
2673          return $ok;
2674      }
2675  
2676      /**
2677       * DELETE where the condition is a join.
2678       *
2679       * MySQL overrides this to use a multi-table DELETE syntax, in other databases
2680       * we use sub-selects
2681       *
2682       * For safety, an empty $conds will not delete everything. If you want to
2683       * delete all rows where the join condition matches, set $conds='*'.
2684       *
2685       * DO NOT put the join condition in $conds.
2686       *
2687       * @param $delTable   String: The table to delete from.
2688       * @param $joinTable  String: The other table.
2689       * @param $delVar     String: The variable to join on, in the first table.
2690       * @param $joinVar    String: The variable to join on, in the second table.
2691       * @param $conds      Array: Condition array of field names mapped to variables,
2692       *                    ANDed together in the WHERE clause
2693       * @param $fname      String: Calling function name (use __METHOD__) for
2694       *                    logs/profiling
2695       * @throws DBUnexpectedError
2696       */
2697  	public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds,
2698          $fname = __METHOD__ )
2699      {
2700          if ( !$conds ) {
2701              throw new DBUnexpectedError( $this,
2702                  'DatabaseBase::deleteJoin() called with empty $conds' );
2703          }
2704  
2705          $delTable = $this->tableName( $delTable );
2706          $joinTable = $this->tableName( $joinTable );
2707          $sql = "DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable ";
2708          if ( $conds != '*' ) {
2709              $sql .= 'WHERE ' . $this->makeList( $conds, LIST_AND );
2710          }
2711          $sql .= ')';
2712  
2713          $this->query( $sql, $fname );
2714      }
2715  
2716      /**
2717       * Returns the size of a text field, or -1 for "unlimited"
2718       *
2719       * @param $table string
2720       * @param $field string
2721       *
2722       * @return int
2723       */
2724  	public function textFieldSize( $table, $field ) {
2725          $table = $this->tableName( $table );
2726          $sql = "SHOW COLUMNS FROM $table LIKE \"$field\";";
2727          $res = $this->query( $sql, 'DatabaseBase::textFieldSize' );
2728          $row = $this->fetchObject( $res );
2729  
2730          $m = array();
2731  
2732          if ( preg_match( '/\((.*)\)/', $row->Type, $m ) ) {
2733              $size = $m[1];
2734          } else {
2735              $size = -1;
2736          }
2737  
2738          return $size;
2739      }
2740  
2741      /**
2742       * A string to insert into queries to show that they're low-priority, like
2743       * MySQL's LOW_PRIORITY.  If no such feature exists, return an empty
2744       * string and nothing bad should happen.
2745       *
2746       * @return string Returns the text of the low priority option if it is
2747       *   supported, or a blank string otherwise
2748       */
2749  	public function lowPriorityOption() {
2750          return '';
2751      }
2752  
2753      /**
2754       * DELETE query wrapper.
2755       *
2756       * @param array $table Table name
2757       * @param string|array $conds of conditions. See $conds in DatabaseBase::select() for
2758       *               the format. Use $conds == "*" to delete all rows
2759       * @param string $fname name of the calling function
2760       *
2761       * @throws DBUnexpectedError
2762       * @return bool|ResultWrapper
2763       */
2764  	public function delete( $table, $conds, $fname = __METHOD__ ) {
2765          if ( !$conds ) {
2766              throw new DBUnexpectedError( $this, 'DatabaseBase::delete() called with no conditions' );
2767          }
2768  
2769          $table = $this->tableName( $table );
2770          $sql = "DELETE FROM $table";
2771  
2772          if ( $conds != '*' ) {
2773              if ( is_array( $conds ) ) {
2774                  $conds = $this->makeList( $conds, LIST_AND );
2775              }
2776              $sql .= ' WHERE ' . $conds;
2777          }
2778  
2779          return $this->query( $sql, $fname );
2780      }
2781  
2782      /**
2783       * INSERT SELECT wrapper. Takes data from a SELECT query and inserts it
2784       * into another table.
2785       *
2786       * @param string $destTable The table name to insert into
2787       * @param string|array $srcTable May be either a table name, or an array of table names
2788       *    to include in a join.
2789       *
2790       * @param array $varMap must be an associative array of the form
2791       *    array( 'dest1' => 'source1', ...). Source items may be literals
2792       *    rather than field names, but strings should be quoted with
2793       *    DatabaseBase::addQuotes()
2794       *
2795       * @param array $conds Condition array. See $conds in DatabaseBase::select() for
2796       *    the details of the format of condition arrays. May be "*" to copy the
2797       *    whole table.
2798       *
2799       * @param string $fname The function name of the caller, from __METHOD__
2800       *
2801       * @param array $insertOptions Options for the INSERT part of the query, see
2802       *    DatabaseBase::insert() for details.
2803       * @param array $selectOptions Options for the SELECT part of the query, see
2804       *    DatabaseBase::select() for details.
2805       *
2806       * @return ResultWrapper
2807       */
2808  	public function insertSelect( $destTable, $srcTable, $varMap, $conds,
2809          $fname = __METHOD__,
2810          $insertOptions = array(), $selectOptions = array() )
2811      {
2812          $destTable = $this->tableName( $destTable );
2813  
2814          if ( is_array( $insertOptions ) ) {
2815              $insertOptions = implode( ' ', $insertOptions );
2816          }
2817  
2818          if ( !is_array( $selectOptions ) ) {
2819              $selectOptions = array( $selectOptions );
2820          }
2821  
2822          list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
2823  
2824          if ( is_array( $srcTable ) ) {
2825              $srcTable = implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) );
2826          } else {
2827              $srcTable = $this->tableName( $srcTable );
2828          }
2829  
2830          $sql = "INSERT $insertOptions INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
2831              " SELECT $startOpts " . implode( ',', $varMap ) .
2832              " FROM $srcTable $useIndex ";
2833  
2834          if ( $conds != '*' ) {
2835              if ( is_array( $conds ) ) {
2836                  $conds = $this->makeList( $conds, LIST_AND );
2837              }
2838              $sql .= " WHERE $conds";
2839          }
2840  
2841          $sql .= " $tailOpts";
2842  
2843          return $this->query( $sql, $fname );
2844      }
2845  
2846      /**
2847       * Construct a LIMIT query with optional offset.  This is used for query
2848       * pages.  The SQL should be adjusted so that only the first $limit rows
2849       * are returned.  If $offset is provided as well, then the first $offset
2850       * rows should be discarded, and the next $limit rows should be returned.
2851       * If the result of the query is not ordered, then the rows to be returned
2852       * are theoretically arbitrary.
2853       *
2854       * $sql is expected to be a SELECT, if that makes a difference.
2855       *
2856       * The version provided by default works in MySQL and SQLite.  It will very
2857       * likely need to be overridden for most other DBMSes.
2858       *
2859       * @param string $sql SQL query we will append the limit too
2860       * @param $limit Integer the SQL limit
2861       * @param $offset Integer|bool the SQL offset (default false)
2862       *
2863       * @throws DBUnexpectedError
2864       * @return string
2865       */
2866  	public function limitResult( $sql, $limit, $offset = false ) {
2867          if ( !is_numeric( $limit ) ) {
2868              throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" );
2869          }
2870          return "$sql LIMIT "
2871              . ( ( is_numeric( $offset ) && $offset != 0 ) ? "{$offset}," : "" )
2872              . "{$limit} ";
2873      }
2874  
2875      /**
2876       * Returns true if current database backend supports ORDER BY or LIMIT for separate subqueries
2877       * within the UNION construct.
2878       * @return Boolean
2879       */
2880  	public function unionSupportsOrderAndLimit() {
2881          return true; // True for almost every DB supported
2882      }
2883  
2884      /**
2885       * Construct a UNION query
2886       * This is used for providing overload point for other DB abstractions
2887       * not compatible with the MySQL syntax.
2888       * @param array $sqls SQL statements to combine
2889       * @param $all Boolean: use UNION ALL
2890       * @return String: SQL fragment
2891       */
2892  	public function unionQueries( $sqls, $all ) {
2893          $glue = $all ? ') UNION ALL (' : ') UNION (';
2894          return '(' . implode( $glue, $sqls ) . ')';
2895      }
2896  
2897      /**
2898       * Returns an SQL expression for a simple conditional.  This doesn't need
2899       * to be overridden unless CASE isn't supported in your DBMS.
2900       *
2901       * @param string|array $cond SQL expression which will result in a boolean value
2902       * @param string $trueVal SQL expression to return if true
2903       * @param string $falseVal SQL expression to return if false
2904       * @return String: SQL fragment
2905       */
2906  	public function conditional( $cond, $trueVal, $falseVal ) {
2907          if ( is_array( $cond ) ) {
2908              $cond = $this->makeList( $cond, LIST_AND );
2909          }
2910          return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
2911      }
2912  
2913      /**
2914       * Returns a comand for str_replace function in SQL query.
2915       * Uses REPLACE() in MySQL
2916       *
2917       * @param string $orig column to modify
2918       * @param string $old column to seek
2919       * @param string $new column to replace with
2920       *
2921       * @return string
2922       */
2923  	public function strreplace( $orig, $old, $new ) {
2924          return "REPLACE({$orig}, {$old}, {$new})";
2925      }
2926  
2927      /**
2928       * Determines how long the server has been up
2929       * STUB
2930       *
2931       * @return int
2932       */
2933  	public function getServerUptime() {
2934          return 0;
2935      }
2936  
2937      /**
2938       * Determines if the last failure was due to a deadlock
2939       * STUB
2940       *
2941       * @return bool
2942       */
2943  	public function wasDeadlock() {
2944          return false;
2945      }
2946  
2947      /**
2948       * Determines if the last failure was due to a lock timeout
2949       * STUB
2950       *
2951       * @return bool
2952       */
2953  	public function wasLockTimeout() {
2954          return false;
2955      }
2956  
2957      /**
2958       * Determines if the last query error was something that should be dealt
2959       * with by pinging the connection and reissuing the query.
2960       * STUB
2961       *
2962       * @return bool
2963       */
2964  	public function wasErrorReissuable() {
2965          return false;
2966      }
2967  
2968      /**
2969       * Determines if the last failure was due to the database being read-only.
2970       * STUB
2971       *
2972       * @return bool
2973       */
2974  	public function wasReadOnlyError() {
2975          return false;
2976      }
2977  
2978      /**
2979       * Perform a deadlock-prone transaction.
2980       *
2981       * This function invokes a callback function to perform a set of write
2982       * queries. If a deadlock occurs during the processing, the transaction
2983       * will be rolled back and the callback function will be called again.
2984       *
2985       * Usage:
2986       *   $dbw->deadlockLoop( callback, ... );
2987       *
2988       * Extra arguments are passed through to the specified callback function.
2989       *
2990       * Returns whatever the callback function returned on its successful,
2991       * iteration, or false on error, for example if the retry limit was
2992       * reached.
2993       *
2994       * @return bool
2995       */
2996  	public function deadlockLoop() {
2997          $this->begin( __METHOD__ );
2998          $args = func_get_args();
2999          $function = array_shift( $args );
3000          $oldIgnore = $this->ignoreErrors( true );
3001          $tries = self::DEADLOCK_TRIES;
3002  
3003          if ( is_array( $function ) ) {
3004              $fname = $function[0];
3005          } else {
3006              $fname = $function;
3007          }
3008  
3009          do {
3010              $retVal = call_user_func_array( $function, $args );
3011              $error = $this->lastError();
3012              $errno = $this->lastErrno();
3013              $sql = $this->lastQuery();
3014  
3015              if ( $errno ) {
3016                  if ( $this->wasDeadlock() ) {
3017                      # Retry
3018                      usleep( mt_rand( self::DEADLOCK_DELAY_MIN, self::DEADLOCK_DELAY_MAX ) );
3019                  } else {
3020                      $this->reportQueryError( $error, $errno, $sql, $fname );
3021                  }
3022              }
3023          } while ( $this->wasDeadlock() && --$tries > 0 );
3024  
3025          $this->ignoreErrors( $oldIgnore );
3026  
3027          if ( $tries <= 0 ) {
3028              $this->rollback( __METHOD__ );
3029              $this->reportQueryError( $error, $errno, $sql, $fname );
3030              return false;
3031          } else {
3032              $this->commit( __METHOD__ );
3033              return $retVal;
3034          }
3035      }
3036  
3037      /**
3038       * Wait for the slave to catch up to a given master position.
3039       *
3040       * @param $pos DBMasterPos object
3041       * @param $timeout Integer: the maximum number of seconds to wait for
3042       *   synchronisation
3043       *
3044       * @return integer: zero if the slave was past that position already,
3045       *   greater than zero if we waited for some period of time, less than
3046       *   zero if we timed out.
3047       */
3048  	public function masterPosWait( DBMasterPos $pos, $timeout ) {
3049          wfProfileIn( __METHOD__ );
3050  
3051          if ( !is_null( $this->mFakeSlaveLag ) ) {
3052              $wait = intval( ( $pos->pos - microtime( true ) + $this->mFakeSlaveLag ) * 1e6 );
3053  
3054              if ( $wait > $timeout * 1e6 ) {
3055                  wfDebug( "Fake slave timed out waiting for $pos ($wait us)\n" );
3056                  wfProfileOut( __METHOD__ );
3057                  return -1;
3058              } elseif ( $wait > 0 ) {
3059                  wfDebug( "Fake slave waiting $wait us\n" );
3060                  usleep( $wait );
3061                  wfProfileOut( __METHOD__ );
3062                  return 1;
3063              } else {
3064                  wfDebug( "Fake slave up to date ($wait us)\n" );
3065                  wfProfileOut( __METHOD__ );
3066                  return 0;
3067              }
3068          }
3069  
3070          wfProfileOut( __METHOD__ );
3071  
3072          # Real waits are implemented in the subclass.
3073          return 0;
3074      }
3075  
3076      /**
3077       * Get the replication position of this slave
3078       *
3079       * @return DBMasterPos, or false if this is not a slave.
3080       */
3081  	public function getSlavePos() {
3082          if ( !is_null( $this->mFakeSlaveLag ) ) {
3083              $pos = new MySQLMasterPos( 'fake', microtime( true ) - $this->mFakeSlaveLag );
3084              wfDebug( __METHOD__ . ": fake slave pos = $pos\n" );
3085              return $pos;
3086          } else {
3087              # Stub
3088              return false;
3089          }
3090      }
3091  
3092      /**
3093       * Get the position of this master
3094       *
3095       * @return DBMasterPos, or false if this is not a master
3096       */
3097  	public function getMasterPos() {
3098          if ( $this->mFakeMaster ) {
3099              return new MySQLMasterPos( 'fake', microtime( true ) );
3100          } else {
3101              return false;
3102          }
3103      }
3104  
3105      /**
3106       * Run an anonymous function as soon as there is no transaction pending.
3107       * If there is a transaction and it is rolled back, then the callback is cancelled.
3108       * Queries in the function will run in AUTO-COMMIT mode unless there are begin() calls.
3109       * Callbacks must commit any transactions that they begin.
3110       *
3111       * This is useful for updates to different systems or when separate transactions are needed.
3112       * For example, one might want to enqueue jobs into a system outside the database, but only
3113       * after the database is updated so that the jobs will see the data when they actually run.
3114       * It can also be used for updates that easily cause deadlocks if locks are held too long.
3115       *
3116       * @param callable $callback
3117       * @since 1.20
3118       */
3119  	final public function onTransactionIdle( $callback ) {
3120          $this->mTrxIdleCallbacks[] = array( $callback, wfGetCaller() );
3121          if ( !$this->mTrxLevel ) {
3122              $this->runOnTransactionIdleCallbacks();
3123          }
3124      }
3125  
3126      /**
3127       * Run an anonymous function before the current transaction commits or now if there is none.
3128       * If there is a transaction and it is rolled back, then the callback is cancelled.
3129       * Callbacks must not start nor commit any transactions.
3130       *
3131       * This is useful for updates that easily cause deadlocks if locks are held too long
3132       * but where atomicity is strongly desired for these updates and some related updates.
3133       *
3134       * @param callable $callback
3135       * @since 1.22
3136       */
3137  	final public function onTransactionPreCommitOrIdle( $callback ) {
3138          if ( $this->mTrxLevel ) {
3139              $this->mTrxPreCommitCallbacks[] = array( $callback, wfGetCaller() );
3140          } else {
3141              $this->onTransactionIdle( $callback ); // this will trigger immediately
3142          }
3143      }
3144  
3145      /**
3146       * Actually any "on transaction idle" callbacks.
3147       *
3148       * @since 1.20
3149       */
3150  	protected function runOnTransactionIdleCallbacks() {
3151          $autoTrx = $this->getFlag( DBO_TRX ); // automatic begin() enabled?
3152  
3153          $e = null; // last exception
3154          do { // callbacks may add callbacks :)
3155              $callbacks = $this->mTrxIdleCallbacks;
3156              $this->mTrxIdleCallbacks = array(); // recursion guard
3157              foreach ( $callbacks as $callback ) {
3158                  try {
3159                      list( $phpCallback ) = $callback;
3160                      $this->clearFlag( DBO_TRX ); // make each query its own transaction
3161                      call_user_func( $phpCallback );
3162                      $this->setFlag( $autoTrx ? DBO_TRX : 0 ); // restore automatic begin()
3163                  } catch ( Exception $e ) {}
3164              }
3165          } while ( count( $this->mTrxIdleCallbacks ) );
3166  
3167          if ( $e instanceof Exception ) {
3168              throw $e; // re-throw any last exception
3169          }
3170      }
3171  
3172      /**
3173       * Actually any "on transaction pre-commit" callbacks.
3174       *
3175       * @since 1.22
3176       */
3177  	protected function runOnTransactionPreCommitCallbacks() {
3178          $e = null; // last exception
3179          do { // callbacks may add callbacks :)
3180              $callbacks = $this->mTrxPreCommitCallbacks;
3181              $this->mTrxPreCommitCallbacks = array(); // recursion guard
3182              foreach ( $callbacks as $callback ) {
3183                  try {
3184                      list( $phpCallback ) = $callback;
3185                      call_user_func( $phpCallback );
3186                  } catch ( Exception $e ) {}
3187              }
3188          } while ( count( $this->mTrxPreCommitCallbacks ) );
3189  
3190          if ( $e instanceof Exception ) {
3191              throw $e; // re-throw any last exception
3192          }
3193      }
3194  
3195      /**
3196       * Begin a transaction. If a transaction is already in progress, that transaction will be committed before the
3197       * new transaction is started.
3198       *
3199       * Note that when the DBO_TRX flag is set (which is usually the case for web requests, but not for maintenance scripts),
3200       * any previous database query will have started a transaction automatically.
3201       *
3202       * Nesting of transactions is not supported. Attempts to nest transactions will cause a warning, unless the current
3203       * transaction was started automatically because of the DBO_TRX flag.
3204       *
3205       * @param $fname string
3206       */
3207  	final public function begin( $fname = __METHOD__ ) {
3208          global $wgDebugDBTransactions;
3209  
3210          if ( $this->mTrxLevel ) { // implicit commit
3211              if ( !$this->mTrxAutomatic ) {
3212                  // We want to warn about inadvertently nested begin/commit pairs, but not about
3213                  // auto-committing implicit transactions that were started by query() via DBO_TRX
3214                  $msg = "$fname: Transaction already in progress (from {$this->mTrxFname}), " .
3215                      " performing implicit commit!";
3216                  wfWarn( $msg );
3217                  wfLogDBError( $msg );
3218              } else {
3219                  // if the transaction was automatic and has done write operations,
3220                  // log it if $wgDebugDBTransactions is enabled.
3221                  if ( $this->mTrxDoneWrites && $wgDebugDBTransactions ) {
3222                      wfDebug( "$fname: Automatic transaction with writes in progress" .
3223                          " (from {$this->mTrxFname}), performing implicit commit!\n"
3224                      );
3225                  }
3226              }
3227  
3228              $this->runOnTransactionPreCommitCallbacks();
3229              $this->doCommit( $fname );
3230              if ( $this->mTrxDoneWrites ) {
3231                  Profiler::instance()->transactionWritingOut( $this->mServer, $this->mDBname );
3232              }
3233              $this->runOnTransactionIdleCallbacks();
3234          }
3235  
3236          $this->doBegin( $fname );
3237          $this->mTrxFname = $fname;
3238          $this->mTrxDoneWrites = false;
3239          $this->mTrxAutomatic = false;
3240      }
3241  
3242      /**
3243       * Issues the BEGIN command to the database server.
3244       *
3245       * @see DatabaseBase::begin()
3246       * @param type $fname
3247       */
3248  	protected function doBegin( $fname ) {
3249          $this->query( 'BEGIN', $fname );
3250          $this->mTrxLevel = 1;
3251      }
3252  
3253      /**
3254       * Commits a transaction previously started using begin().
3255       * If no transaction is in progress, a warning is issued.
3256       *
3257       * Nesting of transactions is not supported.
3258       *
3259       * @param $fname string
3260       * @param string $flush Flush flag, set to 'flush' to disable warnings about explicitly committing implicit
3261       *        transactions, or calling commit when no transaction is in progress.
3262       *        This will silently break any ongoing explicit transaction. Only set the flush flag if you are sure
3263       *        that it is safe to ignore these warnings in your context.
3264       */
3265  	final public function commit( $fname = __METHOD__, $flush = '' ) {
3266          if ( $flush != 'flush' ) {
3267              if ( !$this->mTrxLevel ) {
3268                  wfWarn( "$fname: No transaction to commit, something got out of sync!" );
3269              } elseif ( $this->mTrxAutomatic ) {
3270                  wfWarn( "$fname: Explicit commit of implicit transaction. Something may be out of sync!" );
3271              }
3272          } else {
3273              if ( !$this->mTrxLevel ) {
3274                  return; // nothing to do
3275              } elseif ( !$this->mTrxAutomatic ) {
3276                  wfWarn( "$fname: Flushing an explicit transaction, getting out of sync!" );
3277              }
3278          }
3279  
3280          $this->runOnTransactionPreCommitCallbacks();
3281          $this->doCommit( $fname );
3282          if ( $this->mTrxDoneWrites ) {
3283              Profiler::instance()->transactionWritingOut( $this->mServer, $this->mDBname );
3284          }
3285          $this->mTrxDoneWrites = false;
3286          $this->runOnTransactionIdleCallbacks();
3287      }
3288  
3289      /**
3290       * Issues the COMMIT command to the database server.
3291       *
3292       * @see DatabaseBase::commit()
3293       * @param type $fname
3294       */
3295  	protected function doCommit( $fname ) {
3296          if ( $this->mTrxLevel ) {
3297              $this->query( 'COMMIT', $fname );
3298              $this->mTrxLevel = 0;
3299          }
3300      }
3301  
3302      /**
3303       * Rollback a transaction previously started using begin().
3304       * If no transaction is in progress, a warning is issued.
3305       *
3306       * No-op on non-transactional databases.
3307       *
3308       * @param $fname string
3309       */
3310  	final public function rollback( $fname = __METHOD__ ) {
3311          if ( !$this->mTrxLevel ) {
3312              wfWarn( "$fname: No transaction to rollback, something got out of sync!" );
3313          }
3314          $this->doRollback( $fname );
3315          $this->mTrxIdleCallbacks = array(); // cancel
3316          $this->mTrxPreCommitCallbacks = array(); // cancel
3317          if ( $this->mTrxDoneWrites ) {
3318              Profiler::instance()->transactionWritingOut( $this->mServer, $this->mDBname );
3319          }
3320          $this->mTrxDoneWrites = false;
3321      }
3322  
3323      /**
3324       * Issues the ROLLBACK command to the database server.
3325       *
3326       * @see DatabaseBase::rollback()
3327       * @param type $fname
3328       */
3329  	protected function doRollback( $fname ) {
3330          if ( $this->mTrxLevel ) {
3331              $this->query( 'ROLLBACK', $fname, true );
3332              $this->mTrxLevel = 0;
3333          }
3334      }
3335  
3336      /**
3337       * Creates a new table with structure copied from existing table
3338       * Note that unlike most database abstraction functions, this function does not
3339       * automatically append database prefix, because it works at a lower
3340       * abstraction level.
3341       * The table names passed to this function shall not be quoted (this
3342       * function calls addIdentifierQuotes when needed).
3343       *
3344       * @param string $oldName name of table whose structure should be copied
3345       * @param string $newName name of table to be created
3346       * @param $temporary Boolean: whether the new table should be temporary
3347       * @param string $fname calling function name
3348       * @throws MWException
3349       * @return Boolean: true if operation was successful
3350       */
3351  	public function duplicateTableStructure( $oldName, $newName, $temporary = false,
3352          $fname = __METHOD__
3353      ) {
3354          throw new MWException(
3355              'DatabaseBase::duplicateTableStructure is not implemented in descendant class' );
3356      }
3357  
3358      /**
3359       * List all tables on the database
3360       *
3361       * @param string $prefix Only show tables with this prefix, e.g. mw_
3362       * @param string $fname calling function name
3363       * @throws MWException
3364       */
3365  	function listTables( $prefix = null, $fname = __METHOD__ ) {
3366          throw new MWException( 'DatabaseBase::listTables is not implemented in descendant class' );
3367      }
3368  
3369      /**
3370       * Reset the views process cache set by listViews()
3371       * @since 1.22
3372       */
3373  	final public function clearViewsCache() {
3374          $this->allViews = null;
3375      }
3376  
3377      /**
3378       * Lists all the VIEWs in the database
3379       *
3380       * For caching purposes the list of all views should be stored in
3381       * $this->allViews. The process cache can be cleared with clearViewsCache()
3382       *
3383       * @param string $prefix   Only show VIEWs with this prefix, eg. unit_test_
3384       * @param string $fname    Name of calling function
3385       * @throws MWException
3386       * @since 1.22
3387       */
3388  	public function listViews( $prefix = null, $fname = __METHOD__ ) {
3389          throw new MWException( 'DatabaseBase::listViews is not implemented in descendant class' );
3390      }
3391  
3392      /**
3393       * Differentiates between a TABLE and a VIEW
3394       *
3395       * @param $name string: Name of the database-structure to test.
3396       * @throws MWException
3397       * @since 1.22
3398       */
3399  	public function isView( $name ) {
3400          throw new MWException( 'DatabaseBase::isView is not implemented in descendant class' );
3401      }
3402  
3403      /**
3404       * Convert a timestamp in one of the formats accepted by wfTimestamp()
3405       * to the format used for inserting into timestamp fields in this DBMS.
3406       *
3407       * The result is unquoted, and needs to be passed through addQuotes()
3408       * before it can be included in raw SQL.
3409       *
3410       * @param $ts string|int
3411       *
3412       * @return string
3413       */
3414  	public function timestamp( $ts = 0 ) {
3415          return wfTimestamp( TS_MW, $ts );
3416      }
3417  
3418      /**
3419       * Convert a timestamp in one of the formats accepted by wfTimestamp()
3420       * to the format used for inserting into timestamp fields in this DBMS. If
3421       * NULL is input, it is passed through, allowing NULL values to be inserted
3422       * into timestamp fields.
3423       *
3424       * The result is unquoted, and needs to be passed through addQuotes()
3425       * before it can be included in raw SQL.
3426       *
3427       * @param $ts string|int
3428       *
3429       * @return string
3430       */
3431  	public function timestampOrNull( $ts = null ) {
3432          if ( is_null( $ts ) ) {
3433              return null;
3434          } else {
3435              return $this->timestamp( $ts );
3436          }
3437      }
3438  
3439      /**
3440       * Take the result from a query, and wrap it in a ResultWrapper if
3441       * necessary. Boolean values are passed through as is, to indicate success
3442       * of write queries or failure.
3443       *
3444       * Once upon a time, DatabaseBase::query() returned a bare MySQL result
3445       * resource, and it was necessary to call this function to convert it to
3446       * a wrapper. Nowadays, raw database objects are never exposed to external
3447       * callers, so this is unnecessary in external code. For compatibility with
3448       * old code, ResultWrapper objects are passed through unaltered.
3449       *
3450       * @param $result bool|ResultWrapper
3451       *
3452       * @return bool|ResultWrapper
3453       */
3454  	public function resultObject( $result ) {
3455          if ( empty( $result ) ) {
3456              return false;
3457          } elseif ( $result instanceof ResultWrapper ) {
3458              return $result;
3459          } elseif ( $result === true ) {
3460              // Successful write query
3461              return $result;
3462          } else {
3463              return new ResultWrapper( $this, $result );
3464          }
3465      }
3466  
3467      /**
3468       * Ping the server and try to reconnect if it there is no connection
3469       *
3470       * @return bool Success or failure
3471       */
3472  	public function ping() {
3473          # Stub.  Not essential to override.
3474          return true;
3475      }
3476  
3477      /**
3478       * Get slave lag. Currently supported only by MySQL.
3479       *
3480       * Note that this function will generate a fatal error on many
3481       * installations. Most callers should use LoadBalancer::safeGetLag()
3482       * instead.
3483       *
3484       * @return int Database replication lag in seconds
3485       */
3486  	public function getLag() {
3487          return intval( $this->mFakeSlaveLag );
3488      }
3489  
3490      /**
3491       * Return the maximum number of items allowed in a list, or 0 for unlimited.
3492       *
3493       * @return int
3494       */
3495  	function maxListLen() {
3496          return 0;
3497      }
3498  
3499      /**
3500       * Some DBMSs have a special format for inserting into blob fields, they
3501       * don't allow simple quoted strings to be inserted. To insert into such
3502       * a field, pass the data through this function before passing it to
3503       * DatabaseBase::insert().
3504       * @param $b string
3505       * @return string
3506       */
3507  	public function encodeBlob( $b ) {
3508          return $b;
3509      }
3510  
3511      /**
3512       * Some DBMSs return a special placeholder object representing blob fields
3513       * in result objects. Pass the object through this function to return the
3514       * original string.
3515       * @param $b string
3516       * @return string
3517       */
3518  	public function decodeBlob( $b ) {
3519          return $b;
3520      }
3521  
3522      /**
3523       * Override database's default behavior. $options include:
3524       *     'connTimeout' : Set the connection timeout value in seconds.
3525       *                     May be useful for very long batch queries such as
3526       *                     full-wiki dumps, where a single query reads out over
3527       *                     hours or days.
3528       *
3529       * @param $options Array
3530       * @return void
3531       */
3532  	public function setSessionOptions( array $options ) {
3533      }
3534  
3535      /**
3536       * Read and execute SQL commands from a file.
3537       *
3538       * Returns true on success, error string or exception on failure (depending
3539       * on object's error ignore settings).
3540       *
3541       * @param string $filename File name to open
3542       * @param bool|callable $lineCallback Optional function called before reading each line
3543       * @param bool|callable $resultCallback Optional function called for each MySQL result
3544       * @param bool|string $fname Calling function name or false if name should be
3545       *      generated dynamically using $filename
3546       * @param bool|callable $inputCallback Callback: Optional function called for each complete line sent
3547       * @throws MWException
3548       * @throws Exception|MWException
3549       * @return bool|string
3550       */
3551  	public function sourceFile(
3552          $filename, $lineCallback = false, $resultCallback = false, $fname = false, $inputCallback = false
3553      ) {
3554          wfSuppressWarnings();
3555          $fp = fopen( $filename, 'r' );
3556          wfRestoreWarnings();
3557  
3558          if ( false === $fp ) {
3559              throw new MWException( "Could not open \"{$filename}\".\n" );
3560          }
3561  
3562          if ( !$fname ) {
3563              $fname = __METHOD__ . "( $filename )";
3564          }
3565  
3566          try {
3567              $error = $this->sourceStream( $fp, $lineCallback, $resultCallback, $fname, $inputCallback );
3568          }
3569          catch ( MWException $e ) {
3570              fclose( $fp );
3571              throw $e;
3572          }
3573  
3574          fclose( $fp );
3575  
3576          return $error;
3577      }
3578  
3579      /**
3580       * Get the full path of a patch file. Originally based on archive()
3581       * from updaters.inc. Keep in mind this always returns a patch, as
3582       * it fails back to MySQL if no DB-specific patch can be found
3583       *
3584       * @param string $patch The name of the patch, like patch-something.sql
3585       * @return String Full path to patch file
3586       */
3587  	public function patchPath( $patch ) {
3588          global $IP;
3589  
3590          $dbType = $this->getType();
3591          if ( file_exists( "$IP/maintenance/$dbType/archives/$patch" ) ) {
3592              return "$IP/maintenance/$dbType/archives/$patch";
3593          } else {
3594              return "$IP/maintenance/archives/$patch";
3595          }
3596      }
3597  
3598      /**
3599       * Set variables to be used in sourceFile/sourceStream, in preference to the
3600       * ones in $GLOBALS. If an array is set here, $GLOBALS will not be used at
3601       * all. If it's set to false, $GLOBALS will be used.
3602       *
3603       * @param bool|array $vars mapping variable name to value.
3604       */
3605  	public function setSchemaVars( $vars ) {
3606          $this->mSchemaVars = $vars;
3607      }
3608  
3609      /**
3610       * Read and execute commands from an open file handle.
3611       *
3612       * Returns true on success, error string or exception on failure (depending
3613       * on object's error ignore settings).
3614       *
3615       * @param $fp Resource: File handle
3616       * @param $lineCallback Callback: Optional function called before reading each query
3617       * @param $resultCallback Callback: Optional function called for each MySQL result
3618       * @param string $fname Calling function name
3619       * @param $inputCallback Callback: Optional function called for each complete query sent
3620       * @return bool|string
3621       */
3622  	public function sourceStream( $fp, $lineCallback = false, $resultCallback = false,
3623          $fname = __METHOD__, $inputCallback = false )
3624      {
3625          $cmd = '';
3626  
3627          while ( !feof( $fp ) ) {
3628              if ( $lineCallback ) {
3629                  call_user_func( $lineCallback );
3630              }
3631  
3632              $line = trim( fgets( $fp ) );
3633  
3634              if ( $line == '' ) {
3635                  continue;
3636              }
3637  
3638              if ( '-' == $line[0] && '-' == $line[1] ) {
3639                  continue;
3640              }
3641  
3642              if ( $cmd != '' ) {
3643                  $cmd .= ' ';
3644              }
3645  
3646              $done = $this->streamStatementEnd( $cmd, $line );
3647  
3648              $cmd .= "$line\n";
3649  
3650              if ( $done || feof( $fp ) ) {
3651                  $cmd = $this->replaceVars( $cmd );
3652  
3653                  if ( ( $inputCallback && call_user_func( $inputCallback, $cmd ) ) || !$inputCallback ) {
3654                      $res = $this->query( $cmd, $fname );
3655  
3656                      if ( $resultCallback ) {
3657                          call_user_func( $resultCallback, $res, $this );
3658                      }
3659  
3660                      if ( false === $res ) {
3661                          $err = $this->lastError();
3662                          return "Query \"{$cmd}\" failed with error code \"$err\".\n";
3663                      }
3664                  }
3665                  $cmd = '';
3666              }
3667          }
3668  
3669          return true;
3670      }
3671  
3672      /**
3673       * Called by sourceStream() to check if we've reached a statement end
3674       *
3675       * @param string $sql SQL assembled so far
3676       * @param string $newLine New line about to be added to $sql
3677       * @return Bool Whether $newLine contains end of the statement
3678       */
3679  	public function streamStatementEnd( &$sql, &$newLine ) {
3680          if ( $this->delimiter ) {
3681              $prev = $newLine;
3682              $newLine = preg_replace( '/' . preg_quote( $this->delimiter, '/' ) . '$/', '', $newLine );
3683              if ( $newLine != $prev ) {
3684                  return true;
3685              }
3686          }
3687          return false;
3688      }
3689  
3690      /**
3691       * Database independent variable replacement. Replaces a set of variables
3692       * in an SQL statement with their contents as given by $this->getSchemaVars().
3693       *
3694       * Supports '{$var}' `{$var}` and / *$var* / (without the spaces) style variables.
3695       *
3696       * - '{$var}' should be used for text and is passed through the database's
3697       *   addQuotes method.
3698       * - `{$var}` should be used for identifiers (eg: table and database names),
3699       *   it is passed through the database's addIdentifierQuotes method which
3700       *   can be overridden if the database uses something other than backticks.
3701       * - / *$var* / is just encoded, besides traditional table prefix and
3702       *   table options its use should be avoided.
3703       *
3704       * @param string $ins SQL statement to replace variables in
3705       * @return String The new SQL statement with variables replaced
3706       */
3707  	protected function replaceSchemaVars( $ins ) {
3708          $vars = $this->getSchemaVars();
3709          foreach ( $vars as $var => $value ) {
3710              // replace '{$var}'
3711              $ins = str_replace( '\'{$' . $var . '}\'', $this->addQuotes( $value ), $ins );
3712              // replace `{$var}`
3713              $ins = str_replace( '`{$' . $var . '}`', $this->addIdentifierQuotes( $value ), $ins );
3714              // replace /*$var*/
3715              $ins = str_replace( '/*$' . $var . '*/', $this->strencode( $value ), $ins );
3716          }
3717          return $ins;
3718      }
3719  
3720      /**
3721       * Replace variables in sourced SQL
3722       *
3723       * @param $ins string
3724       *
3725       * @return string
3726       */
3727  	protected function replaceVars( $ins ) {
3728          $ins = $this->replaceSchemaVars( $ins );
3729  
3730          // Table prefixes
3731          $ins = preg_replace_callback( '!/\*(?:\$wgDBprefix|_)\*/([a-zA-Z_0-9]*)!',
3732              array( $this, 'tableNameCallback' ), $ins );
3733  
3734          // Index names
3735          $ins = preg_replace_callback( '!/\*i\*/([a-zA-Z_0-9]*)!',
3736              array( $this, 'indexNameCallback' ), $ins );
3737  
3738          return $ins;
3739      }
3740  
3741      /**
3742       * Get schema variables. If none have been set via setSchemaVars(), then
3743       * use some defaults from the current object.
3744       *
3745       * @return array
3746       */
3747  	protected function getSchemaVars() {
3748          if ( $this->mSchemaVars ) {
3749              return $this->mSchemaVars;
3750          } else {
3751              return $this->getDefaultSchemaVars();
3752          }
3753      }
3754  
3755      /**
3756       * Get schema variables to use if none have been set via setSchemaVars().
3757       *
3758       * Override this in derived classes to provide variables for tables.sql
3759       * and SQL patch files.
3760       *
3761       * @return array
3762       */
3763  	protected function getDefaultSchemaVars() {
3764          return array();
3765      }
3766  
3767      /**
3768       * Table name callback
3769       *
3770       * @param $matches array
3771       *
3772       * @return string
3773       */
3774  	protected function tableNameCallback( $matches ) {
3775          return $this->tableName( $matches[1] );
3776      }
3777  
3778      /**
3779       * Index name callback
3780       *
3781       * @param $matches array
3782       *
3783       * @return string
3784       */
3785  	protected function indexNameCallback( $matches ) {
3786          return $this->indexName( $matches[1] );
3787      }
3788  
3789      /**
3790       * Check to see if a named lock is available. This is non-blocking.
3791       *
3792       * @param string $lockName name of lock to poll
3793       * @param string $method name of method calling us
3794       * @return Boolean
3795       * @since 1.20
3796       */
3797  	public function lockIsFree( $lockName, $method ) {
3798          return true;
3799      }
3800  
3801      /**
3802       * Acquire a named lock
3803       *
3804       * Abstracted from Filestore::lock() so child classes can implement for
3805       * their own needs.
3806       *
3807       * @param string $lockName name of lock to aquire
3808       * @param string $method name of method calling us
3809       * @param $timeout Integer: timeout
3810       * @return Boolean
3811       */
3812  	public function lock( $lockName, $method, $timeout = 5 ) {
3813          return true;
3814      }
3815  
3816      /**
3817       * Release a lock.
3818       *
3819       * @param string $lockName Name of lock to release
3820       * @param string $method Name of method calling us
3821       *
3822       * @return int Returns 1 if the lock was released, 0 if the lock was not established
3823       * by this thread (in which case the lock is not released), and NULL if the named
3824       * lock did not exist
3825       */
3826  	public function unlock( $lockName, $method ) {
3827          return true;
3828      }
3829  
3830      /**
3831       * Lock specific tables
3832       *
3833       * @param array $read of tables to lock for read access
3834       * @param array $write of tables to lock for write access
3835       * @param string $method name of caller
3836       * @param bool $lowPriority Whether to indicate writes to be LOW PRIORITY
3837       *
3838       * @return bool
3839       */
3840  	public function lockTables( $read, $write, $method, $lowPriority = true ) {
3841          return true;
3842      }
3843  
3844      /**
3845       * Unlock specific tables
3846       *
3847       * @param string $method the caller
3848       *
3849       * @return bool
3850       */
3851  	public function unlockTables( $method ) {
3852          return true;
3853      }
3854  
3855      /**
3856       * Delete a table
3857       * @param $tableName string
3858       * @param $fName string
3859       * @return bool|ResultWrapper
3860       * @since 1.18
3861       */
3862  	public function dropTable( $tableName, $fName = __METHOD__ ) {
3863          if ( !$this->tableExists( $tableName, $fName ) ) {
3864              return false;
3865          }
3866          $sql = "DROP TABLE " . $this->tableName( $tableName );
3867          if ( $this->cascadingDeletes() ) {
3868              $sql .= " CASCADE";
3869          }
3870          return $this->query( $sql, $fName );
3871      }
3872  
3873      /**
3874       * Get search engine class. All subclasses of this need to implement this
3875       * if they wish to use searching.
3876       *
3877       * @return String
3878       */
3879  	public function getSearchEngine() {
3880          return 'SearchEngineDummy';
3881      }
3882  
3883      /**
3884       * Find out when 'infinity' is. Most DBMSes support this. This is a special
3885       * keyword for timestamps in PostgreSQL, and works with CHAR(14) as well
3886       * because "i" sorts after all numbers.
3887       *
3888       * @return String
3889       */
3890  	public function getInfinity() {
3891          return 'infinity';
3892      }
3893  
3894      /**
3895       * Encode an expiry time into the DBMS dependent format
3896       *
3897       * @param string $expiry timestamp for expiry, or the 'infinity' string
3898       * @return String
3899       */
3900  	public function encodeExpiry( $expiry ) {
3901          return ( $expiry == '' || $expiry == 'infinity' || $expiry == $this->getInfinity() )
3902              ? $this->getInfinity()
3903              : $this->timestamp( $expiry );
3904      }
3905  
3906      /**
3907       * Decode an expiry time into a DBMS independent format
3908       *
3909       * @param string $expiry DB timestamp field value for expiry
3910       * @param $format integer: TS_* constant, defaults to TS_MW
3911       * @return String
3912       */
3913  	public function decodeExpiry( $expiry, $format = TS_MW ) {
3914          return ( $expiry == '' || $expiry == $this->getInfinity() )
3915              ? 'infinity'
3916              : wfTimestamp( $format, $expiry );
3917      }
3918  
3919      /**
3920       * Allow or deny "big selects" for this session only. This is done by setting
3921       * the sql_big_selects session variable.
3922       *
3923       * This is a MySQL-specific feature.
3924       *
3925       * @param $value Mixed: true for allow, false for deny, or "default" to
3926       *   restore the initial value
3927       */
3928  	public function setBigSelects( $value = true ) {
3929          // no-op
3930      }
3931  
3932      /**
3933       * @since 1.19
3934       */
3935  	public function __toString() {
3936          return (string)$this->mConn;
3937      }
3938  
3939      /**
3940       * Run a few simple sanity checks
3941       */
3942  	public function __destruct() {
3943          if ( $this->mTrxLevel && $this->mTrxDoneWrites ) {
3944              trigger_error( "Uncommitted DB writes (transaction from {$this->mTrxFname})." );
3945          }
3946          if ( count( $this->mTrxIdleCallbacks ) || count( $this->mTrxPreCommitCallbacks ) ) {
3947              $callers = array();
3948              foreach ( $this->mTrxIdleCallbacks as $callbackInfo ) {
3949                  $callers[] = $callbackInfo[1];
3950  
3951              }
3952              $callers = implode( ', ', $callers );
3953              trigger_error( "DB transaction  callbacks still pending (from $callers)." );
3954          }
3955      }
3956  }

title

Description

title

Description

title

Description

title

title

Body