X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=includes%2Flibs%2Frdbms%2Fdatabase%2FDatabase.php;h=014c4af399f433fa44b7e125296e04a48f9ddc71;hp=ddc8df5c14dac315941f7c454f88a6344fc5a76e;hb=5e53629c3c8fd9f0fa476df583e66b69e81ffe88;hpb=a4a25aeb268276eee0719f9761cad6cce3938989 diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php index ddc8df5c14..014c4af399 100644 --- a/includes/libs/rdbms/database/Database.php +++ b/includes/libs/rdbms/database/Database.php @@ -59,6 +59,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware const SLOW_WRITE_SEC = 0.500; const SMALL_WRITE_ROWS = 100; + /** @var string Whether lock granularity is on the level of the entire database */ + const ATTR_DB_LEVEL_LOCKING = 'db-level-locking'; + /** @var string SQL query */ protected $lastQuery = ''; /** @var float|bool UNIX timestamp of last write query */ @@ -385,6 +388,21 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware return $conn; } + /** + * @param string $dbType A possible DB type (sqlite, mysql, postgres,...) + * @param string|null $driver Optional name of a specific DB client driver + * @return array Map of (Database::ATTRIBUTE_* constant => value) for all such constants + * @throws InvalidArgumentException + * @since 1.31 + */ + final public static function attributesFromType( $dbType, $driver = null ) { + static $defaults = [ self::ATTR_DB_LEVEL_LOCKING => false ]; + + $class = self::getClass( $dbType, $driver ); + + return call_user_func( [ $class, 'getAttributes' ] ) + $defaults; + } + /** * @param string $dbType A possible DB type (sqlite, mysql, postgres,...) * @param string|null $driver Optional name of a specific DB client driver @@ -441,6 +459,14 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware return $class; } + /** + * @return array Map of (Database::ATTRIBUTE_* constant => value + * @since 1.31 + */ + protected static function getAttributes() { + return []; + } + /** * Set the PSR-3 logger interface to use for query logging. (The logger * interfaces for connection logging and error logging can be set with the @@ -989,12 +1015,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $this->queryLogger->warning( $msg, $params + [ 'trace' => ( new RuntimeException() )->getTraceAsString() ] ); - if ( !$recoverable ) { - # Callers may catch the exception and continue to use the DB - $this->reportQueryError( $lastError, $lastErrno, $sql, $fname ); - } else { + if ( $recoverable ) { # Should be safe to silently retry the query $ret = $this->doProfiledQuery( $sql, $commentedSql, $isNonTempWrite, $fname ); + } else { + # Callers may catch the exception and continue to use the DB + $this->reportQueryError( $lastError, $lastErrno, $sql, $fname ); } } else { $msg = __METHOD__ . ': lost connection to {dbserver} permanently'; @@ -1155,19 +1181,29 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware */ private function handleSessionLoss() { $this->trxLevel = 0; - $this->trxIdleCallbacks = []; // T67263 - $this->trxPreCommitCallbacks = []; // T67263 + $this->trxIdleCallbacks = []; // T67263; transaction already lost + $this->trxPreCommitCallbacks = []; // T67263; transaction already lost $this->sessionTempTables = []; $this->namedLocksHeld = []; + + // Note: if callback suppression is set then some *Callbacks arrays are not cleared here + $e = null; try { // Handle callbacks in trxEndCallbacks $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK ); + } catch ( Exception $ex ) { + // Already logged; move on... + $e = $e ?: $ex; + } + try { + // Handle callbacks in trxRecurringCallbacks $this->runTransactionListenerCallbacks( self::TRIGGER_ROLLBACK ); - return null; - } catch ( Exception $e ) { + } catch ( Exception $ex ) { // Already logged; move on... - return $e; + $e = $e ?: $ex; } + + return $e; } /** @@ -1828,10 +1864,48 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')'; } + public function buildSubstring( $input, $startPosition, $length = null ) { + $this->assertBuildSubstringParams( $startPosition, $length ); + $functionBody = "$input FROM $startPosition"; + if ( $length !== null ) { + $functionBody .= " FOR $length"; + } + return 'SUBSTRING(' . $functionBody . ')'; + } + + /** + * Check type and bounds for parameters to self::buildSubstring() + * + * All supported databases have substring functions that behave the same for + * positive $startPosition and non-negative $length, but behaviors differ when + * given 0 or negative $startPosition or negative $length. The simplest + * solution to that is to just forbid those values. + * + * @param int $startPosition + * @param int|null $length + * @since 1.31 + */ + protected function assertBuildSubstringParams( $startPosition, $length ) { + if ( !is_int( $startPosition ) || $startPosition <= 0 ) { + throw new InvalidArgumentException( + '$startPosition must be a positive integer' + ); + } + if ( !( is_int( $length ) && $length >= 0 || $length === null ) ) { + throw new InvalidArgumentException( + '$length must be null or an integer greater than or equal to 0' + ); + } + } + public function buildStringCast( $field ) { return $field; } + public function buildIntegerCast( $field ) { + return 'CAST( ' . $field . ' AS INTEGER )'; + } + public function databasesAreIndependent() { return false; } @@ -2132,8 +2206,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } // We can't separate explicit JOIN clauses with ',', use ' ' for those - $implicitJoins = !empty( $ret ) ? implode( ',', $ret ) : ""; - $explicitJoins = !empty( $retJOIN ) ? implode( ' ', $retJOIN ) : ""; + $implicitJoins = $ret ? implode( ',', $ret ) : ""; + $explicitJoins = $retJOIN ? implode( ' ', $retJOIN ) : ""; // Compile our final table clause return implode( ' ', [ $implicitJoins, $explicitJoins ] );