2009-06-15 18:39:41 +00:00
|
|
|
<?php
|
2010-08-01 21:13:44 +00:00
|
|
|
/**
|
|
|
|
|
* This is the MySQL database abstraction layer.
|
|
|
|
|
*
|
|
|
|
|
* @file
|
|
|
|
|
* @ingroup Database
|
|
|
|
|
*/
|
|
|
|
|
|
2009-06-15 18:39:41 +00:00
|
|
|
/**
|
|
|
|
|
* Database abstraction object for mySQL
|
|
|
|
|
* Inherit all methods and properties of Database::Database()
|
|
|
|
|
*
|
|
|
|
|
* @ingroup Database
|
|
|
|
|
* @see Database
|
|
|
|
|
*/
|
|
|
|
|
class DatabaseMysql extends DatabaseBase {
|
2010-01-08 00:31:24 +00:00
|
|
|
function getType() {
|
|
|
|
|
return 'mysql';
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-20 12:09:22 +00:00
|
|
|
protected function doQuery( $sql ) {
|
2009-06-15 18:39:41 +00:00
|
|
|
if( $this->bufferResults() ) {
|
|
|
|
|
$ret = mysql_query( $sql, $this->mConn );
|
|
|
|
|
} else {
|
|
|
|
|
$ret = mysql_unbuffered_query( $sql, $this->mConn );
|
|
|
|
|
}
|
|
|
|
|
return $ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function open( $server, $user, $password, $dbName ) {
|
|
|
|
|
global $wgAllDBsAreLocalhost;
|
|
|
|
|
wfProfileIn( __METHOD__ );
|
|
|
|
|
|
2010-08-26 22:39:14 +00:00
|
|
|
# Load mysql.so if we don't have it
|
2010-06-14 18:09:19 +00:00
|
|
|
wfDl( 'mysql' );
|
2009-06-15 18:39:41 +00:00
|
|
|
|
|
|
|
|
# Fail now
|
|
|
|
|
# Otherwise we get a suppressed fatal error, which is very hard to track down
|
|
|
|
|
if ( !function_exists( 'mysql_connect' ) ) {
|
|
|
|
|
throw new DBConnectionError( $this, "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Debugging hack -- fake cluster
|
|
|
|
|
if ( $wgAllDBsAreLocalhost ) {
|
|
|
|
|
$realServer = 'localhost';
|
|
|
|
|
} else {
|
|
|
|
|
$realServer = $server;
|
|
|
|
|
}
|
|
|
|
|
$this->close();
|
|
|
|
|
$this->mServer = $server;
|
|
|
|
|
$this->mUser = $user;
|
|
|
|
|
$this->mPassword = $password;
|
|
|
|
|
$this->mDBname = $dbName;
|
|
|
|
|
|
|
|
|
|
wfProfileIn("dbconnect-$server");
|
|
|
|
|
|
|
|
|
|
# The kernel's default SYN retransmission period is far too slow for us,
|
|
|
|
|
# so we use a short timeout plus a manual retry. Retrying means that a small
|
|
|
|
|
# but finite rate of SYN packet loss won't cause user-visible errors.
|
|
|
|
|
$this->mConn = false;
|
|
|
|
|
if ( ini_get( 'mysql.connect_timeout' ) <= 3 ) {
|
|
|
|
|
$numAttempts = 2;
|
|
|
|
|
} else {
|
|
|
|
|
$numAttempts = 1;
|
|
|
|
|
}
|
|
|
|
|
$this->installErrorHandler();
|
|
|
|
|
for ( $i = 0; $i < $numAttempts && !$this->mConn; $i++ ) {
|
|
|
|
|
if ( $i > 1 ) {
|
|
|
|
|
usleep( 1000 );
|
|
|
|
|
}
|
|
|
|
|
if ( $this->mFlags & DBO_PERSISTENT ) {
|
|
|
|
|
$this->mConn = mysql_pconnect( $realServer, $user, $password );
|
|
|
|
|
} else {
|
|
|
|
|
# Create a new connection...
|
|
|
|
|
$this->mConn = mysql_connect( $realServer, $user, $password, true );
|
|
|
|
|
}
|
2010-10-24 21:02:57 +00:00
|
|
|
#if ( $this->mConn === false ) {
|
2009-06-15 18:39:41 +00:00
|
|
|
#$iplus = $i + 1;
|
2010-05-19 02:12:59 +00:00
|
|
|
#wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
|
2010-10-24 21:02:57 +00:00
|
|
|
#}
|
2009-06-15 18:39:41 +00:00
|
|
|
}
|
|
|
|
|
$phpError = $this->restoreErrorHandler();
|
|
|
|
|
# Always log connection errors
|
|
|
|
|
if ( !$this->mConn ) {
|
|
|
|
|
$error = $this->lastError();
|
|
|
|
|
if ( !$error ) {
|
|
|
|
|
$error = $phpError;
|
|
|
|
|
}
|
|
|
|
|
wfLogDBError( "Error connecting to {$this->mServer}: $error\n" );
|
|
|
|
|
wfDebug( "DB connection error\n" );
|
|
|
|
|
wfDebug( "Server: $server, User: $user, Password: " .
|
|
|
|
|
substr( $password, 0, 3 ) . "..., error: " . mysql_error() . "\n" );
|
|
|
|
|
}
|
2010-05-19 02:12:59 +00:00
|
|
|
|
2009-06-15 18:39:41 +00:00
|
|
|
wfProfileOut("dbconnect-$server");
|
|
|
|
|
|
|
|
|
|
if ( $dbName != '' && $this->mConn !== false ) {
|
|
|
|
|
$success = @/**/mysql_select_db( $dbName, $this->mConn );
|
|
|
|
|
if ( !$success ) {
|
|
|
|
|
$error = "Error selecting database $dbName on server {$this->mServer} " .
|
|
|
|
|
"from client host " . wfHostname() . "\n";
|
|
|
|
|
wfLogDBError(" Error selecting database $dbName on server {$this->mServer} \n");
|
|
|
|
|
wfDebug( $error );
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
# Delay USE query
|
|
|
|
|
$success = (bool)$this->mConn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $success ) {
|
|
|
|
|
$version = $this->getServerVersion();
|
|
|
|
|
if ( version_compare( $version, '4.1' ) >= 0 ) {
|
|
|
|
|
// Tell the server we're communicating with it in UTF-8.
|
|
|
|
|
// This may engage various charset conversions.
|
|
|
|
|
global $wgDBmysql5;
|
|
|
|
|
if( $wgDBmysql5 ) {
|
|
|
|
|
$this->query( 'SET NAMES utf8', __METHOD__ );
|
2010-07-28 21:59:40 +00:00
|
|
|
} else {
|
|
|
|
|
$this->query( 'SET NAMES binary', __METHOD__ );
|
2009-06-15 18:39:41 +00:00
|
|
|
}
|
2010-06-25 09:19:34 +00:00
|
|
|
// Set SQL mode, default is turning them all off, can be overridden or skipped with null
|
2010-06-25 09:36:39 +00:00
|
|
|
global $wgSQLMode;
|
|
|
|
|
if ( is_string( $wgSQLMode ) ) {
|
|
|
|
|
$mode = $this->addQuotes( $wgSQLMode );
|
|
|
|
|
$this->query( "SET sql_mode = $mode", __METHOD__ );
|
2010-06-25 09:19:34 +00:00
|
|
|
}
|
2009-06-15 18:39:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Turn off strict mode if it is on
|
|
|
|
|
} else {
|
|
|
|
|
$this->reportConnectionError( $phpError );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->mOpened = $success;
|
|
|
|
|
wfProfileOut( __METHOD__ );
|
|
|
|
|
return $success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function close() {
|
|
|
|
|
$this->mOpened = false;
|
|
|
|
|
if ( $this->mConn ) {
|
|
|
|
|
if ( $this->trxLevel() ) {
|
2009-12-14 23:05:35 +00:00
|
|
|
$this->commit();
|
2009-06-15 18:39:41 +00:00
|
|
|
}
|
|
|
|
|
return mysql_close( $this->mConn );
|
|
|
|
|
} else {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function freeResult( $res ) {
|
|
|
|
|
if ( $res instanceof ResultWrapper ) {
|
|
|
|
|
$res = $res->result;
|
|
|
|
|
}
|
|
|
|
|
if ( !@/**/mysql_free_result( $res ) ) {
|
|
|
|
|
throw new DBUnexpectedError( $this, "Unable to free MySQL result" );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function fetchObject( $res ) {
|
|
|
|
|
if ( $res instanceof ResultWrapper ) {
|
|
|
|
|
$res = $res->result;
|
|
|
|
|
}
|
|
|
|
|
@/**/$row = mysql_fetch_object( $res );
|
|
|
|
|
if( $this->lastErrno() ) {
|
|
|
|
|
throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) );
|
|
|
|
|
}
|
|
|
|
|
return $row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function fetchRow( $res ) {
|
|
|
|
|
if ( $res instanceof ResultWrapper ) {
|
|
|
|
|
$res = $res->result;
|
|
|
|
|
}
|
|
|
|
|
@/**/$row = mysql_fetch_array( $res );
|
|
|
|
|
if ( $this->lastErrno() ) {
|
|
|
|
|
throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) );
|
|
|
|
|
}
|
|
|
|
|
return $row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function numRows( $res ) {
|
|
|
|
|
if ( $res instanceof ResultWrapper ) {
|
|
|
|
|
$res = $res->result;
|
|
|
|
|
}
|
|
|
|
|
@/**/$n = mysql_num_rows( $res );
|
|
|
|
|
if( $this->lastErrno() ) {
|
|
|
|
|
throw new DBUnexpectedError( $this, 'Error in numRows(): ' . htmlspecialchars( $this->lastError() ) );
|
|
|
|
|
}
|
|
|
|
|
return $n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function numFields( $res ) {
|
|
|
|
|
if ( $res instanceof ResultWrapper ) {
|
|
|
|
|
$res = $res->result;
|
|
|
|
|
}
|
|
|
|
|
return mysql_num_fields( $res );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function fieldName( $res, $n ) {
|
|
|
|
|
if ( $res instanceof ResultWrapper ) {
|
|
|
|
|
$res = $res->result;
|
|
|
|
|
}
|
|
|
|
|
return mysql_field_name( $res, $n );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function insertId() { return mysql_insert_id( $this->mConn ); }
|
|
|
|
|
|
|
|
|
|
function dataSeek( $res, $row ) {
|
|
|
|
|
if ( $res instanceof ResultWrapper ) {
|
|
|
|
|
$res = $res->result;
|
|
|
|
|
}
|
|
|
|
|
return mysql_data_seek( $res, $row );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function lastErrno() {
|
|
|
|
|
if ( $this->mConn ) {
|
|
|
|
|
return mysql_errno( $this->mConn );
|
|
|
|
|
} else {
|
|
|
|
|
return mysql_errno();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function lastError() {
|
|
|
|
|
if ( $this->mConn ) {
|
|
|
|
|
# Even if it's non-zero, it can still be invalid
|
|
|
|
|
wfSuppressWarnings();
|
|
|
|
|
$error = mysql_error( $this->mConn );
|
|
|
|
|
if ( !$error ) {
|
|
|
|
|
$error = mysql_error();
|
|
|
|
|
}
|
|
|
|
|
wfRestoreWarnings();
|
|
|
|
|
} else {
|
|
|
|
|
$error = mysql_error();
|
|
|
|
|
}
|
|
|
|
|
if( $error ) {
|
|
|
|
|
$error .= ' (' . $this->mServer . ')';
|
|
|
|
|
}
|
|
|
|
|
return $error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function affectedRows() { return mysql_affected_rows( $this->mConn ); }
|
2010-05-19 02:12:59 +00:00
|
|
|
|
2011-06-23 03:14:11 +00:00
|
|
|
function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabaseMysql::replace' ) {
|
|
|
|
|
return $this->nativeReplace( $table, $rows, $fname );
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-21 12:21:09 +00:00
|
|
|
/**
|
|
|
|
|
* Estimate rows in dataset
|
|
|
|
|
* Returns estimated count, based on EXPLAIN output
|
|
|
|
|
* Takes same arguments as Database::select()
|
|
|
|
|
*/
|
2010-11-23 11:21:00 +00:00
|
|
|
public function estimateRowCount( $table, $vars='*', $conds='', $fname = 'DatabaseMysql::estimateRowCount', $options = array() ) {
|
2009-10-21 12:21:09 +00:00
|
|
|
$options['EXPLAIN'] = true;
|
|
|
|
|
$res = $this->select( $table, $vars, $conds, $fname, $options );
|
2010-08-25 17:45:00 +00:00
|
|
|
if ( $res === false ) {
|
2009-10-21 12:21:09 +00:00
|
|
|
return false;
|
2010-08-25 17:45:00 +00:00
|
|
|
}
|
2009-10-21 12:21:09 +00:00
|
|
|
if ( !$this->numRows( $res ) ) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$rows = 1;
|
2010-10-13 23:11:40 +00:00
|
|
|
foreach ( $res as $plan ) {
|
2009-10-21 12:21:09 +00:00
|
|
|
$rows *= $plan->rows > 0 ? $plan->rows : 1; // avoid resetting to zero
|
|
|
|
|
}
|
2010-05-19 02:12:59 +00:00
|
|
|
return $rows;
|
2009-10-21 12:21:09 +00:00
|
|
|
}
|
2009-06-15 18:39:41 +00:00
|
|
|
|
|
|
|
|
function fieldInfo( $table, $field ) {
|
|
|
|
|
$table = $this->tableName( $table );
|
2010-07-02 10:01:09 +00:00
|
|
|
$res = $this->query( "SELECT * FROM $table LIMIT 1", __METHOD__, true );
|
|
|
|
|
if ( !$res ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2009-06-15 18:39:41 +00:00
|
|
|
$n = mysql_num_fields( $res->result );
|
|
|
|
|
for( $i = 0; $i < $n; $i++ ) {
|
|
|
|
|
$meta = mysql_fetch_field( $res->result, $i );
|
|
|
|
|
if( $field == $meta->name ) {
|
|
|
|
|
return new MySQLField($meta);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-23 11:14:48 +00:00
|
|
|
/**
|
|
|
|
|
* Get information about an index into an object
|
|
|
|
|
* Returns false if the index does not exist
|
|
|
|
|
*/
|
|
|
|
|
function indexInfo( $table, $index, $fname = 'DatabaseMysql::indexInfo' ) {
|
|
|
|
|
# SHOW INDEX works in MySQL 3.23.58, but SHOW INDEXES does not.
|
|
|
|
|
# SHOW INDEX should work for 3.x and up:
|
|
|
|
|
# http://dev.mysql.com/doc/mysql/en/SHOW_INDEX.html
|
|
|
|
|
$table = $this->tableName( $table );
|
|
|
|
|
$index = $this->indexName( $index );
|
|
|
|
|
$sql = 'SHOW INDEX FROM ' . $table;
|
|
|
|
|
$res = $this->query( $sql, $fname );
|
|
|
|
|
|
|
|
|
|
if ( !$res ) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$result = array();
|
|
|
|
|
|
|
|
|
|
foreach ( $res as $row ) {
|
|
|
|
|
if ( $row->Key_name == $index ) {
|
|
|
|
|
$result[] = $row;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return empty( $result ) ? false : $result;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-15 18:39:41 +00:00
|
|
|
function selectDB( $db ) {
|
|
|
|
|
$this->mDBname = $db;
|
|
|
|
|
return mysql_select_db( $db, $this->mConn );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function strencode( $s ) {
|
2010-05-19 02:12:59 +00:00
|
|
|
$sQuoted = mysql_real_escape_string( $s, $this->mConn );
|
|
|
|
|
|
|
|
|
|
if($sQuoted === false) {
|
|
|
|
|
$this->ping();
|
|
|
|
|
$sQuoted = mysql_real_escape_string( $s, $this->mConn );
|
|
|
|
|
}
|
|
|
|
|
return $sQuoted;
|
2009-06-15 18:39:41 +00:00
|
|
|
}
|
|
|
|
|
|
2010-12-04 15:35:36 +00:00
|
|
|
/**
|
|
|
|
|
* MySQL uses `backticks` for identifier quoting instead of the sql standard "double quotes".
|
|
|
|
|
*/
|
|
|
|
|
public function addIdentifierQuotes( $s ) {
|
|
|
|
|
return "`" . $this->strencode( $s ) . "`";
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-12 16:34:12 +00:00
|
|
|
public function isQuotedIdentifier( $name ) {
|
2011-06-09 19:57:10 +00:00
|
|
|
return strlen($name) && $name[0] == '`' && substr( $name, -1, 1 ) == '`';
|
2011-04-12 16:34:12 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-15 18:39:41 +00:00
|
|
|
function ping() {
|
|
|
|
|
$ping = mysql_ping( $this->mConn );
|
|
|
|
|
if ( $ping ) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-19 02:12:59 +00:00
|
|
|
mysql_close( $this->mConn );
|
|
|
|
|
$this->mOpened = false;
|
|
|
|
|
$this->mConn = false;
|
|
|
|
|
$this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
|
|
|
|
|
return true;
|
2009-06-15 18:39:41 +00:00
|
|
|
}
|
|
|
|
|
|
2010-07-02 13:17:28 +00:00
|
|
|
/**
|
|
|
|
|
* Returns slave lag.
|
2011-06-23 03:14:11 +00:00
|
|
|
*
|
|
|
|
|
* On MySQL 4.1.9 and later, this will do a SHOW SLAVE STATUS. On earlier
|
|
|
|
|
* versions of MySQL, it uses SHOW PROCESSLIST, which requires the PROCESS
|
|
|
|
|
* privilege.
|
|
|
|
|
*
|
2010-07-02 13:17:28 +00:00
|
|
|
* @result int
|
|
|
|
|
*/
|
|
|
|
|
function getLag() {
|
|
|
|
|
if ( !is_null( $this->mFakeSlaveLag ) ) {
|
|
|
|
|
wfDebug( "getLag: fake slave lagged {$this->mFakeSlaveLag} seconds\n" );
|
|
|
|
|
return $this->mFakeSlaveLag;
|
|
|
|
|
}
|
2011-06-23 03:14:11 +00:00
|
|
|
|
|
|
|
|
if ( version_compare( $this->getServerVersion(), '4.1.9', '>=' ) ) {
|
|
|
|
|
return $this->getLagFromSlaveStatus();
|
|
|
|
|
} else {
|
|
|
|
|
return $this->getLagFromProcesslist();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getLagFromSlaveStatus() {
|
|
|
|
|
$res = $this->query( 'SHOW SLAVE STATUS', __METHOD__ );
|
|
|
|
|
if ( !$res ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$row = $res->fetchObject();
|
|
|
|
|
if ( !$row ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if ( strval( $row->Seconds_Behind_Master ) === '' ) {
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
return intval( $row->Seconds_Behind_Master );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getLagFromProcesslist() {
|
2010-07-02 13:17:28 +00:00
|
|
|
$res = $this->query( 'SHOW PROCESSLIST', __METHOD__ );
|
2010-09-28 11:39:10 +00:00
|
|
|
if( !$res ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2010-07-02 13:17:28 +00:00
|
|
|
# Find slave SQL thread
|
2010-09-28 11:39:10 +00:00
|
|
|
foreach( $res as $row ) {
|
2010-07-02 13:17:28 +00:00
|
|
|
/* This should work for most situations - when default db
|
|
|
|
|
* for thread is not specified, it had no events executed,
|
|
|
|
|
* and therefore it doesn't know yet how lagged it is.
|
|
|
|
|
*
|
|
|
|
|
* Relay log I/O thread does not select databases.
|
|
|
|
|
*/
|
|
|
|
|
if ( $row->User == 'system user' &&
|
|
|
|
|
$row->State != 'Waiting for master to send event' &&
|
|
|
|
|
$row->State != 'Connecting to master' &&
|
|
|
|
|
$row->State != 'Queueing master event to the relay log' &&
|
|
|
|
|
$row->State != 'Waiting for master update' &&
|
|
|
|
|
$row->State != 'Requesting binlog dump' &&
|
|
|
|
|
$row->State != 'Waiting to reconnect after a failed master event read' &&
|
|
|
|
|
$row->State != 'Reconnecting after a failed master event read' &&
|
|
|
|
|
$row->State != 'Registering slave on master'
|
|
|
|
|
) {
|
|
|
|
|
# This is it, return the time (except -ve)
|
|
|
|
|
if ( $row->Time > 0x7fffffff ) {
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
return $row->Time;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-06-23 03:14:11 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Wait for the slave to catch up to a given master position.
|
|
|
|
|
*
|
|
|
|
|
* @param $pos DBMasterPos object
|
|
|
|
|
* @param $timeout Integer: the maximum number of seconds to wait for synchronisation
|
|
|
|
|
*/
|
|
|
|
|
function masterPosWait( DBMasterPos $pos, $timeout ) {
|
|
|
|
|
$fname = 'DatabaseBase::masterPosWait';
|
|
|
|
|
wfProfileIn( $fname );
|
|
|
|
|
|
|
|
|
|
# Commit any open transactions
|
|
|
|
|
if ( $this->mTrxLevel ) {
|
|
|
|
|
$this->commit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !is_null( $this->mFakeSlaveLag ) ) {
|
|
|
|
|
$status = parent::masterPosWait( $pos, $timeout );
|
|
|
|
|
wfProfileOut( $fname );
|
|
|
|
|
return $status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set
|
|
|
|
|
$encFile = $this->addQuotes( $pos->file );
|
|
|
|
|
$encPos = intval( $pos->pos );
|
|
|
|
|
$sql = "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)";
|
|
|
|
|
$res = $this->doQuery( $sql );
|
|
|
|
|
|
|
|
|
|
if ( $res && $row = $this->fetchRow( $res ) ) {
|
|
|
|
|
wfProfileOut( $fname );
|
|
|
|
|
return $row[0];
|
|
|
|
|
} else {
|
|
|
|
|
wfProfileOut( $fname );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the position of the master from SHOW SLAVE STATUS
|
|
|
|
|
*
|
|
|
|
|
* @return MySQLMasterPos|false
|
|
|
|
|
*/
|
|
|
|
|
function getSlavePos() {
|
|
|
|
|
if ( !is_null( $this->mFakeSlaveLag ) ) {
|
|
|
|
|
return parent::getSlavePos();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$res = $this->query( 'SHOW SLAVE STATUS', 'DatabaseBase::getSlavePos' );
|
|
|
|
|
$row = $this->fetchObject( $res );
|
|
|
|
|
|
|
|
|
|
if ( $row ) {
|
|
|
|
|
$pos = isset( $row->Exec_master_log_pos ) ? $row->Exec_master_log_pos : $row->Exec_Master_Log_Pos;
|
|
|
|
|
return new MySQLMasterPos( $row->Relay_Master_Log_File, $pos );
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the position of the master from SHOW MASTER STATUS
|
|
|
|
|
*
|
|
|
|
|
* @return MySQLMasterPos|false
|
|
|
|
|
*/
|
|
|
|
|
function getMasterPos() {
|
|
|
|
|
if ( $this->mFakeMaster ) {
|
|
|
|
|
return parent::getMasterPos();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$res = $this->query( 'SHOW MASTER STATUS', 'DatabaseBase::getMasterPos' );
|
|
|
|
|
$row = $this->fetchObject( $res );
|
|
|
|
|
|
|
|
|
|
if ( $row ) {
|
|
|
|
|
return new MySQLMasterPos( $row->File, $row->Position );
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-07-02 13:17:28 +00:00
|
|
|
|
2009-06-15 18:39:41 +00:00
|
|
|
function getServerVersion() {
|
|
|
|
|
return mysql_get_server_info( $this->mConn );
|
|
|
|
|
}
|
2009-06-16 21:00:38 +00:00
|
|
|
|
|
|
|
|
function useIndexClause( $index ) {
|
|
|
|
|
return "FORCE INDEX (" . $this->indexName( $index ) . ")";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function lowPriorityOption() {
|
|
|
|
|
return 'LOW_PRIORITY';
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-22 20:55:07 +00:00
|
|
|
public static function getSoftwareLink() {
|
2009-06-16 21:00:38 +00:00
|
|
|
return '[http://www.mysql.com/ MySQL]';
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-20 02:20:15 +00:00
|
|
|
function standardSelectDistinct() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-16 21:00:38 +00:00
|
|
|
public function setTimeout( $timeout ) {
|
|
|
|
|
$this->query( "SET net_read_timeout=$timeout" );
|
|
|
|
|
$this->query( "SET net_write_timeout=$timeout" );
|
|
|
|
|
}
|
2009-06-25 00:40:12 +00:00
|
|
|
|
|
|
|
|
public function lock( $lockName, $method, $timeout = 5 ) {
|
|
|
|
|
$lockName = $this->addQuotes( $lockName );
|
|
|
|
|
$result = $this->query( "SELECT GET_LOCK($lockName, $timeout) AS lockstatus", $method );
|
|
|
|
|
$row = $this->fetchObject( $result );
|
|
|
|
|
|
|
|
|
|
if( $row->lockstatus == 1 ) {
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
wfDebug( __METHOD__." failed to acquire lock\n" );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-10 10:15:59 +00:00
|
|
|
/**
|
|
|
|
|
* FROM MYSQL DOCS: http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_release-lock
|
|
|
|
|
*/
|
2009-06-25 00:40:12 +00:00
|
|
|
public function unlock( $lockName, $method ) {
|
|
|
|
|
$lockName = $this->addQuotes( $lockName );
|
|
|
|
|
$result = $this->query( "SELECT RELEASE_LOCK($lockName) as lockstatus", $method );
|
|
|
|
|
$row = $this->fetchObject( $result );
|
|
|
|
|
return $row->lockstatus;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-29 23:41:16 +00:00
|
|
|
public function lockTables( $read, $write, $method, $lowPriority = true ) {
|
2009-06-25 00:40:12 +00:00
|
|
|
$items = array();
|
|
|
|
|
|
|
|
|
|
foreach( $write as $table ) {
|
2010-05-19 02:12:59 +00:00
|
|
|
$tbl = $this->tableName( $table ) .
|
|
|
|
|
( $lowPriority ? ' LOW_PRIORITY' : '' ) .
|
2009-07-29 23:41:16 +00:00
|
|
|
' WRITE';
|
|
|
|
|
$items[] = $tbl;
|
2009-06-25 00:40:12 +00:00
|
|
|
}
|
|
|
|
|
foreach( $read as $table ) {
|
|
|
|
|
$items[] = $this->tableName( $table ) . ' READ';
|
|
|
|
|
}
|
|
|
|
|
$sql = "LOCK TABLES " . implode( ',', $items );
|
2009-07-27 23:36:30 +00:00
|
|
|
$this->query( $sql, $method );
|
2009-06-25 00:40:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function unlockTables( $method ) {
|
|
|
|
|
$this->query( "UNLOCK TABLES", $method );
|
|
|
|
|
}
|
2010-01-18 20:54:43 +00:00
|
|
|
|
2010-07-10 10:15:59 +00:00
|
|
|
/**
|
|
|
|
|
* Get search engine class. All subclasses of this
|
|
|
|
|
* need to implement this if they wish to use searching.
|
|
|
|
|
*
|
|
|
|
|
* @return String
|
|
|
|
|
*/
|
|
|
|
|
public function getSearchEngine() {
|
|
|
|
|
return 'SearchMySQL';
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-09 00:14:43 +00:00
|
|
|
public function setBigSelects( $value = true ) {
|
|
|
|
|
if ( $value === 'default' ) {
|
|
|
|
|
if ( $this->mDefaultBigSelects === null ) {
|
|
|
|
|
# Function hasn't been called before so it must already be set to the default
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
$value = $this->mDefaultBigSelects;
|
|
|
|
|
}
|
|
|
|
|
} elseif ( $this->mDefaultBigSelects === null ) {
|
|
|
|
|
$this->mDefaultBigSelects = (bool)$this->selectField( false, '@@sql_big_selects' );
|
|
|
|
|
}
|
|
|
|
|
$encValue = $value ? '1' : '0';
|
|
|
|
|
$this->query( "SET sql_big_selects=$encValue", __METHOD__ );
|
|
|
|
|
}
|
* Converted BagOStuff.php from the style of memcached-client.php to the standard MediaWiki style, including camel case, using protected visibility instead of initial underscore, abstract functions instead of stubs, stylize.php.
* In SqlBagOStuff, ignore errors due to a read-only database, per my comments on CR r42796. Same for LocalisationCache.
* Merged SqlBagOStuff and MediaWikiBagOStuff, that proved to be an awkward and unnecessary generalisation. Use the standard quoting wrapper functions instead of $db->query().
* Implemented atomic incr() and decr() functions for SqlBagOStuff.
* Made incr() and decr() generally work roughly the same as it does in memcached, respecting negative steps instead of ignoring such operations. This allows decr() to be implemented in terms of incr().
* Per bug 11533, in MessageCache.php, don't retry 20 times on a cache failure, that's really memcached-specific and won't be useful for other cache types. It's not really very useful for memcached either.
* Moved MySQL-specific implementations of wasDeadlock() and wasErrorReissuable() to DatabaseMysql.
* Briefly tested page views with $wgReadOnly=read_only=1, fixed an error from Article::viewUpdates(). A CentralAuth fix will be in a subsequent commit.
2009-08-15 03:45:19 +00:00
|
|
|
|
2011-06-18 20:26:31 +00:00
|
|
|
/**
|
|
|
|
|
* DELETE where the condition is a join. MySql uses multi-table deletes.
|
|
|
|
|
*/
|
|
|
|
|
function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'DatabaseBase::deleteJoin' ) {
|
|
|
|
|
if ( !$conds ) {
|
|
|
|
|
throw new DBUnexpectedError( $this, 'DatabaseBase::deleteJoin() called with empty $conds' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$delTable = $this->tableName( $delTable );
|
|
|
|
|
$joinTable = $this->tableName( $joinTable );
|
|
|
|
|
$sql = "DELETE $delTable FROM $delTable, $joinTable WHERE $delVar=$joinVar ";
|
|
|
|
|
|
|
|
|
|
if ( $conds != '*' ) {
|
|
|
|
|
$sql .= ' AND ' . $this->makeList( $conds, LIST_AND );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->query( $sql, $fname );
|
|
|
|
|
}
|
|
|
|
|
|
* Converted BagOStuff.php from the style of memcached-client.php to the standard MediaWiki style, including camel case, using protected visibility instead of initial underscore, abstract functions instead of stubs, stylize.php.
* In SqlBagOStuff, ignore errors due to a read-only database, per my comments on CR r42796. Same for LocalisationCache.
* Merged SqlBagOStuff and MediaWikiBagOStuff, that proved to be an awkward and unnecessary generalisation. Use the standard quoting wrapper functions instead of $db->query().
* Implemented atomic incr() and decr() functions for SqlBagOStuff.
* Made incr() and decr() generally work roughly the same as it does in memcached, respecting negative steps instead of ignoring such operations. This allows decr() to be implemented in terms of incr().
* Per bug 11533, in MessageCache.php, don't retry 20 times on a cache failure, that's really memcached-specific and won't be useful for other cache types. It's not really very useful for memcached either.
* Moved MySQL-specific implementations of wasDeadlock() and wasErrorReissuable() to DatabaseMysql.
* Briefly tested page views with $wgReadOnly=read_only=1, fixed an error from Article::viewUpdates(). A CentralAuth fix will be in a subsequent commit.
2009-08-15 03:45:19 +00:00
|
|
|
/**
|
|
|
|
|
* Determines if the last failure was due to a deadlock
|
|
|
|
|
*/
|
|
|
|
|
function wasDeadlock() {
|
|
|
|
|
return $this->lastErrno() == 1213;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-05-19 02:12:59 +00:00
|
|
|
* Determines if the last query error was something that should be dealt
|
* Converted BagOStuff.php from the style of memcached-client.php to the standard MediaWiki style, including camel case, using protected visibility instead of initial underscore, abstract functions instead of stubs, stylize.php.
* In SqlBagOStuff, ignore errors due to a read-only database, per my comments on CR r42796. Same for LocalisationCache.
* Merged SqlBagOStuff and MediaWikiBagOStuff, that proved to be an awkward and unnecessary generalisation. Use the standard quoting wrapper functions instead of $db->query().
* Implemented atomic incr() and decr() functions for SqlBagOStuff.
* Made incr() and decr() generally work roughly the same as it does in memcached, respecting negative steps instead of ignoring such operations. This allows decr() to be implemented in terms of incr().
* Per bug 11533, in MessageCache.php, don't retry 20 times on a cache failure, that's really memcached-specific and won't be useful for other cache types. It's not really very useful for memcached either.
* Moved MySQL-specific implementations of wasDeadlock() and wasErrorReissuable() to DatabaseMysql.
* Briefly tested page views with $wgReadOnly=read_only=1, fixed an error from Article::viewUpdates(). A CentralAuth fix will be in a subsequent commit.
2009-08-15 03:45:19 +00:00
|
|
|
* with by pinging the connection and reissuing the query
|
|
|
|
|
*/
|
|
|
|
|
function wasErrorReissuable() {
|
|
|
|
|
return $this->lastErrno() == 2013 || $this->lastErrno() == 2006;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determines if the last failure was due to the database being read-only.
|
|
|
|
|
*/
|
|
|
|
|
function wasReadOnlyError() {
|
2010-05-19 02:12:59 +00:00
|
|
|
return $this->lastErrno() == 1223 ||
|
* Converted BagOStuff.php from the style of memcached-client.php to the standard MediaWiki style, including camel case, using protected visibility instead of initial underscore, abstract functions instead of stubs, stylize.php.
* In SqlBagOStuff, ignore errors due to a read-only database, per my comments on CR r42796. Same for LocalisationCache.
* Merged SqlBagOStuff and MediaWikiBagOStuff, that proved to be an awkward and unnecessary generalisation. Use the standard quoting wrapper functions instead of $db->query().
* Implemented atomic incr() and decr() functions for SqlBagOStuff.
* Made incr() and decr() generally work roughly the same as it does in memcached, respecting negative steps instead of ignoring such operations. This allows decr() to be implemented in terms of incr().
* Per bug 11533, in MessageCache.php, don't retry 20 times on a cache failure, that's really memcached-specific and won't be useful for other cache types. It's not really very useful for memcached either.
* Moved MySQL-specific implementations of wasDeadlock() and wasErrorReissuable() to DatabaseMysql.
* Briefly tested page views with $wgReadOnly=read_only=1, fixed an error from Article::viewUpdates(). A CentralAuth fix will be in a subsequent commit.
2009-08-15 03:45:19 +00:00
|
|
|
( $this->lastErrno() == 1290 && strpos( $this->lastError(), '--read-only' ) !== false );
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-06 10:17:44 +00:00
|
|
|
function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabaseMysql::duplicateTableStructure' ) {
|
2009-12-11 19:53:10 +00:00
|
|
|
$tmp = $temporary ? 'TEMPORARY ' : '';
|
2009-11-06 10:17:44 +00:00
|
|
|
if ( strcmp( $this->getServerVersion(), '4.1' ) < 0 ) {
|
|
|
|
|
# Hack for MySQL versions < 4.1, which don't support
|
|
|
|
|
# "CREATE TABLE ... LIKE". Note that
|
|
|
|
|
# "CREATE TEMPORARY TABLE ... SELECT * FROM ... LIMIT 0"
|
|
|
|
|
# would not create the indexes we need....
|
|
|
|
|
#
|
|
|
|
|
# Note that we don't bother changing around the prefixes here be-
|
|
|
|
|
# cause we know we're using MySQL anyway.
|
|
|
|
|
|
2011-04-12 18:54:51 +00:00
|
|
|
$res = $this->query( 'SHOW CREATE TABLE ' . $this->addIdentifierQuotes( $oldName ) );
|
2009-11-06 10:17:44 +00:00
|
|
|
$row = $this->fetchRow( $res );
|
2009-12-11 19:53:10 +00:00
|
|
|
$oldQuery = $row[1];
|
2010-05-19 02:12:59 +00:00
|
|
|
$query = preg_replace( '/CREATE TABLE `(.*?)`/',
|
2011-04-12 18:54:51 +00:00
|
|
|
"CREATE $tmp TABLE " . $this->addIdentifierQuotes( $newName ), $oldQuery );
|
2009-12-11 19:53:10 +00:00
|
|
|
if ($oldQuery === $query) {
|
2009-11-06 10:17:44 +00:00
|
|
|
# Couldn't do replacement
|
|
|
|
|
throw new MWException( "could not create temporary table $newName" );
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2011-04-12 18:54:51 +00:00
|
|
|
$newName = $this->addIdentifierQuotes( $newName );
|
|
|
|
|
$oldName = $this->addIdentifierQuotes( $oldName );
|
2009-12-11 19:53:10 +00:00
|
|
|
$query = "CREATE $tmp TABLE $newName (LIKE $oldName)";
|
2009-11-06 10:17:44 +00:00
|
|
|
}
|
2009-12-11 19:53:10 +00:00
|
|
|
$this->query( $query, $fname );
|
2009-11-06 10:17:44 +00:00
|
|
|
}
|
2010-12-28 17:15:50 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List all tables on the database
|
|
|
|
|
*
|
|
|
|
|
* @param $prefix Only show tables with this prefix, e.g. mw_
|
|
|
|
|
* @param $fname String: calling function name
|
|
|
|
|
*/
|
|
|
|
|
function listTables( $prefix = null, $fname = 'DatabaseMysql::listTables' ) {
|
|
|
|
|
$result = $this->query( "SHOW TABLES", $fname);
|
2011-05-30 01:57:53 +00:00
|
|
|
|
2010-12-28 17:15:50 +00:00
|
|
|
$endArray = array();
|
2011-05-30 01:57:53 +00:00
|
|
|
|
|
|
|
|
foreach( $result as $table ) {
|
2010-12-28 17:15:50 +00:00
|
|
|
$vars = get_object_vars($table);
|
|
|
|
|
$table = array_pop( $vars );
|
2011-05-30 01:57:53 +00:00
|
|
|
|
2011-03-27 01:22:44 +00:00
|
|
|
if( !$prefix || strpos( $table, $prefix ) === 0 ) {
|
2010-12-28 17:15:50 +00:00
|
|
|
$endArray[] = $table;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $endArray;
|
|
|
|
|
}
|
2009-11-06 10:17:44 +00:00
|
|
|
|
2010-12-28 01:19:16 +00:00
|
|
|
public function dropTable( $tableName, $fName = 'DatabaseMysql::dropTable' ) {
|
|
|
|
|
if( !$this->tableExists( $tableName ) ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return $this->query( "DROP TABLE IF EXISTS " . $this->tableName( $tableName ), $fName );
|
|
|
|
|
}
|
|
|
|
|
|
* Fixed a bug causing the installer to ignore the "engine" and "charset" settings when installing a MySQL database.
* Fixed a bug causing the engine and charset settings to not be properly preserved when adding new tables on upgrade.
* Fixed total breakage of SQLite upgrade, by reusing the administrative connection to the SQLite database instead of creating a new one when wfGetDB() is called. Added LBFactory_Single to support this.
* Introduced a "schema variable" concept to DatabaseBase to avoid the use of globals for communication between the installer and the Database. Removed a lot of old global variable names from Database::replaceVars(), most were only added on a whim and were never used.
* Introduced DatabaseInstaller::getSchemaVars(), to allow schema variables to be supplied by the DatabaseInstaller child classes.
* Removed messages config-mysql-egine-mismatch [sic] and config-mysql-charset-mismatch. In the old installer it was possible for users to request a certain character set for an upgrade, but in the new installer the question is never asked. So these warnings were shown whenever a non-default character set or engine was used in the old database.
* In MysqlInstaller::preUpgrade(), fixed the incorrect strings used to identify the MySQL character sets: mysql5 instead of utf8 and mysql5-binary instead of binary.
* On install, initialise the site_stats table, using code copied from the old installer. Unlike the old installer, use SiteStats to increment the user count when the initial user is added.
* Fixed several instances of inappropriate call-by-reference.
* Replaced call_user_func_array() with call_user_func() where possible, it is shorter and simpler.
* Moved the caching boilerplate for DatabaseInstaller::getConnection() to the base class, and have the derived classes override an uncached function openConnection() instead. Updates r80892.
* In MysqlInstaller::getLocalSettings(), escape PHP strings correctly with LocalSettingsGenerator::escapePhpString().
* Reduce timeout for checks in dirIsExecutable() to 3 seconds, so that it doesn't take 30s to run when apache is in single-threaded mode for debugging.
* MySQL and SQLite have been tested and they appear to work. PostgreSQL upgrade is totally broken, apparently it was like that before I started. The Oracle code is untested.
2011-01-25 07:37:48 +00:00
|
|
|
protected function getDefaultSchemaVars() {
|
|
|
|
|
$vars = parent::getDefaultSchemaVars();
|
|
|
|
|
$vars['wgDBTableOptions'] = $GLOBALS['wgDBTableOptions'];
|
|
|
|
|
return $vars;
|
|
|
|
|
}
|
2011-06-23 03:14:11 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get status information from SHOW STATUS in an associative array
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
function getMysqlStatus( $which = "%" ) {
|
|
|
|
|
$res = $this->query( "SHOW STATUS LIKE '{$which}'" );
|
|
|
|
|
$status = array();
|
|
|
|
|
|
|
|
|
|
foreach ( $res as $row ) {
|
|
|
|
|
$status[$row->Variable_name] = $row->Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $status;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-15 18:39:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Legacy support: Database == DatabaseMysql
|
|
|
|
|
*/
|
|
|
|
|
class Database extends DatabaseMysql {}
|
|
|
|
|
|
2010-11-21 19:56:51 +00:00
|
|
|
/**
|
|
|
|
|
* Utility class.
|
|
|
|
|
* @ingroup Database
|
|
|
|
|
*/
|
|
|
|
|
class MySQLField implements Field {
|
|
|
|
|
private $name, $tablename, $default, $max_length, $nullable,
|
|
|
|
|
$is_pk, $is_unique, $is_multiple, $is_key, $type;
|
|
|
|
|
|
|
|
|
|
function __construct ( $info ) {
|
|
|
|
|
$this->name = $info->name;
|
|
|
|
|
$this->tablename = $info->table;
|
|
|
|
|
$this->default = $info->def;
|
|
|
|
|
$this->max_length = $info->max_length;
|
|
|
|
|
$this->nullable = !$info->not_null;
|
|
|
|
|
$this->is_pk = $info->primary_key;
|
|
|
|
|
$this->is_unique = $info->unique_key;
|
|
|
|
|
$this->is_multiple = $info->multiple_key;
|
|
|
|
|
$this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
|
|
|
|
|
$this->type = $info->type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function name() {
|
|
|
|
|
return $this->name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function tableName() {
|
|
|
|
|
return $this->tableName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function type() {
|
|
|
|
|
return $this->type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isNullable() {
|
|
|
|
|
return $this->nullable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function defaultValue() {
|
|
|
|
|
return $this->default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isKey() {
|
|
|
|
|
return $this->is_key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isMultipleKey() {
|
|
|
|
|
return $this->is_multiple;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-23 03:14:11 +00:00
|
|
|
class MySQLMasterPos implements DBMasterPos {
|
2009-06-15 18:39:41 +00:00
|
|
|
var $file, $pos;
|
|
|
|
|
|
|
|
|
|
function __construct( $file, $pos ) {
|
|
|
|
|
$this->file = $file;
|
|
|
|
|
$this->pos = $pos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function __toString() {
|
|
|
|
|
return "{$this->file}/{$this->pos}";
|
|
|
|
|
}
|
|
|
|
|
}
|