protected $user;
/** @var string Password used to establish the current connection */
protected $password;
- /** @var string Database that this instance is currently connected to */
- protected $dbName;
/** @var array[] Map of (table => (dbname, schema, prefix) map) */
protected $tableAliases = [];
/** @var string[] Map of (index alias => index) */
/** @var bool Whether to suppress triggering of transaction end callbacks */
protected $trxEndCallbacksSuppressed = false;
- /** @var string */
- protected $tablePrefix = '';
- /** @var string */
- protected $schema = '';
/** @var int */
protected $flags;
/** @var array */
* @param array $params Parameters passed from Database::factory()
*/
protected function __construct( array $params ) {
- foreach ( [ 'host', 'user', 'password', 'dbname' ] as $name ) {
+ foreach ( [ 'host', 'user', 'password', 'dbname', 'schema', 'tablePrefix' ] as $name ) {
$this->connectionParams[$name] = $params[$name];
}
- $this->schema = $params['schema'];
- $this->tablePrefix = $params['tablePrefix'];
-
$this->cliMode = $params['cliMode'];
// Agent name is added to SQL queries in a comment, so make sure it can't break out
$this->agent = str_replace( '/', '-', $params['agent'] );
}
// Set initial dummy domain until open() sets the final DB/prefix
- $this->currentDomain = DatabaseDomain::newUnspecified();
+ $this->currentDomain = new DatabaseDomain(
+ $params['dbname'] != '' ? $params['dbname'] : null,
+ $params['schema'] != '' ? $params['schema'] : null,
+ $params['tablePrefix']
+ );
}
/**
}
// Establish the connection
$this->doInitConnection();
- // Set the domain object after open() sets the relevant fields
- if ( $this->dbName != '' ) {
- // Domains with server scope but a table prefix are not used by IDatabase classes
- $this->currentDomain = new DatabaseDomain( $this->dbName, null, $this->tablePrefix );
- }
}
/**
$this->connectionParams['host'],
$this->connectionParams['user'],
$this->connectionParams['password'],
- $this->connectionParams['dbname']
+ $this->connectionParams['dbname'],
+ $this->connectionParams['schema'],
+ $this->connectionParams['tablePrefix']
);
} else {
throw new InvalidArgumentException( "No database user provided." );
* @param string $user Database user name
* @param string $password Database user password
* @param string $dbName Database name
+ * @param string|null $schema Database schema name
+ * @param string $tablePrefix Table prefix
* @return bool
* @throws DBConnectionError
*/
- abstract protected function open( $server, $user, $password, $dbName );
+ abstract protected function open( $server, $user, $password, $dbName, $schema, $tablePrefix );
/**
* Construct a Database subclass instance given a database type and parameters
$p['flags'] = $p['flags'] ?? 0;
$p['variables'] = $p['variables'] ?? [];
$p['tablePrefix'] = $p['tablePrefix'] ?? '';
- $p['schema'] = $p['schema'] ?? '';
+ $p['schema'] = $p['schema'] ?? null;
$p['cliMode'] = $p['cliMode'] ?? ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
$p['agent'] = $p['agent'] ?? '';
if ( !isset( $p['connLogger'] ) ) {
}
public function tablePrefix( $prefix = null ) {
- $old = $this->tablePrefix;
+ $old = $this->currentDomain->getTablePrefix();
if ( $prefix !== null ) {
- $this->tablePrefix = $prefix;
- $this->currentDomain = ( $this->dbName != '' )
- ? new DatabaseDomain( $this->dbName, null, $this->tablePrefix )
- : DatabaseDomain::newUnspecified();
+ $this->currentDomain = new DatabaseDomain(
+ $this->currentDomain->getDatabase(),
+ $this->currentDomain->getSchema(),
+ $prefix
+ );
}
return $old;
}
public function dbSchema( $schema = null ) {
- $old = $this->schema;
+ $old = $this->currentDomain->getSchema();
if ( $schema !== null ) {
- $this->schema = $schema;
+ $this->currentDomain = new DatabaseDomain(
+ $this->currentDomain->getDatabase(),
+ // DatabaseDomain uses null for unspecified schemas
+ strlen( $schema ) ? $schema : null,
+ $this->currentDomain->getTablePrefix()
+ );
}
- return $old;
+ return (string)$old;
+ }
+
+ /**
+ * @return string Schema to use to qualify relations in queries
+ */
+ protected function relationSchemaQualifier() {
+ return $this->dbSchema();
}
public function getLBInfo( $name = null ) {
return array_merge(
[
'db_server' => $this->server,
- 'db_name' => $this->dbName,
+ 'db_name' => $this->getDBname(),
'db_user' => $this->user,
],
$extras
}
}
+ public function assertNoOpenTransactions() {
+ if ( $this->explicitTrxActive() ) {
+ throw new DBTransactionError(
+ $this,
+ "Explicit transaction still active. A caller may have caught an error. "
+ . "Open transactions: " . $this->flatAtomicSectionList()
+ );
+ }
+ }
+
/**
* Determine whether or not it is safe to retry queries after a database
* connection is lost
return false;
}
- public function selectDB( $db ) {
- # 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.
- $this->dbName = $db;
+ final public function selectDB( $db ) {
+ $this->selectDomain( new DatabaseDomain(
+ $db,
+ $this->currentDomain->getSchema(),
+ $this->currentDomain->getTablePrefix()
+ ) );
return true;
}
+ final public function selectDomain( $domain ) {
+ $this->doSelectDomain( DatabaseDomain::newFromId( $domain ) );
+ }
+
+ protected function doSelectDomain( DatabaseDomain $domain ) {
+ $this->currentDomain = $domain;
+ }
+
public function getDBname() {
- return $this->dbName;
+ return $this->currentDomain->getDatabase();
}
public function getServer() {
$database = $this->tableAliases[$table]['dbname'];
$schema = is_string( $this->tableAliases[$table]['schema'] )
? $this->tableAliases[$table]['schema']
- : $this->schema;
+ : $this->relationSchemaQualifier();
$prefix = is_string( $this->tableAliases[$table]['prefix'] )
? $this->tableAliases[$table]['prefix']
- : $this->tablePrefix;
+ : $this->tablePrefix();
} else {
$database = '';
- $schema = $this->schema; # Default schema
- $prefix = $this->tablePrefix; # Default prefix
+ $schema = $this->relationSchemaQualifier(); # Default schema
+ $prefix = $this->tablePrefix(); # Default prefix
}
}
$sectionId = new AtomicSectionIdentifier;
$this->trxAtomicLevels[] = [ $fname, $sectionId, $savepointId ];
+ $this->queryLogger->debug( 'startAtomic: entering level ' .
+ ( count( $this->trxAtomicLevels ) - 1 ) . " ($fname)" );
return $sectionId;
}
// Check if the current section matches $fname
$pos = count( $this->trxAtomicLevels ) - 1;
list( $savedFname, $sectionId, $savepointId ) = $this->trxAtomicLevels[$pos];
+ $this->queryLogger->debug( "endAtomic: leaving level $pos ($fname)" );
if ( $savedFname !== $fname ) {
throw new DBUnexpectedError(
throw new DBUnexpectedError( $this, "No atomic section is open (got $fname)." );
}
+ $excisedFnames = [];
if ( $sectionId !== null ) {
// Find the (last) section with the given $sectionId
$pos = -1;
$excisedIds = [];
$len = count( $this->trxAtomicLevels );
for ( $i = $pos + 1; $i < $len; ++$i ) {
+ $excisedFnames[] = $this->trxAtomicLevels[$i][0];
$excisedIds[] = $this->trxAtomicLevels[$i][1];
}
$this->trxAtomicLevels = array_slice( $this->trxAtomicLevels, 0, $pos + 1 );
$pos = count( $this->trxAtomicLevels ) - 1;
list( $savedFname, $savedSectionId, $savepointId ) = $this->trxAtomicLevels[$pos];
+ if ( $excisedFnames ) {
+ $this->queryLogger->debug( "cancelAtomic: canceling level $pos ($savedFname) " .
+ "and descendants " . implode( ', ', $excisedFnames ) );
+ } else {
+ $this->queryLogger->debug( "cancelAtomic: canceling level $pos ($savedFname)" );
+ }
+
if ( $savedFname !== $fname ) {
throw new DBUnexpectedError(
$this,
$this->opened = false;
$this->conn = false;
try {
- $this->open( $this->server, $this->user, $this->password, $this->dbName );
+ $this->open(
+ $this->server,
+ $this->user,
+ $this->password,
+ $this->getDBname(),
+ $this->dbSchema(),
+ $this->tablePrefix()
+ );
$this->lastPing = microtime( true );
$ok = true;
$this->conn = false;
$this->trxEndCallbacks = []; // don't copy
$this->handleSessionLoss(); // no trx or locks anymore
- $this->open( $this->server, $this->user, $this->password, $this->dbName );
+ $this->open(
+ $this->server,
+ $this->user,
+ $this->password,
+ $this->getDBname(),
+ $this->dbSchema(),
+ $this->tablePrefix()
+ );
$this->lastPing = microtime( true );
}
}