MediaWiki
REL1_24
|
00001 <?php 00032 abstract class DatabaseMysqlBase extends DatabaseBase { 00034 protected $lastKnownSlavePos; 00035 00037 protected $mFakeSlaveLag = null; 00038 00039 protected $mFakeMaster = false; 00040 00044 function getType() { 00045 return 'mysql'; 00046 } 00047 00056 function open( $server, $user, $password, $dbName ) { 00057 global $wgAllDBsAreLocalhost, $wgSQLMode; 00058 wfProfileIn( __METHOD__ ); 00059 00060 # Debugging hack -- fake cluster 00061 if ( $wgAllDBsAreLocalhost ) { 00062 $realServer = 'localhost'; 00063 } else { 00064 $realServer = $server; 00065 } 00066 $this->close(); 00067 $this->mServer = $server; 00068 $this->mUser = $user; 00069 $this->mPassword = $password; 00070 $this->mDBname = $dbName; 00071 00072 wfProfileIn( "dbconnect-$server" ); 00073 00074 # The kernel's default SYN retransmission period is far too slow for us, 00075 # so we use a short timeout plus a manual retry. Retrying means that a small 00076 # but finite rate of SYN packet loss won't cause user-visible errors. 00077 $this->mConn = false; 00078 $this->installErrorHandler(); 00079 try { 00080 $this->mConn = $this->mysqlConnect( $realServer ); 00081 } catch ( Exception $ex ) { 00082 wfProfileOut( "dbconnect-$server" ); 00083 wfProfileOut( __METHOD__ ); 00084 $this->restoreErrorHandler(); 00085 throw $ex; 00086 } 00087 $error = $this->restoreErrorHandler(); 00088 00089 wfProfileOut( "dbconnect-$server" ); 00090 00091 # Always log connection errors 00092 if ( !$this->mConn ) { 00093 if ( !$error ) { 00094 $error = $this->lastError(); 00095 } 00096 wfLogDBError( "Error connecting to {$this->mServer}: $error" ); 00097 wfDebug( "DB connection error\n" . 00098 "Server: $server, User: $user, Password: " . 00099 substr( $password, 0, 3 ) . "..., error: " . $error . "\n" ); 00100 00101 wfProfileOut( __METHOD__ ); 00102 00103 $this->reportConnectionError( $error ); 00104 } 00105 00106 if ( $dbName != '' ) { 00107 wfSuppressWarnings(); 00108 $success = $this->selectDB( $dbName ); 00109 wfRestoreWarnings(); 00110 if ( !$success ) { 00111 wfLogDBError( "Error selecting database $dbName on server {$this->mServer}" ); 00112 wfDebug( "Error selecting database $dbName on server {$this->mServer} " . 00113 "from client host " . wfHostname() . "\n" ); 00114 00115 wfProfileOut( __METHOD__ ); 00116 00117 $this->reportConnectionError( "Error selecting database $dbName" ); 00118 } 00119 } 00120 00121 // Tell the server what we're communicating with 00122 if ( !$this->connectInitCharset() ) { 00123 $this->reportConnectionError( "Error setting character set" ); 00124 } 00125 00126 // Set SQL mode, default is turning them all off, can be overridden or skipped with null 00127 if ( is_string( $wgSQLMode ) ) { 00128 $mode = $this->addQuotes( $wgSQLMode ); 00129 // Use doQuery() to avoid opening implicit transactions (DBO_TRX) 00130 $success = $this->doQuery( "SET sql_mode = $mode", __METHOD__ ); 00131 if ( !$success ) { 00132 wfLogDBError( "Error setting sql_mode to $mode on server {$this->mServer}" ); 00133 wfProfileOut( __METHOD__ ); 00134 $this->reportConnectionError( "Error setting sql_mode to $mode" ); 00135 } 00136 } 00137 00138 $this->mOpened = true; 00139 wfProfileOut( __METHOD__ ); 00140 00141 return true; 00142 } 00143 00148 protected function connectInitCharset() { 00149 global $wgDBmysql5; 00150 00151 if ( $wgDBmysql5 ) { 00152 // Tell the server we're communicating with it in UTF-8. 00153 // This may engage various charset conversions. 00154 return $this->mysqlSetCharset( 'utf8' ); 00155 } else { 00156 return $this->mysqlSetCharset( 'binary' ); 00157 } 00158 } 00159 00167 abstract protected function mysqlConnect( $realServer ); 00168 00175 abstract protected function mysqlSetCharset( $charset ); 00176 00181 function freeResult( $res ) { 00182 if ( $res instanceof ResultWrapper ) { 00183 $res = $res->result; 00184 } 00185 wfSuppressWarnings(); 00186 $ok = $this->mysqlFreeResult( $res ); 00187 wfRestoreWarnings(); 00188 if ( !$ok ) { 00189 throw new DBUnexpectedError( $this, "Unable to free MySQL result" ); 00190 } 00191 } 00192 00199 abstract protected function mysqlFreeResult( $res ); 00200 00206 function fetchObject( $res ) { 00207 if ( $res instanceof ResultWrapper ) { 00208 $res = $res->result; 00209 } 00210 wfSuppressWarnings(); 00211 $row = $this->mysqlFetchObject( $res ); 00212 wfRestoreWarnings(); 00213 00214 $errno = $this->lastErrno(); 00215 // Unfortunately, mysql_fetch_object does not reset the last errno. 00216 // Only check for CR_SERVER_LOST and CR_UNKNOWN_ERROR, as 00217 // these are the only errors mysql_fetch_object can cause. 00218 // See http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html. 00219 if ( $errno == 2000 || $errno == 2013 ) { 00220 throw new DBUnexpectedError( 00221 $this, 00222 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) 00223 ); 00224 } 00225 00226 return $row; 00227 } 00228 00235 abstract protected function mysqlFetchObject( $res ); 00236 00242 function fetchRow( $res ) { 00243 if ( $res instanceof ResultWrapper ) { 00244 $res = $res->result; 00245 } 00246 wfSuppressWarnings(); 00247 $row = $this->mysqlFetchArray( $res ); 00248 wfRestoreWarnings(); 00249 00250 $errno = $this->lastErrno(); 00251 // Unfortunately, mysql_fetch_array does not reset the last errno. 00252 // Only check for CR_SERVER_LOST and CR_UNKNOWN_ERROR, as 00253 // these are the only errors mysql_fetch_array can cause. 00254 // See http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html. 00255 if ( $errno == 2000 || $errno == 2013 ) { 00256 throw new DBUnexpectedError( 00257 $this, 00258 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) 00259 ); 00260 } 00261 00262 return $row; 00263 } 00264 00271 abstract protected function mysqlFetchArray( $res ); 00272 00278 function numRows( $res ) { 00279 if ( $res instanceof ResultWrapper ) { 00280 $res = $res->result; 00281 } 00282 wfSuppressWarnings(); 00283 $n = $this->mysqlNumRows( $res ); 00284 wfRestoreWarnings(); 00285 00286 // Unfortunately, mysql_num_rows does not reset the last errno. 00287 // We are not checking for any errors here, since 00288 // these are no errors mysql_num_rows can cause. 00289 // See http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html. 00290 // See https://bugzilla.wikimedia.org/42430 00291 return $n; 00292 } 00293 00300 abstract protected function mysqlNumRows( $res ); 00301 00306 function numFields( $res ) { 00307 if ( $res instanceof ResultWrapper ) { 00308 $res = $res->result; 00309 } 00310 00311 return $this->mysqlNumFields( $res ); 00312 } 00313 00320 abstract protected function mysqlNumFields( $res ); 00321 00327 function fieldName( $res, $n ) { 00328 if ( $res instanceof ResultWrapper ) { 00329 $res = $res->result; 00330 } 00331 00332 return $this->mysqlFieldName( $res, $n ); 00333 } 00334 00342 abstract protected function mysqlFieldName( $res, $n ); 00343 00350 public function fieldType( $res, $n ) { 00351 if ( $res instanceof ResultWrapper ) { 00352 $res = $res->result; 00353 } 00354 00355 return $this->mysqlFieldType( $res, $n ); 00356 } 00357 00365 abstract protected function mysqlFieldType( $res, $n ); 00366 00372 function dataSeek( $res, $row ) { 00373 if ( $res instanceof ResultWrapper ) { 00374 $res = $res->result; 00375 } 00376 00377 return $this->mysqlDataSeek( $res, $row ); 00378 } 00379 00387 abstract protected function mysqlDataSeek( $res, $row ); 00388 00392 function lastError() { 00393 if ( $this->mConn ) { 00394 # Even if it's non-zero, it can still be invalid 00395 wfSuppressWarnings(); 00396 $error = $this->mysqlError( $this->mConn ); 00397 if ( !$error ) { 00398 $error = $this->mysqlError(); 00399 } 00400 wfRestoreWarnings(); 00401 } else { 00402 $error = $this->mysqlError(); 00403 } 00404 if ( $error ) { 00405 $error .= ' (' . $this->mServer . ')'; 00406 } 00407 00408 return $error; 00409 } 00410 00417 abstract protected function mysqlError( $conn = null ); 00418 00426 function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) { 00427 return $this->nativeReplace( $table, $rows, $fname ); 00428 } 00429 00442 public function estimateRowCount( $table, $vars = '*', $conds = '', 00443 $fname = __METHOD__, $options = array() 00444 ) { 00445 $options['EXPLAIN'] = true; 00446 $res = $this->select( $table, $vars, $conds, $fname, $options ); 00447 if ( $res === false ) { 00448 return false; 00449 } 00450 if ( !$this->numRows( $res ) ) { 00451 return 0; 00452 } 00453 00454 $rows = 1; 00455 foreach ( $res as $plan ) { 00456 $rows *= $plan->rows > 0 ? $plan->rows : 1; // avoid resetting to zero 00457 } 00458 00459 return $rows; 00460 } 00461 00467 function fieldInfo( $table, $field ) { 00468 $table = $this->tableName( $table ); 00469 $res = $this->query( "SELECT * FROM $table LIMIT 1", __METHOD__, true ); 00470 if ( !$res ) { 00471 return false; 00472 } 00473 $n = $this->mysqlNumFields( $res->result ); 00474 for ( $i = 0; $i < $n; $i++ ) { 00475 $meta = $this->mysqlFetchField( $res->result, $i ); 00476 if ( $field == $meta->name ) { 00477 return new MySQLField( $meta ); 00478 } 00479 } 00480 00481 return false; 00482 } 00483 00491 abstract protected function mysqlFetchField( $res, $n ); 00492 00502 function indexInfo( $table, $index, $fname = __METHOD__ ) { 00503 # SHOW INDEX works in MySQL 3.23.58, but SHOW INDEXES does not. 00504 # SHOW INDEX should work for 3.x and up: 00505 # http://dev.mysql.com/doc/mysql/en/SHOW_INDEX.html 00506 $table = $this->tableName( $table ); 00507 $index = $this->indexName( $index ); 00508 00509 $sql = 'SHOW INDEX FROM ' . $table; 00510 $res = $this->query( $sql, $fname ); 00511 00512 if ( !$res ) { 00513 return null; 00514 } 00515 00516 $result = array(); 00517 00518 foreach ( $res as $row ) { 00519 if ( $row->Key_name == $index ) { 00520 $result[] = $row; 00521 } 00522 } 00523 00524 return empty( $result ) ? false : $result; 00525 } 00526 00531 function strencode( $s ) { 00532 $sQuoted = $this->mysqlRealEscapeString( $s ); 00533 00534 if ( $sQuoted === false ) { 00535 $this->ping(); 00536 $sQuoted = $this->mysqlRealEscapeString( $s ); 00537 } 00538 00539 return $sQuoted; 00540 } 00541 00548 public function addIdentifierQuotes( $s ) { 00549 // Characters in the range \u0001-\uFFFF are valid in a quoted identifier 00550 // Remove NUL bytes and escape backticks by doubling 00551 return '`' . str_replace( array( "\0", '`' ), array( '', '``' ), $s ) . '`'; 00552 } 00553 00558 public function isQuotedIdentifier( $name ) { 00559 return strlen( $name ) && $name[0] == '`' && substr( $name, -1, 1 ) == '`'; 00560 } 00561 00565 function ping() { 00566 $ping = $this->mysqlPing(); 00567 if ( $ping ) { 00568 return true; 00569 } 00570 00571 $this->closeConnection(); 00572 $this->mOpened = false; 00573 $this->mConn = false; 00574 $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname ); 00575 00576 return true; 00577 } 00578 00584 abstract protected function mysqlPing(); 00585 00591 public function setFakeSlaveLag( $lag ) { 00592 $this->mFakeSlaveLag = $lag; 00593 } 00594 00600 public function setFakeMaster( $enabled = true ) { 00601 $this->mFakeMaster = $enabled; 00602 } 00603 00611 function getLag() { 00612 if ( !is_null( $this->mFakeSlaveLag ) ) { 00613 wfDebug( "getLag: fake slave lagged {$this->mFakeSlaveLag} seconds\n" ); 00614 00615 return $this->mFakeSlaveLag; 00616 } 00617 00618 return $this->getLagFromSlaveStatus(); 00619 } 00620 00624 function getLagFromSlaveStatus() { 00625 $res = $this->query( 'SHOW SLAVE STATUS', __METHOD__ ); 00626 if ( !$res ) { 00627 return false; 00628 } 00629 $row = $res->fetchObject(); 00630 if ( !$row ) { 00631 return false; 00632 } 00633 if ( strval( $row->Seconds_Behind_Master ) === '' ) { 00634 return false; 00635 } else { 00636 return intval( $row->Seconds_Behind_Master ); 00637 } 00638 } 00639 00650 function masterPosWait( DBMasterPos $pos, $timeout ) { 00651 if ( $this->lastKnownSlavePos && $this->lastKnownSlavePos->hasReached( $pos ) ) { 00652 return '0'; // http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html 00653 } 00654 00655 wfProfileIn( __METHOD__ ); 00656 # Commit any open transactions 00657 $this->commit( __METHOD__, 'flush' ); 00658 00659 if ( !is_null( $this->mFakeSlaveLag ) ) { 00660 $wait = intval( ( $pos->pos - microtime( true ) + $this->mFakeSlaveLag ) * 1e6 ); 00661 00662 if ( $wait > $timeout * 1e6 ) { 00663 wfDebug( "Fake slave timed out waiting for $pos ($wait us)\n" ); 00664 wfProfileOut( __METHOD__ ); 00665 00666 return -1; 00667 } elseif ( $wait > 0 ) { 00668 wfDebug( "Fake slave waiting $wait us\n" ); 00669 usleep( $wait ); 00670 wfProfileOut( __METHOD__ ); 00671 00672 return 1; 00673 } else { 00674 wfDebug( "Fake slave up to date ($wait us)\n" ); 00675 wfProfileOut( __METHOD__ ); 00676 00677 return 0; 00678 } 00679 } 00680 00681 # Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set 00682 $encFile = $this->addQuotes( $pos->file ); 00683 $encPos = intval( $pos->pos ); 00684 $sql = "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)"; 00685 $res = $this->doQuery( $sql ); 00686 00687 $status = false; 00688 if ( $res && $row = $this->fetchRow( $res ) ) { 00689 $status = $row[0]; // can be NULL, -1, or 0+ per the MySQL manual 00690 if ( ctype_digit( $status ) ) { // success 00691 $this->lastKnownSlavePos = $pos; 00692 } 00693 } 00694 00695 wfProfileOut( __METHOD__ ); 00696 00697 return $status; 00698 } 00699 00705 function getSlavePos() { 00706 if ( !is_null( $this->mFakeSlaveLag ) ) { 00707 $pos = new MySQLMasterPos( 'fake', microtime( true ) - $this->mFakeSlaveLag ); 00708 wfDebug( __METHOD__ . ": fake slave pos = $pos\n" ); 00709 00710 return $pos; 00711 } 00712 00713 $res = $this->query( 'SHOW SLAVE STATUS', 'DatabaseBase::getSlavePos' ); 00714 $row = $this->fetchObject( $res ); 00715 00716 if ( $row ) { 00717 $pos = isset( $row->Exec_master_log_pos ) 00718 ? $row->Exec_master_log_pos 00719 : $row->Exec_Master_Log_Pos; 00720 00721 return new MySQLMasterPos( $row->Relay_Master_Log_File, $pos ); 00722 } else { 00723 return false; 00724 } 00725 } 00726 00732 function getMasterPos() { 00733 if ( $this->mFakeMaster ) { 00734 return new MySQLMasterPos( 'fake', microtime( true ) ); 00735 } 00736 00737 $res = $this->query( 'SHOW MASTER STATUS', 'DatabaseBase::getMasterPos' ); 00738 $row = $this->fetchObject( $res ); 00739 00740 if ( $row ) { 00741 return new MySQLMasterPos( $row->File, $row->Position ); 00742 } else { 00743 return false; 00744 } 00745 } 00746 00751 function useIndexClause( $index ) { 00752 return "FORCE INDEX (" . $this->indexName( $index ) . ")"; 00753 } 00754 00758 function lowPriorityOption() { 00759 return 'LOW_PRIORITY'; 00760 } 00761 00765 public function getSoftwareLink() { 00766 // MariaDB includes its name in its version string (sent when the connection is opened), 00767 // and this is how MariaDB's version of the mysql command-line client identifies MariaDB 00768 // servers (see the mariadb_connection() function in libmysql/libmysql.c). 00769 $version = $this->getServerVersion(); 00770 if ( strpos( $version, 'MariaDB' ) !== false || strpos( $version, '-maria-' ) !== false ) { 00771 return '[{{int:version-db-mariadb-url}} MariaDB]'; 00772 } 00773 00774 // Percona Server's version suffix is not very distinctive, and @@version_comment 00775 // doesn't give the necessary info for source builds, so assume the server is MySQL. 00776 // (Even Percona's version of mysql doesn't try to make the distinction.) 00777 return '[{{int:version-db-mysql-url}} MySQL]'; 00778 } 00779 00783 public function setSessionOptions( array $options ) { 00784 if ( isset( $options['connTimeout'] ) ) { 00785 $timeout = (int)$options['connTimeout']; 00786 $this->query( "SET net_read_timeout=$timeout" ); 00787 $this->query( "SET net_write_timeout=$timeout" ); 00788 } 00789 } 00790 00796 public function streamStatementEnd( &$sql, &$newLine ) { 00797 if ( strtoupper( substr( $newLine, 0, 9 ) ) == 'DELIMITER' ) { 00798 preg_match( '/^DELIMITER\s+(\S+)/', $newLine, $m ); 00799 $this->delimiter = $m[1]; 00800 $newLine = ''; 00801 } 00802 00803 return parent::streamStatementEnd( $sql, $newLine ); 00804 } 00805 00814 public function lockIsFree( $lockName, $method ) { 00815 $lockName = $this->addQuotes( $lockName ); 00816 $result = $this->query( "SELECT IS_FREE_LOCK($lockName) AS lockstatus", $method ); 00817 $row = $this->fetchObject( $result ); 00818 00819 return ( $row->lockstatus == 1 ); 00820 } 00821 00828 public function lock( $lockName, $method, $timeout = 5 ) { 00829 $lockName = $this->addQuotes( $lockName ); 00830 $result = $this->query( "SELECT GET_LOCK($lockName, $timeout) AS lockstatus", $method ); 00831 $row = $this->fetchObject( $result ); 00832 00833 if ( $row->lockstatus == 1 ) { 00834 return true; 00835 } else { 00836 wfDebug( __METHOD__ . " failed to acquire lock\n" ); 00837 00838 return false; 00839 } 00840 } 00841 00849 public function unlock( $lockName, $method ) { 00850 $lockName = $this->addQuotes( $lockName ); 00851 $result = $this->query( "SELECT RELEASE_LOCK($lockName) as lockstatus", $method ); 00852 $row = $this->fetchObject( $result ); 00853 00854 return ( $row->lockstatus == 1 ); 00855 } 00856 00864 public function lockTables( $read, $write, $method, $lowPriority = true ) { 00865 $items = array(); 00866 00867 foreach ( $write as $table ) { 00868 $tbl = $this->tableName( $table ) . 00869 ( $lowPriority ? ' LOW_PRIORITY' : '' ) . 00870 ' WRITE'; 00871 $items[] = $tbl; 00872 } 00873 foreach ( $read as $table ) { 00874 $items[] = $this->tableName( $table ) . ' READ'; 00875 } 00876 $sql = "LOCK TABLES " . implode( ',', $items ); 00877 $this->query( $sql, $method ); 00878 00879 return true; 00880 } 00881 00886 public function unlockTables( $method ) { 00887 $this->query( "UNLOCK TABLES", $method ); 00888 00889 return true; 00890 } 00891 00898 public function getSearchEngine() { 00899 return 'SearchMySQL'; 00900 } 00901 00905 public function setBigSelects( $value = true ) { 00906 if ( $value === 'default' ) { 00907 if ( $this->mDefaultBigSelects === null ) { 00908 # Function hasn't been called before so it must already be set to the default 00909 return; 00910 } else { 00911 $value = $this->mDefaultBigSelects; 00912 } 00913 } elseif ( $this->mDefaultBigSelects === null ) { 00914 $this->mDefaultBigSelects = (bool)$this->selectField( false, '@@sql_big_selects' ); 00915 } 00916 $encValue = $value ? '1' : '0'; 00917 $this->query( "SET sql_big_selects=$encValue", __METHOD__ ); 00918 } 00919 00931 function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = __METHOD__ ) { 00932 if ( !$conds ) { 00933 throw new DBUnexpectedError( $this, 'DatabaseBase::deleteJoin() called with empty $conds' ); 00934 } 00935 00936 $delTable = $this->tableName( $delTable ); 00937 $joinTable = $this->tableName( $joinTable ); 00938 $sql = "DELETE $delTable FROM $delTable, $joinTable WHERE $delVar=$joinVar "; 00939 00940 if ( $conds != '*' ) { 00941 $sql .= ' AND ' . $this->makeList( $conds, LIST_AND ); 00942 } 00943 00944 return $this->query( $sql, $fname ); 00945 } 00946 00955 public function upsert( $table, array $rows, array $uniqueIndexes, 00956 array $set, $fname = __METHOD__ 00957 ) { 00958 if ( !count( $rows ) ) { 00959 return true; // nothing to do 00960 } 00961 00962 if ( !is_array( reset( $rows ) ) ) { 00963 $rows = array( $rows ); 00964 } 00965 00966 $table = $this->tableName( $table ); 00967 $columns = array_keys( $rows[0] ); 00968 00969 $sql = "INSERT INTO $table (" . implode( ',', $columns ) . ') VALUES '; 00970 $rowTuples = array(); 00971 foreach ( $rows as $row ) { 00972 $rowTuples[] = '(' . $this->makeList( $row ) . ')'; 00973 } 00974 $sql .= implode( ',', $rowTuples ); 00975 $sql .= " ON DUPLICATE KEY UPDATE " . $this->makeList( $set, LIST_SET ); 00976 00977 return (bool)$this->query( $sql, $fname ); 00978 } 00979 00985 function getServerUptime() { 00986 $vars = $this->getMysqlStatus( 'Uptime' ); 00987 00988 return (int)$vars['Uptime']; 00989 } 00990 00996 function wasDeadlock() { 00997 return $this->lastErrno() == 1213; 00998 } 00999 01005 function wasLockTimeout() { 01006 return $this->lastErrno() == 1205; 01007 } 01008 01015 function wasErrorReissuable() { 01016 return $this->lastErrno() == 2013 || $this->lastErrno() == 2006; 01017 } 01018 01024 function wasReadOnlyError() { 01025 return $this->lastErrno() == 1223 || 01026 ( $this->lastErrno() == 1290 && strpos( $this->lastError(), '--read-only' ) !== false ); 01027 } 01028 01036 function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = __METHOD__ ) { 01037 $tmp = $temporary ? 'TEMPORARY ' : ''; 01038 $newName = $this->addIdentifierQuotes( $newName ); 01039 $oldName = $this->addIdentifierQuotes( $oldName ); 01040 $query = "CREATE $tmp TABLE $newName (LIKE $oldName)"; 01041 01042 return $this->query( $query, $fname ); 01043 } 01044 01052 function listTables( $prefix = null, $fname = __METHOD__ ) { 01053 $result = $this->query( "SHOW TABLES", $fname ); 01054 01055 $endArray = array(); 01056 01057 foreach ( $result as $table ) { 01058 $vars = get_object_vars( $table ); 01059 $table = array_pop( $vars ); 01060 01061 if ( !$prefix || strpos( $table, $prefix ) === 0 ) { 01062 $endArray[] = $table; 01063 } 01064 } 01065 01066 return $endArray; 01067 } 01068 01074 public function dropTable( $tableName, $fName = __METHOD__ ) { 01075 if ( !$this->tableExists( $tableName, $fName ) ) { 01076 return false; 01077 } 01078 01079 return $this->query( "DROP TABLE IF EXISTS " . $this->tableName( $tableName ), $fName ); 01080 } 01081 01085 protected function getDefaultSchemaVars() { 01086 $vars = parent::getDefaultSchemaVars(); 01087 $vars['wgDBTableOptions'] = str_replace( 'TYPE', 'ENGINE', $GLOBALS['wgDBTableOptions'] ); 01088 $vars['wgDBTableOptions'] = str_replace( 01089 'CHARSET=mysql4', 01090 'CHARSET=binary', 01091 $vars['wgDBTableOptions'] 01092 ); 01093 01094 return $vars; 01095 } 01096 01103 function getMysqlStatus( $which = "%" ) { 01104 $res = $this->query( "SHOW STATUS LIKE '{$which}'" ); 01105 $status = array(); 01106 01107 foreach ( $res as $row ) { 01108 $status[$row->Variable_name] = $row->Value; 01109 } 01110 01111 return $status; 01112 } 01113 01123 public function listViews( $prefix = null, $fname = __METHOD__ ) { 01124 01125 if ( !isset( $this->allViews ) ) { 01126 01127 // The name of the column containing the name of the VIEW 01128 $propertyName = 'Tables_in_' . $this->mDBname; 01129 01130 // Query for the VIEWS 01131 $result = $this->query( 'SHOW FULL TABLES WHERE TABLE_TYPE = "VIEW"' ); 01132 $this->allViews = array(); 01133 while ( ( $row = $this->fetchRow( $result ) ) !== false ) { 01134 array_push( $this->allViews, $row[$propertyName] ); 01135 } 01136 } 01137 01138 if ( is_null( $prefix ) || $prefix === '' ) { 01139 return $this->allViews; 01140 } 01141 01142 $filteredViews = array(); 01143 foreach ( $this->allViews as $viewName ) { 01144 // Does the name of this VIEW start with the table-prefix? 01145 if ( strpos( $viewName, $prefix ) === 0 ) { 01146 array_push( $filteredViews, $viewName ); 01147 } 01148 } 01149 01150 return $filteredViews; 01151 } 01152 01161 public function isView( $name, $prefix = null ) { 01162 return in_array( $name, $this->listViews( $prefix ) ); 01163 } 01164 } 01165 01170 class MySQLField implements Field { 01171 private $name, $tablename, $default, $max_length, $nullable, 01172 $is_pk, $is_unique, $is_multiple, $is_key, $type, $binary; 01173 01174 function __construct( $info ) { 01175 $this->name = $info->name; 01176 $this->tablename = $info->table; 01177 $this->default = $info->def; 01178 $this->max_length = $info->max_length; 01179 $this->nullable = !$info->not_null; 01180 $this->is_pk = $info->primary_key; 01181 $this->is_unique = $info->unique_key; 01182 $this->is_multiple = $info->multiple_key; 01183 $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple ); 01184 $this->type = $info->type; 01185 $this->binary = isset( $info->binary ) ? $info->binary : false; 01186 } 01187 01191 function name() { 01192 return $this->name; 01193 } 01194 01198 function tableName() { 01199 return $this->tableName; 01200 } 01201 01205 function type() { 01206 return $this->type; 01207 } 01208 01212 function isNullable() { 01213 return $this->nullable; 01214 } 01215 01216 function defaultValue() { 01217 return $this->default; 01218 } 01219 01223 function isKey() { 01224 return $this->is_key; 01225 } 01226 01230 function isMultipleKey() { 01231 return $this->is_multiple; 01232 } 01233 01234 function isBinary() { 01235 return $this->binary; 01236 } 01237 } 01238 01239 class MySQLMasterPos implements DBMasterPos { 01241 public $file; 01242 01244 public $pos; 01245 01246 function __construct( $file, $pos ) { 01247 $this->file = $file; 01248 $this->pos = $pos; 01249 } 01250 01251 function __toString() { 01252 // e.g db1034-bin.000976/843431247 01253 return "{$this->file}/{$this->pos}"; 01254 } 01255 01259 protected function getCoordinates() { 01260 $m = array(); 01261 if ( preg_match( '!\.(\d+)/(\d+)$!', (string)$this, $m ) ) { 01262 return array( (int)$m[1], (int)$m[2] ); 01263 } 01264 01265 return false; 01266 } 01267 01268 function hasReached( MySQLMasterPos $pos ) { 01269 $thisPos = $this->getCoordinates(); 01270 $thatPos = $pos->getCoordinates(); 01271 01272 return ( $thisPos && $thatPos && $thisPos >= $thatPos ); 01273 } 01274 }