*/
final public function initConnection() {
if ( $this->isOpen() ) {
- throw new LogicException( __METHOD__ . ': already connected.' );
+ throw new LogicException( __METHOD__ . ': already connected' );
}
// Establish the connection
$this->doInitConnection();
$this->connectionParams['tablePrefix']
);
} else {
- throw new InvalidArgumentException( "No database user provided." );
+ throw new InvalidArgumentException( "No database user provided" );
}
}
public function dbSchema( $schema = null ) {
if ( strlen( $schema ) && $this->getDBname() === null ) {
- throw new DBUnexpectedError( $this, "Cannot set schema to '$schema'; no database set." );
+ throw new DBUnexpectedError( $this, "Cannot set schema to '$schema'; no database set" );
}
$old = $this->currentDomain->getSchema();
public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
if ( ( $flag & self::DBO_IGNORE ) ) {
- throw new UnexpectedValueException( "Modifying DBO_IGNORE is not allowed." );
+ throw new UnexpectedValueException( "Modifying DBO_IGNORE is not allowed" );
}
if ( $remember === self::REMEMBER_PRIOR ) {
public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
if ( ( $flag & self::DBO_IGNORE ) ) {
- throw new UnexpectedValueException( "Modifying DBO_IGNORE is not allowed." );
+ throw new UnexpectedValueException( "Modifying DBO_IGNORE is not allowed" );
}
if ( $remember === self::REMEMBER_PRIOR ) {
$levels = $this->flatAtomicSectionList();
$exception = new DBUnexpectedError(
$this,
- __METHOD__ . ": atomic sections $levels are still open."
+ __METHOD__ . ": atomic sections $levels are still open"
);
} elseif ( $this->trxAutomatic ) {
// Only the connection manager can commit non-empty DBO_TRX transactions
$exception = new DBUnexpectedError(
$this,
__METHOD__ .
- ": mass commit/rollback of peer transaction required (DBO_TRX set)."
+ ": mass commit/rollback of peer transaction required (DBO_TRX set)"
);
}
} else {
// back, even if empty.
$exception = new DBUnexpectedError(
$this,
- __METHOD__ . ": transaction is still open (from {$this->trxFname})."
+ __METHOD__ . ": transaction is still open (from {$this->trxFname})"
);
}
if ( $this->trxEndCallbacksSuppressed ) {
$exception = $exception ?: new DBUnexpectedError(
$this,
- __METHOD__ . ': callbacks are suppressed; cannot properly commit.'
+ __METHOD__ . ': callbacks are suppressed; cannot properly commit'
);
}
$fnames = $this->pendingWriteAndCallbackCallers();
if ( $fnames ) {
throw new RuntimeException(
- "Transaction callbacks are still pending:\n" . implode( ', ', $fnames )
+ "Transaction callbacks are still pending: " . implode( ', ', $fnames )
);
}
}
*/
final protected function assertHasConnectionHandle() {
if ( !$this->isOpen() ) {
- throw new DBUnexpectedError( $this, "DB connection was already closed." );
+ throw new DBUnexpectedError( $this, "DB connection was already closed" );
}
}
if ( $this->getLBInfo( 'replica' ) === true ) {
throw new DBReadOnlyRoleError(
$this,
- 'Write operations are not allowed on replica database connections.'
+ 'Write operations are not allowed on replica database connections'
);
}
$reason = $this->getReadOnlyReason();
private function assertQueryIsCurrentlyAllowed( $sql, $fname ) {
$verb = $this->getQueryVerb( $sql );
if ( $verb === 'USE' ) {
- throw new DBUnexpectedError( $this, "Got USE query; use selectDomain() instead." );
+ throw new DBUnexpectedError( $this, "Got USE query; use selectDomain() instead" );
}
if ( $verb === 'ROLLBACK' ) { // transaction/savepoint
if ( $this->trxStatus < self::STATUS_TRX_OK ) {
throw new DBTransactionStateError(
$this,
- "Cannot execute query from $fname while transaction status is ERROR.",
+ "Cannot execute query from $fname while transaction status is ERROR",
[],
$this->trxStatusCause
);
*/
public function reportQueryError( $error, $errno, $sql, $fname, $ignore = false ) {
if ( $ignore ) {
- $this->queryLogger->debug( "SQL ERROR (ignored): $error\n" );
+ $this->queryLogger->debug( "SQL ERROR (ignored): $error" );
} else {
$exception = $this->getQueryExceptionAndLog( $error, $errno, $sql, $fname );
'trace' => ( new RuntimeException() )->getTraceAsString()
] )
);
- $this->queryLogger->debug( "SQL ERROR: " . $error . "\n" );
+ $this->queryLogger->debug( "SQL ERROR: " . $error . "" );
if ( $this->wasQueryTimeout( $error, $errno ) ) {
$e = new DBQueryTimeoutError( $this, $error, $errno, $sql, $fname );
} elseif ( $this->wasConnectionError( $errno ) ) {
// functions. Discourage use of such queries to encourage compatibility.
call_user_func(
$this->deprecationLogger,
- __METHOD__ . ": aggregation used with a locking SELECT ($fname)."
+ __METHOD__ . ": aggregation used with a locking SELECT ($fname)"
);
}
} elseif ( count( $var ) == 1 ) {
$column = $var[0] ?? reset( $var );
} else {
- throw new DBUnexpectedError( $this, __METHOD__ . ': got multiple columns.' );
+ throw new DBUnexpectedError( $this, __METHOD__ . ': got multiple columns' );
}
} else {
$column = $var;
if ( $name instanceof Subquery ) {
throw new DBUnexpectedError(
$this,
- __METHOD__ . ': got Subquery instance when expecting a string.'
+ __METHOD__ . ': got Subquery instance when expecting a string'
);
}
# surrounded by symbols which may be considered word breaks.
if ( preg_match( '/(^|\s)(DISTINCT|JOIN|ON|AS)(\s|$)/i', $name ) !== 0 ) {
$this->queryLogger->warning(
- __METHOD__ . ": use of subqueries is not supported this way.",
+ __METHOD__ . ": use of subqueries is not supported this way",
[ 'trace' => ( new RuntimeException() )->getTraceAsString() ]
);
} elseif ( $table instanceof Subquery ) {
$quotedTable = (string)$table;
} else {
- throw new InvalidArgumentException( "Table must be a string or Subquery." );
+ throw new InvalidArgumentException( "Table must be a string or Subquery" );
}
if ( $alias === false || $alias === $table ) {
if ( $table instanceof Subquery ) {
- throw new InvalidArgumentException( "Subquery table missing alias." );
+ throw new InvalidArgumentException( "Subquery table missing alias" );
}
return $quotedTable;
public function limitResult( $sql, $limit, $offset = false ) {
if ( !is_numeric( $limit ) ) {
- throw new DBUnexpectedError( $this,
- "Invalid non-numeric limit passed to limitResult()\n" );
+ throw new DBUnexpectedError(
+ $this,
+ "Invalid non-numeric limit passed to " . __METHOD__
+ );
}
// This version works in MySQL and SQLite. It will very likely need to be
// overridden for most other RDBMS subclasses.
final public function onTransactionResolution( callable $callback, $fname = __METHOD__ ) {
if ( !$this->trxLevel() ) {
- throw new DBUnexpectedError( $this, "No transaction is active." );
+ throw new DBUnexpectedError( $this, "No transaction is active" );
}
$this->trxEndCallbacks[] = [ $callback, $fname, $this->currentAtomicSectionId() ];
}
final public function onAtomicSectionCancel( callable $callback, $fname = __METHOD__ ) {
if ( !$this->trxLevel() || !$this->trxAtomicLevels ) {
- throw new DBUnexpectedError( $this, "No atomic section is open (got $fname)." );
+ throw new DBUnexpectedError( $this, "No atomic section is open (got $fname)" );
}
$this->trxSectionCancelCallbacks[] = [ $callback, $fname, $this->currentAtomicSectionId() ];
}
*/
public function runOnTransactionIdleCallbacks( $trigger ) {
if ( $this->trxLevel() ) { // sanity
- throw new DBUnexpectedError( $this, __METHOD__ . ': a transaction is still open.' );
+ throw new DBUnexpectedError( $this, __METHOD__ . ': a transaction is still open' );
}
if ( $this->trxEndCallbacksSuppressed ) {
final public function endAtomic( $fname = __METHOD__ ) {
if ( !$this->trxLevel() || !$this->trxAtomicLevels ) {
- throw new DBUnexpectedError( $this, "No atomic section is open (got $fname)." );
+ throw new DBUnexpectedError( $this, "No atomic section is open (got $fname)" );
}
// Check if the current section matches $fname
if ( $savedFname !== $fname ) {
throw new DBUnexpectedError(
$this,
- "Invalid atomic section ended (got $fname but expected $savedFname)."
+ "Invalid atomic section ended (got $fname but expected $savedFname)"
);
}
$fname = __METHOD__, AtomicSectionIdentifier $sectionId = null
) {
if ( !$this->trxLevel() || !$this->trxAtomicLevels ) {
- throw new DBUnexpectedError( $this, "No atomic section is open (got $fname)." );
+ throw new DBUnexpectedError( $this, "No atomic section is open (got $fname)" );
}
$excisedIds = [];
if ( $savedFname !== $fname ) {
throw new DBUnexpectedError(
$this,
- "Invalid atomic section ended (got $fname but expected $savedFname)."
+ "Invalid atomic section ended (got $fname but expected $savedFname)"
);
}
$this->trxStatus = self::STATUS_TRX_ERROR;
$this->trxStatusCause = new DBUnexpectedError(
$this,
- "Uncancelable atomic section canceled (got $fname)."
+ "Uncancelable atomic section canceled (got $fname)"
);
}
} finally {
final public function begin( $fname = __METHOD__, $mode = self::TRANSACTION_EXPLICIT ) {
static $modes = [ self::TRANSACTION_EXPLICIT, self::TRANSACTION_INTERNAL ];
if ( !in_array( $mode, $modes, true ) ) {
- throw new DBUnexpectedError( $this, "$fname: invalid mode parameter '$mode'." );
+ throw new DBUnexpectedError( $this, "$fname: invalid mode parameter '$mode'" );
}
// Protect against mismatched atomic section, transaction nesting, and snapshot loss
if ( $this->trxLevel() ) {
if ( $this->trxAtomicLevels ) {
$levels = $this->flatAtomicSectionList();
- $msg = "$fname: Got explicit BEGIN while atomic section(s) $levels are open.";
+ $msg = "$fname: Got explicit BEGIN while atomic section(s) $levels are open";
throw new DBUnexpectedError( $this, $msg );
} elseif ( !$this->trxAutomatic ) {
- $msg = "$fname: Explicit transaction already active (from {$this->trxFname}).";
+ $msg = "$fname: Explicit transaction already active (from {$this->trxFname})";
throw new DBUnexpectedError( $this, $msg );
} else {
- $msg = "$fname: Implicit transaction already active (from {$this->trxFname}).";
+ $msg = "$fname: Implicit transaction already active (from {$this->trxFname})";
throw new DBUnexpectedError( $this, $msg );
}
} elseif ( $this->getFlag( self::DBO_TRX ) && $mode !== self::TRANSACTION_INTERNAL ) {
- $msg = "$fname: Implicit transaction expected (DBO_TRX set).";
+ $msg = "$fname: Implicit transaction expected (DBO_TRX set)";
throw new DBUnexpectedError( $this, $msg );
}
final public function commit( $fname = __METHOD__, $flush = self::FLUSHING_ONE ) {
static $modes = [ self::FLUSHING_ONE, self::FLUSHING_ALL_PEERS, self::FLUSHING_INTERNAL ];
if ( !in_array( $flush, $modes, true ) ) {
- throw new DBUnexpectedError( $this, "$fname: invalid flush parameter '$flush'." );
+ throw new DBUnexpectedError( $this, "$fname: invalid flush parameter '$flush'" );
}
if ( $this->trxLevel() && $this->trxAtomicLevels ) {
$levels = $this->flatAtomicSectionList();
throw new DBUnexpectedError(
$this,
- "$fname: Got COMMIT while atomic sections $levels are still open."
+ "$fname: Got COMMIT while atomic sections $levels are still open"
);
}
} elseif ( !$this->trxAutomatic ) {
throw new DBUnexpectedError(
$this,
- "$fname: Flushing an explicit transaction, getting out of sync."
+ "$fname: Flushing an explicit transaction, getting out of sync"
);
}
} elseif ( !$this->trxLevel() ) {
$this->queryLogger->error(
- "$fname: No transaction to commit, something got out of sync." );
+ "$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)."
+ "$fname: Expected mass commit of all peer transactions (DBO_TRX set)"
);
}
) {
throw new DBUnexpectedError(
$this,
- "$fname: Expected mass rollback of all peer transactions (DBO_TRX set)."
+ "$fname: Expected mass rollback of all peer transactions (DBO_TRX set)"
);
}
}
}
- public function flushSnapshot( $fname = __METHOD__ ) {
- if ( $this->writesOrCallbacksPending() || $this->explicitTrxActive() ) {
+ public function flushSnapshot( $fname = __METHOD__, $flush = self::FLUSHING_ONE ) {
+ if ( $this->explicitTrxActive() ) {
+ // Committing this transaction would break callers that assume it is still open
+ throw new DBUnexpectedError(
+ $this,
+ "$fname: Cannot flush snapshot; " .
+ "explicit transaction '{$this->trxFname}' is still open"
+ );
+ } elseif ( $this->writesOrCallbacksPending() ) {
// This only flushes transactions to clear snapshots, not to write data
$fnames = implode( ', ', $this->pendingWriteAndCallbackCallers() );
throw new DBUnexpectedError(
$this,
- "$fname: Cannot flush snapshot because writes are pending ($fnames)."
+ "$fname: Cannot flush snapshot; " .
+ "writes from transaction {$this->trxFname} are still pending ($fnames)"
+ );
+ } elseif (
+ $this->trxLevel() &&
+ $this->getTransactionRoundId() &&
+ $flush !== self::FLUSHING_INTERNAL &&
+ $flush !== self::FLUSHING_ALL_PEERS
+ ) {
+ $this->queryLogger->warning(
+ "$fname: Expected mass snapshot flush of all peer transactions " .
+ "in the explicit transactions round '{$this->getTransactionRoundId()}'",
+ [ 'trace' => ( new RuntimeException() )->getTraceAsString() ]
);
}
Wikimedia\restoreWarnings();
if ( $fp === false ) {
- throw new RuntimeException( "Could not open \"{$filename}\".\n" );
+ throw new RuntimeException( "Could not open \"{$filename}\"" );
}
if ( !$fname ) {
$fnames = implode( ', ', $this->pendingWriteAndCallbackCallers() );
throw new DBUnexpectedError(
$this,
- "$fname: Cannot flush pre-lock snapshot because writes are pending ($fnames)."
+ "$fname: Cannot flush pre-lock snapshot; " .
+ "writes from transaction {$this->trxFname} are still pending ($fnames)"
);
}
final public function lockTables( array $read, array $write, $method ) {
if ( $this->writesOrCallbacksPending() ) {
- throw new DBUnexpectedError( $this, "Transaction writes or callbacks still pending." );
+ throw new DBUnexpectedError( $this, "Transaction writes or callbacks still pending" );
}
if ( $this->tableLocksHaveTransactionScope() ) {
if ( !$this->conn ) {
throw new DBUnexpectedError(
$this,
- 'DB connection was already closed or the connection dropped.'
+ 'DB connection was already closed or the connection dropped'
);
}
*/
public function __clone() {
$this->connLogger->warning(
- "Cloning " . static::class . " is not recommended; forking connection:\n" .
- ( new RuntimeException() )->getTraceAsString()
+ "Cloning " . static::class . " is not recommended; forking connection",
+ [ 'trace' => ( new RuntimeException() )->getTraceAsString() ]
);
if ( $this->isOpen() ) {
*/
public function __sleep() {
throw new RuntimeException( 'Database serialization may cause problems, since ' .
- 'the connection is not restored on wakeup.' );
+ 'the connection is not restored on wakeup' );
}
/**
*/
public function __destruct() {
if ( $this->trxLevel() && $this->trxDoneWrites ) {
- trigger_error( "Uncommitted DB writes (transaction from {$this->trxFname})." );
+ trigger_error( "Uncommitted DB writes (transaction from {$this->trxFname})" );
}
$danglingWriters = $this->pendingWriteAndCallbackCallers();
if ( $danglingWriters ) {
$fnames = implode( ', ', $danglingWriters );
- trigger_error( "DB transaction writes or callbacks still pending ($fnames)." );
+ trigger_error( "DB transaction writes or callbacks still pending ($fnames)" );
}
if ( $this->conn ) {