MediaWiki  master
DatabaseMysqlBase.php
Go to the documentation of this file.
1 <?php
20 namespace Wikimedia\Rdbms;
21 
22 use RuntimeException;
26 
41 abstract class DatabaseMysqlBase extends Database {
43  protected $sslKeyPath;
45  protected $sslCertPath;
47  protected $sslCAFile;
49  protected $sslCAPath;
55  protected $sslCiphers;
57  protected $utf8Mode;
58 
60  protected $platform;
61 
64 
84  public function __construct( array $params ) {
85  foreach ( [ 'KeyPath', 'CertPath', 'CAFile', 'CAPath', 'Ciphers' ] as $name ) {
86  $var = "ssl{$name}";
87  if ( isset( $params[$var] ) ) {
88  $this->$var = $params[$var];
89  }
90  }
91  $this->utf8Mode = !empty( $params['utf8Mode'] );
92  parent::__construct( $params );
93  $this->platform = new MySQLPlatform(
94  $this,
95  $this->logger,
96  $this->currentDomain,
97  $this->errorLogger
98  );
99  $this->replicationReporter = new MysqlReplicationReporter(
100  $params['topologyRole'],
101  $this->logger,
102  $params['srvCache'],
103  $params['lagDetectionMethod'] ?? 'Seconds_Behind_Master',
104  $params['lagDetectionOptions'] ?? [],
105  !empty( $params['useGTIDs' ] )
106  );
107  }
108 
112  public function getType() {
113  return 'mysql';
114  }
115 
116  protected function open( $server, $user, $password, $db, $schema, $tablePrefix ) {
117  $this->close( __METHOD__ );
118 
119  if ( $schema !== null ) {
120  throw $this->newExceptionAfterConnectError( "Got schema '$schema'; not supported." );
121  }
122 
123  $this->installErrorHandler();
124  try {
125  $this->conn = $this->mysqlConnect( $server, $user, $password, $db );
126  } catch ( RuntimeException $e ) {
127  $this->restoreErrorHandler();
128  throw $this->newExceptionAfterConnectError( $e->getMessage() );
129  }
130  $error = $this->restoreErrorHandler();
131 
132  if ( !$this->conn ) {
133  throw $this->newExceptionAfterConnectError( $error ?: $this->lastError() );
134  }
135 
136  try {
137  $this->currentDomain = new DatabaseDomain(
138  $db && strlen( $db ) ? $db : null,
139  null,
140  $tablePrefix
141  );
142  $this->platform->setPrefix( $tablePrefix );
143 
144  $set = [];
145  if ( !$this->flagsHolder->getFlag( self::DBO_GAUGE ) ) {
146  // Abstract over any excessive MySQL defaults
147  $set[] = 'group_concat_max_len = 262144';
148  // Set any custom settings defined by site config
149  // https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html
150  foreach ( $this->connectionVariables as $var => $val ) {
151  // Escape strings but not numbers to avoid MySQL complaining
152  if ( !is_int( $val ) && !is_float( $val ) ) {
153  $val = $this->addQuotes( $val );
154  }
155  $set[] = $this->platform->addIdentifierQuotes( $var ) . ' = ' . $val;
156  }
157  }
158 
159  if ( $set ) {
160  $sql = 'SET ' . implode( ', ', $set );
161  $flags = self::QUERY_NO_RETRY | self::QUERY_CHANGE_TRX;
162  $query = new Query( $sql, $flags, 'SET' );
163  // Avoid using query() so that replaceLostConnection() does not throw
164  // errors if the transaction status is STATUS_TRX_ERROR
165  $qs = $this->executeQuery( $query, __METHOD__, $flags );
166  if ( $qs->res === false ) {
167  $this->reportQueryError( $qs->message, $qs->code, $sql, __METHOD__ );
168  }
169  }
170  } catch ( RuntimeException $e ) {
171  throw $this->newExceptionAfterConnectError( $e->getMessage() );
172  }
173  }
174 
175  protected function doSelectDomain( DatabaseDomain $domain ) {
176  if ( $domain->getSchema() !== null ) {
177  throw new DBExpectedError(
178  $this,
179  __CLASS__ . ": domain '{$domain->getId()}' has a schema component"
180  );
181  }
182 
183  $database = $domain->getDatabase();
184  // A null database means "don't care" so leave it as is and update the table prefix
185  if ( $database === null ) {
186  $this->currentDomain = new DatabaseDomain(
187  $this->currentDomain->getDatabase(),
188  null,
189  $domain->getTablePrefix()
190  );
191  $this->platform->setPrefix( $domain->getTablePrefix() );
192 
193  return true;
194  }
195 
196  if ( $database !== $this->getDBname() ) {
197  $sql = 'USE ' . $this->addIdentifierQuotes( $database );
198  $query = new Query( $sql, self::QUERY_CHANGE_TRX, 'USE' );
199  $qs = $this->executeQuery( $query, __METHOD__, self::QUERY_CHANGE_TRX );
200  if ( $qs->res === false ) {
201  $this->reportQueryError( $qs->message, $qs->code, $sql, __METHOD__ );
202  return false; // unreachable
203  }
204  }
205 
206  // Update that domain fields on success (no exception thrown)
207  $this->currentDomain = $domain;
208  $this->platform->setPrefix( $domain->getTablePrefix() );
209 
210  return true;
211  }
212 
223  abstract protected function mysqlConnect( $server, $user, $password, $db );
224 
228  public function lastError() {
229  if ( $this->conn ) {
230  // Even if it's non-zero, it can still be invalid
231  $error = $this->mysqlError( $this->conn );
232  if ( !$error ) {
233  $error = $this->mysqlError();
234  }
235  } else {
236  $error = $this->mysqlError() ?: $this->lastConnectError;
237  }
238 
239  return $error;
240  }
241 
248  abstract protected function mysqlError( $conn = null );
249 
250  protected function isInsertSelectSafe( array $insertOptions, array $selectOptions ) {
251  $row = $this->replicationReporter->getReplicationSafetyInfo( $this );
252  // For row-based-replication, the resulting changes will be relayed, not the query
253  if ( $row->binlog_format === 'ROW' ) {
254  return true;
255  }
256  // LIMIT requires ORDER BY on a unique key or it is non-deterministic
257  if ( isset( $selectOptions['LIMIT'] ) ) {
258  return false;
259  }
260  // In MySQL, an INSERT SELECT is only replication safe with row-based
261  // replication or if innodb_autoinc_lock_mode is 0. When those
262  // conditions aren't met, use non-native mode.
263  // While we could try to determine if the insert is safe anyway by
264  // checking if the target table has an auto-increment column that
265  // isn't set in $varMap, that seems unlikely to be worth the extra
266  // complexity.
267  return (
268  in_array( 'NO_AUTO_COLUMNS', $insertOptions ) ||
269  (int)$row->innodb_autoinc_lock_mode === 0
270  );
271  }
272 
286  public function estimateRowCount(
287  $tables,
288  $var = '*',
289  $conds = '',
290  $fname = __METHOD__,
291  $options = [],
292  $join_conds = []
293  ) {
294  $conds = $this->platform->normalizeConditions( $conds, $fname );
295  $column = $this->platform->extractSingleFieldFromList( $var );
296  if ( is_string( $column ) && !in_array( $column, [ '*', '1' ] ) ) {
297  $conds[] = "$column IS NOT NULL";
298  }
299 
300  $options['EXPLAIN'] = true;
301  $res = $this->select( $tables, $var, $conds, $fname, $options, $join_conds );
302  if ( $res === false ) {
303  return false;
304  }
305  if ( !$res->numRows() ) {
306  return 0;
307  }
308 
309  $rows = 1;
310  foreach ( $res as $plan ) {
311  $rows *= $plan->rows > 0 ? $plan->rows : 1; // avoid resetting to zero
312  }
313 
314  return (int)$rows;
315  }
316 
317  public function tableExists( $table, $fname = __METHOD__ ) {
318  // Split database and table into proper variables as Database::tableName() returns
319  // shared tables prefixed with their database, which do not work in SHOW TABLES statements
320  [ $database, , $prefix, $table ] = $this->platform->qualifiedTableComponents( $table );
321  $tableName = "{$prefix}{$table}";
322 
323  if ( isset( $this->sessionTempTables[$tableName] ) ) {
324  return true; // already known to exist and won't show in SHOW TABLES anyway
325  }
326 
327  // We can't use buildLike() here, because it specifies an escape character
328  // other than the backslash, which is the only one supported by SHOW TABLES
329  // TODO: Avoid using platform's internal methods
330  $encLike = $this->platform->escapeLikeInternal( $tableName, '\\' );
331 
332  // If the database has been specified (such as for shared tables), use "FROM"
333  if ( $database !== '' ) {
334  $encDatabase = $this->platform->addIdentifierQuotes( $database );
335  $sql = "SHOW TABLES FROM $encDatabase LIKE '$encLike'";
336  } else {
337  $sql = "SHOW TABLES LIKE '$encLike'";
338  }
339 
340  $query = new Query( $sql, self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE, 'SHOW', $table );
341  $res = $this->query( $query, $fname );
342 
343  return $res->numRows() > 0;
344  }
345 
351  public function fieldInfo( $table, $field ) {
352  $query = new Query(
353  "SELECT * FROM " . $this->tableName( $table ) . " LIMIT 1",
354  self::QUERY_SILENCE_ERRORS | self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE,
355  'SELECT',
356  $table
357  );
358  $res = $this->query( $query, __METHOD__ );
359  if ( !$res ) {
360  return false;
361  }
363  '@phan-var MysqliResultWrapper $res';
364  return $res->getInternalFieldInfo( $field );
365  }
366 
376  public function indexInfo( $table, $index, $fname = __METHOD__ ) {
377  # https://dev.mysql.com/doc/mysql/en/SHOW_INDEX.html
378  $index = $this->platform->indexName( $index );
379  $query = new Query(
380  'SHOW INDEX FROM ' . $this->tableName( $table ),
381  self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE,
382  'SHOW',
383  $table
384  );
385  $res = $this->query( $query, $fname );
386 
387  if ( !$res ) {
388  return null;
389  }
390 
391  $result = [];
392 
393  foreach ( $res as $row ) {
394  if ( $row->Key_name == $index ) {
395  $result[] = $row;
396  }
397  }
398 
399  return $result ?: false;
400  }
401 
406  public function strencode( $s ) {
407  return $this->mysqlRealEscapeString( $s );
408  }
409 
416  abstract protected function mysqlRealEscapeString( $s );
417 
418  public function serverIsReadOnly() {
419  // Avoid SHOW to avoid internal temporary tables
420  $flags = self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE;
421  $query = new Query( "SELECT @@GLOBAL.read_only AS Value", $flags, 'SELECT' );
422  $res = $this->query( $query, __METHOD__ );
423  $row = $res->fetchObject();
424 
425  return $row && (bool)$row->Value;
426  }
427 
431  public function getSoftwareLink() {
432  [ $variant ] = $this->getMySqlServerVariant();
433  if ( $variant === 'MariaDB' ) {
434  return '[{{int:version-db-mariadb-url}} MariaDB]';
435  }
436 
437  return '[{{int:version-db-mysql-url}} MySQL]';
438  }
439 
443  protected function getMySqlServerVariant() {
444  $version = $this->getServerVersion();
445 
446  // MariaDB includes its name in its version string; this is how MariaDB's version of
447  // the mysql command-line client identifies MariaDB servers.
448  // https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_version
449  // https://mariadb.com/kb/en/version/
450  $parts = explode( '-', $version, 2 );
451  $number = $parts[0];
452  $suffix = $parts[1] ?? '';
453  if ( strpos( $suffix, 'MariaDB' ) !== false || strpos( $suffix, '-maria-' ) !== false ) {
454  $vendor = 'MariaDB';
455  } else {
456  $vendor = 'MySQL';
457  }
458 
459  return [ $vendor, $number ];
460  }
461 
465  public function getServerVersion() {
466  $cache = $this->srvCache;
467  $fname = __METHOD__;
468 
469  return $cache->getWithSetCallback(
470  $cache->makeGlobalKey( 'mysql-server-version', $this->getServerName() ),
471  $cache::TTL_HOUR,
472  function () use ( $fname ) {
473  // Not using mysql_get_server_info() or similar for consistency: in the handshake,
474  // MariaDB 10 adds the prefix "5.5.5-", and only some newer client libraries strip
475  // it off (see RPL_VERSION_HACK in include/mysql_com.h).
476  return $this->selectField( '', 'VERSION()', '', $fname );
477  }
478  );
479  }
480 
484  public function setSessionOptions( array $options ) {
485  $sqlAssignments = [];
486 
487  if ( isset( $options['connTimeout'] ) ) {
488  $encTimeout = (int)$options['connTimeout'];
489  $sqlAssignments[] = "net_read_timeout=$encTimeout";
490  $sqlAssignments[] = "net_write_timeout=$encTimeout";
491  }
492  if ( isset( $options['groupConcatMaxLen'] ) ) {
493  $maxLength = (int)$options['groupConcatMaxLen'];
494  $sqlAssignments[] = "group_concat_max_len=$maxLength";
495  }
496 
497  if ( $sqlAssignments ) {
498  $query = new Query(
499  'SET ' . implode( ', ', $sqlAssignments ),
500  self::QUERY_CHANGE_TRX | self::QUERY_CHANGE_NONE,
501  'SET'
502  );
503  $this->query( $query, __METHOD__ );
504  }
505  }
506 
512  public function streamStatementEnd( &$sql, &$newLine ) {
513  if ( preg_match( '/^DELIMITER\s+(\S+)/i', $newLine, $m ) ) {
514  $this->delimiter = $m[1];
515  $newLine = '';
516  }
517 
518  return parent::streamStatementEnd( $sql, $newLine );
519  }
520 
521  public function doLockIsFree( string $lockName, string $method ) {
522  $query = new Query( $this->platform->lockIsFreeSQLText( $lockName ), self::QUERY_CHANGE_LOCKS, 'SELECT' );
523  $res = $this->query( $query, $method );
524  $row = $res->fetchObject();
525 
526  return ( $row->unlocked == 1 );
527  }
528 
529  public function doLock( string $lockName, string $method, int $timeout ) {
530  $query = new Query( $this->platform->lockSQLText( $lockName, $timeout ), self::QUERY_CHANGE_LOCKS, 'SELECT' );
531  $res = $this->query( $query, $method );
532  $row = $res->fetchObject();
533 
534  return ( $row->acquired !== null ) ? (float)$row->acquired : null;
535  }
536 
537  public function doUnlock( string $lockName, string $method ) {
538  $query = new Query( $this->platform->unlockSQLText( $lockName ), self::QUERY_CHANGE_LOCKS, 'SELECT' );
539  $res = $this->query( $query, $method );
540  $row = $res->fetchObject();
541 
542  return ( $row->released == 1 );
543  }
544 
545  public function namedLocksEnqueue() {
546  return true;
547  }
548 
549  protected function doFlushSession( $fname ) {
550  // Note that RELEASE_ALL_LOCKS() is not supported well enough to use here.
551  // https://mariadb.com/kb/en/release_all_locks/
552  $releaseLockFields = [];
553  foreach ( $this->sessionNamedLocks as $name => $info ) {
554  $encName = $this->addQuotes( $this->platform->makeLockName( $name ) );
555  $releaseLockFields[] = "RELEASE_LOCK($encName)";
556  }
557  if ( $releaseLockFields ) {
558  $sql = 'SELECT ' . implode( ',', $releaseLockFields );
559  $flags = self::QUERY_CHANGE_LOCKS | self::QUERY_NO_RETRY;
560  $query = new Query( $sql, $flags, 'SELECT' );
561  $qs = $this->executeQuery( $query, __METHOD__, $flags );
562  if ( $qs->res === false ) {
563  $this->reportQueryError( $qs->message, $qs->code, $sql, $fname, true );
564  }
565  }
566  }
567 
568  protected function doUpsert(
569  string $table,
570  array $rows,
571  array $identityKey,
572  array $set,
573  string $fname
574  ) {
575  $encTable = $this->tableName( $table );
576  [ $sqlColumns, $sqlTuples ] = $this->platform->makeInsertLists( $rows );
577  $sqlColumnAssignments = $this->makeList( $set, self::LIST_SET );
578  // No need to expose __NEW.* since buildExcludedValue() uses VALUES(column)
579 
580  // https://mariadb.com/kb/en/insert-on-duplicate-key-update/
581  // https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html
582  $sql =
583  "INSERT INTO $encTable " .
584  "($sqlColumns) VALUES $sqlTuples " .
585  "ON DUPLICATE KEY UPDATE $sqlColumnAssignments";
586  $query = new Query( $sql, self::QUERY_CHANGE_ROWS, 'INSERT', $table );
587  $this->query( $query, $fname );
588  }
589 
590  protected function doReplace( $table, array $identityKey, array $rows, $fname ) {
591  $encTable = $this->tableName( $table );
592  [ $sqlColumns, $sqlTuples ] = $this->platform->makeInsertLists( $rows );
593  // https://dev.mysql.com/doc/refman/8.0/en/replace.html
594  $sql = "REPLACE INTO $encTable ($sqlColumns) VALUES $sqlTuples";
595  // Note that any auto-increment columns on conflicting rows will be reassigned
596  // due to combined DELETE+INSERT semantics. This will be reflected in insertId().
597  $query = new Query( $sql, self::QUERY_CHANGE_ROWS, 'REPLACE', $table );
598  $this->query( $query, $fname );
599  }
600 
606  public function wasDeadlock() {
607  return $this->lastErrno() == 1213;
608  }
609 
615  public function wasReadOnlyError() {
616  return $this->lastErrno() == 1223 ||
617  ( $this->lastErrno() == 1290 && strpos( $this->lastError(), '--read-only' ) !== false );
618  }
619 
620  protected function isConnectionError( $errno ) {
621  // https://mariadb.com/kb/en/mariadb-error-codes/
622  // https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html
623  // https://dev.mysql.com/doc/mysql-errors/8.0/en/client-error-reference.html
624  return in_array( $errno, [ 2013, 2006, 2003, 1927, 1053 ], true );
625  }
626 
627  protected function isQueryTimeoutError( $errno ) {
628  // https://mariadb.com/kb/en/mariadb-error-codes/
629  // https://dev.mysql.com/doc/refman/8.0/en/client-error-reference.html
630  // https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html
631  return in_array( $errno, [ 3024, 2062, 1969, 1028 ], true );
632  }
633 
634  protected function isKnownStatementRollbackError( $errno ) {
635  // https://mariadb.com/kb/en/mariadb-error-codes/
636  // https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html
637  return in_array(
638  $errno,
639  [ 3024, 1969, 1022, 1062, 1216, 1217, 1137, 1146, 1051, 1054 ],
640  true
641  );
642  }
643 
651  public function duplicateTableStructure(
652  $oldName, $newName, $temporary = false, $fname = __METHOD__
653  ) {
654  $tmp = $temporary ? 'TEMPORARY ' : '';
655  $newNameQuoted = $this->addIdentifierQuotes( $newName );
656  $oldNameQuoted = $this->addIdentifierQuotes( $oldName );
657 
658  $query = new Query(
659  "CREATE $tmp TABLE $newNameQuoted (LIKE $oldNameQuoted)",
660  self::QUERY_PSEUDO_PERMANENT | self::QUERY_CHANGE_SCHEMA,
661  'CREATE',
662  [ $oldName, $newName ]
663  );
664  return $this->query( $query, $fname );
665  }
666 
674  public function listTables( $prefix = null, $fname = __METHOD__ ) {
675  $query = new Query( "SHOW TABLES", self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE, 'SHOW' );
676  $result = $this->query( $query, $fname );
677 
678  $endArray = [];
679 
680  foreach ( $result as $table ) {
681  $vars = get_object_vars( $table );
682  $table = array_pop( $vars );
683 
684  if ( !$prefix || strpos( $table, $prefix ) === 0 ) {
685  $endArray[] = $table;
686  }
687  }
688 
689  return $endArray;
690  }
691 
701  public function listViews( $prefix = null, $fname = __METHOD__ ) {
702  // The name of the column containing the name of the VIEW
703  $propertyName = 'Tables_in_' . $this->getDBname();
704  $query = new Query(
705  'SHOW FULL TABLES WHERE TABLE_TYPE = "VIEW"',
706  self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE,
707  'SHOW'
708  );
709  // Query for the VIEWS
710  $res = $this->query( $query, $fname );
711 
712  $allViews = [];
713  foreach ( $res as $row ) {
714  $allViews[] = $row->$propertyName;
715  }
716 
717  if ( $prefix === null || $prefix === '' ) {
718  return $allViews;
719  }
720 
721  $filteredViews = [];
722  foreach ( $allViews as $viewName ) {
723  // Does the name of this VIEW start with the table-prefix?
724  if ( strpos( $viewName, $prefix ) === 0 ) {
725  $filteredViews[] = $viewName;
726  }
727  }
728 
729  return $filteredViews;
730  }
731 
732  public function selectSQLText(
733  $table,
734  $vars,
735  $conds = '',
736  $fname = __METHOD__,
737  $options = [],
738  $join_conds = []
739  ) {
740  $sql = parent::selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
741  // https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html
742  // https://mariadb.com/kb/en/library/aborting-statements/
743  $timeoutMsec = intval( $options['MAX_EXECUTION_TIME'] ?? 0 );
744  if ( $timeoutMsec > 0 ) {
745  [ $vendor, $number ] = $this->getMySqlServerVariant();
746  if ( $vendor === 'MariaDB' && version_compare( $number, '10.1.2', '>=' ) ) {
747  $timeoutSec = $timeoutMsec / 1000;
748  $sql = "SET STATEMENT max_statement_time=$timeoutSec FOR $sql";
749  } elseif ( $vendor === 'MySQL' && version_compare( $number, '5.7.0', '>=' ) ) {
750  $sql = preg_replace(
751  '/^SELECT(?=\s)/',
752  "SELECT /*+ MAX_EXECUTION_TIME($timeoutMsec)*/",
753  $sql
754  );
755  }
756  }
757 
758  return $sql;
759  }
760 }
761 
765 class_alias( DatabaseMysqlBase::class, 'DatabaseMysqlBase' );
const LIST_SET
Definition: Defines.php:44
getWithSetCallback( $key, $exptime, $callback, $flags=0)
Get an item, regenerating and setting it if not found.
Definition: BagOStuff.php:212
Base class for the more common types of database errors.
Class to handle database/schema/prefix specifications for IDatabase.
MySQL database abstraction layer.
doSelectDomain(DatabaseDomain $domain)
__construct(array $params)
Additional $params include:
string null $sslCiphers
Open SSL cipher list string.
duplicateTableStructure( $oldName, $newName, $temporary=false, $fname=__METHOD__)
doLock(string $lockName, string $method, int $timeout)
namedLocksEnqueue()
Check to see if a named lock used by lock() use blocking queues.bool 1.26Stability: stableto override
isInsertSelectSafe(array $insertOptions, array $selectOptions)
doUnlock(string $lockName, string $method)
wasReadOnlyError()
Determines if the last failure was due to the database being read-only.
doUpsert(string $table, array $rows, array $identityKey, array $set, string $fname)
Perform an UPSERT query.
isConnectionError( $errno)
Do not use this method outside of Database/DBError classes.
doFlushSession( $fname)
Reset the server-side session state for named locks and table locks.
serverIsReadOnly()
bool Whether this DB server is running in server-side read-only mode If an error occurs,...
indexInfo( $table, $index, $fname=__METHOD__)
Get information about an index into an object Returns false if the index does not exist.
selectSQLText( $table, $vars, $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
Take the same arguments as IDatabase::select() and return the SQL it would use.
mysqlError( $conn=null)
Returns the text of the error message from previous MySQL operation.
open( $server, $user, $password, $db, $schema, $tablePrefix)
Open a new connection to the database (closing any existing one)
bool $utf8Mode
Use experimental UTF-8 transmission encoding.
MysqlReplicationReporter $replicationReporter
doLockIsFree(string $lockName, string $method)
listTables( $prefix=null, $fname=__METHOD__)
List all tables on the database.
mysqlRealEscapeString( $s)
Escape special characters in a string for use in an SQL statement.
doReplace( $table, array $identityKey, array $rows, $fname)
estimateRowCount( $tables, $var=' *', $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
Estimate rows in dataset Returns estimated count, based on EXPLAIN output Takes same arguments as Dat...
mysqlConnect( $server, $user, $password, $db)
Open a connection to a MySQL server.
isQueryTimeoutError( $errno)
Checks whether the cause of the error is detected to be a timeout.
listViews( $prefix=null, $fname=__METHOD__)
Lists VIEWs in the database.
tableExists( $table, $fname=__METHOD__)
Query whether a given table exists.
wasDeadlock()
Determines if the last failure was due to a deadlock.
Relational database abstraction object.
Definition: Database.php:45
restoreErrorHandler()
Restore the previous error handler and return the last PHP error for this DB.
Definition: Database.php:506
object resource null $conn
Database connection.
Definition: Database.php:68
addQuotes( $s)
Escape and quote a raw value string for use in a SQL query.string
Definition: Database.php:1624
newExceptionAfterConnectError( $error)
Definition: Database.php:1306
string $lastConnectError
Last error during connection; empty string if none.
Definition: Database.php:113
select( $table, $vars, $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
Execute a SELECT query constructed using the various parameters provided.
Definition: Database.php:1412
installErrorHandler()
Set a custom error handler for logging errors during database connection.
Definition: Database.php:495
tableName( $name, $format='quoted')
Format a table name ready for use in constructing an SQL query.
Definition: Database.php:3612
addIdentifierQuotes( $s)
Escape a SQL identifier (e.g.
Definition: Database.php:3624
executeQuery( $sql, $fname, $flags)
Execute a query without enforcing public (non-Database) caller restrictions.
Definition: Database.php:765
close( $fname=__METHOD__)
Close the database connection.
Definition: Database.php:558
reportQueryError( $error, $errno, $sql, $fname, $ignore=false)
Report a query error.
Definition: Database.php:1249
makeList(array $a, $mode=self::LIST_COMMA)
Makes an encoded list of strings from an array.
Definition: Database.php:3564
BagOStuff $srvCache
APC cache.
Definition: Database.php:47
query( $sql, $fname=__METHOD__, $flags=0)
Run an SQL query statement and return the result.
Definition: Database.php:722
getDBname()
Get the current database name; null if there isn't one.
Definition: Database.php:1608
selectField( $table, $var, $cond='', $fname=__METHOD__, $options=[], $join_conds=[])
A SELECT wrapper which returns a single field from a single result row.
Definition: Database.php:1364
Holds information on Query to be executed.
Definition: Query.php:31
lastErrno()
Get the RDBMS-specific error code from the last attempted query statement.