2004-02-18 02:15:00 +00:00
< ? php
2004-09-02 23:28:24 +00:00
/**
2005-08-02 13:35:19 +00:00
* This file deals with MySQL interface functions
2004-09-02 23:28:24 +00:00
* and query specifics / optimisations
2004-09-03 23:00:01 +00:00
* @ package MediaWiki
2004-09-02 23:28:24 +00:00
*/
2006-06-01 08:19:02 +00:00
/**
* Depends on the CacheManager
*/
require_once ( 'CacheManager.php' );
2004-10-24 07:10:33 +00:00
/** See Database::makeList() */
2004-08-22 17:24:50 +00:00
define ( 'LIST_COMMA' , 0 );
define ( 'LIST_AND' , 1 );
define ( 'LIST_SET' , 2 );
2004-09-06 08:30:05 +00:00
define ( 'LIST_NAMES' , 3 );
2005-08-21 01:17:46 +00:00
define ( 'LIST_OR' , 4 );
2004-01-17 05:49:39 +00:00
2004-09-03 16:36:46 +00:00
/** Number of times to re-try an operation in case of deadlock */
2004-08-22 17:24:50 +00:00
define ( 'DEADLOCK_TRIES' , 4 );
2004-09-03 16:36:46 +00:00
/** Minimum time to wait before retry, in microseconds */
2004-08-22 17:24:50 +00:00
define ( 'DEADLOCK_DELAY_MIN' , 500000 );
2004-09-03 16:36:46 +00:00
/** Maximum time to wait before retry */
2004-08-22 17:24:50 +00:00
define ( 'DEADLOCK_DELAY_MAX' , 1500000 );
2004-07-18 08:48:43 +00:00
2005-08-02 13:35:19 +00:00
class DBObject {
2006-05-11 22:40:38 +00:00
var $mData ;
2005-08-02 13:35:19 +00:00
function DBObject ( $data ) {
$this -> mData = $data ;
}
function isLOB () {
return false ;
}
function data () {
return $this -> mData ;
}
};
2004-09-02 23:28:24 +00:00
/**
* Database abstraction object
2004-09-03 23:00:01 +00:00
* @ package MediaWiki
2004-09-02 23:28:24 +00:00
*/
2004-01-10 16:44:31 +00:00
class Database {
#------------------------------------------------------------------------------
# Variables
2004-09-03 16:36:46 +00:00
#------------------------------------------------------------------------------
/** #@+
2006-04-19 15:46:24 +00:00
* @ private
2004-09-03 16:36:46 +00:00
*/
2006-05-11 22:40:38 +00:00
var $mLastQuery = '' ;
2005-08-02 13:35:19 +00:00
2006-05-11 22:40:38 +00:00
var $mServer , $mUser , $mPassword , $mConn = null , $mDBname ;
var $mOut , $mOpened = false ;
2005-08-02 13:35:19 +00:00
2006-05-11 22:40:38 +00:00
var $mFailFunction ;
var $mTablePrefix ;
var $mFlags ;
var $mTrxLevel = 0 ;
var $mErrorCount = 0 ;
var $mLBInfo = array ();
2004-09-03 16:36:46 +00:00
/**#@-*/
2004-01-10 16:44:31 +00:00
#------------------------------------------------------------------------------
# Accessors
#------------------------------------------------------------------------------
2004-07-24 07:24:04 +00:00
# These optionally set a variable and return the previous state
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Fail function , takes a Database as a parameter
* Set to false for default , 1 for ignore errors
*/
2005-08-02 13:35:19 +00:00
function failFunction ( $function = NULL ) {
return wfSetVar ( $this -> mFailFunction , $function );
2004-07-24 07:24:04 +00:00
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Output page , used for reporting errors
* FALSE means discard output
*/
2005-08-02 13:35:19 +00:00
function & setOutputPage ( & $out ) {
$this -> mOut =& $out ;
2004-07-24 07:24:04 +00:00
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Boolean , controls output of large amounts of debug information
*/
2005-08-02 13:35:19 +00:00
function debug ( $debug = NULL ) {
return wfSetBit ( $this -> mFlags , DBO_DEBUG , $debug );
2004-07-24 07:24:04 +00:00
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Turns buffering of SQL result sets on ( true ) or off ( false ) .
* Default is " on " and it should not be changed without good reasons .
*/
2004-07-24 07:24:04 +00:00
function bufferResults ( $buffer = NULL ) {
if ( is_null ( $buffer ) ) {
return ! ( bool )( $this -> mFlags & DBO_NOBUFFER );
} else {
2005-08-02 13:35:19 +00:00
return ! wfSetBit ( $this -> mFlags , DBO_NOBUFFER , ! $buffer );
2004-07-24 07:24:04 +00:00
}
}
2004-01-10 16:44:31 +00:00
2004-09-03 16:36:46 +00:00
/**
* Turns on ( false ) or off ( true ) the automatic generation and sending
* of a " we're sorry, but there has been a database error " page on
* database errors . Default is on ( false ) . When turned off , the
* code should use wfLastErrno () and wfLastError () to handle the
* situation as appropriate .
*/
2005-08-02 13:35:19 +00:00
function ignoreErrors ( $ignoreErrors = NULL ) {
return wfSetBit ( $this -> mFlags , DBO_IGNORE , $ignoreErrors );
2004-07-24 07:24:04 +00:00
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* The current depth of nested transactions
2006-04-19 15:46:24 +00:00
* @ param $level Integer : , default NULL .
2004-09-03 16:36:46 +00:00
*/
2004-07-24 07:24:04 +00:00
function trxLevel ( $level = NULL ) {
return wfSetVar ( $this -> mTrxLevel , $level );
}
2005-08-02 13:35:19 +00:00
/**
2005-04-24 08:31:12 +00:00
* Number of errors logged , only useful when errors are ignored
*/
function errorCount ( $count = NULL ) {
return wfSetVar ( $this -> mErrorCount , $count );
}
2005-10-29 01:41:36 +00:00
/**
* Properties passed down from the server info array of the load balancer
*/
function getLBInfo ( $name = NULL ) {
if ( is_null ( $name ) ) {
return $this -> mLBInfo ;
} else {
if ( array_key_exists ( $name , $this -> mLBInfo ) ) {
return $this -> mLBInfo [ $name ];
} else {
return NULL ;
}
}
}
function setLBInfo ( $name , $value = NULL ) {
if ( is_null ( $value ) ) {
$this -> mLBInfo = $name ;
} else {
$this -> mLBInfo [ $name ] = $value ;
}
}
2004-09-03 16:36:46 +00:00
/** #@+
* Get function
*/
2004-01-10 16:44:31 +00:00
function lastQuery () { return $this -> mLastQuery ; }
2004-01-17 05:49:39 +00:00
function isOpen () { return $this -> mOpened ; }
2004-09-03 16:36:46 +00:00
/**#@-*/
2004-01-17 05:49:39 +00:00
2005-08-14 14:42:54 +00:00
function setFlag ( $flag ) {
$this -> mFlags |= $flag ;
}
function clearFlag ( $flag ) {
$this -> mFlags &= ~ $flag ;
}
function getFlag ( $flag ) {
return !! ( $this -> mFlags & $flag );
}
2004-01-10 16:44:31 +00:00
#------------------------------------------------------------------------------
# Other functions
#------------------------------------------------------------------------------
2006-04-19 15:46:24 +00:00
/**@ {{
2004-09-03 16:36:46 +00:00
* @ param string $server database server host
* @ param string $user database user name
* @ param string $password database user password
* @ param string $dbname database name
*/
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* @ param failFunction
* @ param $flags
2006-04-19 15:46:24 +00:00
* @ param $tablePrefix String : database table prefixes . By default use the prefix gave in LocalSettings . php
2004-09-03 16:36:46 +00:00
*/
2005-08-02 13:35:19 +00:00
function Database ( $server = false , $user = false , $password = false , $dbName = false ,
2004-09-03 16:36:46 +00:00
$failFunction = false , $flags = 0 , $tablePrefix = 'get from global' ) {
2005-08-02 13:35:19 +00:00
2004-07-24 07:24:04 +00:00
global $wgOut , $wgDBprefix , $wgCommandLineMode ;
2004-03-23 10:13:59 +00:00
# Can't get a reference if it hasn't been set yet
if ( ! isset ( $wgOut ) ) {
$wgOut = NULL ;
}
$this -> mOut =& $wgOut ;
2004-07-10 03:09:26 +00:00
$this -> mFailFunction = $failFunction ;
2004-07-24 07:24:04 +00:00
$this -> mFlags = $flags ;
2005-08-02 13:35:19 +00:00
2004-07-24 07:24:04 +00:00
if ( $this -> mFlags & DBO_DEFAULT ) {
if ( $wgCommandLineMode ) {
$this -> mFlags &= ~ DBO_TRX ;
} else {
$this -> mFlags |= DBO_TRX ;
}
}
2005-04-12 04:03:21 +00:00
/*
// Faster read-only access
if ( wfReadOnly () ) {
$this -> mFlags |= DBO_PERSISTENT ;
$this -> mFlags &= ~ DBO_TRX ;
} */
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/** Get the default table prefix*/
2004-07-18 08:48:43 +00:00
if ( $tablePrefix == 'get from global' ) {
$this -> mTablePrefix = $wgDBprefix ;
} else {
$this -> mTablePrefix = $tablePrefix ;
}
2004-07-10 03:09:26 +00:00
if ( $server ) {
$this -> open ( $server , $user , $password , $dbName );
}
2004-01-10 16:44:31 +00:00
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* @ static
* @ param failFunction
* @ param $flags
*/
2005-08-02 13:35:19 +00:00
function newFromParams ( $server , $user , $password , $dbName ,
2004-07-24 07:24:04 +00:00
$failFunction = false , $flags = 0 )
2004-01-10 16:44:31 +00:00
{
2004-07-24 07:24:04 +00:00
return new Database ( $server , $user , $password , $dbName , $failFunction , $flags );
2004-01-10 16:44:31 +00:00
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Usually aborts on failure
* If the failFunction is set to a non - zero integer , returns success
*/
function open ( $server , $user , $password , $dbName ) {
2005-10-28 01:08:49 +00:00
global $wguname ;
2006-01-07 13:31:29 +00:00
2004-06-25 04:32:45 +00:00
# Test for missing mysql.so
2004-11-09 15:25:40 +00:00
# First try to load it
if ( !@ extension_loaded ( 'mysql' )) {
@ dl ( 'mysql.so' );
}
2004-06-25 04:32:45 +00:00
# Otherwise we get a suppressed fatal error, which is very hard to track down
if ( ! function_exists ( 'mysql_connect' ) ) {
2006-01-14 02:49:43 +00:00
wfDie ( " MySQL functions missing, have you compiled PHP with the --with-mysql option? \n " );
2004-06-25 04:32:45 +00:00
}
2005-08-02 13:35:19 +00:00
2004-01-10 16:44:31 +00:00
$this -> close ();
$this -> mServer = $server ;
$this -> mUser = $user ;
$this -> mPassword = $password ;
2004-01-17 05:49:39 +00:00
$this -> mDBname = $dbName ;
2005-08-02 13:35:19 +00:00
2004-01-10 16:44:31 +00:00
$success = false ;
2005-08-02 13:35:19 +00:00
2005-04-12 04:03:21 +00:00
if ( $this -> mFlags & DBO_PERSISTENT ) {
@/**/ $this -> mConn = mysql_pconnect ( $server , $user , $password );
} else {
2005-05-08 08:17:12 +00:00
# Create a new connection...
2006-05-23 20:49:30 +00:00
@/**/ $this -> mConn = mysql_connect ( $server , $user , $password , true );
2005-04-12 04:03:21 +00:00
}
2004-08-22 17:24:50 +00:00
if ( $dbName != '' ) {
2004-01-18 10:39:36 +00:00
if ( $this -> mConn !== false ) {
2004-08-07 03:41:50 +00:00
$success = @/**/ mysql_select_db ( $dbName , $this -> mConn );
2004-01-18 10:39:36 +00:00
if ( ! $success ) {
2005-12-01 07:00:11 +00:00
$error = " Error selecting database $dbName on server { $this -> mServer } " .
2005-10-28 01:08:49 +00:00
" from client host { $wguname [ 'nodename' ] } \n " ;
wfDebug ( $error );
2004-01-18 10:39:36 +00:00
}
} else {
2004-03-23 10:13:59 +00:00
wfDebug ( " DB connection error \n " );
2005-08-02 13:35:19 +00:00
wfDebug ( " Server: $server , User: $user , Password: " .
2005-06-22 10:32:39 +00:00
substr ( $password , 0 , 3 ) . " ..., error: " . mysql_error () . " \n " );
2004-01-18 10:39:36 +00:00
$success = false ;
2004-01-10 16:44:31 +00:00
}
} else {
2004-03-06 01:49:16 +00:00
# Delay USE query
2005-07-01 05:28:07 +00:00
$success = ( bool ) $this -> mConn ;
2004-01-10 16:44:31 +00:00
}
2005-08-02 13:35:19 +00:00
2004-01-10 16:44:31 +00:00
if ( ! $success ) {
$this -> reportConnectionError ();
}
2006-01-07 13:31:29 +00:00
2005-10-26 01:45:23 +00:00
global $wgDBmysql5 ;
if ( $wgDBmysql5 ) {
// Tell the server we're communicating with it in UTF-8.
// This may engage various charset conversions.
$this -> query ( 'SET NAMES utf8' );
}
2006-01-07 13:31:29 +00:00
2004-01-17 05:49:39 +00:00
$this -> mOpened = $success ;
2004-01-10 16:44:31 +00:00
return $success ;
}
2006-04-19 15:46:24 +00:00
/**@}}*/
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Closes a database connection .
* if it is open : commits any open transactions
*
* @ return bool operation success . true if already closed .
*/
2004-01-10 16:44:31 +00:00
function close ()
{
2004-01-17 05:49:39 +00:00
$this -> mOpened = false ;
2004-01-10 16:44:31 +00:00
if ( $this -> mConn ) {
2004-07-24 07:24:04 +00:00
if ( $this -> trxLevel () ) {
$this -> immediateCommit ();
}
2004-01-10 16:44:31 +00:00
return mysql_close ( $this -> mConn );
} else {
return true ;
}
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
2006-04-19 15:46:24 +00:00
* @ private
2005-10-28 01:08:49 +00:00
* @ param string $error fallback error message , used if none is given by MySQL
2004-09-03 16:36:46 +00:00
*/
2005-10-28 01:08:49 +00:00
function reportConnectionError ( $error = 'Unknown error' ) {
$myError = $this -> lastError ();
if ( $myError ) {
$error = $myError ;
}
2006-01-07 13:31:29 +00:00
2004-01-10 16:44:31 +00:00
if ( $this -> mFailFunction ) {
if ( ! is_int ( $this -> mFailFunction ) ) {
2004-06-15 15:00:54 +00:00
$ff = $this -> mFailFunction ;
2005-10-28 01:08:49 +00:00
$ff ( $this , $error );
2004-01-10 16:44:31 +00:00
}
} else {
2005-10-28 01:08:49 +00:00
wfEmergencyAbort ( $this , $error );
2004-01-10 16:44:31 +00:00
}
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Usually aborts on failure
* If errors are explicitly ignored , returns success
*/
function query ( $sql , $fname = '' , $tempIgnore = false ) {
2006-03-07 01:10:39 +00:00
global $wgProfiling ;
2005-08-02 13:35:19 +00:00
2004-01-10 16:44:31 +00:00
if ( $wgProfiling ) {
# generalizeSQL will probably cut down the query to reasonable
# logging size most of the time. The substr is really just a sanity check.
2005-10-22 20:52:30 +00:00
# Who's been wasting my precious column space? -- TS
2006-01-07 13:09:30 +00:00
#$profName = 'query: ' . $fname . ' ' . substr( Database::generalizeSQL( $sql ), 0, 255 );
2005-10-22 20:52:30 +00:00
2006-03-28 05:11:40 +00:00
if ( is_null ( $this -> getLBInfo ( 'master' ) ) ) {
$queryProf = 'query: ' . substr ( Database :: generalizeSQL ( $sql ), 0 , 255 );
$totalProf = 'Database::query' ;
} else {
$queryProf = 'query-m: ' . substr ( Database :: generalizeSQL ( $sql ), 0 , 255 );
$totalProf = 'Database::query-master' ;
}
wfProfileIn ( $totalProf );
wfProfileIn ( $queryProf );
2004-01-10 16:44:31 +00:00
}
2005-08-02 13:35:19 +00:00
2004-01-10 16:44:31 +00:00
$this -> mLastQuery = $sql ;
2005-08-02 13:35:19 +00:00
2004-07-10 03:09:26 +00:00
# Add a comment for easy SHOW PROCESSLIST interpretation
if ( $fname ) {
2005-11-12 16:17:01 +00:00
$commentedSql = preg_replace ( " / \ s/ " , " /* $fname */ " , $sql , 1 );
2004-07-10 03:09:26 +00:00
} else {
$commentedSql = $sql ;
}
2005-03-28 07:56:17 +00:00
2004-07-24 07:24:04 +00:00
# If DBO_TRX is set, start a transaction
if ( ( $this -> mFlags & DBO_TRX ) && ! $this -> trxLevel () && $sql != 'BEGIN' ) {
$this -> begin ();
2004-01-10 16:44:31 +00:00
}
2005-03-28 07:56:17 +00:00
if ( $this -> debug () ) {
2005-06-01 02:10:29 +00:00
$sqlx = substr ( $commentedSql , 0 , 500 );
$sqlx = strtr ( $sqlx , " \t \n " , ' ' );
2005-03-28 07:56:17 +00:00
wfDebug ( " SQL: $sqlx\n " );
}
2005-08-02 13:35:19 +00:00
2004-07-24 07:24:04 +00:00
# Do the query and handle errors
$ret = $this -> doQuery ( $commentedSql );
2005-04-24 07:21:15 +00:00
# Try reconnecting if the connection was lost
2005-04-24 08:31:12 +00:00
if ( false === $ret && ( $this -> lastErrno () == 2013 || $this -> lastErrno () == 2006 ) ) {
2005-04-24 07:21:15 +00:00
# Transaction is gone, like it or not
$this -> mTrxLevel = 0 ;
wfDebug ( " Connection lost, reconnecting... \n " );
if ( $this -> ping () ) {
wfDebug ( " Reconnected \n " );
$ret = $this -> doQuery ( $commentedSql );
} else {
wfDebug ( " Failed \n " );
}
}
2004-01-10 16:44:31 +00:00
if ( false === $ret ) {
2004-07-24 07:24:04 +00:00
$this -> reportQueryError ( $this -> lastError (), $this -> lastErrno (), $sql , $fname , $tempIgnore );
2004-01-10 16:44:31 +00:00
}
2005-08-02 13:35:19 +00:00
2004-01-10 16:44:31 +00:00
if ( $wgProfiling ) {
2006-03-28 05:11:40 +00:00
wfProfileOut ( $queryProf );
wfProfileOut ( $totalProf );
2004-01-10 16:44:31 +00:00
}
return $ret ;
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* The DBMS - dependent part of query ()
* @ param string $sql SQL query .
*/
2004-07-24 07:24:04 +00:00
function doQuery ( $sql ) {
if ( $this -> bufferResults () ) {
$ret = mysql_query ( $sql , $this -> mConn );
} else {
$ret = mysql_unbuffered_query ( $sql , $this -> mConn );
2005-08-02 13:35:19 +00:00
}
2004-07-24 07:24:04 +00:00
return $ret ;
}
2004-09-03 16:36:46 +00:00
/**
* @ param $error
* @ param $errno
* @ param $sql
* @ param string $fname
* @ param bool $tempIgnore
*/
2004-07-18 08:48:43 +00:00
function reportQueryError ( $error , $errno , $sql , $fname , $tempIgnore = false ) {
2006-04-04 05:53:21 +00:00
global $wgCommandLineMode , $wgFullyInitialised , $wgColorErrors ;
2004-07-18 08:48:43 +00:00
# Ignore errors during error handling to avoid infinite recursion
2004-07-24 07:24:04 +00:00
$ignore = $this -> ignoreErrors ( true );
2005-08-29 16:36:37 +00:00
++ $this -> mErrorCount ;
2004-07-18 08:48:43 +00:00
if ( $ignore || $tempIgnore ) {
2005-08-29 16:36:37 +00:00
wfDebug ( " SQL ERROR (ignored): $error\n " );
2004-07-18 08:48:43 +00:00
} else {
$sql1line = str_replace ( " \n " , " \\ n " , $sql );
2005-04-12 04:03:21 +00:00
wfLogDBError ( " $fname\t { $this -> mServer } \t $errno\t $error\t $sql1line\n " );
2004-07-18 08:48:43 +00:00
wfDebug ( " SQL ERROR: " . $error . " \n " );
if ( $wgCommandLineMode || ! $this -> mOut || empty ( $wgFullyInitialised ) ) {
$message = " A database error has occurred \n " .
" Query: $sql\n " .
" Function: $fname\n " .
" Error: $errno $error\n " ;
2005-08-02 13:35:19 +00:00
if ( ! $wgCommandLineMode ) {
2004-07-18 08:48:43 +00:00
$message = nl2br ( $message );
}
2006-04-04 05:53:21 +00:00
if ( $wgCommandLineMode && $wgColorErrors && ! wfIsWindows () && posix_isatty ( 1 ) ) {
$color = 31 ; // bright red!
$message = " \x1b [1; { $color } m { $message } \x1b [0m " ;
}
2004-07-18 08:48:43 +00:00
wfDebugDieBacktrace ( $message );
} else {
// this calls wfAbruptExit()
2005-08-02 13:35:19 +00:00
$this -> mOut -> databaseError ( $fname , $sql , $error , $errno );
2004-07-18 08:48:43 +00:00
}
}
2004-07-24 07:24:04 +00:00
$this -> ignoreErrors ( $ignore );
2004-07-18 08:48:43 +00:00
}
2004-09-03 16:36:46 +00:00
2004-10-18 07:25:56 +00:00
/**
* Intended to be compatible with the PEAR :: DB wrapper functions .
* http :// pear . php . net / manual / en / package . database . db . intro - execute . php
*
* ? = scalar value , quoted as necessary
* ! = raw SQL bit ( a function for instance )
* & = filename ; reads the file and inserts as a blob
* ( we don ' t use this though ... )
*/
function prepare ( $sql , $func = 'Database::prepare' ) {
/* MySQL doesn ' t support prepared statements ( yet ), so just
pack up the query for reference . We ' ll manually replace
the bits later . */
return array ( 'query' => $sql , 'func' => $func );
}
2005-08-02 13:35:19 +00:00
2004-10-18 07:25:56 +00:00
function freePrepared ( $prepared ) {
/* No-op for MySQL */
}
2005-08-02 13:35:19 +00:00
2004-10-18 07:25:56 +00:00
/**
* Execute a prepared query with the various arguments
* @ param string $prepared the prepared sql
* @ param mixed $args Either an array here , or put scalars as varargs
*/
function execute ( $prepared , $args = null ) {
if ( ! is_array ( $args ) ) {
# Pull the var args
$args = func_get_args ();
array_shift ( $args );
}
$sql = $this -> fillPrepared ( $prepared [ 'query' ], $args );
return $this -> query ( $sql , $prepared [ 'func' ] );
}
2005-08-02 13:35:19 +00:00
2004-10-18 07:25:56 +00:00
/**
* Prepare & execute an SQL statement , quoting and inserting arguments
* in the appropriate places .
2005-01-27 18:28:30 +00:00
* @ param string $query
2005-05-26 10:23:36 +00:00
* @ param string $args ...
2004-10-18 07:25:56 +00:00
*/
function safeQuery ( $query , $args = null ) {
$prepared = $this -> prepare ( $query , 'Database::safeQuery' );
if ( ! is_array ( $args ) ) {
# Pull the var args
$args = func_get_args ();
array_shift ( $args );
}
$retval = $this -> execute ( $prepared , $args );
$this -> freePrepared ( $prepared );
return $retval ;
}
2005-08-02 13:35:19 +00:00
2004-10-18 07:25:56 +00:00
/**
* For faking prepared SQL statements on DBs that don ' t support
* it directly .
* @ param string $preparedSql - a 'preparable' SQL statement
* @ param array $args - array of arguments to fill it with
* @ return string executable SQL
*/
function fillPrepared ( $preparedQuery , $args ) {
reset ( $args );
$this -> preparedArgs =& $args ;
return preg_replace_callback ( '/(\\\\[?!&]|[?!&])/' ,
array ( & $this , 'fillPreparedArg' ), $preparedQuery );
}
2005-08-02 13:35:19 +00:00
2004-10-18 07:25:56 +00:00
/**
* preg_callback func for fillPrepared ()
* The arguments should be in $this -> preparedArgs and must not be touched
* while we ' re doing this .
2005-08-02 13:35:19 +00:00
*
2004-10-18 07:25:56 +00:00
* @ param array $matches
* @ return string
2006-04-19 15:46:24 +00:00
* @ private
2004-10-18 07:25:56 +00:00
*/
function fillPreparedArg ( $matches ) {
switch ( $matches [ 1 ] ) {
case '\\?' : return '?' ;
case '\\!' : return '!' ;
case '\\&' : return '&' ;
}
list ( $n , $arg ) = each ( $this -> preparedArgs );
switch ( $matches [ 1 ] ) {
case '?' : return $this -> addQuotes ( $arg );
case '!' : return $arg ;
case '&' :
# return $this->addQuotes( file_get_contents( $arg ) );
wfDebugDieBacktrace ( '& mode is not implemented. If it\'s really needed, uncomment the line above.' );
default :
wfDebugDieBacktrace ( 'Received invalid match. This should never happen!' );
}
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/** #@+
* @ param mixed $res A SQL result
*/
/**
2004-10-24 07:10:33 +00:00
* Free a result object
2004-09-03 16:36:46 +00:00
*/
2004-03-20 14:07:56 +00:00
function freeResult ( $res ) {
2004-08-07 03:41:50 +00:00
if ( !@/**/ mysql_free_result ( $res ) ) {
2004-03-20 14:07:56 +00:00
wfDebugDieBacktrace ( " Unable to free MySQL result \n " );
}
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Fetch the next row from the given result object , in object form
2004-09-03 16:36:46 +00:00
*/
2004-03-11 09:06:13 +00:00
function fetchObject ( $res ) {
2004-08-07 03:41:50 +00:00
@/**/ $row = mysql_fetch_object ( $res );
2004-03-11 09:06:13 +00:00
if ( mysql_errno () ) {
2004-08-22 17:24:50 +00:00
wfDebugDieBacktrace ( 'Error in fetchObject(): ' . htmlspecialchars ( mysql_error () ) );
2004-03-11 09:06:13 +00:00
}
return $row ;
}
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Fetch the next row from the given result object
* Returns an array
2004-09-03 16:36:46 +00:00
*/
2004-06-10 13:02:27 +00:00
function fetchRow ( $res ) {
2004-08-07 03:41:50 +00:00
@/**/ $row = mysql_fetch_array ( $res );
2004-06-10 13:02:27 +00:00
if ( mysql_errno () ) {
2004-08-22 17:24:50 +00:00
wfDebugDieBacktrace ( 'Error in fetchRow(): ' . htmlspecialchars ( mysql_error () ) );
2004-06-10 13:02:27 +00:00
}
return $row ;
2005-08-02 13:35:19 +00:00
}
2004-06-10 13:02:27 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Get the number of rows in a result object
2004-09-03 16:36:46 +00:00
*/
2004-03-11 09:06:13 +00:00
function numRows ( $res ) {
2005-08-02 13:35:19 +00:00
@/**/ $n = mysql_num_rows ( $res );
2004-03-11 09:06:13 +00:00
if ( mysql_errno () ) {
2004-08-22 17:24:50 +00:00
wfDebugDieBacktrace ( 'Error in numRows(): ' . htmlspecialchars ( mysql_error () ) );
2004-03-11 09:06:13 +00:00
}
return $n ;
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Get the number of fields in a result object
* See documentation for mysql_num_fields ()
2004-09-03 16:36:46 +00:00
*/
2004-01-10 16:44:31 +00:00
function numFields ( $res ) { return mysql_num_fields ( $res ); }
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Get a field name in a result object
2006-04-19 15:46:24 +00:00
* See documentation for mysql_field_name () :
* http :// www . php . net / mysql_field_name
2004-09-03 16:36:46 +00:00
*/
2004-01-10 16:44:31 +00:00
function fieldName ( $res , $n ) { return mysql_field_name ( $res , $n ); }
2004-10-24 07:10:33 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Get the inserted value of an auto - increment row
*
* The value inserted should be fetched from nextSequenceValue ()
*
* Example :
2005-01-30 19:34:07 +00:00
* $id = $dbw -> nextSequenceValue ( 'page_page_id_seq' );
* $dbw -> insert ( 'page' , array ( 'page_id' => $id ));
2004-10-24 07:10:33 +00:00
* $id = $dbw -> insertId ();
2004-09-03 16:36:46 +00:00
*/
2004-01-10 16:44:31 +00:00
function insertId () { return mysql_insert_id ( $this -> mConn ); }
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Change the position of the cursor in a result object
* See mysql_data_seek ()
2004-09-03 16:36:46 +00:00
*/
2004-01-10 16:44:31 +00:00
function dataSeek ( $res , $row ) { return mysql_data_seek ( $res , $row ); }
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Get the last error number
* See mysql_errno ()
2004-09-03 16:36:46 +00:00
*/
2005-08-02 13:35:19 +00:00
function lastErrno () {
2005-01-14 13:00:17 +00:00
if ( $this -> mConn ) {
2005-08-02 13:35:19 +00:00
return mysql_errno ( $this -> mConn );
2005-01-14 13:00:17 +00:00
} else {
return mysql_errno ();
}
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Get a description of the last error
* See mysql_error () for more details
2004-09-03 16:36:46 +00:00
*/
2005-08-02 13:35:19 +00:00
function lastError () {
2005-01-14 13:00:17 +00:00
if ( $this -> mConn ) {
2005-08-14 04:42:55 +00:00
# Even if it's non-zero, it can still be invalid
wfSuppressWarnings ();
2005-08-02 13:35:19 +00:00
$error = mysql_error ( $this -> mConn );
2005-08-14 04:42:55 +00:00
if ( ! $error ) {
$error = mysql_error ();
}
wfRestoreWarnings ();
2005-01-14 13:00:17 +00:00
} else {
2005-01-14 13:03:34 +00:00
$error = mysql_error ();
2005-01-14 13:00:17 +00:00
}
2005-01-14 13:03:34 +00:00
if ( $error ) {
$error .= ' (' . $this -> mServer . ')' ;
}
return $error ;
2005-08-02 13:35:19 +00:00
}
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Get the number of rows affected by the last write query
* See mysql_affected_rows () for more details
2004-09-03 16:36:46 +00:00
*/
2004-01-10 16:44:31 +00:00
function affectedRows () { return mysql_affected_rows ( $this -> mConn ); }
2004-09-03 16:36:46 +00:00
/**#@-*/ // end of template : @param $result
/**
* Simple UPDATE wrapper
* Usually aborts on failure
* If errors are explicitly ignored , returns success
2004-09-11 09:44:15 +00:00
*
2005-08-02 13:35:19 +00:00
* This function exists for historical reasons , Database :: update () has a more standard
2004-09-11 09:44:15 +00:00
* calling convention and feature set
2004-09-03 16:36:46 +00:00
*/
2004-08-22 17:24:50 +00:00
function set ( $table , $var , $value , $cond , $fname = 'Database::set' )
2004-01-10 16:44:31 +00:00
{
2004-07-10 03:09:26 +00:00
$table = $this -> tableName ( $table );
2004-01-10 16:44:31 +00:00
$sql = " UPDATE $table SET $var = ' " .
2004-07-18 08:48:43 +00:00
$this -> strencode ( $value ) . " ' WHERE ( $cond ) " ;
2006-01-17 08:40:16 +00:00
return ( bool ) $this -> query ( $sql , $fname );
2004-01-10 16:44:31 +00:00
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Simple SELECT wrapper , returns a single field , input must be encoded
* Usually aborts on failure
* If errors are explicitly ignored , returns FALSE on failure
*/
function selectField ( $table , $var , $cond = '' , $fname = 'Database::selectField' , $options = array () ) {
2004-07-18 08:48:43 +00:00
if ( ! is_array ( $options ) ) {
$options = array ( $options );
2004-07-10 03:09:26 +00:00
}
2004-07-18 08:48:43 +00:00
$options [ 'LIMIT' ] = 1 ;
$res = $this -> select ( $table , $var , $cond , $fname , $options );
if ( $res === false || ! $this -> numRows ( $res ) ) {
return false ;
}
$row = $this -> fetchRow ( $res );
if ( $row !== false ) {
$this -> freeResult ( $res );
return $row [ 0 ];
} else {
return false ;
2004-01-10 16:44:31 +00:00
}
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Returns an optional USE INDEX clause to go after the table , and a
* string to go at the end of the query
2005-05-03 07:47:08 +00:00
*
2006-04-19 15:46:24 +00:00
* @ private
2005-05-03 07:47:08 +00:00
*
* @ param array $options an associative array of options to be turned into
* an SQL query , valid keys are listed in the function .
* @ return array
2004-09-03 16:36:46 +00:00
*/
2004-07-18 08:48:43 +00:00
function makeSelectOptions ( $options ) {
2004-07-10 03:09:26 +00:00
$tailOpts = '' ;
2006-04-11 14:49:31 +00:00
$startOpts = '' ;
2004-07-10 03:09:26 +00:00
2006-04-11 14:49:31 +00:00
$noKeyOptions = array ();
foreach ( $options as $key => $option ) {
if ( is_numeric ( $key ) ) {
$noKeyOptions [ $option ] = true ;
}
2004-07-10 03:09:26 +00:00
}
2006-04-11 14:49:31 +00:00
if ( isset ( $options [ 'GROUP BY' ] ) ) $tailOpts .= " GROUP BY { $options [ 'GROUP BY' ] } " ;
if ( isset ( $options [ 'ORDER BY' ] ) ) $tailOpts .= " ORDER BY { $options [ 'ORDER BY' ] } " ;
2005-08-14 14:42:54 +00:00
if ( isset ( $options [ 'LIMIT' ])) {
2006-01-07 13:09:30 +00:00
$tailOpts .= $this -> limitResult ( '' , $options [ 'LIMIT' ],
2005-08-14 14:42:54 +00:00
isset ( $options [ 'OFFSET' ]) ? $options [ 'OFFSET' ] : false );
}
2005-08-02 13:35:19 +00:00
2006-04-11 14:49:31 +00:00
if ( isset ( $noKeyOptions [ 'FOR UPDATE' ] ) ) $tailOpts .= ' FOR UPDATE' ;
if ( isset ( $noKeyOptions [ 'LOCK IN SHARE MODE' ] ) ) $tailOpts .= ' LOCK IN SHARE MODE' ;
if ( isset ( $noKeyOptions [ 'DISTINCT' ] ) && isset ( $noKeyOptions [ 'DISTINCTROW' ] ) ) $startOpts .= 'DISTINCT' ;
# Various MySQL extensions
if ( isset ( $noKeyOptions [ 'HIGH_PRIORITY' ] ) ) $startOpts .= ' HIGH_PRIORITY' ;
if ( isset ( $noKeyOptions [ 'SQL_BIG_RESULT' ] ) ) $startOpts .= ' SQL_BIG_RESULT' ;
if ( isset ( $noKeyOptions [ 'SQL_BUFFER_RESULT' ] ) ) $startOpts .= ' SQL_BUFFER_RESULT' ;
if ( isset ( $noKeyOptions [ 'SQL_SMALL_RESULT' ] ) ) $startOpts .= ' SQL_SMALL_RESULT' ;
if ( isset ( $noKeyOptions [ 'SQL_CALC_FOUND_ROWS' ] ) ) $startOpts .= ' SQL_CALC_FOUND_ROWS' ;
if ( isset ( $noKeyOptions [ 'SQL_CACHE' ] ) ) $startOpts .= ' SQL_CACHE' ;
if ( isset ( $noKeyOptions [ 'SQL_NO_CACHE' ] ) ) $startOpts .= ' SQL_NO_CACHE' ;
2004-07-10 03:09:26 +00:00
2005-10-02 16:10:39 +00:00
if ( isset ( $options [ 'USE INDEX' ] ) && ! is_array ( $options [ 'USE INDEX' ] ) ) {
2004-07-10 03:09:26 +00:00
$useIndex = $this -> useIndexClause ( $options [ 'USE INDEX' ] );
} else {
$useIndex = '' ;
}
2006-04-11 14:49:31 +00:00
return array ( $startOpts , $useIndex , $tailOpts );
2004-07-18 08:48:43 +00:00
}
2004-07-10 03:09:26 +00:00
2004-09-03 16:36:46 +00:00
/**
* SELECT wrapper
*/
2004-08-22 17:24:50 +00:00
function select ( $table , $vars , $conds = '' , $fname = 'Database::select' , $options = array () )
2004-07-18 08:48:43 +00:00
{
2004-12-13 03:54:56 +00:00
if ( is_array ( $vars ) ) {
2004-08-22 17:24:50 +00:00
$vars = implode ( ',' , $vars );
2004-07-18 08:48:43 +00:00
}
2005-08-02 13:35:19 +00:00
if ( ! is_array ( $options ) ) {
$options = array ( $options );
}
2004-10-03 05:59:45 +00:00
if ( is_array ( $table ) ) {
2005-10-02 16:10:39 +00:00
if ( @ is_array ( $options [ 'USE INDEX' ] ) )
$from = ' FROM ' . $this -> tableNamesWithUseIndex ( $table , $options [ 'USE INDEX' ] );
else
$from = ' FROM ' . implode ( ',' , array_map ( array ( & $this , 'tableName' ), $table ) );
2004-10-03 05:59:45 +00:00
} elseif ( $table != '' ) {
2005-08-29 16:36:37 +00:00
$from = ' FROM ' . $this -> tableName ( $table );
2004-10-03 05:59:45 +00:00
} else {
2004-08-22 17:24:50 +00:00
$from = '' ;
2004-10-03 05:59:45 +00:00
}
2004-08-19 13:02:01 +00:00
2006-04-11 14:49:31 +00:00
list ( $startOpts , $useIndex , $tailOpts ) = $this -> makeSelectOptions ( $options );
2005-08-02 13:35:19 +00:00
2004-12-13 03:54:56 +00:00
if ( ! empty ( $conds ) ) {
2004-07-18 08:48:43 +00:00
if ( is_array ( $conds ) ) {
$conds = $this -> makeList ( $conds , LIST_AND );
}
2006-04-11 14:49:31 +00:00
$sql = " SELECT $startOpts $vars $from $useIndex WHERE $conds $tailOpts " ;
2004-06-21 07:31:41 +00:00
} else {
2006-04-11 14:49:31 +00:00
$sql = " SELECT $startOpts $vars $from $useIndex $tailOpts " ;
2004-06-21 07:31:41 +00:00
}
2005-08-14 14:42:54 +00:00
2004-07-10 03:09:26 +00:00
return $this -> query ( $sql , $fname );
}
2004-09-03 16:36:46 +00:00
/**
* Single row SELECT wrapper
* Aborts or returns FALSE on error
2005-08-02 13:35:19 +00:00
*
2004-09-03 16:36:46 +00:00
* $vars : the selected variables
2005-08-02 13:35:19 +00:00
* $conds : a condition map , terms are ANDed together .
2004-09-03 16:36:46 +00:00
* Items with numeric keys are taken to be literal conditions
* Takes an array of selected variables , and a condition map , which is ANDed
2005-01-11 18:18:16 +00:00
* 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
2004-09-03 16:36:46 +00:00
*
* @ todo migrate documentation to phpdocumentor format
*/
2004-08-22 17:24:50 +00:00
function selectRow ( $table , $vars , $conds , $fname = 'Database::selectRow' , $options = array () ) {
2004-07-10 03:09:26 +00:00
$options [ 'LIMIT' ] = 1 ;
$res = $this -> select ( $table , $vars , $conds , $fname , $options );
2005-08-02 13:35:19 +00:00
if ( $res === false )
return false ;
if ( ! $this -> numRows ( $res ) ) {
$this -> freeResult ( $res );
2004-01-17 05:49:39 +00:00
return false ;
}
2004-03-08 09:37:53 +00:00
$obj = $this -> fetchObject ( $res );
$this -> freeResult ( $res );
return $obj ;
2005-08-02 13:35:19 +00:00
2004-01-17 05:49:39 +00:00
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Removes most variables from an SQL query and replaces them with X or N for numbers .
* It 's only slightly flawed. Don' t use for anything important .
*
* @ param string $sql A SQL Query
* @ static
*/
2005-08-02 13:35:19 +00:00
function generalizeSQL ( $sql ) {
2004-01-10 16:44:31 +00:00
# This does the same as the regexp below would do, but in such a way
# as to avoid crashing php on some large strings.
# $sql = preg_replace ( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql);
2005-08-02 13:35:19 +00:00
2004-08-22 17:24:50 +00:00
$sql = str_replace ( " \\ \\ " , '' , $sql );
$sql = str_replace ( " \\ ' " , '' , $sql );
$sql = str_replace ( " \\ \" " , '' , $sql );
2004-01-10 16:44:31 +00:00
$sql = preg_replace ( " /'.*'/s " , " 'X' " , $sql );
$sql = preg_replace ( '/".*"/s' , " 'X' " , $sql );
2005-08-02 13:35:19 +00:00
2004-01-10 16:44:31 +00:00
# All newlines, tabs, etc replaced by single space
2004-08-22 17:24:50 +00:00
$sql = preg_replace ( " / \ s+/ " , ' ' , $sql );
2005-08-02 13:35:19 +00:00
# All numbers => N
2004-08-22 17:24:50 +00:00
$sql = preg_replace ( '/-?[0-9]+/s' , 'N' , $sql );
2005-08-02 13:35:19 +00:00
2004-01-10 16:44:31 +00:00
return $sql ;
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Determines whether a field exists in a table
* Usually aborts on failure
* If errors are explicitly ignored , returns NULL on failure
*/
function fieldExists ( $table , $field , $fname = 'Database::fieldExists' ) {
2004-07-10 03:09:26 +00:00
$table = $this -> tableName ( $table );
2006-01-17 08:40:16 +00:00
$res = $this -> query ( 'DESCRIBE ' . $table , $fname );
2004-01-10 16:44:31 +00:00
if ( ! $res ) {
return NULL ;
}
2005-08-02 13:35:19 +00:00
2004-01-10 16:44:31 +00:00
$found = false ;
2005-08-02 13:35:19 +00:00
2004-01-10 16:44:31 +00:00
while ( $row = $this -> fetchObject ( $res ) ) {
if ( $row -> Field == $field ) {
$found = true ;
break ;
}
}
return $found ;
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Determines whether an index exists
* Usually aborts on failure
* If errors are explicitly ignored , returns NULL on failure
*/
function indexExists ( $table , $index , $fname = 'Database::indexExists' ) {
2004-07-10 03:09:26 +00:00
$info = $this -> indexInfo ( $table , $index , $fname );
if ( is_null ( $info ) ) {
return NULL ;
} else {
return $info !== false ;
}
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Get information about an index into an object
* Returns false if the index does not exist
2004-09-03 16:36:46 +00:00
*/
2004-08-22 17:24:50 +00:00
function indexInfo ( $table , $index , $fname = 'Database::indexInfo' ) {
2004-05-24 22:24:49 +00:00
# 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
2004-07-10 03:09:26 +00:00
$table = $this -> tableName ( $table );
2004-08-22 17:24:50 +00:00
$sql = 'SHOW INDEX FROM ' . $table ;
2004-07-10 03:09:26 +00:00
$res = $this -> query ( $sql , $fname );
2004-01-10 16:44:31 +00:00
if ( ! $res ) {
return NULL ;
}
2005-08-02 13:35:19 +00:00
2004-01-10 16:44:31 +00:00
while ( $row = $this -> fetchObject ( $res ) ) {
if ( $row -> Key_name == $index ) {
2004-07-10 03:09:26 +00:00
return $row ;
2004-01-10 16:44:31 +00:00
}
}
2004-07-10 03:09:26 +00:00
return false ;
2004-01-10 16:44:31 +00:00
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Query whether a given table exists
2004-09-03 16:36:46 +00:00
*/
function tableExists ( $table ) {
2004-07-10 03:09:26 +00:00
$table = $this -> tableName ( $table );
2004-07-24 07:24:04 +00:00
$old = $this -> ignoreErrors ( true );
2004-03-24 07:49:50 +00:00
$res = $this -> query ( " SELECT 1 FROM $table LIMIT 1 " );
2004-07-24 07:24:04 +00:00
$this -> ignoreErrors ( $old );
2004-03-24 07:49:50 +00:00
if ( $res ) {
$this -> freeResult ( $res );
return true ;
} else {
2004-01-17 05:49:39 +00:00
return false ;
}
}
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* mysql_fetch_field () wrapper
* Returns false if the field doesn ' t exist
*
2004-09-03 16:36:46 +00:00
* @ param $table
* @ param $field
*/
function fieldInfo ( $table , $field ) {
2004-07-10 03:09:26 +00:00
$table = $this -> tableName ( $table );
2004-01-17 05:49:39 +00:00
$res = $this -> query ( " SELECT * FROM $table LIMIT 1 " );
$n = mysql_num_fields ( $res );
for ( $i = 0 ; $i < $n ; $i ++ ) {
$meta = mysql_fetch_field ( $res , $i );
if ( $field == $meta -> name ) {
return $meta ;
}
}
return false ;
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* mysql_field_type () wrapper
2004-09-03 16:36:46 +00:00
*/
2004-07-18 08:48:43 +00:00
function fieldType ( $res , $index ) {
return mysql_field_type ( $res , $index );
}
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Determines if a given index is unique
2004-09-03 16:36:46 +00:00
*/
2004-07-18 08:48:43 +00:00
function indexUnique ( $table , $index ) {
$indexInfo = $this -> indexInfo ( $table , $index );
if ( ! $indexInfo ) {
return NULL ;
}
return ! $indexInfo -> Non_unique ;
}
2004-09-03 16:36:46 +00:00
/**
* INSERT wrapper , inserts an array into a table
*
2005-08-02 13:35:19 +00:00
* $a may be a single associative array , or an array of these with numeric keys , for
2004-09-03 16:36:46 +00:00
* multi - row insert .
*
* Usually aborts on failure
* If errors are explicitly ignored , returns success
*/
function insert ( $table , $a , $fname = 'Database::insert' , $options = array () ) {
2004-07-24 07:24:04 +00:00
# No rows to insert, easy just return now
if ( ! count ( $a ) ) {
return true ;
}
2004-07-10 03:09:26 +00:00
$table = $this -> tableName ( $table );
2004-07-18 08:48:43 +00:00
if ( ! is_array ( $options ) ) {
$options = array ( $options );
}
2004-07-10 03:09:26 +00:00
if ( isset ( $a [ 0 ] ) && is_array ( $a [ 0 ] ) ) {
$multi = true ;
$keys = array_keys ( $a [ 0 ] );
} else {
$multi = false ;
$keys = array_keys ( $a );
}
2004-07-24 07:24:04 +00:00
2005-08-02 13:35:19 +00:00
$sql = 'INSERT ' . implode ( ' ' , $options ) .
2004-07-10 03:09:26 +00:00
" INTO $table ( " . implode ( ',' , $keys ) . ') VALUES ' ;
if ( $multi ) {
$first = true ;
foreach ( $a as $row ) {
if ( $first ) {
$first = false ;
} else {
2004-08-22 17:24:50 +00:00
$sql .= ',' ;
2004-07-10 03:09:26 +00:00
}
$sql .= '(' . $this -> makeList ( $row ) . ')' ;
2004-01-10 16:44:31 +00:00
}
2004-07-10 03:09:26 +00:00
} else {
$sql .= '(' . $this -> makeList ( $a ) . ')' ;
2004-01-17 05:49:39 +00:00
}
2005-07-01 05:28:07 +00:00
return ( bool ) $this -> query ( $sql , $fname );
2004-01-17 05:49:39 +00:00
}
2004-07-18 08:48:43 +00:00
2004-09-03 16:36:46 +00:00
/**
2005-07-18 02:30:04 +00:00
* Make UPDATE options for the Database :: update function
*
2006-04-19 15:46:24 +00:00
* @ private
2005-07-18 02:30:04 +00:00
* @ param array $options The options passed to Database :: update
* @ return string
2004-09-03 16:36:46 +00:00
*/
2005-07-18 02:30:04 +00:00
function makeUpdateOptions ( $options ) {
2005-07-22 11:29:15 +00:00
if ( ! is_array ( $options ) ) {
2005-08-14 14:42:54 +00:00
$options = array ( $options );
2005-07-22 11:29:15 +00:00
}
2005-07-18 02:30:04 +00:00
$opts = array ();
if ( in_array ( 'LOW_PRIORITY' , $options ) )
2005-07-18 05:40:24 +00:00
$opts [] = $this -> lowPriorityOption ();
2006-01-07 13:09:30 +00:00
if ( in_array ( 'IGNORE' , $options ) )
2005-07-18 02:30:04 +00:00
$opts [] = 'IGNORE' ;
return implode ( ' ' , $opts );
}
2006-01-07 13:31:29 +00:00
2005-07-18 02:30:04 +00:00
/**
* UPDATE wrapper , takes a condition array and a SET array
*
* @ param string $table The table to UPDATE
* @ param array $values An array of values to SET
2006-03-28 04:52:05 +00:00
* @ param array $conds An array of conditions ( WHERE ) . Use '*' to update all rows .
2005-07-18 02:30:04 +00:00
* @ param string $fname The Class :: Function calling this function
* ( for the log )
* @ param array $options An array of UPDATE options , can be one or
* more of IGNORE , LOW_PRIORITY
*/
function update ( $table , $values , $conds , $fname = 'Database::update' , $options = array () ) {
2004-07-10 03:09:26 +00:00
$table = $this -> tableName ( $table );
2005-07-18 02:30:04 +00:00
$opts = $this -> makeUpdateOptions ( $options );
$sql = " UPDATE $opts $table SET " . $this -> makeList ( $values , LIST_SET );
2005-06-19 00:21:49 +00:00
if ( $conds != '*' ) {
$sql .= " WHERE " . $this -> makeList ( $conds , LIST_AND );
}
2004-03-23 10:13:59 +00:00
$this -> query ( $sql , $fname );
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Makes a wfStrencoded list from an array
2006-04-22 02:12:59 +00:00
* $mode :
* LIST_COMMA - comma separated , no field names
2004-09-03 16:36:46 +00:00
* LIST_AND - ANDed WHERE clause ( without the WHERE )
2006-04-22 02:12:59 +00:00
* LIST_OR - ORed WHERE clause ( without the WHERE )
2004-09-03 16:36:46 +00:00
* LIST_SET - comma separated with field names , like a SET clause
2005-12-30 09:33:11 +00:00
* LIST_NAMES - comma separated field names
2004-09-03 16:36:46 +00:00
*/
function makeList ( $a , $mode = LIST_COMMA ) {
2004-07-10 03:09:26 +00:00
if ( ! is_array ( $a ) ) {
wfDebugDieBacktrace ( 'Database::makeList called with incorrect parameters' );
}
2004-01-17 05:49:39 +00:00
$first = true ;
2004-08-22 17:24:50 +00:00
$list = '' ;
2004-01-17 05:49:39 +00:00
foreach ( $a as $field => $value ) {
if ( ! $first ) {
if ( $mode == LIST_AND ) {
2004-08-22 17:24:50 +00:00
$list .= ' AND ' ;
2005-08-21 01:17:46 +00:00
} elseif ( $mode == LIST_OR ) {
$list .= ' OR ' ;
2004-01-17 05:49:39 +00:00
} else {
2004-08-22 17:24:50 +00:00
$list .= ',' ;
2004-01-17 05:49:39 +00:00
}
} else {
$first = false ;
}
2005-08-21 01:17:46 +00:00
if ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_numeric ( $field ) ) {
2004-07-10 03:09:26 +00:00
$list .= " ( $value ) " ;
2006-04-22 02:12:59 +00:00
} elseif ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_array ( $value ) ) {
2004-10-04 19:43:49 +00:00
$list .= $field . " IN ( " . $this -> makeList ( $value ) . " ) " ;
2004-01-10 16:44:31 +00:00
} else {
2005-08-21 01:17:46 +00:00
if ( $mode == LIST_AND || $mode == LIST_OR || $mode == LIST_SET ) {
2005-07-21 21:05:02 +00:00
$list .= " $field = " ;
2004-07-10 03:09:26 +00:00
}
2005-07-21 21:05:02 +00:00
$list .= $mode == LIST_NAMES ? $value : $this -> addQuotes ( $value );
2004-01-10 16:44:31 +00:00
}
}
2004-01-17 05:49:39 +00:00
return $list ;
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Change the current database
2004-09-03 16:36:46 +00:00
*/
function selectDB ( $db ) {
2004-03-02 09:26:57 +00:00
$this -> mDBname = $db ;
2004-09-15 06:02:16 +00:00
return mysql_select_db ( $db , $this -> mConn );
2004-01-10 16:44:31 +00:00
}
2004-02-11 13:03:58 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Format a table name ready for use in constructing an SQL query
2005-08-02 13:35:19 +00:00
*
* This does two important things : it quotes table names which as necessary ,
2004-10-24 07:10:33 +00:00
* and it adds a table prefix if there is one .
2005-08-02 13:35:19 +00:00
*
* All functions of this object which require a table name call this function
2004-10-24 07:10:33 +00:00
* themselves . Pass the canonical name to such functions . This is only needed
2005-08-02 13:35:19 +00:00
* when calling query () directly .
2004-10-24 07:10:33 +00:00
*
2004-09-03 16:36:46 +00:00
* @ param string $name database table name
*/
2004-07-10 03:09:26 +00:00
function tableName ( $name ) {
2004-08-28 13:32:14 +00:00
global $wgSharedDB ;
2005-04-10 18:26:26 +00:00
# Skip quoted literals
if ( $name { 0 } != '`' ) {
if ( $this -> mTablePrefix !== '' && strpos ( '.' , $name ) === false ) {
$name = " { $this -> mTablePrefix } $name " ;
2004-07-18 08:48:43 +00:00
}
2005-06-06 00:38:31 +00:00
if ( isset ( $wgSharedDB ) && " { $this -> mTablePrefix } user " == $name ) {
2005-04-10 18:26:26 +00:00
$name = " ` $wgSharedDB `.` $name ` " ;
} else {
# Standard quoting
$name = " ` $name ` " ;
}
2005-08-02 13:35:19 +00:00
}
2004-07-10 03:09:26 +00:00
return $name ;
}
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Fetch a number of table names into an array
* This is handy when you need to construct SQL for joins
*
* Example :
* extract ( $dbr -> tableNames ( 'user' , 'watchlist' ));
2005-08-02 13:35:19 +00:00
* $sql = " SELECT wl_namespace,wl_title FROM $watchlist , $user
2004-10-24 07:10:33 +00:00
* WHERE wl_user = user_id AND wl_user = $nameWithQuotes " ;
2004-09-03 16:36:46 +00:00
*/
2004-07-18 08:48:43 +00:00
function tableNames () {
$inArray = func_get_args ();
$retVal = array ();
foreach ( $inArray as $name ) {
$retVal [ $name ] = $this -> tableName ( $name );
}
return $retVal ;
}
2005-08-02 13:35:19 +00:00
2005-10-02 16:10:39 +00:00
/**
2006-04-19 15:46:24 +00:00
* @ private
2005-10-02 16:10:39 +00:00
*/
function tableNamesWithUseIndex ( $tables , $use_index ) {
$ret = array ();
foreach ( $tables as $table )
if ( @ $use_index [ $table ] !== null )
$ret [] = $this -> tableName ( $table ) . ' ' . $this -> useIndexClause ( implode ( ',' , ( array ) $use_index [ $table ] ) );
else
$ret [] = $this -> tableName ( $table );
return implode ( ',' , $ret );
}
2004-09-03 16:36:46 +00:00
/**
* Wrapper for addslashes ()
* @ param string $s String to be slashed .
* @ return string slashed string .
*/
2004-07-10 03:09:26 +00:00
function strencode ( $s ) {
return addslashes ( $s );
}
2004-09-03 16:36:46 +00:00
/**
* If it ' s a string , adds quotes and backslashes
* Otherwise returns as - is
*/
2004-07-10 03:09:26 +00:00
function addQuotes ( $s ) {
2004-07-19 06:35:56 +00:00
if ( is_null ( $s ) ) {
2005-05-03 07:47:08 +00:00
return 'NULL' ;
2004-08-31 01:01:44 +00:00
} else {
# This will also quote numeric values. This should be harmless,
# and protects against weird problems that occur when they really
# _are_ strings such as article titles and string->number->string
# conversion is not 1:1.
2005-05-03 07:47:08 +00:00
return " ' " . $this -> strencode ( $s ) . " ' " ;
2005-08-02 13:35:19 +00:00
}
2004-07-10 03:09:26 +00:00
}
2005-08-02 13:35:19 +00:00
2005-08-09 13:25:42 +00:00
/**
* Escape string for safe LIKE usage
*/
function escapeLike ( $s ) {
$s = $this -> strencode ( $s );
$s = str_replace ( array ( '%' , '_' ), array ( '\%' , '\_' ), $s );
return $s ;
}
2006-01-07 13:31:29 +00:00
2004-09-03 16:36:46 +00:00
/**
* Returns an appropriately quoted sequence value for inserting a new row .
* MySQL has autoincrement fields , so this is just NULL . But the PostgreSQL
* subclass will return an integer , and save the value for insertId ()
*/
2004-07-10 03:09:26 +00:00
function nextSequenceValue ( $seqName ) {
return NULL ;
}
2004-09-03 16:36:46 +00:00
/**
* USE INDEX clause
* PostgreSQL doesn ' t have them and returns " "
*/
2004-07-10 03:09:26 +00:00
function useIndexClause ( $index ) {
2005-08-26 23:02:54 +00:00
return " FORCE INDEX ( $index ) " ;
2004-07-10 03:09:26 +00:00
}
2004-09-03 16:36:46 +00:00
/**
* REPLACE query wrapper
* PostgreSQL simulates this with a DELETE followed by INSERT
* $row is the row to insert , an associative array
2005-08-02 13:35:19 +00:00
* $uniqueIndexes is an array of indexes . Each element may be either a
2004-09-03 16:36:46 +00:00
* field name or an array of field names
2005-08-02 13:35:19 +00:00
*
* It may be more efficient to leave off unique indexes which are unlikely to collide .
* However if you do this , you run the risk of encountering errors which wouldn ' t have
2004-09-03 16:36:46 +00:00
* occurred in MySQL
*
* @ todo migrate comment to phodocumentor format
*/
2004-08-22 17:24:50 +00:00
function replace ( $table , $uniqueIndexes , $rows , $fname = 'Database::replace' ) {
2004-07-10 03:09:26 +00:00
$table = $this -> tableName ( $table );
# Single row case
if ( ! is_array ( reset ( $rows ) ) ) {
$rows = array ( $rows );
}
2004-09-07 08:25:35 +00:00
$sql = " REPLACE INTO $table ( " . implode ( ',' , array_keys ( $rows [ 0 ] ) ) . ') VALUES ' ;
2004-07-10 03:09:26 +00:00
$first = true ;
foreach ( $rows as $row ) {
if ( $first ) {
$first = false ;
} else {
2004-08-22 17:24:50 +00:00
$sql .= ',' ;
2004-07-10 03:09:26 +00:00
}
2004-08-22 17:24:50 +00:00
$sql .= '(' . $this -> makeList ( $row ) . ')' ;
2004-07-10 03:09:26 +00:00
}
return $this -> query ( $sql , $fname );
}
2004-09-03 16:36:46 +00:00
/**
* DELETE where the condition is a join
* MySQL does this with a multi - table DELETE syntax , PostgreSQL does it with sub - selects
*
2005-08-02 13:35:19 +00:00
* For safety , an empty $conds will not delete everything . If you want to delete all rows where the
2004-09-03 16:36:46 +00:00
* join condition matches , set $conds = '*'
*
* DO NOT put the join condition in $conds
*
* @ param string $delTable The table to delete from .
* @ param string $joinTable The other table .
* @ param string $delVar The variable to join on , in the first table .
* @ param string $joinVar The variable to join on , in the second table .
* @ param array $conds Condition array of field names mapped to variables , ANDed together in the WHERE clause
*/
2004-08-22 17:24:50 +00:00
function deleteJoin ( $delTable , $joinTable , $delVar , $joinVar , $conds , $fname = 'Database::deleteJoin' ) {
2004-07-10 03:09:26 +00:00
if ( ! $conds ) {
wfDebugDieBacktrace ( 'Database::deleteJoin() called with empty $conds' );
}
$delTable = $this -> tableName ( $delTable );
$joinTable = $this -> tableName ( $joinTable );
$sql = " DELETE $delTable FROM $delTable , $joinTable WHERE $delVar = $joinVar " ;
if ( $conds != '*' ) {
2004-08-22 17:24:50 +00:00
$sql .= ' AND ' . $this -> makeList ( $conds , LIST_AND );
2004-07-10 03:09:26 +00:00
}
2005-08-02 13:35:19 +00:00
2004-07-10 03:09:26 +00:00
return $this -> query ( $sql , $fname );
}
2004-09-03 16:36:46 +00:00
/**
* Returns the size of a text field , or - 1 for " unlimited "
*/
2004-07-10 03:09:26 +00:00
function textFieldSize ( $table , $field ) {
$table = $this -> tableName ( $table );
$sql = " SHOW COLUMNS FROM $table LIKE \" $field\ " ; " ;
2004-08-22 17:24:50 +00:00
$res = $this -> query ( $sql , 'Database::textFieldSize' );
2004-07-18 08:48:43 +00:00
$row = $this -> fetchObject ( $res );
2004-07-10 03:09:26 +00:00
$this -> freeResult ( $res );
if ( preg_match ( " / \ ((.*) \ )/ " , $row -> Type , $m ) ) {
$size = $m [ 1 ];
} else {
$size = - 1 ;
}
return $size ;
}
2004-09-03 16:36:46 +00:00
/**
2005-03-20 14:26:37 +00:00
* @ return string Returns the text of the low priority option if it is supported , or a blank string otherwise
2004-09-03 16:36:46 +00:00
*/
2004-07-10 03:09:26 +00:00
function lowPriorityOption () {
return 'LOW_PRIORITY' ;
}
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* DELETE query wrapper
*
2004-09-03 16:36:46 +00:00
* Use $conds == " * " to delete all rows
*/
2004-08-22 17:24:50 +00:00
function delete ( $table , $conds , $fname = 'Database::delete' ) {
2004-07-10 03:09:26 +00:00
if ( ! $conds ) {
2004-08-22 17:24:50 +00:00
wfDebugDieBacktrace ( 'Database::delete() called with no conditions' );
2004-07-10 03:09:26 +00:00
}
$table = $this -> tableName ( $table );
2005-05-03 07:47:08 +00:00
$sql = " DELETE FROM $table " ;
2004-07-10 03:09:26 +00:00
if ( $conds != '*' ) {
2005-05-03 07:47:08 +00:00
$sql .= ' WHERE ' . $this -> makeList ( $conds , LIST_AND );
2004-07-10 03:09:26 +00:00
}
return $this -> query ( $sql , $fname );
}
2004-09-03 16:36:46 +00:00
/**
* INSERT SELECT wrapper
* $varMap must be an associative array of the form array ( 'dest1' => 'source1' , ... )
* Source items may be literals rather than field names , but strings should be quoted with Database :: addQuotes ()
* $conds may be " * " to copy the whole table
2005-01-30 19:34:07 +00:00
* srcTable may be an array of tables .
2004-09-03 16:36:46 +00:00
*/
2006-03-11 17:13:49 +00:00
function insertSelect ( $destTable , $srcTable , $varMap , $conds , $fname = 'Database::insertSelect' ,
2006-04-04 06:06:36 +00:00
$insertOptions = array (), $selectOptions = array () )
2006-02-12 02:20:32 +00:00
{
2004-07-10 03:09:26 +00:00
$destTable = $this -> tableName ( $destTable );
2006-04-04 06:06:36 +00:00
if ( is_array ( $insertOptions ) ) {
$insertOptions = implode ( ' ' , $insertOptions );
2006-02-12 02:20:32 +00:00
}
2006-04-04 06:06:36 +00:00
if ( ! is_array ( $selectOptions ) ) {
$selectOptions = array ( $selectOptions );
}
2006-04-11 14:49:31 +00:00
list ( $startOpts , $useIndex , $tailOpts ) = $this -> makeSelectOptions ( $selectOptions );
2006-02-12 02:20:32 +00:00
if ( is_array ( $srcTable ) ) {
$srcTable = implode ( ',' , array_map ( array ( & $this , 'tableName' ), $srcTable ) );
2006-03-11 17:13:49 +00:00
} else {
2005-01-30 19:34:07 +00:00
$srcTable = $this -> tableName ( $srcTable );
}
2006-04-04 06:06:36 +00:00
$sql = " INSERT $insertOptions INTO $destTable ( " . implode ( ',' , array_keys ( $varMap ) ) . ')' .
2006-04-11 14:49:31 +00:00
" SELECT $startOpts " . implode ( ',' , $varMap ) .
2006-04-04 06:06:36 +00:00
" FROM $srcTable $useIndex " ;
2004-07-10 03:09:26 +00:00
if ( $conds != '*' ) {
2004-08-22 17:24:50 +00:00
$sql .= ' WHERE ' . $this -> makeList ( $conds , LIST_AND );
2004-07-10 03:09:26 +00:00
}
2006-04-04 06:06:36 +00:00
$sql .= " $tailOpts " ;
2004-07-10 03:09:26 +00:00
return $this -> query ( $sql , $fname );
}
2004-07-15 14:50:22 +00:00
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Construct a LIMIT query with optional offset
* This is used for query pages
2005-08-09 10:37:37 +00:00
* $sql string SQL query we will append the limit too
* $limit integer the SQL limit
* $offset integer the SQL offset ( default false )
2004-09-03 16:36:46 +00:00
*/
2005-08-09 10:37:37 +00:00
function limitResult ( $sql , $limit , $offset = false ) {
2006-05-01 05:20:52 +00:00
if ( ! is_numeric ( $limit ) ) {
wfDie ( " Invalid non-numeric limit passed to limitResult() \n " );
}
return " $sql LIMIT "
. ( ( is_numeric ( $offset ) && $offset != 0 ) ? " { $offset } , " : " " )
. " { $limit } " ;
2005-08-02 13:35:19 +00:00
}
2005-08-09 10:37:37 +00:00
function limitResultForUpdate ( $sql , $num ) {
return $this -> limitResult ( $sql , $num , 0 );
2004-07-15 14:50:22 +00:00
}
2004-07-18 08:48:43 +00:00
2004-09-09 00:02:38 +00:00
/**
* Returns an SQL expression for a simple conditional .
* Uses IF on MySQL .
*
* @ param string $cond SQL expression which will result in a boolean value
* @ param string $trueVal SQL expression to return if true
* @ param string $falseVal SQL expression to return if false
* @ return string SQL fragment
*/
function conditional ( $cond , $trueVal , $falseVal ) {
return " IF( $cond , $trueVal , $falseVal ) " ;
}
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Determines if the last failure was due to a deadlock
2004-09-03 16:36:46 +00:00
*/
2004-07-18 08:48:43 +00:00
function wasDeadlock () {
return $this -> lastErrno () == 1213 ;
}
2004-09-03 16:36:46 +00:00
/**
2004-10-24 07:10:33 +00:00
* Perform a deadlock - prone transaction .
*
2005-08-02 13:35:19 +00:00
* This function invokes a callback function to perform a set of write
* queries . If a deadlock occurs during the processing , the transaction
2004-10-24 07:10:33 +00:00
* will be rolled back and the callback function will be called again .
*
2005-08-02 13:35:19 +00:00
* Usage :
2004-10-24 07:10:33 +00:00
* $dbw -> deadlockLoop ( callback , ... );
*
2005-08-02 13:35:19 +00:00
* Extra arguments are passed through to the specified callback function .
*
* Returns whatever the callback function returned on its successful ,
* iteration , or false on error , for example if the retry limit was
2004-10-24 07:10:33 +00:00
* reached .
2004-09-03 16:36:46 +00:00
*/
2004-07-18 08:48:43 +00:00
function deadlockLoop () {
$myFname = 'Database::deadlockLoop' ;
2005-08-02 13:35:19 +00:00
$this -> begin ();
2004-07-18 08:48:43 +00:00
$args = func_get_args ();
$function = array_shift ( $args );
2005-03-13 06:43:33 +00:00
$oldIgnore = $this -> ignoreErrors ( true );
2004-07-18 08:48:43 +00:00
$tries = DEADLOCK_TRIES ;
if ( is_array ( $function ) ) {
$fname = $function [ 0 ];
} else {
$fname = $function ;
}
do {
$retVal = call_user_func_array ( $function , $args );
$error = $this -> lastError ();
$errno = $this -> lastErrno ();
$sql = $this -> lastQuery ();
2005-08-02 13:35:19 +00:00
2004-07-18 08:48:43 +00:00
if ( $errno ) {
2005-03-13 06:43:33 +00:00
if ( $this -> wasDeadlock () ) {
2004-07-18 08:48:43 +00:00
# Retry
usleep ( mt_rand ( DEADLOCK_DELAY_MIN , DEADLOCK_DELAY_MAX ) );
} else {
2005-03-13 06:43:33 +00:00
$this -> reportQueryError ( $error , $errno , $sql , $fname );
2004-07-18 08:48:43 +00:00
}
}
2005-03-13 06:43:33 +00:00
} while ( $this -> wasDeadlock () && -- $tries > 0 );
2004-07-24 07:24:04 +00:00
$this -> ignoreErrors ( $oldIgnore );
2004-07-18 08:48:43 +00:00
if ( $tries <= 0 ) {
2004-08-22 17:24:50 +00:00
$this -> query ( 'ROLLBACK' , $myFname );
2004-07-18 08:48:43 +00:00
$this -> reportQueryError ( $error , $errno , $sql , $fname );
return false ;
} else {
2004-08-22 17:24:50 +00:00
$this -> query ( 'COMMIT' , $myFname );
2004-07-18 08:48:43 +00:00
return $retVal ;
}
}
2004-09-03 16:36:46 +00:00
/**
* Do a SELECT MASTER_POS_WAIT ()
2004-10-24 07:10:33 +00:00
*
* @ param string $file the binlog file
* @ param string $pos the binlog position
* @ param integer $timeout the maximum number of seconds to wait for synchronisation
2004-09-03 16:36:46 +00:00
*/
2004-07-23 12:36:22 +00:00
function masterPosWait ( $file , $pos , $timeout ) {
2004-12-22 03:58:25 +00:00
$fname = 'Database::masterPosWait' ;
wfProfileIn ( $fname );
2005-08-02 13:35:19 +00:00
2004-12-22 03:58:25 +00:00
# Commit any open transactions
$this -> immediateCommit ();
2005-08-02 13:35:19 +00:00
2004-12-22 03:58:25 +00:00
# Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set
2004-07-18 08:48:43 +00:00
$encFile = $this -> strencode ( $file );
2004-07-23 12:36:22 +00:00
$sql = " SELECT MASTER_POS_WAIT(' $encFile ', $pos , $timeout ) " ;
2004-12-22 03:58:25 +00:00
$res = $this -> doQuery ( $sql );
2004-07-18 08:48:43 +00:00
if ( $res && $row = $this -> fetchRow ( $res ) ) {
$this -> freeResult ( $res );
2005-07-27 01:32:54 +00:00
wfProfileOut ( $fname );
2004-07-18 08:48:43 +00:00
return $row [ 0 ];
} else {
2005-07-27 01:32:54 +00:00
wfProfileOut ( $fname );
2004-07-18 08:48:43 +00:00
return false ;
}
}
2004-09-03 16:36:46 +00:00
/**
* Get the position of the master from SHOW SLAVE STATUS
*/
2004-07-18 08:48:43 +00:00
function getSlavePos () {
$res = $this -> query ( 'SHOW SLAVE STATUS' , 'Database::getSlavePos' );
$row = $this -> fetchObject ( $res );
if ( $row ) {
return array ( $row -> Master_Log_File , $row -> Read_Master_Log_Pos );
} else {
return array ( false , false );
}
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Get the position of the master from SHOW MASTER STATUS
*/
2004-07-18 08:48:43 +00:00
function getMasterPos () {
$res = $this -> query ( 'SHOW MASTER STATUS' , 'Database::getMasterPos' );
$row = $this -> fetchObject ( $res );
if ( $row ) {
return array ( $row -> File , $row -> Position );
} else {
return array ( false , false );
}
}
2004-07-24 07:24:04 +00:00
2004-09-03 16:36:46 +00:00
/**
* Begin a transaction , or if a transaction has already started , continue it
*/
2004-07-24 07:24:04 +00:00
function begin ( $fname = 'Database::begin' ) {
if ( ! $this -> mTrxLevel ) {
$this -> immediateBegin ( $fname );
} else {
$this -> mTrxLevel ++ ;
}
}
2004-09-03 16:36:46 +00:00
/**
* End a transaction , or decrement the nest level if transactions are nested
*/
2004-07-24 07:24:04 +00:00
function commit ( $fname = 'Database::commit' ) {
if ( $this -> mTrxLevel ) {
$this -> mTrxLevel -- ;
}
if ( ! $this -> mTrxLevel ) {
$this -> immediateCommit ( $fname );
}
}
2004-09-03 16:36:46 +00:00
/**
* Rollback a transaction
*/
2004-07-24 07:24:04 +00:00
function rollback ( $fname = 'Database::rollback' ) {
$this -> query ( 'ROLLBACK' , $fname );
$this -> mTrxLevel = 0 ;
}
2004-09-03 16:36:46 +00:00
/**
* Begin a transaction , committing any previously open transaction
*/
2004-07-24 07:24:04 +00:00
function immediateBegin ( $fname = 'Database::immediateBegin' ) {
$this -> query ( 'BEGIN' , $fname );
$this -> mTrxLevel = 1 ;
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* Commit transaction , if one is open
*/
2004-07-24 07:24:04 +00:00
function immediateCommit ( $fname = 'Database::immediateCommit' ) {
$this -> query ( 'COMMIT' , $fname );
$this -> mTrxLevel = 0 ;
}
2004-08-10 11:12:18 +00:00
2004-09-03 16:36:46 +00:00
/**
* Return MW - style timestamp used for MySQL schema
*/
2004-08-10 11:12:18 +00:00
function timestamp ( $ts = 0 ) {
return wfTimestamp ( TS_MW , $ts );
}
2005-08-02 13:35:19 +00:00
2005-04-25 18:38:43 +00:00
/**
* Local database timestamp format or null
*/
function timestampOrNull ( $ts = null ) {
if ( is_null ( $ts ) ) {
return null ;
} else {
return $this -> timestamp ( $ts );
}
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* @ todo document
*/
2005-08-28 21:58:14 +00:00
function resultObject ( $result ) {
2004-09-02 02:43:13 +00:00
if ( empty ( $result ) ) {
return NULL ;
} else {
return new ResultWrapper ( $this , $result );
}
}
2004-09-09 12:04:39 +00:00
/**
* Return aggregated value alias
*/
function aggregateValue ( $valuedata , $valuename = 'value' ) {
return $valuename ;
}
2005-08-02 13:35:19 +00:00
2004-09-08 20:36:41 +00:00
/**
* @ return string wikitext of a link to the server software ' s web site
*/
function getSoftwareLink () {
return " [http://www.mysql.com/ MySQL] " ;
}
2005-08-02 13:35:19 +00:00
2004-09-08 20:36:41 +00:00
/**
* @ return string Version information from the database
*/
function getServerVersion () {
return mysql_get_server_info ();
}
2005-04-24 07:21:15 +00:00
/**
* Ping the server and try to reconnect if it there is no connection
*/
function ping () {
2005-06-19 01:05:56 +00:00
if ( function_exists ( 'mysql_ping' ) ) {
return mysql_ping ( $this -> mConn );
} else {
wfDebug ( " Tried to call mysql_ping but this is ancient PHP version. Faking it! \n " );
return true ;
}
2005-04-24 07:21:15 +00:00
}
2005-08-02 13:35:19 +00:00
2005-06-01 06:18:49 +00:00
/**
* Get slave lag .
* At the moment , this will only work if the DB user has the PROCESS privilege
*/
function getLag () {
$res = $this -> query ( 'SHOW PROCESSLIST' );
2005-08-02 13:35:19 +00:00
# Find slave SQL thread. Assumed to be the second one running, which is a bit
2005-06-01 06:18:49 +00:00
# dubious, but unfortunately there's no easy rigorous way
$slaveThreads = 0 ;
while ( $row = $this -> fetchObject ( $res ) ) {
if ( $row -> User == 'system user' ) {
if ( ++ $slaveThreads == 2 ) {
2006-01-04 22:45:55 +00:00
# This is it, return the time (except -ve)
2006-04-16 06:41:29 +00:00
if ( $row -> Time > 0x7fffffff ) {
2006-04-16 06:36:57 +00:00
return false ;
2006-01-04 22:45:55 +00:00
} else {
return $row -> Time ;
}
2005-06-01 06:18:49 +00:00
}
}
}
return false ;
}
/**
* Get status information from SHOW STATUS in an associative array
*/
2006-01-10 10:59:09 +00:00
function getStatus ( $which = " % " ) {
$res = $this -> query ( " SHOW STATUS LIKE ' { $which } ' " );
2005-06-01 06:18:49 +00:00
$status = array ();
while ( $row = $this -> fetchObject ( $res ) ) {
$status [ $row -> Variable_name ] = $row -> Value ;
}
return $status ;
}
2005-08-02 13:35:19 +00:00
/**
* Return the maximum number of items allowed in a list , or 0 for unlimited .
*/
function maxListLen () {
return 0 ;
}
function encodeBlob ( $b ) {
return $b ;
}
2006-01-17 11:48:18 +00:00
/**
2006-03-11 17:13:49 +00:00
* Read and execute SQL commands from a file .
2006-01-17 11:48:18 +00:00
* Returns true on success , error string on failure
*/
function sourceFile ( $filename ) {
$fp = fopen ( $filename , 'r' );
if ( false === $fp ) {
return " Could not open \" { $fname } \" . \n " ;
}
$cmd = " " ;
$done = false ;
2006-04-12 08:15:28 +00:00
$dollarquote = false ;
2006-01-17 11:48:18 +00:00
while ( ! feof ( $fp ) ) {
$line = trim ( fgets ( $fp , 1024 ) );
$sl = strlen ( $line ) - 1 ;
if ( $sl < 0 ) { continue ; }
if ( '-' == $line { 0 } && '-' == $line { 1 } ) { continue ; }
2006-04-12 08:15:28 +00:00
## Allow dollar quoting for function declarations
if ( substr ( $line , 0 , 4 ) == '$mw$' ) {
if ( $dollarquote ) {
$dollarquote = false ;
$done = true ;
}
else {
$dollarquote = true ;
}
}
else if ( ! $dollarquote ) {
if ( ';' == $line { $sl } && ( $sl < 2 || ';' != $line { $sl - 1 })) {
$done = true ;
$line = substr ( $line , 0 , $sl );
}
2006-01-17 11:48:18 +00:00
}
if ( '' != $cmd ) { $cmd .= ' ' ; }
$cmd .= " $line\n " ;
if ( $done ) {
$cmd = str_replace ( ';;' , " ; " , $cmd );
$cmd = $this -> replaceVars ( $cmd );
$res = $this -> query ( $cmd , 'dbsource' , true );
if ( false === $res ) {
2006-01-17 12:14:27 +00:00
$err = $this -> lastError ();
2006-01-17 11:48:18 +00:00
return " Query \" { $cmd } \" failed with error code \" $err\ " . \n " ;
}
$cmd = '' ;
$done = false ;
}
}
fclose ( $fp );
return true ;
}
/**
* Replace variables in sourced SQL
*/
function replaceVars ( $ins ) {
$varnames = array (
'wgDBserver' , 'wgDBname' , 'wgDBintlname' , 'wgDBuser' ,
'wgDBpassword' , 'wgDBsqluser' , 'wgDBsqlpassword' ,
'wgDBadminuser' , 'wgDBadminpassword' ,
);
// Ordinary variables
foreach ( $varnames as $var ) {
if ( isset ( $GLOBALS [ $var ] ) ) {
$val = addslashes ( $GLOBALS [ $var ] );
$ins = str_replace ( '{$' . $var . '}' , $val , $ins );
$ins = str_replace ( '/*$' . $var . '*/`' , '`' . $val , $ins );
$ins = str_replace ( '/*$' . $var . '*/' , $val , $ins );
}
}
// Table prefixes
2006-03-11 17:13:49 +00:00
$ins = preg_replace_callback ( '/\/\*(?:\$wgDBprefix|_)\*\/([a-z_]*)/' ,
2006-01-17 11:48:18 +00:00
array ( & $this , 'tableNameCallback' ), $ins );
return $ins ;
}
/**
* Table name callback
2006-04-19 15:46:24 +00:00
* @ private
2006-01-17 11:48:18 +00:00
*/
function tableNameCallback ( $matches ) {
return $this -> tableName ( $matches [ 1 ] );
}
2005-08-02 13:35:19 +00:00
}
2004-01-10 16:44:31 +00:00
2004-09-02 23:28:24 +00:00
/**
* Database abstraction object for mySQL
2004-09-03 16:36:46 +00:00
* Inherit all methods and properties of Database :: Database ()
2004-09-03 23:00:01 +00:00
*
* @ package MediaWiki
2004-09-03 16:36:46 +00:00
* @ see Database
2004-09-02 23:28:24 +00:00
*/
2004-07-10 03:09:26 +00:00
class DatabaseMysql extends Database {
# Inherit all
}
2004-09-02 02:43:13 +00:00
2004-09-02 23:28:24 +00:00
/**
* Result wrapper for grabbing data queried by someone else
2004-09-03 23:00:01 +00:00
*
* @ package MediaWiki
2004-09-02 23:28:24 +00:00
*/
2004-09-02 02:43:13 +00:00
class ResultWrapper {
2006-05-11 22:40:38 +00:00
var $db , $result ;
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* @ todo document
*/
2005-12-30 09:33:11 +00:00
function ResultWrapper ( & $database , $result ) {
2004-09-02 02:43:13 +00:00
$this -> db =& $database ;
$this -> result =& $result ;
}
2004-09-03 16:36:46 +00:00
/**
* @ todo document
*/
2004-09-02 02:43:13 +00:00
function numRows () {
return $this -> db -> numRows ( $this -> result );
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* @ todo document
*/
2005-07-22 11:29:15 +00:00
function fetchObject () {
2004-09-02 02:43:13 +00:00
return $this -> db -> fetchObject ( $this -> result );
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* @ todo document
*/
2004-09-02 02:43:13 +00:00
function & fetchRow () {
return $this -> db -> fetchRow ( $this -> result );
}
2005-08-02 13:35:19 +00:00
2004-09-03 16:36:46 +00:00
/**
* @ todo document
*/
2004-09-02 02:43:13 +00:00
function free () {
$this -> db -> freeResult ( $this -> result );
unset ( $this -> result );
unset ( $this -> db );
}
2005-04-03 07:27:25 +00:00
function seek ( $row ) {
$this -> db -> dataSeek ( $this -> result , $row );
}
2006-01-17 11:48:18 +00:00
2004-09-02 02:43:13 +00:00
}
2006-01-17 11:48:18 +00:00
2004-01-10 16:44:31 +00:00
#------------------------------------------------------------------------------
# Global functions
#------------------------------------------------------------------------------
2004-09-02 23:28:24 +00:00
/**
* Standard fail function , called by default when a connection cannot be
* established .
* Displays the file cache if possible
*/
2004-06-15 15:00:54 +00:00
function wfEmergencyAbort ( & $conn , $error ) {
2005-03-27 16:56:11 +00:00
global $wgTitle , $wgUseFileCache , $title , $wgInputEncoding , $wgOutputEncoding ;
2005-10-06 03:24:02 +00:00
global $wgSitename , $wgServer , $wgMessageCache , $wgLogo ;
2005-08-02 13:35:19 +00:00
2004-12-12 04:44:17 +00:00
# I give up, Brion is right. Getting the message cache to work when there is no DB is tricky.
# Hard coding strings instead.
2005-10-10 18:02:50 +00:00
$noconnect = " <h1><img src=' $wgLogo ' style='float:left;margin-right:1em' alt=''> $wgSitename has a problem</h1><p><strong>Sorry! This site is experiencing technical difficulties.</strong></p><p>Try waiting a few minutes and reloading.</p><p><small>(Can't contact the database server: $ 1)</small></p> " ;
2004-12-12 04:44:17 +00:00
$mainpage = 'Main Page' ;
$searchdisabled = <<< EOT
< p style = " margin: 1.5em 2em 1em " > $wgSitename search is disabled for performance reasons . You can search via Google in the meantime .
< span style = " font-size: 89%; display: block; margin-left: .2em " > Note that their indexes of $wgSitename content may be out of date .</ span ></ p > ' ,
EOT ;
$googlesearch = "
<!-- SiteSearch Google -->
< FORM method = GET action = \ " http://www.google.com/search \" >
< TABLE bgcolor = \ " #FFFFFF \" ><tr><td>
< A HREF = \ " http://www.google.com/ \" >
< IMG SRC = \ " http://www.google.com/logos/Logo_40wht.gif \"
border = \ " 0 \" ALT= \" Google \" ></A>
</ td >
< td >
< INPUT TYPE = text name = q size = 31 maxlength = 255 value = \ " $ 1 \" >
< INPUT type = submit name = btnG VALUE = \ " Google Search \" >
< font size =- 1 >
< input type = hidden name = domains value = \ " $wgServer\ " >< br />< input type = radio name = sitesearch value = \ " \" > WWW <input type=radio name=sitesearch value= \" $wgServer\ " checked > $wgServer < br />
< input type = 'hidden' name = 'ie' value = '$2' >
< input type = 'hidden' name = 'oe' value = '$2' >
</ font >
</ td ></ tr ></ TABLE >
</ FORM >
<!-- SiteSearch Google --> " ;
$cachederror = " The following is a cached copy of the requested page, and may not be up to date. " ;
2004-06-12 03:13:46 +00:00
if ( ! headers_sent () ) {
2004-08-22 17:24:50 +00:00
header ( 'HTTP/1.0 500 Internal Server Error' );
header ( 'Content-type: text/html; charset=' . $wgOutputEncoding );
2004-06-12 03:13:46 +00:00
/* Don't cache error pages! They cause no end of trouble... */
2004-08-22 17:24:50 +00:00
header ( 'Cache-control: none' );
header ( 'Pragma: nocache' );
2004-06-12 03:13:46 +00:00
}
2005-08-14 04:42:55 +00:00
# No database access
2005-08-23 16:54:45 +00:00
if ( is_object ( $wgMessageCache ) ) {
$wgMessageCache -> disable ();
}
2005-10-28 01:08:49 +00:00
if ( trim ( $error ) == '' ) {
$error = $this -> mServer ;
}
wfLogDBError ( " Connection error: $error\n " );
2006-01-07 13:31:29 +00:00
2005-12-22 06:30:39 +00:00
$text = str_replace ( '$1' , $error , $noconnect );
$text .= wfGetSiteNotice ();
2004-01-10 16:44:31 +00:00
if ( $wgUseFileCache ) {
if ( $wgTitle ) {
$t =& $wgTitle ;
} else {
if ( $title ) {
$t = Title :: newFromURL ( $title );
2004-08-07 03:41:50 +00:00
} elseif ( @/**/ $_REQUEST [ 'search' ]) {
2004-01-10 16:44:31 +00:00
$search = $_REQUEST [ 'search' ];
2004-12-12 04:44:17 +00:00
echo $searchdisabled ;
2005-08-02 13:35:19 +00:00
echo str_replace ( array ( '$1' , '$2' ), array ( htmlspecialchars ( $search ),
2004-12-12 04:44:17 +00:00
$wgInputEncoding ), $googlesearch );
2004-07-24 07:24:04 +00:00
wfErrorExit ();
2004-01-10 16:44:31 +00:00
} else {
2004-12-12 04:44:17 +00:00
$t = Title :: newFromText ( $mainpage );
2004-01-10 16:44:31 +00:00
}
}
$cache = new CacheManager ( $t );
if ( $cache -> isFileCached () ) {
2004-08-22 17:24:50 +00:00
$msg = '<p style="color: red"><b>' . $msg . " <br /> \n " .
2004-12-12 04:44:17 +00:00
$cachederror . " </b></p> \n " ;
2005-08-02 13:35:19 +00:00
2004-08-22 17:24:50 +00:00
$tag = '<div id="article">' ;
2004-01-10 16:44:31 +00:00
$text = str_replace (
$tag ,
$tag . $msg ,
$cache -> fetchPageText () );
}
}
2005-08-02 13:35:19 +00:00
2004-01-10 16:44:31 +00:00
echo $text ;
2004-07-24 07:24:04 +00:00
wfErrorExit ();
2004-01-10 16:44:31 +00:00
}
?>