From 4fdddc9292e397de7e6a0b386f3432adee477965 Mon Sep 17 00:00:00 2001 From: Alexandre Emsenhuber Date: Fri, 29 Apr 2011 18:27:44 +0000 Subject: [PATCH] * (bug 21196) Article::getContributors() no longer fail on PostgreSQL Changed User::loadFromRow() to allow partial row (user_id, user_name, user_real_name can be set independently from other fields) userand removed User::$mDataLoaded, replaced by User::$mLoadedItems (marked as private) that can be an array with already loaded items or true when all data has been loaded. Changed GlobalFunctions.php and Database.php accordingly, no use of User::$mDataLoaded in extensions. --- RELEASE-NOTES | 1 + includes/Article.php | 9 ++- includes/GlobalFunctions.php | 4 +- includes/User.php | 126 +++++++++++++++++++++++++++-------- includes/db/Database.php | 2 +- 5 files changed, 110 insertions(+), 32 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index fc602e7421..9f330063f6 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -254,6 +254,7 @@ PHP if you have not done so prior to upgrading MediaWiki. * (bug 27249) "Installed software" table in Special:Version should always be left-to-right. * (bug 28719) Do not call mLinkHolders __destruct explicitly +* (bug 21196) Article::getContributors() no longer fail on PostgreSQL. === API changes in 1.18 === * (bug 26339) Throw warning when truncating an overlarge API result. diff --git a/includes/Article.php b/includes/Article.php index 834a6be1fc..4a0416257a 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -836,11 +836,18 @@ class Article { $dbr = wfGetDB( DB_SLAVE ); $userTable = $dbr->tableName( 'user' ); + if ( $dbr->implicitGroupby() ) { + $realNameField = 'user_real_name'; + } else { + $realNameField = 'FIRST(user_real_name) AS user_real_name'; + } + $tables = array( 'revision', 'user' ); $fields = array( - "$userTable.*", + 'rev_user as user_id', 'rev_user_text AS user_name', + $realNameField, 'MAX(rev_timestamp) AS timestamp', ); diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 4a0b95172f..20ce45a018 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -389,9 +389,9 @@ function wfLogProfilingData() { if ( $forward ) { $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})"; } - // Don't unstub $wgUser at this late stage just for statistics purposes + // Don't load $wgUser at this late stage just for statistics purposes // FIXME: We can detect some anons even if it is not loaded. See User::getId() - if ( $wgUser->mDataLoaded && $wgUser->isAnon() ) { + if ( $wgUser->isItemLoaded( 'id' ) && $wgUser->isAnon() ) { $forward .= ' anon'; } $log = sprintf( "%s\t%04.3f\t%s\n", diff --git a/includes/User.php b/includes/User.php index 5df021b5fb..66d8b9fa03 100644 --- a/includes/User.php +++ b/includes/User.php @@ -157,10 +157,13 @@ class User { /** * Bool Whether the cache variables have been loaded. */ - var $mDataLoaded, $mAuthLoaded, $mOptionsLoaded; + //@{ + var $mOptionsLoaded; + private $mLoadedItems = array(); + //@} /** - * String Initialization data source if mDataLoaded==false. May be one of: + * String Initialization data source if mLoadedItems!==true. May be one of: * - 'defaults' anonymous user initialised from class defaults * - 'name' initialise from mName * - 'id' initialise from mId @@ -211,13 +214,13 @@ class User { * Load the user table data for this object from the source given by mFrom. */ function load() { - if ( $this->mDataLoaded ) { + if ( $this->mLoadedItems === true ) { return; } wfProfileIn( __METHOD__ ); # Set it now to avoid infinite recursion in accessors - $this->mDataLoaded = true; + $this->mLoadedItems = true; switch ( $this->mFrom ) { case 'defaults': @@ -336,6 +339,7 @@ class User { $u = new User; $u->mName = $name; $u->mFrom = 'name'; + $u->setItemLoaded( 'name' ); return $u; } } @@ -350,6 +354,7 @@ class User { $u = new User; $u->mId = $id; $u->mFrom = 'id'; + $u->setItemLoaded( 'id' ); return $u; } @@ -390,7 +395,14 @@ class User { /** * Create a new user object from a user row. - * The row should have all fields from the user table in it. + * The row should have the following fields from the user table in it: + * - either user_name or user_id to load further data if needed (or both) + * - user_real_name + * - all other fields (email, password, etc.) + * It is useless to provide the remaining fields if either user_id, + * user_name and user_real_name are not provided because the whole row + * will be loaded once more from the database when accessing them. + * * @param $row Array A row from the user table * @return User */ @@ -846,6 +858,31 @@ class User { wfProfileOut( __METHOD__ ); } + /** + * Return whether an item has been loaded. + * + * @param $item String: item to check. Current possibilities: + * - 'id' + * - name + * - realname + * @return Boolean + */ + public function isItemLoaded( $item ) { + return $this->mLoadedItems === true || ( isset( $this->mLoadedItems[$item] ) + && $this->mLoadedItems[$item] === true ); + } + + /** + * Set that an item has been loaded + * + * @param $item String + */ + private function setItemLoaded( $item ) { + if ( is_array( $this->mLoadedItems ) ) { + $this->mLoadedItems[$item] = true; + } + } + /** * Load user data from the session or login cookie. If there are no valid * credentials, initialises the user as an anonymous user. @@ -936,7 +973,7 @@ class User { /** * Load user and user_group data from the database. - * $this::mId must be set, this is how the user is identified. + * $this->mId must be set, this is how the user is identified. * * @return Bool True if the user exists, false if the user is anonymous * @private @@ -976,25 +1013,51 @@ class User { * @param $row Array Row from the user table to load. */ function loadFromRow( $row ) { - $this->mDataLoaded = true; + $all = true; + + if ( isset( $row->user_name ) ) { + $this->mName = $row->user_name; + $this->mFrom = 'name'; + $this->setItemLoaded( 'name' ); + } else { + $all = false; + } + + if ( isset( $row->user_name ) ) { + $this->mRealName = $row->user_real_name; + $this->setItemLoaded( 'realname' ); + } else { + $all = false; + } if ( isset( $row->user_id ) ) { $this->mId = intval( $row->user_id ); + $this->mFrom = 'id'; + $this->setItemLoaded( 'id' ); + } else { + $all = false; + } + + if ( isset( $row->user_password ) ) { + $this->mPassword = $row->user_password; + $this->mNewpassword = $row->user_newpassword; + $this->mNewpassTime = wfTimestampOrNull( TS_MW, $row->user_newpass_time ); + $this->mEmail = $row->user_email; + $this->decodeOptions( $row->user_options ); + $this->mTouched = wfTimestamp(TS_MW,$row->user_touched); + $this->mToken = $row->user_token; + $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated ); + $this->mEmailToken = $row->user_email_token; + $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires ); + $this->mRegistration = wfTimestampOrNull( TS_MW, $row->user_registration ); + $this->mEditCount = $row->user_editcount; + } else { + $all = false; + } + + if ( $all ) { + $this->mLoadedItems = true; } - $this->mName = $row->user_name; - $this->mRealName = $row->user_real_name; - $this->mPassword = $row->user_password; - $this->mNewpassword = $row->user_newpassword; - $this->mNewpassTime = wfTimestampOrNull( TS_MW, $row->user_newpass_time ); - $this->mEmail = $row->user_email; - $this->decodeOptions( $row->user_options ); - $this->mTouched = wfTimestamp(TS_MW,$row->user_touched); - $this->mToken = $row->user_token; - $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated ); - $this->mEmailToken = $row->user_email_token; - $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires ); - $this->mRegistration = wfTimestampOrNull( TS_MW, $row->user_registration ); - $this->mEditCount = $row->user_editcount; } /** @@ -1032,7 +1095,7 @@ class User { $this->mOptions = null; if ( $reloadFrom ) { - $this->mDataLoaded = false; + $this->mLoadedItems = array(); $this->mFrom = $reloadFrom; } } @@ -1461,7 +1524,7 @@ class User { && User::isIP( $this->mName ) ) { // Special case, we know the user is anonymous return 0; - } elseif( $this->mId === null ) { + } elseif( !$this->isItemLoaded( 'id' ) ) { // Don't load if this was initialized from an ID $this->load(); } @@ -1482,7 +1545,7 @@ class User { * @return String User's name or IP address */ function getName() { - if ( !$this->mDataLoaded && $this->mFrom == 'name' ) { + if ( $this->isItemLoaded( 'name' ) ) { # Special case optimisation return $this->mName; } else { @@ -1910,7 +1973,10 @@ class User { * @return String User's real name */ function getRealName() { - $this->load(); + if ( !$this->isItemLoaded( 'realname' ) ) { + $this->load(); + } + return $this->mRealName; } @@ -2859,6 +2925,8 @@ class User { */ function checkTemporaryPassword( $plaintext ) { global $wgNewPasswordExpiry; + + $this->load(); if( self::comparePasswords( $this->mNewpassword, $plaintext, $this->getId() ) ) { if ( is_null( $this->mNewpassTime ) ) { return true; @@ -3174,9 +3242,11 @@ class User { * non-existent/anonymous user accounts. */ public function getRegistration() { - return $this->getId() > 0 - ? $this->mRegistration - : false; + if ( $this->isAnon() ) { + return false; + } + $this->load(); + return $this->mRegistration; } /** diff --git a/includes/db/Database.php b/includes/db/Database.php index 4d2d3357ba..77f7c7c758 100644 --- a/includes/db/Database.php +++ b/includes/db/Database.php @@ -668,7 +668,7 @@ abstract class DatabaseBase implements DatabaseType { # Add a comment for easy SHOW PROCESSLIST interpretation # if ( $fname ) { global $wgUser; - if ( is_object( $wgUser ) && $wgUser->mDataLoaded ) { + if ( is_object( $wgUser ) && $wgUser->isItemLoaded( 'name' ) ) { $userName = $wgUser->getName(); if ( mb_strlen( $userName ) > 15 ) { $userName = mb_substr( $userName, 0, 15 ) . '...'; -- 2.20.1