X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;ds=sidebyside;f=includes%2Flibs%2Frdbms%2Fdatabase%2FDatabase.php;h=f1af074f8dc02addf9403af257d0cabefb90f34a;hb=55ab84e17da1ffa49558584d0902fca55a8ba167;hp=df28f933640412481f426828c33eba7c5585832d;hpb=99b65649c0f9b4ab1156cfc2ee50959d4b8a12c6;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php index df28f93364..f1af074f8d 100644 --- a/includes/libs/rdbms/database/Database.php +++ b/includes/libs/rdbms/database/Database.php @@ -27,6 +27,7 @@ namespace Wikimedia\Rdbms; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use Wikimedia\ScopedCallback; use Wikimedia\Timestamp\ConvertibleTimestamp; use Wikimedia; @@ -306,7 +307,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware * * This also connects to the database immediately upon object construction * - * @param string $dbType A possible DB type (sqlite, mysql, postgres) + * @param string $dbType A possible DB type (sqlite, mysql, postgres,...) * @param array $p Parameter map with keys: * - host : The hostname of the DB server * - user : The name of the database user the client operates under @@ -344,6 +345,53 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware * @since 1.18 */ final public static function factory( $dbType, $p = [] ) { + $class = self::getClass( $dbType, isset( $p['driver'] ) ? $p['driver'] : null ); + + if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) { + // Resolve some defaults for b/c + $p['host'] = isset( $p['host'] ) ? $p['host'] : false; + $p['user'] = isset( $p['user'] ) ? $p['user'] : false; + $p['password'] = isset( $p['password'] ) ? $p['password'] : false; + $p['dbname'] = isset( $p['dbname'] ) ? $p['dbname'] : false; + $p['flags'] = isset( $p['flags'] ) ? $p['flags'] : 0; + $p['variables'] = isset( $p['variables'] ) ? $p['variables'] : []; + $p['tablePrefix'] = isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : ''; + $p['schema'] = isset( $p['schema'] ) ? $p['schema'] : ''; + $p['cliMode'] = isset( $p['cliMode'] ) + ? $p['cliMode'] + : ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ); + $p['agent'] = isset( $p['agent'] ) ? $p['agent'] : ''; + if ( !isset( $p['connLogger'] ) ) { + $p['connLogger'] = new NullLogger(); + } + if ( !isset( $p['queryLogger'] ) ) { + $p['queryLogger'] = new NullLogger(); + } + $p['profiler'] = isset( $p['profiler'] ) ? $p['profiler'] : null; + if ( !isset( $p['trxProfiler'] ) ) { + $p['trxProfiler'] = new TransactionProfiler(); + } + if ( !isset( $p['errorLogger'] ) ) { + $p['errorLogger'] = function ( Exception $e ) { + trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING ); + }; + } + + $conn = new $class( $p ); + } else { + $conn = null; + } + + 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 string Database subclass name to use + * @throws InvalidArgumentException + */ + private static function getClass( $dbType, $driver = null ) { // For database types with built-in support, the below maps type to IDatabase // implementations. For types with multipe driver implementations (PHP extensions), // an array can be used, keyed by extension name. In case of an array, the @@ -359,17 +407,18 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $dbType = strtolower( $dbType ); $class = false; + if ( isset( $builtinTypes[$dbType] ) ) { $possibleDrivers = $builtinTypes[$dbType]; if ( is_string( $possibleDrivers ) ) { $class = $possibleDrivers; } else { - if ( !empty( $p['driver'] ) ) { - if ( !isset( $possibleDrivers[$p['driver']] ) ) { + if ( (string)$driver !== '' ) { + if ( !isset( $possibleDrivers[$driver] ) ) { throw new InvalidArgumentException( __METHOD__ . - " type '$dbType' does not support driver '{$p['driver']}'" ); + " type '$dbType' does not support driver '{$driver}'" ); } else { - $class = $possibleDrivers[$p['driver']]; + $class = $possibleDrivers[$driver]; } } else { foreach ( $possibleDrivers as $posDriver => $possibleClass ) { @@ -389,42 +438,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware " no viable database extension found for type '$dbType'" ); } - if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) { - // Resolve some defaults for b/c - $p['host'] = isset( $p['host'] ) ? $p['host'] : false; - $p['user'] = isset( $p['user'] ) ? $p['user'] : false; - $p['password'] = isset( $p['password'] ) ? $p['password'] : false; - $p['dbname'] = isset( $p['dbname'] ) ? $p['dbname'] : false; - $p['flags'] = isset( $p['flags'] ) ? $p['flags'] : 0; - $p['variables'] = isset( $p['variables'] ) ? $p['variables'] : []; - $p['tablePrefix'] = isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : ''; - $p['schema'] = isset( $p['schema'] ) ? $p['schema'] : ''; - $p['cliMode'] = isset( $p['cliMode'] ) - ? $p['cliMode'] - : ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ); - $p['agent'] = isset( $p['agent'] ) ? $p['agent'] : ''; - if ( !isset( $p['connLogger'] ) ) { - $p['connLogger'] = new \Psr\Log\NullLogger(); - } - if ( !isset( $p['queryLogger'] ) ) { - $p['queryLogger'] = new \Psr\Log\NullLogger(); - } - $p['profiler'] = isset( $p['profiler'] ) ? $p['profiler'] : null; - if ( !isset( $p['trxProfiler'] ) ) { - $p['trxProfiler'] = new TransactionProfiler(); - } - if ( !isset( $p['errorLogger'] ) ) { - $p['errorLogger'] = function ( Exception $e ) { - trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING ); - }; - } - - $conn = new $class( $p ); - } else { - $conn = null; - } - - return $conn; + return $class; } /** @@ -2105,8 +2119,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 ] ); @@ -2252,11 +2266,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $rows = [ $rows ]; } - $useTrx = !$this->trxLevel; - if ( $useTrx ) { - $this->begin( $fname, self::TRANSACTION_INTERNAL ); - } try { + $this->startAtomic( $fname ); $affectedRowCount = 0; foreach ( $rows as $row ) { // Delete rows which collide with this one @@ -2289,17 +2300,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $this->insert( $table, $row, $fname ); $affectedRowCount += $this->affectedRows(); } + $this->endAtomic( $fname ); + $this->affectedRowCount = $affectedRowCount; } catch ( Exception $e ) { - if ( $useTrx ) { - $this->rollback( $fname, self::FLUSHING_INTERNAL ); - } + $this->rollback( $fname, self::FLUSHING_INTERNAL ); throw $e; } - if ( $useTrx ) { - $this->commit( $fname, self::FLUSHING_INTERNAL ); - } - - $this->affectedRowCount = $affectedRowCount; } /** @@ -2365,11 +2371,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } $affectedRowCount = 0; - $useTrx = !$this->trxLevel; - if ( $useTrx ) { - $this->begin( $fname, self::TRANSACTION_INTERNAL ); - } try { + $this->startAtomic( $fname ); # Update any existing conflicting row(s) if ( $where !== false ) { $ok = $this->update( $table, $set, $where, $fname ); @@ -2380,16 +2383,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware # Now insert any non-conflicting row(s) $ok = $this->insert( $table, $rows, $fname, [ 'IGNORE' ] ) && $ok; $affectedRowCount += $this->affectedRows(); + $this->endAtomic( $fname ); + $this->affectedRowCount = $affectedRowCount; } catch ( Exception $e ) { - if ( $useTrx ) { - $this->rollback( $fname, self::FLUSHING_INTERNAL ); - } + $this->rollback( $fname, self::FLUSHING_INTERNAL ); throw $e; } - if ( $useTrx ) { - $this->commit( $fname, self::FLUSHING_INTERNAL ); - } - $this->affectedRowCount = $affectedRowCount; return $ok; } @@ -2447,11 +2446,16 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware return $this->query( $sql, $fname ); } - public function insertSelect( + final public function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__, $insertOptions = [], $selectOptions = [], $selectJoinConds = [] ) { - if ( $this->cliMode ) { + static $hints = [ 'NO_AUTO_COLUMNS' ]; + + $insertOptions = (array)$insertOptions; + $selectOptions = (array)$selectOptions; + + if ( $this->cliMode && $this->isInsertSelectSafe( $insertOptions, $selectOptions ) ) { // For massive migrations with downtime, we don't want to select everything // into memory and OOM, so do all this native on the server side if possible. return $this->nativeInsertSelect( @@ -2460,7 +2464,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $varMap, $conds, $fname, - $insertOptions, + array_diff( $insertOptions, $hints ), $selectOptions, $selectJoinConds ); @@ -2472,12 +2476,22 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $varMap, $conds, $fname, - $insertOptions, + array_diff( $insertOptions, $hints ), $selectOptions, $selectJoinConds ); } + /** + * @param array $insertOptions INSERT options + * @param array $selectOptions SELECT options + * @return bool Whether an INSERT SELECT with these options will be replication safe + * @since 1.31 + */ + protected function isInsertSelectSafe( array $insertOptions, array $selectOptions ) { + return true; + } + /** * Implementation of insertSelect() based on select() and insert() * @@ -2497,8 +2511,6 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $fname = __METHOD__, $insertOptions = [], $selectOptions = [], $selectJoinConds = [] ) { - $insertOptions = array_diff( (array)$insertOptions, [ 'NO_AUTO_COLUMNS' ] ); - // For web requests, do a locking SELECT and then INSERT. This puts the SELECT burden // on only the master (without needing row-based-replication). It also makes it easy to // know how big the INSERT is going to be. @@ -2543,12 +2555,10 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $this->affectedRowCount = $affectedRowCount; } else { $this->rollback( $fname, self::FLUSHING_INTERNAL ); - $this->affectedRowCount = 0; } return $ok; } catch ( Exception $e ) { $this->rollback( $fname, self::FLUSHING_INTERNAL ); - $this->affectedRowCount = 0; throw $e; } } @@ -2577,7 +2587,6 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware if ( !is_array( $insertOptions ) ) { $insertOptions = [ $insertOptions ]; } - $insertOptions = array_diff( (array)$insertOptions, [ 'NO_AUTO_COLUMNS' ] ); $insertOptions = $this->makeInsertOptions( $insertOptions ); @@ -3180,6 +3189,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } catch ( Exception $e ) { // already logged; let LoadBalancer move on during mass-rollback } + + $this->affectedRowCount = 0; // for the sake of consistency } /**