diff --git a/classes/File.php b/classes/File.php index a273a60d3a..63fe6e8f4f 100644 --- a/classes/File.php +++ b/classes/File.php @@ -760,9 +760,12 @@ class File extends Managed_DataObject if ($file instanceof File) { throw new ServerException('URL already exists in DB'); } - $sql = 'UPDATE %1$s SET urlhash = %2$s, url = %3$s WHERE urlhash = %4$s;'; $result = $this->query(sprintf( - $sql, + <<<'END' + UPDATE %1$s + SET urlhash = %2$s, url = %3$s, modified = CURRENT_TIMESTAMP + WHERE urlhash = %4$s; + END, $this->tableName(), $this->_quote((string)self::hashurl($url)), $this->_quote((string)$url), @@ -939,7 +942,7 @@ class File extends Managed_DataObject } } echo "...and now all the non-duplicates which are longer than 191 characters...\n"; - $file->query('UPDATE file SET url=LEFT(url, 191) WHERE LENGTH(url)>191'); + $file->query('UPDATE file SET url = LEFT(url, 191) WHERE LENGTH(url) > 191'); echo "\n...now running hacky pre-schemaupdate change for $table:"; // We have to create a urlhash that is _not_ the primary key, @@ -973,7 +976,7 @@ class File extends Managed_DataObject throw new ServerException('Unknown DB type selected.'); } $tablefix->query(sprintf( - 'UPDATE %1$s SET urlhash = %2$s;', + 'UPDATE %1$s SET urlhash = %2$s, modified = CURRENT_TIMESTAMP;', $tablefix->escapedTableName(), $url_sha256 )); diff --git a/classes/File_redirection.php b/classes/File_redirection.php index 234cb9de1e..4bbb3f1484 100644 --- a/classes/File_redirection.php +++ b/classes/File_redirection.php @@ -468,7 +468,7 @@ class File_redirection extends Managed_DataObject throw new ServerException('Unknown DB type selected.'); } $tablefix->query(sprintf( - 'UPDATE %1$s SET urlhash = %2$s;', + 'UPDATE %1$s SET urlhash = %2$s, modified = CURRENT_TIMESTAMP;', $tablefix->escapedTableName(), $url_sha256 )); diff --git a/classes/Local_group.php b/classes/Local_group.php index cd95998029..2a1ff71dfc 100644 --- a/classes/Local_group.php +++ b/classes/Local_group.php @@ -74,15 +74,20 @@ class Local_group extends Managed_DataObject public function setNickname($nickname) { $this->decache(); + $modified = common_sql_now(); $result = $this->query(sprintf( - 'UPDATE local_group SET nickname = %1$s WHERE group_id = %2$d;', + <<<'END' + UPDATE local_group SET nickname = %1$s, modified = %2$s + WHERE group_id = %3$d; + END, $this->_quote($nickname), + $this->_quote($modified), $this->group_id )); if ($result) { $this->nickname = $nickname; - $this->fixupTimestamps(); + $this->modified = $modified; $this->encache(); } else { common_log_db_error($local, 'UPDATE', __FILE__); diff --git a/classes/Managed_DataObject.php b/classes/Managed_DataObject.php index ead8a9a07f..100c751c87 100644 --- a/classes/Managed_DataObject.php +++ b/classes/Managed_DataObject.php @@ -522,6 +522,25 @@ abstract class Managed_DataObject extends Memcached_DataObject return $aliases; } + /** + * Set the attribute defined as "timestamp" to CURRENT_TIMESTAMP. + * This is hooked in update() and updateWithKeys() to update "modified". + * + * @access private + * @return void + */ + private function updateAutoTimestamps(): void + { + $table = static::schemaDef(); + foreach ($table['fields'] as $name => $col) { + if ($col['type'] === 'timestamp' + && !array_key_exists('default', $col) + && !isset($this->$name)) { + $this->$name = common_sql_now(); + } + } + } + /** * update() won't write key columns, so we have to do it ourselves. * This also automatically calls "update" _before_ it sets the keys. @@ -548,6 +567,10 @@ abstract class Managed_DataObject extends Memcached_DataObject // do it in a transaction $this->query('START TRANSACTION'); + // ON UPDATE CURRENT_TIMESTAMP behaviour + // @fixme Should the value be reverted back if transaction failed? + $this->updateAutoTimestamps(); + $parts = []; foreach ($this->keys() as $k) { $v = $this->table()[$k]; @@ -664,12 +687,25 @@ abstract class Managed_DataObject extends Memcached_DataObject public function insert() { $this->onInsert(); - return parent::insert(); + $result = parent::insert(); + + // Make this object aware of the changed "modified" attribute. + // Sets it approximately to the same value as DEFAULT CURRENT_TIMESTAMP + // just did (@fixme). + if ($result) { + $this->updateAutoTimestamps(); + } + return $result; } - public function update($dataObject=false) + public function update($dataObject = false) { $this->onUpdate($dataObject); + + // ON UPDATE CURRENT_TIMESTAMP behaviour + // @fixme Should the value be reverted back if transaction failed? + $this->updateAutoTimestamps(); + return parent::update($dataObject); } } diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 350b7659b4..f26018b33d 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -443,7 +443,6 @@ class Memcached_DataObject extends Safe_DataObject { $result = parent::insert(); if ($result) { - $this->fixupTimestamps(); $this->encache(); // in case of cached negative lookups } return $result; @@ -456,7 +455,6 @@ class Memcached_DataObject extends Safe_DataObject } $result = parent::update($dataObject); if ($result !== false) { - $this->fixupTimestamps(); $this->encache(); } return $result; @@ -931,22 +929,6 @@ class Memcached_DataObject extends Safe_DataObject return $c->delete($cacheKey); } - public function fixupTimestamps() - { - // Fake up timestamp columns - $columns = $this->table(); - foreach ($columns as $name => $type) { - if ($type & DB_DATAOBJECT_MYSQLTIMESTAMP) { - $this->$name = common_sql_now(); - } - } - } - - public function debugDump() - { - common_debug("debugDump: " . common_log_objstring($this)); - } - public function raiseError($message, $type = null, $behavior = null) { $id = get_class($this); diff --git a/classes/Notice.php b/classes/Notice.php index f24283fa60..96368fe5b4 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -3243,7 +3243,11 @@ class Notice extends Managed_DataObject unset($notice); $notice = new Notice(); $notice->query(sprintf( - 'UPDATE %1$s SET %2$s = NULL WHERE id IN (%3$s)', + <<<'END' + UPDATE %1$s + SET %2$s = NULL, modified = CURRENT_TIMESTAMP + WHERE id IN (%3$s) + END, $notice->escapedTableName(), $field, implode(',', $ids) diff --git a/classes/Oauth_application_user.php b/classes/Oauth_application_user.php index 8165688226..1ed8bc706c 100644 --- a/classes/Oauth_application_user.php +++ b/classes/Oauth_application_user.php @@ -86,6 +86,7 @@ class Oauth_application_user extends Managed_DataObject return true; } $toupdate = implode(', ', $parts); + $toupdate .= ', modified = CURRENT_TIMESTAMP'; $table = $this->tableName(); $tableName = $this->escapedTableName(); diff --git a/classes/Profile_tag.php b/classes/Profile_tag.php index 66a922de79..596ef5b11c 100644 --- a/classes/Profile_tag.php +++ b/classes/Profile_tag.php @@ -292,14 +292,16 @@ class Profile_tag extends Managed_DataObject public static function moveTag($orig, $new) { $tags = new Profile_tag(); - $qry = "UPDATE profile_tag SET tag = '%s', tagger = '%s' " . - "WHERE tag = '%s' AND tagger = '%s'"; $result = $tags->query(sprintf( - $qry, - $tags->escape($new->tag), - $tags->escape($new->tagger), - $tags->escape($orig->tag), - $tags->escape($orig->tagger) + <<<'END' + UPDATE profile_tag + SET tag = %1$s, tagger = %2$s, modified = CURRENT_TIMESTAMP + WHERE tag = %3$s AND tagger = %4$s + END, + $tags->_quote($new->tag), + $tags->_quote($new->tagger), + $tags->_quote($orig->tag), + $tags->_quote($orig->tagger) )); if ($result === false) { diff --git a/classes/Queue_item.php b/classes/Queue_item.php index 709b35435f..39380b9938 100644 --- a/classes/Queue_item.php +++ b/classes/Queue_item.php @@ -104,11 +104,22 @@ class Queue_item extends Managed_DataObject */ public function releaseClaim() { - // DB_DataObject doesn't let us save nulls right now - $sql = sprintf("UPDATE queue_item SET claimed=NULL WHERE id=%d", $this->getID()); - $this->query($sql); + $modified = common_sql_now(); + // @fixme Consider $this->sqlValue('NULL') + $ret = $this->query(sprintf( + <<<'END' + UPDATE queue_item + SET claimed = NULL, modified = %1$s + WHERE id = %2$d + END, + $this->_quote($modified), + $this->getID() + )); - $this->claimed = null; - $this->encache(); + if ($ret) { + $this->claimed = null; + $this->modified = $modified; + $this->encache(); + } } } diff --git a/lib/database/mysqlschema.php b/lib/database/mysqlschema.php index b66911951a..6a4a5f017f 100644 --- a/lib/database/mysqlschema.php +++ b/lib/database/mysqlschema.php @@ -145,12 +145,6 @@ class MysqlSchema extends Schema if (preg_match('/(^|\s)auto_increment(\s|$)/i', $extra)) { $field['auto_increment'] = true; } - if (preg_match( - '/(^|\s)on update CURRENT_TIMESTAMP(\(\))?(\s|$)/i', - $extra - )) { - $field['auto_update_timestamp'] = true; - } } $table_props = $this->getTableProperties($table, ['TABLE_COLLATION']); @@ -519,10 +513,6 @@ class MysqlSchema extends Schema if (!empty($cd['auto_increment'])) { $line[] = 'AUTO_INCREMENT'; } - // This'll have been added from our transform of "timestamp" type - if (!empty($cd['auto_update_timestamp'])) { - $line[] = 'ON UPDATE CURRENT_TIMESTAMP'; - } if (!empty($cd['description'])) { $line[] = 'COMMENT'; diff --git a/lib/database/schema.php b/lib/database/schema.php index 835dd4a8f1..0fa02e7199 100644 --- a/lib/database/schema.php +++ b/lib/database/schema.php @@ -1043,7 +1043,6 @@ class Schema $col['type'] = 'datetime'; if (!array_key_exists('default', $col)) { $col['default'] = 'CURRENT_TIMESTAMP'; - // FIXME: PostgreSQL support. $col['auto_update_timestamp'] = true; } // no break diff --git a/plugins/Embed/scripts/fixup_files.php b/plugins/Embed/scripts/fixup_files.php index 1a4d1ed3dc..246091d2f3 100755 --- a/plugins/Embed/scripts/fixup_files.php +++ b/plugins/Embed/scripts/fixup_files.php @@ -116,10 +116,15 @@ while ($fn->fetch()) { echo " (unchanged, but embedding lookup failed)\n"; } } elseif (!$dry) { - $sql = "UPDATE file " . - "SET mimetype=null, title=null,size=null,protected=null " . - "WHERE id={$f->id}"; - $f->query($sql); + $f->query(sprintf( + <<<'END' + UPDATE file + SET mimetype = NULL, title = NULL, size = NULL, + protected = NULL, modified = CURRENT_TIMESTAMP + WHERE id = %d + END, + $f->getID() + )); $f->decache(); if ($data instanceof File_embed) { $fetch = true; @@ -144,12 +149,18 @@ while ($fn->fetch()) { echo "Found broken file with "; } - echo "ID: {$f->id}, URL {$f->url}\n"; + echo "ID: {$f->getID()}, URL {$f->url}\n"; if (!$dry) { $fetch = true; - $sql = "UPDATE file SET title=null, size=null, protected=null " . - "WHERE id={$f->id}"; - $f->query($sql); + $f->query(sprintf( + <<<'END' + UPDATE file + SET title = NULL, size = NULL, + protected = NULL, modified = CURRENT_TIMESTAMP + WHERE id = %d, + END, + $f->getID() + )); $f->decache(); if ($data instanceof File_embed) { @@ -159,7 +170,7 @@ while ($fn->fetch()) { if ($thumb instanceof File_thumbnail) { // Delete all thumbnails, not just this one - $f->query("DELETE from file_thumbnail WHERE file_id = {$f->id}"); + $f->query("DELETE FROM file_thumbnail WHERE file_id = {$f->getID()}"); $thumb->decache(); } } diff --git a/plugins/OStatus/scripts/update-profile.php b/plugins/OStatus/scripts/update-profile.php index 7e2a58ff56..6f44217be7 100755 --- a/plugins/OStatus/scripts/update-profile.php +++ b/plugins/OStatus/scripts/update-profile.php @@ -109,9 +109,10 @@ if ($feedurl != $oprofile->feeduri || $salmonuri != $oprofile->salmonuri) { $ok = $oprofile->query( <<_quote($feedurl)}, + salmonuri = {$oprofile->_quote($salmonuri)}, + modified = CURRENT_TIMESTAMP + WHERE uri = {$oprofile->_quote($uri)} END ); diff --git a/plugins/RSSCloud/lib/rsscloudnotifier.php b/plugins/RSSCloud/lib/rsscloudnotifier.php index 21d9597302..b4d919f884 100644 --- a/plugins/RSSCloud/lib/rsscloudnotifier.php +++ b/plugins/RSSCloud/lib/rsscloudnotifier.php @@ -1,45 +1,39 @@ . + /** - * StatusNet, the distributed open-source microblogging tool - * * Class to ping an rssCloud endpoint when a feed has been updated * - * PHP version 5 - * - * LICENCE: This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * * @category Plugin - * @package StatusNet + * @package GNUsocial * @author Zach Copley * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ -if (!defined('STATUSNET')) { - exit(1); -} +defined('STATUSNET') || die(); /** * Class for notifying cloud-enabled RSS aggregators that StatusNet * feeds have been updated. * - * @category Plugin - * @package StatusNet - * @author Zach Copley - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ + * @category Plugin + * @package GNUsocial + * @author Zach Copley + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ class RSSCloudNotifier { @@ -54,7 +48,7 @@ class RSSCloudNotifier * * @return boolean success */ - function challenge($endpoint, $feed) + public function challenge($endpoint, $feed) { $code = common_confirmation_code(128); $params = array('url' => $feed, 'challenge' => $code); @@ -64,9 +58,11 @@ class RSSCloudNotifier $client = new HTTPClient(); $response = $client->get($url); } catch (Exception $e) { - common_log(LOG_INFO, - 'RSSCloud plugin - failure testing notify handler ' . - $endpoint . ' - ' . $e->getMessage()); + common_log( + LOG_INFO, + 'RSSCloud plugin - failure testing notify handler ' + . $endpoint . ' - ' . $e->getMessage() + ); return false; } @@ -110,7 +106,7 @@ class RSSCloudNotifier * * @return boolean success */ - function postUpdate($endpoint, $feed) + public function postUpdate($endpoint, $feed) { $headers = array(); $postdata = array('url' => $feed); @@ -147,7 +143,7 @@ class RSSCloudNotifier * * @return boolean success */ - function notify($profile) + public function notify($profile) { $feed = common_path('api/statuses/user_timeline/') . $profile->id . '.rss'; @@ -180,18 +176,16 @@ class RSSCloudNotifier * * @return boolean success */ - function handleFailure($cloudSub) + public function handleFailure($cloudSub) { $failCnt = $cloudSub->failures + 1; if ($failCnt == self::MAX_FAILURES) { - - common_log(LOG_INFO, - 'Deleting RSSCloud subcription ' . - '(max failure count reached), profile: ' . - $cloudSub->subscribed . - ' handler: ' . - $cloudSub->url); + common_log( + LOG_INFO, + 'Deleting RSSCloud subcription (max failure count reached), ' + . "profile: {$cloudSub->subscribed} handler: {$cloudSub->url}" + ); // XXX: WTF! ->delete() doesn't work. Clearly, there are some issues with // the DB_DataObject, or my understanding of it. Have to drop into SQL. @@ -216,18 +210,23 @@ class RSSCloudNotifier // XXX: ->update() not working either, gar! - $qry = 'UPDATE rsscloud_subscription' . - ' SET failures = ' . $failCnt . - ' WHERE subscribed = ' . $cloudSub->subscribed . - ' AND url = \'' . $cloudSub->url . '\''; - - $result = $cloudSub->query($qry); + $result = $cloudSub->query(sprintf( + <<<'END' + UPDATE rsscloud_subscription + SET failures = %1$d, modified = CURRENT_TIMESTAMP + WHERE subscribed = %2$d AND url = %3$s + END, + $failCnt, + $cloudSub->subscribed, + $cloudSub->_quote($cloudSub->url) + )); if (!$result) { common_log_db_error($cloudsub, 'UPDATE', __FILE__); - common_log(LOG_ERR, - 'Could not update failure ' . - 'count on RSSCloud subscription'); + common_log( + LOG_ERR, + 'Could not update failure count on RSSCloud subscription' + ); } } } diff --git a/plugins/UserFlag/actions/clearflag.php b/plugins/UserFlag/actions/clearflag.php index af61753095..ad1ad0d852 100644 --- a/plugins/UserFlag/actions/clearflag.php +++ b/plugins/UserFlag/actions/clearflag.php @@ -89,10 +89,14 @@ class ClearflagAction extends ProfileFormAction { $ufp = new User_flag_profile(); - $result = $ufp->query('UPDATE user_flag_profile ' . - 'SET cleared = CURRENT_TIMESTAMP ' . - 'WHERE cleared IS NULL ' . - 'AND profile_id = ' . $this->profile->id); + $result = $ufp->query(sprintf( + <<<'END' + UPDATE user_flag_profile + SET cleared = CURRENT_TIMESTAMP, modified = cleared + WHERE cleared IS NULL AND profile_id = %d + END, + $this->profile->getID() + )); if ($result === false) { // TRANS: Server exception given when flags could not be cleared. diff --git a/scripts/strip_geo.php b/scripts/strip_geo.php index 9cc7f8409c..0a232a7b68 100755 --- a/scripts/strip_geo.php +++ b/scripts/strip_geo.php @@ -104,10 +104,15 @@ if ($notice->N) { // but we're going to have to decache them individually anyway and // it doesn't hurt to make sure we don't hold up replication with // what might be a very slow single UPDATE. - $query = sprintf('UPDATE notice ' . - 'SET lat=NULL,lon=NULL,location_ns=NULL,location_id=NULL ' . - 'WHERE id=%d', $notice->id); - $ok = $update->query($query); + $ok = $update->query(sprintf( + <<<'END' + UPDATE notice + SET lat = NULL, lon = NULL, location_ns = NULL, location_id = NULL + modified = CURRENT_TIMESTAMP + WHERE id = %d + END, + $notice->getID() + )); if ($ok) { // And now we decache him manually, as query() doesn't know what we're doing... $orig->decache(); diff --git a/scripts/upgrade.php b/scripts/upgrade.php index 8b384c02f0..1509dcb2b7 100755 --- a/scripts/upgrade.php +++ b/scripts/upgrade.php @@ -131,9 +131,16 @@ function fixupUserBadNulls(): void if ($user->find()) { while ($user->fetch()) { - $sql = "UPDATE {$user->escapedTableName()} SET {$col} = NULL " - . "WHERE id = {$user->id}"; - $user->query($sql); + $user->query(sprintf( + <<<'END' + UPDATE %1$s + SET %2$s = NULL, modified = CURRENT_TIMESTAMP + WHERE id = %3$d + END, + $user->escapedTableName(), + $col, + $user->getID() + )); } } } @@ -269,8 +276,12 @@ function fixupConversationURIs() while ($conv->fetch()) { $uri = common_local_url('conversation', ['id' => $conv->id]); $sql = sprintf( - 'UPDATE conversation SET uri = \'%1$s\' WHERE id = %2$d;', - $conv->escape($uri), + <<<'END' + UPDATE conversation + SET uri = %1$s, modified = CURRENT_TIMESTAMP + WHERE id = %2$d; + END, + $conv->_quote($uri), $conv->id ); $conv->query($sql); @@ -310,7 +321,15 @@ function initGroupProfileId() $profile->query('ROLLBACK'); throw new Exception('Profile insertion failed, profileurl: '.$profile->profileurl); } - $group->query("UPDATE user_group SET profile_id={$id} WHERE id={$group->id}"); + $group->query(sprintf( + <<<'END' + UPDATE user_group + SET profile_id = %1$d, modified = CURRENT_TIMESTAMP + WHERE id = %2$d + END, + $id, + $group->getID() + )); $profile->query('COMMIT'); $profile->free(); @@ -400,18 +419,24 @@ function initSubscriptionURI() if ($sub->find()) { while ($sub->fetch()) { try { + $uri = Subscription::newUri( + $sub->getSubscriber(), + $sub->getSubscribed(), + $sub->created + ); $sub->decache(); $sub->query(sprintf( - 'UPDATE subscription '. - "SET uri = '%s' " . - 'WHERE subscriber = %d '. - 'AND subscribed = %d', - $sub->escape(Subscription::newUri($sub->getSubscriber(), $sub->getSubscribed(), $sub->created)), + <<<'END' + UPDATE subscription + SET uri = %1$s, modified = CURRENT_TIMESTAMP + WHERE subscriber = %2$d AND subscribed = %3$d + END, + $sub->_quote($uri), $sub->subscriber, $sub->subscribed )); } catch (Exception $e) { - common_log(LOG_ERR, "Error updated subscription URI: " . $e->getMessage()); + common_log(LOG_ERR, 'Error updating subscription URI: ' . $e->getMessage()); } } } @@ -429,13 +454,19 @@ function initGroupMemberURI() if ($mem->find()) { while ($mem->fetch()) { try { + $uri = Group_member::newUri( + Profile::getByID($mem->profile_id), + User_group::getByID($mem->group_id), + $mem->created + ); $mem->decache(); $mem->query(sprintf( - 'UPDATE group_member '. - "SET uri = '%s' " . - 'WHERE profile_id = %d ' . - 'AND group_id = %d', - Group_member::newUri(Profile::getByID($mem->profile_id), User_group::getByID($mem->group_id), $mem->created), + <<<'END' + UPDATE group_member + SET uri = %s, modified = CURRENT_TIMESTAMP + WHERE profile_id = %d AND group_id = %d + END, + $mem->_quote($uri), $mem->profile_id, $mem->group_id )); @@ -643,7 +674,7 @@ function fixupFileThumbnailUrlhash() $thumb = new File_thumbnail(); $thumb->query(sprintf( 'UPDATE %1$s ' . - 'SET urlhash = %2$s ' . + 'SET urlhash = %2$s, modified = CURRENT_TIMESTAMP ' . 'WHERE url IS NOT NULL ' . // find all entries with a url value "AND url <> '' " . // precaution against non-null empty strings 'AND urlhash IS NULL', // but don't touch those we've already calculated