ApiQueryLinks: Avoid MySQL order-by bug
authorBrad Jorsch <bjorsch@wikimedia.org>
Wed, 12 Apr 2017 15:38:20 +0000 (11:38 -0400)
committerTim Starling <tstarling@wikimedia.org>
Fri, 13 Oct 2017 00:27:01 +0000 (00:27 +0000)
MySQL (and MariaDB) have a strange bug where it will often get confused
and insist on filesorting if a field that is constant in the WHERE
clause is also present in ORDER BY. We've worked around this in several
places in the API, and in fact it was done in this very module in r34720
and r37250.

But when r67450 added the pltitles and tltemplates parameters, it didn't
adjust the logic for avoiding the DB bug. This does that now.

Change-Id: I9f37f8812a94cdd088d3940da43d1046ebd455d6

includes/api/ApiQueryLinks.php

index 2f75e76..508bdf3 100644 (file)
@@ -87,25 +87,34 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
 
                $this->addTables( $this->table );
                $this->addWhereFld( $this->prefix . '_from', array_keys( $this->getPageSet()->getGoodTitles() ) );
-               $this->addWhereFld( $this->prefix . '_namespace', $params['namespace'] );
 
+               $multiNS = true;
+               $multiTitle = true;
                if ( $params[$this->titlesParam] ) {
+                       // Filter the titles in PHP so our ORDER BY bug avoidance below works right.
+                       $filterNS = $params['namespace'] ? array_flip( $params['namespace'] ) : false;
+
                        $lb = new LinkBatch;
                        foreach ( $params[$this->titlesParam] as $t ) {
                                $title = Title::newFromText( $t );
                                if ( !$title ) {
                                        $this->addWarning( [ 'apiwarn-invalidtitle', wfEscapeWikiText( $t ) ] );
-                               } else {
+                               } elseif ( !$filterNS || isset( $filterNS[$title->getNamespace()] ) ) {
                                        $lb->addObj( $title );
                                }
                        }
                        $cond = $lb->constructSet( $this->prefix, $this->getDB() );
                        if ( $cond ) {
                                $this->addWhere( $cond );
+                               $multiNS = count( $lb->data ) !== 1;
+                               $multiTitle = count( call_user_func_array( 'array_merge', $lb->data ) ) !== 1;
                        } else {
                                // No titles so no results
                                return;
                        }
+               } elseif ( $params['namespace'] ) {
+                       $this->addWhereFld( $this->prefix . '_namespace', $params['namespace'] );
+                       $multiNS = count( $params['namespace'] ) !== 1;
                }
 
                if ( !is_null( $params['continue'] ) ) {
@@ -134,12 +143,15 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
                if ( count( $this->getPageSet()->getGoodTitles() ) != 1 ) {
                        $order[] = $this->prefix . '_from' . $sort;
                }
-               if ( count( $params['namespace'] ) != 1 ) {
+               if ( $multiNS ) {
                        $order[] = $this->prefix . '_namespace' . $sort;
                }
-
-               $order[] = $this->prefix . '_title' . $sort;
-               $this->addOption( 'ORDER BY', $order );
+               if ( $multiTitle ) {
+                       $order[] = $this->prefix . '_title' . $sort;
+               }
+               if ( $order ) {
+                       $this->addOption( 'ORDER BY', $order );
+               }
                $this->addOption( 'LIMIT', $params['limit'] + 1 );
 
                $res = $this->select( __METHOD__ );