MediaWiki
REL1_22
|
00001 <?php 00032 abstract class DatabaseMysqlBase extends DatabaseBase { 00034 protected $lastKnownSlavePos; 00035 00039 function getType() { 00040 return 'mysql'; 00041 } 00042 00051 function open( $server, $user, $password, $dbName ) { 00052 global $wgAllDBsAreLocalhost, $wgDBmysql5, $wgSQLMode; 00053 wfProfileIn( __METHOD__ ); 00054 00055 # Debugging hack -- fake cluster 00056 if ( $wgAllDBsAreLocalhost ) { 00057 $realServer = 'localhost'; 00058 } else { 00059 $realServer = $server; 00060 } 00061 $this->close(); 00062 $this->mServer = $server; 00063 $this->mUser = $user; 00064 $this->mPassword = $password; 00065 $this->mDBname = $dbName; 00066 00067 wfProfileIn( "dbconnect-$server" ); 00068 00069 # The kernel's default SYN retransmission period is far too slow for us, 00070 # so we use a short timeout plus a manual retry. Retrying means that a small 00071 # but finite rate of SYN packet loss won't cause user-visible errors. 00072 $this->mConn = false; 00073 $this->installErrorHandler(); 00074 try { 00075 $this->mConn = $this->mysqlConnect( $realServer ); 00076 } catch ( Exception $ex ) { 00077 wfProfileOut( "dbconnect-$server" ); 00078 wfProfileOut( __METHOD__ ); 00079 throw $ex; 00080 } 00081 $error = $this->restoreErrorHandler(); 00082 00083 wfProfileOut( "dbconnect-$server" ); 00084 00085 # Always log connection errors 00086 if ( !$this->mConn ) { 00087 if ( !$error ) { 00088 $error = $this->lastError(); 00089 } 00090 wfLogDBError( "Error connecting to {$this->mServer}: $error\n" ); 00091 wfDebug( "DB connection error\n" . 00092 "Server: $server, User: $user, Password: " . 00093 substr( $password, 0, 3 ) . "..., error: " . $error . "\n" ); 00094 00095 wfProfileOut( __METHOD__ ); 00096 return $this->reportConnectionError( $error ); 00097 } 00098 00099 if ( $dbName != '' ) { 00100 wfSuppressWarnings(); 00101 $success = $this->selectDB( $dbName ); 00102 wfRestoreWarnings(); 00103 if ( !$success ) { 00104 wfLogDBError( "Error selecting database $dbName on server {$this->mServer}\n" ); 00105 wfDebug( "Error selecting database $dbName on server {$this->mServer} " . 00106 "from client host " . wfHostname() . "\n" ); 00107 00108 wfProfileOut( __METHOD__ ); 00109 return $this->reportConnectionError( "Error selecting database $dbName" ); 00110 } 00111 } 00112 00113 // Tell the server we're communicating with it in UTF-8. 00114 // This may engage various charset conversions. 00115 if ( $wgDBmysql5 ) { 00116 $this->query( 'SET NAMES utf8', __METHOD__ ); 00117 } else { 00118 $this->query( 'SET NAMES binary', __METHOD__ ); 00119 } 00120 // Set SQL mode, default is turning them all off, can be overridden or skipped with null 00121 if ( is_string( $wgSQLMode ) ) { 00122 $mode = $this->addQuotes( $wgSQLMode ); 00123 $this->query( "SET sql_mode = $mode", __METHOD__ ); 00124 } 00125 00126 $this->mOpened = true; 00127 wfProfileOut( __METHOD__ ); 00128 return true; 00129 } 00130 00138 abstract protected function mysqlConnect( $realServer ); 00139 00144 function freeResult( $res ) { 00145 if ( $res instanceof ResultWrapper ) { 00146 $res = $res->result; 00147 } 00148 wfSuppressWarnings(); 00149 $ok = $this->mysqlFreeResult( $res ); 00150 wfRestoreWarnings(); 00151 if ( !$ok ) { 00152 throw new DBUnexpectedError( $this, "Unable to free MySQL result" ); 00153 } 00154 } 00155 00162 abstract protected function mysqlFreeResult( $res ); 00163 00169 function fetchObject( $res ) { 00170 if ( $res instanceof ResultWrapper ) { 00171 $res = $res->result; 00172 } 00173 wfSuppressWarnings(); 00174 $row = $this->mysqlFetchObject( $res ); 00175 wfRestoreWarnings(); 00176 00177 $errno = $this->lastErrno(); 00178 // Unfortunately, mysql_fetch_object does not reset the last errno. 00179 // Only check for CR_SERVER_LOST and CR_UNKNOWN_ERROR, as 00180 // these are the only errors mysql_fetch_object can cause. 00181 // See http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html. 00182 if ( $errno == 2000 || $errno == 2013 ) { 00183 throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) ); 00184 } 00185 return $row; 00186 } 00187 00194 abstract protected function mysqlFetchObject( $res ); 00195 00201 function fetchRow( $res ) { 00202 if ( $res instanceof ResultWrapper ) { 00203 $res = $res->result; 00204 } 00205 wfSuppressWarnings(); 00206 $row = $this->mysqlFetchArray( $res ); 00207 wfRestoreWarnings(); 00208 00209 $errno = $this->lastErrno(); 00210 // Unfortunately, mysql_fetch_array does not reset the last errno. 00211 // Only check for CR_SERVER_LOST and CR_UNKNOWN_ERROR, as 00212 // these are the only errors mysql_fetch_array can cause. 00213 // See http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html. 00214 if ( $errno == 2000 || $errno == 2013 ) { 00215 throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) ); 00216 } 00217 return $row; 00218 } 00219 00226 abstract protected function mysqlFetchArray( $res ); 00227 00233 function numRows( $res ) { 00234 if ( $res instanceof ResultWrapper ) { 00235 $res = $res->result; 00236 } 00237 wfSuppressWarnings(); 00238 $n = $this->mysqlNumRows( $res ); 00239 wfRestoreWarnings(); 00240 // Unfortunately, mysql_num_rows does not reset the last errno. 00241 // We are not checking for any errors here, since 00242 // these are no errors mysql_num_rows can cause. 00243 // See http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html. 00244 // See https://bugzilla.wikimedia.org/42430 00245 return $n; 00246 } 00247 00254 abstract protected function mysqlNumRows( $res ); 00255 00260 function numFields( $res ) { 00261 if ( $res instanceof ResultWrapper ) { 00262 $res = $res->result; 00263 } 00264 return $this->mysqlNumFields( $res ); 00265 } 00266 00273 abstract protected function mysqlNumFields( $res ); 00274 00280 function fieldName( $res, $n ) { 00281 if ( $res instanceof ResultWrapper ) { 00282 $res = $res->result; 00283 } 00284 return $this->mysqlFieldName( $res, $n ); 00285 } 00286 00294 abstract protected function mysqlFieldName( $res, $n ); 00295 00301 function dataSeek( $res, $row ) { 00302 if ( $res instanceof ResultWrapper ) { 00303 $res = $res->result; 00304 } 00305 return $this->mysqlDataSeek( $res, $row ); 00306 } 00307 00315 abstract protected function mysqlDataSeek( $res, $row ); 00316 00320 function lastError() { 00321 if ( $this->mConn ) { 00322 # Even if it's non-zero, it can still be invalid 00323 wfSuppressWarnings(); 00324 $error = $this->mysqlError( $this->mConn ); 00325 if ( !$error ) { 00326 $error = $this->mysqlError(); 00327 } 00328 wfRestoreWarnings(); 00329 } else { 00330 $error = $this->mysqlError(); 00331 } 00332 if ( $error ) { 00333 $error .= ' (' . $this->mServer . ')'; 00334 } 00335 return $error; 00336 } 00337 00344 abstract protected function mysqlError( $conn = null ); 00345 00353 function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) { 00354 return $this->nativeReplace( $table, $rows, $fname ); 00355 } 00356 00369 public function estimateRowCount( $table, $vars = '*', $conds = '', $fname = __METHOD__, $options = array() ) { 00370 $options['EXPLAIN'] = true; 00371 $res = $this->select( $table, $vars, $conds, $fname, $options ); 00372 if ( $res === false ) { 00373 return false; 00374 } 00375 if ( !$this->numRows( $res ) ) { 00376 return 0; 00377 } 00378 00379 $rows = 1; 00380 foreach ( $res as $plan ) { 00381 $rows *= $plan->rows > 0 ? $plan->rows : 1; // avoid resetting to zero 00382 } 00383 return $rows; 00384 } 00385 00391 function fieldInfo( $table, $field ) { 00392 $table = $this->tableName( $table ); 00393 $res = $this->query( "SELECT * FROM $table LIMIT 1", __METHOD__, true ); 00394 if ( !$res ) { 00395 return false; 00396 } 00397 $n = $this->mysqlNumFields( $res->result ); 00398 for ( $i = 0; $i < $n; $i++ ) { 00399 $meta = $this->mysqlFetchField( $res->result, $i ); 00400 if ( $field == $meta->name ) { 00401 return new MySQLField( $meta ); 00402 } 00403 } 00404 return false; 00405 } 00406 00414 abstract protected function mysqlFetchField( $res, $n ); 00415 00425 function indexInfo( $table, $index, $fname = __METHOD__ ) { 00426 # SHOW INDEX works in MySQL 3.23.58, but SHOW INDEXES does not. 00427 # SHOW INDEX should work for 3.x and up: 00428 # http://dev.mysql.com/doc/mysql/en/SHOW_INDEX.html 00429 $table = $this->tableName( $table ); 00430 $index = $this->indexName( $index ); 00431 00432 $sql = 'SHOW INDEX FROM ' . $table; 00433 $res = $this->query( $sql, $fname ); 00434 00435 if ( !$res ) { 00436 return null; 00437 } 00438 00439 $result = array(); 00440 00441 foreach ( $res as $row ) { 00442 if ( $row->Key_name == $index ) { 00443 $result[] = $row; 00444 } 00445 } 00446 return empty( $result ) ? false : $result; 00447 } 00448 00454 function strencode( $s ) { 00455 $sQuoted = $this->mysqlRealEscapeString( $s ); 00456 00457 if ( $sQuoted === false ) { 00458 $this->ping(); 00459 $sQuoted = $this->mysqlRealEscapeString( $s ); 00460 } 00461 return $sQuoted; 00462 } 00463 00471 public function addIdentifierQuotes( $s ) { 00472 // Characters in the range \u0001-\uFFFF are valid in a quoted identifier 00473 // Remove NUL bytes and escape backticks by doubling 00474 return '`' . str_replace( array( "\0", '`' ), array( '', '``' ), $s ) . '`'; 00475 } 00476 00481 public function isQuotedIdentifier( $name ) { 00482 return strlen( $name ) && $name[0] == '`' && substr( $name, -1, 1 ) == '`'; 00483 } 00484 00488 function ping() { 00489 $ping = $this->mysqlPing(); 00490 if ( $ping ) { 00491 return true; 00492 } 00493 00494 $this->closeConnection(); 00495 $this->mOpened = false; 00496 $this->mConn = false; 00497 $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname ); 00498 return true; 00499 } 00500 00506 abstract protected function mysqlPing(); 00507 00515 function getLag() { 00516 if ( !is_null( $this->mFakeSlaveLag ) ) { 00517 wfDebug( "getLag: fake slave lagged {$this->mFakeSlaveLag} seconds\n" ); 00518 return $this->mFakeSlaveLag; 00519 } 00520 00521 return $this->getLagFromSlaveStatus(); 00522 } 00523 00527 function getLagFromSlaveStatus() { 00528 $res = $this->query( 'SHOW SLAVE STATUS', __METHOD__ ); 00529 if ( !$res ) { 00530 return false; 00531 } 00532 $row = $res->fetchObject(); 00533 if ( !$row ) { 00534 return false; 00535 } 00536 if ( strval( $row->Seconds_Behind_Master ) === '' ) { 00537 return false; 00538 } else { 00539 return intval( $row->Seconds_Behind_Master ); 00540 } 00541 } 00542 00548 function getLagFromProcesslist() { 00549 wfDeprecated( __METHOD__, '1.19' ); 00550 $res = $this->query( 'SHOW PROCESSLIST', __METHOD__ ); 00551 if ( !$res ) { 00552 return false; 00553 } 00554 # Find slave SQL thread 00555 foreach ( $res as $row ) { 00556 /* This should work for most situations - when default db 00557 * for thread is not specified, it had no events executed, 00558 * and therefore it doesn't know yet how lagged it is. 00559 * 00560 * Relay log I/O thread does not select databases. 00561 */ 00562 if ( $row->User == 'system user' && 00563 $row->State != 'Waiting for master to send event' && 00564 $row->State != 'Connecting to master' && 00565 $row->State != 'Queueing master event to the relay log' && 00566 $row->State != 'Waiting for master update' && 00567 $row->State != 'Requesting binlog dump' && 00568 $row->State != 'Waiting to reconnect after a failed master event read' && 00569 $row->State != 'Reconnecting after a failed master event read' && 00570 $row->State != 'Registering slave on master' 00571 ) { 00572 # This is it, return the time (except -ve) 00573 if ( $row->Time > 0x7fffffff ) { 00574 return false; 00575 } else { 00576 return $row->Time; 00577 } 00578 } 00579 } 00580 return false; 00581 } 00582 00591 function masterPosWait( DBMasterPos $pos, $timeout ) { 00592 if ( $this->lastKnownSlavePos && $this->lastKnownSlavePos->hasReached( $pos ) ) { 00593 return '0'; // http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html 00594 } 00595 00596 wfProfileIn( __METHOD__ ); 00597 # Commit any open transactions 00598 $this->commit( __METHOD__, 'flush' ); 00599 00600 if ( !is_null( $this->mFakeSlaveLag ) ) { 00601 $status = parent::masterPosWait( $pos, $timeout ); 00602 wfProfileOut( __METHOD__ ); 00603 return $status; 00604 } 00605 00606 # Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set 00607 $encFile = $this->addQuotes( $pos->file ); 00608 $encPos = intval( $pos->pos ); 00609 $sql = "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)"; 00610 $res = $this->doQuery( $sql ); 00611 00612 $status = false; 00613 if ( $res && $row = $this->fetchRow( $res ) ) { 00614 $status = $row[0]; // can be NULL, -1, or 0+ per the MySQL manual 00615 if ( ctype_digit( $status ) ) { // success 00616 $this->lastKnownSlavePos = $pos; 00617 } 00618 } 00619 00620 wfProfileOut( __METHOD__ ); 00621 return $status; 00622 } 00623 00629 function getSlavePos() { 00630 if ( !is_null( $this->mFakeSlaveLag ) ) { 00631 return parent::getSlavePos(); 00632 } 00633 00634 $res = $this->query( 'SHOW SLAVE STATUS', 'DatabaseBase::getSlavePos' ); 00635 $row = $this->fetchObject( $res ); 00636 00637 if ( $row ) { 00638 $pos = isset( $row->Exec_master_log_pos ) ? $row->Exec_master_log_pos : $row->Exec_Master_Log_Pos; 00639 return new MySQLMasterPos( $row->Relay_Master_Log_File, $pos ); 00640 } else { 00641 return false; 00642 } 00643 } 00644 00650 function getMasterPos() { 00651 if ( $this->mFakeMaster ) { 00652 return parent::getMasterPos(); 00653 } 00654 00655 $res = $this->query( 'SHOW MASTER STATUS', 'DatabaseBase::getMasterPos' ); 00656 $row = $this->fetchObject( $res ); 00657 00658 if ( $row ) { 00659 return new MySQLMasterPos( $row->File, $row->Position ); 00660 } else { 00661 return false; 00662 } 00663 } 00664 00669 function useIndexClause( $index ) { 00670 return "FORCE INDEX (" . $this->indexName( $index ) . ")"; 00671 } 00672 00676 function lowPriorityOption() { 00677 return 'LOW_PRIORITY'; 00678 } 00679 00683 public function getSoftwareLink() { 00684 $version = $this->getServerVersion(); 00685 if ( strpos( $version, 'MariaDB' ) !== false ) { 00686 return '[{{int:version-db-mariadb-url}} MariaDB]'; 00687 } elseif ( strpos( $version, 'percona' ) !== false ) { 00688 return '[{{int:version-db-percona-url}} Percona Server]'; 00689 } else { 00690 return '[{{int:version-db-mysql-url}} MySQL]'; 00691 } 00692 } 00693 00697 public function setSessionOptions( array $options ) { 00698 if ( isset( $options['connTimeout'] ) ) { 00699 $timeout = (int)$options['connTimeout']; 00700 $this->query( "SET net_read_timeout=$timeout" ); 00701 $this->query( "SET net_write_timeout=$timeout" ); 00702 } 00703 } 00704 00705 public function streamStatementEnd( &$sql, &$newLine ) { 00706 if ( strtoupper( substr( $newLine, 0, 9 ) ) == 'DELIMITER' ) { 00707 preg_match( '/^DELIMITER\s+(\S+)/', $newLine, $m ); 00708 $this->delimiter = $m[1]; 00709 $newLine = ''; 00710 } 00711 return parent::streamStatementEnd( $sql, $newLine ); 00712 } 00713 00722 public function lockIsFree( $lockName, $method ) { 00723 $lockName = $this->addQuotes( $lockName ); 00724 $result = $this->query( "SELECT IS_FREE_LOCK($lockName) AS lockstatus", $method ); 00725 $row = $this->fetchObject( $result ); 00726 return ( $row->lockstatus == 1 ); 00727 } 00728 00735 public function lock( $lockName, $method, $timeout = 5 ) { 00736 $lockName = $this->addQuotes( $lockName ); 00737 $result = $this->query( "SELECT GET_LOCK($lockName, $timeout) AS lockstatus", $method ); 00738 $row = $this->fetchObject( $result ); 00739 00740 if ( $row->lockstatus == 1 ) { 00741 return true; 00742 } else { 00743 wfDebug( __METHOD__ . " failed to acquire lock\n" ); 00744 return false; 00745 } 00746 } 00747 00754 public function unlock( $lockName, $method ) { 00755 $lockName = $this->addQuotes( $lockName ); 00756 $result = $this->query( "SELECT RELEASE_LOCK($lockName) as lockstatus", $method ); 00757 $row = $this->fetchObject( $result ); 00758 return ( $row->lockstatus == 1 ); 00759 } 00760 00768 public function lockTables( $read, $write, $method, $lowPriority = true ) { 00769 $items = array(); 00770 00771 foreach ( $write as $table ) { 00772 $tbl = $this->tableName( $table ) . 00773 ( $lowPriority ? ' LOW_PRIORITY' : '' ) . 00774 ' WRITE'; 00775 $items[] = $tbl; 00776 } 00777 foreach ( $read as $table ) { 00778 $items[] = $this->tableName( $table ) . ' READ'; 00779 } 00780 $sql = "LOCK TABLES " . implode( ',', $items ); 00781 $this->query( $sql, $method ); 00782 return true; 00783 } 00784 00789 public function unlockTables( $method ) { 00790 $this->query( "UNLOCK TABLES", $method ); 00791 return true; 00792 } 00793 00800 public function getSearchEngine() { 00801 return 'SearchMySQL'; 00802 } 00803 00808 public function setBigSelects( $value = true ) { 00809 if ( $value === 'default' ) { 00810 if ( $this->mDefaultBigSelects === null ) { 00811 # Function hasn't been called before so it must already be set to the default 00812 return; 00813 } else { 00814 $value = $this->mDefaultBigSelects; 00815 } 00816 } elseif ( $this->mDefaultBigSelects === null ) { 00817 $this->mDefaultBigSelects = (bool)$this->selectField( false, '@@sql_big_selects' ); 00818 } 00819 $encValue = $value ? '1' : '0'; 00820 $this->query( "SET sql_big_selects=$encValue", __METHOD__ ); 00821 } 00822 00834 function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = __METHOD__ ) { 00835 if ( !$conds ) { 00836 throw new DBUnexpectedError( $this, 'DatabaseBase::deleteJoin() called with empty $conds' ); 00837 } 00838 00839 $delTable = $this->tableName( $delTable ); 00840 $joinTable = $this->tableName( $joinTable ); 00841 $sql = "DELETE $delTable FROM $delTable, $joinTable WHERE $delVar=$joinVar "; 00842 00843 if ( $conds != '*' ) { 00844 $sql .= ' AND ' . $this->makeList( $conds, LIST_AND ); 00845 } 00846 00847 return $this->query( $sql, $fname ); 00848 } 00849 00859 public function upsert( 00860 $table, array $rows, array $uniqueIndexes, array $set, $fname = __METHOD__ 00861 ) { 00862 if ( !count( $rows ) ) { 00863 return true; // nothing to do 00864 } 00865 $rows = is_array( reset( $rows ) ) ? $rows : array( $rows ); 00866 00867 $table = $this->tableName( $table ); 00868 $columns = array_keys( $rows[0] ); 00869 00870 $sql = "INSERT INTO $table (" . implode( ',', $columns ) . ') VALUES '; 00871 $rowTuples = array(); 00872 foreach ( $rows as $row ) { 00873 $rowTuples[] = '(' . $this->makeList( $row ) . ')'; 00874 } 00875 $sql .= implode( ',', $rowTuples ); 00876 $sql .= " ON DUPLICATE KEY UPDATE " . $this->makeList( $set, LIST_SET ); 00877 00878 return (bool)$this->query( $sql, $fname ); 00879 } 00880 00886 function getServerUptime() { 00887 $vars = $this->getMysqlStatus( 'Uptime' ); 00888 return (int)$vars['Uptime']; 00889 } 00890 00896 function wasDeadlock() { 00897 return $this->lastErrno() == 1213; 00898 } 00899 00905 function wasLockTimeout() { 00906 return $this->lastErrno() == 1205; 00907 } 00908 00915 function wasErrorReissuable() { 00916 return $this->lastErrno() == 2013 || $this->lastErrno() == 2006; 00917 } 00918 00924 function wasReadOnlyError() { 00925 return $this->lastErrno() == 1223 || 00926 ( $this->lastErrno() == 1290 && strpos( $this->lastError(), '--read-only' ) !== false ); 00927 } 00928 00935 function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = __METHOD__ ) { 00936 $tmp = $temporary ? 'TEMPORARY ' : ''; 00937 $newName = $this->addIdentifierQuotes( $newName ); 00938 $oldName = $this->addIdentifierQuotes( $oldName ); 00939 $query = "CREATE $tmp TABLE $newName (LIKE $oldName)"; 00940 $this->query( $query, $fname ); 00941 } 00942 00950 function listTables( $prefix = null, $fname = __METHOD__ ) { 00951 $result = $this->query( "SHOW TABLES", $fname ); 00952 00953 $endArray = array(); 00954 00955 foreach ( $result as $table ) { 00956 $vars = get_object_vars( $table ); 00957 $table = array_pop( $vars ); 00958 00959 if ( !$prefix || strpos( $table, $prefix ) === 0 ) { 00960 $endArray[] = $table; 00961 } 00962 } 00963 00964 return $endArray; 00965 } 00966 00972 public function dropTable( $tableName, $fName = __METHOD__ ) { 00973 if ( !$this->tableExists( $tableName, $fName ) ) { 00974 return false; 00975 } 00976 return $this->query( "DROP TABLE IF EXISTS " . $this->tableName( $tableName ), $fName ); 00977 } 00978 00982 protected function getDefaultSchemaVars() { 00983 $vars = parent::getDefaultSchemaVars(); 00984 $vars['wgDBTableOptions'] = str_replace( 'TYPE', 'ENGINE', $GLOBALS['wgDBTableOptions'] ); 00985 $vars['wgDBTableOptions'] = str_replace( 'CHARSET=mysql4', 'CHARSET=binary', $vars['wgDBTableOptions'] ); 00986 return $vars; 00987 } 00988 00995 function getMysqlStatus( $which = "%" ) { 00996 $res = $this->query( "SHOW STATUS LIKE '{$which}'" ); 00997 $status = array(); 00998 00999 foreach ( $res as $row ) { 01000 $status[$row->Variable_name] = $row->Value; 01001 } 01002 01003 return $status; 01004 } 01005 01015 public function listViews( $prefix = null, $fname = __METHOD__ ) { 01016 01017 if ( !isset( $this->allViews ) ) { 01018 01019 // The name of the column containing the name of the VIEW 01020 $propertyName = 'Tables_in_' . $this->mDBname; 01021 01022 // Query for the VIEWS 01023 $result = $this->query( 'SHOW FULL TABLES WHERE TABLE_TYPE = "VIEW"' ); 01024 $this->allViews = array(); 01025 while ( ($row = $this->fetchRow($result)) !== false ) { 01026 array_push( $this->allViews, $row[$propertyName] ); 01027 } 01028 } 01029 01030 if ( is_null($prefix) || $prefix === '' ) { 01031 return $this->allViews; 01032 } 01033 01034 $filteredViews = array(); 01035 foreach ( $this->allViews as $viewName ) { 01036 // Does the name of this VIEW start with the table-prefix? 01037 if ( strpos( $viewName, $prefix ) === 0 ) { 01038 array_push( $filteredViews, $viewName ); 01039 } 01040 } 01041 return $filteredViews; 01042 } 01043 01051 public function isView( $name, $prefix = null ) { 01052 return in_array( $name, $this->listViews( $prefix ) ); 01053 } 01054 01055 } 01056 01057 01058 01063 class MySQLField implements Field { 01064 private $name, $tablename, $default, $max_length, $nullable, 01065 $is_pk, $is_unique, $is_multiple, $is_key, $type, $binary; 01066 01067 function __construct( $info ) { 01068 $this->name = $info->name; 01069 $this->tablename = $info->table; 01070 $this->default = $info->def; 01071 $this->max_length = $info->max_length; 01072 $this->nullable = !$info->not_null; 01073 $this->is_pk = $info->primary_key; 01074 $this->is_unique = $info->unique_key; 01075 $this->is_multiple = $info->multiple_key; 01076 $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple ); 01077 $this->type = $info->type; 01078 $this->binary = isset( $info->binary ) ? $info->binary : false; 01079 } 01080 01084 function name() { 01085 return $this->name; 01086 } 01087 01091 function tableName() { 01092 return $this->tableName; 01093 } 01094 01098 function type() { 01099 return $this->type; 01100 } 01101 01105 function isNullable() { 01106 return $this->nullable; 01107 } 01108 01109 function defaultValue() { 01110 return $this->default; 01111 } 01112 01116 function isKey() { 01117 return $this->is_key; 01118 } 01119 01123 function isMultipleKey() { 01124 return $this->is_multiple; 01125 } 01126 01127 function isBinary() { 01128 return $this->binary; 01129 } 01130 } 01131 01132 class MySQLMasterPos implements DBMasterPos { 01133 var $file, $pos; 01134 01135 function __construct( $file, $pos ) { 01136 $this->file = $file; 01137 $this->pos = $pos; 01138 } 01139 01140 function __toString() { 01141 // e.g db1034-bin.000976/843431247 01142 return "{$this->file}/{$this->pos}"; 01143 } 01144 01148 protected function getCoordinates() { 01149 $m = array(); 01150 if ( preg_match( '!\.(\d+)/(\d+)$!', (string)$this, $m ) ) { 01151 return array( (int)$m[1], (int)$m[2] ); 01152 } 01153 return false; 01154 } 01155 01156 function hasReached( MySQLMasterPos $pos ) { 01157 $thisPos = $this->getCoordinates(); 01158 $thatPos = $pos->getCoordinates(); 01159 return ( $thisPos && $thatPos && $thisPos >= $thatPos ); 01160 } 01161 }