Avoid potential race condition when saving search keywords
authorMarcel Werk <burntime@woltlab.com>
Mon, 13 Apr 2020 11:34:34 +0000 (13:34 +0200)
committerMarcel Werk <burntime@woltlab.com>
Mon, 13 Apr 2020 11:34:34 +0000 (13:34 +0200)
wcfsetup/install/files/lib/data/search/keyword/SearchKeywordAction.class.php
wcfsetup/install/files/lib/form/SearchForm.class.php
wcfsetup/install/files/lib/system/search/SearchKeywordManager.class.php

index 6c0d8834933d8ef1ede16c5ccea1f9a8a2946f86..dfb4d332d36de10052e1b1a6b0d420e3c02d146d 100644 (file)
@@ -56,4 +56,21 @@ class SearchKeywordAction extends AbstractDatabaseObjectAction implements ISearc
                
                return $list;
        }
+       
+       /**
+        * Inserts a new keyword if it does not already exist, or updates it if it does.
+        */
+       public function upsert() {
+               $sql = "INSERT INTO             wcf".WCF_N."_search_keyword
+                                               (keyword, searches, lastSearchTime)
+                       VALUES                  (?, ?, ?)
+                       ON DUPLICATE KEY UPDATE searches = searches + VALUES(searches),
+                                               lastSearchTime = VALUES(lastSearchTime)";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute([
+                       mb_substr($this->parameters['data']['keyword'], 0, 191),
+                       ($this->parameters['data']['searches'] ?? 1),
+                       ($this->parameters['data']['lastSearchTime'] ?? TIME_NOW),
+               ]);
+       }
 }
index 62b6c128533047474c81ccefbb7d3a3d903fc556..a9f84e9b65f91e8e72d3b1ede34eaf77c1b85a38 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\form;
+use wcf\data\search\keyword\SearchKeywordAction;
 use wcf\data\search\Search;
 use wcf\data\search\SearchAction;
 use wcf\system\exception\IllegalLinkException;
@@ -370,7 +371,9 @@ class SearchForm extends AbstractCaptchaForm {
                }
                // save keyword
                if (!empty($this->query)) {
-                       SearchKeywordManager::getInstance()->add($this->query);
+                       (new SearchKeywordAction([], 'upsert', ['data' => [
+                               'keyword' => $this->query,
+                       ]]))->executeAction();
                }
                $this->saved();
                
index 0a795953bd3d3f7f7df76b96a3a9aa1080cfb9d8..380e772aac6458b2a0add078ea0df65bd915ae81 100644 (file)
@@ -1,9 +1,7 @@
 <?php
 namespace wcf\system\search;
-use wcf\data\search\keyword\SearchKeyword;
 use wcf\data\search\keyword\SearchKeywordAction;
 use wcf\system\SingletonFactory;
-use wcf\system\WCF;
 
 /**
  * Manages the search keywords.
@@ -12,6 +10,7 @@ use wcf\system\WCF;
  * @copyright  2001-2019 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    WoltLabSuite\Core\System\Search
+ * @deprecated  5.2
  */
 class SearchKeywordManager extends SingletonFactory {
        /**
@@ -20,28 +19,8 @@ class SearchKeywordManager extends SingletonFactory {
         * @param       string          $keyword
         */
        public function add($keyword) {
-               $keyword = mb_substr($keyword, 0, 191);
-               
-               // search existing entry
-               $sql = "SELECT  *
-                       FROM    wcf".WCF_N."_search_keyword
-                       WHERE   keyword = ?";
-               $statement = WCF::getDB()->prepareStatement($sql);
-               $statement->execute([$keyword]);
-               if (($object = $statement->fetchObject(SearchKeyword::class)) !== null) {
-                       $action = new SearchKeywordAction([$object], 'update', ['data' => [
-                               'searches' => $object->searches + 1,
-                               'lastSearchTime' => TIME_NOW
-                       ]]);
-                       $action->executeAction();
-               }
-               else {
-                       $action = new SearchKeywordAction([], 'create', ['data' => [
-                               'keyword' => $keyword,
-                               'searches' => 1,
-                               'lastSearchTime' => TIME_NOW
-                       ]]);
-                       $action->executeAction();
-               }
+               (new SearchKeywordAction([], 'upsert', ['data' => [
+                       'keyword' => $keyword,
+               ]]))->executeAction();
        }
 }