/** @var string Transaction is requested internally via DBO_TRX/startAtomic() */
const TRANSACTION_INTERNAL = 'implicit';
- /** @var string Transaction operation comes from service managing all DBs */
+ /** @var string Atomic section is not cancelable */
+ const ATOMIC_NOT_CANCELABLE = '';
+ /** @var string Atomic section is cancelable */
+ const ATOMIC_CANCELABLE = 'cancelable';
+
+ /** @var string Commit/rollback is from outside the IDatabase handle and connection manager */
+ const FLUSHING_ONE = '';
+ /** @var string Commit/rollback is from the connection manager for the IDatabase handle */
const FLUSHING_ALL_PEERS = 'flush';
- /** @var string Transaction operation comes from the database class internally */
+ /** @var string Commit/rollback is from the IDatabase handle internally */
const FLUSHING_INTERNAL = 'flush';
/** @var string Do not remember the prior flags */
const DBO_NOBUFFER = 2;
/** @var int Ignore query errors (internal use only!) */
const DBO_IGNORE = 4;
- /** @var int Autoatically start transaction on first query (work with ILoadBalancer rounds) */
+ /** @var int Automatically start a transaction before running a query if none is active */
const DBO_TRX = 8;
/** @var int Use DBO_TRX in non-CLI mode */
const DBO_DEFAULT = 16;
* Should return true if unsure.
*
* @return bool
+ * @deprecated Since 1.31; use lastDoneWrites()
*/
public function doneWrites();
public function writesPending();
/**
- * Returns true if there is a transaction open with possible write
+ * Returns true if there is a transaction/round open with possible write
* queries or transaction pre-commit/idle callbacks waiting on it to finish.
* This does *not* count recurring callbacks, e.g. from setTransactionListener().
*
*/
public function lastError();
- /**
- * mysql_fetch_field() wrapper
- * Returns false if the field doesn't exist
- *
- * @param string $table Table name
- * @param string $field Field name
- *
- * @return Field
- */
- public function fieldInfo( $table, $field );
-
/**
* Get the number of rows affected by the last write query
* @see https://secure.php.net/mysql_affected_rows
*/
public function close();
- /**
- * @param string $error Fallback error message, used if none is given by DB
- * @throws DBConnectionError
- */
- public function reportConnectionError( $error = 'Unknown error' );
-
/**
* Run an SQL query and return the result. Normally throws a DBQueryError
* on failure. If errors are ignored, returns false instead.
*
+ * If a connection loss is detected, then an attempt to reconnect will be made.
+ * For queries that involve no larger transactions or locks, they will be re-issued
+ * for convenience, provided the connection was re-established.
+ *
* In new code, the query wrappers select(), insert(), update(), delete(),
* etc. should be used where possible, since they give much better DBMS
* independence and automatically quote or validate user input in a variety
*/
public function query( $sql, $fname = __METHOD__, $tempIgnore = false );
- /**
- * Report a query error. Log the error, and if neither the object ignore
- * flag nor the $tempIgnore flag is set, throw a DBQueryError.
- *
- * @param string $error
- * @param int $errno
- * @param string $sql
- * @param string $fname
- * @param bool $tempIgnore
- * @throws DBQueryError
- */
- public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false );
-
/**
* Free a result object returned by query() or select(). It's usually not
* necessary to call this, just use unset() or let the variable holding
* This includes the user table in the query, with the alias "a" available
* for use in field names (e.g. a.user_name).
*
+ * A derived table, defined by the result of selectSQLText(), requires an alias
+ * key and a Subquery instance value which wraps the SQL query, for example:
+ *
+ * [ 'c' => new Subquery( 'SELECT ...' ) ]
+ *
* Joins using parentheses for grouping (since MediaWiki 1.31) may be
* constructed using nested arrays. For example,
*
* doing UNION queries, where the SQL text of each query is needed. In general,
* however, callers outside of Database classes should just use select().
*
+ * @see IDatabase::select()
+ *
* @param string|array $table Table name
* @param string|array $vars Field names
* @param string|array $conds Conditions
* @param string $fname Caller function name
* @param string|array $options Query options
* @param string|array $join_conds Join conditions
- *
- * @return string SQL query string.
- * @see IDatabase::select()
+ * @return string SQL query string
*/
public function selectSQLText(
$table, $vars, $conds = '', $fname = __METHOD__,
* Takes the same arguments as IDatabase::select().
*
* @param string $table Table name
- * @param string $vars Unused
+ * @param string $var Column for which NULL values are not counted [default "*"]
* @param array|string $conds Filters on the table
* @param string $fname Function name for profiling
* @param array $options Options for select
* @throws DBError
*/
public function estimateRowCount(
- $table, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
+ $table, $var = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
);
/**
* @since 1.27 Added $join_conds parameter
*
* @param array|string $tables Table names
- * @param string $vars Unused
+ * @param string $var Column for which NULL values are not counted [default "*"]
* @param array|string $conds Filters on the table
* @param string $fname Function name for profiling
* @param array $options Options for select
* @throws DBError
*/
public function selectRowCount(
- $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
+ $tables, $var = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
);
/**
*/
public function tableExists( $table, $fname = __METHOD__ );
- /**
- * Determines if a given index is unique
- *
- * @param string $table
- * @param string $index
- *
- * @return bool
- */
- public function indexUnique( $table, $index );
-
/**
* INSERT wrapper, inserts an array into a table.
*
*/
public function buildIntegerCast( $field );
+ /**
+ * Equivalent to IDatabase::selectSQLText() except wraps the result in Subqyery
+ *
+ * @see IDatabase::selectSQLText()
+ *
+ * @param string|array $table Table name
+ * @param string|array $vars Field names
+ * @param string|array $conds Conditions
+ * @param string $fname Caller function name
+ * @param string|array $options Query options
+ * @param string|array $join_conds Join conditions
+ * @return Subquery
+ * @since 1.31
+ */
+ public function buildSelectSubquery(
+ $table, $vars, $conds = '', $fname = __METHOD__,
+ $options = [], $join_conds = []
+ );
+
/**
* Returns true if DBs are assumed to be on potentially different servers
*
/**
* Determines if the last failure was due to a deadlock
*
+ * Note that during a deadlock, the prior transaction will have been lost
+ *
* @return bool
*/
public function wasDeadlock();
/**
* Determines if the last failure was due to a lock timeout
*
+ * Note that during a lock wait timeout, the prior transaction will have been lost
+ *
* @return bool
*/
public function wasLockTimeout();
/**
- * Determines if the last query error was due to a dropped connection and should
- * be dealt with by pinging the connection and reissuing the query.
+ * Determines if the last query error was due to a dropped connection
+ *
+ * Note that during a connection loss, the prior transaction will have been lost
*
* @return bool
+ * @since 1.31
*/
- public function wasErrorReissuable();
+ public function wasConnectionLoss();
/**
* Determines if the last failure was due to the database being read-only.
*/
public function wasReadOnlyError();
+ /**
+ * Determines if the last query error was due to something outside of the query itself
+ *
+ * Note that the transaction may have been lost, discarding prior writes and results
+ *
+ * @return bool
+ */
+ public function wasErrorReissuable();
+
/**
* Wait for the replica DB to catch up to a given master position
*
/**
* Run a callback as soon as there is no transaction pending.
* If there is a transaction and it is rolled back, then the callback is cancelled.
+ *
+ * When transaction round mode (DBO_TRX) is set, the callback will run at the end
+ * of the round, just after all peer transactions COMMIT. If the transaction round
+ * is rolled back, then the callback is cancelled.
+ *
* Queries in the function will run in AUTO-COMMIT mode unless there are begin() calls.
* Callbacks must commit any transactions that they begin.
*
* This is useful for updates to different systems or when separate transactions are needed.
* For example, one might want to enqueue jobs into a system outside the database, but only
* after the database is updated so that the jobs will see the data when they actually run.
- * It can also be used for updates that easily cause deadlocks if locks are held too long.
+ * It can also be used for updates that easily suffer from lock timeouts and deadlocks,
+ * but where atomicity is not essential.
*
* Updates will execute in the order they were enqueued.
*
/**
* Run a callback before the current transaction commits or now if there is none.
* If there is a transaction and it is rolled back, then the callback is cancelled.
+ *
+ * When transaction round mode (DBO_TRX) is set, the callback will run at the end
+ * of the round, just before all peer transactions COMMIT. If the transaction round
+ * is rolled back, then the callback is cancelled.
+ *
* Callbacks must not start nor commit any transactions. If no transaction is active,
* then a transaction will wrap the callback.
*
- * This is useful for updates that easily cause deadlocks if locks are held too long
+ * This is useful for updates that easily suffer from lock timeouts and deadlocks,
* but where atomicity is strongly desired for these updates and some related updates.
*
* Updates will execute in the order they were enqueued.
/**
* Begin an atomic section of statements
*
- * If a transaction has been started already, just keep track of the given
- * section name to make sure the transaction is not committed pre-maturely.
- * This function can be used in layers (with sub-sections), so use a stack
- * to keep track of the different atomic sections. If there is no transaction,
- * start one implicitly.
+ * If a transaction has been started already, (optionally) sets a savepoint
+ * and tracks the given section name to make sure the transaction is not
+ * committed pre-maturely. This function can be used in layers (with
+ * sub-sections), so use a stack to keep track of the different atomic
+ * sections. If there is no transaction, one is started implicitly.
*
* The goal of this function is to create an atomic section of SQL queries
* without having to start a new transaction if it already exists.
*
- * All atomic levels *must* be explicitly closed using IDatabase::endAtomic(),
- * and any database transactions cannot be began or committed until all atomic
- * levels are closed. There is no such thing as implicitly opening or closing
- * an atomic section.
+ * All atomic levels *must* be explicitly closed using IDatabase::endAtomic()
+ * or IDatabase::cancelAtomic(), and any database transactions cannot be
+ * began or committed until all atomic levels are closed. There is no such
+ * thing as implicitly opening or closing an atomic section.
*
* @since 1.23
* @param string $fname
+ * @param string $cancelable Pass self::ATOMIC_CANCELABLE to use a
+ * savepoint and enable self::cancelAtomic() for this section.
+ * @return AtomicSectionIdentifier section ID token
* @throws DBError
*/
- public function startAtomic( $fname = __METHOD__ );
+ public function startAtomic( $fname = __METHOD__, $cancelable = self::ATOMIC_NOT_CANCELABLE );
/**
* Ends an atomic section of SQL statements
*/
public function endAtomic( $fname = __METHOD__ );
+ /**
+ * Cancel an atomic section of SQL statements
+ *
+ * This will roll back only the statements executed since the start of the
+ * most recent atomic section, and close that section. If a transaction was
+ * open before the corresponding startAtomic() call, any statements before
+ * that call are *not* rolled back and the transaction remains open. If the
+ * corresponding startAtomic() implicitly started a transaction, that
+ * transaction is rolled back.
+ *
+ * Note that a call to IDatabase::rollback() will also roll back any open
+ * atomic sections.
+ *
+ * @note As a micro-optimization to save a few DB calls, this method may only
+ * be called when startAtomic() was called with the ATOMIC_CANCELABLE flag.
+ * @since 1.31
+ * @see IDatabase::startAtomic
+ * @param string $fname
+ * @param AtomicSectionIdentifier $sectionId Section ID from startAtomic();
+ * passing this enables cancellation of unclosed nested sections [optional]
+ * @throws DBError
+ */
+ public function cancelAtomic( $fname = __METHOD__, AtomicSectionIdentifier $sectionId = null );
+
/**
* Run a callback to do an atomic set of updates for this database
*
* - This database object
* - The value of $fname
*
- * If any exception occurs in the callback, then rollback() will be called and the error will
- * be re-thrown. It may also be that the rollback itself fails with an exception before then.
- * In any case, such errors are expected to terminate the request, without any outside caller
- * attempting to catch errors and commit anyway. Note that any rollback undoes all prior
- * atomic section and uncommitted updates, which trashes the current request, requiring an
- * error to be displayed.
+ * If any exception occurs in the callback, then cancelAtomic() will be
+ * called to back out any statements executed by the callback and the error
+ * will be re-thrown. It may also be that the cancel itself fails with an
+ * exception before then. In any case, such errors are expected to
+ * terminate the request, without any outside caller attempting to catch
+ * errors and commit anyway.
*
- * This can be an alternative to explicit startAtomic()/endAtomic() calls.
+ * This can be an alternative to explicit startAtomic()/endAtomic()/cancelAtomic() calls.
*
* @see Database::startAtomic
* @see Database::endAtomic
+ * @see Database::cancelAtomic
*
* @param string $fname Caller name (usually __METHOD__)
* @param callable $callback Callback that issues DB updates
+ * @param string $cancelable Pass self::ATOMIC_CANCELABLE to use a
+ * savepoint and enable self::cancelAtomic() for this section.
* @return mixed $res Result of the callback (since 1.28)
* @throws DBError
* @throws RuntimeException
* @throws UnexpectedValueException
- * @since 1.27
+ * @since 1.27; prior to 1.31 this did a rollback() instead of
+ * cancelAtomic(), and assumed no callers up the stack would ever try to
+ * catch the exception.
*/
- public function doAtomicSection( $fname, callable $callback );
+ public function doAtomicSection(
+ $fname, callable $callback, $cancelable = self::ATOMIC_NOT_CANCELABLE
+ );
/**
* Begin a transaction. If a transaction is already in progress,
*/
public function flushSnapshot( $fname = __METHOD__ );
- /**
- * List all tables on the database
- *
- * @param string $prefix Only show tables with this prefix, e.g. mw_
- * @param string $fname Calling function name
- * @throws DBError
- * @return array
- */
- public function listTables( $prefix = null, $fname = __METHOD__ );
-
/**
* Convert a timestamp in one of the formats accepted by wfTimestamp()
* to the format used for inserting into timestamp fields in this DBMS.
public function ping( &$rtt = null );
/**
- * Get replica DB lag. Currently supported only by MySQL.
+ * Get the amount of replication lag for this database server
*
- * Note that this function will generate a fatal error on many
- * installations. Most callers should use LoadBalancer::safeGetLag()
- * instead.
+ * Callers should avoid using this method while a transaction is active
*
* @return int|bool Database replication lag in seconds or false on error
* @throws DBError