26use Psr\Log\LoggerAwareInterface;
27use Psr\Log\LoggerInterface;
28use Wikimedia\ScopedCallback;
237 $password =
$params[
'password'];
240 $this->mSchema =
$params[
'schema'];
241 $this->mTablePrefix =
$params[
'tablePrefix'];
243 $this->cliMode =
$params[
'cliMode'];
245 $this->agent = str_replace(
'/',
'-',
$params[
'agent'] );
247 $this->mFlags =
$params[
'flags'];
248 if ( $this->mFlags & self::DBO_DEFAULT ) {
249 if ( $this->cliMode ) {
256 $this->mSessionVars =
$params[
'variables'];
258 $this->srvCache = isset(
$params[
'srvCache'] )
262 $this->profiler =
$params[
'profiler'];
263 $this->trxProfiler =
$params[
'trxProfiler'];
264 $this->connLogger =
$params[
'connLogger'];
265 $this->queryLogger =
$params[
'queryLogger'];
266 $this->errorLogger =
$params[
'errorLogger'];
272 $this->
open( $server,
$user, $password, $dbName );
274 throw new InvalidArgumentException(
"No database user provided." );
278 if ( $this->mDBname !=
'' ) {
280 $this->currentDomain =
new DatabaseDomain( $this->mDBname,
null, $this->mTablePrefix );
325 final public static function factory( $dbType, $p = [] ) {
326 static $canonicalDBTypes = [
327 'mysql' => [
'mysqli',
'mysql' ],
335 $dbType = strtolower( $dbType );
336 if ( isset( $canonicalDBTypes[$dbType] ) && $canonicalDBTypes[$dbType] ) {
337 $possibleDrivers = $canonicalDBTypes[$dbType];
338 if ( !empty( $p[
'driver'] ) ) {
339 if ( in_array( $p[
'driver'], $possibleDrivers ) ) {
340 $driver = $p[
'driver'];
342 throw new InvalidArgumentException( __METHOD__ .
343 " type '$dbType' does not support driver '{$p['driver']}'" );
346 foreach ( $possibleDrivers
as $posDriver ) {
347 if ( extension_loaded( $posDriver ) ) {
348 $driver = $posDriver;
356 if ( $driver ===
false || $driver ===
'' ) {
357 throw new InvalidArgumentException( __METHOD__ .
358 " no viable database extension found for type '$dbType'" );
361 $class =
'Database' . ucfirst( $driver );
362 if ( class_exists( $class ) && is_subclass_of( $class,
'IDatabase' ) ) {
364 $p[
'host'] = isset( $p[
'host'] ) ? $p[
'host'] :
false;
365 $p[
'user'] = isset( $p[
'user'] ) ? $p[
'user'] :
false;
366 $p[
'password'] = isset( $p[
'password'] ) ? $p[
'password'] :
false;
367 $p[
'dbname'] = isset( $p[
'dbname'] ) ? $p[
'dbname'] :
false;
368 $p[
'flags'] = isset( $p[
'flags'] ) ? $p[
'flags'] : 0;
369 $p[
'variables'] = isset( $p[
'variables'] ) ? $p[
'variables'] : [];
370 $p[
'tablePrefix'] = isset( $p[
'tablePrefix'] ) ? $p[
'tablePrefix'] :
'';
371 $p[
'schema'] = isset( $p[
'schema'] ) ? $p[
'schema'] :
'';
372 $p[
'cliMode'] = isset( $p[
'cliMode'] ) ? $p[
'cliMode'] : ( PHP_SAPI ===
'cli' );
373 $p[
'agent'] = isset( $p[
'agent'] ) ? $p[
'agent'] :
'';
374 if ( !isset( $p[
'connLogger'] ) ) {
375 $p[
'connLogger'] = new \Psr\Log\NullLogger();
377 if ( !isset( $p[
'queryLogger'] ) ) {
378 $p[
'queryLogger'] = new \Psr\Log\NullLogger();
380 $p[
'profiler'] = isset( $p[
'profiler'] ) ? $p[
'profiler'] :
null;
381 if ( !isset( $p[
'trxProfiler'] ) ) {
384 if ( !isset( $p[
'errorLogger'] ) ) {
385 $p[
'errorLogger'] =
function ( Exception
$e ) {
386 trigger_error( get_class(
$e ) .
': ' .
$e->getMessage(), E_USER_WARNING );
390 $conn =
new $class( $p );
399 $this->queryLogger = $logger;
411 : $this->
setFlag( self::DBO_NOBUFFER );
431 if ( $ignoreErrors !==
null ) {
433 ? $this->
setFlag( self::DBO_IGNORE )
441 return $this->mTrxLevel;
445 return $this->mTrxLevel ? $this->mTrxTimestamp :
null;
449 $old = $this->mTablePrefix;
450 if ( $prefix !==
null ) {
451 $this->mTablePrefix = $prefix;
452 $this->currentDomain = ( $this->mDBname !=
'' )
461 $old = $this->mSchema;
462 if ( $schema !==
null ) {
463 $this->mSchema = $schema;
470 if ( is_null(
$name ) ) {
471 return $this->mLBInfo;
473 if ( array_key_exists(
$name, $this->mLBInfo ) ) {
474 return $this->mLBInfo[
$name];
482 if ( is_null(
$value ) ) {
483 $this->mLBInfo =
$name;
490 $this->lazyMasterHandle = $conn;
499 return $this->lazyMasterHandle;
511 return $this->mLastQuery;
515 return (
bool)$this->mLastWriteTime;
519 return $this->mLastWriteTime ?:
false;
523 return $this->mTrxLevel && $this->mTrxDoneWrites;
527 return $this->mTrxLevel && (
528 $this->mTrxDoneWrites || $this->mTrxIdleCallbacks || $this->mTrxPreCommitCallbacks
533 if ( !$this->mTrxLevel ) {
535 } elseif ( !$this->mTrxDoneWrites ) {
540 case self::ESTIMATE_DB_APPLY:
542 $rttAdjTotal = $this->mTrxWriteAdjQueryCount * $rtt;
543 $applyTime = max( $this->mTrxWriteAdjDuration - $rttAdjTotal, 0 );
545 $omitted = $this->mTrxWriteQueryCount - $this->mTrxWriteAdjQueryCount;
546 $applyTime += self::TINY_WRITE_SEC * $omitted;
550 return $this->mTrxWriteDuration;
555 return $this->mTrxLevel ? $this->mTrxWriteCallers : [];
559 if ( !$this->mTrxLevel ) {
563 $fnames = $this->mTrxWriteCallers;
565 $this->mTrxIdleCallbacks,
566 $this->mTrxPreCommitCallbacks,
567 $this->mTrxEndCallbacks
569 foreach ( $callbacks
as $callback ) {
570 $fnames[] = $callback[1];
578 return $this->mOpened;
581 public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
582 if ( $remember === self::REMEMBER_PRIOR ) {
583 array_push( $this->priorFlags, $this->mFlags );
585 $this->mFlags |= $flag;
588 public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
589 if ( $remember === self::REMEMBER_PRIOR ) {
590 array_push( $this->priorFlags, $this->mFlags );
592 $this->mFlags &= ~$flag;
596 if ( !$this->priorFlags ) {
600 if ( $state === self::RESTORE_INITIAL ) {
601 $this->mFlags = reset( $this->priorFlags );
602 $this->priorFlags = [];
604 $this->mFlags = array_pop( $this->priorFlags );
609 return !!( $this->mFlags & $flag );
622 return $this->currentDomain->getId();
647 $this->mPHPError =
false;
648 $this->htmlErrors = ini_set(
'html_errors',
'0' );
649 set_error_handler( [ $this,
'connectionErrorLogger' ] );
656 restore_error_handler();
657 if ( $this->htmlErrors !==
false ) {
658 ini_set(
'html_errors', $this->htmlErrors );
668 if ( $this->mPHPError ) {
669 $error = preg_replace(
'!\[<a.*</a>\]!',
'', $this->mPHPError );
670 $error = preg_replace(
'!^.*?:\s?(.*)$!',
'$1', $error );
685 $this->mPHPError = $errstr;
697 'db_server' => $this->mServer,
698 'db_name' => $this->mDBname,
699 'db_user' => $this->mUser,
706 if ( $this->mConn ) {
708 $this->
commit( __METHOD__, self::FLUSHING_INTERNAL );
712 $this->mConn =
false;
713 } elseif ( $this->mTrxIdleCallbacks || $this->mTrxEndCallbacks ) {
714 throw new RuntimeException(
"Transaction callbacks still pending." );
718 $this->mOpened =
false;
769 '/^(?:SELECT|BEGIN|ROLLBACK|COMMIT|SET|SHOW|EXPLAIN|\(SELECT)\b/i', $sql );
777 return preg_match(
'/^\s*([a-z]+)/i', $sql, $m ) ? strtoupper( $m[1] ) :
null;
792 [
'BEGIN',
'COMMIT',
'ROLLBACK',
'SHOW',
'SET',
'CREATE',
'ALTER' ],
803 '/^CREATE\s+TEMPORARY\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"\']?(\w+)[`"\']?/i',
807 $this->mSessionTempTables[
$matches[1]] = 1;
810 } elseif ( preg_match(
811 '/^DROP\s+(?:TEMPORARY\s+)?TABLE\s+(?:IF\s+EXISTS\s+)?[`"\']?(\w+)[`"\']?/i',
815 unset( $this->mSessionTempTables[
$matches[1]] );
818 } elseif ( preg_match(
819 '/^(?:INSERT\s+(?:\w+\s+)?INTO|UPDATE|DELETE\s+FROM)\s+[`"\']?(\w+)[`"\']?/i',
823 return isset( $this->mSessionTempTables[
$matches[1]] );
829 public function query( $sql,
$fname = __METHOD__, $tempIgnore =
false ) {
831 $this->mLastQuery = $sql;
836 if ( $reason !==
false ) {
839 # Set a flag indicating that writes have been done
840 $this->mLastWriteTime = microtime(
true );
845 $commentedSql = preg_replace(
'/\s|$/',
" /* $fname {$this->agent} */ ", $sql, 1 );
847 # Start implicit transactions that wrap the request if DBO_TRX is enabled
848 if ( !$this->mTrxLevel && $this->
getFlag( self::DBO_TRX )
851 $this->
begin( __METHOD__ .
" ($fname)", self::TRANSACTION_INTERNAL );
852 $this->mTrxAutomatic =
true;
855 # Keep track of whether the transaction has write queries pending
856 if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $isWrite ) {
857 $this->mTrxDoneWrites =
true;
858 $this->trxProfiler->transactionWritingIn(
859 $this->mServer, $this->mDBname, $this->mTrxShortId );
862 if ( $this->
getFlag( self::DBO_DEBUG ) ) {
863 $this->queryLogger->debug(
"{$this->mDBname} {$commentedSql}" );
866 # Avoid fatals if close() was called
869 # Send the query to the server
872 # Try reconnecting if the connection was lost
875 # Stash the last error values before anything might clear them
878 # Update state tracking to reflect transaction loss due to disconnection
881 $msg = __METHOD__ .
": lost connection to {$this->getServer()}; reconnected";
882 $this->connLogger->warning( $msg );
883 $this->queryLogger->warning(
884 "$msg:\n" . (
new RuntimeException() )->getTraceAsString() );
886 if ( !$recoverable ) {
887 # Callers may catch the exception and continue to use the DB
890 # Should be safe to silently retry the query
894 $msg = __METHOD__ .
": lost connection to {$this->getServer()} permanently";
895 $this->connLogger->error( $msg );
899 if (
false ===
$ret ) {
900 # Deadlocks cause the entire transaction to abort, not just the statement.
907 # Update state tracking to reflect transaction loss
921 $isMaster = !is_null( $this->
getLBInfo(
'master' ) );
922 # generalizeSQL() will probably cut down the query to reasonable
923 # logging size most of the time. The substr is really just a sanity check.
925 $queryProf =
'query-m: ' . substr( self::generalizeSQL( $sql ), 0, 255 );
927 $queryProf =
'query: ' . substr( self::generalizeSQL( $sql ), 0, 255 );
930 # Include query transaction state
931 $queryProf .= $this->mTrxShortId ?
" [TRX#{$this->mTrxShortId}]" :
"";
933 $startTime = microtime(
true );
934 if ( $this->profiler ) {
935 call_user_func( [ $this->profiler,
'profileIn' ], $queryProf );
938 if ( $this->profiler ) {
939 call_user_func( [ $this->profiler,
'profileOut' ], $queryProf );
941 $queryRuntime = max( microtime(
true ) - $startTime, 0.0 );
943 unset( $queryProfSection );
945 if (
$ret !==
false ) {
946 $this->lastPing = $startTime;
947 if ( $isWrite && $this->mTrxLevel ) {
949 $this->mTrxWriteCallers[] =
$fname;
953 if ( $sql === self::PING_QUERY ) {
954 $this->mRTTEstimate = $queryRuntime;
957 $this->trxProfiler->recordQueryCompletion(
960 $this->queryLogger->debug( $sql, [
962 'master' => $isMaster,
963 'runtime' => $queryRuntime,
982 $indicativeOfReplicaRuntime =
true;
983 if ( $runtime > self::SLOW_WRITE_SEC ) {
986 if ( $verb ===
'INSERT' ) {
987 $indicativeOfReplicaRuntime = $this->
affectedRows() > self::SMALL_WRITE_ROWS;
988 } elseif ( $verb ===
'REPLACE' ) {
989 $indicativeOfReplicaRuntime = $this->
affectedRows() > self::SMALL_WRITE_ROWS / 2;
993 $this->mTrxWriteDuration += $runtime;
994 $this->mTrxWriteQueryCount += 1;
995 if ( $indicativeOfReplicaRuntime ) {
996 $this->mTrxWriteAdjDuration += $runtime;
997 $this->mTrxWriteAdjQueryCount += 1;
1002 # Transaction dropped; this can mean lost writes, or REPEATABLE-READ snapshots.
1003 # Dropped connections also mean that named locks are automatically released.
1004 # Only allow error suppression in autocommit mode or when the lost transaction
1005 # didn't matter anyway (aside from DBO_TRX snapshot loss).
1006 if ( $this->mNamedLocksHeld ) {
1008 } elseif ( $sql ===
'COMMIT' ) {
1009 return !$priorWritesPending;
1010 } elseif ( $sql ===
'ROLLBACK' ) {
1014 } elseif ( $priorWritesPending ) {
1022 $this->mTrxLevel = 0;
1023 $this->mTrxIdleCallbacks = [];
1024 $this->mTrxPreCommitCallbacks = [];
1025 $this->mSessionTempTables = [];
1026 $this->mNamedLocksHeld = [];
1032 }
catch ( Exception
$e ) {
1040 $this->queryLogger->debug(
"SQL ERROR (ignored): $error\n" );
1042 $sql1line = mb_substr( str_replace(
"\n",
"\\n", $sql ), 0, 5 * 1024 );
1043 $this->queryLogger->error(
1044 "{fname}\t{db_server}\t{errno}\t{error}\t{sql1line}",
1046 'method' => __METHOD__,
1049 'sql1line' => $sql1line,
1053 $this->queryLogger->debug(
"SQL ERROR: " . $error .
"\n" );
1064 if ( $var ===
'*' ) {
1081 if ( $row !==
false ) {
1082 return reset( $row );
1089 $table, $var, $cond =
'',
$fname = __METHOD__,
$options = [], $join_conds = []
1091 if ( $var ===
'*' ) {
1093 } elseif ( !is_string( $var ) ) {
1102 if (
$res ===
false ) {
1107 foreach (
$res as $row ) {
1108 $values[] = $row->$var;
1124 $preLimitTail = $postLimitTail =
'';
1130 if ( is_numeric( $key ) ) {
1131 $noKeyOptions[$option] =
true;
1145 if ( isset( $noKeyOptions[
'FOR UPDATE'] ) ) {
1146 $postLimitTail .=
' FOR UPDATE';
1149 if ( isset( $noKeyOptions[
'LOCK IN SHARE MODE'] ) ) {
1150 $postLimitTail .=
' LOCK IN SHARE MODE';
1153 if ( isset( $noKeyOptions[
'DISTINCT'] ) || isset( $noKeyOptions[
'DISTINCTROW'] ) ) {
1154 $startOpts .=
'DISTINCT';
1157 # Various MySQL extensions
1158 if ( isset( $noKeyOptions[
'STRAIGHT_JOIN'] ) ) {
1159 $startOpts .=
' /*! STRAIGHT_JOIN */';
1162 if ( isset( $noKeyOptions[
'HIGH_PRIORITY'] ) ) {
1163 $startOpts .=
' HIGH_PRIORITY';
1166 if ( isset( $noKeyOptions[
'SQL_BIG_RESULT'] ) ) {
1167 $startOpts .=
' SQL_BIG_RESULT';
1170 if ( isset( $noKeyOptions[
'SQL_BUFFER_RESULT'] ) ) {
1171 $startOpts .=
' SQL_BUFFER_RESULT';
1174 if ( isset( $noKeyOptions[
'SQL_SMALL_RESULT'] ) ) {
1175 $startOpts .=
' SQL_SMALL_RESULT';
1178 if ( isset( $noKeyOptions[
'SQL_CALC_FOUND_ROWS'] ) ) {
1179 $startOpts .=
' SQL_CALC_FOUND_ROWS';
1182 if ( isset( $noKeyOptions[
'SQL_CACHE'] ) ) {
1183 $startOpts .=
' SQL_CACHE';
1186 if ( isset( $noKeyOptions[
'SQL_NO_CACHE'] ) ) {
1187 $startOpts .=
' SQL_NO_CACHE';
1190 if ( isset(
$options[
'USE INDEX'] ) && is_string(
$options[
'USE INDEX'] ) ) {
1195 if ( isset(
$options[
'IGNORE INDEX'] ) && is_string(
$options[
'IGNORE INDEX'] ) ) {
1201 return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ];
1214 if ( isset(
$options[
'GROUP BY'] ) ) {
1215 $gb = is_array(
$options[
'GROUP BY'] )
1216 ? implode(
',',
$options[
'GROUP BY'] )
1218 $sql .=
' GROUP BY ' . $gb;
1220 if ( isset(
$options[
'HAVING'] ) ) {
1221 $having = is_array(
$options[
'HAVING'] )
1224 $sql .=
' HAVING ' . $having;
1239 if ( isset(
$options[
'ORDER BY'] ) ) {
1240 $ob = is_array(
$options[
'ORDER BY'] )
1241 ? implode(
',',
$options[
'ORDER BY'] )
1244 return ' ORDER BY ' . $ob;
1251 $options = [], $join_conds = [] ) {
1260 if ( is_array(
$vars ) ) {
1265 $useIndexes = ( isset(
$options[
'USE INDEX'] ) && is_array(
$options[
'USE INDEX'] ) )
1269 isset(
$options[
'IGNORE INDEX'] ) &&
1270 is_array(
$options[
'IGNORE INDEX'] )
1275 if ( is_array( $table ) ) {
1278 $table, $useIndexes, $ignoreIndexes, $join_conds );
1279 } elseif ( $table !=
'' ) {
1280 if ( $table[0] ==
' ' ) {
1281 $from =
' FROM ' . $table;
1285 [ $table ], $useIndexes, $ignoreIndexes, [] );
1291 list( $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ) =
1294 if ( !empty( $conds ) ) {
1295 if ( is_array( $conds ) ) {
1296 $conds = $this->
makeList( $conds, self::LIST_AND );
1298 $sql =
"SELECT $startOpts $vars $from $useIndex $ignoreIndex " .
1299 "WHERE $conds $preLimitTail";
1301 $sql =
"SELECT $startOpts $vars $from $useIndex $ignoreIndex $preLimitTail";
1304 if ( isset(
$options[
'LIMIT'] ) ) {
1308 $sql =
"$sql $postLimitTail";
1310 if ( isset(
$options[
'EXPLAIN'] ) ) {
1311 $sql =
'EXPLAIN ' . $sql;
1324 if (
$res ===
false ) {
1345 $rows = ( isset( $row[
'rowcount'] ) ) ? (
int)$row[
'rowcount'] : 0;
1359 $res = $this->
query(
"SELECT COUNT(*) AS $rowCountCol FROM ($sql) $tableName",
$fname );
1363 $rows = ( isset( $row[
'rowcount'] ) ) ? (
int)$row[
'rowcount'] : 0;
1378 # This does the same as the regexp below would do, but in such a way
1379 # as to avoid crashing php on some large strings.
1380 # $sql = preg_replace( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql );
1382 $sql = str_replace(
"\\\\",
'', $sql );
1383 $sql = str_replace(
"\\'",
'', $sql );
1384 $sql = str_replace(
"\\\"",
'', $sql );
1385 $sql = preg_replace(
"/'.*'/s",
"'X'", $sql );
1386 $sql = preg_replace(
'/".*"/s',
"'X'", $sql );
1388 # All newlines, tabs, etc replaced by single space
1389 $sql = preg_replace(
'/\s+/',
' ', $sql );
1392 # except the ones surrounded by characters, e.g. l10n
1393 $sql = preg_replace(
'/-?\d+(,-?\d+)+/s',
'N,...,N', $sql );
1394 $sql = preg_replace(
'/(?<![a-zA-Z])-?\d+(?![a-zA-Z])/s',
'N', $sql );
1400 $info = $this->
fieldInfo( $table, $field );
1411 if ( is_null( $info ) ) {
1414 return $info !==
false;
1419 $tableRaw = $this->
tableName( $table,
'raw' );
1420 if ( isset( $this->mSessionTempTables[$tableRaw] ) ) {
1433 $indexInfo = $this->
indexInfo( $table, $index );
1435 if ( !$indexInfo ) {
1439 return !$indexInfo[0]->Non_unique;
1453 # No rows to insert, easy just return now
1454 if ( !count( $a ) ) {
1465 if ( isset(
$options[
'fileHandle'] ) ) {
1470 if ( isset( $a[0] ) && is_array( $a[0] ) ) {
1472 $keys = array_keys( $a[0] );
1475 $keys = array_keys( $a );
1479 " INTO $table (" . implode(
',',
$keys ) .
') VALUES ';
1483 foreach ( $a
as $row ) {
1489 $sql .=
'(' . $this->
makeList( $row ) .
')';
1492 $sql .=
'(' . $this->
makeList( $a ) .
')';
1495 if ( $fh !==
null &&
false === fwrite( $fh, $sql ) ) {
1497 } elseif ( $fh !==
null ) {
1517 if ( in_array(
'IGNORE',
$options ) ) {
1533 return implode(
' ', $opts );
1539 $sql =
"UPDATE $opts $table SET " . $this->
makeList( $values, self::LIST_SET );
1541 if ( $conds !== [] && $conds !==
'*' ) {
1542 $sql .=
" WHERE " . $this->
makeList( $conds, self::LIST_AND );
1548 public function makeList( $a, $mode = self::LIST_COMMA ) {
1549 if ( !is_array( $a ) ) {
1550 throw new DBUnexpectedError( $this, __METHOD__ .
' called with incorrect parameters' );
1556 foreach ( $a
as $field =>
$value ) {
1558 if ( $mode == self::LIST_AND ) {
1560 } elseif ( $mode == self::LIST_OR ) {
1569 if ( ( $mode == self::LIST_AND || $mode == self::LIST_OR ) && is_numeric( $field ) ) {
1570 $list .=
"($value)";
1571 } elseif ( $mode == self::LIST_SET && is_numeric( $field ) ) {
1574 ( $mode == self::LIST_AND || $mode == self::LIST_OR ) && is_array(
$value )
1577 $includeNull =
false;
1578 foreach ( array_keys(
$value,
null,
true )
as $nullKey ) {
1579 $includeNull =
true;
1580 unset(
$value[$nullKey] );
1582 if ( count(
$value ) == 0 && !$includeNull ) {
1583 throw new InvalidArgumentException(
1584 __METHOD__ .
": empty input for field $field" );
1585 } elseif ( count(
$value ) == 0 ) {
1587 $list .=
"$field IS NULL";
1590 if ( $includeNull ) {
1594 if ( count(
$value ) == 1 ) {
1604 if ( $includeNull ) {
1605 $list .=
" OR $field IS NULL)";
1608 } elseif (
$value ===
null ) {
1609 if ( $mode == self::LIST_AND || $mode == self::LIST_OR ) {
1610 $list .=
"$field IS ";
1611 } elseif ( $mode == self::LIST_SET ) {
1612 $list .=
"$field = ";
1617 $mode == self::LIST_AND || $mode == self::LIST_OR || $mode == self::LIST_SET
1619 $list .=
"$field = ";
1631 foreach ( $data
as $base => $sub ) {
1632 if ( count( $sub ) ) {
1634 [ $baseKey =>
$base, $subKey => array_keys( $sub ) ],
1640 return $this->
makeList( $conds, self::LIST_OR );
1655 public function bitAnd( $fieldLeft, $fieldRight ) {
1656 return "($fieldLeft & $fieldRight)";
1659 public function bitOr( $fieldLeft, $fieldRight ) {
1660 return "($fieldLeft | $fieldRight)";
1664 return 'CONCAT(' . implode(
',', $stringList ) .
')';
1668 $delim, $table, $field, $conds =
'', $join_conds = []
1670 $fld =
"GROUP_CONCAT($field SEPARATOR " . $this->
addQuotes( $delim ) .
')';
1672 return '(' . $this->
selectSQLText( $table, $fld, $conds,
null, [], $join_conds ) .
')';
1680 # Stub. Shouldn't cause serious problems if it's not overridden, but
1681 # if your database engine supports a concept similar to MySQL's
1682 # databases you may as well.
1683 $this->mDBname = $db;
1689 return $this->mDBname;
1693 return $this->mServer;
1697 # Skip the entire process when we have a string quoted on both ends.
1698 # Note that we check the end so that we will still quote any use of
1699 # use of `database`.table. But won't break things if someone wants
1700 # to query a database table with a dot in the name.
1705 # Lets test for any bits of text that should never show up in a table
1706 # name. Basically anything like JOIN or ON which are actually part of
1707 # SQL queries, but may end up inside of the table value to combine
1708 # sql. Such as how the API is doing.
1709 # Note that we use a whitespace test rather than a \b test to avoid
1710 # any remote case where a word like on may be inside of a table name
1711 # surrounded by symbols which may be considered word breaks.
1712 if ( preg_match(
'/(^|\s)(DISTINCT|JOIN|ON|AS)(\s|$)/i',
$name ) !== 0 ) {
1716 # Split database and table into proper variables.
1717 # We reverse the explode so that database.table and table both output
1718 # the correct table.
1719 $dbDetails = explode(
'.',
$name, 3 );
1720 if ( count( $dbDetails ) == 3 ) {
1721 list( $database, $schema, $table ) = $dbDetails;
1722 # We don't want any prefix added in this case
1724 } elseif ( count( $dbDetails ) == 2 ) {
1725 list( $database, $table ) = $dbDetails;
1726 # We don't want any prefix added in this case
1728 # In dbs that support it, $database may actually be the schema
1729 # but that doesn't affect any of the functionality here
1732 list( $table ) = $dbDetails;
1733 if ( isset( $this->tableAliases[$table] ) ) {
1734 $database = $this->tableAliases[$table][
'dbname'];
1735 $schema = is_string( $this->tableAliases[$table][
'schema'] )
1736 ? $this->tableAliases[$table][
'schema']
1738 $prefix = is_string( $this->tableAliases[$table][
'prefix'] )
1739 ? $this->tableAliases[$table][
'prefix']
1740 : $this->mTablePrefix;
1743 $schema = $this->mSchema; # Default schema
1744 $prefix = $this->mTablePrefix; # Default prefix
1748 # Quote $table and apply the prefix if not quoted.
1749 # $tableName might be empty if this is called from Database::replaceVars()
1750 $tableName =
"{$prefix}{$table}";
1751 if ( $format ===
'quoted'
1753 && $tableName !==
''
1758 # Quote $schema and $database and merge them with the table name if needed
1772 if ( strlen( $namespace ) ) {
1776 $relation = $namespace .
'.' . $relation;
1783 $inArray = func_get_args();
1786 foreach ( $inArray
as $name ) {
1794 $inArray = func_get_args();
1797 foreach ( $inArray
as $name ) {
1813 if ( !$alias || $alias ==
$name ) {
1828 foreach (
$tables as $alias => $table ) {
1829 if ( is_numeric( $alias ) ) {
1847 if ( !$alias || (
string)$alias === (
string)
$name ) {
1862 foreach ( $fields
as $alias => $field ) {
1863 if ( is_numeric( $alias ) ) {
1883 $tables, $use_index = [], $ignore_index = [], $join_conds = []
1887 $use_index = (
array)$use_index;
1888 $ignore_index = (
array)$ignore_index;
1889 $join_conds = (
array)$join_conds;
1891 foreach (
$tables as $alias => $table ) {
1892 if ( !is_string( $alias ) ) {
1897 if ( isset( $join_conds[$alias] ) ) {
1898 list( $joinType, $conds ) = $join_conds[$alias];
1899 $tableClause = $joinType;
1901 if ( isset( $use_index[$alias] ) ) {
1904 $tableClause .=
' ' . $use;
1907 if ( isset( $ignore_index[$alias] ) ) {
1909 implode(
',', (
array)$ignore_index[$alias] ) );
1910 if ( $ignore !=
'' ) {
1911 $tableClause .=
' ' . $ignore;
1916 $tableClause .=
' ON (' . $on .
')';
1919 $retJOIN[] = $tableClause;
1920 } elseif ( isset( $use_index[$alias] ) ) {
1924 implode(
',', (
array)$use_index[$alias] )
1927 $ret[] = $tableClause;
1928 } elseif ( isset( $ignore_index[$alias] ) ) {
1932 implode(
',', (
array)$ignore_index[$alias] )
1935 $ret[] = $tableClause;
1939 $ret[] = $tableClause;
1944 $implicitJoins = !empty(
$ret ) ? implode(
',',
$ret ) :
"";
1945 $explicitJoins = !empty( $retJOIN ) ? implode(
' ', $retJOIN ) :
"";
1948 return implode(
' ', [ $implicitJoins, $explicitJoins ] );
1960 'ar_usertext_timestamp' =>
'usertext_timestamp',
1961 'un_user_id' =>
'user_id',
1962 'un_user_ip' =>
'user_ip',
1965 if ( isset( $renamed[$index] ) ) {
1966 return $renamed[$index];
1973 if (
$s instanceof
Blob ) {
1976 if (
$s ===
null ) {
1978 } elseif ( is_bool(
$s ) ) {
1981 # This will also quote numeric values. This should be harmless,
1982 # and protects against weird problems that occur when they really
1983 # _are_ strings such as article titles and string->number->string
1984 # conversion is not 1:1.
1999 return '"' . str_replace(
'"',
'""',
$s ) .
'"';
2012 return $name[0] ==
'"' && substr(
$name, -1, 1 ) ==
'"';
2020 return addcslashes(
$s,
'\%_' );
2040 return " LIKE {$this->addQuotes( $s )} ";
2084 $quotedTable = $this->
tableName( $table );
2086 if ( count( $rows ) == 0 ) {
2091 if ( !is_array( reset( $rows ) ) ) {
2096 foreach ( $rows
as $row ) {
2097 # Delete rows which collide
2098 if ( $uniqueIndexes ) {
2099 $sql =
"DELETE FROM $quotedTable WHERE ";
2101 foreach ( $uniqueIndexes
as $index ) {
2108 if ( is_array( $index ) ) {
2110 foreach ( $index
as $col ) {
2116 $sql .= $col .
'=' . $this->
addQuotes( $row[$col] );
2119 $sql .= $index .
'=' . $this->
addQuotes( $row[$index] );
2126 # Now insert the row
2145 if ( !is_array( reset( $rows ) ) ) {
2149 $sql =
"REPLACE INTO $table (" . implode(
',', array_keys( $rows[0] ) ) .
') VALUES ';
2152 foreach ( $rows
as $row ) {
2159 $sql .=
'(' . $this->
makeList( $row ) .
')';
2168 if ( !count( $rows ) ) {
2172 if ( !is_array( reset( $rows ) ) ) {
2176 if ( count( $uniqueIndexes ) ) {
2178 foreach ( $rows
as $row ) {
2179 foreach ( $uniqueIndexes
as $index ) {
2180 $index = is_array( $index ) ? $index : [ $index ];
2182 foreach ( $index
as $column ) {
2183 $rowKey[$column] = $row[$column];
2185 $clauses[] = $this->
makeList( $rowKey, self::LIST_AND );
2188 $where = [ $this->
makeList( $clauses, self::LIST_OR ) ];
2193 $useTrx = !$this->mTrxLevel;
2195 $this->
begin(
$fname, self::TRANSACTION_INTERNAL );
2198 # Update any existing conflicting row(s)
2199 if ( $where !==
false ) {
2204 # Now insert any non-conflicting row(s)
2205 $ok = $this->
insert( $table, $rows,
$fname, [
'IGNORE' ] ) && $ok;
2206 }
catch ( Exception
$e ) {
2219 public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds,
2226 $delTable = $this->
tableName( $delTable );
2227 $joinTable = $this->
tableName( $joinTable );
2228 $sql =
"DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable ";
2229 if ( $conds !=
'*' ) {
2230 $sql .=
'WHERE ' . $this->
makeList( $conds, self::LIST_AND );
2239 $sql =
"SHOW COLUMNS FROM $table LIKE \"$field\";";
2240 $res = $this->
query( $sql, __METHOD__ );
2245 if ( preg_match(
'/\((.*)\)/', $row->Type, $m ) ) {
2254 public function delete( $table, $conds,
$fname = __METHOD__ ) {
2256 throw new DBUnexpectedError( $this, __METHOD__ .
' called with no conditions' );
2260 $sql =
"DELETE FROM $table";
2262 if ( $conds !=
'*' ) {
2263 if ( is_array( $conds ) ) {
2264 $conds = $this->
makeList( $conds, self::LIST_AND );
2266 $sql .=
' WHERE ' . $conds;
2273 $destTable, $srcTable, $varMap, $conds,
2274 $fname = __METHOD__, $insertOptions = [], $selectOptions = []
2276 if ( $this->cliMode ) {
2294 foreach ( $varMap
as $dstColumn => $sourceColumnOrSql ) {
2297 $selectOptions[] =
'FOR UPDATE';
2298 $res = $this->
select( $srcTable, implode(
',', $fields ), $conds,
$fname, $selectOptions );
2304 foreach (
$res as $row ) {
2305 $rows[] = (
array)$row;
2308 return $this->
insert( $destTable, $rows,
$fname, $insertOptions );
2313 $insertOptions = [], $selectOptions = []
2315 $destTable = $this->
tableName( $destTable );
2317 if ( !is_array( $insertOptions ) ) {
2318 $insertOptions = [ $insertOptions ];
2323 if ( !is_array( $selectOptions ) ) {
2324 $selectOptions = [ $selectOptions ];
2330 if ( is_array( $srcTable ) ) {
2331 $srcTable = implode(
',', array_map( [ $this,
'tableName' ], $srcTable ) );
2333 $srcTable = $this->
tableName( $srcTable );
2336 $sql =
"INSERT $insertOptions" .
2337 " INTO $destTable (" . implode(
',', array_keys( $varMap ) ) .
')' .
2338 " SELECT $startOpts " . implode(
',', $varMap ) .
2339 " FROM $srcTable $useIndex $ignoreIndex ";
2341 if ( $conds !=
'*' ) {
2342 if ( is_array( $conds ) ) {
2343 $conds = $this->
makeList( $conds, self::LIST_AND );
2345 $sql .=
" WHERE $conds";
2348 $sql .=
" $tailOpts";
2373 if ( !is_numeric(
$limit ) ) {
2375 "Invalid non-numeric limit passed to limitResult()\n" );
2378 return "$sql LIMIT "
2379 . ( ( is_numeric( $offset ) && $offset != 0 ) ?
"{$offset}," :
"" )
2388 $glue = $all ?
') UNION ALL (' :
') UNION (';
2390 return '(' . implode( $glue, $sqls ) .
')';
2394 if ( is_array( $cond ) ) {
2395 $cond = $this->
makeList( $cond, self::LIST_AND );
2398 return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
2402 return "REPLACE({$orig}, {$old}, {$new})";
2436 $args = func_get_args();
2437 $function = array_shift(
$args );
2438 $tries = self::DEADLOCK_TRIES;
2440 $this->
begin( __METHOD__ );
2447 $retVal = call_user_func_array( $function,
$args );
2452 usleep( mt_rand( self::DEADLOCK_DELAY_MIN, self::DEADLOCK_DELAY_MAX ) );
2458 }
while ( --$tries > 0 );
2460 if ( $tries <= 0 ) {
2465 $this->
commit( __METHOD__ );
2472 # Real waits are implemented in the subclass.
2491 if ( !$this->mTrxLevel ) {
2494 $this->mTrxEndCallbacks[] = [ $callback,
$fname ];
2498 $this->mTrxIdleCallbacks[] = [ $callback,
$fname ];
2499 if ( !$this->mTrxLevel ) {
2505 if ( $this->mTrxLevel ) {
2506 $this->mTrxPreCommitCallbacks[] = [ $callback,
$fname ];
2511 call_user_func( $callback );
2513 }
catch ( Exception
$e ) {
2514 $this->
rollback( __METHOD__, self::FLUSHING_INTERNAL );
2522 $this->mTrxRecurringCallbacks[
$name] = $callback;
2524 unset( $this->mTrxRecurringCallbacks[
$name] );
2537 $this->mTrxEndCallbacksSuppressed = $suppress;
2550 if ( $this->mTrxEndCallbacksSuppressed ) {
2554 $autoTrx = $this->
getFlag( self::DBO_TRX );
2558 $callbacks = array_merge(
2559 $this->mTrxIdleCallbacks,
2560 $this->mTrxEndCallbacks
2562 $this->mTrxIdleCallbacks = [];
2563 $this->mTrxEndCallbacks = [];
2564 foreach ( $callbacks
as $callback ) {
2566 list( $phpCallback ) = $callback;
2568 call_user_func_array( $phpCallback, [ $trigger ] );
2570 $this->
setFlag( self::DBO_TRX );
2574 }
catch ( Exception $ex ) {
2575 call_user_func( $this->errorLogger, $ex );
2580 $this->
rollback( __METHOD__, self::FLUSHING_INTERNAL );
2584 }
while ( count( $this->mTrxIdleCallbacks ) );
2586 if (
$e instanceof Exception ) {
2602 $callbacks = $this->mTrxPreCommitCallbacks;
2603 $this->mTrxPreCommitCallbacks = [];
2604 foreach ( $callbacks
as $callback ) {
2606 list( $phpCallback ) = $callback;
2607 call_user_func( $phpCallback );
2608 }
catch ( Exception $ex ) {
2609 call_user_func( $this->errorLogger, $ex );
2613 }
while ( count( $this->mTrxPreCommitCallbacks ) );
2615 if (
$e instanceof Exception ) {
2630 if ( $this->mTrxEndCallbacksSuppressed ) {
2637 foreach ( $this->mTrxRecurringCallbacks
as $phpCallback ) {
2639 $phpCallback( $trigger, $this );
2640 }
catch ( Exception $ex ) {
2641 call_user_func( $this->errorLogger, $ex );
2646 if (
$e instanceof Exception ) {
2652 if ( !$this->mTrxLevel ) {
2653 $this->
begin(
$fname, self::TRANSACTION_INTERNAL );
2656 if ( !$this->
getFlag( self::DBO_TRX ) ) {
2657 $this->mTrxAutomaticAtomic =
true;
2661 $this->mTrxAtomicLevels[] =
$fname;
2665 if ( !$this->mTrxLevel ) {
2666 throw new DBUnexpectedError( $this,
"No atomic transaction is open (got $fname)." );
2668 if ( !$this->mTrxAtomicLevels ||
2669 array_pop( $this->mTrxAtomicLevels ) !==
$fname
2671 throw new DBUnexpectedError( $this,
"Invalid atomic section ended (got $fname)." );
2674 if ( !$this->mTrxAtomicLevels && $this->mTrxAutomaticAtomic ) {
2682 $res = call_user_func_array( $callback, [ $this,
$fname ] );
2683 }
catch ( Exception
$e ) {
2692 final public function begin(
$fname = __METHOD__, $mode = self::TRANSACTION_EXPLICIT ) {
2694 if ( $this->mTrxLevel ) {
2695 if ( $this->mTrxAtomicLevels ) {
2696 $levels = implode(
', ', $this->mTrxAtomicLevels );
2697 $msg =
"$fname: Got explicit BEGIN while atomic section(s) $levels are open.";
2699 } elseif ( !$this->mTrxAutomatic ) {
2700 $msg =
"$fname: Explicit transaction already active (from {$this->mTrxFname}).";
2704 $msg =
"$fname: Implicit transaction already active (from {$this->mTrxFname}).";
2705 $this->queryLogger->error( $msg );
2708 } elseif ( $this->
getFlag( self::DBO_TRX ) && $mode !== self::TRANSACTION_INTERNAL ) {
2710 $msg =
"$fname: Implicit transaction expected (DBO_TRX set).";
2711 $this->queryLogger->error( $msg );
2719 $this->mTrxTimestamp = microtime(
true );
2720 $this->mTrxFname =
$fname;
2721 $this->mTrxDoneWrites =
false;
2722 $this->mTrxAutomaticAtomic =
false;
2723 $this->mTrxAtomicLevels = [];
2724 $this->mTrxShortId = sprintf(
'%06x', mt_rand( 0, 0xffffff ) );
2725 $this->mTrxWriteDuration = 0.0;
2726 $this->mTrxWriteQueryCount = 0;
2727 $this->mTrxWriteAdjDuration = 0.0;
2728 $this->mTrxWriteAdjQueryCount = 0;
2729 $this->mTrxWriteCallers = [];
2734 $this->mTrxReplicaLag =
$status[
'lag'] + ( microtime(
true ) -
$status[
'since'] );
2738 $this->mTrxAutomatic = ( $mode === self::TRANSACTION_INTERNAL );
2749 $this->mTrxLevel = 1;
2753 if ( $this->mTrxLevel && $this->mTrxAtomicLevels ) {
2755 $levels = implode(
', ', $this->mTrxAtomicLevels );
2758 "$fname: Got COMMIT while atomic sections $levels are still open."
2762 if ( $flush === self::FLUSHING_INTERNAL || $flush === self::FLUSHING_ALL_PEERS ) {
2763 if ( !$this->mTrxLevel ) {
2765 } elseif ( !$this->mTrxAutomatic ) {
2768 "$fname: Flushing an explicit transaction, getting out of sync."
2772 if ( !$this->mTrxLevel ) {
2773 $this->queryLogger->error(
2774 "$fname: No transaction to commit, something got out of sync." );
2776 } elseif ( $this->mTrxAutomatic ) {
2778 $msg =
"$fname: Explicit commit of implicit transaction.";
2779 $this->queryLogger->error( $msg );
2790 if ( $this->mTrxDoneWrites ) {
2791 $this->mLastWriteTime = microtime(
true );
2792 $this->trxProfiler->transactionWritingOut(
2793 $this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
2807 if ( $this->mTrxLevel ) {
2809 $this->mTrxLevel = 0;
2814 if ( $flush === self::FLUSHING_INTERNAL || $flush === self::FLUSHING_ALL_PEERS ) {
2815 if ( !$this->mTrxLevel ) {
2819 if ( !$this->mTrxLevel ) {
2820 $this->queryLogger->error(
2821 "$fname: No transaction to rollback, something got out of sync." );
2823 } elseif ( $this->
getFlag( self::DBO_TRX ) ) {
2826 "$fname: Expected mass rollback of all peer databases (DBO_TRX set)."
2835 $this->mTrxAtomicLevels = [];
2836 if ( $this->mTrxDoneWrites ) {
2837 $this->trxProfiler->transactionWritingOut(
2838 $this->mServer, $this->mDBname, $this->mTrxShortId );
2841 $this->mTrxIdleCallbacks = [];
2842 $this->mTrxPreCommitCallbacks = [];
2854 if ( $this->mTrxLevel ) {
2855 # Disconnects cause rollback anyway, so ignore those errors
2856 $ignoreErrors =
true;
2857 $this->
query(
'ROLLBACK',
$fname, $ignoreErrors );
2858 $this->mTrxLevel = 0;
2868 "$fname: Cannot flush snapshot because writes are pending ($fnames)."
2876 return $this->mTrxLevel && ( $this->mTrxAtomicLevels || !$this->mTrxAutomatic );
2897 throw new RuntimeException( __METHOD__ .
' is not implemented in descendant class' );
2901 throw new RuntimeException( __METHOD__ .
' is not implemented in descendant class' );
2905 throw new RuntimeException( __METHOD__ .
' is not implemented in descendant class' );
2911 return $t->getTimestamp(
TS_MW );
2915 if ( is_null( $ts ) ) {
2940 } elseif (
$result ===
true ) {
2948 public function ping( &$rtt =
null ) {
2950 if ( $this->
isOpen() && ( microtime(
true ) - $this->lastPing ) < self::PING_TTL ) {
2951 if ( !func_num_args() || $this->mRTTEstimate > 0 ) {
2952 $rtt = $this->mRTTEstimate;
2958 $this->
clearFlag( self::DBO_TRX, self::REMEMBER_PRIOR );
2959 $ok = ( $this->
query( self::PING_QUERY, __METHOD__,
true ) !==
false );
2963 $rtt = $this->mRTTEstimate;
2974 $this->mOpened =
false;
2975 $this->mConn =
false;
2977 $this->
open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
2978 $this->lastPing = microtime(
true );
3003 return $this->mTrxLevel
3004 ? [
'lag' => $this->mTrxReplicaLag,
'since' => $this->
trxTimestamp() ]
3017 'since' => microtime(
true )
3040 $res = [
'lag' => 0,
'since' => INF,
'pending' =>
false ];
3041 foreach ( func_get_args()
as $db ) {
3043 $status = $db->getSessionLagStatus();
3044 if (
$status[
'lag'] ===
false ) {
3045 $res[
'lag'] =
false;
3046 } elseif (
$res[
'lag'] !==
false ) {
3050 $res[
'pending'] =
$res[
'pending'] ?: $db->writesPending();
3069 if ( $b instanceof
Blob ) {
3080 callable $lineCallback =
null,
3081 callable $resultCallback =
null,
3083 callable $inputCallback =
null
3085 MediaWiki\suppressWarnings();
3086 $fp = fopen( $filename,
'r' );
3087 MediaWiki\restoreWarnings();
3089 if (
false === $fp ) {
3090 throw new RuntimeException(
"Could not open \"{$filename}\".\n" );
3094 $fname = __METHOD__ .
"( $filename )";
3099 $fp, $lineCallback, $resultCallback,
$fname, $inputCallback );
3100 }
catch ( Exception
$e ) {
3111 $this->mSchemaVars =
$vars;
3116 callable $lineCallback =
null,
3117 callable $resultCallback =
null,
3119 callable $inputCallback =
null
3123 while ( !feof( $fp ) ) {
3124 if ( $lineCallback ) {
3125 call_user_func( $lineCallback );
3128 $line = trim( fgets( $fp ) );
3130 if (
$line ==
'' ) {
3146 if ( $done || feof( $fp ) ) {
3149 if ( !$inputCallback || call_user_func( $inputCallback, $cmd ) ) {
3152 if ( $resultCallback ) {
3153 call_user_func( $resultCallback,
$res, $this );
3156 if (
false ===
$res ) {
3159 return "Query \"{$cmd}\" failed with error code \"$err\".\n";
3177 if ( $this->delimiter ) {
3179 $newLine = preg_replace(
3180 '/' . preg_quote( $this->delimiter,
'/' ) .
'$/',
'', $newLine );
3181 if ( $newLine != $prev ) {
3211 return preg_replace_callback(
3213 /\* (\$wgDBprefix|[_i]) \*/ (\w*) | # 1-2. tableName, indexName
3214 \'\{\$ (\w+) }\' | # 3. addQuotes
3215 `\{\$ (\w+) }` | # 4. addIdentifierQuotes
3216 /\*\$ (\w+) \*/ # 5. leave unencoded
3221 if ( isset( $m[1] ) && $m[1] !==
'' ) {
3222 if ( $m[1] ===
'i' ) {
3227 } elseif ( isset( $m[3] ) && $m[3] !==
'' && array_key_exists( $m[3],
$vars ) ) {
3229 } elseif ( isset( $m[4] ) && $m[4] !==
'' && array_key_exists( $m[4],
$vars ) ) {
3231 } elseif ( isset( $m[5] ) && $m[5] !==
'' && array_key_exists( $m[5],
$vars ) ) {
3232 return $vars[$m[5]];
3248 if ( $this->mSchemaVars ) {
3249 return $this->mSchemaVars;
3271 public function lock( $lockName, $method, $timeout = 5 ) {
3272 $this->mNamedLocksHeld[$lockName] = 1;
3277 public function unlock( $lockName, $method ) {
3278 unset( $this->mNamedLocksHeld[$lockName] );
3289 "$fname: Cannot flush pre-lock snapshot because writes are pending ($fnames)."
3293 if ( !$this->
lock( $lockKey,
$fname, $timeout ) ) {
3297 $unlocker =
new ScopedCallback(
function ()
use ( $lockKey,
$fname ) {
3331 public function lockTables( $read, $write, $method, $lowPriority =
true ) {
3352 public function dropTable( $tableName, $fName = __METHOD__ ) {
3353 if ( !$this->
tableExists( $tableName, $fName ) ) {
3356 $sql =
"DROP TABLE " . $this->
tableName( $tableName ) .
" CASCADE";
3358 return $this->
query( $sql, $fName );
3366 return ( $expiry ==
'' || $expiry ==
'infinity' || $expiry == $this->
getInfinity() )
3372 if ( $expiry ==
'' || $expiry ==
'infinity' || $expiry == $this->
getInfinity() ) {
3391 $reason = $this->
getLBInfo(
'readOnlyReason' );
3393 return is_string( $reason ) ? $reason :
false;
3397 $this->tableAliases = $aliases;
3420 if ( !$this->mConn ) {
3423 'DB connection was already closed or the connection dropped.'
3427 return $this->mConn;
3435 return (
string)$this->mConn;
3443 $this->connLogger->warning(
3444 "Cloning " . get_class( $this ) .
" is not recomended; forking connection:\n" .
3445 (
new RuntimeException() )->getTraceAsString()
3450 $this->mOpened =
false;
3451 $this->mConn =
false;
3452 $this->mTrxEndCallbacks = [];
3454 $this->
open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
3455 $this->lastPing = microtime(
true );
3465 throw new RuntimeException(
'Database serialization may cause problems, since ' .
3466 'the connection is not restored on wakeup.' );
3473 if ( $this->mTrxLevel && $this->mTrxDoneWrites ) {
3474 trigger_error(
"Uncommitted DB writes (transaction from {$this->mTrxFname})." );
3478 if ( $danglingWriters ) {
3479 $fnames = implode(
', ', $danglingWriters );
3480 trigger_error(
"DB transaction writes or callbacks still pending ($fnames)." );
3483 if ( $this->mConn ) {
3486 \MediaWiki\suppressWarnings();
3488 \MediaWiki\restoreWarnings();
3489 $this->mConn =
false;
3490 $this->mOpened =
false;
3500 return SearchEngineFactory::getSearchEngineClass( $this );
3504class_alias(
'Database',
'DatabaseBase' );
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined.
interface is intended to be more or less compatible with the PHP memcached client.
Library for creating, parsing, and converting timestamps.
static convert( $style=TS_UNIX, $ts)
Convert a timestamp string to a given format.
Class to handle database/prefix specification for IDatabase domains.
Relational database abstraction object.
__destruct()
Run a few simple sanity checks and close dangling connections.
LoggerInterface $queryLogger
setSchemaVars( $vars)
Set variables to be used in sourceFile/sourceStream, in preference to the ones in $GLOBALS.
implicitGroupby()
Returns true if this database does an implicit sort when doing GROUP BY.
fieldExists( $table, $field, $fname=__METHOD__)
Determines whether a field exists in a table.
__sleep()
Called by serialize.
float null $mTrxTimestamp
The UNIX time that the transaction started.
makeList( $a, $mode=self::LIST_COMMA)
Makes an encoded list of strings from an array.
dbSchema( $schema=null)
Get/set the db schema.
float $mTrxReplicaLag
Lag estimate at the time of BEGIN.
trxTimestamp()
Get the UNIX timestamp of the time that the transaction was established.
getTransactionLagStatus()
Get the replica DB lag when the current transaction started.
makeInsertOptions( $options)
Helper for Database::insert().
prependDatabaseOrSchema( $namespace, $relation, $format)
float $lastPing
UNIX timestamp.
callback $errorLogger
Error logging callback.
fieldNamesWithAlias( $fields)
Gets an array of aliased field names.
const DEADLOCK_TRIES
Number of times to re-try an operation in case of deadlock.
array[] $mTrxEndCallbacks
List of (callable, method name)
decodeBlob( $b)
Some DBMSs return a special placeholder object representing blob fields in result objects.
estimateRowCount( $table, $vars=' *', $conds='', $fname=__METHOD__, $options=[])
Estimate the number of rows in dataset.
bool $mTrxEndCallbacksSuppressed
Whether to suppress triggering of transaction end callbacks.
deadlockLoop()
Perform a deadlock-prone transaction.
wasErrorReissuable()
Determines if the last query error was due to a dropped connection and should be dealt with by pingin...
fieldNameWithAlias( $name, $alias=false)
Get an aliased field name e.g.
anyString()
Returns a token for buildLike() that denotes a '' to be used in a LIKE query.
buildGroupConcatField( $delim, $table, $field, $conds='', $join_conds=[])
Build a GROUP_CONCAT or equivalent statement for a query.
object string $profiler
Class name or object With profileIn/profileOut methods.
lockTables( $read, $write, $method, $lowPriority=true)
Lock specific tables.
getLag()
Get replica DB lag.
wasDeadlock()
Determines if the last failure was due to a deadlock.
close()
Closes a database connection.
startAtomic( $fname=__METHOD__)
Begin an atomic section of statements.
onTransactionResolution(callable $callback, $fname=__METHOD__)
Run a callback as soon as the current transaction commits or rolls back.
tableNamesWithAlias( $tables)
Gets an array of aliased table names.
isQuotedIdentifier( $name)
Returns if the given identifier looks quoted or not according to the database convention for quoting ...
integer $mTrxWriteAdjQueryCount
Number of write queries counted in mTrxWriteAdjDuration.
setLogger(LoggerInterface $logger)
nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname=__METHOD__, $insertOptions=[], $selectOptions=[])
getSessionLagStatus()
Get the replica DB lag when the current transaction started or a general lag estimate if not transact...
resource null $mConn
Database connection.
float $mTrxWriteAdjDuration
Like mTrxWriteQueryCount but excludes lock-bound, easy to replicate, queries.
wasReadOnlyError()
Determines if the last failure was due to the database being read-only.
flushSnapshot( $fname=__METHOD__)
Commit any transaction but error out if writes or callbacks are pending.
replaceVars( $ins)
Database independent variable replacement.
BagOStuff $srvCache
APC cache.
string $agent
Agent name for query profiling.
runOnTransactionIdleCallbacks( $trigger)
Actually run and consume any "on transaction idle/resolution" callbacks.
encodeExpiry( $expiry)
Encode an expiry time into the DBMS dependent format.
float $mRTTEstimate
RTT time estimate.
selectSQLText( $table, $vars, $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
The equivalent of IDatabase::select() except that the constructed SQL is returned,...
indexName( $index)
Get the name of an index in a given table.
updateTrxWriteQueryTime( $sql, $runtime)
Update the estimated run-time of a query, not counting large row lock times.
doRollback( $fname)
Issues the ROLLBACK command to the database server.
tableNamesWithIndexClauseOrJOIN( $tables, $use_index=[], $ignore_index=[], $join_conds=[])
Get the aliased table name clause for a FROM clause which might have a JOIN and/or USE INDEX or IGNOR...
static getCacheSetOptions(IDatabase $db1)
Merge the result of getSessionLagStatus() for several DBs using the most pessimistic values to estima...
doProfiledQuery( $sql, $commentedSql, $isWrite, $fname)
LoggerInterface $connLogger
upsert( $table, array $rows, array $uniqueIndexes, array $set, $fname=__METHOD__)
INSERT ON DUPLICATE KEY UPDATE wrapper, upserts an array into a table.
writesOrCallbacksPending()
Returns true if there is a transaction open with possible write queries or transaction pre-commit/idl...
getFlag( $flag)
Returns a boolean whether the flag $flag is set for this connection.
getDBname()
Get the current DB name.
IDatabase null $lazyMasterHandle
Lazy handle to the master DB this server replicates from.
tableNameWithAlias( $name, $alias=false)
Get an aliased table name e.g.
restoreFlags( $state=self::RESTORE_PRIOR)
Restore the flags to their prior state before the last setFlag/clearFlag call.
bool $mTrxDoneWrites
Record if possible write queries were done in the last transaction started.
masterPosWait(DBMasterPos $pos, $timeout)
Wait for the replica DB to catch up to a given master position.
doAtomicSection( $fname, callable $callback)
Run a callback to do an atomic set of updates for this database.
const DEADLOCK_DELAY_MAX
Maximum time to wait before retry.
tablePrefix( $prefix=null)
Get/set the table prefix.
ignoreIndexClause( $index)
IGNORE INDEX clause.
resultObject( $result)
Take the result from a query, and wrap it in a ResultWrapper if necessary.
static generalizeSQL( $sql)
Removes most variables from an SQL query and replaces them with X or N for numbers.
indexUnique( $table, $index)
Determines if a given index is unique.
getReplicaPos()
Get the replication position of this replica DB.
onTransactionIdle(callable $callback, $fname=__METHOD__)
Run a callback as soon as there is no transaction pending.
runOnTransactionPreCommitCallbacks()
Actually run and consume any "on transaction pre-commit" callbacks.
decodeExpiry( $expiry, $format=TS_MW)
Decode an expiry time into a DBMS independent format.
aggregateValue( $valuedata, $valuename='value')
Return aggregated value alias.
addIdentifierQuotes( $s)
Quotes an identifier using backticks or "double quotes" depending on the database type.
anyChar()
Returns a token for buildLike() that denotes a '_' to be used in a LIKE query.
buildConcat( $stringList)
Build a concatenation list to feed into a SQL query.
static factory( $dbType, $p=[])
Construct a Database subclass instance given a database type and parameters.
ping(&$rtt=null)
Ping the server and try to reconnect if it there is no connection.
ignoreErrors( $ignoreErrors=null)
Turns on (false) or off (true) the automatic generation and sending of a "we're sorry,...
makeUpdateOptionsArray( $options)
Make UPDATE options array for Database::makeUpdateOptions.
replace( $table, $uniqueIndexes, $rows, $fname=__METHOD__)
REPLACE query wrapper.
bool $mTrxAutomatic
Record if the current transaction was started implicitly due to DBO_TRX being set.
tableExists( $table, $fname=__METHOD__)
Query whether a given table exists.
float bool $mLastWriteTime
UNIX timestamp of last write query.
makeGroupByWithHaving( $options)
Returns an optional GROUP BY with an optional HAVING.
pendingWriteQueryDuration( $type=self::ESTIMATE_TOTAL)
Get the time spend running write queries for this transaction.
bitOr( $fieldLeft, $fieldRight)
conditional( $cond, $trueVal, $falseVal)
Returns an SQL expression for a simple conditional.
lastQuery()
Return the last query that went through IDatabase::query()
array $mNamedLocksHeld
Map of (name => 1) for locks obtained via lock()
unionQueries( $sqls, $all)
Construct a UNION query This is used for providing overload point for other DB abstractions not compa...
dropTable( $tableName, $fName=__METHOD__)
Delete a table.
__clone()
Make sure that copies do not share the same client binding handle.
const PING_TTL
How long before it is worth doing a dummy query to test the connection.
array[] $mTrxIdleCallbacks
List of (callable, method name)
setTrxEndCallbackSuppression( $suppress)
Whether to disable running of post-COMMIT/ROLLBACK callbacks.
insert( $table, $a, $fname=__METHOD__, $options=[])
INSERT wrapper, inserts an array into a table.
reportQueryError( $error, $errno, $sql, $fname, $tempIgnore=false)
Report a query error.
indexInfo( $table, $index, $fname=__METHOD__)
Get information about an index into an object.
maxListLen()
Return the maximum number of items allowed in a list, or 0 for unlimited.
setTableAliases(array $aliases)
Make certain table names use their own database, schema, and table prefix when passed into SQL querie...
isWriteQuery( $sql)
Determine whether a query writes to the DB.
implicitOrderby()
Returns true if this database does an implicit order by when the column has an index For example: SEL...
unionSupportsOrderAndLimit()
Returns true if current database backend supports ORDER BY or LIMIT for separate subqueries within th...
onTransactionPreCommitOrIdle(callable $callback, $fname=__METHOD__)
Run a callback before the current transaction commits or now if there is none.
setSessionOptions(array $options)
Override database's default behavior.
string $mTrxFname
Remembers the function name given for starting the most recent transaction via begin().
bufferResults( $buffer=null)
Turns buffering of SQL result sets on (true) or off (false).
getServerInfo()
A string describing the current software version, and possibly other details in a user-friendly way.
encodeBlob( $b)
Some DBMSs have a special format for inserting into blob fields, they don't allow simple quoted strin...
getServer()
Get the server hostname or IP address.
bitAnd( $fieldLeft, $fieldRight)
integer $mTrxWriteQueryCount
Number of write queries for the current transaction.
doCommit( $fname)
Issues the COMMIT command to the database server.
setTransactionListener( $name, callable $callback=null)
Run a callback each time any transaction commits or rolls back.
selectDB( $db)
Change the current database.
buildLike()
LIKE statement wrapper, receives a variable-length argument list with parts of pattern to match conta...
tableNamesN()
Fetch a number of table names into an zero-indexed numerical array This is handy when you need to con...
int $mTrxLevel
Either 1 if a transaction is active or 0 otherwise.
makeOrderBy( $options)
Returns an optional ORDER BY.
wasLockTimeout()
Determines if the last failure was due to a lock timeout.
closeConnection()
Closes underlying database connection.
makeUpdateOptions( $options)
Make UPDATE options for the Database::update function.
nextSequenceValue( $seqName)
Returns an appropriately quoted sequence value for inserting a new row.
nativeReplace( $table, $rows, $fname)
REPLACE query wrapper for MySQL and SQLite, which have a native REPLACE statement.
select( $table, $vars, $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
Execute a SELECT query constructed using the various parameters provided.
__construct(array $params)
Constructor and database handle and attempt to connect to the DB server.
makeWhereFrom2d( $data, $baseKey, $subKey)
Build a partial where clause from a 2-d array such as used for LinkBatch.
useIndexClause( $index)
USE INDEX clause.
string bool null $htmlErrors
Stashed value of html_errors INI setting.
selectFieldValues( $table, $var, $cond='', $fname=__METHOD__, $options=[], $join_conds=[])
getApproximateLagStatus()
Get a replica DB lag estimate for this server.
unlock( $lockName, $method)
Release a lock.
isOpen()
Is a connection to the database open?
getBindingHandle()
Get the underlying binding handle, mConn.
addQuotes( $s)
Adds quotes and backslashes.
limitResult( $sql, $limit, $offset=false)
Construct a LIMIT query with optional offset.
TransactionProfiler $trxProfiler
tableNames()
Fetch a number of table names into an array This is handy when you need to construct SQL for joins.
array $mSessionTempTables
Map of (table name => 1) for TEMPORARY tables.
selectField( $table, $var, $cond='', $fname=__METHOD__, $options=[])
A SELECT wrapper which returns a single field from a single result row.
setBigSelects( $value=true)
Allow or deny "big selects" for this session only.
getScopedLockAndFlush( $lockKey, $fname, $timeout)
Acquire a named lock, flush any transaction, and return an RAII style unlocker object.
indexExists( $table, $index, $fname=__METHOD__)
Determines whether an index exists Usually throws a DBQueryError on failure If errors are explicitly ...
unlockTables( $method)
Unlock specific tables.
bool $mTrxAutomaticAtomic
Record if the current transaction was started implicitly by Database::startAtomic.
reportConnectionError( $error='Unknown error')
sourceStream( $fp, callable $lineCallback=null, callable $resultCallback=null, $fname=__METHOD__, callable $inputCallback=null)
Read and execute commands from an open file handle.
commit( $fname=__METHOD__, $flush='')
Commits a transaction previously started using begin().
doBegin( $fname)
Issues the BEGIN command to the database server.
query( $sql, $fname=__METHOD__, $tempIgnore=false)
Run an SQL query and return the result.
selectRow( $table, $vars, $conds, $fname=__METHOD__, $options=[], $join_conds=[])
Single row SELECT wrapper.
getLBInfo( $name=null)
Get properties passed down from the server info array of the load balancer.
getDefaultSchemaVars()
Get schema variables to use if none have been set via setSchemaVars().
trxLevel()
Gets the current transaction level.
lastDoneWrites()
Returns the last time the connection may have been used for write queries.
pendingWriteCallers()
Get the list of method names that did write queries for this transaction.
registerTempTableOperation( $sql)
getInfinity()
Find out when 'infinity' is.
DatabaseDomain $currentDomain
sourceFile( $filename, callable $lineCallback=null, callable $resultCallback=null, $fname=false, callable $inputCallback=null)
Read and execute SQL commands from a file.
isTransactableQuery( $sql)
Determine whether a SQL statement is sensitive to isolation level.
bool $cliMode
Whether this PHP instance is for a CLI script.
setFlag( $flag, $remember=self::REMEMBER_NOTHING)
Set a flag for this connection.
duplicateTableStructure( $oldName, $newName, $temporary=false, $fname=__METHOD__)
Creates a new table with structure copied from existing table Note that unlike most database abstract...
strreplace( $orig, $old, $new)
Returns a comand for str_replace function in SQL query.
setLBInfo( $name, $value=null)
Set the LB info array, or a member of it.
string $mTrxShortId
Either a short hexidecimal string if a transaction is active or "".
tableName( $name, $format='quoted')
Format a table name ready for use in constructing an SQL query.
lock( $lockName, $method, $timeout=5)
Acquire a named lock.
array[] $mTrxPreCommitCallbacks
List of (callable, method name)
pendingWriteAndCallbackCallers()
assertOpen()
Make sure isOpen() returns true as a sanity check.
update( $table, $values, $conds, $fname=__METHOD__, $options=[])
UPDATE wrapper.
runTransactionListenerCallbacks( $trigger)
Actually run any "transaction listener" callbacks.
getWikiID()
Alias for getDomainID()
makeSelectOptions( $options)
Returns an optional USE INDEX clause to go after the table, and a string to go at the end of the quer...
array $mTrxAtomicLevels
Array of levels of atomicity within transactions.
string[] $mTrxWriteCallers
Track the write query callers of the current transaction.
rollback( $fname=__METHOD__, $flush='')
Rollback a transaction previously started using begin().
begin( $fname=__METHOD__, $mode=self::TRANSACTION_EXPLICIT)
Begin a transaction.
listViews( $prefix=null, $fname=__METHOD__)
Lists all the VIEWs in the database.
setLazyMasterHandle(IDatabase $conn)
Set a lazy-connecting DB handle to the master DB (for replication status purposes)
bool null $mDefaultBigSelects
connectionErrorLogger( $errno, $errstr)
This method should not be used outside of Database classes.
freeResult( $res)
Free a result object returned by query() or select().
getSchemaVars()
Get schema variables.
clearFlag( $flag, $remember=self::REMEMBER_NOTHING)
Clear a flag for this connection.
lockIsFree( $lockName, $method)
Check to see if a named lock is available (non-blocking)
doneWrites()
Returns true if the connection may have been used for write queries.
canRecoverFromDisconnect( $sql, $priorWritesPending)
getMasterPos()
Get the position of this master.
callable[] $mTrxRecurringCallbacks
Map of (name => callable)
getLogContext(array $extras=[])
Create a log context to pass to PSR-3 logger functions.
selectRowCount( $tables, $vars=' *', $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
Get the number of rows in dataset.
endAtomic( $fname=__METHOD__)
Ends an atomic section of SQL statements.
timestampOrNull( $ts=null)
Convert a timestamp in one of the formats accepted by wfTimestamp() to the format used for inserting ...
insertSelect( $destTable, $srcTable, $varMap, $conds, $fname=__METHOD__, $insertOptions=[], $selectOptions=[])
INSERT SELECT wrapper.
const DEADLOCK_DELAY_MIN
Minimum time to wait before retry, in microseconds.
listTables( $prefix=null, $fname=__METHOD__)
List all tables on the database.
string $mLastQuery
SQL query.
namedLocksEnqueue()
Check to see if a named lock used by lock() use blocking queues.
streamStatementEnd(&$sql, &$newLine)
Called by sourceStream() to check if we've reached a statement end.
getServerUptime()
Determines how long the server has been up.
strencode( $s)
Wrapper for addslashes()
deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname=__METHOD__)
DELETE where the condition is a join.
float $mTrxWriteDuration
Seconds spent in write queries for the current transaction.
wasConnectionError( $errno)
Do not use this method outside of Database/DBError classes.
int[] $priorFlags
Prior mFlags values.
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by wfTimestamp() to the format used for inserting ...
textFieldSize( $table, $field)
Returns the size of a text field, or -1 for "unlimited".
doQuery( $sql)
The DBMS-dependent part of query()
Simple store for keeping values in an associative array for the current process.
Used by Database::buildLike() to represent characters that have special meaning in SQL LIKE clauses a...
Result wrapper for grabbing data queried from an IDatabase object.
Helper class that detects high-contention DB queries via profiling calls.
We use the convention $dbr for read and $dbw for write to help you keep track of whether the database object is a the world will explode Or to be a subsequent write query which succeeded on the master may fail when replicated to the slave due to a unique key collision Replication on the slave will stop and it may take hours to repair the database and get it back online Setting read_only in my cnf on the slave will avoid this but given the dire we prefer to have as many checks as possible We provide a but the wrapper functions like select() and insert() are usually more convenient. They take care of things like table prefixes and escaping for you. If you really need to make your own SQL
We use the convention $dbr for read and $dbw for write to help you keep track of whether the database object is a the world will explode Or to be a subsequent write query which succeeded on the master may fail when replicated to the slave due to a unique key collision Replication on the slave will stop and it may take hours to repair the database and get it back online Setting read_only in my cnf on the slave will avoid this but given the dire we prefer to have as many checks as possible We provide a but the wrapper functions like please read the documentation for tableName() and addQuotes(). You will need both of them. ------------------------------------------------------------------------ Basic query optimisation ------------------------------------------------------------------------ MediaWiki developers who need to write DB queries should have some understanding of databases and the performance issues associated with them. Patches containing unacceptably slow features will not be accepted. Unindexed queries are generally not welcome in MediaWiki
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
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
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
the array() calling protocol came about after MediaWiki 1.4rc1.
static configuration should be added through ResourceLoaderGetConfigVars instead & $vars
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
namespace are movable Hooks may change this value to override the return value of MWNamespace::isMovable(). 'NewDifferenceEngine' do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached one of or reset my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled allows for interception of redirect as a string mapping parameter names to values & $type
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist & $tables
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
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 incomplete not yet checked for validity & $retval
presenting them properly to the user as errors is done by the caller return true use this to change the list i e rollback
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
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
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
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and insert
Allows to change the fields on the form that will be generated $name
processing should stop and the error should be shown to the user * false
returning false will NOT prevent logging $e
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
An object representing a master or replica DB position in a replicated setup.
Basic database interface for live and lazy-loaded relation database handles.
fetchObject( $res)
Fetch the next row from the given result object, in object form.
lastErrno()
Get the last error number.
open( $server, $user, $password, $dbName)
Open a connection to the database.
lastError()
Get a description of the last error.
fieldInfo( $table, $field)
mysql_fetch_field() wrapper Returns false if the field doesn't exist
fetchRow( $res)
Fetch the next row from the given result object, in associative array form.
affectedRows()
Get the number of rows affected by the last write query.
numRows( $res)
Get the number of rows in a result object.
getServerVersion()
A string describing the current software version, like from mysql_get_server_info().
Advanced database interface for IDatabase handles that include maintenance methods.
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)