protected $cliMode;
/** @var string Agent name for query profiling */
protected $agent;
- /** @var int Bitfield of class DBO_* constants */
+ /** @var int Bit field of class DBO_* constants */
protected $flags;
/** @var array LoadBalancer tracking information */
protected $lbInfo = [];
/** @var float Assume an insert of this many rows or less should be fast to replicate */
private static $SMALL_WRITE_ROWS = 100;
+ /** @var string[] List of DBO_* flags that can be changed after connection */
+ protected static $MUTABLE_FLAGS = [
+ 'DBO_DEBUG',
+ 'DBO_NOBUFFER',
+ 'DBO_TRX',
+ 'DBO_DDLMODE',
+ ];
+ /** @var int Bit field of all DBO_* flags that can be changed after connection */
+ protected static $DBO_MUTABLE = (
+ self::DBO_DEBUG | self::DBO_NOBUFFER | self::DBO_TRX | self::DBO_DDLMODE
+ );
+
/**
* @note exceptions for missing libraries/drivers should be thrown in initConnection()
* @param array $params Parameters passed from Database::factory()
/**
* Actually connect to the database over the wire (or to local files)
*
- * @throws InvalidArgumentException
* @throws DBConnectionError
* @since 1.31
*/
protected function doInitConnection() {
- if ( strlen( $this->connectionParams['user'] ) ) {
- $this->open(
- $this->connectionParams['host'],
- $this->connectionParams['user'],
- $this->connectionParams['password'],
- $this->connectionParams['dbname'],
- $this->connectionParams['schema'],
- $this->connectionParams['tablePrefix']
- );
- } else {
- throw new InvalidArgumentException( "No database user provided" );
- }
+ $this->open(
+ $this->connectionParams['host'],
+ $this->connectionParams['user'],
+ $this->connectionParams['password'],
+ $this->connectionParams['dbname'],
+ $this->connectionParams['schema'],
+ $this->connectionParams['tablePrefix']
+ );
}
/**
* equivalent to a "database" in MySQL. Note that MySQL and SQLite do not use schemas.
* - tablePrefix : Optional table prefix that is implicitly added on to all table names
* recognized in queries. This can be used in place of schemas for handle site farms.
- * - flags : Optional bitfield of DBO_* constants that define connection, protocol,
+ * - flags : Optional bit field of DBO_* constants that define connection, protocol,
* buffering, and transaction behavior. It is STRONGLY adviced to leave the DBO_DEFAULT
* flag in place UNLESS this this database simply acts as a key/value store.
* - driver: Optional name of a specific DB client driver. For MySQL, there is only the
return null;
}
- public function setLBInfo( $name, $value = null ) {
- if ( is_null( $value ) ) {
- $this->lbInfo = $name;
+ public function setLBInfo( $nameOrArray, $value = null ) {
+ if ( is_array( $nameOrArray ) ) {
+ $this->lbInfo = $nameOrArray;
+ } elseif ( is_string( $nameOrArray ) ) {
+ if ( $value !== null ) {
+ $this->lbInfo[$nameOrArray] = $value;
+ } else {
+ unset( $this->lbInfo[$nameOrArray] );
+ }
} else {
- $this->lbInfo[$name] = $value;
+ throw new InvalidArgumentException( "Got non-string key" );
}
}
return $this->lazyMasterHandle;
}
- public function implicitGroupby() {
- return true;
- }
-
public function implicitOrderby() {
return true;
}
return $this->lastQuery;
}
- public function doneWrites() {
- return (bool)$this->lastWriteTime;
- }
-
public function lastDoneWrites() {
return $this->lastWriteTime ?: false;
}
}
public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
- if ( ( $flag & self::DBO_IGNORE ) ) {
- throw new UnexpectedValueException( "Modifying DBO_IGNORE is not allowed" );
+ if ( $flag & ~static::$DBO_MUTABLE ) {
+ throw new DBUnexpectedError(
+ $this,
+ "Got $flag (allowed: " . implode( ', ', static::$MUTABLE_FLAGS ) . ')'
+ );
}
if ( $remember === self::REMEMBER_PRIOR ) {
array_push( $this->priorFlags, $this->flags );
}
+
$this->flags |= $flag;
}
public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
- if ( ( $flag & self::DBO_IGNORE ) ) {
- throw new UnexpectedValueException( "Modifying DBO_IGNORE is not allowed" );
+ if ( $flag & ~static::$DBO_MUTABLE ) {
+ throw new DBUnexpectedError(
+ $this,
+ "Got $flag (allowed: " . implode( ', ', static::$MUTABLE_FLAGS ) . ')'
+ );
}
if ( $remember === self::REMEMBER_PRIOR ) {
array_push( $this->priorFlags, $this->flags );
}
+
$this->flags &= ~$flag;
}
}
public function getFlag( $flag ) {
- return (bool)( $this->flags & $flag );
- }
-
- /**
- * @param string $name Class field name
- * @return mixed
- * @deprecated Since 1.28
- */
- public function getProperty( $name ) {
- return $this->$name;
+ return ( ( $this->flags & $flag ) === $flag );
}
public function getDomainID() {
return $this->currentDomain->getId();
}
- final public function getWikiID() {
- return $this->getDomainID();
- }
-
/**
* Get information about an index into an object
* @param string $table Table name
$closed = true; // already closed; nothing to do
}
- $this->conn = false;
+ $this->conn = null;
// Throw any unexpected errors after having disconnected
if ( $exception instanceof Exception ) {
*
* @param string $sql Original SQL query
* @param string $fname Name of the calling function
- * @param int $flags Bitfield of class QUERY_* constants
+ * @param int $flags Bit field of class QUERY_* constants
* @return array An n-tuple of:
* - mixed|bool: An object, resource, or true on success; false on failure
* - string: The result of calling lastError()
* @param string $commentedSql SQL query with debugging/trace comment
* @param bool $isPermWrite Whether the query is a (non-temporary table) write
* @param string $fname Name of the calling function
- * @param int $flags Bitfield of class QUERY_* constants
+ * @param int $flags Bit field of class QUERY_* constants
* @return array An n-tuple of:
* - mixed|bool: An object, resource, or true on success; false on failure
* - string: The result of calling lastError()
// Avoid the overhead of logging calls unless debug mode is enabled
if ( $this->getFlag( self::DBO_DEBUG ) ) {
$this->queryLogger->debug(
- "{method} [{runtime}s]: $sql",
+ "{method} [{runtime}s] {db_host}: $sql",
[
'method' => $fname,
'db_host' => $this->getServer(),
if ( $ignore ) {
$this->queryLogger->debug( "SQL ERROR (ignored): $error" );
} else {
- $exception = $this->getQueryExceptionAndLog( $error, $errno, $sql, $fname );
-
- throw $exception;
+ throw $this->getQueryExceptionAndLog( $error, $errno, $sql, $fname );
}
}
* @return DBError
*/
private function getQueryExceptionAndLog( $error, $errno, $sql, $fname ) {
- $sql1line = mb_substr( str_replace( "\n", "\\n", $sql ), 0, 5 * 1024 );
$this->queryLogger->error(
"{fname}\t{db_server}\t{errno}\t{error}\t{sql1line}",
$this->getLogContext( [
'method' => __METHOD__,
'errno' => $errno,
'error' => $error,
- 'sql1line' => $sql1line,
+ 'sql1line' => mb_substr( str_replace( "\n", "\\n", $sql ), 0, 5 * 1024 ),
'fname' => $fname,
'trace' => ( new RuntimeException() )->getTraceAsString()
] )
);
- $this->queryLogger->debug( "SQL ERROR: " . $error . "" );
+
if ( $this->wasQueryTimeout( $error, $errno ) ) {
$e = new DBQueryTimeoutError( $this, $error, $errno, $sql, $fname );
} elseif ( $this->wasConnectionError( $errno ) ) {
return $e;
}
+ /**
+ * @param string $error
+ * @return DBConnectionError
+ */
+ final protected function newExceptionAfterConnectError( $error ) {
+ // Connection was not fully initialized and is not safe for use
+ $this->conn = null;
+
+ $this->connLogger->error(
+ "Error connecting to {db_server} as user {db_user}: {error}",
+ $this->getLogContext( [
+ 'error' => $error,
+ 'trace' => ( new RuntimeException() )->getTraceAsString()
+ ] )
+ );
+
+ return new DBConnectionError( $this, $error );
+ }
+
public function freeResult( $res ) {
}
$this->doSelectDomain( DatabaseDomain::newFromId( $domain ) );
}
+ /**
+ * @param DatabaseDomain $domain
+ * @throws DBConnectionError
+ * @throws DBError
+ * @since 1.32
+ */
protected function doSelectDomain( DatabaseDomain $domain ) {
$this->currentDomain = $domain;
}
*/
protected function replaceLostConnection( $fname ) {
$this->closeConnection();
- $this->conn = false;
+ $this->conn = null;
$this->handleSessionLossPreconnect();
if ( $this->isOpen() ) {
// Open a new connection resource without messing with the old one
- $this->conn = false;
+ $this->conn = null;
$this->trxEndCallbacks = []; // don't copy
$this->trxSectionCancelCallbacks = []; // don't copy
$this->handleSessionLossPreconnect(); // no trx or locks anymore