MediaWiki  master
Database.php
Go to the documentation of this file.
1 <?php
2 
32 abstract class DatabaseBase implements IDatabase {
34  const DEADLOCK_TRIES = 4;
35 
37  const DEADLOCK_DELAY_MIN = 500000;
38 
40  const DEADLOCK_DELAY_MAX = 1500000;
41 
42  protected $mLastQuery = '';
43  protected $mDoneWrites = false;
44  protected $mPHPError = false;
45 
47 
49  protected $srvCache;
50 
52  protected $mConn = null;
53  protected $mOpened = false;
54 
56  protected $mTrxIdleCallbacks = [];
58  protected $mTrxPreCommitCallbacks = [];
60  protected $mTrxEndCallbacks = [];
62  protected $suppressPostCommitCallbacks = false;
63 
64  protected $mTablePrefix;
65  protected $mSchema;
66  protected $mFlags;
67  protected $mForeign;
68  protected $mLBInfo = [];
69  protected $mDefaultBigSelects = null;
70  protected $mSchemaVars = false;
72  protected $mSessionVars = [];
73 
74  protected $preparedArgs;
75 
76  protected $htmlErrors;
77 
78  protected $delimiter = ';';
79 
86  protected $mTrxLevel = 0;
87 
94  protected $mTrxShortId = '';
95 
104  private $mTrxTimestamp = null;
105 
107  private $mTrxSlaveLag = null;
108 
116  private $mTrxFname = null;
117 
124  private $mTrxDoneWrites = false;
125 
132  private $mTrxAutomatic = false;
133 
139  private $mTrxAtomicLevels = [];
140 
146  private $mTrxAutomaticAtomic = false;
147 
153  private $mTrxWriteCallers = [];
154 
160  private $mTrxWriteDuration = 0.0;
161 
163  private $mNamedLocksHeld = [];
164 
167 
172  protected $fileHandle = null;
173 
178  protected $allViews = null;
179 
181  protected $trxProfiler;
182 
183  public function getServerInfo() {
184  return $this->getServerVersion();
185  }
186 
190  public function getDelimiter() {
191  return $this->delimiter;
192  }
193 
203  public function debug( $debug = null ) {
204  return wfSetBit( $this->mFlags, DBO_DEBUG, $debug );
205  }
206 
207  public function bufferResults( $buffer = null ) {
208  if ( is_null( $buffer ) ) {
209  return !(bool)( $this->mFlags & DBO_NOBUFFER );
210  } else {
211  return !wfSetBit( $this->mFlags, DBO_NOBUFFER, !$buffer );
212  }
213  }
214 
227  protected function ignoreErrors( $ignoreErrors = null ) {
228  return wfSetBit( $this->mFlags, DBO_IGNORE, $ignoreErrors );
229  }
230 
231  public function trxLevel() {
232  return $this->mTrxLevel;
233  }
234 
235  public function trxTimestamp() {
236  return $this->mTrxLevel ? $this->mTrxTimestamp : null;
237  }
238 
239  public function tablePrefix( $prefix = null ) {
240  return wfSetVar( $this->mTablePrefix, $prefix );
241  }
242 
243  public function dbSchema( $schema = null ) {
244  return wfSetVar( $this->mSchema, $schema );
245  }
246 
252  public function setFileHandle( $fh ) {
253  $this->fileHandle = $fh;
254  }
255 
256  public function getLBInfo( $name = null ) {
257  if ( is_null( $name ) ) {
258  return $this->mLBInfo;
259  } else {
260  if ( array_key_exists( $name, $this->mLBInfo ) ) {
261  return $this->mLBInfo[$name];
262  } else {
263  return null;
264  }
265  }
266  }
267 
268  public function setLBInfo( $name, $value = null ) {
269  if ( is_null( $value ) ) {
270  $this->mLBInfo = $name;
271  } else {
272  $this->mLBInfo[$name] = $value;
273  }
274  }
275 
282  public function setLazyMasterHandle( IDatabase $conn ) {
283  $this->lazyMasterHandle = $conn;
284  }
285 
291  public function getLazyMasterHandle() {
293  }
294 
298  protected function getTransactionProfiler() {
299  if ( !$this->trxProfiler ) {
300  $this->trxProfiler = new TransactionProfiler();
301  }
302 
303  return $this->trxProfiler;
304  }
305 
310  public function setTransactionProfiler( TransactionProfiler $profiler ) {
311  $this->trxProfiler = $profiler;
312  }
313 
319  public function cascadingDeletes() {
320  return false;
321  }
322 
328  public function cleanupTriggers() {
329  return false;
330  }
331 
338  public function strictIPs() {
339  return false;
340  }
341 
347  public function realTimestamps() {
348  return false;
349  }
350 
351  public function implicitGroupby() {
352  return true;
353  }
354 
355  public function implicitOrderby() {
356  return true;
357  }
358 
365  public function searchableIPs() {
366  return false;
367  }
368 
374  public function functionalIndexes() {
375  return false;
376  }
377 
378  public function lastQuery() {
379  return $this->mLastQuery;
380  }
381 
382  public function doneWrites() {
383  return (bool)$this->mDoneWrites;
384  }
385 
386  public function lastDoneWrites() {
387  return $this->mDoneWrites ?: false;
388  }
389 
390  public function writesPending() {
391  return $this->mTrxLevel && $this->mTrxDoneWrites;
392  }
393 
394  public function writesOrCallbacksPending() {
395  return $this->mTrxLevel && (
396  $this->mTrxDoneWrites || $this->mTrxIdleCallbacks || $this->mTrxPreCommitCallbacks
397  );
398  }
399 
400  public function pendingWriteQueryDuration() {
401  return $this->mTrxLevel ? $this->mTrxWriteDuration : false;
402  }
403 
404  public function pendingWriteCallers() {
405  return $this->mTrxLevel ? $this->mTrxWriteCallers : [];
406  }
407 
408  public function isOpen() {
409  return $this->mOpened;
410  }
411 
412  public function setFlag( $flag ) {
413  $this->mFlags |= $flag;
414  }
415 
416  public function clearFlag( $flag ) {
417  $this->mFlags &= ~$flag;
418  }
419 
420  public function getFlag( $flag ) {
421  return !!( $this->mFlags & $flag );
422  }
423 
424  public function getProperty( $name ) {
425  return $this->$name;
426  }
427 
428  public function getWikiID() {
429  if ( $this->mTablePrefix ) {
430  return "{$this->mDBname}-{$this->mTablePrefix}";
431  } else {
432  return $this->mDBname;
433  }
434  }
435 
443  private function getSqlFilePath( $filename ) {
444  global $IP;
445  $dbmsSpecificFilePath = "$IP/maintenance/" . $this->getType() . "/$filename";
446  if ( file_exists( $dbmsSpecificFilePath ) ) {
447  return $dbmsSpecificFilePath;
448  } else {
449  return "$IP/maintenance/$filename";
450  }
451  }
452 
459  public function getSchemaPath() {
460  return $this->getSqlFilePath( 'tables.sql' );
461  }
462 
469  public function getUpdateKeysPath() {
470  return $this->getSqlFilePath( 'update-keys.sql' );
471  }
472 
480  abstract function indexInfo( $table, $index, $fname = __METHOD__ );
481 
488  abstract function strencode( $s );
489 
502  function __construct( array $params ) {
504 
505  $this->srvCache = ObjectCache::getLocalServerInstance( 'hash' );
506 
507  $server = $params['host'];
508  $user = $params['user'];
509  $password = $params['password'];
510  $dbName = $params['dbname'];
511  $flags = $params['flags'];
512  $tablePrefix = $params['tablePrefix'];
513  $schema = $params['schema'];
514  $foreign = $params['foreign'];
515 
516  $this->mFlags = $flags;
517  if ( $this->mFlags & DBO_DEFAULT ) {
518  if ( $wgCommandLineMode ) {
519  $this->mFlags &= ~DBO_TRX;
520  } else {
521  $this->mFlags |= DBO_TRX;
522  }
523  }
524 
525  $this->mSessionVars = $params['variables'];
526 
528  if ( $tablePrefix === 'get from global' ) {
529  $this->mTablePrefix = $wgDBprefix;
530  } else {
531  $this->mTablePrefix = $tablePrefix;
532  }
533 
535  if ( $schema === 'get from global' ) {
536  $this->mSchema = $wgDBmwschema;
537  } else {
538  $this->mSchema = $schema;
539  }
540 
541  $this->mForeign = $foreign;
542 
543  if ( isset( $params['trxProfiler'] ) ) {
544  $this->trxProfiler = $params['trxProfiler']; // override
545  }
546 
547  if ( $user ) {
548  $this->open( $server, $user, $password, $dbName );
549  }
550  }
551 
557  public function __sleep() {
558  throw new MWException( 'Database serialization may cause problems, since ' .
559  'the connection is not restored on wakeup.' );
560  }
561 
584  final public static function factory( $dbType, $p = [] ) {
585  $canonicalDBTypes = [
586  'mysql' => [ 'mysqli', 'mysql' ],
587  'postgres' => [],
588  'sqlite' => [],
589  'oracle' => [],
590  'mssql' => [],
591  ];
592 
593  $driver = false;
594  $dbType = strtolower( $dbType );
595  if ( isset( $canonicalDBTypes[$dbType] ) && $canonicalDBTypes[$dbType] ) {
596  $possibleDrivers = $canonicalDBTypes[$dbType];
597  if ( !empty( $p['driver'] ) ) {
598  if ( in_array( $p['driver'], $possibleDrivers ) ) {
599  $driver = $p['driver'];
600  } else {
601  throw new MWException( __METHOD__ .
602  " cannot construct Database with type '$dbType' and driver '{$p['driver']}'" );
603  }
604  } else {
605  foreach ( $possibleDrivers as $posDriver ) {
606  if ( extension_loaded( $posDriver ) ) {
607  $driver = $posDriver;
608  break;
609  }
610  }
611  }
612  } else {
613  $driver = $dbType;
614  }
615  if ( $driver === false ) {
616  throw new MWException( __METHOD__ .
617  " no viable database extension found for type '$dbType'" );
618  }
619 
620  // Determine schema defaults. Currently Microsoft SQL Server uses $wgDBmwschema,
621  // and everything else doesn't use a schema (e.g. null)
622  // Although postgres and oracle support schemas, we don't use them (yet)
623  // to maintain backwards compatibility
624  $defaultSchemas = [
625  'mssql' => 'get from global',
626  ];
627 
628  $class = 'Database' . ucfirst( $driver );
629  if ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) {
630  // Resolve some defaults for b/c
631  $p['host'] = isset( $p['host'] ) ? $p['host'] : false;
632  $p['user'] = isset( $p['user'] ) ? $p['user'] : false;
633  $p['password'] = isset( $p['password'] ) ? $p['password'] : false;
634  $p['dbname'] = isset( $p['dbname'] ) ? $p['dbname'] : false;
635  $p['flags'] = isset( $p['flags'] ) ? $p['flags'] : 0;
636  $p['variables'] = isset( $p['variables'] ) ? $p['variables'] : [];
637  $p['tablePrefix'] = isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : 'get from global';
638  if ( !isset( $p['schema'] ) ) {
639  $p['schema'] = isset( $defaultSchemas[$dbType] ) ? $defaultSchemas[$dbType] : null;
640  }
641  $p['foreign'] = isset( $p['foreign'] ) ? $p['foreign'] : false;
642 
643  return new $class( $p );
644  } else {
645  return null;
646  }
647  }
648 
649  protected function installErrorHandler() {
650  $this->mPHPError = false;
651  $this->htmlErrors = ini_set( 'html_errors', '0' );
652  set_error_handler( [ $this, 'connectionErrorHandler' ] );
653  }
654 
658  protected function restoreErrorHandler() {
659  restore_error_handler();
660  if ( $this->htmlErrors !== false ) {
661  ini_set( 'html_errors', $this->htmlErrors );
662  }
663  if ( $this->mPHPError ) {
664  $error = preg_replace( '!\[<a.*</a>\]!', '', $this->mPHPError );
665  $error = preg_replace( '!^.*?:\s?(.*)$!', '$1', $error );
666 
667  return $error;
668  } else {
669  return false;
670  }
671  }
672 
677  public function connectionErrorHandler( $errno, $errstr ) {
678  $this->mPHPError = $errstr;
679  }
680 
687  protected function getLogContext( array $extras = [] ) {
688  return array_merge(
689  [
690  'db_server' => $this->mServer,
691  'db_name' => $this->mDBname,
692  'db_user' => $this->mUser,
693  ],
694  $extras
695  );
696  }
697 
698  public function close() {
699  if ( $this->mConn ) {
700  if ( $this->trxLevel() ) {
701  if ( !$this->mTrxAutomatic ) {
702  wfWarn( "Transaction still in progress (from {$this->mTrxFname}), " .
703  " performing implicit commit before closing connection!" );
704  }
705 
706  $this->commit( __METHOD__, 'flush' );
707  }
708 
709  $closed = $this->closeConnection();
710  $this->mConn = false;
711  } elseif ( $this->mTrxIdleCallbacks || $this->mTrxEndCallbacks ) { // sanity
712  throw new MWException( "Transaction callbacks still pending." );
713  } else {
714  $closed = true;
715  }
716  $this->mOpened = false;
717 
718  return $closed;
719  }
720 
726  protected function assertOpen() {
727  if ( !$this->isOpen() ) {
728  throw new DBUnexpectedError( $this, "DB connection was already closed." );
729  }
730  }
731 
737  abstract protected function closeConnection();
738 
739  function reportConnectionError( $error = 'Unknown error' ) {
740  $myError = $this->lastError();
741  if ( $myError ) {
742  $error = $myError;
743  }
744 
745  # New method
746  throw new DBConnectionError( $this, $error );
747  }
748 
756  abstract protected function doQuery( $sql );
757 
765  protected function isWriteQuery( $sql ) {
766  return !preg_match( '/^(?:SELECT|BEGIN|ROLLBACK|COMMIT|SET|SHOW|EXPLAIN|\(SELECT)\b/i', $sql );
767  }
768 
778  protected function isTransactableQuery( $sql ) {
779  $verb = substr( $sql, 0, strcspn( $sql, " \t\r\n" ) );
780  return !in_array( $verb, [ 'BEGIN', 'COMMIT', 'ROLLBACK', 'SHOW', 'SET' ] );
781  }
782 
783  public function query( $sql, $fname = __METHOD__, $tempIgnore = false ) {
784  global $wgUser;
785 
786  $this->mLastQuery = $sql;
787 
788  $isWriteQuery = $this->isWriteQuery( $sql );
789  if ( $isWriteQuery ) {
790  $reason = $this->getReadOnlyReason();
791  if ( $reason !== false ) {
792  throw new DBReadOnlyError( $this, "Database is read-only: $reason" );
793  }
794  # Set a flag indicating that writes have been done
795  $this->mDoneWrites = microtime( true );
796  }
797 
798  # Add a comment for easy SHOW PROCESSLIST interpretation
799  if ( is_object( $wgUser ) && $wgUser->isItemLoaded( 'name' ) ) {
800  $userName = $wgUser->getName();
801  if ( mb_strlen( $userName ) > 15 ) {
802  $userName = mb_substr( $userName, 0, 15 ) . '...';
803  }
804  $userName = str_replace( '/', '', $userName );
805  } else {
806  $userName = '';
807  }
808 
809  // Add trace comment to the begin of the sql string, right after the operator.
810  // Or, for one-word queries (like "BEGIN" or COMMIT") add it to the end (bug 42598)
811  $commentedSql = preg_replace( '/\s|$/', " /* $fname $userName */ ", $sql, 1 );
812 
813  if ( !$this->mTrxLevel && $this->getFlag( DBO_TRX ) && $this->isTransactableQuery( $sql ) ) {
814  $this->begin( __METHOD__ . " ($fname)" );
815  $this->mTrxAutomatic = true;
816  }
817 
818  # Keep track of whether the transaction has write queries pending
819  if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $isWriteQuery ) {
820  $this->mTrxDoneWrites = true;
821  $this->getTransactionProfiler()->transactionWritingIn(
822  $this->mServer, $this->mDBname, $this->mTrxShortId );
823  }
824 
825  $isMaster = !is_null( $this->getLBInfo( 'master' ) );
826  # generalizeSQL will probably cut down the query to reasonable
827  # logging size most of the time. The substr is really just a sanity check.
828  if ( $isMaster ) {
829  $queryProf = 'query-m: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
830  $totalProf = 'DatabaseBase::query-master';
831  } else {
832  $queryProf = 'query: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
833  $totalProf = 'DatabaseBase::query';
834  }
835  # Include query transaction state
836  $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : "";
837 
838  $profiler = Profiler::instance();
839  if ( !$profiler instanceof ProfilerStub ) {
840  $totalProfSection = $profiler->scopedProfileIn( $totalProf );
841  $queryProfSection = $profiler->scopedProfileIn( $queryProf );
842  }
843 
844  if ( $this->debug() ) {
845  wfDebugLog( 'queries', sprintf( "%s: %s", $this->mDBname, $commentedSql ) );
846  }
847 
848  $queryId = MWDebug::query( $sql, $fname, $isMaster );
849 
850  # Avoid fatals if close() was called
851  $this->assertOpen();
852 
853  # Do the query and handle errors
854  $startTime = microtime( true );
855  $ret = $this->doQuery( $commentedSql );
856  $queryRuntime = microtime( true ) - $startTime;
857  # Log the query time and feed it into the DB trx profiler
858  $this->getTransactionProfiler()->recordQueryCompletion(
859  $queryProf, $startTime, $isWriteQuery, $this->affectedRows() );
860 
861  MWDebug::queryTime( $queryId );
862 
863  # Try reconnecting if the connection was lost
864  if ( false === $ret && $this->wasErrorReissuable() ) {
865  # Transaction is gone; this can mean lost writes or REPEATABLE-READ snapshots
866  $hadTrx = $this->mTrxLevel;
867  # T127428: for non-write transactions, a disconnect and a COMMIT are similar:
868  # neither changed data and in both cases any read snapshots are reset anyway.
869  $isNoopCommit = ( !$this->writesOrCallbacksPending() && $sql === 'COMMIT' );
870  # Update state tracking to reflect transaction loss
871  $this->mTrxLevel = 0;
872  $this->mTrxIdleCallbacks = []; // bug 65263
873  $this->mTrxPreCommitCallbacks = []; // bug 65263
874  wfDebug( "Connection lost, reconnecting...\n" );
875  # Stash the last error values since ping() might clear them
876  $lastError = $this->lastError();
877  $lastErrno = $this->lastErrno();
878  if ( $this->ping() ) {
879  wfDebug( "Reconnected\n" );
880  $server = $this->getServer();
881  $msg = __METHOD__ . ": lost connection to $server; reconnected";
882  wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
883 
884  if ( ( $hadTrx && !$isNoopCommit ) || $this->mNamedLocksHeld ) {
885  # Leave $ret as false and let an error be reported.
886  # Callers may catch the exception and continue to use the DB.
887  $this->reportQueryError( $lastError, $lastErrno, $sql, $fname, $tempIgnore );
888  } else {
889  # Should be safe to silently retry (no trx/callbacks/locks)
890  $startTime = microtime( true );
891  $ret = $this->doQuery( $commentedSql );
892  $queryRuntime = microtime( true ) - $startTime;
893  # Log the query time and feed it into the DB trx profiler
894  $this->getTransactionProfiler()->recordQueryCompletion(
895  $queryProf, $startTime, $isWriteQuery, $this->affectedRows() );
896  }
897  } else {
898  wfDebug( "Failed\n" );
899  }
900  }
901 
902  if ( false === $ret ) {
903  $this->reportQueryError(
904  $this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore );
905  }
906 
907  $res = $this->resultObject( $ret );
908 
909  // Destroy profile sections in the opposite order to their creation
910  ScopedCallback::consume( $queryProfSection );
911  ScopedCallback::consume( $totalProfSection );
912 
913  if ( $isWriteQuery && $this->mTrxLevel ) {
914  $this->mTrxWriteDuration += $queryRuntime;
915  $this->mTrxWriteCallers[] = $fname;
916  }
917 
918  return $res;
919  }
920 
921  public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
922  if ( $this->ignoreErrors() || $tempIgnore ) {
923  wfDebug( "SQL ERROR (ignored): $error\n" );
924  } else {
925  $sql1line = mb_substr( str_replace( "\n", "\\n", $sql ), 0, 5 * 1024 );
926  wfLogDBError(
927  "{fname}\t{db_server}\t{errno}\t{error}\t{sql1line}",
928  $this->getLogContext( [
929  'method' => __METHOD__,
930  'errno' => $errno,
931  'error' => $error,
932  'sql1line' => $sql1line,
933  'fname' => $fname,
934  ] )
935  );
936  wfDebug( "SQL ERROR: " . $error . "\n" );
937  throw new DBQueryError( $this, $error, $errno, $sql, $fname );
938  }
939  }
940 
955  protected function prepare( $sql, $func = 'DatabaseBase::prepare' ) {
956  /* MySQL doesn't support prepared statements (yet), so just
957  * pack up the query for reference. We'll manually replace
958  * the bits later.
959  */
960  return [ 'query' => $sql, 'func' => $func ];
961  }
962 
967  protected function freePrepared( $prepared ) {
968  /* No-op by default */
969  }
970 
978  public function execute( $prepared, $args = null ) {
979  if ( !is_array( $args ) ) {
980  # Pull the var args
981  $args = func_get_args();
982  array_shift( $args );
983  }
984 
985  $sql = $this->fillPrepared( $prepared['query'], $args );
986 
987  return $this->query( $sql, $prepared['func'] );
988  }
989 
997  public function fillPrepared( $preparedQuery, $args ) {
998  reset( $args );
999  $this->preparedArgs =& $args;
1000 
1001  return preg_replace_callback( '/(\\\\[?!&]|[?!&])/',
1002  [ &$this, 'fillPreparedArg' ], $preparedQuery );
1003  }
1004 
1014  protected function fillPreparedArg( $matches ) {
1015  switch ( $matches[1] ) {
1016  case '\\?':
1017  return '?';
1018  case '\\!':
1019  return '!';
1020  case '\\&':
1021  return '&';
1022  }
1023 
1024  list( /* $n */, $arg ) = each( $this->preparedArgs );
1025 
1026  switch ( $matches[1] ) {
1027  case '?':
1028  return $this->addQuotes( $arg );
1029  case '!':
1030  return $arg;
1031  case '&':
1032  # return $this->addQuotes( file_get_contents( $arg ) );
1033  throw new DBUnexpectedError(
1034  $this,
1035  '& mode is not implemented. If it\'s really needed, uncomment the line above.'
1036  );
1037  default:
1038  throw new DBUnexpectedError(
1039  $this,
1040  'Received invalid match. This should never happen!'
1041  );
1042  }
1043  }
1044 
1045  public function freeResult( $res ) {
1046  }
1047 
1048  public function selectField(
1049  $table, $var, $cond = '', $fname = __METHOD__, $options = []
1050  ) {
1051  if ( $var === '*' ) { // sanity
1052  throw new DBUnexpectedError( $this, "Cannot use a * field: got '$var'" );
1053  }
1054 
1055  if ( !is_array( $options ) ) {
1056  $options = [ $options ];
1057  }
1058 
1059  $options['LIMIT'] = 1;
1060 
1061  $res = $this->select( $table, $var, $cond, $fname, $options );
1062  if ( $res === false || !$this->numRows( $res ) ) {
1063  return false;
1064  }
1065 
1066  $row = $this->fetchRow( $res );
1067 
1068  if ( $row !== false ) {
1069  return reset( $row );
1070  } else {
1071  return false;
1072  }
1073  }
1074 
1075  public function selectFieldValues(
1076  $table, $var, $cond = '', $fname = __METHOD__, $options = [], $join_conds = []
1077  ) {
1078  if ( $var === '*' ) { // sanity
1079  throw new DBUnexpectedError( $this, "Cannot use a * field" );
1080  } elseif ( !is_string( $var ) ) { // sanity
1081  throw new DBUnexpectedError( $this, "Cannot use an array of fields" );
1082  }
1083 
1084  if ( !is_array( $options ) ) {
1085  $options = [ $options ];
1086  }
1087 
1088  $res = $this->select( $table, $var, $cond, $fname, $options, $join_conds );
1089  if ( $res === false ) {
1090  return false;
1091  }
1092 
1093  $values = [];
1094  foreach ( $res as $row ) {
1095  $values[] = $row->$var;
1096  }
1097 
1098  return $values;
1099  }
1100 
1110  public function makeSelectOptions( $options ) {
1111  $preLimitTail = $postLimitTail = '';
1112  $startOpts = '';
1113 
1114  $noKeyOptions = [];
1115 
1116  foreach ( $options as $key => $option ) {
1117  if ( is_numeric( $key ) ) {
1118  $noKeyOptions[$option] = true;
1119  }
1120  }
1121 
1122  $preLimitTail .= $this->makeGroupByWithHaving( $options );
1123 
1124  $preLimitTail .= $this->makeOrderBy( $options );
1125 
1126  // if (isset($options['LIMIT'])) {
1127  // $tailOpts .= $this->limitResult('', $options['LIMIT'],
1128  // isset($options['OFFSET']) ? $options['OFFSET']
1129  // : false);
1130  // }
1131 
1132  if ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
1133  $postLimitTail .= ' FOR UPDATE';
1134  }
1135 
1136  if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) {
1137  $postLimitTail .= ' LOCK IN SHARE MODE';
1138  }
1139 
1140  if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) {
1141  $startOpts .= 'DISTINCT';
1142  }
1143 
1144  # Various MySQL extensions
1145  if ( isset( $noKeyOptions['STRAIGHT_JOIN'] ) ) {
1146  $startOpts .= ' /*! STRAIGHT_JOIN */';
1147  }
1148 
1149  if ( isset( $noKeyOptions['HIGH_PRIORITY'] ) ) {
1150  $startOpts .= ' HIGH_PRIORITY';
1151  }
1152 
1153  if ( isset( $noKeyOptions['SQL_BIG_RESULT'] ) ) {
1154  $startOpts .= ' SQL_BIG_RESULT';
1155  }
1156 
1157  if ( isset( $noKeyOptions['SQL_BUFFER_RESULT'] ) ) {
1158  $startOpts .= ' SQL_BUFFER_RESULT';
1159  }
1160 
1161  if ( isset( $noKeyOptions['SQL_SMALL_RESULT'] ) ) {
1162  $startOpts .= ' SQL_SMALL_RESULT';
1163  }
1164 
1165  if ( isset( $noKeyOptions['SQL_CALC_FOUND_ROWS'] ) ) {
1166  $startOpts .= ' SQL_CALC_FOUND_ROWS';
1167  }
1168 
1169  if ( isset( $noKeyOptions['SQL_CACHE'] ) ) {
1170  $startOpts .= ' SQL_CACHE';
1171  }
1172 
1173  if ( isset( $noKeyOptions['SQL_NO_CACHE'] ) ) {
1174  $startOpts .= ' SQL_NO_CACHE';
1175  }
1176 
1177  if ( isset( $options['USE INDEX'] ) && is_string( $options['USE INDEX'] ) ) {
1178  $useIndex = $this->useIndexClause( $options['USE INDEX'] );
1179  } else {
1180  $useIndex = '';
1181  }
1182 
1183  return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail ];
1184  }
1185 
1194  public function makeGroupByWithHaving( $options ) {
1195  $sql = '';
1196  if ( isset( $options['GROUP BY'] ) ) {
1197  $gb = is_array( $options['GROUP BY'] )
1198  ? implode( ',', $options['GROUP BY'] )
1199  : $options['GROUP BY'];
1200  $sql .= ' GROUP BY ' . $gb;
1201  }
1202  if ( isset( $options['HAVING'] ) ) {
1203  $having = is_array( $options['HAVING'] )
1204  ? $this->makeList( $options['HAVING'], LIST_AND )
1205  : $options['HAVING'];
1206  $sql .= ' HAVING ' . $having;
1207  }
1208 
1209  return $sql;
1210  }
1211 
1220  public function makeOrderBy( $options ) {
1221  if ( isset( $options['ORDER BY'] ) ) {
1222  $ob = is_array( $options['ORDER BY'] )
1223  ? implode( ',', $options['ORDER BY'] )
1224  : $options['ORDER BY'];
1225 
1226  return ' ORDER BY ' . $ob;
1227  }
1228 
1229  return '';
1230  }
1231 
1232  // See IDatabase::select for the docs for this function
1233  public function select( $table, $vars, $conds = '', $fname = __METHOD__,
1234  $options = [], $join_conds = [] ) {
1235  $sql = $this->selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
1236 
1237  return $this->query( $sql, $fname );
1238  }
1239 
1240  public function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__,
1241  $options = [], $join_conds = []
1242  ) {
1243  if ( is_array( $vars ) ) {
1244  $vars = implode( ',', $this->fieldNamesWithAlias( $vars ) );
1245  }
1246 
1247  $options = (array)$options;
1248  $useIndexes = ( isset( $options['USE INDEX'] ) && is_array( $options['USE INDEX'] ) )
1249  ? $options['USE INDEX']
1250  : [];
1251 
1252  if ( is_array( $table ) ) {
1253  $from = ' FROM ' .
1254  $this->tableNamesWithUseIndexOrJOIN( $table, $useIndexes, $join_conds );
1255  } elseif ( $table != '' ) {
1256  if ( $table[0] == ' ' ) {
1257  $from = ' FROM ' . $table;
1258  } else {
1259  $from = ' FROM ' .
1260  $this->tableNamesWithUseIndexOrJOIN( [ $table ], $useIndexes, [] );
1261  }
1262  } else {
1263  $from = '';
1264  }
1265 
1266  list( $startOpts, $useIndex, $preLimitTail, $postLimitTail ) =
1267  $this->makeSelectOptions( $options );
1268 
1269  if ( !empty( $conds ) ) {
1270  if ( is_array( $conds ) ) {
1271  $conds = $this->makeList( $conds, LIST_AND );
1272  }
1273  $sql = "SELECT $startOpts $vars $from $useIndex WHERE $conds $preLimitTail";
1274  } else {
1275  $sql = "SELECT $startOpts $vars $from $useIndex $preLimitTail";
1276  }
1277 
1278  if ( isset( $options['LIMIT'] ) ) {
1279  $sql = $this->limitResult( $sql, $options['LIMIT'],
1280  isset( $options['OFFSET'] ) ? $options['OFFSET'] : false );
1281  }
1282  $sql = "$sql $postLimitTail";
1283 
1284  if ( isset( $options['EXPLAIN'] ) ) {
1285  $sql = 'EXPLAIN ' . $sql;
1286  }
1287 
1288  return $sql;
1289  }
1290 
1291  public function selectRow( $table, $vars, $conds, $fname = __METHOD__,
1292  $options = [], $join_conds = []
1293  ) {
1294  $options = (array)$options;
1295  $options['LIMIT'] = 1;
1296  $res = $this->select( $table, $vars, $conds, $fname, $options, $join_conds );
1297 
1298  if ( $res === false ) {
1299  return false;
1300  }
1301 
1302  if ( !$this->numRows( $res ) ) {
1303  return false;
1304  }
1305 
1306  $obj = $this->fetchObject( $res );
1307 
1308  return $obj;
1309  }
1310 
1311  public function estimateRowCount(
1312  $table, $vars = '*', $conds = '', $fname = __METHOD__, $options = []
1313  ) {
1314  $rows = 0;
1315  $res = $this->select( $table, [ 'rowcount' => 'COUNT(*)' ], $conds, $fname, $options );
1316 
1317  if ( $res ) {
1318  $row = $this->fetchRow( $res );
1319  $rows = ( isset( $row['rowcount'] ) ) ? (int)$row['rowcount'] : 0;
1320  }
1321 
1322  return $rows;
1323  }
1324 
1325  public function selectRowCount(
1326  $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
1327  ) {
1328  $rows = 0;
1329  $sql = $this->selectSQLText( $tables, '1', $conds, $fname, $options, $join_conds );
1330  $res = $this->query( "SELECT COUNT(*) AS rowcount FROM ($sql) tmp_count", $fname );
1331 
1332  if ( $res ) {
1333  $row = $this->fetchRow( $res );
1334  $rows = ( isset( $row['rowcount'] ) ) ? (int)$row['rowcount'] : 0;
1335  }
1336 
1337  return $rows;
1338  }
1339 
1348  protected static function generalizeSQL( $sql ) {
1349  # This does the same as the regexp below would do, but in such a way
1350  # as to avoid crashing php on some large strings.
1351  # $sql = preg_replace( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql );
1352 
1353  $sql = str_replace( "\\\\", '', $sql );
1354  $sql = str_replace( "\\'", '', $sql );
1355  $sql = str_replace( "\\\"", '', $sql );
1356  $sql = preg_replace( "/'.*'/s", "'X'", $sql );
1357  $sql = preg_replace( '/".*"/s', "'X'", $sql );
1358 
1359  # All newlines, tabs, etc replaced by single space
1360  $sql = preg_replace( '/\s+/', ' ', $sql );
1361 
1362  # All numbers => N,
1363  # except the ones surrounded by characters, e.g. l10n
1364  $sql = preg_replace( '/-?\d+(,-?\d+)+/s', 'N,...,N', $sql );
1365  $sql = preg_replace( '/(?<![a-zA-Z])-?\d+(?![a-zA-Z])/s', 'N', $sql );
1366 
1367  return $sql;
1368  }
1369 
1370  public function fieldExists( $table, $field, $fname = __METHOD__ ) {
1371  $info = $this->fieldInfo( $table, $field );
1372 
1373  return (bool)$info;
1374  }
1375 
1376  public function indexExists( $table, $index, $fname = __METHOD__ ) {
1377  if ( !$this->tableExists( $table ) ) {
1378  return null;
1379  }
1380 
1381  $info = $this->indexInfo( $table, $index, $fname );
1382  if ( is_null( $info ) ) {
1383  return null;
1384  } else {
1385  return $info !== false;
1386  }
1387  }
1388 
1389  public function tableExists( $table, $fname = __METHOD__ ) {
1390  $table = $this->tableName( $table );
1391  $old = $this->ignoreErrors( true );
1392  $res = $this->query( "SELECT 1 FROM $table LIMIT 1", $fname );
1393  $this->ignoreErrors( $old );
1394 
1395  return (bool)$res;
1396  }
1397 
1398  public function indexUnique( $table, $index ) {
1399  $indexInfo = $this->indexInfo( $table, $index );
1400 
1401  if ( !$indexInfo ) {
1402  return null;
1403  }
1404 
1405  return !$indexInfo[0]->Non_unique;
1406  }
1407 
1414  protected function makeInsertOptions( $options ) {
1415  return implode( ' ', $options );
1416  }
1417 
1418  public function insert( $table, $a, $fname = __METHOD__, $options = [] ) {
1419  # No rows to insert, easy just return now
1420  if ( !count( $a ) ) {
1421  return true;
1422  }
1423 
1424  $table = $this->tableName( $table );
1425 
1426  if ( !is_array( $options ) ) {
1427  $options = [ $options ];
1428  }
1429 
1430  $fh = null;
1431  if ( isset( $options['fileHandle'] ) ) {
1432  $fh = $options['fileHandle'];
1433  }
1434  $options = $this->makeInsertOptions( $options );
1435 
1436  if ( isset( $a[0] ) && is_array( $a[0] ) ) {
1437  $multi = true;
1438  $keys = array_keys( $a[0] );
1439  } else {
1440  $multi = false;
1441  $keys = array_keys( $a );
1442  }
1443 
1444  $sql = 'INSERT ' . $options .
1445  " INTO $table (" . implode( ',', $keys ) . ') VALUES ';
1446 
1447  if ( $multi ) {
1448  $first = true;
1449  foreach ( $a as $row ) {
1450  if ( $first ) {
1451  $first = false;
1452  } else {
1453  $sql .= ',';
1454  }
1455  $sql .= '(' . $this->makeList( $row ) . ')';
1456  }
1457  } else {
1458  $sql .= '(' . $this->makeList( $a ) . ')';
1459  }
1460 
1461  if ( $fh !== null && false === fwrite( $fh, $sql ) ) {
1462  return false;
1463  } elseif ( $fh !== null ) {
1464  return true;
1465  }
1466 
1467  return (bool)$this->query( $sql, $fname );
1468  }
1469 
1476  protected function makeUpdateOptionsArray( $options ) {
1477  if ( !is_array( $options ) ) {
1478  $options = [ $options ];
1479  }
1480 
1481  $opts = [];
1482 
1483  if ( in_array( 'LOW_PRIORITY', $options ) ) {
1484  $opts[] = $this->lowPriorityOption();
1485  }
1486 
1487  if ( in_array( 'IGNORE', $options ) ) {
1488  $opts[] = 'IGNORE';
1489  }
1490 
1491  return $opts;
1492  }
1493 
1500  protected function makeUpdateOptions( $options ) {
1501  $opts = $this->makeUpdateOptionsArray( $options );
1502 
1503  return implode( ' ', $opts );
1504  }
1505 
1506  function update( $table, $values, $conds, $fname = __METHOD__, $options = [] ) {
1507  $table = $this->tableName( $table );
1508  $opts = $this->makeUpdateOptions( $options );
1509  $sql = "UPDATE $opts $table SET " . $this->makeList( $values, LIST_SET );
1510 
1511  if ( $conds !== [] && $conds !== '*' ) {
1512  $sql .= " WHERE " . $this->makeList( $conds, LIST_AND );
1513  }
1514 
1515  return $this->query( $sql, $fname );
1516  }
1517 
1518  public function makeList( $a, $mode = LIST_COMMA ) {
1519  if ( !is_array( $a ) ) {
1520  throw new DBUnexpectedError( $this, 'DatabaseBase::makeList called with incorrect parameters' );
1521  }
1522 
1523  $first = true;
1524  $list = '';
1525 
1526  foreach ( $a as $field => $value ) {
1527  if ( !$first ) {
1528  if ( $mode == LIST_AND ) {
1529  $list .= ' AND ';
1530  } elseif ( $mode == LIST_OR ) {
1531  $list .= ' OR ';
1532  } else {
1533  $list .= ',';
1534  }
1535  } else {
1536  $first = false;
1537  }
1538 
1539  if ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_numeric( $field ) ) {
1540  $list .= "($value)";
1541  } elseif ( ( $mode == LIST_SET ) && is_numeric( $field ) ) {
1542  $list .= "$value";
1543  } elseif ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_array( $value ) ) {
1544  // Remove null from array to be handled separately if found
1545  $includeNull = false;
1546  foreach ( array_keys( $value, null, true ) as $nullKey ) {
1547  $includeNull = true;
1548  unset( $value[$nullKey] );
1549  }
1550  if ( count( $value ) == 0 && !$includeNull ) {
1551  throw new MWException( __METHOD__ . ": empty input for field $field" );
1552  } elseif ( count( $value ) == 0 ) {
1553  // only check if $field is null
1554  $list .= "$field IS NULL";
1555  } else {
1556  // IN clause contains at least one valid element
1557  if ( $includeNull ) {
1558  // Group subconditions to ensure correct precedence
1559  $list .= '(';
1560  }
1561  if ( count( $value ) == 1 ) {
1562  // Special-case single values, as IN isn't terribly efficient
1563  // Don't necessarily assume the single key is 0; we don't
1564  // enforce linear numeric ordering on other arrays here.
1565  $value = array_values( $value )[0];
1566  $list .= $field . " = " . $this->addQuotes( $value );
1567  } else {
1568  $list .= $field . " IN (" . $this->makeList( $value ) . ") ";
1569  }
1570  // if null present in array, append IS NULL
1571  if ( $includeNull ) {
1572  $list .= " OR $field IS NULL)";
1573  }
1574  }
1575  } elseif ( $value === null ) {
1576  if ( $mode == LIST_AND || $mode == LIST_OR ) {
1577  $list .= "$field IS ";
1578  } elseif ( $mode == LIST_SET ) {
1579  $list .= "$field = ";
1580  }
1581  $list .= 'NULL';
1582  } else {
1583  if ( $mode == LIST_AND || $mode == LIST_OR || $mode == LIST_SET ) {
1584  $list .= "$field = ";
1585  }
1586  $list .= $mode == LIST_NAMES ? $value : $this->addQuotes( $value );
1587  }
1588  }
1589 
1590  return $list;
1591  }
1592 
1593  public function makeWhereFrom2d( $data, $baseKey, $subKey ) {
1594  $conds = [];
1595 
1596  foreach ( $data as $base => $sub ) {
1597  if ( count( $sub ) ) {
1598  $conds[] = $this->makeList(
1599  [ $baseKey => $base, $subKey => array_keys( $sub ) ],
1600  LIST_AND );
1601  }
1602  }
1603 
1604  if ( $conds ) {
1605  return $this->makeList( $conds, LIST_OR );
1606  } else {
1607  // Nothing to search for...
1608  return false;
1609  }
1610  }
1611 
1620  public function aggregateValue( $valuedata, $valuename = 'value' ) {
1621  return $valuename;
1622  }
1623 
1624  public function bitNot( $field ) {
1625  return "(~$field)";
1626  }
1627 
1628  public function bitAnd( $fieldLeft, $fieldRight ) {
1629  return "($fieldLeft & $fieldRight)";
1630  }
1631 
1632  public function bitOr( $fieldLeft, $fieldRight ) {
1633  return "($fieldLeft | $fieldRight)";
1634  }
1635 
1636  public function buildConcat( $stringList ) {
1637  return 'CONCAT(' . implode( ',', $stringList ) . ')';
1638  }
1639 
1640  public function buildGroupConcatField(
1641  $delim, $table, $field, $conds = '', $join_conds = []
1642  ) {
1643  $fld = "GROUP_CONCAT($field SEPARATOR " . $this->addQuotes( $delim ) . ')';
1644 
1645  return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
1646  }
1647 
1648  public function selectDB( $db ) {
1649  # Stub. Shouldn't cause serious problems if it's not overridden, but
1650  # if your database engine supports a concept similar to MySQL's
1651  # databases you may as well.
1652  $this->mDBname = $db;
1653 
1654  return true;
1655  }
1656 
1657  public function getDBname() {
1658  return $this->mDBname;
1659  }
1660 
1661  public function getServer() {
1662  return $this->mServer;
1663  }
1664 
1684  public function tableName( $name, $format = 'quoted' ) {
1686  # Skip the entire process when we have a string quoted on both ends.
1687  # Note that we check the end so that we will still quote any use of
1688  # use of `database`.table. But won't break things if someone wants
1689  # to query a database table with a dot in the name.
1690  if ( $this->isQuotedIdentifier( $name ) ) {
1691  return $name;
1692  }
1693 
1694  # Lets test for any bits of text that should never show up in a table
1695  # name. Basically anything like JOIN or ON which are actually part of
1696  # SQL queries, but may end up inside of the table value to combine
1697  # sql. Such as how the API is doing.
1698  # Note that we use a whitespace test rather than a \b test to avoid
1699  # any remote case where a word like on may be inside of a table name
1700  # surrounded by symbols which may be considered word breaks.
1701  if ( preg_match( '/(^|\s)(DISTINCT|JOIN|ON|AS)(\s|$)/i', $name ) !== 0 ) {
1702  return $name;
1703  }
1704 
1705  # Split database and table into proper variables.
1706  # We reverse the explode so that database.table and table both output
1707  # the correct table.
1708  $dbDetails = explode( '.', $name, 3 );
1709  if ( count( $dbDetails ) == 3 ) {
1710  list( $database, $schema, $table ) = $dbDetails;
1711  # We don't want any prefix added in this case
1712  $prefix = '';
1713  } elseif ( count( $dbDetails ) == 2 ) {
1714  list( $database, $table ) = $dbDetails;
1715  # We don't want any prefix added in this case
1716  # In dbs that support it, $database may actually be the schema
1717  # but that doesn't affect any of the functionality here
1718  $prefix = '';
1719  $schema = null;
1720  } else {
1721  list( $table ) = $dbDetails;
1722  if ( $wgSharedDB !== null # We have a shared database
1723  && $this->mForeign == false # We're not working on a foreign database
1724  && !$this->isQuotedIdentifier( $table ) # Prevent shared tables listing '`table`'
1725  && in_array( $table, $wgSharedTables ) # A shared table is selected
1726  ) {
1727  $database = $wgSharedDB;
1728  $schema = $wgSharedSchema === null ? $this->mSchema : $wgSharedSchema;
1729  $prefix = $wgSharedPrefix === null ? $this->mTablePrefix : $wgSharedPrefix;
1730  } else {
1731  $database = null;
1732  $schema = $this->mSchema; # Default schema
1733  $prefix = $this->mTablePrefix; # Default prefix
1734  }
1735  }
1736 
1737  # Quote $table and apply the prefix if not quoted.
1738  # $tableName might be empty if this is called from Database::replaceVars()
1739  $tableName = "{$prefix}{$table}";
1740  if ( $format == 'quoted' && !$this->isQuotedIdentifier( $tableName ) && $tableName !== '' ) {
1741  $tableName = $this->addIdentifierQuotes( $tableName );
1742  }
1743 
1744  # Quote $schema and merge it with the table name if needed
1745  if ( strlen( $schema ) ) {
1746  if ( $format == 'quoted' && !$this->isQuotedIdentifier( $schema ) ) {
1747  $schema = $this->addIdentifierQuotes( $schema );
1748  }
1749  $tableName = $schema . '.' . $tableName;
1750  }
1751 
1752  # Quote $database and merge it with the table name if needed
1753  if ( $database !== null ) {
1754  if ( $format == 'quoted' && !$this->isQuotedIdentifier( $database ) ) {
1755  $database = $this->addIdentifierQuotes( $database );
1756  }
1757  $tableName = $database . '.' . $tableName;
1758  }
1759 
1760  return $tableName;
1761  }
1762 
1774  public function tableNames() {
1775  $inArray = func_get_args();
1776  $retVal = [];
1777 
1778  foreach ( $inArray as $name ) {
1779  $retVal[$name] = $this->tableName( $name );
1780  }
1781 
1782  return $retVal;
1783  }
1784 
1796  public function tableNamesN() {
1797  $inArray = func_get_args();
1798  $retVal = [];
1799 
1800  foreach ( $inArray as $name ) {
1801  $retVal[] = $this->tableName( $name );
1802  }
1803 
1804  return $retVal;
1805  }
1806 
1815  public function tableNameWithAlias( $name, $alias = false ) {
1816  if ( !$alias || $alias == $name ) {
1817  return $this->tableName( $name );
1818  } else {
1819  return $this->tableName( $name ) . ' ' . $this->addIdentifierQuotes( $alias );
1820  }
1821  }
1822 
1829  public function tableNamesWithAlias( $tables ) {
1830  $retval = [];
1831  foreach ( $tables as $alias => $table ) {
1832  if ( is_numeric( $alias ) ) {
1833  $alias = $table;
1834  }
1835  $retval[] = $this->tableNameWithAlias( $table, $alias );
1836  }
1837 
1838  return $retval;
1839  }
1840 
1849  public function fieldNameWithAlias( $name, $alias = false ) {
1850  if ( !$alias || (string)$alias === (string)$name ) {
1851  return $name;
1852  } else {
1853  return $name . ' AS ' . $this->addIdentifierQuotes( $alias ); // PostgreSQL needs AS
1854  }
1855  }
1856 
1863  public function fieldNamesWithAlias( $fields ) {
1864  $retval = [];
1865  foreach ( $fields as $alias => $field ) {
1866  if ( is_numeric( $alias ) ) {
1867  $alias = $field;
1868  }
1869  $retval[] = $this->fieldNameWithAlias( $field, $alias );
1870  }
1871 
1872  return $retval;
1873  }
1874 
1884  protected function tableNamesWithUseIndexOrJOIN(
1885  $tables, $use_index = [], $join_conds = []
1886  ) {
1887  $ret = [];
1888  $retJOIN = [];
1889  $use_index = (array)$use_index;
1890  $join_conds = (array)$join_conds;
1891 
1892  foreach ( $tables as $alias => $table ) {
1893  if ( !is_string( $alias ) ) {
1894  // No alias? Set it equal to the table name
1895  $alias = $table;
1896  }
1897  // Is there a JOIN clause for this table?
1898  if ( isset( $join_conds[$alias] ) ) {
1899  list( $joinType, $conds ) = $join_conds[$alias];
1900  $tableClause = $joinType;
1901  $tableClause .= ' ' . $this->tableNameWithAlias( $table, $alias );
1902  if ( isset( $use_index[$alias] ) ) { // has USE INDEX?
1903  $use = $this->useIndexClause( implode( ',', (array)$use_index[$alias] ) );
1904  if ( $use != '' ) {
1905  $tableClause .= ' ' . $use;
1906  }
1907  }
1908  $on = $this->makeList( (array)$conds, LIST_AND );
1909  if ( $on != '' ) {
1910  $tableClause .= ' ON (' . $on . ')';
1911  }
1912 
1913  $retJOIN[] = $tableClause;
1914  } elseif ( isset( $use_index[$alias] ) ) {
1915  // Is there an INDEX clause for this table?
1916  $tableClause = $this->tableNameWithAlias( $table, $alias );
1917  $tableClause .= ' ' . $this->useIndexClause(
1918  implode( ',', (array)$use_index[$alias] )
1919  );
1920 
1921  $ret[] = $tableClause;
1922  } else {
1923  $tableClause = $this->tableNameWithAlias( $table, $alias );
1924 
1925  $ret[] = $tableClause;
1926  }
1927  }
1928 
1929  // We can't separate explicit JOIN clauses with ',', use ' ' for those
1930  $implicitJoins = !empty( $ret ) ? implode( ',', $ret ) : "";
1931  $explicitJoins = !empty( $retJOIN ) ? implode( ' ', $retJOIN ) : "";
1932 
1933  // Compile our final table clause
1934  return implode( ' ', [ $implicitJoins, $explicitJoins ] );
1935  }
1936 
1943  protected function indexName( $index ) {
1944  // Backwards-compatibility hack
1945  $renamed = [
1946  'ar_usertext_timestamp' => 'usertext_timestamp',
1947  'un_user_id' => 'user_id',
1948  'un_user_ip' => 'user_ip',
1949  ];
1950 
1951  if ( isset( $renamed[$index] ) ) {
1952  return $renamed[$index];
1953  } else {
1954  return $index;
1955  }
1956  }
1957 
1958  public function addQuotes( $s ) {
1959  if ( $s instanceof Blob ) {
1960  $s = $s->fetch();
1961  }
1962  if ( $s === null ) {
1963  return 'NULL';
1964  } else {
1965  # This will also quote numeric values. This should be harmless,
1966  # and protects against weird problems that occur when they really
1967  # _are_ strings such as article titles and string->number->string
1968  # conversion is not 1:1.
1969  return "'" . $this->strencode( $s ) . "'";
1970  }
1971  }
1972 
1982  public function addIdentifierQuotes( $s ) {
1983  return '"' . str_replace( '"', '""', $s ) . '"';
1984  }
1985 
1995  public function isQuotedIdentifier( $name ) {
1996  return $name[0] == '"' && substr( $name, -1, 1 ) == '"';
1997  }
1998 
2003  protected function escapeLikeInternal( $s ) {
2004  return addcslashes( $s, '\%_' );
2005  }
2006 
2007  public function buildLike() {
2008  $params = func_get_args();
2009 
2010  if ( count( $params ) > 0 && is_array( $params[0] ) ) {
2011  $params = $params[0];
2012  }
2013 
2014  $s = '';
2015 
2016  foreach ( $params as $value ) {
2017  if ( $value instanceof LikeMatch ) {
2018  $s .= $value->toString();
2019  } else {
2020  $s .= $this->escapeLikeInternal( $value );
2021  }
2022  }
2023 
2024  return " LIKE {$this->addQuotes( $s )} ";
2025  }
2026 
2027  public function anyChar() {
2028  return new LikeMatch( '_' );
2029  }
2030 
2031  public function anyString() {
2032  return new LikeMatch( '%' );
2033  }
2034 
2035  public function nextSequenceValue( $seqName ) {
2036  return null;
2037  }
2038 
2049  public function useIndexClause( $index ) {
2050  return '';
2051  }
2052 
2053  public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
2054  $quotedTable = $this->tableName( $table );
2055 
2056  if ( count( $rows ) == 0 ) {
2057  return;
2058  }
2059 
2060  # Single row case
2061  if ( !is_array( reset( $rows ) ) ) {
2062  $rows = [ $rows ];
2063  }
2064 
2065  // @FXIME: this is not atomic, but a trx would break affectedRows()
2066  foreach ( $rows as $row ) {
2067  # Delete rows which collide
2068  if ( $uniqueIndexes ) {
2069  $sql = "DELETE FROM $quotedTable WHERE ";
2070  $first = true;
2071  foreach ( $uniqueIndexes as $index ) {
2072  if ( $first ) {
2073  $first = false;
2074  $sql .= '( ';
2075  } else {
2076  $sql .= ' ) OR ( ';
2077  }
2078  if ( is_array( $index ) ) {
2079  $first2 = true;
2080  foreach ( $index as $col ) {
2081  if ( $first2 ) {
2082  $first2 = false;
2083  } else {
2084  $sql .= ' AND ';
2085  }
2086  $sql .= $col . '=' . $this->addQuotes( $row[$col] );
2087  }
2088  } else {
2089  $sql .= $index . '=' . $this->addQuotes( $row[$index] );
2090  }
2091  }
2092  $sql .= ' )';
2093  $this->query( $sql, $fname );
2094  }
2095 
2096  # Now insert the row
2097  $this->insert( $table, $row, $fname );
2098  }
2099  }
2100 
2111  protected function nativeReplace( $table, $rows, $fname ) {
2112  $table = $this->tableName( $table );
2113 
2114  # Single row case
2115  if ( !is_array( reset( $rows ) ) ) {
2116  $rows = [ $rows ];
2117  }
2118 
2119  $sql = "REPLACE INTO $table (" . implode( ',', array_keys( $rows[0] ) ) . ') VALUES ';
2120  $first = true;
2121 
2122  foreach ( $rows as $row ) {
2123  if ( $first ) {
2124  $first = false;
2125  } else {
2126  $sql .= ',';
2127  }
2128 
2129  $sql .= '(' . $this->makeList( $row ) . ')';
2130  }
2131 
2132  return $this->query( $sql, $fname );
2133  }
2134 
2135  public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
2136  $fname = __METHOD__
2137  ) {
2138  if ( !count( $rows ) ) {
2139  return true; // nothing to do
2140  }
2141 
2142  if ( !is_array( reset( $rows ) ) ) {
2143  $rows = [ $rows ];
2144  }
2145 
2146  if ( count( $uniqueIndexes ) ) {
2147  $clauses = []; // list WHERE clauses that each identify a single row
2148  foreach ( $rows as $row ) {
2149  foreach ( $uniqueIndexes as $index ) {
2150  $index = is_array( $index ) ? $index : [ $index ]; // columns
2151  $rowKey = []; // unique key to this row
2152  foreach ( $index as $column ) {
2153  $rowKey[$column] = $row[$column];
2154  }
2155  $clauses[] = $this->makeList( $rowKey, LIST_AND );
2156  }
2157  }
2158  $where = [ $this->makeList( $clauses, LIST_OR ) ];
2159  } else {
2160  $where = false;
2161  }
2162 
2163  $useTrx = !$this->mTrxLevel;
2164  if ( $useTrx ) {
2165  $this->begin( $fname );
2166  }
2167  try {
2168  # Update any existing conflicting row(s)
2169  if ( $where !== false ) {
2170  $ok = $this->update( $table, $set, $where, $fname );
2171  } else {
2172  $ok = true;
2173  }
2174  # Now insert any non-conflicting row(s)
2175  $ok = $this->insert( $table, $rows, $fname, [ 'IGNORE' ] ) && $ok;
2176  } catch ( Exception $e ) {
2177  if ( $useTrx ) {
2178  $this->rollback( $fname );
2179  }
2180  throw $e;
2181  }
2182  if ( $useTrx ) {
2183  $this->commit( $fname );
2184  }
2185 
2186  return $ok;
2187  }
2188 
2189  public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds,
2190  $fname = __METHOD__
2191  ) {
2192  if ( !$conds ) {
2193  throw new DBUnexpectedError( $this,
2194  'DatabaseBase::deleteJoin() called with empty $conds' );
2195  }
2196 
2197  $delTable = $this->tableName( $delTable );
2198  $joinTable = $this->tableName( $joinTable );
2199  $sql = "DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable ";
2200  if ( $conds != '*' ) {
2201  $sql .= 'WHERE ' . $this->makeList( $conds, LIST_AND );
2202  }
2203  $sql .= ')';
2204 
2205  $this->query( $sql, $fname );
2206  }
2207 
2215  public function textFieldSize( $table, $field ) {
2216  $table = $this->tableName( $table );
2217  $sql = "SHOW COLUMNS FROM $table LIKE \"$field\";";
2218  $res = $this->query( $sql, 'DatabaseBase::textFieldSize' );
2219  $row = $this->fetchObject( $res );
2220 
2221  $m = [];
2222 
2223  if ( preg_match( '/\((.*)\)/', $row->Type, $m ) ) {
2224  $size = $m[1];
2225  } else {
2226  $size = -1;
2227  }
2228 
2229  return $size;
2230  }
2231 
2240  public function lowPriorityOption() {
2241  return '';
2242  }
2243 
2244  public function delete( $table, $conds, $fname = __METHOD__ ) {
2245  if ( !$conds ) {
2246  throw new DBUnexpectedError( $this, 'DatabaseBase::delete() called with no conditions' );
2247  }
2248 
2249  $table = $this->tableName( $table );
2250  $sql = "DELETE FROM $table";
2251 
2252  if ( $conds != '*' ) {
2253  if ( is_array( $conds ) ) {
2254  $conds = $this->makeList( $conds, LIST_AND );
2255  }
2256  $sql .= ' WHERE ' . $conds;
2257  }
2258 
2259  return $this->query( $sql, $fname );
2260  }
2261 
2262  public function insertSelect( $destTable, $srcTable, $varMap, $conds,
2263  $fname = __METHOD__,
2264  $insertOptions = [], $selectOptions = []
2265  ) {
2266  $destTable = $this->tableName( $destTable );
2267 
2268  if ( !is_array( $insertOptions ) ) {
2269  $insertOptions = [ $insertOptions ];
2270  }
2271 
2272  $insertOptions = $this->makeInsertOptions( $insertOptions );
2273 
2274  if ( !is_array( $selectOptions ) ) {
2275  $selectOptions = [ $selectOptions ];
2276  }
2277 
2278  list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
2279 
2280  if ( is_array( $srcTable ) ) {
2281  $srcTable = implode( ',', array_map( [ &$this, 'tableName' ], $srcTable ) );
2282  } else {
2283  $srcTable = $this->tableName( $srcTable );
2284  }
2285 
2286  $sql = "INSERT $insertOptions INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
2287  " SELECT $startOpts " . implode( ',', $varMap ) .
2288  " FROM $srcTable $useIndex ";
2289 
2290  if ( $conds != '*' ) {
2291  if ( is_array( $conds ) ) {
2292  $conds = $this->makeList( $conds, LIST_AND );
2293  }
2294  $sql .= " WHERE $conds";
2295  }
2296 
2297  $sql .= " $tailOpts";
2298 
2299  return $this->query( $sql, $fname );
2300  }
2301 
2321  public function limitResult( $sql, $limit, $offset = false ) {
2322  if ( !is_numeric( $limit ) ) {
2323  throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" );
2324  }
2325 
2326  return "$sql LIMIT "
2327  . ( ( is_numeric( $offset ) && $offset != 0 ) ? "{$offset}," : "" )
2328  . "{$limit} ";
2329  }
2330 
2331  public function unionSupportsOrderAndLimit() {
2332  return true; // True for almost every DB supported
2333  }
2334 
2335  public function unionQueries( $sqls, $all ) {
2336  $glue = $all ? ') UNION ALL (' : ') UNION (';
2337 
2338  return '(' . implode( $glue, $sqls ) . ')';
2339  }
2340 
2341  public function conditional( $cond, $trueVal, $falseVal ) {
2342  if ( is_array( $cond ) ) {
2343  $cond = $this->makeList( $cond, LIST_AND );
2344  }
2345 
2346  return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
2347  }
2348 
2349  public function strreplace( $orig, $old, $new ) {
2350  return "REPLACE({$orig}, {$old}, {$new})";
2351  }
2352 
2353  public function getServerUptime() {
2354  return 0;
2355  }
2356 
2357  public function wasDeadlock() {
2358  return false;
2359  }
2360 
2361  public function wasLockTimeout() {
2362  return false;
2363  }
2364 
2365  public function wasErrorReissuable() {
2366  return false;
2367  }
2368 
2369  public function wasReadOnlyError() {
2370  return false;
2371  }
2372 
2380  public function wasConnectionError( $errno ) {
2381  return false;
2382  }
2383 
2408  public function deadlockLoop() {
2409  $args = func_get_args();
2410  $function = array_shift( $args );
2411  $tries = self::DEADLOCK_TRIES;
2412 
2413  $this->begin( __METHOD__ );
2414 
2415  $retVal = null;
2417  $e = null;
2418  do {
2419  try {
2420  $retVal = call_user_func_array( $function, $args );
2421  break;
2422  } catch ( DBQueryError $e ) {
2423  if ( $this->wasDeadlock() ) {
2424  // Retry after a randomized delay
2425  usleep( mt_rand( self::DEADLOCK_DELAY_MIN, self::DEADLOCK_DELAY_MAX ) );
2426  } else {
2427  // Throw the error back up
2428  throw $e;
2429  }
2430  }
2431  } while ( --$tries > 0 );
2432 
2433  if ( $tries <= 0 ) {
2434  // Too many deadlocks; give up
2435  $this->rollback( __METHOD__ );
2436  throw $e;
2437  } else {
2438  $this->commit( __METHOD__ );
2439 
2440  return $retVal;
2441  }
2442  }
2443 
2444  public function masterPosWait( DBMasterPos $pos, $timeout ) {
2445  # Real waits are implemented in the subclass.
2446  return 0;
2447  }
2448 
2449  public function getSlavePos() {
2450  # Stub
2451  return false;
2452  }
2453 
2454  public function getMasterPos() {
2455  # Stub
2456  return false;
2457  }
2458 
2459  public function serverIsReadOnly() {
2460  return false;
2461  }
2462 
2463  final public function onTransactionResolution( callable $callback ) {
2464  if ( !$this->mTrxLevel ) {
2465  throw new DBUnexpectedError( $this, "No transaction is active." );
2466  }
2467  $this->mTrxEndCallbacks[] = [ $callback, wfGetCaller() ];
2468  }
2469 
2470  final public function onTransactionIdle( callable $callback ) {
2471  $this->mTrxIdleCallbacks[] = [ $callback, wfGetCaller() ];
2472  if ( !$this->mTrxLevel ) {
2473  $this->runOnTransactionIdleCallbacks( self::TRIGGER_IDLE );
2474  }
2475  }
2476 
2477  final public function onTransactionPreCommitOrIdle( callable $callback ) {
2478  if ( $this->mTrxLevel ) {
2479  $this->mTrxPreCommitCallbacks[] = [ $callback, wfGetCaller() ];
2480  } else {
2481  // If no transaction is active, then make one for this callback
2482  $this->begin( __METHOD__ );
2483  try {
2484  call_user_func( $callback );
2485  $this->commit( __METHOD__ );
2486  } catch ( Exception $e ) {
2487  $this->rollback( __METHOD__ );
2488  throw $e;
2489  }
2490  }
2491  }
2492 
2501  final public function setPostCommitCallbackSupression( $suppress ) {
2502  $this->suppressPostCommitCallbacks = $suppress;
2503  }
2504 
2513  public function runOnTransactionIdleCallbacks( $trigger ) {
2514  if ( $this->suppressPostCommitCallbacks ) {
2515  return;
2516  }
2517 
2518  $autoTrx = $this->getFlag( DBO_TRX ); // automatic begin() enabled?
2519 
2520  $e = $ePrior = null; // last exception
2521  do { // callbacks may add callbacks :)
2522  $callbacks = array_merge(
2523  $this->mTrxIdleCallbacks,
2524  $this->mTrxEndCallbacks // include "transaction resolution" callbacks
2525  );
2526  $this->mTrxIdleCallbacks = []; // consumed (and recursion guard)
2527  $this->mTrxEndCallbacks = []; // consumed (recursion guard)
2528  foreach ( $callbacks as $callback ) {
2529  try {
2530  list( $phpCallback ) = $callback;
2531  $this->clearFlag( DBO_TRX ); // make each query its own transaction
2532  call_user_func_array( $phpCallback, [ $trigger ] );
2533  if ( $autoTrx ) {
2534  $this->setFlag( DBO_TRX ); // restore automatic begin()
2535  } else {
2536  $this->clearFlag( DBO_TRX ); // restore auto-commit
2537  }
2538  } catch ( Exception $e ) {
2539  if ( $ePrior ) {
2541  }
2542  $ePrior = $e;
2543  // Some callbacks may use startAtomic/endAtomic, so make sure
2544  // their transactions are ended so other callbacks don't fail
2545  if ( $this->trxLevel() ) {
2546  $this->rollback( __METHOD__ );
2547  }
2548  }
2549  }
2550  } while ( count( $this->mTrxIdleCallbacks ) );
2551 
2552  if ( $e instanceof Exception ) {
2553  throw $e; // re-throw any last exception
2554  }
2555  }
2556 
2565  $e = $ePrior = null; // last exception
2566  do { // callbacks may add callbacks :)
2567  $callbacks = $this->mTrxPreCommitCallbacks;
2568  $this->mTrxPreCommitCallbacks = []; // consumed (and recursion guard)
2569  foreach ( $callbacks as $callback ) {
2570  try {
2571  list( $phpCallback ) = $callback;
2572  call_user_func( $phpCallback );
2573  } catch ( Exception $e ) {
2574  if ( $ePrior ) {
2576  }
2577  $ePrior = $e;
2578  }
2579  }
2580  } while ( count( $this->mTrxPreCommitCallbacks ) );
2581 
2582  if ( $e instanceof Exception ) {
2583  throw $e; // re-throw any last exception
2584  }
2585  }
2586 
2587  final public function startAtomic( $fname = __METHOD__ ) {
2588  if ( !$this->mTrxLevel ) {
2589  $this->begin( $fname );
2590  $this->mTrxAutomatic = true;
2591  // If DBO_TRX is set, a series of startAtomic/endAtomic pairs will result
2592  // in all changes being in one transaction to keep requests transactional.
2593  if ( !$this->getFlag( DBO_TRX ) ) {
2594  $this->mTrxAutomaticAtomic = true;
2595  }
2596  }
2597 
2598  $this->mTrxAtomicLevels[] = $fname;
2599  }
2600 
2601  final public function endAtomic( $fname = __METHOD__ ) {
2602  if ( !$this->mTrxLevel ) {
2603  throw new DBUnexpectedError( $this, "No atomic transaction is open (got $fname)." );
2604  }
2605  if ( !$this->mTrxAtomicLevels ||
2606  array_pop( $this->mTrxAtomicLevels ) !== $fname
2607  ) {
2608  throw new DBUnexpectedError( $this, "Invalid atomic section ended (got $fname)." );
2609  }
2610 
2611  if ( !$this->mTrxAtomicLevels && $this->mTrxAutomaticAtomic ) {
2612  $this->commit( $fname, 'flush' );
2613  }
2614  }
2615 
2616  final public function doAtomicSection( $fname, callable $callback ) {
2617  $this->startAtomic( $fname );
2618  try {
2619  call_user_func_array( $callback, [ $this, $fname ] );
2620  } catch ( Exception $e ) {
2621  $this->rollback( $fname );
2622  throw $e;
2623  }
2624  $this->endAtomic( $fname );
2625  }
2626 
2627  final public function begin( $fname = __METHOD__ ) {
2628  if ( $this->mTrxLevel ) { // implicit commit
2629  if ( $this->mTrxAtomicLevels ) {
2630  // If the current transaction was an automatic atomic one, then we definitely have
2631  // a problem. Same if there is any unclosed atomic level.
2632  $levels = implode( ', ', $this->mTrxAtomicLevels );
2633  throw new DBUnexpectedError(
2634  $this,
2635  "Got explicit BEGIN from $fname while atomic section(s) $levels are open."
2636  );
2637  } elseif ( !$this->mTrxAutomatic ) {
2638  // We want to warn about inadvertently nested begin/commit pairs, but not about
2639  // auto-committing implicit transactions that were started by query() via DBO_TRX
2640  throw new DBUnexpectedError(
2641  $this,
2642  "$fname: Transaction already in progress (from {$this->mTrxFname}), " .
2643  " performing implicit commit!"
2644  );
2645  } else {
2646  // The transaction was automatic and has done write operations
2647  if ( $this->mTrxDoneWrites ) {
2648  wfLogDBError( "$fname: Automatic transaction with writes in progress" .
2649  " (from {$this->mTrxFname}), performing implicit commit!\n"
2650  );
2651  }
2652  }
2653 
2655  $writeTime = $this->pendingWriteQueryDuration();
2656  $this->doCommit( $fname );
2657  if ( $this->mTrxDoneWrites ) {
2658  $this->mDoneWrites = microtime( true );
2659  $this->getTransactionProfiler()->transactionWritingOut(
2660  $this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
2661  }
2662 
2663  $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
2664  }
2665 
2666  // Avoid fatals if close() was called
2667  $this->assertOpen();
2668 
2669  $this->doBegin( $fname );
2670  $this->mTrxTimestamp = microtime( true );
2671  $this->mTrxFname = $fname;
2672  $this->mTrxDoneWrites = false;
2673  $this->mTrxAutomatic = false;
2674  $this->mTrxAutomaticAtomic = false;
2675  $this->mTrxAtomicLevels = [];
2676  $this->mTrxShortId = wfRandomString( 12 );
2677  $this->mTrxWriteDuration = 0.0;
2678  $this->mTrxWriteCallers = [];
2679  // First SELECT after BEGIN will establish the snapshot in REPEATABLE-READ.
2680  // Get an estimate of the slave lag before then, treating estimate staleness
2681  // as lag itself just to be safe
2682  $status = $this->getApproximateLagStatus();
2683  $this->mTrxSlaveLag = $status['lag'] + ( microtime( true ) - $status['since'] );
2684  }
2685 
2692  protected function doBegin( $fname ) {
2693  $this->query( 'BEGIN', $fname );
2694  $this->mTrxLevel = 1;
2695  }
2696 
2697  final public function commit( $fname = __METHOD__, $flush = '' ) {
2698  if ( $this->mTrxLevel && $this->mTrxAtomicLevels ) {
2699  // There are still atomic sections open. This cannot be ignored
2700  $levels = implode( ', ', $this->mTrxAtomicLevels );
2701  throw new DBUnexpectedError(
2702  $this,
2703  "Got COMMIT while atomic sections $levels are still open"
2704  );
2705  }
2706 
2707  if ( $flush === 'flush' ) {
2708  if ( !$this->mTrxLevel ) {
2709  return; // nothing to do
2710  } elseif ( !$this->mTrxAutomatic ) {
2711  throw new DBUnexpectedError(
2712  $this,
2713  "$fname: Flushing an explicit transaction, getting out of sync!"
2714  );
2715  }
2716  } else {
2717  if ( !$this->mTrxLevel ) {
2718  wfWarn( "$fname: No transaction to commit, something got out of sync!" );
2719  return; // nothing to do
2720  } elseif ( $this->mTrxAutomatic ) {
2721  wfWarn( "$fname: Explicit commit of implicit transaction. Something may be out of sync!" );
2722  }
2723  }
2724 
2725  // Avoid fatals if close() was called
2726  $this->assertOpen();
2727 
2729  $writeTime = $this->pendingWriteQueryDuration();
2730  $this->doCommit( $fname );
2731  if ( $this->mTrxDoneWrites ) {
2732  $this->mDoneWrites = microtime( true );
2733  $this->getTransactionProfiler()->transactionWritingOut(
2734  $this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
2735  }
2736 
2737  $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
2738  }
2739 
2746  protected function doCommit( $fname ) {
2747  if ( $this->mTrxLevel ) {
2748  $this->query( 'COMMIT', $fname );
2749  $this->mTrxLevel = 0;
2750  }
2751  }
2752 
2753  final public function rollback( $fname = __METHOD__, $flush = '' ) {
2754  if ( $flush !== 'flush' ) {
2755  if ( !$this->mTrxLevel ) {
2756  wfWarn( "$fname: No transaction to rollback, something got out of sync!" );
2757  return; // nothing to do
2758  }
2759  } else {
2760  if ( !$this->mTrxLevel ) {
2761  return; // nothing to do
2762  }
2763  }
2764 
2765  // Avoid fatals if close() was called
2766  $this->assertOpen();
2767 
2768  $this->doRollback( $fname );
2769  $this->mTrxAtomicLevels = [];
2770  if ( $this->mTrxDoneWrites ) {
2771  $this->getTransactionProfiler()->transactionWritingOut(
2772  $this->mServer, $this->mDBname, $this->mTrxShortId );
2773  }
2774 
2775  $this->mTrxIdleCallbacks = []; // clear
2776  $this->mTrxPreCommitCallbacks = []; // clear
2777  $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
2778  }
2779 
2786  protected function doRollback( $fname ) {
2787  if ( $this->mTrxLevel ) {
2788  $this->query( 'ROLLBACK', $fname, true );
2789  $this->mTrxLevel = 0;
2790  }
2791  }
2792 
2808  public function duplicateTableStructure( $oldName, $newName, $temporary = false,
2809  $fname = __METHOD__
2810  ) {
2811  throw new MWException(
2812  'DatabaseBase::duplicateTableStructure is not implemented in descendant class' );
2813  }
2814 
2815  function listTables( $prefix = null, $fname = __METHOD__ ) {
2816  throw new MWException( 'DatabaseBase::listTables is not implemented in descendant class' );
2817  }
2818 
2823  final public function clearViewsCache() {
2824  $this->allViews = null;
2825  }
2826 
2839  public function listViews( $prefix = null, $fname = __METHOD__ ) {
2840  throw new MWException( 'DatabaseBase::listViews is not implemented in descendant class' );
2841  }
2842 
2851  public function isView( $name ) {
2852  throw new MWException( 'DatabaseBase::isView is not implemented in descendant class' );
2853  }
2854 
2855  public function timestamp( $ts = 0 ) {
2856  return wfTimestamp( TS_MW, $ts );
2857  }
2858 
2859  public function timestampOrNull( $ts = null ) {
2860  if ( is_null( $ts ) ) {
2861  return null;
2862  } else {
2863  return $this->timestamp( $ts );
2864  }
2865  }
2866 
2880  protected function resultObject( $result ) {
2881  if ( !$result ) {
2882  return false;
2883  } elseif ( $result instanceof ResultWrapper ) {
2884  return $result;
2885  } elseif ( $result === true ) {
2886  // Successful write query
2887  return $result;
2888  } else {
2889  return new ResultWrapper( $this, $result );
2890  }
2891  }
2892 
2893  public function ping() {
2894  # Stub. Not essential to override.
2895  return true;
2896  }
2897 
2898  public function getSessionLagStatus() {
2899  return $this->getTransactionLagStatus() ?: $this->getApproximateLagStatus();
2900  }
2901 
2913  public function getTransactionLagStatus() {
2914  return $this->mTrxLevel
2915  ? [ 'lag' => $this->mTrxSlaveLag, 'since' => $this->trxTimestamp() ]
2916  : null;
2917  }
2918 
2925  public function getApproximateLagStatus() {
2926  return [
2927  'lag' => $this->getLBInfo( 'slave' ) ? $this->getLag() : 0,
2928  'since' => microtime( true )
2929  ];
2930  }
2931 
2950  public static function getCacheSetOptions( IDatabase $db1 ) {
2951  $res = [ 'lag' => 0, 'since' => INF, 'pending' => false ];
2952  foreach ( func_get_args() as $db ) {
2954  $status = $db->getSessionLagStatus();
2955  if ( $status['lag'] === false ) {
2956  $res['lag'] = false;
2957  } elseif ( $res['lag'] !== false ) {
2958  $res['lag'] = max( $res['lag'], $status['lag'] );
2959  }
2960  $res['since'] = min( $res['since'], $status['since'] );
2961  $res['pending'] = $res['pending'] ?: $db->writesPending();
2962  }
2963 
2964  return $res;
2965  }
2966 
2967  public function getLag() {
2968  return 0;
2969  }
2970 
2971  function maxListLen() {
2972  return 0;
2973  }
2974 
2975  public function encodeBlob( $b ) {
2976  return $b;
2977  }
2978 
2979  public function decodeBlob( $b ) {
2980  if ( $b instanceof Blob ) {
2981  $b = $b->fetch();
2982  }
2983  return $b;
2984  }
2985 
2986  public function setSessionOptions( array $options ) {
2987  }
2988 
3005  public function sourceFile(
3006  $filename, $lineCallback = false, $resultCallback = false, $fname = false, $inputCallback = false
3007  ) {
3008  MediaWiki\suppressWarnings();
3009  $fp = fopen( $filename, 'r' );
3010  MediaWiki\restoreWarnings();
3011 
3012  if ( false === $fp ) {
3013  throw new MWException( "Could not open \"{$filename}\".\n" );
3014  }
3015 
3016  if ( !$fname ) {
3017  $fname = __METHOD__ . "( $filename )";
3018  }
3019 
3020  try {
3021  $error = $this->sourceStream( $fp, $lineCallback, $resultCallback, $fname, $inputCallback );
3022  } catch ( Exception $e ) {
3023  fclose( $fp );
3024  throw $e;
3025  }
3026 
3027  fclose( $fp );
3028 
3029  return $error;
3030  }
3031 
3040  public function patchPath( $patch ) {
3041  global $IP;
3042 
3043  $dbType = $this->getType();
3044  if ( file_exists( "$IP/maintenance/$dbType/archives/$patch" ) ) {
3045  return "$IP/maintenance/$dbType/archives/$patch";
3046  } else {
3047  return "$IP/maintenance/archives/$patch";
3048  }
3049  }
3050 
3051  public function setSchemaVars( $vars ) {
3052  $this->mSchemaVars = $vars;
3053  }
3054 
3068  public function sourceStream( $fp, $lineCallback = false, $resultCallback = false,
3069  $fname = __METHOD__, $inputCallback = false
3070  ) {
3071  $cmd = '';
3072 
3073  while ( !feof( $fp ) ) {
3074  if ( $lineCallback ) {
3075  call_user_func( $lineCallback );
3076  }
3077 
3078  $line = trim( fgets( $fp ) );
3079 
3080  if ( $line == '' ) {
3081  continue;
3082  }
3083 
3084  if ( '-' == $line[0] && '-' == $line[1] ) {
3085  continue;
3086  }
3087 
3088  if ( $cmd != '' ) {
3089  $cmd .= ' ';
3090  }
3091 
3092  $done = $this->streamStatementEnd( $cmd, $line );
3093 
3094  $cmd .= "$line\n";
3095 
3096  if ( $done || feof( $fp ) ) {
3097  $cmd = $this->replaceVars( $cmd );
3098 
3099  if ( ( $inputCallback && call_user_func( $inputCallback, $cmd ) ) || !$inputCallback ) {
3100  $res = $this->query( $cmd, $fname );
3101 
3102  if ( $resultCallback ) {
3103  call_user_func( $resultCallback, $res, $this );
3104  }
3105 
3106  if ( false === $res ) {
3107  $err = $this->lastError();
3108 
3109  return "Query \"{$cmd}\" failed with error code \"$err\".\n";
3110  }
3111  }
3112  $cmd = '';
3113  }
3114  }
3115 
3116  return true;
3117  }
3118 
3126  public function streamStatementEnd( &$sql, &$newLine ) {
3127  if ( $this->delimiter ) {
3128  $prev = $newLine;
3129  $newLine = preg_replace( '/' . preg_quote( $this->delimiter, '/' ) . '$/', '', $newLine );
3130  if ( $newLine != $prev ) {
3131  return true;
3132  }
3133  }
3134 
3135  return false;
3136  }
3137 
3158  protected function replaceVars( $ins ) {
3159  $vars = $this->getSchemaVars();
3160  return preg_replace_callback(
3161  '!
3162  /\* (\$wgDBprefix|[_i]) \*/ (\w*) | # 1-2. tableName, indexName
3163  \'\{\$ (\w+) }\' | # 3. addQuotes
3164  `\{\$ (\w+) }` | # 4. addIdentifierQuotes
3165  /\*\$ (\w+) \*/ # 5. leave unencoded
3166  !x',
3167  function ( $m ) use ( $vars ) {
3168  // Note: Because of <https://bugs.php.net/bug.php?id=51881>,
3169  // check for both nonexistent keys *and* the empty string.
3170  if ( isset( $m[1] ) && $m[1] !== '' ) {
3171  if ( $m[1] === 'i' ) {
3172  return $this->indexName( $m[2] );
3173  } else {
3174  return $this->tableName( $m[2] );
3175  }
3176  } elseif ( isset( $m[3] ) && $m[3] !== '' && array_key_exists( $m[3], $vars ) ) {
3177  return $this->addQuotes( $vars[$m[3]] );
3178  } elseif ( isset( $m[4] ) && $m[4] !== '' && array_key_exists( $m[4], $vars ) ) {
3179  return $this->addIdentifierQuotes( $vars[$m[4]] );
3180  } elseif ( isset( $m[5] ) && $m[5] !== '' && array_key_exists( $m[5], $vars ) ) {
3181  return $vars[$m[5]];
3182  } else {
3183  return $m[0];
3184  }
3185  },
3186  $ins
3187  );
3188  }
3189 
3196  protected function getSchemaVars() {
3197  if ( $this->mSchemaVars ) {
3198  return $this->mSchemaVars;
3199  } else {
3200  return $this->getDefaultSchemaVars();
3201  }
3202  }
3203 
3212  protected function getDefaultSchemaVars() {
3213  return [];
3214  }
3215 
3216  public function lockIsFree( $lockName, $method ) {
3217  return true;
3218  }
3219 
3220  public function lock( $lockName, $method, $timeout = 5 ) {
3221  $this->mNamedLocksHeld[$lockName] = 1;
3222 
3223  return true;
3224  }
3225 
3226  public function unlock( $lockName, $method ) {
3227  unset( $this->mNamedLocksHeld[$lockName] );
3228 
3229  return true;
3230  }
3231 
3232  public function getScopedLockAndFlush( $lockKey, $fname, $timeout ) {
3233  if ( !$this->lock( $lockKey, $fname, $timeout ) ) {
3234  return null;
3235  }
3236 
3237  $unlocker = new ScopedCallback( function () use ( $lockKey, $fname ) {
3238  $this->commit( __METHOD__, 'flush' );
3239  $this->unlock( $lockKey, $fname );
3240  } );
3241 
3242  $this->commit( __METHOD__, 'flush' );
3243 
3244  return $unlocker;
3245  }
3246 
3247  public function namedLocksEnqueue() {
3248  return false;
3249  }
3250 
3260  public function lockTables( $read, $write, $method, $lowPriority = true ) {
3261  return true;
3262  }
3263 
3270  public function unlockTables( $method ) {
3271  return true;
3272  }
3273 
3281  public function dropTable( $tableName, $fName = __METHOD__ ) {
3282  if ( !$this->tableExists( $tableName, $fName ) ) {
3283  return false;
3284  }
3285  $sql = "DROP TABLE " . $this->tableName( $tableName );
3286  if ( $this->cascadingDeletes() ) {
3287  $sql .= " CASCADE";
3288  }
3289 
3290  return $this->query( $sql, $fName );
3291  }
3292 
3299  public function getSearchEngine() {
3300  return 'SearchEngineDummy';
3301  }
3302 
3303  public function getInfinity() {
3304  return 'infinity';
3305  }
3306 
3307  public function encodeExpiry( $expiry ) {
3308  return ( $expiry == '' || $expiry == 'infinity' || $expiry == $this->getInfinity() )
3309  ? $this->getInfinity()
3310  : $this->timestamp( $expiry );
3311  }
3312 
3313  public function decodeExpiry( $expiry, $format = TS_MW ) {
3314  return ( $expiry == '' || $expiry == 'infinity' || $expiry == $this->getInfinity() )
3315  ? 'infinity'
3316  : wfTimestamp( $format, $expiry );
3317  }
3318 
3319  public function setBigSelects( $value = true ) {
3320  // no-op
3321  }
3322 
3323  public function isReadOnly() {
3324  return ( $this->getReadOnlyReason() !== false );
3325  }
3326 
3330  protected function getReadOnlyReason() {
3331  $reason = $this->getLBInfo( 'readOnlyReason' );
3332 
3333  return is_string( $reason ) ? $reason : false;
3334  }
3335 
3340  public function __toString() {
3341  return (string)$this->mConn;
3342  }
3343 
3347  public function __destruct() {
3348  if ( $this->mTrxLevel && $this->mTrxDoneWrites ) {
3349  trigger_error( "Uncommitted DB writes (transaction from {$this->mTrxFname})." );
3350  }
3351  $danglingCallbacks = array_merge(
3352  $this->mTrxIdleCallbacks,
3353  $this->mTrxPreCommitCallbacks,
3354  $this->mTrxEndCallbacks
3355  );
3356  if ( $danglingCallbacks ) {
3357  $callers = [];
3358  foreach ( $danglingCallbacks as $callbackInfo ) {
3359  $callers[] = $callbackInfo[1];
3360  }
3361  $callers = implode( ', ', $callers );
3362  trigger_error( "DB transaction callbacks still pending (from $callers)." );
3363  }
3364  }
3365 }
3366 
3370 abstract class Database extends DatabaseBase {
3371  // B/C until nothing type hints for DatabaseBase
3372  // @TODO: finish renaming DatabaseBase => Database
3373 }
doneWrites()
Returns true if the connection may have been used for write queries.
Definition: Database.php:382
select($table, $vars, $conds= '', $fname=__METHOD__, $options=[], $join_conds=[])
Execute a SELECT query constructed using the various parameters provided.
Definition: Database.php:1233
indexName($index)
Get the name of an index in a given table.
Definition: Database.php:1943
lowPriorityOption()
A string to insert into queries to show that they're low-priority, like MySQL's LOW_PRIORITY.
Definition: Database.php:2240
ping()
Ping the server and try to reconnect if it there is no connection.
Definition: Database.php:2893
doAtomicSection($fname, callable $callback)
Run a callback to do an atomic set of updates for this database.
Definition: Database.php:2616
setLBInfo($name, $value=null)
Set the LB info array, or a member of it.
Definition: Database.php:268
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
TransactionProfiler $trxProfiler
Definition: Database.php:181
const DEADLOCK_DELAY_MAX
Maximum time to wait before retry.
Definition: Database.php:40
maxListLen()
Return the maximum number of items allowed in a list, or 0 for unlimited.
Definition: Database.php:2971
bool $suppressPostCommitCallbacks
Whether to suppress triggering of post-commit callbacks.
Definition: Database.php:62
the array() calling protocol came about after MediaWiki 1.4rc1.
array $mNamedLocksHeld
Map of (name => 1) for locks obtained via lock()
Definition: Database.php:163
Utility classThis allows us to distinguish a blob from a normal string and an array of strings...
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the database
Definition: design.txt:12
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:189
float $mTrxSlaveLag
Lag estimate at the time of BEGIN.
Definition: Database.php:107
tableName($name, $format= 'quoted')
Format a table name ready for use in constructing an SQL query.
Definition: Database.php:1684
getScopedLockAndFlush($lockKey, $fname, $timeout)
Acquire a named lock, flush any transaction, and return an RAII style unlocker object.
Definition: Database.php:3232
bitOr($fieldLeft, $fieldRight)
Definition: Database.php:1632
$IP
Definition: WebStart.php:58
aggregateValue($valuedata, $valuename= 'value')
Return aggregated value alias.
Definition: Database.php:1620
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
patchPath($patch)
Get the full path of a patch file.
Definition: Database.php:3040
setSessionOptions(array $options)
Override database's default behavior.
Definition: Database.php:2986
array[] $mTrxEndCallbacks
List of (callable, method name)
Definition: Database.php:60
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException'returning false will NOT prevent logging $e
Definition: hooks.txt:1980
encodeExpiry($expiry)
Encode an expiry time into the DBMS dependent format.
Definition: Database.php:3307
const LIST_NAMES
Definition: Defines.php:196
__destruct()
Run a few simple sanity checks.
Definition: Database.php:3347
static instance()
Singleton.
Definition: Profiler.php:60
decodeExpiry($expiry, $format=TS_MW)
Decode an expiry time into a DBMS independent format.
Definition: Database.php:3313
estimateRowCount($table, $vars= '*', $conds= '', $fname=__METHOD__, $options=[])
Estimate the number of rows in dataset.
Definition: Database.php:1311
clearFlag($flag)
Clear a flag for this connection.
Definition: Database.php:416
makeOrderBy($options)
Returns an optional ORDER BY.
Definition: Database.php:1220
setTransactionProfiler(TransactionProfiler $profiler)
Definition: Database.php:310
open($server, $user, $password, $dbName)
Open a connection to the database.
resource $mConn
Database connection.
Definition: Database.php:52
listViews($prefix=null, $fname=__METHOD__)
Lists all the VIEWs in the database.
Definition: Database.php:2839
wfBacktrace($raw=null)
Get a debug backtrace as a string.
$wgDBmwschema
Mediawiki schema.
runOnTransactionIdleCallbacks($trigger)
Actually run and consume any "on transaction idle/resolution" callbacks.
Definition: Database.php:2513
wfLogDBError($text, array $context=[])
Log for database errors.
$value
const DBO_TRX
Definition: Defines.php:33
$wgSharedTables
getServerVersion()
A string describing the current software version, like from mysql_get_server_info().
Stub profiler that does nothing.
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2588
makeList($a, $mode=LIST_COMMA)
Makes an encoded list of strings from an array.
Definition: Database.php:1518
lastError()
Get a description of the last error.
doRollback($fname)
Issues the ROLLBACK command to the database server.
Definition: Database.php:2786
getProperty($name)
General read-only accessor.
Definition: Database.php:424
dropTable($tableName, $fName=__METHOD__)
Delete a table.
Definition: Database.php:3281
An object representing a master or slave position in a replicated setup.
functionalIndexes()
Returns true if this database can use functional indexes.
Definition: Database.php:374
const DBO_DEBUG
Definition: Defines.php:30
sourceFile($filename, $lineCallback=false, $resultCallback=false, $fname=false, $inputCallback=false)
Read and execute SQL commands from a file.
Definition: Database.php:3005
getDefaultSchemaVars()
Get schema variables to use if none have been set via setSchemaVars().
Definition: Database.php:3212
getTransactionProfiler()
Definition: Database.php:298
when a variable name is used in a it is silently declared as a new local masking the global
Definition: design.txt:93
lockIsFree($lockName, $method)
Check to see if a named lock is available (non-blocking)
Definition: Database.php:3216
doQuery($sql)
The DBMS-dependent part of query()
fieldInfo($table, $field)
mysql_fetch_field() wrapper Returns false if the field doesn't exist
cleanupTriggers()
Returns true if this database supports (and uses) triggers (e.g.
Definition: Database.php:328
getSchemaPath()
Return a path to the DBMS-specific schema file, otherwise default to tables.sql.
Definition: Database.php:459
wasReadOnlyError()
Determines if the last failure was due to the database being read-only.
Definition: Database.php:2369
encodeBlob($b)
Some DBMSs have a special format for inserting into blob fields, they don't allow simple quoted strin...
Definition: Database.php:2975
duplicateTableStructure($oldName, $newName, $temporary=false, $fname=__METHOD__)
Creates a new table with structure copied from existing table Note that unlike most database abstract...
Definition: Database.php:2808
pendingWriteCallers()
Get the list of method names that did write queries for this transaction.
Definition: Database.php:404
restoreErrorHandler()
Definition: Database.php:658
tableNamesWithUseIndexOrJOIN($tables, $use_index=[], $join_conds=[])
Get the aliased table name clause for a FROM clause which might have a JOIN and/or USE INDEX clause...
Definition: Database.php:1884
bool $mTrxAutomatic
Record if the current transaction was started implicitly due to DBO_TRX being set.
Definition: Database.php:132
listTables($prefix=null, $fname=__METHOD__)
List all tables on the database.
Definition: Database.php:2815
selectFieldValues($table, $var, $cond= '', $fname=__METHOD__, $options=[], $join_conds=[])
Definition: Database.php:1075
getType()
Get the type of the DBMS, as it appears in $wgDBtype.
lastErrno()
Get the last error number.
resource $fileHandle
Definition: Database.php:172
wfRandomString($length=32)
Get a random string containing a number of pseudo-random hex characters.
const DEADLOCK_TRIES
Number of times to re-try an operation in case of deadlock.
Definition: Database.php:34
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist & $tables
Definition: hooks.txt:981
close()
Closes a database connection.
Definition: Database.php:698
getSqlFilePath($filename)
Return a path to the DBMS-specific SQL file if it exists, otherwise default SQL file.
Definition: Database.php:443
selectSQLText($table, $vars, $conds= '', $fname=__METHOD__, $options=[], $join_conds=[])
The equivalent of IDatabase::select() except that the constructed SQL is returned, instead of being immediately executed.
Definition: Database.php:1240
indexExists($table, $index, $fname=__METHOD__)
Determines whether an index exists Usually throws a DBQueryError on failure If errors are explicitly ...
Definition: Database.php:1376
wfDebug($text, $dest= 'all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
tablePrefix($prefix=null)
Get/set the table prefix.
Definition: Database.php:239
runOnTransactionPreCommitCallbacks()
Actually run and consume any "on transaction pre-commit" callbacks.
Definition: Database.php:2564
useIndexClause($index)
USE INDEX clause.
Definition: Database.php:2049
update($table, $values, $conds, $fname=__METHOD__, $options=[])
UPDATE wrapper.
Definition: Database.php:1506
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message.Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item.Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page.Return false to stop further processing of the tag $reader:XMLReader object &$pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision.Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag.Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload.Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports.&$fullInterwikiPrefix:Interwiki prefix, may contain colons.&$pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable.Can be used to lazy-load the import sources list.&$importSources:The value of $wgImportSources.Modify as necessary.See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page.$context:IContextSource object &$pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect.&$title:Title object for the current page &$request:WebRequest &$ignoreRedirect:boolean to skip redirect check &$target:Title/string of redirect target &$article:Article object 'InternalParseBeforeLinks':during Parser's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings.&$parser:Parser object &$text:string containing partially parsed text &$stripState:Parser's internal StripState object 'InternalParseBeforeSanitize':during Parser's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings.Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments.&$parser:Parser object &$text:string containing partially parsed text &$stripState:Parser's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not.Return true without providing an interwiki to continue interwiki search.$prefix:interwiki prefix we are looking for.&$iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user's email has been invalidated successfully.$user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification.Callee may modify $url and $query, URL will be constructed as $url.$query &$url:URL to index.php &$query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) &$article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() &$ip:IP being check &$result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from &$allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn't match your organization.$addr:The e-mail address entered by the user &$result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user &$result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we're looking for a messages file for &$file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED!Use $magicWords in a file listed in $wgExtensionMessagesFiles instead.Use this to define synonyms of magic words depending of the language &$magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces.Do not use this hook to add namespaces.Use CanonicalNamespaces for that.&$namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED!Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead.Use to define aliases of special pages names depending of the language &$specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names.&$names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page's language links.This is called in various places to allow extensions to define the effective language links for a page.$title:The page's Title.&$links:Associative array mapping language codes to prefixed links of the form"language:title".&$linkFlags:Associative array mapping prefixed links to arrays of flags.Currently unused, but planned to provide support for marking individual language links in the UI, e.g.for featured articles. 'LanguageSelector':Hook to change the language selector available on a page.$out:The output page.$cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED!Use HtmlPageLinkRendererBegin instead.Used when generating internal and interwiki links in Linker::link(), before processing starts.Return false to skip default processing and return $ret.See documentation for Linker::link() for details on the expected meanings of parameters.$skin:the Skin object $target:the Title that the link is pointing to &$html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1814
if($line===false) $args
Definition: cdb.php:64
affectedRows()
Get the number of rows affected by the last write query.
strreplace($orig, $old, $new)
Returns a comand for str_replace function in SQL query.
Definition: Database.php:2349
trxTimestamp()
Get the UNIX timestamp of the time that the transaction was established.
Definition: Database.php:235
wfTimestamp($outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Class for asserting that a callback happens when an dummy object leaves scope.
onTransactionPreCommitOrIdle(callable $callback)
Run a callback before the current transaction commits or now if there is none.
Definition: Database.php:2477
timestamp($ts=0)
Convert a timestamp in one of the formats accepted by wfTimestamp() to the format used for inserting ...
Definition: Database.php:2855
wfDebugLog($logGroup, $text, $dest= 'all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not...
getSlavePos()
Get the replication position of this slave.
Definition: Database.php:2449
global $wgCommandLineMode
Definition: Setup.php:511
implicitGroupby()
Returns true if this database does an implicit sort when doing GROUP BY.
Definition: Database.php:351
string[] $mTrxWriteCallers
Track the write query callers of the current transaction.
Definition: Database.php:153
bitAnd($fieldLeft, $fieldRight)
Definition: Database.php:1628
ignoreErrors($ignoreErrors=null)
Turns on (false) or off (true) the automatic generation and sending of a "we're sorry, but there has been a database error" page on database errors.
Definition: Database.php:227
isQuotedIdentifier($name)
Returns if the given identifier looks quoted or not according to the database convention for quoting ...
Definition: Database.php:1995
anyString()
Returns a token for buildLike() that denotes a '' to be used in a LIKE query.
Definition: Database.php:2031
deadlockLoop()
Perform a deadlock-prone transaction.
Definition: Database.php:2408
string[] $allViews
Definition: Database.php:178
fillPreparedArg($matches)
preg_callback func for fillPrepared() The arguments should be in $this->preparedArgs and must not be t...
Definition: Database.php:1014
wfSetBit(&$dest, $bit, $state=true)
As for wfSetVar except setting a bit.
doBegin($fname)
Issues the BEGIN command to the database server.
Definition: Database.php:2692
unlock($lockName, $method)
Release a lock.
Definition: Database.php:3226
doCommit($fname)
Issues the COMMIT command to the database server.
Definition: Database.php:2746
buildLike()
LIKE statement wrapper, receives a variable-length argument list with parts of pattern to match conta...
Definition: Database.php:2007
strictIPs()
Returns true if this database is strict about what can be put into an IP field.
Definition: Database.php:338
tableExists($table, $fname=__METHOD__)
Query whether a given table exists.
Definition: Database.php:1389
unionSupportsOrderAndLimit()
Returns true if current database backend supports ORDER BY or LIMIT for separate subqueries within th...
Definition: Database.php:2331
bufferResults($buffer=null)
Turns buffering of SQL result sets on (true) or off (false).
Definition: Database.php:207
const LIST_AND
Definition: Defines.php:194
makeUpdateOptionsArray($options)
Make UPDATE options array for DatabaseBase::makeUpdateOptions.
Definition: Database.php:1476
getSchemaVars()
Get schema variables.
Definition: Database.php:3196
trxLevel()
Gets the current transaction level.
Definition: Database.php:231
array[] $mTrxPreCommitCallbacks
List of (callable, method name)
Definition: Database.php:58
wfWarn($msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
strencode($s)
Wrapper for addslashes()
Helper class that detects high-contention DB queries via profiling calls.
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition: hooks.txt:1020
getUpdateKeysPath()
Return a path to the DBMS-specific update key file, otherwise default to update-keys.sql.
Definition: Database.php:469
getDBname()
Get the current DB name.
Definition: Database.php:1657
fetchObject($res)
Fetch the next row from the given result object, in object form.
const LIST_COMMA
Definition: Defines.php:193
freePrepared($prepared)
Free a prepared query, generated by prepare().
Definition: Database.php:967
unlockTables($method)
Unlock specific tables.
Definition: Database.php:3270
$res
Definition: database.txt:21
endAtomic($fname=__METHOD__)
Ends an atomic section of SQL statements.
Definition: Database.php:2601
namedLocksEnqueue()
Check to see if a named lock used by lock() use blocking queues.
Definition: Database.php:3247
MediaWiki exception.
Definition: MWException.php:26
implicitOrderby()
Returns true if this database does an implicit order by when the column has an index For example: SEL...
Definition: Database.php:355
assertOpen()
Make sure isOpen() returns true as a sanity check.
Definition: Database.php:726
const DBO_NOBUFFER
Definition: Defines.php:31
bool $mTrxAutomaticAtomic
Record if the current transaction was started implicitly by DatabaseBase::startAtomic.
Definition: Database.php:146
setLazyMasterHandle(IDatabase $conn)
Set a lazy-connecting DB handle to the master DB (for replication status purposes) ...
Definition: Database.php:282
getFlag($flag)
Returns a boolean whether the flag $flag is set for this connection.
Definition: Database.php:420
$params
isTransactableQuery($sql)
Determine whether a SQL statement is sensitive to isolation level.
Definition: Database.php:778
insert($table, $a, $fname=__METHOD__, $options=[])
INSERT wrapper, inserts an array into a table.
Definition: Database.php:1418
static queryTime($id)
Calculates how long a query took.
Definition: MWDebug.php:400
$wgSharedDB
Shared database for multiple wikis.
static factory($dbType, $p=[])
Given a DB type, construct the name of the appropriate child class of DatabaseBase.
Definition: Database.php:584
conditional($cond, $trueVal, $falseVal)
Returns an SQL expression for a simple conditional.
Definition: Database.php:2341
$buffer
debug($debug=null)
Boolean, controls output of large amounts of debug information.
Definition: Database.php:203
IDatabase null $lazyMasterHandle
Lazy handle to the master DB this server replicates from.
Definition: Database.php:166
getSearchEngine()
Get search engine class.
Definition: Database.php:3299
addQuotes($s)
Adds quotes and backslashes.
Definition: Database.php:1958
addIdentifierQuotes($s)
Quotes an identifier using backticks or "double quotes" depending on the database type...
Definition: Database.php:1982
bitNot($field)
Definition: Database.php:1624
Used by DatabaseBase::buildLike() to represent characters that have special meaning in SQL LIKE claus...
searchableIPs()
Returns true if this database can do a native search on IP columns e.g.
Definition: Database.php:365
freeResult($res)
Free a result object returned by query() or select().
Definition: Database.php:1045
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1816
static getCacheSetOptions(IDatabase $db1)
Merge the result of getSessionLagStatus() for several DBs using the most pessimistic values to estima...
Definition: Database.php:2950
lockTables($read, $write, $method, $lowPriority=true)
Lock specific tables.
Definition: Database.php:3260
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
getApproximateLagStatus()
Get a slave lag estimate for this server.
Definition: Database.php:2925
replaceVars($ins)
Database independent variable replacement.
Definition: Database.php:3158
const LIST_SET
Definition: Defines.php:195
fetchRow($res)
Fetch the next row from the given result object, in associative array form.
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
Definition: hooks.txt:242
selectDB($db)
Change the current database.
Definition: Database.php:1648
selectRow($table, $vars, $conds, $fname=__METHOD__, $options=[], $join_conds=[])
Single row SELECT wrapper.
Definition: Database.php:1291
int $mTrxLevel
Either 1 if a transaction is active or 0 otherwise.
Definition: Database.php:86
const DBO_IGNORE
Definition: Defines.php:32
pendingWriteQueryDuration()
Get the time spend running write queries for this transaction.
Definition: Database.php:400
string $mTrxShortId
Either a short hexidecimal string if a transaction is active or "".
Definition: Database.php:94
const LIST_OR
Definition: Defines.php:197
numRows($res)
Get the number of rows in a result object.
setFlag($flag)
Set a flag for this connection.
Definition: Database.php:412
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
wasErrorReissuable()
Determines if the last query error was something that should be dealt with by pinging the connection ...
Definition: Database.php:2365
buildGroupConcatField($delim, $table, $field, $conds= '', $join_conds=[])
Build a GROUP_CONCAT or equivalent statement for a query.
Definition: Database.php:1640
Database abstraction object.
Definition: Database.php:32
indexUnique($table, $index)
Determines if a given index is unique.
Definition: Database.php:1398
wasConnectionError($errno)
Determines if the given query error was a connection drop STUB.
Definition: Database.php:2380
$from
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
wfSetVar(&$dest, $source, $force=false)
Sets dest to source and returns the original value of dest If source is NULL, it just returns the val...
closeConnection()
Closes underlying database connection.
selectRowCount($tables, $vars= '*', $conds= '', $fname=__METHOD__, $options=[], $join_conds=[])
Get the number of rows in dataset.
Definition: Database.php:1325
onTransactionResolution(callable $callback)
Run a callback as soon as the current transaction commits or rolls back.
Definition: Database.php:2463
cascadingDeletes()
Returns true if this database supports (and uses) cascading deletes.
Definition: Database.php:319
selectField($table, $var, $cond= '', $fname=__METHOD__, $options=[])
A SELECT wrapper which returns a single field from a single result row.
Definition: Database.php:1048
unionQueries($sqls, $all)
Construct a UNION query This is used for providing overload point for other DB abstractions not compa...
Definition: Database.php:2335
indexInfo($table, $index, $fname=__METHOD__)
Get information about an index into an object.
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined...
Definition: Setup.php:36
setBigSelects($value=true)
Allow or deny "big selects" for this session only.
Definition: Database.php:3319
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global then executing the whole list after the page is displayed We don t do anything smart like collating updates to the same table or such because the list is almost always going to have just one item on if so it s not worth the trouble Since there is a job queue in the jobs table
Definition: deferred.txt:11
clearViewsCache()
Reset the views process cache set by listViews()
Definition: Database.php:2823
float null $mTrxTimestamp
The UNIX time that the transaction started.
Definition: Database.php:104
wasDeadlock()
Determines if the last failure was due to a deadlock STUB.
Definition: Database.php:2357
__sleep()
Called by serialize.
Definition: Database.php:557
upsert($table, array $rows, array $uniqueIndexes, array $set, $fname=__METHOD__)
INSERT ON DUPLICATE KEY UPDATE wrapper, upserts an array into a table.
Definition: Database.php:2135
textFieldSize($table, $field)
Returns the size of a text field, or -1 for "unlimited".
Definition: Database.php:2215
fillPrepared($preparedQuery, $args)
For faking prepared SQL statements on DBs that don't support it directly.
Definition: Database.php:997
onTransactionIdle(callable $callback)
Run a callback as soon as there is no transaction pending.
Definition: Database.php:2470
getServer()
Get the server hostname or IP address.
Definition: Database.php:1661
static getLocalServerInstance($fallback=CACHE_NONE)
Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
array[] $mTrxIdleCallbacks
List of (callable, method name)
Definition: Database.php:56
begin($fname=__METHOD__)
Begin a transaction.
Definition: Database.php:2627
getServerInfo()
A string describing the current software version, and possibly other details in a user-friendly way...
Definition: Database.php:183
setPostCommitCallbackSupression($suppress)
Whether to disable running of post-commit callbacks.
Definition: Database.php:2501
anyChar()
Returns a token for buildLike() that denotes a '_' to be used in a LIKE query.
Definition: Database.php:2027
$line
Definition: cdb.php:59
timestampOrNull($ts=null)
Convert a timestamp in one of the formats accepted by wfTimestamp() to the format used for inserting ...
Definition: Database.php:2859
reportQueryError($error, $errno, $sql, $fname, $tempIgnore=false)
Report a query error.
Definition: Database.php:921
makeInsertOptions($options)
Helper for DatabaseBase::insert().
Definition: Database.php:1414
nextSequenceValue($seqName)
Returns an appropriately quoted sequence value for inserting a new row.
Definition: Database.php:2035
array $mSessionVars
Definition: Database.php:72
$wgDBprefix
Table name prefix.
getTransactionLagStatus()
Get the slave lag when the current transaction started.
Definition: Database.php:2913
query($sql, $fname=__METHOD__, $tempIgnore=false)
Run an SQL query and return the result.
Definition: Database.php:783
static generalizeSQL($sql)
Removes most variables from an SQL query and replaces them with X or N for numbers.
Definition: Database.php:1348
static query($sql, $function, $isMaster)
Begins profiling on a database query.
Definition: MWDebug.php:353
lastDoneWrites()
Returns the last time the connection may have been used for write queries.
Definition: Database.php:386
isWriteQuery($sql)
Determine whether a query writes to the DB.
Definition: Database.php:765
lock($lockName, $method, $timeout=5)
Acquire a named lock.
Definition: Database.php:3220
makeUpdateOptions($options)
Make UPDATE options for the DatabaseBase::update function.
Definition: Database.php:1500
const DBO_DEFAULT
Definition: Defines.php:34
getServerUptime()
Determines how long the server has been up STUB.
Definition: Database.php:2353
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive $limit
Definition: hooks.txt:1020
commit($fname=__METHOD__, $flush= '')
Commits a transaction previously started using begin().
Definition: Database.php:2697
masterPosWait(DBMasterPos $pos, $timeout)
Wait for the slave to catch up to a given master position.
Definition: Database.php:2444
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
Definition: hooks.txt:1020
getInfinity()
Find out when 'infinity' is.
Definition: Database.php:3303
startAtomic($fname=__METHOD__)
Begin an atomic section of statements.
Definition: Database.php:2587
array $mTrxAtomicLevels
Array of levels of atomicity within transactions.
Definition: Database.php:139
static consume(ScopedCallback &$sc=null)
Trigger a scoped callback and destroy it.
bool $mTrxDoneWrites
Record if possible write queries were done in the last transaction started.
Definition: Database.php:124
connectionErrorHandler($errno, $errstr)
Definition: Database.php:677
getMasterPos()
Get the position of this master.
Definition: Database.php:2454
streamStatementEnd(&$sql, &$newLine)
Called by sourceStream() to check if we've reached a statement end.
Definition: Database.php:3126
float $mTrxWriteDuration
Track the seconds spent in write queries for the current transaction.
Definition: Database.php:160
rollback($fname=__METHOD__, $flush= '')
Rollback a transaction previously started using begin().
Definition: Database.php:2753
execute($prepared, $args=null)
Execute a prepared query with the various arguments.
Definition: Database.php:978
makeWhereFrom2d($data, $baseKey, $subKey)
Build a partial where clause from a 2-d array such as used for LinkBatch.
Definition: Database.php:1593
makeGroupByWithHaving($options)
Returns an optional GROUP BY with an optional HAVING.
Definition: Database.php:1194
getLBInfo($name=null)
Get properties passed down from the server info array of the load balancer.
Definition: Database.php:256
Result wrapper for grabbing data queried by someone else.
isView($name)
Differentiates between a TABLE and a VIEW.
Definition: Database.php:2851
deleteJoin($delTable, $joinTable, $delVar, $joinVar, $conds, $fname=__METHOD__)
DELETE where the condition is a join.
Definition: Database.php:2189
getLogContext(array $extras=[])
Create a log context to pass to wfLogDBError or other logging functions.
Definition: Database.php:687
BagOStuff $srvCache
APC cache.
Definition: Database.php:49
escapeLikeInternal($s)
Definition: Database.php:2003
$wgSharedPrefix
writesOrCallbacksPending()
Returns true if there is a transaction open with possible write queries or transaction pre-commit/idl...
Definition: Database.php:394
prepare($sql, $func= 'DatabaseBase::prepare')
Intended to be compatible with the PEAR::DB wrapper functions.
Definition: Database.php:955
setSchemaVars($vars)
Set variables to be used in sourceFile/sourceStream, in preference to the ones in $GLOBALS...
Definition: Database.php:3051
static logException($e)
Log an exception to the exception log (if enabled).
lastQuery()
Return the last query that went through IDatabase::query()
Definition: Database.php:378
nativeReplace($table, $rows, $fname)
REPLACE query wrapper for MySQL and SQLite, which have a native REPLACE statement.
Definition: Database.php:2111
__construct(array $params)
Constructor.
Definition: Database.php:502
$debug
Definition: mcc.php:31
resultObject($result)
Take the result from a query, and wrap it in a ResultWrapper if necessary.
Definition: Database.php:2880
replace($table, $uniqueIndexes, $rows, $fname=__METHOD__)
REPLACE query wrapper.
Definition: Database.php:2053
getLazyMasterHandle()
Definition: Database.php:291
$wgSharedSchema
realTimestamps()
Returns true if this database uses timestamps rather than integers.
Definition: Database.php:347
isOpen()
Is a connection to the database open?
Definition: Database.php:408
decodeBlob($b)
Some DBMSs return a special placeholder object representing blob fields in result objects...
Definition: Database.php:2979
getSessionLagStatus()
Get the slave lag when the current transaction started or a general lag estimate if not transaction i...
Definition: Database.php:2898
makeSelectOptions($options)
Returns an optional USE INDEX clause to go after the table, and a string to go at the end of the quer...
Definition: Database.php:1110
insertSelect($destTable, $srcTable, $varMap, $conds, $fname=__METHOD__, $insertOptions=[], $selectOptions=[])
INSERT SELECT wrapper.
Definition: Database.php:2262
installErrorHandler()
Definition: Database.php:649
reportConnectionError($error= 'Unknown error')
Definition: Database.php:739
static configuration should be added through ResourceLoaderGetConfigVars instead & $vars
Definition: hooks.txt:2044
buildConcat($stringList)
Build a concatenation list to feed into a SQL query.
Definition: Database.php:1636
sourceStream($fp, $lineCallback=false, $resultCallback=false, $fname=__METHOD__, $inputCallback=false)
Read and execute commands from an open file handle.
Definition: Database.php:3068
wfGetCaller($level=2)
Get the name of the function which called this function wfGetCaller( 1 ) is the function with the wfG...
limitResult($sql, $limit, $offset=false)
Construct a LIMIT query with optional offset.
Definition: Database.php:2321
fieldNamesWithAlias($fields)
Gets an array of aliased field names.
Definition: Database.php:1863
Basic database interface for live and lazy-loaded DB handles.
Definition: IDatabase.php:35
setFileHandle($fh)
Set the filehandle to copy write statements to.
Definition: Database.php:252
const DEADLOCK_DELAY_MIN
Minimum time to wait before retry, in microseconds.
Definition: Database.php:37
string $mTrxFname
Remembers the function name given for starting the most recent transaction via begin().
Definition: Database.php:116
fieldExists($table, $field, $fname=__METHOD__)
Determines whether a field exists in a table.
Definition: Database.php:1370
dbSchema($schema=null)
Get/set the db schema.
Definition: Database.php:243
$wgUser
Definition: Setup.php:801
$matches
getLag()
Get slave lag.
Definition: Database.php:2967
wasLockTimeout()
Determines if the last failure was due to a lock timeout STUB.
Definition: Database.php:2361
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:310