docs/kss/package.json: Update Gerrit /r/p/ link to /r/
[lhc/web/wiklou.git] / includes / api / ApiQueryUserContribs.php
index 6082617..379f1af 100644 (file)
@@ -61,14 +61,11 @@ class ApiQueryUserContribs extends ApiQueryBase {
                $this->fld_patrolled = isset( $prop['patrolled'] );
                $this->fld_tags = isset( $prop['tags'] );
 
-               // Most of this code will use the 'contributions' group DB, which can map to replica DBs
+               // The main query may use the 'contributions' group DB, which can map to replica DBs
                // with extra user based indexes or partioning by user. The additional metadata
                // queries should use a regular replica DB since the lookup pattern is not all by user.
                $dbSecondary = $this->getDB(); // any random replica DB
 
-               // TODO: if the query is going only against the revision table, should this be done?
-               $this->selectNamedDB( 'contributions', DB_REPLICA, 'contributions' );
-
                $sort = ( $this->params['dir'] == 'newer' ? '' : ' DESC' );
                $op = ( $this->params['dir'] == 'older' ? '<' : '>' );
 
@@ -269,6 +266,13 @@ class ApiQueryUserContribs extends ApiQueryBase {
                        $this->orderBy = 'actor';
                }
 
+               // Use the 'contributions' replica, but only if we're querying by user ID (T216656).
+               if ( $this->orderBy === 'id' &&
+                       !( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
+               ) {
+                       $this->selectNamedDB( 'contributions', DB_REPLICA, 'contributions' );
+               }
+
                $count = 0;
                $limit = $this->params['limit'];
                $userIter->rewind();
@@ -327,9 +331,6 @@ class ApiQueryUserContribs extends ApiQueryBase {
                $db = $this->getDB();
 
                $revQuery = MediaWikiServices::getInstance()->getRevisionStore()->getQueryInfo( [ 'page' ] );
-               $this->addTables( $revQuery['tables'] );
-               $this->addJoinConds( $revQuery['joins'] );
-               $this->addFields( $revQuery['fields'] );
 
                if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
                        $revWhere = ActorMigration::newMigration()->getWhere( $db, 'rev_user', $users );
@@ -337,6 +338,24 @@ class ApiQueryUserContribs extends ApiQueryBase {
                        $userField = $this->orderBy === 'actor' ? 'revactor_actor' : 'actor_name';
                        $tsField = 'revactor_timestamp';
                        $idField = 'revactor_rev';
+
+                       // T221511: MySQL/MariaDB (10.1.37) can sometimes irrationally decide that querying `actor`
+                       // before `revision_actor_temp` and filesorting is somehow better than querying $limit+1 rows
+                       // from `revision_actor_temp`. Tell it not to reorder the query (and also reorder it ourselves
+                       // because as generated by RevisionStore it'll have `revision` first rather than
+                       // `revision_actor_temp`). But not when uctag is used, as it seems as likely to be harmed as
+                       // helped in that case, and not when there's only one User because in that case it fetches
+                       // the one `actor` row as a constant and doesn't filesort.
+                       if ( count( $users ) > 1 && !isset( $this->params['tag'] ) ) {
+                               $revQuery['joins']['revision'] = $revQuery['joins']['temp_rev_user'];
+                               unset( $revQuery['joins']['temp_rev_user'] );
+                               $this->addOption( 'STRAIGHT_JOIN' );
+                               // It isn't actually necesssary to reorder $revQuery['tables'] as Database does the right thing
+                               // when join conditions are given for all joins, but GergÅ‘ is wary of relying on that so pull
+                               // `revision_actor_temp` to the start.
+                               $revQuery['tables'] =
+                                       [ 'temp_rev_user' => $revQuery['tables']['temp_rev_user'] ] + $revQuery['tables'];
+                       }
                } else {
                        // If we're dealing with user names (rather than IDs) in read-old mode,
                        // pass false for ActorMigration::getWhere()'s $useId parameter so
@@ -349,6 +368,9 @@ class ApiQueryUserContribs extends ApiQueryBase {
                        $idField = 'rev_id';
                }
 
+               $this->addTables( $revQuery['tables'] );
+               $this->addJoinConds( $revQuery['joins'] );
+               $this->addFields( $revQuery['fields'] );
                $this->addWhere( $revWhere['conds'] );
 
                // Handle continue parameter
@@ -484,7 +506,7 @@ class ApiQueryUserContribs extends ApiQueryBase {
                if ( isset( $this->params['tag'] ) ) {
                        $this->addTables( 'change_tag' );
                        $this->addJoinConds(
-                               [ 'change_tag' => [ 'INNER JOIN', [ $idField . ' = ct_rev_id' ] ] ]
+                               [ 'change_tag' => [ 'JOIN', [ $idField . ' = ct_rev_id' ] ] ]
                        );
                        $changeTagDefStore = MediaWikiServices::getInstance()->getChangeTagDefStore();
                        try {
@@ -519,12 +541,11 @@ class ApiQueryUserContribs extends ApiQueryBase {
                        $anyHidden = true;
                }
                if ( $this->fld_ids ) {
-                       $vals['pageid'] = intval( $row->rev_page );
-                       $vals['revid'] = intval( $row->rev_id );
-                       // $vals['textid'] = intval( $row->rev_text_id ); // todo: Should this field be exposed?
+                       $vals['pageid'] = (int)$row->rev_page;
+                       $vals['revid'] = (int)$row->rev_id;
 
                        if ( !is_null( $row->rev_parent_id ) ) {
-                               $vals['parentid'] = intval( $row->rev_parent_id );
+                               $vals['parentid'] = (int)$row->rev_parent_id;
                        }
                }
 
@@ -573,7 +594,7 @@ class ApiQueryUserContribs extends ApiQueryBase {
                }
 
                if ( $this->fld_size && !is_null( $row->rev_len ) ) {
-                       $vals['size'] = intval( $row->rev_len );
+                       $vals['size'] = (int)$row->rev_len;
                }
 
                if ( $this->fld_sizediff
@@ -581,7 +602,7 @@ class ApiQueryUserContribs extends ApiQueryBase {
                        && !is_null( $row->rev_parent_id )
                ) {
                        $parentLen = $this->parentLens[$row->rev_parent_id] ?? 0;
-                       $vals['sizediff'] = intval( $row->rev_len - $parentLen );
+                       $vals['sizediff'] = (int)$row->rev_len - $parentLen;
                }
 
                if ( $this->fld_tags ) {