From 32306a78d2e53bbbc864f3eabda9a2f1a3dd2322 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 16 Nov 2013 21:03:25 +0100 Subject: SQL : grosse mise à jour avec mise en cache du nombre d'articles lus/non-lus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Mise en cache du nombre d'articles lus et non-lus par flux, via `f.cache_nbEntries, f.cache_nbUnreads` pour de biens meilleures performances * Implémente https://github.com/marienfressinaud/FreshRSS/issues/268 * Révision de la plupart des requêtes de modification en conséquence * En cas d'affichage `not_read`, évite de faire une requête si on sait déjà qu'il n'y a pas d'article non lu et fait directement un affichage `all`. * Appelle `cleanOldEntries` seulement une fois de temps en temps aléatoirement (1 fois sur 30 actuellement) pour économiser les ressources, et avant les insertions pour plus de robustesse. * Utilisation des transactions lors de mises à jour multiples et liées * Lors de requêtes de modifications, retourne le nombre de lignes impactées plutôt qu'un booléen en cas de succès * Suppression de code oublié relatif à is_public qui n'est plus utilisé --- app/controllers/entryController.php | 14 +-- app/controllers/feedController.php | 22 +++- app/controllers/indexController.php | 27 ++++- app/models/Category.php | 11 +- app/models/EntriesGetter.php | 8 -- app/models/Entry.php | 228 ++++++++++++++++++++++++++++-------- app/models/Feed.php | 84 +++++++------ lib/minz/dao/Model_pdo.php | 10 ++ public/install.php | 24 ++-- 9 files changed, 294 insertions(+), 134 deletions(-) diff --git a/app/controllers/entryController.php b/app/controllers/entryController.php index 1ba9cc3cd..4d2d92c1b 100755 --- a/app/controllers/entryController.php +++ b/app/controllers/entryController.php @@ -43,28 +43,27 @@ class entryController extends ActionController { $entryDAO = new EntryDAO (); if ($id == false) { if (!$get) { - $entryDAO->markReadEntries ($is_read, $dateMax); + $entryDAO->markReadEntries ($dateMax); } else { $typeGet = $get[0]; $get = substr ($get, 2); if ($typeGet == 'c') { - $entryDAO->markReadCat ($get, $is_read, $dateMax); + $entryDAO->markReadCat ($get, $dateMax); $this->params = array ('get' => $nextGet); } elseif ($typeGet == 'f') { - $entryDAO->markReadFeed ($get, $is_read, $dateMax); + $entryDAO->markReadFeed ($get, $dateMax); $this->params = array ('get' => $nextGet); } } - // notif $notif = array ( 'type' => 'good', 'content' => Translate::t ('feeds_marked_read') ); Session::_param ('notification', $notif); } else { - $entryDAO->updateEntry ($id, array ('is_read' => $is_read)); + $entryDAO->markRead ($id, $is_read); } } @@ -74,10 +73,7 @@ class entryController extends ActionController { $id = Request::param ('id'); if ($id) { $entryDAO = new EntryDAO (); - $values = array ( - 'is_favorite' => (bool)(Request::param ('is_favorite')), - ); - $entryDAO->updateEntry ($id, $values); + $entryDAO->markFavorite ($id, Request::param ('is_favorite')); } } diff --git a/app/controllers/feedController.php b/app/controllers/feedController.php index 76fca8828..0ba82631c 100755 --- a/app/controllers/feedController.php +++ b/app/controllers/feedController.php @@ -33,6 +33,7 @@ class feedController extends ActionController { $pass = Request::param ('password'); $params = array (); + $transactionStarted = false; try { $feed = new Feed ($url); $feed->_category ($cat); @@ -79,6 +80,8 @@ class feedController extends ActionController { $nb_month_old = $this->view->conf->oldEntries (); $date_min = time () - (60 * 60 * 24 * 30 * $nb_month_old); + $transactionStarted = true; + $feedDAO->beginTransaction (); // on ajoute les articles en masse sans vérification foreach ($entries as $entry) { if ($entry->date (true) >= $date_min || @@ -87,6 +90,9 @@ class feedController extends ActionController { $entryDAO->addEntry ($values); } } + $feedDAO->updateLastUpdate ($feed->id ()); + $feedDAO->commit (); + $transactionStarted = false; // ok, ajout terminé $notif = array ( @@ -121,6 +127,9 @@ class feedController extends ActionController { ); Session::_param ('notification', $notif); } + if ($transactionStarted) { + $feedDAO->rollBack (); + } Request::forward (array ('c' => 'configure', 'a' => 'feed', 'params' => $params), true); } @@ -149,6 +158,13 @@ class feedController extends ActionController { // on calcule la date des articles les plus anciens qu'on accepte $nb_month_old = $this->view->conf->oldEntries (); $date_min = time () - (60 * 60 * 24 * 30 * $nb_month_old); + if (rand(0, 30) === 1) { + Minz_Log::record ('CleanOldEntries', Minz_Log::NOTICE); //TODO: Remove + if ($entryDAO->cleanOldEntries ($date_min) > 0) { + Minz_Log::record ('UpdateCachedValues', Minz_Log::NOTICE); //TODO: Remove + $feedDAO->updateCachedValues (); + } + } $i = 0; $flux_update = 0; @@ -165,6 +181,7 @@ class feedController extends ActionController { // car demanderait plus de ressources // La BDD refusera l'ajout de son côté car l'id doit être // unique + $feedDAO->beginTransaction (); foreach ($entries as $entry) { if ((!isset ($existingIds[$entry->id ()])) && ($entry->date (true) >= $date_min || @@ -176,10 +193,11 @@ class feedController extends ActionController { // on indique que le flux vient d'être mis à jour en BDD $feedDAO->updateLastUpdate ($feed->id ()); + $feedDAO->commit (); $flux_update++; } catch (FeedException $e) { Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); - $feedDAO->isInError ($feed->id ()); + $feedDAO->updateLastUpdate ($feed->id (), 1); } // On arrête à 10 flux pour ne pas surcharger le serveur @@ -190,8 +208,6 @@ class feedController extends ActionController { } } - $entryDAO->cleanOldEntries ($nb_month_old); - $url = array (); if ($flux_update === 1) { // on a mis un seul flux à jour diff --git a/app/controllers/indexController.php b/app/controllers/indexController.php index 7b5dbd264..10c29bc7b 100755 --- a/app/controllers/indexController.php +++ b/app/controllers/indexController.php @@ -90,6 +90,30 @@ class indexController extends ActionController { $nb = Request::param ('nb', $this->view->conf->postsPerPage ()); $first = Request::param ('next', ''); + if ($state === 'not_read') { //Any unread article in this category at all? + switch ($type['type']) { + case 'all': + $hasUnread = $this->view->nb_not_read > 0; + break; + case 'favoris': + $hasUnread = $this->view->nb_favorites['unread'] > 0; + break; + case 'c': + $hasUnread = (!isset($this->view->cat_aside[$type['id']])) || ($this->view->cat_aside[$type['id']]->nbNotRead() > 0); + break; + case 'f': + $myFeed = HelperCategory::findFeed($this->view->cat_aside, $type['id']); + $hasUnread = ($myFeed === null) || ($myFeed->nbNotRead() > 0); + break; + default: + $hasUnread = true; + break; + } + if (!$hasUnread) { + $this->view->state = $state = 'all'; + } + } + try { // EntriesGetter permet de déporter la complexité du filtrage $getter = new EntriesGetter ($type, $state, $filter, $order, $nb, $first); @@ -98,7 +122,8 @@ class indexController extends ActionController { // Si on a récupéré aucun article "non lus" // on essaye de récupérer tous les articles - if ($state == 'not_read' && $entries->isEmpty ()) { + if ($state === 'not_read' && $entries->isEmpty ()) { //TODO: Remove in v0.8 + Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::NOTICE); //TODO: Consider adding a Minz_Log::DEBUG level $this->view->state = 'all'; $getter->_state ('all'); $getter->execute (); diff --git a/app/models/Category.php b/app/models/Category.php index 9b36b3bcc..6e61b5a0e 100755 --- a/app/models/Category.php +++ b/app/models/Category.php @@ -100,7 +100,7 @@ class CategoryDAO extends Model_pdo { ); if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); @@ -119,7 +119,7 @@ class CategoryDAO extends Model_pdo { ); if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); @@ -134,7 +134,7 @@ class CategoryDAO extends Model_pdo { $values = array ($id); if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); @@ -179,12 +179,9 @@ class CategoryDAO extends Model_pdo { if ($prePopulateFeeds) { $sql = 'SELECT c.id AS c_id, c.name AS c_name, ' . ($details ? 'c.color AS c_color, ' : '') - . 'COUNT(CASE WHEN e.is_read = 0 THEN 1 END) AS nbNotRead, ' - . 'COUNT(e.id) AS nbEntries, ' - . ($details ? 'f.* ' : 'f.id, f.name, f.website, f.priority, f.error ') + . ($details ? 'f.* ' : 'f.id, f.name, f.website, f.priority, f.error, f.cache_nbEntries, f.cache_nbUnreads ') . 'FROM ' . $this->prefix . 'category c ' . 'LEFT OUTER JOIN ' . $this->prefix . 'feed f ON f.category = c.id ' - . 'LEFT OUTER JOIN ' . $this->prefix . 'entry e ON e.id_feed = f.id ' . 'GROUP BY f.id ' . 'ORDER BY c.name, f.name'; $stm = $this->bd->prepare ($sql); diff --git a/app/models/EntriesGetter.php b/app/models/EntriesGetter.php index 803aad732..ce026f252 100644 --- a/app/models/EntriesGetter.php +++ b/app/models/EntriesGetter.php @@ -117,14 +117,6 @@ class EntriesGetter { $sqlLimit ); break; - case 'public': - list ($this->entries, $this->next) = $entryDAO->listPublic ( - $this->state, - $this->order, - $this->first, - $sqlLimit - ); - break; case 'c': list ($this->entries, $this->next) = $entryDAO->listByCategory ( $this->type['id'], diff --git a/app/models/Entry.php b/app/models/Entry.php index c6715a431..8b817cc14 100755 --- a/app/models/Entry.php +++ b/app/models/Entry.php @@ -213,17 +213,17 @@ class EntryDAO extends Model_pdo { ); if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); if ((int)($info[0] / 1000) !== 23) { //Filter out "SQLSTATE Class code 23: Constraint Violation" because of expected duplicate entries - Minz_Log::record ('SQL error ' . $info[0] . ': ' . $info[1] . ' ' . $info[2], Minz_Log::NOTICE); //TODO: Consider adding a Minz_Log::DEBUG level + Minz_Log::record ('SQL error ' . $info[0] . ': ' . $info[1] . ' ' . $info[2], Minz_Log::ERROR); } return false; } } - public function updateEntry ($id, $valuesTmp) { + /*public function updateEntry ($id, $valuesTmp) { if (isset ($valuesTmp['content'])) { $valuesTmp['content'] = base64_encode (gzdeflate (serialize ($valuesTmp['content']))); } @@ -243,73 +243,201 @@ class EntryDAO extends Model_pdo { $values[] = $id; if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); return false; } - } - - public function markReadEntries ($read, $dateMax = 0) { - $sql = 'UPDATE ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id SET is_read = ? WHERE priority > 0'; - - $values = array ($read); - if ($dateMax > 0) { - $sql .= ' AND date < ?'; - $values[] = $dateMax; - } - + }*/ + public function markFavorite ($id, $is_favorite = true) { + $sql = 'UPDATE ' . $this->prefix . 'entry e ' + . 'SET e.is_favorite = ? ' + . 'WHERE e.id=?'; + $values = array ($is_favorite ? 1 : 0, $id); $stm = $this->bd->prepare ($sql); if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); return false; } } - public function markReadCat ($id, $read, $dateMax = 0) { - $sql = 'UPDATE ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id SET is_read = ? WHERE category = ?'; - - $values = array ($read, $id); - if ($dateMax > 0) { - $sql .= ' AND date < ?'; - $values[] = $dateMax; - } - + public function markRead ($id, $is_read = true) { + $sql = 'UPDATE ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id ' + . 'SET e.is_read = ?,' + . 'f.cache_nbUnreads=f.cache_nbUnreads' . ($is_read ? '-' : '+') . '1 ' + . 'WHERE e.id=?'; + $values = array ($is_read ? 1 : 0, $id); $stm = $this->bd->prepare ($sql); if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); return false; } } - public function markReadFeed ($id, $read, $dateMax = 0) { - $sql = 'UPDATE ' . $this->prefix . 'entry SET is_read = ? WHERE id_feed = ?'; + public function markReadEntries ($dateMax = 0) { + if ($dateMax === 0) { + $sql = 'UPDATE ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id ' + . 'SET e.is_read = 1, f.cache_nbUnreads=0 ' + . 'WHERE e.is_read = 0 AND f.priority > 0'; + $stm = $this->bd->prepare ($sql); + if ($stm && $stm->execute ()) { + return $stm->rowCount(); + } else { + $info = $stm->errorInfo(); + Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + return false; + } + } else { + $this->bd->beginTransaction (); + + $sql = 'UPDATE ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id ' + . 'SET e.is_read = 1 ' + . 'WHERE e.is_read = 0 AND e.date < ? AND f.priority > 0'; + $values = array ($dateMax); + $stm = $this->bd->prepare ($sql); + if (!($stm && $stm->execute ($values))) { + $info = $stm->errorInfo(); + Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack (); + return false; + } + $affected = $stm->rowCount(); + + if ($affected > 0) { + $sql = 'UPDATE freshrss_feed f ' + . 'LEFT OUTER JOIN (' + . 'SELECT e.id_feed, ' + . 'COUNT(*) AS nbUnreads ' + . 'FROM freshrss_entry e ' + . 'WHERE e.is_read = 0 ' + . 'GROUP BY e.id_feed' + . ') x ON x.id_feed=f.id ' + . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0)'; + $stm = $this->bd->prepare ($sql); + if (!($stm && $stm->execute ())) { + $info = $stm->errorInfo(); + Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack (); + return false; + } + } - $values = array ($read, $id); - if ($dateMax > 0) { - $sql .= ' AND date < ?'; - $values[] = $dateMax; + $this->bd->commit (); + return $affected; } + } + public function markReadCat ($id, $dateMax = 0) { + if ($dateMax === 0) { + $sql = 'UPDATE ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id ' + . 'SET e.is_read = 1, f.cache_nbUnreads=0 ' + . 'WHERE f.category = ? AND e.is_read = 0'; + $values = array ($id); + $stm = $this->bd->prepare ($sql); + if ($stm && $stm->execute ($values)) { + return $stm->rowCount(); + } else { + $info = $stm->errorInfo(); + Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + return false; + } + } else { + $this->bd->beginTransaction (); + + $sql = 'UPDATE ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id ' + . 'SET e.is_read = 1 ' + . 'WHERE f.category = ? AND e.is_read = 0 AND e.date < ?'; + $values = array ($id, $dateMax); + $stm = $this->bd->prepare ($sql); + if (!($stm && $stm->execute ($values))) { + $info = $stm->errorInfo(); + Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack (); + return false; + } + $affected = $stm->rowCount(); + + if ($affected > 0) { + $sql = 'UPDATE freshrss_feed f ' + . 'LEFT OUTER JOIN (' + . 'SELECT e.id_feed, ' + . 'COUNT(*) AS nbUnreads ' + . 'FROM freshrss_entry e ' + . 'WHERE e.is_read = 0 ' + . 'GROUP BY e.id_feed' + . ') x ON x.id_feed=f.id ' + . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0) ' + . 'WHERE f.category = ?'; + $values = array ($id); + $stm = $this->bd->prepare ($sql); + if (!($stm && $stm->execute ($values))) { + $info = $stm->errorInfo(); + Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack (); + return false; + } + } - $stm = $this->bd->prepare ($sql); - - if ($stm && $stm->execute ($values)) { - return true; + $this->bd->commit (); + return $affected; + } + } + public function markReadFeed ($id, $dateMax = 0) { + if ($dateMax === 0) { + $sql = 'UPDATE ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id ' + . 'SET e.is_read = 1, f.cache_nbUnreads=0 ' + . 'WHERE f.id=? AND e.is_read = 0'; + $values = array ($id); + $stm = $this->bd->prepare ($sql); + if ($stm && $stm->execute ($values)) { + return $stm->rowCount(); + } else { + $info = $stm->errorInfo(); + Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + return false; + } } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); - return false; + $this->bd->beginTransaction (); + + $sql = 'UPDATE ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id ' + . 'SET e.is_read = 1 ' + . 'WHERE f.id=? AND e.is_read = 0 AND e.date < ?'; + $values = array ($id, $dateMax); + $stm = $this->bd->prepare ($sql); + if (!($stm && $stm->execute ($values))) { + $info = $stm->errorInfo(); + Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack (); + return false; + } + $affected = $stm->rowCount(); + + if ($affected > 0) { + $sql = 'UPDATE ' . $this->prefix . 'feed f ' + . 'SET f.cache_nbUnreads=f.cache_nbUnreads-' . $affected + . ' WHERE f.id=?'; + $values = array ($id); + $stm = $this->bd->prepare ($sql); + if (!($stm && $stm->execute ($values))) { + $info = $stm->errorInfo(); + Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack (); + return false; + } + } + + $this->bd->commit (); + return $affected; } } - public function updateEntries ($valuesTmp) { + /*public function updateEntries ($valuesTmp) { if (isset ($valuesTmp['content'])) { $valuesTmp['content'] = base64_encode (gzdeflate (serialize ($valuesTmp['content']))); } @@ -328,25 +456,24 @@ class EntryDAO extends Model_pdo { } if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); return false; } - } + }*/ - public function cleanOldEntries ($nb_month) { - $date = 60 * 60 * 24 * 30 * $nb_month; + public function cleanOldEntries ($date_min) { $sql = 'DELETE e.* FROM ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id WHERE e.date <= ? AND e.is_favorite = 0 AND f.keep_history = 0'; $stm = $this->bd->prepare ($sql); $values = array ( - time () - $date + $date_min ); if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); @@ -393,16 +520,16 @@ class EntryDAO extends Model_pdo { } private function listWhere ($where, $state, $order, $limitFromId = '', $limitCount = '', $values = array ()) { - if ($state == 'not_read') { + if ($state === 'not_read') { $where .= ' AND is_read = 0'; - } elseif ($state == 'read') { + } elseif ($state === 'read') { $where .= ' AND is_read = 1'; } if (!empty($limitFromId)) { //TODO: Consider using LPAD(e.date, 11) //CONCAT is for cases when many entries have the same date $where .= ' AND CONCAT(e.date, e.id) ' . ($order === 'low_to_high' ? '<=' : '>=') . ' (SELECT CONCAT(s.date, s.id) FROM ' . $this->prefix . 'entry s WHERE s.id = "' . $limitFromId . '")'; } - if ($order == 'low_to_high') { + if ($order === 'low_to_high') { $order = ' DESC'; } else { $order = ''; @@ -430,9 +557,6 @@ class EntryDAO extends Model_pdo { public function listFavorites ($state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { return $this->listWhere (' WHERE is_favorite = 1', $state, $order, $limitFromId, $limitCount); } - public function listPublic ($state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { - return $this->listWhere (' WHERE is_public = 1', $state, $order, $limitFromId, $limitCount); - } public function listByCategory ($cat, $state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { return $this->listWhere (' WHERE category = ?', $state, $order, $limitFromId, $limitCount, array ($cat)); } diff --git a/app/models/Feed.php b/app/models/Feed.php index 407614f9f..ae3a9af83 100644 --- a/app/models/Feed.php +++ b/app/models/Feed.php @@ -337,7 +337,7 @@ class FeedDAO extends Model_pdo { ); if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); @@ -365,7 +365,7 @@ class FeedDAO extends Model_pdo { $values[] = $id; if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); @@ -373,34 +373,23 @@ class FeedDAO extends Model_pdo { } } - public function updateLastUpdate ($id) { - $sql = 'UPDATE ' . $this->prefix . 'feed SET lastUpdate=?, error=0 WHERE id=?'; - $stm = $this->bd->prepare ($sql); - - $values = array ( - time (), - $id - ); + public function updateLastUpdate ($id, $inError = 0) { + $sql = 'UPDATE ' . $this->prefix . 'feed f ' //2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE + . 'SET f.cache_nbEntries=(SELECT COUNT(e1.id) FROM ' . $this->prefix . 'entry e1 WHERE e1.id_feed=f.id),' + . 'f.cache_nbUnreads=(SELECT COUNT(e2.id) FROM ' . $this->prefix . 'entry e2 WHERE e2.id_feed=f.id AND e2.is_read=0),' + . 'lastUpdate=?, error=? ' + . 'WHERE f.id=?'; - if ($stm && $stm->execute ($values)) { - return true; - } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); - return false; - } - } - - public function isInError ($id) { - $sql = 'UPDATE ' . $this->prefix . 'feed SET error=1 WHERE id=?'; $stm = $this->bd->prepare ($sql); $values = array ( - $id + time (), + $inError, + $id, ); if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); @@ -424,7 +413,7 @@ class FeedDAO extends Model_pdo { ); if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); @@ -439,7 +428,7 @@ class FeedDAO extends Model_pdo { $values = array ($id); if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); @@ -453,7 +442,7 @@ class FeedDAO extends Model_pdo { $values = array ($id); if ($stm && $stm->execute ($values)) { - return true; + return $stm->rowCount(); } else { $info = $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); @@ -521,15 +510,6 @@ class FeedDAO extends Model_pdo { return HelperFeed::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); } - public function count () { //Is this used? - $sql = 'SELECT COUNT(*) AS count FROM ' . $this->prefix . 'feed'; - $stm = $this->bd->prepare ($sql); - $stm->execute (); - $res = $stm->fetchAll (PDO::FETCH_ASSOC); - - return $res[0]['count']; - } - public function countEntries ($id) { $sql = 'SELECT COUNT(*) AS count FROM ' . $this->prefix . 'entry WHERE id_feed=?'; $stm = $this->bd->prepare ($sql); @@ -539,8 +519,8 @@ class FeedDAO extends Model_pdo { return $res[0]['count']; } - public function countNotRead ($id) { //Is this used? - $sql = 'SELECT COUNT(*) AS count FROM ' . $this->prefix . 'entry WHERE is_read=0 AND id_feed=?'; + public function countNotRead ($id) { + $sql = 'SELECT COUNT(*) AS count FROM ' . $this->prefix . 'entry WHERE id_feed=? AND is_read=0'; $stm = $this->bd->prepare ($sql); $values = array ($id); $stm->execute ($values); @@ -548,6 +528,28 @@ class FeedDAO extends Model_pdo { return $res[0]['count']; } + public function updateCachedValues () { //For one single feed, call updateLastUpdate($id) + $sql = 'UPDATE freshrss_feed f ' + . 'INNER JOIN (' + . 'SELECT e.id_feed, ' + . 'COUNT(CASE WHEN e.is_read = 0 THEN 1 END) AS nbUnreads, ' + . 'COUNT(e.id) AS nbEntries ' + . 'FROM freshrss_entry e ' + . 'GROUP BY e.id_feed' + . ') x ON x.id_feed=f.id ' + . 'SET f.cache_nbEntries=x.nbEntries, f.cache_nbUnreads=x.nbUnreads'; + $stm = $this->bd->prepare ($sql); + + $values = array ($feed_id); + + if ($stm && $stm->execute ($values)) { + return $stm->rowCount(); + } else { + $info = $stm->errorInfo(); + Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + return false; + } + } } class HelperFeed { @@ -577,12 +579,8 @@ class HelperFeed { $myFeed->_httpAuth (isset($dao['httpAuth']) ? base64_decode ($dao['httpAuth']) : ''); $myFeed->_error ($dao['error']); $myFeed->_keepHistory (isset($dao['keep_history']) ? $dao['keep_history'] : ''); - if (isset ($dao['nbNotRead'])) { - $myFeed->_nbNotRead ($dao['nbNotRead']); - } - if (isset ($dao['nbEntries'])) { - $myFeed->_nbEntries ($dao['nbEntries']); - } + $myFeed->_nbNotRead ($dao['cache_nbUnreads']); + $myFeed->_nbEntries ($dao['cache_nbEntries']); if (isset ($dao['id'])) { $myFeed->_id ($dao['id']); } diff --git a/lib/minz/dao/Model_pdo.php b/lib/minz/dao/Model_pdo.php index a91a4fa00..dd75153be 100755 --- a/lib/minz/dao/Model_pdo.php +++ b/lib/minz/dao/Model_pdo.php @@ -71,6 +71,16 @@ class Model_pdo { ); } } + + public function beginTransaction() { + $this->bd->beginTransaction(); + } + public function commit() { + $this->bd->commit(); + } + public function rollBack() { + $this->bd->rollBack(); + } } class FreshPDO extends PDO { diff --git a/public/install.php b/public/install.php index 457605224..47856b6a7 100644 --- a/public/install.php +++ b/public/install.php @@ -15,7 +15,7 @@ define ('SQL_REQ_CAT', 'CREATE TABLE IF NOT EXISTS `%scategory` ( `name` varchar(255) NOT NULL, `color` char(7) NOT NULL, PRIMARY KEY (`id`), - INDEX (`name`) + INDEX (`name`) //v0.7 );'); define ('SQL_REQ_FEED', 'CREATE TABLE IF NOT EXISTS `%sfeed` ( @@ -26,16 +26,18 @@ define ('SQL_REQ_FEED', 'CREATE TABLE IF NOT EXISTS `%sfeed` ( `website` varchar(255) NOT NULL, `description` text NOT NULL, `lastUpdate` int(11) NOT NULL, - `priority` tinyint(2) NOT NULL DEFAULT \'10\', + `priority` tinyint(2) NOT NULL DEFAULT 10, `pathEntries` varchar(511) DEFAULT NULL, `httpAuth` varchar(511) DEFAULT NULL, - `error` boolean NOT NULL DEFAULT \'0\', - `keep_history` boolean NOT NULL DEFAULT \'0\', + `error` boolean NOT NULL DEFAULT 0, + `keep_history` boolean NOT NULL DEFAULT 0, + `cache_nbEntries` int NOT NULL DEFAULT 0, //v0.7 + `cache_nbUnreads` int NOT NULL DEFAULT 0, //v0.7 PRIMARY KEY (`id`), FOREIGN KEY (`category`) REFERENCES %scategory(id) ON DELETE SET NULL ON UPDATE CASCADE, - INDEX (`name`), - INDEX (`priority`), - INDEX (`keep_history`) + INDEX (`name`), //v0.7 + INDEX (`priority`), //v0.7 + INDEX (`keep_history`) //v0.7 );'); define ('SQL_REQ_ENTRY', 'CREATE TABLE IF NOT EXISTS `%sentry` ( @@ -46,14 +48,14 @@ define ('SQL_REQ_ENTRY', 'CREATE TABLE IF NOT EXISTS `%sentry` ( `content` text NOT NULL, `link` varchar(1023) NOT NULL, `date` int(11) NOT NULL, - `is_read` boolean NOT NULL DEFAULT \'0\', - `is_favorite` boolean NOT NULL DEFAULT \'0\', + `is_read` boolean NOT NULL DEFAULT 0, + `is_favorite` boolean NOT NULL DEFAULT 0, `id_feed` char(6) NOT NULL, `tags` varchar(1023) NOT NULL, PRIMARY KEY (`id`), FOREIGN KEY (`id_feed`) REFERENCES %sfeed(id) ON DELETE CASCADE ON UPDATE CASCADE, - INDEX (`is_favorite`), - INDEX (`is_read`) + INDEX (`is_favorite`), //v0.7 + INDEX (`is_read`) //v0.7 );'); -- cgit v1.2.3