protected $mTablePrefix = '';
/** @var string */
protected $mSchema = '';
- /** @var integer */
+ /** @var int */
protected $mFlags;
/** @var array */
protected $mLBInfo = [];
*/
private $mTrxWriteDuration = 0.0;
/**
- * @var integer Number of write queries for the current transaction
+ * @var int Number of write queries for the current transaction
*/
private $mTrxWriteQueryCount = 0;
/**
- * @var integer Number of rows affected by write queries for the current transaction
+ * @var int Number of rows affected by write queries for the current transaction
*/
private $mTrxWriteAffectedRows = 0;
/**
*/
private $mTrxWriteAdjDuration = 0.0;
/**
- * @var integer Number of write queries counted in mTrxWriteAdjDuration
+ * @var int Number of write queries counted in mTrxWriteAdjDuration
*/
private $mTrxWriteAdjQueryCount = 0;
/**
$closed = $this->closeConnection();
$this->mConn = false;
- } elseif ( $this->mTrxIdleCallbacks || $this->mTrxEndCallbacks ) { // sanity
+ } elseif (
+ $this->mTrxIdleCallbacks ||
+ $this->mTrxPreCommitCallbacks ||
+ $this->mTrxEndCallbacks
+ ) { // sanity
throw new RuntimeException( "Transaction callbacks still pending." );
} else {
$closed = true;
}
/**
- * @param $sql
+ * @param string $sql
* @return string|null
*/
protected function getQueryVerb( $sql ) {
*
* @param string $sql A SQL write query
* @param float $runtime Total runtime, including RTT
- * @param integer $affected Affected row count
+ * @param int $affected Affected row count
*/
private function updateTrxWriteQueryTime( $sql, $runtime, $affected ) {
// Whether this is indicative of replica DB runtime (except for RBR or ws_repl)
* Quotes an identifier using `backticks` or "double quotes" depending on the database type.
* MySQL uses `backticks` while basically everything else uses double quotes.
* Since MySQL is the odd one out here the double quotes are our generic
- * and we implement backticks in DatabaseMysql.
+ * and we implement backticks in DatabaseMysqlBase.
*
* @param string $s
* @return string
return '(' . implode( $glue, $sqls ) . ')';
}
+ public function unionConditionPermutations(
+ $table, $vars, array $permute_conds, $extra_conds = '', $fname = __METHOD__,
+ $options = [], $join_conds = []
+ ) {
+ // First, build the Cartesian product of $permute_conds
+ $conds = [ [] ];
+ foreach ( $permute_conds as $field => $values ) {
+ if ( !$values ) {
+ // Skip empty $values
+ continue;
+ }
+ $values = array_unique( $values ); // For sanity
+ $newConds = [];
+ foreach ( $conds as $cond ) {
+ foreach ( $values as $value ) {
+ $cond[$field] = $value;
+ $newConds[] = $cond; // Arrays are by-value, not by-reference, so this works
+ }
+ }
+ $conds = $newConds;
+ }
+
+ $extra_conds = $extra_conds === '' ? [] : (array)$extra_conds;
+
+ // If there's just one condition and no subordering, hand off to
+ // selectSQLText directly.
+ if ( count( $conds ) === 1 &&
+ ( !isset( $options['INNER ORDER BY'] ) || !$this->unionSupportsOrderAndLimit() )
+ ) {
+ return $this->selectSQLText(
+ $table, $vars, $conds[0] + $extra_conds, $fname, $options, $join_conds
+ );
+ }
+
+ // Otherwise, we need to pull out the order and limit to apply after
+ // the union. Then build the SQL queries for each set of conditions in
+ // $conds. Then union them together (using UNION ALL, because the
+ // product *should* already be distinct).
+ $orderBy = $this->makeOrderBy( $options );
+ $limit = isset( $options['LIMIT'] ) ? $options['LIMIT'] : null;
+ $offset = isset( $options['OFFSET'] ) ? $options['OFFSET'] : false;
+ $all = empty( $options['NOTALL'] ) && !in_array( 'NOTALL', $options );
+ if ( !$this->unionSupportsOrderAndLimit() ) {
+ unset( $options['ORDER BY'], $options['LIMIT'], $options['OFFSET'] );
+ } else {
+ if ( array_key_exists( 'INNER ORDER BY', $options ) ) {
+ $options['ORDER BY'] = $options['INNER ORDER BY'];
+ }
+ if ( $limit !== null && is_numeric( $offset ) && $offset != 0 ) {
+ // We need to increase the limit by the offset rather than
+ // using the offset directly, otherwise it'll skip incorrectly
+ // in the subqueries.
+ $options['LIMIT'] = $limit + $offset;
+ unset( $options['OFFSET'] );
+ }
+ }
+
+ $sqls = [];
+ foreach ( $conds as $cond ) {
+ $sqls[] = $this->selectSQLText(
+ $table, $vars, $cond + $extra_conds, $fname, $options, $join_conds
+ );
+ }
+ $sql = $this->unionQueries( $sqls, $all ) . $orderBy;
+ if ( $limit !== null ) {
+ $sql = $this->limitResult( $sql, $limit, $offset );
+ }
+
+ return $sql;
+ }
+
public function conditional( $cond, $trueVal, $falseVal ) {
if ( is_array( $cond ) ) {
$cond = $this->makeList( $cond, self::LIST_AND );
/**
* Do not use this method outside of Database/DBError classes
*
- * @param integer|string $errno
+ * @param int|string $errno
* @return bool Whether the given query error was a connection drop
*/
public function wasConnectionError( $errno ) {
}
final public function onTransactionPreCommitOrIdle( callable $callback, $fname = __METHOD__ ) {
- if ( $this->mTrxLevel ) {
+ if ( $this->mTrxLevel || $this->getFlag( self::DBO_TRX ) ) {
+ // As long as DBO_TRX is set, writes will accumulate until the load balancer issues
+ // an implicit commit of all peer databases. This is true even if a transaction has
+ // not yet been triggered by writes; make sure $callback runs *after* any such writes.
$this->mTrxPreCommitCallbacks[] = [ $callback, $fname ];
} else {
- // If no transaction is active, then make one for this callback
+ // No transaction is active nor will start implicitly, so make one for this callback
$this->startAtomic( __METHOD__ );
try {
call_user_func( $callback );
*
* This method should not be used outside of Database/LoadBalancer
*
- * @param integer $trigger IDatabase::TRIGGER_* constant
+ * @param int $trigger IDatabase::TRIGGER_* constant
* @since 1.20
* @throws Exception
*/
*
* This method should not be used outside of Database/LoadBalancer
*
- * @param integer $trigger IDatabase::TRIGGER_* constant
+ * @param int $trigger IDatabase::TRIGGER_* constant
* @throws Exception
* @since 1.20
*/
* @see WANObjectCache::getWithSetCallback()
*
* @param IDatabase $db1
- * @param IDatabase ...
+ * @param IDatabase $dbs,...
* @return array Map of values:
* - lag: highest lag of any of the DBs or false on error (e.g. replication stopped)
* - since: oldest UNIX timestamp of any of the DB lag estimates