MediaWiki  REL1_21
DatabaseMysql.php
Go to the documentation of this file.
00001 <?php
00031 class DatabaseMysql extends DatabaseBase {
00032 
00036         function getType() {
00037                 return 'mysql';
00038         }
00039 
00044         protected function doQuery( $sql ) {
00045                 if( $this->bufferResults() ) {
00046                         $ret = mysql_query( $sql, $this->mConn );
00047                 } else {
00048                         $ret = mysql_unbuffered_query( $sql, $this->mConn );
00049                 }
00050                 return $ret;
00051         }
00052 
00061         function open( $server, $user, $password, $dbName ) {
00062                 global $wgAllDBsAreLocalhost, $wgDBmysql5, $wgSQLMode;
00063                 wfProfileIn( __METHOD__ );
00064 
00065                 # Load mysql.so if we don't have it
00066                 wfDl( 'mysql' );
00067 
00068                 # Fail now
00069                 # Otherwise we get a suppressed fatal error, which is very hard to track down
00070                 if ( !function_exists( 'mysql_connect' ) ) {
00071                         throw new DBConnectionError( $this, "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n" );
00072                 }
00073 
00074                 # Debugging hack -- fake cluster
00075                 if ( $wgAllDBsAreLocalhost ) {
00076                         $realServer = 'localhost';
00077                 } else {
00078                         $realServer = $server;
00079                 }
00080                 $this->close();
00081                 $this->mServer = $server;
00082                 $this->mUser = $user;
00083                 $this->mPassword = $password;
00084                 $this->mDBname = $dbName;
00085 
00086                 $connFlags = 0;
00087                 if ( $this->mFlags & DBO_SSL ) {
00088                         $connFlags |= MYSQL_CLIENT_SSL;
00089                 }
00090                 if ( $this->mFlags & DBO_COMPRESS ) {
00091                         $connFlags |= MYSQL_CLIENT_COMPRESS;
00092                 }
00093 
00094                 wfProfileIn( "dbconnect-$server" );
00095 
00096                 # The kernel's default SYN retransmission period is far too slow for us,
00097                 # so we use a short timeout plus a manual retry. Retrying means that a small
00098                 # but finite rate of SYN packet loss won't cause user-visible errors.
00099                 $this->mConn = false;
00100                 if ( ini_get( 'mysql.connect_timeout' ) <= 3 ) {
00101                         $numAttempts = 2;
00102                 } else {
00103                         $numAttempts = 1;
00104                 }
00105                 $this->installErrorHandler();
00106                 for ( $i = 0; $i < $numAttempts && !$this->mConn; $i++ ) {
00107                         if ( $i > 1 ) {
00108                                 usleep( 1000 );
00109                         }
00110                         if ( $this->mFlags & DBO_PERSISTENT ) {
00111                                 $this->mConn = mysql_pconnect( $realServer, $user, $password, $connFlags );
00112                         } else {
00113                                 # Create a new connection...
00114                                 $this->mConn = mysql_connect( $realServer, $user, $password, true, $connFlags );
00115                         }
00116                         #if ( $this->mConn === false ) {
00117                                 #$iplus = $i + 1;
00118                                 #wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
00119                         #}
00120                 }
00121                 $error = $this->restoreErrorHandler();
00122 
00123                 wfProfileOut( "dbconnect-$server" );
00124 
00125                 # Always log connection errors
00126                 if ( !$this->mConn ) {
00127                         if ( !$error ) {
00128                                 $error = $this->lastError();
00129                         }
00130                         wfLogDBError( "Error connecting to {$this->mServer}: $error\n" );
00131                         wfDebug( "DB connection error\n" .
00132                                 "Server: $server, User: $user, Password: " .
00133                                 substr( $password, 0, 3 ) . "..., error: " . $error . "\n" );
00134 
00135                         wfProfileOut( __METHOD__ );
00136                         return $this->reportConnectionError( $error );
00137                 }
00138 
00139                 if ( $dbName != '' ) {
00140                         wfSuppressWarnings();
00141                         $success = mysql_select_db( $dbName, $this->mConn );
00142                         wfRestoreWarnings();
00143                         if ( !$success ) {
00144                                 wfLogDBError( "Error selecting database $dbName on server {$this->mServer}\n" );
00145                                 wfDebug( "Error selecting database $dbName on server {$this->mServer} " .
00146                                         "from client host " . wfHostname() . "\n" );
00147 
00148                                 wfProfileOut( __METHOD__ );
00149                                 return $this->reportConnectionError( "Error selecting database $dbName" );
00150                         }
00151                 }
00152 
00153                 // Tell the server we're communicating with it in UTF-8.
00154                 // This may engage various charset conversions.
00155                 if( $wgDBmysql5 ) {
00156                         $this->query( 'SET NAMES utf8', __METHOD__ );
00157                 } else {
00158                         $this->query( 'SET NAMES binary', __METHOD__ );
00159                 }
00160                 // Set SQL mode, default is turning them all off, can be overridden or skipped with null
00161                 if ( is_string( $wgSQLMode ) ) {
00162                         $mode = $this->addQuotes( $wgSQLMode );
00163                         $this->query( "SET sql_mode = $mode", __METHOD__ );
00164                 }
00165 
00166                 $this->mOpened = true;
00167                 wfProfileOut( __METHOD__ );
00168                 return true;
00169         }
00170 
00174         protected function closeConnection() {
00175                 return mysql_close( $this->mConn );
00176         }
00177 
00182         function freeResult( $res ) {
00183                 if ( $res instanceof ResultWrapper ) {
00184                         $res = $res->result;
00185                 }
00186                 wfSuppressWarnings();
00187                 $ok = mysql_free_result( $res );
00188                 wfRestoreWarnings();
00189                 if ( !$ok ) {
00190                         throw new DBUnexpectedError( $this, "Unable to free MySQL result" );
00191                 }
00192         }
00193 
00199         function fetchObject( $res ) {
00200                 if ( $res instanceof ResultWrapper ) {
00201                         $res = $res->result;
00202                 }
00203                 wfSuppressWarnings();
00204                 $row = mysql_fetch_object( $res );
00205                 wfRestoreWarnings();
00206 
00207                 $errno = $this->lastErrno();
00208                 // Unfortunately, mysql_fetch_object does not reset the last errno.
00209                 // Only check for CR_SERVER_LOST and CR_UNKNOWN_ERROR, as
00210                 // these are the only errors mysql_fetch_object can cause.
00211                 // See http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html.
00212                 if( $errno == 2000 || $errno == 2013 ) {
00213                         throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) );
00214                 }
00215                 return $row;
00216         }
00217 
00223         function fetchRow( $res ) {
00224                 if ( $res instanceof ResultWrapper ) {
00225                         $res = $res->result;
00226                 }
00227                 wfSuppressWarnings();
00228                 $row = mysql_fetch_array( $res );
00229                 wfRestoreWarnings();
00230 
00231                 $errno = $this->lastErrno();
00232                 // Unfortunately, mysql_fetch_array does not reset the last errno.
00233                 // Only check for CR_SERVER_LOST and CR_UNKNOWN_ERROR, as
00234                 // these are the only errors mysql_fetch_object can cause.
00235                 // See http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html.
00236                 if( $errno == 2000 || $errno == 2013 ) {
00237                         throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) );
00238                 }
00239                 return $row;
00240         }
00241 
00247         function numRows( $res ) {
00248                 if ( $res instanceof ResultWrapper ) {
00249                         $res = $res->result;
00250                 }
00251                 wfSuppressWarnings();
00252                 $n = mysql_num_rows( $res );
00253                 wfRestoreWarnings();
00254                 // Unfortunately, mysql_num_rows does not reset the last errno.
00255                 // We are not checking for any errors here, since
00256                 // these are no errors mysql_num_rows can cause.
00257                 // See http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html.
00258                 // See https://bugzilla.wikimedia.org/42430
00259                 return $n;
00260         }
00261 
00266         function numFields( $res ) {
00267                 if ( $res instanceof ResultWrapper ) {
00268                         $res = $res->result;
00269                 }
00270                 return mysql_num_fields( $res );
00271         }
00272 
00278         function fieldName( $res, $n ) {
00279                 if ( $res instanceof ResultWrapper ) {
00280                         $res = $res->result;
00281                 }
00282                 return mysql_field_name( $res, $n );
00283         }
00284 
00288         function insertId() {
00289                 return mysql_insert_id( $this->mConn );
00290         }
00291 
00297         function dataSeek( $res, $row ) {
00298                 if ( $res instanceof ResultWrapper ) {
00299                         $res = $res->result;
00300                 }
00301                 return mysql_data_seek( $res, $row );
00302         }
00303 
00307         function lastErrno() {
00308                 if ( $this->mConn ) {
00309                         return mysql_errno( $this->mConn );
00310                 } else {
00311                         return mysql_errno();
00312                 }
00313         }
00314 
00318         function lastError() {
00319                 if ( $this->mConn ) {
00320                         # Even if it's non-zero, it can still be invalid
00321                         wfSuppressWarnings();
00322                         $error = mysql_error( $this->mConn );
00323                         if ( !$error ) {
00324                                 $error = mysql_error();
00325                         }
00326                         wfRestoreWarnings();
00327                 } else {
00328                         $error = mysql_error();
00329                 }
00330                 if( $error ) {
00331                         $error .= ' (' . $this->mServer . ')';
00332                 }
00333                 return $error;
00334         }
00335 
00339         function affectedRows() {
00340                 return mysql_affected_rows( $this->mConn );
00341         }
00342 
00350         function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabaseMysql::replace' ) {
00351                 return $this->nativeReplace( $table, $rows, $fname );
00352         }
00353 
00366         public function estimateRowCount( $table, $vars = '*', $conds = '', $fname = 'DatabaseMysql::estimateRowCount', $options = array() ) {
00367                 $options['EXPLAIN'] = true;
00368                 $res = $this->select( $table, $vars, $conds, $fname, $options );
00369                 if ( $res === false ) {
00370                         return false;
00371                 }
00372                 if ( !$this->numRows( $res ) ) {
00373                         return 0;
00374                 }
00375 
00376                 $rows = 1;
00377                 foreach ( $res as $plan ) {
00378                         $rows *= $plan->rows > 0 ? $plan->rows : 1; // avoid resetting to zero
00379                 }
00380                 return $rows;
00381         }
00382 
00388         function fieldInfo( $table, $field ) {
00389                 $table = $this->tableName( $table );
00390                 $res = $this->query( "SELECT * FROM $table LIMIT 1", __METHOD__, true );
00391                 if ( !$res ) {
00392                         return false;
00393                 }
00394                 $n = mysql_num_fields( $res->result );
00395                 for( $i = 0; $i < $n; $i++ ) {
00396                         $meta = mysql_fetch_field( $res->result, $i );
00397                         if( $field == $meta->name ) {
00398                                 return new MySQLField( $meta );
00399                         }
00400                 }
00401                 return false;
00402         }
00403 
00413         function indexInfo( $table, $index, $fname = 'DatabaseMysql::indexInfo' ) {
00414                 # SHOW INDEX works in MySQL 3.23.58, but SHOW INDEXES does not.
00415                 # SHOW INDEX should work for 3.x and up:
00416                 # http://dev.mysql.com/doc/mysql/en/SHOW_INDEX.html
00417                 $table = $this->tableName( $table );
00418                 $index = $this->indexName( $index );
00419 
00420                 $sql = 'SHOW INDEX FROM ' . $table;
00421                 $res = $this->query( $sql, $fname );
00422 
00423                 if ( !$res ) {
00424                         return null;
00425                 }
00426 
00427                 $result = array();
00428 
00429                 foreach ( $res as $row ) {
00430                         if ( $row->Key_name == $index ) {
00431                                 $result[] = $row;
00432                         }
00433                 }
00434                 return empty( $result ) ? false : $result;
00435         }
00436 
00441         function selectDB( $db ) {
00442                 $this->mDBname = $db;
00443                 return mysql_select_db( $db, $this->mConn );
00444         }
00445 
00451         function strencode( $s ) {
00452                 $sQuoted = mysql_real_escape_string( $s, $this->mConn );
00453 
00454                 if( $sQuoted === false ) {
00455                         $this->ping();
00456                         $sQuoted = mysql_real_escape_string( $s, $this->mConn );
00457                 }
00458                 return $sQuoted;
00459         }
00460 
00468         public function addIdentifierQuotes( $s ) {
00469                 return "`" . $this->strencode( $s ) . "`";
00470         }
00471 
00476         public function isQuotedIdentifier( $name ) {
00477                 return strlen( $name ) && $name[0] == '`' && substr( $name, -1, 1 ) == '`';
00478         }
00479 
00483         function ping() {
00484                 $ping = mysql_ping( $this->mConn );
00485                 if ( $ping ) {
00486                         return true;
00487                 }
00488 
00489                 mysql_close( $this->mConn );
00490                 $this->mOpened = false;
00491                 $this->mConn = false;
00492                 $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
00493                 return true;
00494         }
00495 
00503         function getLag() {
00504                 if ( !is_null( $this->mFakeSlaveLag ) ) {
00505                         wfDebug( "getLag: fake slave lagged {$this->mFakeSlaveLag} seconds\n" );
00506                         return $this->mFakeSlaveLag;
00507                 }
00508 
00509                 return $this->getLagFromSlaveStatus();
00510         }
00511 
00515         function getLagFromSlaveStatus() {
00516                 $res = $this->query( 'SHOW SLAVE STATUS', __METHOD__ );
00517                 if ( !$res ) {
00518                         return false;
00519                 }
00520                 $row = $res->fetchObject();
00521                 if ( !$row ) {
00522                         return false;
00523                 }
00524                 if ( strval( $row->Seconds_Behind_Master ) === '' ) {
00525                         return false;
00526                 } else {
00527                         return intval( $row->Seconds_Behind_Master );
00528                 }
00529         }
00530 
00536         function getLagFromProcesslist() {
00537                 wfDeprecated( __METHOD__, '1.19' );
00538                 $res = $this->query( 'SHOW PROCESSLIST', __METHOD__ );
00539                 if( !$res ) {
00540                         return false;
00541                 }
00542                 # Find slave SQL thread
00543                 foreach( $res as $row ) {
00544                         /* This should work for most situations - when default db
00545                          * for thread is not specified, it had no events executed,
00546                          * and therefore it doesn't know yet how lagged it is.
00547                          *
00548                          * Relay log I/O thread does not select databases.
00549                          */
00550                         if ( $row->User == 'system user' &&
00551                                 $row->State != 'Waiting for master to send event' &&
00552                                 $row->State != 'Connecting to master' &&
00553                                 $row->State != 'Queueing master event to the relay log' &&
00554                                 $row->State != 'Waiting for master update' &&
00555                                 $row->State != 'Requesting binlog dump' &&
00556                                 $row->State != 'Waiting to reconnect after a failed master event read' &&
00557                                 $row->State != 'Reconnecting after a failed master event read' &&
00558                                 $row->State != 'Registering slave on master'
00559                                 ) {
00560                                 # This is it, return the time (except -ve)
00561                                 if ( $row->Time > 0x7fffffff ) {
00562                                         return false;
00563                                 } else {
00564                                         return $row->Time;
00565                                 }
00566                         }
00567                 }
00568                 return false;
00569         }
00570 
00578         function masterPosWait( DBMasterPos $pos, $timeout ) {
00579                 $fname = 'DatabaseBase::masterPosWait';
00580                 wfProfileIn( $fname );
00581 
00582                 # Commit any open transactions
00583                 if ( $this->mTrxLevel ) {
00584                         $this->commit( __METHOD__ );
00585                 }
00586 
00587                 if ( !is_null( $this->mFakeSlaveLag ) ) {
00588                         $status = parent::masterPosWait( $pos, $timeout );
00589                         wfProfileOut( $fname );
00590                         return $status;
00591                 }
00592 
00593                 # Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set
00594                 $encFile = $this->addQuotes( $pos->file );
00595                 $encPos = intval( $pos->pos );
00596                 $sql = "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)";
00597                 $res = $this->doQuery( $sql );
00598 
00599                 if ( $res && $row = $this->fetchRow( $res ) ) {
00600                         wfProfileOut( $fname );
00601                         return $row[0];
00602                 }
00603                 wfProfileOut( $fname );
00604                 return false;
00605         }
00606 
00612         function getSlavePos() {
00613                 if ( !is_null( $this->mFakeSlaveLag ) ) {
00614                         return parent::getSlavePos();
00615                 }
00616 
00617                 $res = $this->query( 'SHOW SLAVE STATUS', 'DatabaseBase::getSlavePos' );
00618                 $row = $this->fetchObject( $res );
00619 
00620                 if ( $row ) {
00621                         $pos = isset( $row->Exec_master_log_pos ) ? $row->Exec_master_log_pos : $row->Exec_Master_Log_Pos;
00622                         return new MySQLMasterPos( $row->Relay_Master_Log_File, $pos );
00623                 } else {
00624                         return false;
00625                 }
00626         }
00627 
00633         function getMasterPos() {
00634                 if ( $this->mFakeMaster ) {
00635                         return parent::getMasterPos();
00636                 }
00637 
00638                 $res = $this->query( 'SHOW MASTER STATUS', 'DatabaseBase::getMasterPos' );
00639                 $row = $this->fetchObject( $res );
00640 
00641                 if ( $row ) {
00642                         return new MySQLMasterPos( $row->File, $row->Position );
00643                 } else {
00644                         return false;
00645                 }
00646         }
00647 
00651         function getServerVersion() {
00652                 return mysql_get_server_info( $this->mConn );
00653         }
00654 
00659         function useIndexClause( $index ) {
00660                 return "FORCE INDEX (" . $this->indexName( $index ) . ")";
00661         }
00662 
00666         function lowPriorityOption() {
00667                 return 'LOW_PRIORITY';
00668         }
00669 
00673         public static function getSoftwareLink() {
00674                 return '[http://www.mysql.com/ MySQL]';
00675         }
00676 
00680         public function setSessionOptions( array $options ) {
00681                 if ( isset( $options['connTimeout'] ) ) {
00682                         $timeout = (int)$options['connTimeout'];
00683                         $this->query( "SET net_read_timeout=$timeout" );
00684                         $this->query( "SET net_write_timeout=$timeout" );
00685                 }
00686         }
00687 
00688         public function streamStatementEnd( &$sql, &$newLine ) {
00689                 if ( strtoupper( substr( $newLine, 0, 9 ) ) == 'DELIMITER' ) {
00690                         preg_match( '/^DELIMITER\s+(\S+)/', $newLine, $m );
00691                         $this->delimiter = $m[1];
00692                         $newLine = '';
00693                 }
00694                 return parent::streamStatementEnd( $sql, $newLine );
00695         }
00696 
00705         public function lockIsFree( $lockName, $method ) {
00706                 $lockName = $this->addQuotes( $lockName );
00707                 $result = $this->query( "SELECT IS_FREE_LOCK($lockName) AS lockstatus", $method );
00708                 $row = $this->fetchObject( $result );
00709                 return ( $row->lockstatus == 1 );
00710         }
00711 
00718         public function lock( $lockName, $method, $timeout = 5 ) {
00719                 $lockName = $this->addQuotes( $lockName );
00720                 $result = $this->query( "SELECT GET_LOCK($lockName, $timeout) AS lockstatus", $method );
00721                 $row = $this->fetchObject( $result );
00722 
00723                 if( $row->lockstatus == 1 ) {
00724                         return true;
00725                 } else {
00726                         wfDebug( __METHOD__ . " failed to acquire lock\n" );
00727                         return false;
00728                 }
00729         }
00730 
00737         public function unlock( $lockName, $method ) {
00738                 $lockName = $this->addQuotes( $lockName );
00739                 $result = $this->query( "SELECT RELEASE_LOCK($lockName) as lockstatus", $method );
00740                 $row = $this->fetchObject( $result );
00741                 return ( $row->lockstatus == 1 );
00742         }
00743 
00751         public function lockTables( $read, $write, $method, $lowPriority = true ) {
00752                 $items = array();
00753 
00754                 foreach( $write as $table ) {
00755                         $tbl = $this->tableName( $table ) .
00756                                         ( $lowPriority ? ' LOW_PRIORITY' : '' ) .
00757                                         ' WRITE';
00758                         $items[] = $tbl;
00759                 }
00760                 foreach( $read as $table ) {
00761                         $items[] = $this->tableName( $table ) . ' READ';
00762                 }
00763                 $sql = "LOCK TABLES " . implode( ',', $items );
00764                 $this->query( $sql, $method );
00765                 return true;
00766         }
00767 
00772         public function unlockTables( $method ) {
00773                 $this->query( "UNLOCK TABLES", $method );
00774                 return true;
00775         }
00776 
00783         public function getSearchEngine() {
00784                 return 'SearchMySQL';
00785         }
00786 
00791         public function setBigSelects( $value = true ) {
00792                 if ( $value === 'default' ) {
00793                         if ( $this->mDefaultBigSelects === null ) {
00794                                 # Function hasn't been called before so it must already be set to the default
00795                                 return;
00796                         } else {
00797                                 $value = $this->mDefaultBigSelects;
00798                         }
00799                 } elseif ( $this->mDefaultBigSelects === null ) {
00800                         $this->mDefaultBigSelects = (bool)$this->selectField( false, '@@sql_big_selects' );
00801                 }
00802                 $encValue = $value ? '1' : '0';
00803                 $this->query( "SET sql_big_selects=$encValue", __METHOD__ );
00804         }
00805 
00817         function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'DatabaseBase::deleteJoin' ) {
00818                 if ( !$conds ) {
00819                         throw new DBUnexpectedError( $this, 'DatabaseBase::deleteJoin() called with empty $conds' );
00820                 }
00821 
00822                 $delTable = $this->tableName( $delTable );
00823                 $joinTable = $this->tableName( $joinTable );
00824                 $sql = "DELETE $delTable FROM $delTable, $joinTable WHERE $delVar=$joinVar ";
00825 
00826                 if ( $conds != '*' ) {
00827                         $sql .= ' AND ' . $this->makeList( $conds, LIST_AND );
00828                 }
00829 
00830                 return $this->query( $sql, $fname );
00831         }
00832 
00838         function getServerUptime() {
00839                 $vars = $this->getMysqlStatus( 'Uptime' );
00840                 return (int)$vars['Uptime'];
00841         }
00842 
00848         function wasDeadlock() {
00849                 return $this->lastErrno() == 1213;
00850         }
00851 
00857         function wasLockTimeout() {
00858                 return $this->lastErrno() == 1205;
00859         }
00860 
00867         function wasErrorReissuable() {
00868                 return $this->lastErrno() == 2013 || $this->lastErrno() == 2006;
00869         }
00870 
00876         function wasReadOnlyError() {
00877                 return $this->lastErrno() == 1223 ||
00878                         ( $this->lastErrno() == 1290 && strpos( $this->lastError(), '--read-only' ) !== false );
00879         }
00880 
00887         function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabaseMysql::duplicateTableStructure' ) {
00888                 $tmp = $temporary ? 'TEMPORARY ' : '';
00889                 $newName = $this->addIdentifierQuotes( $newName );
00890                 $oldName = $this->addIdentifierQuotes( $oldName );
00891                 $query = "CREATE $tmp TABLE $newName (LIKE $oldName)";
00892                 $this->query( $query, $fname );
00893         }
00894 
00902         function listTables( $prefix = null, $fname = 'DatabaseMysql::listTables' ) {
00903                 $result = $this->query( "SHOW TABLES", $fname);
00904 
00905                 $endArray = array();
00906 
00907                 foreach( $result as $table ) {
00908                         $vars = get_object_vars( $table );
00909                         $table = array_pop( $vars );
00910 
00911                         if( !$prefix || strpos( $table, $prefix ) === 0 ) {
00912                                 $endArray[] = $table;
00913                         }
00914                 }
00915 
00916                 return $endArray;
00917         }
00918 
00924         public function dropTable( $tableName, $fName = 'DatabaseMysql::dropTable' ) {
00925                 if( !$this->tableExists( $tableName, $fName ) ) {
00926                         return false;
00927                 }
00928                 return $this->query( "DROP TABLE IF EXISTS " . $this->tableName( $tableName ), $fName );
00929         }
00930 
00934         protected function getDefaultSchemaVars() {
00935                 $vars = parent::getDefaultSchemaVars();
00936                 $vars['wgDBTableOptions'] = str_replace( 'TYPE', 'ENGINE', $GLOBALS['wgDBTableOptions'] );
00937                 $vars['wgDBTableOptions'] = str_replace( 'CHARSET=mysql4', 'CHARSET=binary', $vars['wgDBTableOptions'] );
00938                 return $vars;
00939         }
00940 
00947         function getMysqlStatus( $which = "%" ) {
00948                 $res = $this->query( "SHOW STATUS LIKE '{$which}'" );
00949                 $status = array();
00950 
00951                 foreach ( $res as $row ) {
00952                         $status[$row->Variable_name] = $row->Value;
00953                 }
00954 
00955                 return $status;
00956         }
00957 
00958 }
00959 
00964 class MySQLField implements Field {
00965         private $name, $tablename, $default, $max_length, $nullable,
00966                 $is_pk, $is_unique, $is_multiple, $is_key, $type;
00967 
00968         function __construct ( $info ) {
00969                 $this->name = $info->name;
00970                 $this->tablename = $info->table;
00971                 $this->default = $info->def;
00972                 $this->max_length = $info->max_length;
00973                 $this->nullable = !$info->not_null;
00974                 $this->is_pk = $info->primary_key;
00975                 $this->is_unique = $info->unique_key;
00976                 $this->is_multiple = $info->multiple_key;
00977                 $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
00978                 $this->type = $info->type;
00979         }
00980 
00984         function name() {
00985                 return $this->name;
00986         }
00987 
00991         function tableName() {
00992                 return $this->tableName;
00993         }
00994 
00998         function type() {
00999                 return $this->type;
01000         }
01001 
01005         function isNullable() {
01006                 return $this->nullable;
01007         }
01008 
01009         function defaultValue() {
01010                 return $this->default;
01011         }
01012 
01016         function isKey() {
01017                 return $this->is_key;
01018         }
01019 
01023         function isMultipleKey() {
01024                 return $this->is_multiple;
01025         }
01026 }
01027 
01028 class MySQLMasterPos implements DBMasterPos {
01029         var $file, $pos;
01030 
01031         function __construct( $file, $pos ) {
01032                 $this->file = $file;
01033                 $this->pos = $pos;
01034         }
01035 
01036         function __toString() {
01037                 return "{$this->file}/{$this->pos}";
01038         }
01039 }