/** @var TransactionProfiler */
protected $trxProfiler;
+ /**
+ * @var bool Whether writing is allowed on this connection.
+ * Should be false for connections to replicas.
+ */
+ protected $allowWrite = true;
+
/**
* Constructor and database handle and attempt to connect to the DB server
*
$this->connLogger = $params['connLogger'];
$this->queryLogger = $params['queryLogger'];
$this->errorLogger = $params['errorLogger'];
+ $this->allowWrite = empty( $params['noWrite'] );
// Set initial dummy domain until open() sets the final DB/prefix
$this->currentDomain = DatabaseDomain::newUnspecified();
}
if ( $isWrite ) {
+ if ( !$this->allowWrite ) {
+ throw new DBError(
+ $this,
+ 'Write operations are not allowed on this database connection!'
+ );
+ }
+
# In theory, non-persistent writes are allowed in read-only mode, but due to things
# like https://bugs.mysql.com/bug.php?id=33669 that might not work anyway...
$reason = $this->getReadOnlyReason();
}
final public function begin( $fname = __METHOD__, $mode = self::TRANSACTION_EXPLICIT ) {
+ if ( !$this->allowWrite ) {
+ throw new DBError(
+ $this,
+ 'Write operations are not allowed on this database connection!'
+ );
+ }
+
// Protect against mismatched atomic section, transaction nesting, and snapshot loss
if ( $this->mTrxLevel ) {
if ( $this->mTrxAtomicLevels ) {
}
final public function commit( $fname = __METHOD__, $flush = '' ) {
+ if ( !$this->allowWrite ) {
+ // we should never get here, since begin() would already throw
+ throw new DBError(
+ $this,
+ 'Write operations are not allowed on this database connection!'
+ );
+ }
+
if ( $this->mTrxLevel && $this->mTrxAtomicLevels ) {
// There are still atomic sections open. This cannot be ignored
$levels = implode( ', ', $this->mTrxAtomicLevels );
$oldConnsOpened = $this->connsOpened; // connections open now
if ( $i == self::DB_MASTER ) {
+ if ( $flags & self::CONN_NO_WRITE ) {
+ throw new InvalidArgumentException(
+ 'Cannot set CONN_NO_WRITE flag on master connection!'
+ );
+ }
+
$i = $this->getWriterIndex();
} else {
# Try to find an available server in any the query groups (in order)
break;
}
}
+
+ // Request no-write connection, even if $i == $this->getWriterIndex().
+ $flags |= self::CONN_NO_WRITE;
}
# Operation-based index
$this->reportConnectionError();
return null; // not reached
}
+
+ // Request no-write connection, even if $i == $this->getWriterIndex().
+ $flags |= self::CONN_NO_WRITE;
}
# Now we have an explicit index into the servers array
// a) those are usually set to implicitly use transaction rounds via DBO_TRX
// b) those must support the use of explicit transaction rounds via beginMasterChanges()
$autoCommit = ( ( $flags & self::CONN_TRX_AUTO ) == self::CONN_TRX_AUTO );
+ $noWrite = ( ( $flags & self::CONN_NO_WRITE ) == self::CONN_NO_WRITE );
+
+ if ( $noWrite && $i === $this->getWriterIndex() ) {
+ // We can't disable writes on the master connection!
+ // TODO: Wrap the master connection, so write operations fail!
+ $noWrite = false;
+ }
if ( $domain !== false ) {
// Connection is to a foreign domain
// Open a new connection
$server = $this->mServers[$i];
$server['serverIndex'] = $i;
+ $server['noWrite'] = $noWrite;
$server['autoCommitOnly'] = $autoCommit;
$conn = $this->reallyOpenConnection( $server, false );
$host = $this->getServerName( $i );
$dbName = $domainInstance->getDatabase();
$prefix = $domainInstance->getTablePrefix();
$autoCommit = ( ( $flags & self::CONN_TRX_AUTO ) == self::CONN_TRX_AUTO );
+ $noWrite = ( ( $flags & self::CONN_NO_WRITE ) == self::CONN_NO_WRITE );
+
+ if ( $noWrite && $i === $this->getWriterIndex() ) {
+ // We can't disable writes on the master connection!
+ // TODO: Wrap the master connection, so write operations fail!
+ $noWrite = false;
+ }
if ( $autoCommit ) {
$connFreeKey = self::KEY_FOREIGN_FREE_NOROUND;
$server['foreignPoolRefCount'] = 0;
$server['foreign'] = true;
$server['autoCommitOnly'] = $autoCommit;
+ $server['noWrite'] = $noWrite;
$conn = $this->reallyOpenConnection( $server, $dbName );
if ( !$conn->isOpen() ) {
$this->connLogger->warning( __METHOD__ . ": connection error for $i/$domain" );