Handle edge case in WikiPage::lock()
authorAaron Schulz <aschulz@wikimedia.org>
Wed, 7 Oct 2015 20:42:42 +0000 (13:42 -0700)
committerAaron Schulz <aschulz@wikimedia.org>
Wed, 7 Oct 2015 21:21:39 +0000 (14:21 -0700)
If the page row for the title was replaced with another
row with the same title *and* page_latest but a different
page_id, callers will want to bail when doing CAS logic

Change-Id: Ia98323d5adf59b91eb86849fd21dd90f586b9207

includes/page/WikiPage.php

index e47e06c..5f3a59e 100644 (file)
@@ -2781,15 +2781,14 @@ class WikiPage implements Page, IDBAccessObject {
                $dbw->begin( __METHOD__ );
 
                if ( $id == 0 ) {
+                       $this->loadPageData( self::READ_LATEST );
+                       $id = $this->getID();
                        // T98706: lock the page from various other updates but avoid using
                        // WikiPage::READ_LOCKING as that will carry over the FOR UPDATE to
                        // the revisions queries (which also JOIN on user). Only lock the page
                        // row and CAS check on page_latest to see if the trx snapshot matches.
-                       $latest = $this->lock();
-
-                       $this->loadPageData( WikiPage::READ_LATEST );
-                       $id = $this->getID();
-                       if ( $id == 0 || $this->getLatest() != $latest ) {
+                       $lockedLatest = $this->lock();
+                       if ( $id == 0 || $this->getLatest() != $lockedLatest ) {
                                // Page not there or trx snapshot is stale
                                $dbw->rollback( __METHOD__ );
                                $status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
@@ -2904,15 +2903,18 @@ class WikiPage implements Page, IDBAccessObject {
        }
 
        /**
-        * Lock the page row for this title and return page_latest (or 0)
+        * Lock the page row for this title+id and return page_latest (or 0)
         *
-        * @return integer
+        * @return integer Returns 0 if no row was found with this title+id
         */
        protected function lock() {
                return (int)wfGetDB( DB_MASTER )->selectField(
                        'page',
                        'page_latest',
                        array(
+                               'page_id' => $this->getId(),
+                               // Typically page_id is enough, but some code might try to do
+                               // updates assuming the title is the same, so verify that
                                'page_namespace' => $this->getTitle()->getNamespace(),
                                'page_title' => $this->getTitle()->getDBkey()
                        ),