* Database abstraction object
* @ingroup Database
*/
-class Database {
+abstract class DatabaseBase {
#------------------------------------------------------------------------------
# Variables
protected $mErrorCount = 0;
protected $mLBInfo = array();
protected $mFakeSlaveLag = null, $mFakeMaster = false;
+ protected $mDefaultBigSelects = null;
#------------------------------------------------------------------------------
# Accessors
return true;
}
+ /**
+ * Returns true if this database requires that SELECT DISTINCT queries require that all
+ ORDER BY expressions occur in the SELECT list per the SQL92 standard
+ */
+ function standardSelectDistinct() {
+ return true;
+ }
+
/**
* Returns true if this database can do a native search on IP columns
* e.g. this works as expected: .. WHERE rc_ip = '127.42.12.102/32';
*/
function isOpen() { return $this->mOpened; }
+ /**
+ * Set a flag for this connection
+ *
+ * @param $flag Integer: DBO_* constants from Defines.php:
+ * - DBO_DEBUG: output some debug info (same as debug())
+ * - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
+ * - DBO_IGNORE: ignore errors (same as ignoreErrors())
+ * - DBO_TRX: automatically start transactions
+ * - DBO_DEFAULT: automatically sets DBO_TRX if not in command line mode
+ * and removes it in command line mode
+ * - DBO_PERSISTENT: use persistant database connection
+ */
function setFlag( $flag ) {
$this->mFlags |= $flag;
}
+ /**
+ * Clear a flag for this connection
+ *
+ * @param $flag: same as setFlag()'s $flag param
+ */
function clearFlag( $flag ) {
$this->mFlags &= ~$flag;
}
+ /**
+ * Returns a boolean whether the flag $flag is set for this connection
+ *
+ * @param $flag: same as setFlag()'s $flag param
+ * @return Boolean
+ */
function getFlag( $flag ) {
return !!($this->mFlags & $flag);
}
}
/**
- * Same as new Database( ... ), kept for backward compatibility
+ * Same as new DatabaseMysql( ... ), kept for backward compatibility
* @param $server String: database server host
* @param $user String: database user name
* @param $password String: database user password
*/
static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0 )
{
- return new Database( $server, $user, $password, $dbName, $failFunction, $flags );
+ return new DatabaseMysql( $server, $user, $password, $dbName, $failFunction, $flags );
}
/**
* @param $password String: database user password
* @param $dbName String: database name
*/
- function open( $server, $user, $password, $dbName ) {
- global $wgAllDBsAreLocalhost;
- wfProfileIn( __METHOD__ );
-
- # Test for missing mysql.so
- # First try to load it
- if (!@extension_loaded('mysql')) {
- @dl('mysql.so');
- }
-
- # 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;
-
- $success = false;
-
- 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 );
- }
- if ($this->mConn === false) {
- #$iplus = $i + 1;
- #wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
- }
- }
- $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" );
- $success = false;
- }
-
- 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__ );
- }
- // Turn off strict mode
- $this->query( "SET sql_mode = ''", __METHOD__ );
- }
-
- // Turn off strict mode if it is on
- } else {
- $this->reportConnectionError( $phpError );
- }
-
- $this->mOpened = $success;
- wfProfileOut( __METHOD__ );
- return $success;
- }
+ abstract function open( $server, $user, $password, $dbName );
protected function installErrorHandler() {
$this->mPHPError = false;
*
* @return Bool operation success. true if already closed.
*/
- function close()
- {
- $this->mOpened = false;
- if ( $this->mConn ) {
- if ( $this->trxLevel() ) {
- $this->immediateCommit();
- }
- return mysql_close( $this->mConn );
- } else {
- return true;
- }
+ function close() {
+ # Stub, should probably be overridden
+ return true;
}
/**
* @return Result object to feed to fetchObject, fetchRow, ...; or false on failure
* @private
*/
- /*private*/ function doQuery( $sql ) {
- if( $this->bufferResults() ) {
- $ret = mysql_query( $sql, $this->mConn );
- } else {
- $ret = mysql_unbuffered_query( $sql, $this->mConn );
- }
- return $ret;
- }
+ /*private*/ abstract function doQuery( $sql );
/**
* @param $error String
* @param $res Mixed: A SQL result
*/
function freeResult( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
- if ( !@/**/mysql_free_result( $res ) ) {
- throw new DBUnexpectedError( $this, "Unable to free MySQL result" );
- }
+ # Stub. Might not really need to be overridden, since results should
+ # be freed by PHP when the variable goes out of scope anyway.
}
/**
* @return MySQL row object
* @throws DBUnexpectedError Thrown if the database returns an error
*/
- 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;
- }
+ abstract function fetchObject( $res );
/**
* Fetch the next row from the given result object, in associative array
* @return MySQL row object
* @throws DBUnexpectedError Thrown if the database returns an error
*/
- 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;
- }
+ abstract function fetchRow( $res );
/**
* Get the number of rows in a result object
* @param $res Mixed: A SQL result
*/
- 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;
- }
+ abstract function numRows( $res );
/**
* Get the number of fields in a result object
* See documentation for mysql_num_fields()
* @param $res Mixed: A SQL result
*/
- function numFields( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
- return mysql_num_fields( $res );
- }
+ abstract function numFields( $res );
/**
* Get a field name in a result object
* @param $res Mixed: A SQL result
* @param $n Integer
*/
- function fieldName( $res, $n ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
- return mysql_field_name( $res, $n );
- }
+ abstract function fieldName( $res, $n );
/**
* Get the inserted value of an auto-increment row
* $dbw->insert('page',array('page_id' => $id));
* $id = $dbw->insertId();
*/
- function insertId() { return mysql_insert_id( $this->mConn ); }
+ abstract function insertId();
/**
* Change the position of the cursor in a result object
* @param $res Mixed: A SQL result
* @param $row Mixed: Either MySQL row or ResultWrapper
*/
- function dataSeek( $res, $row ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
- return mysql_data_seek( $res, $row );
- }
+ abstract function dataSeek( $res, $row );
/**
* Get the last error number
* See mysql_errno()
*/
- function lastErrno() {
- if ( $this->mConn ) {
- return mysql_errno( $this->mConn );
- } else {
- return mysql_errno();
- }
- }
+ abstract function lastErrno();
/**
* Get a description of the last error
* See mysql_error() for more details
*/
- 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;
- }
+ abstract function lastError();
+
/**
* Get the number of rows affected by the last write query
* See mysql_affected_rows() for more details
*/
- function affectedRows() { return mysql_affected_rows( $this->mConn ); }
+ abstract function affectedRows();
/**
* Simple UPDATE wrapper
* e.g: selectRow( "page", array( "page_id" ), array( "page_namespace" =>
* NS_MAIN, "page_title" => "Astronomy" ) ) would return an object where
* $obj- >page_id is the ID of the Astronomy article
- * @param $fname String: Calling functio name
+ * @param $fname String: Calling function name
* @param $options Array
* @param $join_conds Array
*
/**
* Estimate rows in dataset
- * Returns estimated count, based on EXPLAIN output
+ * Returns estimated count - not necessarily an accurate estimate across different databases,
+ * so use sparingly
* Takes same arguments as Database::select()
- */
-
- 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
+ *
+ * @param string $table table name
+ * @param array $vars unused
+ * @param array $conds filters on the table
+ * @param string $fname function name for profiling
+ * @param array $options options for select
+ * @return int row count
+ */
+ public function estimateRowCount( $table, $vars='*', $conds='', $fname = 'Database::estimateRowCount', $options = array() ) {
+ $rows = 0;
+ $res = $this->select ( $table, 'COUNT(*) AS rowcount', $conds, $fname, $options );
+ if ( $res ) {
+ $row = $this->fetchRow( $res );
+ $rows = ( isset( $row['rowcount'] ) ) ? $row['rowcount'] : 0;
}
-
- $this->freeResult($res);
- return $rows;
+ $this->freeResult( $res );
+ return $rows;
}
-
/**
* Removes most variables from an SQL query and replaces them with X or N for numbers.
* @param $table
* @param $field
*/
- function fieldInfo( $table, $field ) {
- $table = $this->tableName( $table );
- $res = $this->query( "SELECT * FROM $table LIMIT 1" );
- $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;
- }
+ abstract function fieldInfo( $table, $field );
/**
* mysql_field_type() wrapper
return $list;
}
+ /**
+ * Bitwise operations
+ */
+
+ function bitNot($field) {
+ return "(~$bitField)";
+ }
+
+ function bitAnd($fieldLeft, $fieldRight) {
+ return "($fieldLeft & $fieldRight)";
+ }
+
+ function bitOr($fieldLeft, $fieldRight) {
+ return "($fieldLeft | $fieldRight)";
+ }
+
/**
* Change the current database
+ *
+ * @return bool Success or failure
*/
function selectDB( $db ) {
- $this->mDBname = $db;
- return mysql_select_db( $db, $this->mConn );
+ # Stub. Shouldn't cause serious problems if it's not overridden, but
+ # if your database engine supports a concept similar to MySQL's
+ # databases you may as well. TODO: explain what exactly will fail if
+ # this is not overridden.
+ return true;
}
/**
* @param $s String: to be slashed.
* @return String: slashed string.
*/
- function strencode( $s ) {
- return mysql_real_escape_string( $s, $this->mConn );
- }
+ abstract function strencode( $s );
/**
* If it's a string, adds quotes and backslashes
}
/**
- * Escape string for safe LIKE usage
+ * Escape string for safe LIKE usage.
+ * WARNING: you should almost never use this function directly,
+ * instead use buildLike() that escapes everything automatically
*/
function escapeLike( $s ) {
- $s=str_replace('\\','\\\\',$s);
- $s=$this->strencode( $s );
- $s=str_replace(array('%','_'),array('\%','\_'),$s);
+ $s = str_replace( '\\', '\\\\', $s );
+ $s = $this->strencode( $s );
+ $s = str_replace( array( '%', '_' ), array( '\%', '\_' ), $s );
return $s;
}
+ /**
+ * LIKE statement wrapper, receives a variable-length argument list with parts of pattern to match
+ * containing either string literals that will be escaped or tokens returned by anyChar() or anyString().
+ * Alternatively, the function could be provided with an array of aforementioned parameters.
+ *
+ * Example: $dbr->buildLike( 'My_page_title/', $dbr->anyString() ) returns a LIKE clause that searches
+ * for subpages of 'My page title'.
+ * Alternatively: $pattern = array( 'My_page_title/', $dbr->anyString() ); $query .= $dbr->buildLike( $pattern );
+ *
+ * @ return String: fully built LIKE statement
+ */
+ function buildLike() {
+ $params = func_get_args();
+ if (count($params) > 0 && is_array($params[0])) {
+ $params = $params[0];
+ }
+
+ $s = '';
+ foreach( $params as $value) {
+ if( $value instanceof LikeMatch ) {
+ $s .= $value->toString();
+ } else {
+ $s .= $this->escapeLike( $value );
+ }
+ }
+ return " LIKE '" . $s . "' ";
+ }
+
+ /**
+ * Returns a token for buildLike() that denotes a '_' to be used in a LIKE query
+ */
+ function anyChar() {
+ return new LikeMatch( '_' );
+ }
+
+ /**
+ * Returns a token for buildLike() that denotes a '%' to be used in a LIKE query
+ */
+ function anyString() {
+ return new LikeMatch( '%' );
+ }
+
/**
* Returns an appropriately quoted sequence value for inserting a new row.
* MySQL has autoincrement fields, so this is just NULL. But the PostgreSQL
}
/**
- * USE INDEX clause
- * PostgreSQL doesn't have them and returns ""
+ * USE INDEX clause. Unlikely to be useful for anything but MySQL. This
+ * is only needed because a) MySQL must be as efficient as possible due to
+ * its use on Wikipedia, and b) MySQL 4.0 is kind of dumb sometimes about
+ * which index to pick. Anyway, other databases might have different
+ * indexes on a given table. So don't bother overriding this unless you're
+ * MySQL.
*/
function useIndexClause( $index ) {
- return "FORCE INDEX (" . $this->indexName( $index ) . ")";
+ return '';
}
/**
}
/**
+ * A string to insert into queries to show that they're low-priority, like
+ * MySQL's LOW_PRIORITY. If no such feature exists, return an empty
+ * string and nothing bad should happen.
+ *
* @return string Returns the text of the low priority option if it is supported, or a blank string otherwise
*/
function lowPriorityOption() {
- return 'LOW_PRIORITY';
+ return '';
}
/**
}
/**
- * Construct a LIMIT query with optional offset
- * This is used for query pages
+ * Construct a LIMIT query with optional offset. This is used for query
+ * pages. The SQL should be adjusted so that only the first $limit rows
+ * are returned. If $offset is provided as well, then the first $offset
+ * rows should be discarded, and the next $limit rows should be returned.
+ * If the result of the query is not ordered, then the rows to be returned
+ * are theoretically arbitrary.
+ *
+ * $sql is expected to be a SELECT, if that makes a difference. For
+ * UPDATE, limitResultForUpdate should be used.
+ *
+ * The version provided by default works in MySQL and SQLite. It will very
+ * likely need to be overridden for most other DBMSes.
+ *
* @param $sql String: SQL query we will append the limit too
* @param $limit Integer: the SQL limit
* @param $offset Integer the SQL offset (default false)
*/
- function limitResult($sql, $limit, $offset=false) {
- if( !is_numeric($limit) ) {
+ function limitResult( $sql, $limit, $offset=false ) {
+ if( !is_numeric( $limit ) ) {
throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" );
}
return "$sql LIMIT "
. ( (is_numeric($offset) && $offset != 0) ? "{$offset}," : "" )
. "{$limit} ";
}
- function limitResultForUpdate($sql, $num) {
- return $this->limitResult($sql, $num, 0);
+ function limitResultForUpdate( $sql, $num ) {
+ return $this->limitResult( $sql, $num, 0 );
}
/**
- * Returns an SQL expression for a simple conditional.
- * Uses IF on MySQL.
+ * Returns true if current database backend supports ORDER BY or LIMIT for separate subqueries
+ * within the UNION construct.
+ * @return Boolean
+ */
+ function unionSupportsOrderAndLimit() {
+ return true; // True for almost every DB supported
+ }
+
+ /**
+ * Construct a UNION query
+ * This is used for providing overload point for other DB abstractions
+ * not compatible with the MySQL syntax.
+ * @param $sqls Array: SQL statements to combine
+ * @param $all Boolean: use UNION ALL
+ * @return String: SQL fragment
+ */
+ function unionQueries($sqls, $all) {
+ $glue = $all ? ') UNION ALL (' : ') UNION (';
+ return '('.implode( $glue, $sqls ) . ')';
+ }
+
+ /**
+ * Returns an SQL expression for a simple conditional. This doesn't need
+ * to be overridden unless CASE isn't supported in your DBMS.
*
* @param $cond String: SQL expression which will result in a boolean value
* @param $trueVal String: SQL expression to return if true
* @return String: SQL fragment
*/
function conditional( $cond, $trueVal, $falseVal ) {
- return " IF($cond, $trueVal, $falseVal) ";
+ return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
}
/**
/**
* Determines if the last failure was due to a deadlock
+ * STUB
*/
function wasDeadlock() {
- return $this->lastErrno() == 1213;
+ return false;
}
/**
* Determines if the last query error was something that should be dealt
- * with by pinging the connection and reissuing the query
+ * with by pinging the connection and reissuing the query.
+ * STUB
*/
function wasErrorReissuable() {
- return $this->lastErrno() == 2013 || $this->lastErrno() == 2006;
+ return false;
+ }
+
+ /**
+ * Determines if the last failure was due to the database being read-only.
+ * STUB
+ */
+ function wasReadOnlyError() {
+ return false;
}
/**
}
/**
+ * Returns a wikitext link to the DB's website, e.g.,
+ * return "[http://www.mysql.com/ MySQL]";
+ * Should at least contain plain text, if for some reason
+ * your database has no website.
+ *
* @return String: wikitext of a link to the server software's web site
*/
- function getSoftwareLink() {
- return "[http://www.mysql.com/ MySQL]";
- }
+ abstract function getSoftwareLink();
/**
+ * A string describing the current software version, like from
+ * mysql_get_server_info(). Will be listed on Special:Version, etc.
+ *
* @return String: Version information from the database
*/
- function getServerVersion() {
- return mysql_get_server_info( $this->mConn );
- }
+ abstract function getServerVersion();
/**
* Ping the server and try to reconnect if it there is no connection
+ *
+ * @return bool Success or failure
*/
function ping() {
- if( !function_exists( 'mysql_ping' ) ) {
- wfDebug( "Tried to call mysql_ping but this is ancient PHP version. Faking it!\n" );
- return true;
- }
- $ping = mysql_ping( $this->mConn );
- if ( $ping ) {
- return true;
- }
-
- // Need to reconnect manually in MySQL client 5.0.13+
- if ( version_compare( mysql_get_client_info(), '5.0.13', '>=' ) ) {
- mysql_close( $this->mConn );
- $this->mOpened = false;
- $this->mConn = false;
- $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
- return true;
- }
- return false;
+ # Stub. Not essential to override.
+ return true;
}
/**
$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 != '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 ) {
}
/**
- * Override database's default connection timeout.
- * May be useful for very long batch queries such as
- * full-wiki dumps, where a single query reads out
- * over hours or days.
+ * Override database's default connection timeout. May be useful for very
+ * long batch queries such as full-wiki dumps, where a single query reads
+ * out over hours or days. May or may not be necessary for non-MySQL
+ * databases. For most purposes, leaving it as a no-op should be fine.
+ *
* @param $timeout Integer in seconds
*/
- public function setTimeout( $timeout ) {
- $this->query( "SET net_read_timeout=$timeout" );
- $this->query( "SET net_write_timeout=$timeout" );
- }
+ public function setTimeout( $timeout ) {}
/**
* Read and execute SQL commands from a file.
return $error;
}
+ /**
+ * Get the full path of a patch file. Originally based on archive()
+ * from updaters.inc. Keep in mind this always returns a patch, as
+ * it fails back to MySQL if no DB-specific patch can be found
+ *
+ * @param $patch String The name of the patch, like patch-something.sql
+ * @return String Full path to patch file
+ */
+ public static function patchPath( $patch ) {
+ global $wgDBtype, $IP;
+ if ( file_exists( "$IP/maintenance/$wgDBtype/archives/$name" ) ) {
+ return "$IP/maintenance/$wgDBtype/archives/$name";
+ } else {
+ return "$IP/maintenance/archives/$name";
+ }
+ }
+
/**
* Read and execute commands from an open file handle
* Returns true on success, error string or exception on failure (depending on object's error ignore settings)
return $this->indexName( $matches[1] );
}
- /*
+ /**
* Build a concatenation list to feed into a SQL query
- */
+ * @param $stringList Array: list of raw SQL expressions; caller is responsible for any quoting
+ * @return String
+ */
function buildConcat( $stringList ) {
return 'CONCAT(' . implode( ',', $stringList ) . ')';
}
/**
- * Acquire a lock
+ * Acquire a named lock
*
* Abstracted from Filestore::lock() so child classes can implement for
* their own needs.
* @param $method String: Name of method calling us
* @return bool
*/
- public function lock( $lockName, $method ) {
- $lockName = $this->addQuotes( $lockName );
- $result = $this->query( "SELECT GET_LOCK($lockName, 5) AS lockstatus", $method );
- $row = $this->fetchObject( $result );
- $this->freeResult( $result );
-
- if( $row->lockstatus == 1 ) {
- return true;
- } else {
- wfDebug( __METHOD__." failed to acquire lock\n" );
- return false;
- }
+ public function lock( $lockName, $method, $timeout = 5 ) {
+ return true;
}
+
/**
* Release a lock.
*
- * @todo fixme - Figure out a way to return a bool
- * based on successful lock release.
- *
* @param $lockName String: Name of lock to release
* @param $method String: Name of method calling us
+ *
+ * FROM MYSQL DOCS: http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_release-lock
+ * @return Returns 1 if the lock was released, 0 if the lock was not established
+ * by this thread (in which case the lock is not released), and NULL if the named
+ * lock did not exist
*/
public function unlock( $lockName, $method ) {
- $lockName = $this->addQuotes( $lockName );
- $result = $this->query( "SELECT RELEASE_LOCK($lockName)", $method );
- $this->freeResult( $result );
+ return true;
+ }
+
+ /**
+ * Lock specific tables
+ *
+ * @param $read Array of tables to lock for read access
+ * @param $write Array of tables to lock for write access
+ * @param $method String name of caller
+ * @param $lowPriority bool Whether to indicate writes to be LOW PRIORITY
+ */
+ public function lockTables( $read, $write, $method, $lowPriority = true ) {
+ return true;
+ }
+
+ /**
+ * Unlock specific tables
+ *
+ * @param $method String the caller
+ */
+ public function unlockTables( $method ) {
+ return true;
}
/**
public function getSearchEngine() {
return "SearchMySQL";
}
-}
-/**
- * Database abstraction object for mySQL
- * Inherit all methods and properties of Database::Database()
- *
- * @ingroup Database
- * @see Database
- */
-class DatabaseMysql extends Database {
- # Inherit all
+ /**
+ * Allow or deny "big selects" for this session only. This is done by setting
+ * the sql_big_selects session variable.
+ *
+ * This is a MySQL-specific feature.
+ *
+ * @param mixed $value true for allow, false for deny, or "default" to restore the initial value
+ */
+ public function setBigSelects( $value = true ) {
+ // no-op
+ }
}
+
/******************************************************************************
* Utility classes
*****************************************************************************/
* @param $db Database object which threw the error
* @param $error A simple error message to be used for debugging
*/
- function __construct( Database &$db, $error ) {
+ function __construct( DatabaseBase &$db, $error ) {
$this->db =& $db;
parent::__construct( $error );
}
+
+ function getText() {
+ global $wgShowDBErrorBacktrace;
+ $s = $this->getMessage() . "\n";
+ if ( $wgShowDBErrorBacktrace ) {
+ $s .= "Backtrace:\n" . $this->getTraceAsString() . "\n";
+ }
+ return $s;
+ }
}
/**
class DBConnectionError extends DBError {
public $error;
- function __construct( Database &$db, $error = 'unknown error' ) {
+ function __construct( DatabaseBase &$db, $error = 'unknown error' ) {
$msg = 'DB connection error';
if ( trim( $error ) != '' ) {
$msg .= ": $error";
return false;
}
- function getText() {
- return $this->getMessage() . "\n";
- }
-
function getLogMessage() {
# Don't send to the exception log
return false;
}
function getHTML() {
- global $wgLang, $wgMessageCache, $wgUseFileCache;
+ global $wgLang, $wgMessageCache, $wgUseFileCache, $wgShowDBErrorBacktrace;
$sorry = 'Sorry! This site is experiencing technical difficulties.';
$again = 'Try waiting a few minutes and reloading.';
$noconnect = "<p><strong>$sorry</strong><br />$again</p><p><small>$info</small></p>";
$text = str_replace( '$1', $this->error, $noconnect );
- /*
- if ( $GLOBALS['wgShowExceptionDetails'] ) {
- $text .= '</p><p>Backtrace:</p><p>' .
- nl2br( htmlspecialchars( $this->getTraceAsString() ) ) .
- "</p>\n";
- }*/
+ if ( $wgShowDBErrorBacktrace ) {
+ $text .= '<p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) );
+ }
$extra = $this->searchForm();
class DBQueryError extends DBError {
public $error, $errno, $sql, $fname;
- function __construct( Database &$db, $error, $errno, $sql, $fname ) {
+ function __construct( DatabaseBase &$db, $error, $errno, $sql, $fname ) {
$message = "A database error has occurred\n" .
"Query: $sql\n" .
"Function: $fname\n" .
}
function getText() {
+ global $wgShowDBErrorBacktrace;
if ( $this->useMessageCache() ) {
- return wfMsg( 'dberrortextcl', htmlspecialchars( $this->getSQL() ),
- htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) ) . "\n";
+ $s = wfMsg( 'dberrortextcl', htmlspecialchars( $this->getSQL() ),
+ htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) ) . "\n";
+ if ( $wgShowDBErrorBacktrace ) {
+ $s .= "Backtrace:\n" . $this->getTraceAsString() . "\n";
+ }
+ return $s;
} else {
- return $this->getMessage();
+ return parent::getText();
}
}
}
function getHTML() {
+ global $wgShowDBErrorBacktrace;
if ( $this->useMessageCache() ) {
- return wfMsgNoDB( 'dberrortext', htmlspecialchars( $this->getSQL() ),
+ $s = wfMsgNoDB( 'dberrortext', htmlspecialchars( $this->getSQL() ),
htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) );
} else {
- return nl2br( htmlspecialchars( $this->getMessage() ) );
+ $s = nl2br( htmlspecialchars( $this->getMessage() ) );
}
+ if ( $wgShowDBErrorBacktrace ) {
+ $s .= '<p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) );
+ }
+ return $s;
}
}
}
}
-class MySQLMasterPos {
- var $file, $pos;
+/**
+ * Used by DatabaseBase::buildLike() to represent characters that have special meaning in SQL LIKE clauses
+ * and thus need no escaping. Don't instantiate it manually, use Database::anyChar() and anyString() instead.
+ */
+class LikeMatch {
+ private $str;
- function __construct( $file, $pos ) {
- $this->file = $file;
- $this->pos = $pos;
+ public function __construct( $s ) {
+ $this->str = $s;
}
- function __toString() {
- return "{$this->file}/{$this->pos}";
+ public function toString() {
+ return $this->str;
}
-}
+}
\ No newline at end of file