* @see Database
*/
class DatabaseMysql extends DatabaseBase {
+ static $mMinSearchLength;
+
+ function getType() {
+ return 'mysql';
+ }
+
/*private*/ function doQuery( $sql ) {
if( $this->bufferResults() ) {
$ret = mysql_query( $sql, $this->mConn );
$this->mOpened = false;
if ( $this->mConn ) {
if ( $this->trxLevel() ) {
- $this->immediateCommit();
+ $this->commit();
}
return mysql_close( $this->mConn );
} else {
}
function affectedRows() { return mysql_affected_rows( $this->mConn ); }
+
+ /**
+ * Estimate rows in dataset
+ * Returns estimated count, based on EXPLAIN output
+ * Takes same arguments as Database::select()
+ */
+ public function estimateRowCount( $table, $vars='*', $conds='', $fname = 'Database::estimateRowCount', $options = array() ) {
+ $options['EXPLAIN'] = true;
+ $res = $this->select( $table, $vars, $conds, $fname, $options );
+ if ( $res === false )
+ return false;
+ if ( !$this->numRows( $res ) ) {
+ $this->freeResult($res);
+ return 0;
+ }
+
+ $rows = 1;
+ while( $plan = $this->fetchObject( $res ) ) {
+ $rows *= $plan->rows > 0 ? $plan->rows : 1; // avoid resetting to zero
+ }
+
+ $this->freeResult($res);
+ return $rows;
+ }
function fieldInfo( $table, $field ) {
$table = $this->tableName( $table );
return '[http://www.mysql.com/ MySQL]';
}
- /**
- * @return String: Database type for use in messages
- */
- function getDBtypeForMsg() {
- return 'MySQL';
+ function standardSelectDistinct() {
+ return false;
}
public function setTimeout( $timeout ) {
return $row->lockstatus;
}
- public function lockTables( $read, $write, $method ) {
+ public function lockTables( $read, $write, $method, $lowPriority = true ) {
$items = array();
foreach( $write as $table ) {
- $items[] = $this->tableName( $table ) . ' LOW_PRIORITY WRITE';
+ $tbl = $this->tableName( $table ) .
+ ( $lowPriority ? ' LOW_PRIORITY' : '' ) .
+ ' WRITE';
+ $items[] = $tbl;
}
foreach( $read as $table ) {
$items[] = $this->tableName( $table ) . ' READ';
}
$sql = "LOCK TABLES " . implode( ',', $items );
- $db->query( $sql, $method );
+ $this->query( $sql, $method );
}
public function unlockTables( $method ) {
$this->query( "UNLOCK TABLES", $method );
}
+ /**
+ * Converts some characters for MySQL's indexing to grok it correctly,
+ * and pads short words to overcome limitations.
+ */
+ function stripForSearch( $string ) {
+ global $wgContLang;
+
+ wfProfileIn( __METHOD__ );
+
+ // MySQL fulltext index doesn't grok utf-8, so we
+ // need to fold cases and convert to hex
+ $out = preg_replace_callback(
+ "/([\\xc0-\\xff][\\x80-\\xbf]*)/",
+ array( $this, 'stripForSearchCallback' ),
+ $wgContLang->lc( $string ) );
+
+ // And to add insult to injury, the default indexing
+ // ignores short words... Pad them so we can pass them
+ // through without reconfiguring the server...
+ $minLength = $this->minSearchLength();
+ if( $minLength > 1 ) {
+ $n = $minLength - 1;
+ $out = preg_replace(
+ "/\b(\w{1,$n})\b/",
+ "$1u800",
+ $out );
+ }
+
+ // Periods within things like hostnames and IP addresses
+ // are also important -- we want a search for "example.com"
+ // or "192.168.1.1" to work sanely.
+ //
+ // MySQL's search seems to ignore them, so you'd match on
+ // "example.wikipedia.com" and "192.168.83.1" as well.
+ $out = preg_replace(
+ "/(\w)\.(\w|\*)/u",
+ "$1u82e$2",
+ $out );
+
+ wfProfileOut( __METHOD__ );
+
+ return $out;
+ }
+
+ /**
+ * Armor a case-folded UTF-8 string to get through MySQL's
+ * fulltext search without being mucked up by funny charset
+ * settings or anything else of the sort.
+ */
+ protected function stripForSearchCallback( $matches ) {
+ return 'u8' . bin2hex( $matches[1] );
+ }
+
+ /**
+ * Check MySQL server's ft_min_word_len setting so we know
+ * if we need to pad short words...
+ *
+ * @return int
+ */
+ protected function minSearchLength() {
+ if( is_null( self::$mMinSearchLength ) ) {
+ $sql = "show global variables like 'ft\\_min\\_word\\_len'";
+
+ // Even though this query is pretty fast, let's not overload the master
+ $dbr = wfGetDB( DB_SLAVE );
+ $result = $dbr->query( $sql );
+ $row = $result->fetchObject();
+ $result->free();
+
+ if( $row && $row->Variable_name == 'ft_min_word_len' ) {
+ self::$mMinSearchLength = intval( $row->Value );
+ } else {
+ self::$mMinSearchLength = 0;
+ }
+ }
+ return self::$mMinSearchLength;
+ }
+
public function setBigSelects( $value = true ) {
if ( $value === 'default' ) {
if ( $this->mDefaultBigSelects === null ) {
$encValue = $value ? '1' : '0';
$this->query( "SET sql_big_selects=$encValue", __METHOD__ );
}
+
+
+ /**
+ * Determines if the last failure was due to a deadlock
+ */
+ function wasDeadlock() {
+ return $this->lastErrno() == 1213;
+ }
+
+ /**
+ * Determines if the last query error was something that should be dealt
+ * 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() {
+ return $this->lastErrno() == 1223 ||
+ ( $this->lastErrno() == 1290 && strpos( $this->lastError(), '--read-only' ) !== false );
+ }
+
+ function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabaseMysql::duplicateTableStructure' ) {
+ $tmp = $temporary ? 'TEMPORARY ' : '';
+ 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.
+
+ $res = $this->query( "SHOW CREATE TABLE $oldName" );
+ $row = $this->fetchRow( $res );
+ $oldQuery = $row[1];
+ $query = preg_replace( '/CREATE TABLE `(.*?)`/',
+ "CREATE $tmp TABLE `$newName`", $oldQuery );
+ if ($oldQuery === $query) {
+ # Couldn't do replacement
+ throw new MWException( "could not create temporary table $newName" );
+ }
+ } else {
+ $query = "CREATE $tmp TABLE $newName (LIKE $oldName)";
+ }
+ $this->query( $query, $fname );
+ }
+
}
/**