MediaWiki  REL1_19
DatabaseMysql.php
Go to the documentation of this file.
00001 <?php
00016 class DatabaseMysql extends DatabaseBase {
00017 
00021         function getType() {
00022                 return 'mysql';
00023         }
00024 
00029         protected function doQuery( $sql ) {
00030                 if( $this->bufferResults() ) {
00031                         $ret = mysql_query( $sql, $this->mConn );
00032                 } else {
00033                         $ret = mysql_unbuffered_query( $sql, $this->mConn );
00034                 }
00035                 return $ret;
00036         }
00037 
00046         function open( $server, $user, $password, $dbName ) {
00047                 global $wgAllDBsAreLocalhost;
00048                 wfProfileIn( __METHOD__ );
00049 
00050                 # Load mysql.so if we don't have it
00051                 wfDl( 'mysql' );
00052 
00053                 # Fail now
00054                 # Otherwise we get a suppressed fatal error, which is very hard to track down
00055                 if ( !function_exists( 'mysql_connect' ) ) {
00056                         throw new DBConnectionError( $this, "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n" );
00057                 }
00058 
00059                 # Debugging hack -- fake cluster
00060                 if ( $wgAllDBsAreLocalhost ) {
00061                         $realServer = 'localhost';
00062                 } else {
00063                         $realServer = $server;
00064                 }
00065                 $this->close();
00066                 $this->mServer = $server;
00067                 $this->mUser = $user;
00068                 $this->mPassword = $password;
00069                 $this->mDBname = $dbName;
00070 
00071                 wfProfileIn("dbconnect-$server");
00072 
00073                 # The kernel's default SYN retransmission period is far too slow for us,
00074                 # so we use a short timeout plus a manual retry. Retrying means that a small
00075                 # but finite rate of SYN packet loss won't cause user-visible errors.
00076                 $this->mConn = false;
00077                 if ( ini_get( 'mysql.connect_timeout' ) <= 3 ) {
00078                         $numAttempts = 2;
00079                 } else {
00080                         $numAttempts = 1;
00081                 }
00082                 $this->installErrorHandler();
00083                 for ( $i = 0; $i < $numAttempts && !$this->mConn; $i++ ) {
00084                         if ( $i > 1 ) {
00085                                 usleep( 1000 );
00086                         }
00087                         if ( $this->mFlags & DBO_PERSISTENT ) {
00088                                 $this->mConn = mysql_pconnect( $realServer, $user, $password );
00089                         } else {
00090                                 # Create a new connection...
00091                                 $this->mConn = mysql_connect( $realServer, $user, $password, true );
00092                         }
00093                         #if ( $this->mConn === false ) {
00094                                 #$iplus = $i + 1;
00095                                 #wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
00096                         #}
00097                 }
00098                 $phpError = $this->restoreErrorHandler();
00099                 # Always log connection errors
00100                 if ( !$this->mConn ) {
00101                         $error = $this->lastError();
00102                         if ( !$error ) {
00103                                 $error = $phpError;
00104                         }
00105                         wfLogDBError( "Error connecting to {$this->mServer}: $error\n" );
00106                         wfDebug( "DB connection error\n" );
00107                         wfDebug( "Server: $server, User: $user, Password: " .
00108                                 substr( $password, 0, 3 ) . "..., error: " . mysql_error() . "\n" );
00109                 }
00110 
00111                 wfProfileOut("dbconnect-$server");
00112 
00113                 if ( $dbName != '' && $this->mConn !== false ) {
00114                         wfSuppressWarnings();
00115                         $success = mysql_select_db( $dbName, $this->mConn );
00116                         wfRestoreWarnings();
00117                         if ( !$success ) {
00118                                 $error = "Error selecting database $dbName on server {$this->mServer} " .
00119                                         "from client host " . wfHostname() . "\n";
00120                                 wfLogDBError(" Error selecting database $dbName on server {$this->mServer} \n");
00121                                 wfDebug( $error );
00122                         }
00123                 } else {
00124                         # Delay USE query
00125                         $success = (bool)$this->mConn;
00126                 }
00127 
00128                 if ( $success ) {
00129                         // Tell the server we're communicating with it in UTF-8.
00130                         // This may engage various charset conversions.
00131                         global $wgDBmysql5;
00132                         if( $wgDBmysql5 ) {
00133                                 $this->query( 'SET NAMES utf8', __METHOD__ );
00134                         } else {
00135                                 $this->query( 'SET NAMES binary', __METHOD__ );
00136                         }
00137                         // Set SQL mode, default is turning them all off, can be overridden or skipped with null
00138                         global $wgSQLMode;
00139                         if ( is_string( $wgSQLMode ) ) {
00140                                 $mode = $this->addQuotes( $wgSQLMode );
00141                                 $this->query( "SET sql_mode = $mode", __METHOD__ );
00142                         }
00143 
00144                         // Turn off strict mode if it is on
00145                 } else {
00146                         $this->reportConnectionError( $phpError );
00147                 }
00148 
00149                 $this->mOpened = $success;
00150                 wfProfileOut( __METHOD__ );
00151                 return $success;
00152         }
00153 
00157         function close() {
00158                 $this->mOpened = false;
00159                 if ( $this->mConn ) {
00160                         if ( $this->trxLevel() ) {
00161                                 $this->commit();
00162                         }
00163                         return mysql_close( $this->mConn );
00164                 } else {
00165                         return true;
00166                 }
00167         }
00168 
00173         function freeResult( $res ) {
00174                 if ( $res instanceof ResultWrapper ) {
00175                         $res = $res->result;
00176                 }
00177                 wfSuppressWarnings();
00178                 $ok = mysql_free_result( $res );
00179                 wfRestoreWarnings();
00180                 if ( !$ok ) {
00181                         throw new DBUnexpectedError( $this, "Unable to free MySQL result" );
00182                 }
00183         }
00184 
00190         function fetchObject( $res ) {
00191                 if ( $res instanceof ResultWrapper ) {
00192                         $res = $res->result;
00193                 }
00194                 wfSuppressWarnings();
00195                 $row = mysql_fetch_object( $res );
00196                 wfRestoreWarnings();
00197                 if( $this->lastErrno() ) {
00198                         throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) );
00199                 }
00200                 return $row;
00201         }
00202 
00208         function fetchRow( $res ) {
00209                 if ( $res instanceof ResultWrapper ) {
00210                         $res = $res->result;
00211                 }
00212                 wfSuppressWarnings();
00213                 $row = mysql_fetch_array( $res );
00214                 wfRestoreWarnings();
00215                 if ( $this->lastErrno() ) {
00216                         throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) );
00217                 }
00218                 return $row;
00219         }
00220 
00226         function numRows( $res ) {
00227                 if ( $res instanceof ResultWrapper ) {
00228                         $res = $res->result;
00229                 }
00230                 wfSuppressWarnings();
00231                 $n = mysql_num_rows( $res );
00232                 wfRestoreWarnings();
00233                 if( $this->lastErrno() ) {
00234                         throw new DBUnexpectedError( $this, 'Error in numRows(): ' . htmlspecialchars( $this->lastError() ) );
00235                 }
00236                 return $n;
00237         }
00238 
00243         function numFields( $res ) {
00244                 if ( $res instanceof ResultWrapper ) {
00245                         $res = $res->result;
00246                 }
00247                 return mysql_num_fields( $res );
00248         }
00249 
00255         function fieldName( $res, $n ) {
00256                 if ( $res instanceof ResultWrapper ) {
00257                         $res = $res->result;
00258                 }
00259                 return mysql_field_name( $res, $n );
00260         }
00261 
00265         function insertId() {
00266                 return mysql_insert_id( $this->mConn );
00267         }
00268 
00274         function dataSeek( $res, $row ) {
00275                 if ( $res instanceof ResultWrapper ) {
00276                         $res = $res->result;
00277                 }
00278                 return mysql_data_seek( $res, $row );
00279         }
00280 
00284         function lastErrno() {
00285                 if ( $this->mConn ) {
00286                         return mysql_errno( $this->mConn );
00287                 } else {
00288                         return mysql_errno();
00289                 }
00290         }
00291 
00295         function lastError() {
00296                 if ( $this->mConn ) {
00297                         # Even if it's non-zero, it can still be invalid
00298                         wfSuppressWarnings();
00299                         $error = mysql_error( $this->mConn );
00300                         if ( !$error ) {
00301                                 $error = mysql_error();
00302                         }
00303                         wfRestoreWarnings();
00304                 } else {
00305                         $error = mysql_error();
00306                 }
00307                 if( $error ) {
00308                         $error .= ' (' . $this->mServer . ')';
00309                 }
00310                 return $error;
00311         }
00312 
00316         function affectedRows() {
00317                 return mysql_affected_rows( $this->mConn );
00318         }
00319 
00327         function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabaseMysql::replace' ) {
00328                 return $this->nativeReplace( $table, $rows, $fname );
00329         }
00330 
00343         public function estimateRowCount( $table, $vars='*', $conds='', $fname = 'DatabaseMysql::estimateRowCount', $options = array() ) {
00344                 $options['EXPLAIN'] = true;
00345                 $res = $this->select( $table, $vars, $conds, $fname, $options );
00346                 if ( $res === false ) {
00347                         return false;
00348                 }
00349                 if ( !$this->numRows( $res ) ) {
00350                         return 0;
00351                 }
00352 
00353                 $rows = 1;
00354                 foreach ( $res as $plan ) {
00355                         $rows *= $plan->rows > 0 ? $plan->rows : 1; // avoid resetting to zero
00356                 }
00357                 return $rows;
00358         }
00359 
00365         function fieldInfo( $table, $field ) {
00366                 $table = $this->tableName( $table );
00367                 $res = $this->query( "SELECT * FROM $table LIMIT 1", __METHOD__, true );
00368                 if ( !$res ) {
00369                         return false;
00370                 }
00371                 $n = mysql_num_fields( $res->result );
00372                 for( $i = 0; $i < $n; $i++ ) {
00373                         $meta = mysql_fetch_field( $res->result, $i );
00374                         if( $field == $meta->name ) {
00375                                 return new MySQLField($meta);
00376                         }
00377                 }
00378                 return false;
00379         }
00380 
00390         function indexInfo( $table, $index, $fname = 'DatabaseMysql::indexInfo' ) {
00391                 # SHOW INDEX works in MySQL 3.23.58, but SHOW INDEXES does not.
00392                 # SHOW INDEX should work for 3.x and up:
00393                 # http://dev.mysql.com/doc/mysql/en/SHOW_INDEX.html
00394                 $table = $this->tableName( $table );
00395                 $index = $this->indexName( $index );
00396                 $sql = 'SHOW INDEX FROM ' . $table;
00397                 $res = $this->query( $sql, $fname );
00398 
00399                 if ( !$res ) {
00400                         return null;
00401                 }
00402 
00403                 $result = array();
00404 
00405                 foreach ( $res as $row ) {
00406                         if ( $row->Key_name == $index ) {
00407                                 $result[] = $row;
00408                         }
00409                 }
00410 
00411                 return empty( $result ) ? false : $result;
00412         }
00413 
00418         function selectDB( $db ) {
00419                 $this->mDBname = $db;
00420                 return mysql_select_db( $db, $this->mConn );
00421         }
00422 
00428         function strencode( $s ) {
00429                 $sQuoted = mysql_real_escape_string( $s, $this->mConn );
00430 
00431                 if($sQuoted === false) {
00432                         $this->ping();
00433                         $sQuoted = mysql_real_escape_string( $s, $this->mConn );
00434                 }
00435                 return $sQuoted;
00436         }
00437 
00445         public function addIdentifierQuotes( $s ) {
00446                 return "`" . $this->strencode( $s ) . "`";
00447         }
00448 
00453         public function isQuotedIdentifier( $name ) {
00454                 return strlen( $name ) && $name[0] == '`' && substr( $name, -1, 1 ) == '`';
00455         }
00456 
00460         function ping() {
00461                 $ping = mysql_ping( $this->mConn );
00462                 if ( $ping ) {
00463                         return true;
00464                 }
00465 
00466                 mysql_close( $this->mConn );
00467                 $this->mOpened = false;
00468                 $this->mConn = false;
00469                 $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
00470                 return true;
00471         }
00472 
00480         function getLag() {
00481                 if ( !is_null( $this->mFakeSlaveLag ) ) {
00482                         wfDebug( "getLag: fake slave lagged {$this->mFakeSlaveLag} seconds\n" );
00483                         return $this->mFakeSlaveLag;
00484                 }
00485 
00486                 return $this->getLagFromSlaveStatus();
00487         }
00488 
00492         function getLagFromSlaveStatus() {
00493                 $res = $this->query( 'SHOW SLAVE STATUS', __METHOD__ );
00494                 if ( !$res ) {
00495                         return false;
00496                 }
00497                 $row = $res->fetchObject();
00498                 if ( !$row ) {
00499                         return false;
00500                 }
00501                 if ( strval( $row->Seconds_Behind_Master ) === '' ) {
00502                         return false;
00503                 } else {
00504                         return intval( $row->Seconds_Behind_Master );
00505                 }
00506         }
00507 
00513         function getLagFromProcesslist() {
00514                 wfDeprecated( __METHOD__, '1.19' );
00515                 $res = $this->query( 'SHOW PROCESSLIST', __METHOD__ );
00516                 if( !$res ) {
00517                         return false;
00518                 }
00519                 # Find slave SQL thread
00520                 foreach( $res as $row ) {
00521                         /* This should work for most situations - when default db
00522                          * for thread is not specified, it had no events executed,
00523                          * and therefore it doesn't know yet how lagged it is.
00524                          *
00525                          * Relay log I/O thread does not select databases.
00526                          */
00527                         if ( $row->User == 'system user' &&
00528                                 $row->State != 'Waiting for master to send event' &&
00529                                 $row->State != 'Connecting to master' &&
00530                                 $row->State != 'Queueing master event to the relay log' &&
00531                                 $row->State != 'Waiting for master update' &&
00532                                 $row->State != 'Requesting binlog dump' &&
00533                                 $row->State != 'Waiting to reconnect after a failed master event read' &&
00534                                 $row->State != 'Reconnecting after a failed master event read' &&
00535                                 $row->State != 'Registering slave on master'
00536                                 ) {
00537                                 # This is it, return the time (except -ve)
00538                                 if ( $row->Time > 0x7fffffff ) {
00539                                         return false;
00540                                 } else {
00541                                         return $row->Time;
00542                                 }
00543                         }
00544                 }
00545                 return false;
00546         }
00547 
00555         function masterPosWait( DBMasterPos $pos, $timeout ) {
00556                 $fname = 'DatabaseBase::masterPosWait';
00557                 wfProfileIn( $fname );
00558 
00559                 # Commit any open transactions
00560                 if ( $this->mTrxLevel ) {
00561                         $this->commit();
00562                 }
00563 
00564                 if ( !is_null( $this->mFakeSlaveLag ) ) {
00565                         $status = parent::masterPosWait( $pos, $timeout );
00566                         wfProfileOut( $fname );
00567                         return $status;
00568                 }
00569 
00570                 # Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set
00571                 $encFile = $this->addQuotes( $pos->file );
00572                 $encPos = intval( $pos->pos );
00573                 $sql = "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)";
00574                 $res = $this->doQuery( $sql );
00575 
00576                 if ( $res && $row = $this->fetchRow( $res ) ) {
00577                         wfProfileOut( $fname );
00578                         return $row[0];
00579                 } else {
00580                         wfProfileOut( $fname );
00581                         return false;
00582                 }
00583         }
00584 
00590         function getSlavePos() {
00591                 if ( !is_null( $this->mFakeSlaveLag ) ) {
00592                         return parent::getSlavePos();
00593                 }
00594 
00595                 $res = $this->query( 'SHOW SLAVE STATUS', 'DatabaseBase::getSlavePos' );
00596                 $row = $this->fetchObject( $res );
00597 
00598                 if ( $row ) {
00599                         $pos = isset( $row->Exec_master_log_pos ) ? $row->Exec_master_log_pos : $row->Exec_Master_Log_Pos;
00600                         return new MySQLMasterPos( $row->Relay_Master_Log_File, $pos );
00601                 } else {
00602                         return false;
00603                 }
00604         }
00605 
00611         function getMasterPos() {
00612                 if ( $this->mFakeMaster ) {
00613                         return parent::getMasterPos();
00614                 }
00615 
00616                 $res = $this->query( 'SHOW MASTER STATUS', 'DatabaseBase::getMasterPos' );
00617                 $row = $this->fetchObject( $res );
00618 
00619                 if ( $row ) {
00620                         return new MySQLMasterPos( $row->File, $row->Position );
00621                 } else {
00622                         return false;
00623                 }
00624         }
00625 
00629         function getServerVersion() {
00630                 return mysql_get_server_info( $this->mConn );
00631         }
00632 
00637         function useIndexClause( $index ) {
00638                 return "FORCE INDEX (" . $this->indexName( $index ) . ")";
00639         }
00640 
00644         function lowPriorityOption() {
00645                 return 'LOW_PRIORITY';
00646         }
00647 
00651         public static function getSoftwareLink() {
00652                 return '[http://www.mysql.com/ MySQL]';
00653         }
00654 
00658         function standardSelectDistinct() {
00659                 return false;
00660         }
00661 
00665         public function setSessionOptions( array $options ) {
00666                 if ( isset( $options['connTimeout'] ) ) {
00667                         $timeout = (int)$options['connTimeout'];
00668                         $this->query( "SET net_read_timeout=$timeout" );
00669                         $this->query( "SET net_write_timeout=$timeout" );
00670                 }
00671         }
00672 
00673         public function streamStatementEnd( &$sql, &$newLine ) {
00674                 if ( strtoupper( substr( $newLine, 0, 9 ) ) == 'DELIMITER' ) {
00675                         preg_match( '/^DELIMITER\s+(\S+)/' , $newLine, $m );
00676                         $this->delimiter = $m[1];
00677                         $newLine = '';
00678                 }
00679                 return parent::streamStatementEnd( $sql, $newLine );
00680         }
00681 
00688         public function lock( $lockName, $method, $timeout = 5 ) {
00689                 $lockName = $this->addQuotes( $lockName );
00690                 $result = $this->query( "SELECT GET_LOCK($lockName, $timeout) AS lockstatus", $method );
00691                 $row = $this->fetchObject( $result );
00692 
00693                 if( $row->lockstatus == 1 ) {
00694                         return true;
00695                 } else {
00696                         wfDebug( __METHOD__." failed to acquire lock\n" );
00697                         return false;
00698                 }
00699         }
00700 
00707         public function unlock( $lockName, $method ) {
00708                 $lockName = $this->addQuotes( $lockName );
00709                 $result = $this->query( "SELECT RELEASE_LOCK($lockName) as lockstatus", $method );
00710                 $row = $this->fetchObject( $result );
00711                 return $row->lockstatus;
00712         }
00713 
00720         public function lockTables( $read, $write, $method, $lowPriority = true ) {
00721                 $items = array();
00722 
00723                 foreach( $write as $table ) {
00724                         $tbl = $this->tableName( $table ) .
00725                                         ( $lowPriority ? ' LOW_PRIORITY' : '' ) .
00726                                         ' WRITE';
00727                         $items[] = $tbl;
00728                 }
00729                 foreach( $read as $table ) {
00730                         $items[] = $this->tableName( $table ) . ' READ';
00731                 }
00732                 $sql = "LOCK TABLES " . implode( ',', $items );
00733                 $this->query( $sql, $method );
00734         }
00735 
00739         public function unlockTables( $method ) {
00740                 $this->query( "UNLOCK TABLES", $method );
00741         }
00742 
00749         public function getSearchEngine() {
00750                 return 'SearchMySQL';
00751         }
00752 
00757         public function setBigSelects( $value = true ) {
00758                 if ( $value === 'default' ) {
00759                         if ( $this->mDefaultBigSelects === null ) {
00760                                 # Function hasn't been called before so it must already be set to the default
00761                                 return;
00762                         } else {
00763                                 $value = $this->mDefaultBigSelects;
00764                         }
00765                 } elseif ( $this->mDefaultBigSelects === null ) {
00766                         $this->mDefaultBigSelects = (bool)$this->selectField( false, '@@sql_big_selects' );
00767                 }
00768                 $encValue = $value ? '1' : '0';
00769                 $this->query( "SET sql_big_selects=$encValue", __METHOD__ );
00770         }
00771 
00782         function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'DatabaseBase::deleteJoin' ) {
00783                 if ( !$conds ) {
00784                         throw new DBUnexpectedError( $this, 'DatabaseBase::deleteJoin() called with empty $conds' );
00785                 }
00786 
00787                 $delTable = $this->tableName( $delTable );
00788                 $joinTable = $this->tableName( $joinTable );
00789                 $sql = "DELETE $delTable FROM $delTable, $joinTable WHERE $delVar=$joinVar ";
00790 
00791                 if ( $conds != '*' ) {
00792                         $sql .= ' AND ' . $this->makeList( $conds, LIST_AND );
00793                 }
00794 
00795                 return $this->query( $sql, $fname );
00796         }
00797 
00803         function getServerUptime() {
00804                 $vars = $this->getMysqlStatus( 'Uptime' );
00805                 return (int)$vars['Uptime'];
00806         }
00807 
00813         function wasDeadlock() {
00814                 return $this->lastErrno() == 1213;
00815         }
00816 
00822         function wasLockTimeout() {
00823                 return $this->lastErrno() == 1205;
00824         }
00825 
00832         function wasErrorReissuable() {
00833                 return $this->lastErrno() == 2013 || $this->lastErrno() == 2006;
00834         }
00835 
00841         function wasReadOnlyError() {
00842                 return $this->lastErrno() == 1223 ||
00843                         ( $this->lastErrno() == 1290 && strpos( $this->lastError(), '--read-only' ) !== false );
00844         }
00845 
00852         function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabaseMysql::duplicateTableStructure' ) {
00853                 $tmp = $temporary ? 'TEMPORARY ' : '';
00854                 $newName = $this->addIdentifierQuotes( $newName );
00855                 $oldName = $this->addIdentifierQuotes( $oldName );
00856                 $query = "CREATE $tmp TABLE $newName (LIKE $oldName)";
00857                 $this->query( $query, $fname );
00858         }
00859 
00867         function listTables( $prefix = null, $fname = 'DatabaseMysql::listTables' ) {
00868                 $result = $this->query( "SHOW TABLES", $fname);
00869 
00870                 $endArray = array();
00871 
00872                 foreach( $result as $table ) {
00873                         $vars = get_object_vars($table);
00874                         $table = array_pop( $vars );
00875 
00876                         if( !$prefix || strpos( $table, $prefix ) === 0 ) {
00877                                 $endArray[] = $table;
00878                         }
00879                 }
00880 
00881                 return $endArray;
00882         }
00883 
00889         public function dropTable( $tableName, $fName = 'DatabaseMysql::dropTable' ) {
00890                 if( !$this->tableExists( $tableName, $fName ) ) {
00891                         return false;
00892                 }
00893                 return $this->query( "DROP TABLE IF EXISTS " . $this->tableName( $tableName ), $fName );
00894         }
00895 
00899         protected function getDefaultSchemaVars() {
00900                 $vars = parent::getDefaultSchemaVars();
00901                 $vars['wgDBTableOptions'] = str_replace( 'TYPE', 'ENGINE', $GLOBALS['wgDBTableOptions'] );
00902                 $vars['wgDBTableOptions'] = str_replace( 'CHARSET=mysql4', 'CHARSET=binary', $vars['wgDBTableOptions'] );
00903                 return $vars;
00904         }
00905 
00912         function getMysqlStatus( $which = "%" ) {
00913                 $res = $this->query( "SHOW STATUS LIKE '{$which}'" );
00914                 $status = array();
00915 
00916                 foreach ( $res as $row ) {
00917                         $status[$row->Variable_name] = $row->Value;
00918                 }
00919 
00920                 return $status;
00921         }
00922 
00923 }
00924 
00930 class Database extends DatabaseMysql {}
00931 
00936 class MySQLField implements Field {
00937         private $name, $tablename, $default, $max_length, $nullable,
00938                 $is_pk, $is_unique, $is_multiple, $is_key, $type;
00939 
00940         function __construct ( $info ) {
00941                 $this->name = $info->name;
00942                 $this->tablename = $info->table;
00943                 $this->default = $info->def;
00944                 $this->max_length = $info->max_length;
00945                 $this->nullable = !$info->not_null;
00946                 $this->is_pk = $info->primary_key;
00947                 $this->is_unique = $info->unique_key;
00948                 $this->is_multiple = $info->multiple_key;
00949                 $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
00950                 $this->type = $info->type;
00951         }
00952 
00956         function name() {
00957                 return $this->name;
00958         }
00959 
00963         function tableName() {
00964                 return $this->tableName;
00965         }
00966 
00970         function type() {
00971                 return $this->type;
00972         }
00973 
00977         function isNullable() {
00978                 return $this->nullable;
00979         }
00980 
00981         function defaultValue() {
00982                 return $this->default;
00983         }
00984 
00988         function isKey() {
00989                 return $this->is_key;
00990         }
00991 
00995         function isMultipleKey() {
00996                 return $this->is_multiple;
00997         }
00998 }
00999 
01000 class MySQLMasterPos implements DBMasterPos {
01001         var $file, $pos;
01002 
01003         function __construct( $file, $pos ) {
01004                 $this->file = $file;
01005                 $this->pos = $pos;
01006         }
01007 
01008         function __toString() {
01009                 return "{$this->file}/{$this->pos}";
01010         }
01011 }