Merge "rdbms: add IDatabase::lockForUpdate() convenience method"
[lhc/web/wiklou.git] / includes / libs / rdbms / database / Database.php
index 1a68a93..d11b51b 100644 (file)
@@ -315,9 +315,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
                $this->sessionVars = $params['variables'];
 
-               $this->srvCache = isset( $params['srvCache'] )
-                       ? $params['srvCache']
-                       : new HashBagOStuff();
+               $this->srvCache = $params['srvCache'] ?? new HashBagOStuff();
 
                $this->profiler = $params['profiler'];
                $this->trxProfiler = $params['trxProfiler'];
@@ -420,29 +418,27 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @since 1.18
         */
        final public static function factory( $dbType, $p = [], $connect = self::NEW_CONNECTED ) {
-               $class = self::getClass( $dbType, isset( $p['driver'] ) ? $p['driver'] : null );
+               $class = self::getClass( $dbType, $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'] : '';
+                       $p['host'] = $p['host'] ?? false;
+                       $p['user'] = $p['user'] ?? false;
+                       $p['password'] = $p['password'] ?? false;
+                       $p['dbname'] = $p['dbname'] ?? false;
+                       $p['flags'] = $p['flags'] ?? 0;
+                       $p['variables'] = $p['variables'] ?? [];
+                       $p['tablePrefix'] = $p['tablePrefix'] ?? '';
+                       $p['schema'] = $p['schema'] ?? '';
+                       $p['cliMode'] = $p['cliMode'] ?? ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
+                       $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;
+                       $p['profiler'] = $p['profiler'] ?? null;
                        if ( !isset( $p['trxProfiler'] ) ) {
                                $p['trxProfiler'] = new TransactionProfiler();
                        }
@@ -1169,27 +1165,21 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
                if ( $ret === false ) {
                        if ( $this->trxLevel ) {
-                               if ( !$this->wasKnownStatementRollbackError() ) {
-                                       # Either the query was aborted or all queries after BEGIN where aborted.
-                                       if ( $this->explicitTrxActive() || $priorWritesPending ) {
-                                               # In the first case, the only options going forward are (a) ROLLBACK, or
-                                               # (b) ROLLBACK TO SAVEPOINT (if one was set). If the later case, the only
-                                               # option is ROLLBACK, since the snapshots would have been released.
-                                               $this->trxStatus = self::STATUS_TRX_ERROR;
-                                               $this->trxStatusCause =
-                                                       $this->makeQueryException( $lastError, $lastErrno, $sql, $fname );
-                                               $tempIgnore = false; // cannot recover
-                                       } else {
-                                               # Nothing prior was there to lose from the transaction,
-                                               # so just roll it back.
-                                               $this->rollback( __METHOD__ . " ($fname)", self::FLUSHING_INTERNAL );
-                                       }
-                                       $this->trxStatusIgnoredCause = null;
-                               } else {
+                               if ( $this->wasKnownStatementRollbackError() ) {
                                        # We're ignoring an error that caused just the current query to be aborted.
-                                       # But log the cause so we can log a deprecation notice if a
-                                       # caller actually does ignore it.
+                                       # But log the cause so we can log a deprecation notice if a caller actually
+                                       # does ignore it.
                                        $this->trxStatusIgnoredCause = [ $lastError, $lastErrno, $fname ];
+                               } else {
+                                       # Either the query was aborted or all queries after BEGIN where aborted.
+                                       # In the first case, the only options going forward are (a) ROLLBACK, or
+                                       # (b) ROLLBACK TO SAVEPOINT (if one was set). If the later case, the only
+                                       # option is ROLLBACK, since the snapshots would have been released.
+                                       $this->trxStatus = self::STATUS_TRX_ERROR;
+                                       $this->trxStatusCause =
+                                               $this->makeQueryException( $lastError, $lastErrno, $sql, $fname );
+                                       $tempIgnore = false; // cannot recover
+                                       $this->trxStatusIgnoredCause = null;
                                }
                        }
 
@@ -1224,13 +1214,13 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
                $startTime = microtime( true );
                if ( $this->profiler ) {
-                       call_user_func( [ $this->profiler, 'profileIn' ], $queryProf );
+                       $this->profiler->profileIn( $queryProf );
                }
                $this->affectedRowCount = null;
                $ret = $this->doQuery( $commentedSql );
                $this->affectedRowCount = $this->affectedRows();
                if ( $this->profiler ) {
-                       call_user_func( [ $this->profiler, 'profileOut' ], $queryProf );
+                       $this->profiler->profileOut( $queryProf );
                }
                $queryRuntime = max( microtime( true ) - $startTime, 0.0 );
 
@@ -1722,7 +1712,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
                if ( isset( $options['LIMIT'] ) ) {
                        $sql = $this->limitResult( $sql, $options['LIMIT'],
-                               isset( $options['OFFSET'] ) ? $options['OFFSET'] : false );
+                               $options['OFFSET'] ?? false );
                }
                $sql = "$sql $postLimitTail";
 
@@ -1875,7 +1865,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        if ( !$var ) {
                                $column = null;
                        } elseif ( count( $var ) == 1 ) {
-                               $column = isset( $var[0] ) ? $var[0] : reset( $var );
+                               $column = $var[0] ?? reset( $var );
                        } else {
                                throw new DBUnexpectedError( $this, __METHOD__ . ': got multiple columns.' );
                        }
@@ -1886,6 +1876,22 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                return $column;
        }
 
+       public function lockForUpdate(
+               $table, $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
+       ) {
+               if ( !$this->trxLevel && !$this->getFlag( self::DBO_TRX ) ) {
+                       throw new DBUnexpectedError(
+                               $this,
+                               __METHOD__ . ': no transaction is active nor is DBO_TRX set'
+                       );
+               }
+
+               $options = (array)$options;
+               $options[] = 'FOR UPDATE';
+
+               return $this->selectRowCount( $table, '*', $conds, $fname, $options, $join_conds );
+       }
+
        /**
         * Removes most variables from an SQL query and replaces them with X or N for numbers.
         * It's only slightly flawed. Don't use for anything important.
@@ -1935,18 +1941,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
        }
 
-       public function tableExists( $table, $fname = __METHOD__ ) {
-               $tableRaw = $this->tableName( $table, 'raw' );
-               if ( isset( $this->sessionTempTables[$tableRaw] ) ) {
-                       return true; // already known to exist
-               }
-
-               $table = $this->tableName( $table );
-               $ignoreErrors = true;
-               $res = $this->query( "SELECT 1 FROM $table LIMIT 1", $fname, $ignoreErrors );
-
-               return (bool)$res;
-       }
+       abstract public function tableExists( $table, $fname = __METHOD__ );
 
        public function indexUnique( $table, $index ) {
                $indexInfo = $this->indexInfo( $table, $index );
@@ -2583,9 +2578,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @return string
         */
        protected function indexName( $index ) {
-               return isset( $this->indexAliases[$index] )
-                       ? $this->indexAliases[$index]
-                       : $index;
+               return $this->indexAliases[$index] ?? $index;
        }
 
        public function addQuotes( $s ) {
@@ -3138,8 +3131,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                // $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;
+               $limit = $options['LIMIT'] ?? null;
+               $offset = $options['OFFSET'] ?? false;
                $all = empty( $options['NOTALL'] ) && !in_array( 'NOTALL', $options );
                if ( !$this->unionSupportsOrderAndLimit() ) {
                        unset( $options['ORDER BY'], $options['LIMIT'], $options['OFFSET'] );
@@ -3242,7 +3235,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $e = null;
                do {
                        try {
-                               $retVal = call_user_func_array( $function, $args );
+                               $retVal = $function( ...$args );
                                break;
                        } catch ( DBQueryError $e ) {
                                if ( $this->wasDeadlock() ) {
@@ -3322,7 +3315,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        // No transaction is active nor will start implicitly, so make one for this callback
                        $this->startAtomic( __METHOD__, self::ATOMIC_CANCELABLE );
                        try {
-                               call_user_func( $callback, $this );
+                               $callback( $this );
                                $this->endAtomic( __METHOD__ );
                        } catch ( Exception $e ) {
                                $this->cancelAtomic( __METHOD__ );
@@ -3448,16 +3441,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        $this->trxIdleCallbacks = []; // consumed (and recursion guard)
                        $this->trxEndCallbacks = []; // consumed (recursion guard)
                        foreach ( $callbacks as $callback ) {
+                               ++$count;
+                               list( $phpCallback ) = $callback;
+                               $this->clearFlag( self::DBO_TRX ); // make each query its own transaction
                                try {
-                                       ++$count;
-                                       list( $phpCallback ) = $callback;
-                                       $this->clearFlag( self::DBO_TRX ); // make each query its own transaction
                                        call_user_func( $phpCallback, $trigger, $this );
-                                       if ( $autoTrx ) {
-                                               $this->setFlag( self::DBO_TRX ); // restore automatic begin()
-                                       } else {
-                                               $this->clearFlag( self::DBO_TRX ); // restore auto-commit
-                                       }
                                } catch ( Exception $ex ) {
                                        call_user_func( $this->errorLogger, $ex );
                                        $e = $e ?: $ex;
@@ -3466,6 +3454,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                        if ( $this->trxLevel() ) {
                                                $this->rollback( __METHOD__, self::FLUSHING_INTERNAL );
                                        }
+                               } finally {
+                                       if ( $autoTrx ) {
+                                               $this->setFlag( self::DBO_TRX ); // restore automatic begin()
+                                       } else {
+                                               $this->clearFlag( self::DBO_TRX ); // restore auto-commit
+                                       }
                                }
                        }
                } while ( count( $this->trxIdleCallbacks ) );
@@ -3497,9 +3491,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                try {
                                        ++$count;
                                        list( $phpCallback ) = $callback;
-                                       call_user_func( $phpCallback, $this );
+                                       $phpCallback( $this );
                                } catch ( Exception $ex ) {
-                                       call_user_func( $this->errorLogger, $ex );
+                                       $this->errorLogger( $ex );
                                        $e = $e ?: $ex;
                                }
                        }
@@ -3533,7 +3527,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        try {
                                $phpCallback( $trigger, $this );
                        } catch ( Exception $ex ) {
-                               call_user_func( $this->errorLogger, $ex );
+                               ( $this->errorLogger )( $ex );
                                $e = $e ?: $ex;
                        }
                }
@@ -3734,7 +3728,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        ) {
                $sectionId = $this->startAtomic( $fname, $cancelable );
                try {
-                       $res = call_user_func_array( $callback, [ $this, $fname ] );
+                       $res = $callback( $this, $fname );
                } catch ( Exception $e ) {
                        $this->cancelAtomic( $fname, $sectionId );
 
@@ -4135,7 +4129,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @see WANObjectCache::getWithSetCallback()
         *
         * @param IDatabase $db1
-        * @param IDatabase $db2 [optional]
+        * @param IDatabase|null $db2 [optional]
         * @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
@@ -4260,7 +4254,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                $cmd = $this->replaceVars( $cmd );
 
                                if ( $inputCallback ) {
-                                       $callbackResult = call_user_func( $inputCallback, $cmd );
+                                       $callbackResult = $inputCallback( $cmd );
 
                                        if ( is_string( $callbackResult ) || !$callbackResult ) {
                                                $cmd = $callbackResult;
@@ -4271,7 +4265,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                        $res = $this->query( $cmd, $fname );
 
                                        if ( $resultCallback ) {
-                                               call_user_func( $resultCallback, $res, $this );
+                                               $resultCallback( $res, $this );
                                        }
 
                                        if ( false === $res ) {
@@ -4638,8 +4632,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 }
 
-class_alias( Database::class, 'DatabaseBase' ); // b/c for old name
+/**
+ * @deprecated since 1.28
+ */
+class_alias( Database::class, 'DatabaseBase' );
+
 /**
  * @deprecated since 1.29
  */
-class_alias( Database::class, 'Database' ); // b/c global alias
+class_alias( Database::class, 'Database' );