const SLOW_WRITE_SEC = 0.500;
const SMALL_WRITE_ROWS = 100;
- /** @var string Whether lock granularity is on the level of the entire database */
+ /** @var string Lock granularity is on the level of the entire database */
const ATTR_DB_LEVEL_LOCKING = 'db-level-locking';
+ /** @var string The SCHEMA keyword refers to a grouping of tables in a database */
+ const ATTR_SCHEMAS_AS_TABLE_GROUPS = 'supports-schemas';
/** @var int New Database instance will not be connected yet when returned */
const NEW_UNCONNECTED = 0;
/**
* @param string $dbType A possible DB type (sqlite, mysql, postgres,...)
* @param string|null $driver Optional name of a specific DB client driver
- * @return array Map of (Database::ATTRIBUTE_* constant => value) for all such constants
+ * @return array Map of (Database::ATTR_* constant => value) for all such constants
* @throws InvalidArgumentException
* @since 1.31
*/
final public static function attributesFromType( $dbType, $driver = null ) {
- static $defaults = [ self::ATTR_DB_LEVEL_LOCKING => false ];
+ static $defaults = [
+ self::ATTR_DB_LEVEL_LOCKING => false,
+ self::ATTR_SCHEMAS_AS_TABLE_GROUPS => false
+ ];
$class = self::getClass( $dbType, $driver );
$possibleDrivers = $builtinTypes[$dbType];
if ( is_string( $possibleDrivers ) ) {
$class = $possibleDrivers;
+ } elseif ( (string)$driver !== '' ) {
+ if ( !isset( $possibleDrivers[$driver] ) ) {
+ throw new InvalidArgumentException( __METHOD__ .
+ " type '$dbType' does not support driver '{$driver}'" );
+ }
+
+ $class = $possibleDrivers[$driver];
} else {
- if ( (string)$driver !== '' ) {
- if ( !isset( $possibleDrivers[$driver] ) ) {
- throw new InvalidArgumentException( __METHOD__ .
- " type '$dbType' does not support driver '{$driver}'" );
- } else {
- $class = $possibleDrivers[$driver];
- }
- } else {
- foreach ( $possibleDrivers as $posDriver => $possibleClass ) {
- if ( extension_loaded( $posDriver ) ) {
- $class = $possibleClass;
- break;
- }
+ foreach ( $possibleDrivers as $posDriver => $possibleClass ) {
+ if ( extension_loaded( $posDriver ) ) {
+ $class = $possibleClass;
+ break;
}
}
}
}
/**
- * @return array Map of (Database::ATTRIBUTE_* constant => value
+ * @return array Map of (Database::ATTR_* constant => value
* @since 1.31
*/
protected static function getAttributes() {
}
public function dbSchema( $schema = null ) {
+ if ( strlen( $schema ) && $this->getDBname() === null ) {
+ throw new DBUnexpectedError( $this, "Cannot set schema to '$schema'; no database set." );
+ }
+
$old = $this->currentDomain->getSchema();
if ( $schema !== null ) {
$this->currentDomain = new DatabaseDomain(
public function getLBInfo( $name = null ) {
if ( is_null( $name ) ) {
return $this->lbInfo;
- } else {
- if ( array_key_exists( $name, $this->lbInfo ) ) {
- return $this->lbInfo[$name];
- } else {
- return null;
- }
}
+
+ if ( array_key_exists( $name, $this->lbInfo ) ) {
+ return $this->lbInfo[$name];
+ }
+
+ return null;
}
public function setLBInfo( $name, $value = null ) {
*/
protected function assertIsWritableMaster() {
if ( $this->getLBInfo( 'replica' ) === true ) {
- throw new DBUnexpectedError(
+ throw new DBReadOnlyRoleError(
$this,
'Write operations are not allowed on replica database connections.'
);
$flags = (int)$flags; // b/c; this field used to be a bool
$ignoreErrors = $this->hasFlags( $flags, self::QUERY_SILENCE_ERRORS );
- $pseudoPermanent = $this->hasFlags( $flags, self::QUERY_PSEUDO_PERMANENT );
$priorTransaction = $this->trxLevel;
$priorWritesPending = $this->writesOrCallbacksPending();
$this->assertIsWritableMaster();
# Do not treat temporary table writes as "meaningful writes" that need committing.
# Profile them as reads. Integration tests can override this behavior via $flags.
+ $pseudoPermanent = $this->hasFlags( $flags, self::QUERY_PSEUDO_PERMANENT );
$tableType = $this->registerTempTableWrite( $sql, $pseudoPermanent );
$isEffectiveWrite = ( $tableType !== self::TEMP_NORMAL );
+ # DBConnRef uses QUERY_REPLICA_ROLE to enforce the replica role for raw SQL queries
+ if ( $isEffectiveWrite && $this->hasFlags( $flags, self::QUERY_REPLICA_ROLE ) ) {
+ throw new DBReadOnlyRoleError( $this, "Cannot write; target role is DB_REPLICA" );
+ }
} else {
$isEffectiveWrite = false;
}
throw new InvalidArgumentException( "Table must be a string or Subquery." );
}
- if ( !strlen( $alias ) || $alias === $table ) {
+ if ( $alias === false || $alias === $table ) {
if ( $table instanceof Subquery ) {
throw new InvalidArgumentException( "Subquery table missing alias." );
}
list( $phpCallback ) = $callback;
$this->clearFlag( self::DBO_TRX ); // make each query its own transaction
try {
+ // @phan-suppress-next-line PhanParamTooManyCallable
call_user_func( $phpCallback, $trigger, $this );
} catch ( Exception $ex ) {
call_user_func( $this->errorLogger, $ex );
"$fname: Flushing an explicit transaction, getting out of sync."
);
}
- } else {
- if ( !$this->trxLevel ) {
- $this->queryLogger->error(
- "$fname: No transaction to commit, something got out of sync." );
- return; // nothing to do
- } elseif ( $this->trxAutomatic ) {
- throw new DBUnexpectedError(
- $this,
- "$fname: Expected mass commit of all peer transactions (DBO_TRX set)."
- );
- }
+ } elseif ( !$this->trxLevel ) {
+ $this->queryLogger->error(
+ "$fname: No transaction to commit, something got out of sync." );
+ return; // nothing to do
+ } elseif ( $this->trxAutomatic ) {
+ throw new DBUnexpectedError(
+ $this,
+ "$fname: Expected mass commit of all peer transactions (DBO_TRX set)."
+ );
}
$this->assertHasConnectionHandle();
final public function rollback( $fname = __METHOD__, $flush = '' ) {
$trxActive = $this->trxLevel;
- if ( $flush !== self::FLUSHING_INTERNAL && $flush !== self::FLUSHING_ALL_PEERS ) {
- if ( $this->getFlag( self::DBO_TRX ) ) {
- throw new DBUnexpectedError(
- $this,
- "$fname: Expected mass rollback of all peer transactions (DBO_TRX set)."
- );
- }
+ if ( $flush !== self::FLUSHING_INTERNAL
+ && $flush !== self::FLUSHING_ALL_PEERS
+ && $this->getFlag( self::DBO_TRX )
+ ) {
+ throw new DBUnexpectedError(
+ $this,
+ "$fname: Expected mass rollback of all peer transactions (DBO_TRX set)."
+ );
}
if ( $trxActive ) {