Prechádzať zdrojové kódy

Add support for SQLite

https://github.com/marienfressinaud/FreshRSS/issues/100
Warning: MySQL has been changed too, so bugs may have been introduced
Alexandre Alapetite 11 rokov pred
rodič
commit
805c91da98

+ 2 - 0
CHANGELOG

@@ -2,6 +2,8 @@
 
 ## 2014-xx-xx FreshRSS 0.7.x
 
+* SQL
+	* Add support for SQLite (beta) in addition to MySQL
 * SimplePie
 	* Complies with HTTP "301 Moved Permanently" responses by automatically updating the URL of feeds that have changed address.
 

+ 1 - 1
README.md

@@ -35,7 +35,7 @@ Privilégiez pour cela des demandes sur GitHub
 * PHP 5.2.1+ (PHP 5.3.7+ recommandé)
 	* Requis : [PDO_MySQL](http://php.net/pdo-mysql), [cURL](http://php.net/curl), [LibXML](http://php.net/xml), [PCRE](http://php.net/pcre), [ctype](http://php.net/ctype)
 	* Recommandés : [JSON](http://php.net/json), [zlib](http://php.net/zlib), [mbstring](http://php.net/mbstring), [iconv](http://php.net/iconv), [Zip](http://php.net/zip)
-* MySQL 5.0.3+ (ou SQLite 3.7.4+ à venir)
+* MySQL 5.0.3+ (recommandé) ou SQLite 3.7.4+ (en bêta)
 * Un navigateur Web récent tel Firefox 4+, Chrome, Opera, Safari, Internet Explorer 9+
 	* Fonctionne aussi sur mobile
 

+ 2 - 2
app/Models/CategoryDAO.php

@@ -102,7 +102,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo {
 			$sql = 'SELECT c.id AS c_id, c.name AS c_name, '
 			     . ($details ? 'f.* ' : 'f.id, f.name, f.url, 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 . 'feed` f ON f.category=c.id '
 			     . 'GROUP BY f.id '
 			     . 'ORDER BY c.name, f.name';
 			$stm = $this->bd->prepare ($sql);
@@ -166,7 +166,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo {
 	}
 
 	public function countNotRead ($id) {
-		$sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE category=? AND e.is_read=0';
+		$sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE category=? AND e.is_read=0';
 		$stm = $this->bd->prepare ($sql);
 		$values = array ($id);
 		$stm->execute ($values);

+ 214 - 312
app/Models/EntryDAO.php

@@ -6,16 +6,16 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 		return parent::$sharedDbType !== 'sqlite';
 	}
 
-	public function addEntry ($valuesTmp) {
+	public function addEntry($valuesTmp) {
 		$sql = 'INSERT INTO `' . $this->prefix . 'entry`(id, guid, title, author, '
 		     . ($this->isCompressed() ? 'content_bin' : 'content')
 		     . ', link, date, is_read, is_favorite, id_feed, tags) '
 		     . 'VALUES(?, ?, ?, ?, '
 		     . ($this->isCompressed() ? 'COMPRESS(?)' : '?')
 		     . ', ?, ?, ?, ?, ?, ?)';
-		$stm = $this->bd->prepare ($sql);
+		$stm = $this->bd->prepare($sql);
 
-		$values = array (
+		$values = array(
 			$valuesTmp['id'],
 			substr($valuesTmp['guid'], 0, 760),
 			substr($valuesTmp['title'], 0, 255),
@@ -29,7 +29,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 			substr($valuesTmp['tags'], 0, 1023),
 		);
 
-		if ($stm && $stm->execute ($values)) {
+		if ($stm && $stm->execute($values)) {
 			return $this->bd->lastInsertId();
 		} else {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
@@ -78,13 +78,13 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 		if (!is_array($ids)) {
 			$ids = array($ids);
 		}
-		$sql = 'UPDATE `' . $this->prefix . 'entry` e '
-		     . 'SET e.is_favorite = ? '
-		     . 'WHERE e.id IN (' . str_repeat('?,', count($ids) - 1). '?)';
-		$values = array ($is_favorite ? 1 : 0);
+		$sql = 'UPDATE `' . $this->prefix . 'entry` '
+		     . 'SET is_favorite=? '
+		     . 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?)';
+		$values = array($is_favorite ? 1 : 0);
 		$values = array_merge($values, $ids);
-		$stm = $this->bd->prepare ($sql);
-		if ($stm && $stm->execute ($values)) {
+		$stm = $this->bd->prepare($sql);
+		if ($stm && $stm->execute($values)) {
 			return $stm->rowCount();
 		} else {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
@@ -93,8 +93,38 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 		}
 	}
 
+	protected function updateCacheUnreads($catId = false, $feedId = false) {
+		$sql = 'UPDATE `' . $this->prefix . 'feed` f '
+		 . 'LEFT OUTER JOIN ('
+		 .	'SELECT e.id_feed, '
+		 .	'COUNT(*) AS nbUnreads '
+		 .	'FROM `' . $this->prefix . '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 1';
+		$values = array();
+		if ($feedId !== false) {
+			$sql .= ' AND f.id=?';
+			$values[] = $id;
+		}
+		if ($catId !== false) {
+			$sql .= ' AND f.category=?';
+			$values[] = $catId;
+		}
+		$stm = $this->bd->prepare($sql);
+		if ($stm && $stm->execute($values)) {
+			return true;
+		} else {
+			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+			Minz_Log::record('SQL error updateCacheUnreads: ' . $info[2], Minz_Log::ERROR);
+			return false;
+		}
+	}
+
 	public function markRead($ids, $is_read = true) {
-		if (is_array($ids)) {	//Many IDs at once
+		if (is_array($ids)) {	//Many IDs at once (used by API)
 			if (count($ids) < 6) {	//Speed heuristics
 				$affected = 0;
 				foreach ($ids as $id) {
@@ -104,9 +134,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 			}
 
 			$this->bd->beginTransaction();
-			$sql = 'UPDATE `' . $this->prefix . 'entry` e '
-				 . 'SET e.is_read = ? '
-				 . 'WHERE e.id IN (' . str_repeat('?,', count($ids) - 1). '?)';
+			$sql = 'UPDATE `' . $this->prefix . 'entry` '
+				 . 'SET is_read=? '
+				 . 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?)';
 			$values = array($is_read ? 1 : 0);
 			$values = array_merge($values, $ids);
 			$stm = $this->bd->prepare($sql);
@@ -117,34 +147,18 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 				return false;
 			}
 			$affected = $stm->rowCount();
-
-			if ($affected > 0) {
-				$sql = 'UPDATE `' . $this->prefix . '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 `' . $this->prefix . '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);
-				if (!($stm && $stm->execute())) {
-					$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
-					Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR);
-					$this->bd->rollBack();
-					return false;
-				}
+			if (($affected > 0) && (!$this->updateCacheUnreads(false, false))) {
+				$this->bd->rollBack();
+				return false;
 			}
-
 			$this->bd->commit();
 			return $affected;
 		} else {
-			$sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
-				 . 'SET e.is_read = ?,'
+			$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=? AND e.is_read<>?';
-			$values = array($is_read ? 1 : 0, $ids, $is_read ? 1 : 0);
+				 . 'WHERE e.id=? AND e.is_read=?';
+			$values = array($is_read ? 1 : 0, $ids, $is_read ? 0 : 1);
 			$stm = $this->bd->prepare($sql);
 			if ($stm && $stm->execute($values)) {
 				return $stm->rowCount();
@@ -156,267 +170,139 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 		}
 	}
 
-	public function markReadEntries ($idMax = 0, $onlyFavorites = false, $priorityMin = 0) {
+	public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0) {
 		if ($idMax == 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';
-			if ($onlyFavorites) {
-				$sql .= ' AND e.is_favorite = 1';
-			} elseif ($priorityMin >= 0) {
-				$sql .= ' AND f.priority > ' . intval($priorityMin);
-			}
-			$stm = $this->bd->prepare ($sql);
-			if ($stm && $stm->execute ()) {
-				return $stm->rowCount();
-			} else {
-				$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
-				Minz_Log::record('SQL error markReadEntries: ' . $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.id <= ?';
-			if ($onlyFavorites) {
-				$sql .= ' AND e.is_favorite = 1';
-			} elseif ($priorityMin >= 0) {
-				$sql .= ' AND f.priority > ' . intval($priorityMin);
-			}
-			$values = array ($idMax);
-			$stm = $this->bd->prepare ($sql);
-			if (!($stm && $stm->execute ($values))) {
-				$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
-				Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR);
-				$this->bd->rollBack ();
-				return false;
-			}
-			$affected = $stm->rowCount();
-
-			if ($affected > 0) {
-				$sql = 'UPDATE `' . $this->prefix . 'feed` f '
-			     . 'LEFT OUTER JOIN ('
-			     .	'SELECT e.id_feed, '
-			     .	'COUNT(*) AS nbUnreads '
-			     .	'FROM `' . $this->prefix . '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 == null ? array(2 => 'syntax error') : $stm->errorInfo();
-					Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR);
-					$this->bd->rollBack ();
-					return false;
-				}
-			}
-
-			$this->bd->commit ();
-			return $affected;
+			$idMax = time() . '000000';
+			Minz_Log::record($nb . 'Calling markReadEntries(0) is deprecated!', Minz_Log::DEBUG);
+		}
+		$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.id <= ?';
+		if ($onlyFavorites) {
+			$sql .= ' AND e.is_favorite=1';
+		} elseif ($priorityMin >= 0) {
+			$sql .= ' AND f.priority > ' . intval($priorityMin);
+		}
+		$values = array($idMax);
+		$stm = $this->bd->prepare($sql);
+		if (!($stm && $stm->execute($values))) {
+			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+			Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR);
+			$this->bd->rollBack();
+			return false;
+		}
+		$affected = $stm->rowCount();
+		if (($affected > 0) && (!$this->updateCacheUnreads(false, false))) {
+			$this->bd->rollBack();
+			return false;
 		}
+		$this->bd->commit();
+		return $affected;
 	}
 
-	public function markReadCat ($id, $idMax = 0) {
+	public function markReadCat($id, $idMax = 0) {
 		if ($idMax == 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 == null ? array(2 => 'syntax error') : $stm->errorInfo();
-				Minz_Log::record('SQL error markReadCat: ' . $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.id <= ?';
-			$values = array ($id, $idMax);
-			$stm = $this->bd->prepare ($sql);
-			if (!($stm && $stm->execute ($values))) {
-				$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
-				Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR);
-				$this->bd->rollBack ();
-				return false;
-			}
-			$affected = $stm->rowCount();
-
-			if ($affected > 0) {
-				$sql = 'UPDATE `' . $this->prefix . 'feed` f '
-			     . 'LEFT OUTER JOIN ('
-			     .	'SELECT e.id_feed, '
-			     .	'COUNT(*) AS nbUnreads '
-			     .	'FROM `' . $this->prefix . '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 == null ? array(2 => 'syntax error') : $stm->errorInfo();
-					Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR);
-					$this->bd->rollBack ();
-					return false;
-				}
-			}
+			$idMax = time() . '000000';
+			Minz_Log::record($nb . 'Calling markReadCat(0) is deprecated!', Minz_Log::DEBUG);
+		}
+		$this->bd->beginTransaction();
 
-			$this->bd->commit ();
-			return $affected;
+		$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.id <= ?';
+		$values = array($id, $idMax);
+		$stm = $this->bd->prepare($sql);
+		if (!($stm && $stm->execute($values))) {
+			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+			Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR);
+			$this->bd->rollBack();
+			return false;
 		}
+		$affected = $stm->rowCount();
+		if (($affected > 0) && (!$this->updateCacheUnreads($id, false))) {
+			$this->bd->rollBack();
+			return false;
+		}
+		$this->bd->commit();
+		return $affected;
 	}
 
-	public function markReadCatName($name, $idMax = 0) {
+	public function markReadFeed($id, $idMax = 0) {
 		if ($idMax == 0) {
-			$sql = 'UPDATE `' . $this->prefix . 'entry` e '
-			     . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
-			     . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category '
-			     . 'SET e.is_read = 1, f.cache_nbUnreads=0 '
-			     . 'WHERE c.name = ?';
-			$values = array($name);
-			$stm = $this->bd->prepare($sql);
-			if ($stm && $stm->execute($values)) {
-				return $stm->rowCount();
-			} else {
-				$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
-				Minz_Log::record('SQL error markReadCatName: ' . $info[2], Minz_Log::ERROR);
-				return false;
-			}
-		} else {
-			$this->bd->beginTransaction();
+			$idMax = time() . '000000';
+			Minz_Log::record($nb . 'Calling markReadFeed(0) is deprecated!', Minz_Log::DEBUG);
+		}
+		$this->bd->beginTransaction();
 
-			$sql = 'UPDATE `' . $this->prefix . 'entry` e '
-			     . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
-			     . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category '
-			     . 'SET e.is_read = 1 '
-			     . 'WHERE c.name = ? AND e.id <= ?';
-			$values = array($name, $idMax);
+		$sql = 'UPDATE `' . $this->prefix . 'entry`  '
+			 . 'SET is_read=1 '
+			 . 'WHERE id_feed=? AND is_read=0 AND id <= ?';
+		$values = array($id, $idMax);
+		$stm = $this->bd->prepare($sql);
+		if (!($stm && $stm->execute($values))) {
+			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+			Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR);
+			$this->bd->rollBack();
+			return false;
+		}
+		$affected = $stm->rowCount();
+
+		if ($affected > 0) {
+			$sql = 'UPDATE `' . $this->prefix . 'feed` '
+				 . 'SET cache_nbUnreads=cache_nbUnreads-' . $affected
+				 . ' WHERE id=?';
+			$values = array($id);
 			$stm = $this->bd->prepare($sql);
 			if (!($stm && $stm->execute($values))) {
 				$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
-				Minz_Log::record('SQL error markReadCatName: ' . $info[2], Minz_Log::ERROR);
+				Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR);
 				$this->bd->rollBack();
 				return false;
 			}
-			$affected = $stm->rowCount();
-
-			if ($affected > 0) {
-				$sql = 'UPDATE `' . $this->prefix . 'feed` f '
-			     . 'LEFT OUTER JOIN ('
-			     .	'SELECT e.id_feed, '
-			     .	'COUNT(*) AS nbUnreads '
-			     .	'FROM `' . $this->prefix . 'entry` e '
-			     .	'WHERE e.is_read = 0 '
-			     .	'GROUP BY e.id_feed'
-			     . ') x ON x.id_feed=f.id '
-			     . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category '
-			     . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0) '
-			     . 'WHERE c.name = ?';
-				$values = array($name);
-				$stm = $this->bd->prepare($sql);
-				if (!($stm && $stm->execute($values))) {
-					$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
-					Minz_Log::record('SQL error markReadCatName: ' . $info[2], Minz_Log::ERROR);
-					$this->bd->rollBack();
-					return false;
-				}
-			}
-
-			$this->bd->commit();
-			return $affected;
 		}
-	}
 
-	public function markReadFeed ($id, $idMax = 0) {
-		if ($idMax == 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 == null ? array(2 => 'syntax error') : $stm->errorInfo();
-				Minz_Log::record('SQL error markReadFeed: ' . $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.id=? AND e.is_read = 0 AND e.id <= ?';
-			$values = array ($id, $idMax);
-			$stm = $this->bd->prepare ($sql);
-			if (!($stm && $stm->execute ($values))) {
-				$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
-				Minz_Log::record('SQL error markReadFeed: ' . $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 == null ? array(2 => 'syntax error') : $stm->errorInfo();
-					Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR);
-					$this->bd->rollBack ();
-					return false;
-				}
-			}
-
-			$this->bd->commit ();
-			return $affected;
-		}
+		$this->bd->commit();
+		return $affected;
 	}
 
-	public function searchByGuid ($feed_id, $id) {
+	public function searchByGuid($feed_id, $id) {
 		// un guid est unique pour un flux donné
 		$sql = 'SELECT id, guid, title, author, '
 		     . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
 		     . ', link, date, is_read, is_favorite, id_feed, tags '
 		     . 'FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid=?';
-		$stm = $this->bd->prepare ($sql);
+		$stm = $this->bd->prepare($sql);
 
-		$values = array (
+		$values = array(
 			$feed_id,
 			$id
 		);
 
-		$stm->execute ($values);
-		$res = $stm->fetchAll (PDO::FETCH_ASSOC);
-		$entries = self::daoToEntry ($res);
-		return isset ($entries[0]) ? $entries[0] : null;
+		$stm->execute($values);
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+		$entries = self::daoToEntry($res);
+		return isset($entries[0]) ? $entries[0] : null;
 	}
 
-	public function searchById ($id) {
+	public function searchById($id) {
 		$sql = 'SELECT id, guid, title, author, '
 		     . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
 		     . ', link, date, is_read, is_favorite, id_feed, tags '
 		     . 'FROM `' . $this->prefix . 'entry` WHERE id=?';
-		$stm = $this->bd->prepare ($sql);
+		$stm = $this->bd->prepare($sql);
 
-		$values = array ($id);
+		$values = array($id);
 
-		$stm->execute ($values);
-		$res = $stm->fetchAll (PDO::FETCH_ASSOC);
-		$entries = self::daoToEntry ($res);
-		return isset ($entries[0]) ? $entries[0] : null;
+		$stm->execute($values);
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+		$entries = self::daoToEntry($res);
+		return isset($entries[0]) ? $entries[0] : null;
+	}
+
+	protected function sqlConcat($s1, $s2) {
+		return 'CONCAT(' . $s1 . ',' . $s2 . ')';	//MySQL
 	}
 
 	private function sqlListWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) {
@@ -432,39 +318,39 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 				$joinFeed = true;
 				break;
 			case 's':	//Deprecated: use $state instead
-				$where .= 'e1.is_favorite = 1 ';
+				$where .= 'e1.is_favorite=1 ';
 				break;
 			case 'c':
-				$where .= 'f.category = ? ';
+				$where .= 'f.category=? ';
 				$values[] = intval($id);
 				$joinFeed = true;
 				break;
 			case 'f':
-				$where .= 'e1.id_feed = ? ';
+				$where .= 'e1.id_feed=? ';
 				$values[] = intval($id);
 				break;
 			case 'A':
 				$where .= '1 ';
 				break;
 			default:
-				throw new FreshRSS_EntriesGetter_Exception ('Bad type in Entry->listByType: [' . $type . ']!');
+				throw new FreshRSS_EntriesGetter_Exception('Bad type in Entry->listByType: [' . $type . ']!');
 		}
 
 		if ($state & FreshRSS_Entry::STATE_NOT_READ) {
 			if (!($state & FreshRSS_Entry::STATE_READ)) {
-				$where .= 'AND e1.is_read = 0 ';
+				$where .= 'AND e1.is_read=0 ';
 			}
 		}
 		elseif ($state & FreshRSS_Entry::STATE_READ) {
-			$where .= 'AND e1.is_read = 1 ';
+			$where .= 'AND e1.is_read=1 ';
 		}
 		if ($state & FreshRSS_Entry::STATE_FAVORITE) {
 			if (!($state & FreshRSS_Entry::STATE_NOT_FAVORITE)) {
-				$where .= 'AND e1.is_favorite = 1 ';
+				$where .= 'AND e1.is_favorite=1 ';
 			}
 		}
 		elseif ($state & FreshRSS_Entry::STATE_NOT_FAVORITE) {
-			$where .= 'AND e1.is_favorite = 0 ';
+			$where .= 'AND e1.is_favorite=0 ';
 		}
 
 		switch ($order) {
@@ -472,7 +358,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 			case 'ASC':
 				break;
 			default:
-				throw new FreshRSS_EntriesGetter_Exception ('Bad order in Entry->listByType: [' . $order . ']!');
+				throw new FreshRSS_EntriesGetter_Exception('Bad order in Entry->listByType: [' . $order . ']!');
 		}
 		if ($firstId !== '') {
 			$where .= 'AND e1.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' ';
@@ -480,7 +366,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 		if (($date_min > 0) && ($type !== 's')) {
 			$where .= 'AND (e1.id >= ' . $date_min . '000000';
 			if ($showOlderUnreadsorFavorites) {	//Lax date constraint
-				$where .= ' OR e1.is_read = 0 OR e1.is_favorite = 1 OR (f.keep_history <> 0';
+				$where .= ' OR e1.is_read=0 OR e1.is_favorite=1 OR (f.keep_history <> 0';
 				if (intval($keepHistoryDefault) === 0) {
 					$where .= ' AND f.keep_history <> -2';	//default
 				}
@@ -533,7 +419,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 						$search .= 'AND e1.tags LIKE ? ';
 						$values[] = '%' . $word .'%';
 					} else {
-						$search .= 'AND CONCAT(e1.title, ' . ($this->isCompressed() ? 'UNCOMPRESS(content_bin)' : 'content') . ') LIKE ? ';
+						$search .= 'AND ' . $this->sqlconcat('e1.title', $this->isCompressed() ? 'UNCOMPRESS(content_bin)' : 'content') . ' LIKE ? ';
 						$values[] = '%' . $word .'%';
 					}
 				}
@@ -542,7 +428,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 
 		return array($values,
 			'SELECT e1.id FROM `' . $this->prefix . 'entry` e1 '
-			. ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e1.id_feed = f.id ' : '')
+			. ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e1.id_feed=f.id ' : '')
 			. 'WHERE ' . $where
 			. $search
 			. 'ORDER BY e1.id ' . $order
@@ -558,13 +444,13 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 		     . 'FROM `' . $this->prefix . 'entry` e '
 		     . 'INNER JOIN ('
 		     . $sql
-		     . ') e2 ON e2.id = e.id '
+		     . ') e2 ON e2.id=e.id '
 		     . 'ORDER BY e.id ' . $order;
 
-		$stm = $this->bd->prepare ($sql);
-		$stm->execute ($values);
+		$stm = $this->bd->prepare($sql);
+		$stm->execute($values);
 
-		return self::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC));
+		return self::daoToEntry($stm->fetchAll(PDO::FETCH_ASSOC));
 	}
 
 	public function listIdsWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) {	//For API
@@ -578,69 +464,85 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 
 	public function listLastGuidsByFeed($id, $n) {
 		$sql = 'SELECT guid FROM `' . $this->prefix . 'entry` WHERE id_feed=? ORDER BY id DESC LIMIT ' . intval($n);
-		$stm = $this->bd->prepare ($sql);
-		$values = array ($id);
-		$stm->execute ($values);
-		return $stm->fetchAll (PDO::FETCH_COLUMN, 0);
+		$stm = $this->bd->prepare($sql);
+		$values = array($id);
+		$stm->execute($values);
+		return $stm->fetchAll(PDO::FETCH_COLUMN, 0);
 	}
 
-	public function countUnreadRead () {
-		$sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE priority > 0'
-		     . ' UNION SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE priority > 0 AND is_read = 0';
-		$stm = $this->bd->prepare ($sql);
-		$stm->execute ();
-		$res = $stm->fetchAll (PDO::FETCH_COLUMN, 0);
+	public function countUnreadRead() {
+		$sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE priority > 0'
+		     . ' UNION SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE priority > 0 AND is_read=0';
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
 		$all = empty($res[0]) ? 0 : $res[0];
 		$unread = empty($res[1]) ? 0 : $res[1];
 		return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread);
 	}
-	public function count ($minPriority = null) {
-		$sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id';
+	public function count($minPriority = null) {
+		$sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id';
 		if ($minPriority !== null) {
 			$sql = ' WHERE priority > ' . intval($minPriority);
 		}
-		$stm = $this->bd->prepare ($sql);
-		$stm->execute ();
-		$res = $stm->fetchAll (PDO::FETCH_COLUMN, 0);
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
 		return $res[0];
 	}
-	public function countNotRead ($minPriority = null) {
-		$sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE is_read = 0';
+	public function countNotRead($minPriority = null) {
+		$sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE is_read=0';
 		if ($minPriority !== null) {
 			$sql = ' AND priority > ' . intval($minPriority);
 		}
-		$stm = $this->bd->prepare ($sql);
-		$stm->execute ();
-		$res = $stm->fetchAll (PDO::FETCH_COLUMN, 0);
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
 		return $res[0];
 	}
 
-	public function countUnreadReadFavorites () {
-		$sql = 'SELECT COUNT(id) FROM `' . $this->prefix . 'entry` WHERE is_favorite=1'
-		     . ' UNION SELECT COUNT(id) FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 AND is_read = 0';
-		$stm = $this->bd->prepare ($sql);
-		$stm->execute ();
-		$res = $stm->fetchAll (PDO::FETCH_COLUMN, 0);
+	public function countUnreadReadFavorites() {
+		$sql = 'SELECT c FROM ('
+		     .	'SELECT COUNT(id) AS c, 1 as o FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 '
+		     .	'UNION SELECT COUNT(id) AS c, 2 AS o FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 AND is_read=0'
+		     .	') u ORDER BY o';
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
 		$all = empty($res[0]) ? 0 : $res[0];
 		$unread = empty($res[1]) ? 0 : $res[1];
 		return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread);
 	}
 
 	public function optimizeTable() {
-		$sql = 'OPTIMIZE TABLE `' . $this->prefix . 'entry`';
-		$stm = $this->bd->prepare ($sql);
-		$stm->execute ();
+		$sql = 'OPTIMIZE TABLE `' . $this->prefix . 'entry`';	//MySQL
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+	}
+
+	public function size($all = false) {
+		$db = Minz_Configuration::dataBase();
+		$sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema=?';	//MySQL
+		$values = array($db['base']);
+		if (!$all) {
+			$sql .= ' AND table_name LIKE ?';
+			$values[] = $this->prefix . '%';
+		}
+		$stm = $this->bd->prepare($sql);
+		$stm->execute($values);
+		$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
+		return $res[0];
 	}
 
-	public static function daoToEntry ($listDAO) {
-		$list = array ();
+	public static function daoToEntry($listDAO) {
+		$list = array();
 
-		if (!is_array ($listDAO)) {
-			$listDAO = array ($listDAO);
+		if (!is_array($listDAO)) {
+			$listDAO = array($listDAO);
 		}
 
 		foreach ($listDAO as $key => $dao) {
-			$entry = new FreshRSS_Entry (
+			$entry = new FreshRSS_Entry(
 				$dao['id_feed'],
 				$dao['guid'],
 				$dao['title'],
@@ -652,13 +554,13 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 				$dao['is_favorite'],
 				$dao['tags']
 			);
-			if (isset ($dao['id'])) {
-				$entry->_id ($dao['id']);
+			if (isset($dao['id'])) {
+				$entry->_id($dao['id']);
 			}
 			$list[] = $entry;
 		}
 
-		unset ($listDAO);
+		unset($listDAO);
 
 		return $list;
 	}

+ 103 - 8
app/Models/EntryDAOSQLite.php

@@ -2,9 +2,38 @@
 
 class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
 
+	protected function sqlConcat($s1, $s2) {
+		return $s1 . '||' . $s2;
+	}
+
+	protected function updateCacheUnreads($catId = false, $feedId = false) {
+		$sql = 'UPDATE `' . $this->prefix . 'feed` '
+		 . 'SET cache_nbUnreads=('
+		 .	'SELECT COUNT(*) AS nbUnreads FROM `' . $this->prefix . 'entry` e '
+		 .	'WHERE e.id_feed=feed.id AND e.is_read=0) '
+		 . 'WHERE 1';
+		$values = array();
+		if ($feedId !== false) {
+			$sql .= ' AND id=?';
+			$values[] = $feedId;
+		}
+		if ($catId !== false) {
+			$sql .= ' AND category=?';
+			$values[] = $catId;
+		}
+		$stm = $this->bd->prepare($sql);
+		if ($stm && $stm->execute($values)) {
+			return true;
+		} else {
+			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+			Minz_Log::record('SQL error updateCacheUnreads: ' . $info[2], Minz_Log::ERROR);
+			return false;
+		}
+	}
+
 	public function markRead($ids, $is_read = true) {
-		if (is_array($ids)) {	//Many IDs at once
-			if (true) {	//Not supported yet in SQLite, so always call IDs one by one
+		if (is_array($ids)) {	//Many IDs at once (used by API)
+			if (true) {	//Speed heuristics	//TODO: Not implemented yet for SQLite (so always call IDs one by one)
 				$affected = 0;
 				foreach ($ids as $id) {
 					$affected += $this->markRead($id, $is_read);
@@ -13,13 +42,13 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
 			}
 		} else {
 			$this->bd->beginTransaction();
-			$sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read=? WHERE id=? AND is_read<>?';
-			$values = array($is_read ? 1 : 0, $ids, $is_read ? 1 : 0);
+			$sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read=? WHERE id=? AND is_read=?';
+			$values = array($is_read ? 1 : 0, $ids, $is_read ? 0 : 1);
 			$stm = $this->bd->prepare($sql);
-			if (!($stm && $stm->execute ($values))) {
+			if (!($stm && $stm->execute($values))) {
 				$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
 				Minz_Log::record('SQL error markRead 1: ' . $info[2], Minz_Log::ERROR);
-				$this->bd->rollBack ();
+				$this->bd->rollBack();
 				return false;
 			}
 			$affected = $stm->rowCount();
@@ -28,10 +57,10 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
 				 . 'WHERE id=(SELECT e.id_feed FROM `' . $this->prefix . 'entry` e WHERE e.id=?)';
 				$values = array($ids);
 				$stm = $this->bd->prepare($sql);
-				if (!($stm && $stm->execute ($values))) {
+				if (!($stm && $stm->execute($values))) {
 					$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
 					Minz_Log::record('SQL error markRead 2: ' . $info[2], Minz_Log::ERROR);
-					$this->bd->rollBack ();
+					$this->bd->rollBack();
 					return false;
 				}
 			}
@@ -39,4 +68,70 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
 			return $affected;
 		}
 	}
+
+	public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0) {
+		if ($idMax == 0) {
+			$idMax = time() . '000000';
+			Minz_Log::record($nb . 'Calling markReadEntries(0) is deprecated!', Minz_Log::DEBUG);
+		}
+
+		$this->bd->beginTransaction();
+		$sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read=1 WHERE is_read=0 AND id <= ?';
+		if ($onlyFavorites) {
+			$sql .= ' AND is_favorite=1';
+		} elseif ($priorityMin >= 0) {
+			$sql .= ' AND id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.priority > ' . intval($priorityMin) . ')';
+		}
+		$values = array($idMax);
+		$stm = $this->bd->prepare($sql);
+		if (!($stm && $stm->execute($values))) {
+			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+			Minz_Log::record('SQL error markReadEntries 1: ' . $info[2], Minz_Log::ERROR);
+			$this->bd->rollBack();
+			return false;
+		}
+		$affected = $stm->rowCount();
+		if (($affected > 0) && (!$this->updateCacheUnreads(false, false))) {
+			$this->bd->rollBack();
+			return false;
+		}
+		$this->bd->commit();
+		return $affected;
+	}
+
+	public function markReadCat($id, $idMax = 0) {
+		if ($idMax == 0) {
+			$idMax = time() . '000000';
+			Minz_Log::record($nb . 'Calling markReadCat(0) is deprecated!', Minz_Log::DEBUG);
+		}
+		$this->bd->beginTransaction();
+
+		$sql = 'UPDATE `' . $this->prefix . 'entry` '
+			 . 'SET is_read=1 '
+			 . 'WHERE is_read=0 AND id <= ? AND '
+			 . 'id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.category=?)';
+		$values = array($idMax, $id);
+		$stm = $this->bd->prepare($sql);
+		if (!($stm && $stm->execute($values))) {
+			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+			Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR);
+			$this->bd->rollBack();
+			return false;
+		}
+		$affected = $stm->rowCount();
+		if (($affected > 0) && (!$this->updateCacheUnreads($id, false))) {
+			$this->bd->rollBack();
+			return false;
+		}
+		$this->bd->commit();
+		return $affected;
+	}
+
+	public function optimizeTable() {
+		//TODO: Search for an equivalent in SQLite
+	}
+
+	public function size($all = false) {
+		return @filesize(DATA_PATH . '/' . Minz_Session::param('currentUser', '_') . '.sqlite');
+	}
 }

+ 98 - 120
app/Models/FeedDAO.php

@@ -1,21 +1,21 @@
 <?php
 
 class FreshRSS_FeedDAO extends Minz_ModelPdo {
-	public function addFeed ($valuesTmp) {
+	public function addFeed($valuesTmp) {
 		$sql = 'INSERT INTO `' . $this->prefix . 'feed` (url, category, name, website, description, lastUpdate, priority, httpAuth, error, keep_history) VALUES(?, ?, ?, ?, ?, ?, 10, ?, 0, -2)';
-		$stm = $this->bd->prepare ($sql);
+		$stm = $this->bd->prepare($sql);
 
-		$values = array (
+		$values = array(
 			substr($valuesTmp['url'], 0, 511),
 			$valuesTmp['category'],
 			substr($valuesTmp['name'], 0, 255),
 			substr($valuesTmp['website'], 0, 255),
 			substr($valuesTmp['description'], 0, 1023),
 			$valuesTmp['lastUpdate'],
-			base64_encode ($valuesTmp['httpAuth']),
+			base64_encode($valuesTmp['httpAuth']),
 		);
 
-		if ($stm && $stm->execute ($values)) {
+		if ($stm && $stm->execute($values)) {
 			return $this->bd->lastInsertId();
 		} else {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
@@ -54,26 +54,26 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 		return $feed_search->id();
 	}
 
-	public function updateFeed ($id, $valuesTmp) {
+	public function updateFeed($id, $valuesTmp) {
 		$set = '';
 		foreach ($valuesTmp as $key => $v) {
 			$set .= $key . '=?, ';
 
 			if ($key == 'httpAuth') {
-				$valuesTmp[$key] = base64_encode ($v);
+				$valuesTmp[$key] = base64_encode($v);
 			}
 		}
-		$set = substr ($set, 0, -2);
+		$set = substr($set, 0, -2);
 
 		$sql = 'UPDATE `' . $this->prefix . 'feed` SET ' . $set . ' WHERE id=?';
-		$stm = $this->bd->prepare ($sql);
+		$stm = $this->bd->prepare($sql);
 
 		foreach ($valuesTmp as $v) {
 			$values[] = $v;
 		}
 		$values[] = $id;
 
-		if ($stm && $stm->execute ($values)) {
+		if ($stm && $stm->execute($values)) {
 			return $stm->rowCount();
 		} else {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
@@ -82,11 +82,11 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 		}
 	}
 
-	public function updateLastUpdate ($id, $inError = 0, $updateCache = true) {
+	public function updateLastUpdate($id, $inError = 0, $updateCache = true) {
 		if ($updateCache) {
 			$sql = 'UPDATE `' . $this->prefix . 'feed` '	//2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE
-			     . 'SET cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=feed.id),'
-			     . 'cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=feed.id AND e2.is_read=0),'
+			     . 'SET cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),'
+			     . 'cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0),'
 			     . 'lastUpdate=?, error=? '
 			     . 'WHERE id=?';
 		} else {
@@ -95,15 +95,15 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 			     . 'WHERE id=?';
 		}
 
-		$values = array (
+		$values = array(
 			time(),
 			$inError,
 			$id,
 		);
 
-		$stm = $this->bd->prepare ($sql);
+		$stm = $this->bd->prepare($sql);
 
-		if ($stm && $stm->execute ($values)) {
+		if ($stm && $stm->execute($values)) {
 			return $stm->rowCount();
 		} else {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
@@ -112,22 +112,22 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 		}
 	}
 
-	public function changeCategory ($idOldCat, $idNewCat) {
-		$catDAO = new FreshRSS_CategoryDAO ();
-		$newCat = $catDAO->searchById ($idNewCat);
+	public function changeCategory($idOldCat, $idNewCat) {
+		$catDAO = new FreshRSS_CategoryDAO();
+		$newCat = $catDAO->searchById($idNewCat);
 		if (!$newCat) {
-			$newCat = $catDAO->getDefault ();
+			$newCat = $catDAO->getDefault();
 		}
 
 		$sql = 'UPDATE `' . $this->prefix . 'feed` SET category=? WHERE category=?';
-		$stm = $this->bd->prepare ($sql);
+		$stm = $this->bd->prepare($sql);
 
-		$values = array (
-			$newCat->id (),
+		$values = array(
+			$newCat->id(),
 			$idOldCat
 		);
 
-		if ($stm && $stm->execute ($values)) {
+		if ($stm && $stm->execute($values)) {
 			return $stm->rowCount();
 		} else {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
@@ -136,23 +136,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 		}
 	}
 
-	public function deleteFeed ($id) {
-		/*//For MYISAM (MySQL 5.5-) without FOREIGN KEY
-		$sql = 'DELETE FROM `' . $this->prefix . 'entry` WHERE id_feed=?';
-		$stm = $this->bd->prepare ($sql);
-		$values = array ($id);
-		if (!($stm && $stm->execute ($values))) {
-			$info = $stm->errorInfo();
-			Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
-			return false;
-		}*/
-
+	public function deleteFeed($id) {
 		$sql = 'DELETE FROM `' . $this->prefix . 'feed` WHERE id=?';
-		$stm = $this->bd->prepare ($sql);
+		$stm = $this->bd->prepare($sql);
 
-		$values = array ($id);
+		$values = array($id);
 
-		if ($stm && $stm->execute ($values)) {
+		if ($stm && $stm->execute($values)) {
 			return $stm->rowCount();
 		} else {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
@@ -160,25 +150,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 			return false;
 		}
 	}
-	public function deleteFeedByCategory ($id) {
-		/*//For MYISAM (MySQL 5.5-) without FOREIGN KEY
-		$sql = 'DELETE FROM `' . $this->prefix . 'entry` e '
-		     . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
-		     . 'WHERE f.category=?';
-		$stm = $this->bd->prepare ($sql);
-		$values = array ($id);
-		if (!($stm && $stm->execute ($values))) {
-			$info = $stm->errorInfo();
-			Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
-			return false;
-		}*/
-
+	public function deleteFeedByCategory($id) {
 		$sql = 'DELETE FROM `' . $this->prefix . 'feed` WHERE category=?';
-		$stm = $this->bd->prepare ($sql);
+		$stm = $this->bd->prepare($sql);
 
-		$values = array ($id);
+		$values = array($id);
 
-		if ($stm && $stm->execute ($values)) {
+		if ($stm && $stm->execute($values)) {
 			return $stm->rowCount();
 		} else {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
@@ -187,52 +165,52 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 		}
 	}
 
-	public function searchById ($id) {
+	public function searchById($id) {
 		$sql = 'SELECT * FROM `' . $this->prefix . 'feed` WHERE id=?';
-		$stm = $this->bd->prepare ($sql);
+		$stm = $this->bd->prepare($sql);
 
-		$values = array ($id);
+		$values = array($id);
 
-		$stm->execute ($values);
-		$res = $stm->fetchAll (PDO::FETCH_ASSOC);
-		$feed = self::daoToFeed ($res);
+		$stm->execute($values);
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+		$feed = self::daoToFeed($res);
 
-		if (isset ($feed[$id])) {
+		if (isset($feed[$id])) {
 			return $feed[$id];
 		} else {
 			return null;
 		}
 	}
-	public function searchByUrl ($url) {
+	public function searchByUrl($url) {
 		$sql = 'SELECT * FROM `' . $this->prefix . 'feed` WHERE url=?';
-		$stm = $this->bd->prepare ($sql);
+		$stm = $this->bd->prepare($sql);
 
-		$values = array ($url);
+		$values = array($url);
 
-		$stm->execute ($values);
-		$res = $stm->fetchAll (PDO::FETCH_ASSOC);
-		$feed = current (self::daoToFeed ($res));
+		$stm->execute($values);
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+		$feed = current(self::daoToFeed($res));
 
-		if (isset ($feed)) {
+		if (isset($feed)) {
 			return $feed;
 		} else {
 			return null;
 		}
 	}
 
-	public function listFeeds () {
+	public function listFeeds() {
 		$sql = 'SELECT * FROM `' . $this->prefix . 'feed` ORDER BY name';
-		$stm = $this->bd->prepare ($sql);
-		$stm->execute ();
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
 
-		return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC));
+		return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
 	}
 
 	public function arrayFeedCategoryNames() {	//For API
 		$sql = 'SELECT f.id, f.name, c.name as c_name FROM `' . $this->prefix . 'feed` f '
 		     . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category';
-		$stm = $this->bd->prepare ($sql);
-		$stm->execute ();
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
 		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
 		$feedCategoryNames = array();
 		foreach ($res as $line) {
@@ -244,49 +222,49 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 		return $feedCategoryNames;
 	}
 
-	public function listFeedsOrderUpdate ($cacheDuration = 1500) {
+	public function listFeedsOrderUpdate($cacheDuration = 1500) {
 		$sql = 'SELECT id, url, name, website, lastUpdate, pathEntries, httpAuth, keep_history '
 		     . 'FROM `' . $this->prefix . 'feed` '
 		     . 'WHERE lastUpdate < ' . (time() - intval($cacheDuration))
 		     . ' ORDER BY lastUpdate';
-		$stm = $this->bd->prepare ($sql);
-		$stm->execute ();
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
 
-		return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC));
+		return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
 	}
 
-	public function listByCategory ($cat) {
+	public function listByCategory($cat) {
 		$sql = 'SELECT * FROM `' . $this->prefix . 'feed` WHERE category=? ORDER BY name';
-		$stm = $this->bd->prepare ($sql);
+		$stm = $this->bd->prepare($sql);
 
-		$values = array ($cat);
+		$values = array($cat);
 
-		$stm->execute ($values);
+		$stm->execute($values);
 
-		return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC));
+		return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
 	}
 
-	public function countEntries ($id) {
+	public function countEntries($id) {
 		$sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` WHERE id_feed=?';
-		$stm = $this->bd->prepare ($sql);
-		$values = array ($id);
-		$stm->execute ($values);
-		$res = $stm->fetchAll (PDO::FETCH_ASSOC);
+		$stm = $this->bd->prepare($sql);
+		$values = array($id);
+		$stm->execute($values);
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
 
 		return $res[0]['count'];
 	}
 
-	public function countNotRead ($id) {
+	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);
-		$res = $stm->fetchAll (PDO::FETCH_ASSOC);
+		$stm = $this->bd->prepare($sql);
+		$values = array($id);
+		$stm->execute($values);
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
 
 		return $res[0]['count'];
 	}
 
-	public function updateCachedValues () {	//For one single feed, call updateLastUpdate($id)
+	public function updateCachedValues() {	//For one single feed, call updateLastUpdate($id)
 		$sql = 'UPDATE `' . $this->prefix . 'feed` f '
 		     . 'INNER JOIN ('
 		     .	'SELECT e.id_feed, '
@@ -296,7 +274,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 		     .	'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);
+		$stm = $this->bd->prepare($sql);
 
 		if ($stm && $stm->execute()) {
 			return $stm->rowCount();
@@ -307,39 +285,39 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 		}
 	}
 
-	public function truncate ($id) {
-		$sql = 'DELETE e.* FROM `' . $this->prefix . 'entry` e WHERE e.id_feed=?';
+	public function truncate($id) {
+		$sql = 'DELETE FROM `' . $this->prefix . 'entry` WHERE id_feed=?';
 		$stm = $this->bd->prepare($sql);
 		$values = array($id);
-		$this->bd->beginTransaction ();
-		if (!($stm && $stm->execute ($values))) {
+		$this->bd->beginTransaction();
+		if (!($stm && $stm->execute($values))) {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
 			Minz_Log::record('SQL error truncate: ' . $info[2], Minz_Log::ERROR);
-			$this->bd->rollBack ();
+			$this->bd->rollBack();
 			return false;
 		}
 		$affected = $stm->rowCount();
 
 		$sql = 'UPDATE `' . $this->prefix . 'feed` '
 			 . 'SET cache_nbEntries=0, cache_nbUnreads=0 WHERE id=?';
-		$values = array ($id);
-		$stm = $this->bd->prepare ($sql);
-		if (!($stm && $stm->execute ($values))) {
+		$values = array($id);
+		$stm = $this->bd->prepare($sql);
+		if (!($stm && $stm->execute($values))) {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
 			Minz_Log::record('SQL error truncate: ' . $info[2], Minz_Log::ERROR);
-			$this->bd->rollBack ();
+			$this->bd->rollBack();
 			return false;
 		}
 
-		$this->bd->commit ();
+		$this->bd->commit();
 		return $affected;
 	}
 
-	public function cleanOldEntries ($id, $date_min, $keep = 15) {	//Remember to call updateLastUpdate($id) just after
-		$sql = 'DELETE e.* FROM `' . $this->prefix . 'entry` e '
-		     . 'WHERE e.id_feed = :id_feed AND e.id <= :id_max AND e.is_favorite = 0 AND e.id NOT IN '
-		     . '(SELECT id FROM (SELECT e2.id FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed = :id_feed ORDER BY id DESC LIMIT :keep) keep)';	//Double select because of: MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
-		$stm = $this->bd->prepare ($sql);
+	public function cleanOldEntries($id, $date_min, $keep = 15) {	//Remember to call updateLastUpdate($id) just after
+		$sql = 'DELETE FROM `' . $this->prefix . 'entry` '
+		     . 'WHERE id_feed = :id_feed AND id <= :id_max AND is_favorite=0 AND id NOT IN '
+		     . '(SELECT id FROM (SELECT e2.id FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed = :id_feed ORDER BY id DESC LIMIT :keep) keep)';	//Double select MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
+		$stm = $this->bd->prepare($sql);
 
 		$id_max = intval($date_min) . '000000';
 
@@ -347,7 +325,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 		$stm->bindParam(':id_max', $id_max, PDO::PARAM_INT);
 		$stm->bindParam(':keep', $keep, PDO::PARAM_INT);
 
-		if ($stm && $stm->execute ()) {
+		if ($stm && $stm->execute()) {
 			return $stm->rowCount();
 		} else {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
@@ -356,18 +334,18 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 		}
 	}
 
-	public static function daoToFeed ($listDAO, $catID = null) {
-		$list = array ();
+	public static function daoToFeed($listDAO, $catID = null) {
+		$list = array();
 
-		if (!is_array ($listDAO)) {
-			$listDAO = array ($listDAO);
+		if (!is_array($listDAO)) {
+			$listDAO = array($listDAO);
 		}
 
 		foreach ($listDAO as $key => $dao) {
-			if (!isset ($dao['name'])) {
+			if (!isset($dao['name'])) {
 				continue;
 			}
-			if (isset ($dao['id'])) {
+			if (isset($dao['id'])) {
 				$key = $dao['id'];
 			}
 			if ($catID === null) {
@@ -384,13 +362,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 			$myFeed->_lastUpdate(isset($dao['lastUpdate']) ? $dao['lastUpdate'] : 0);
 			$myFeed->_priority(isset($dao['priority']) ? $dao['priority'] : 10);
 			$myFeed->_pathEntries(isset($dao['pathEntries']) ? $dao['pathEntries'] : '');
-			$myFeed->_httpAuth(isset($dao['httpAuth']) ? base64_decode ($dao['httpAuth']) : '');
+			$myFeed->_httpAuth(isset($dao['httpAuth']) ? base64_decode($dao['httpAuth']) : '');
 			$myFeed->_error(isset($dao['error']) ? $dao['error'] : 0);
 			$myFeed->_keepHistory(isset($dao['keep_history']) ? $dao['keep_history'] : -2);
 			$myFeed->_nbNotRead(isset($dao['cache_nbUnreads']) ? $dao['cache_nbUnreads'] : 0);
 			$myFeed->_nbEntries(isset($dao['cache_nbEntries']) ? $dao['cache_nbEntries'] : 0);
-			if (isset ($dao['id'])) {
-				$myFeed->_id ($dao['id']);
+			if (isset($dao['id'])) {
+				$myFeed->_id($dao['id']);
 			}
 			$list[$key] = $myFeed;
 		}

+ 14 - 0
app/Models/FeedDAOSQLite.php

@@ -2,4 +2,18 @@
 
 class FreshRSS_FeedDAOSQLite extends FreshRSS_FeedDAO {
 
+	public function updateCachedValues() {	//For one single feed, call updateLastUpdate($id)
+		$sql = 'UPDATE `' . $this->prefix . 'feed` '
+		     . 'SET cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=feed.id),'
+		     . 'cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=feed.id AND e2.is_read=0)';
+		$stm = $this->bd->prepare($sql);
+		if ($stm && $stm->execute()) {
+			return $stm->rowCount();
+		} else {
+			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+			Minz_Log::record('SQL error updateCachedValues: ' . $info[2], Minz_Log::ERROR);
+			return false;
+		}
+	}
+
 }

+ 0 - 14
lib/Minz/ModelPdo.php

@@ -92,20 +92,6 @@ class Minz_ModelPdo {
 		$this->bd->rollBack();
 	}
 
-	public function size($all = false) {
-		$db = Minz_Configuration::dataBase();
-		$sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = ?';
-		$values = array($db['base']);
-		if (!$all) {
-			$sql .= ' AND table_name LIKE ?';
-			$values[] = $this->prefix . '%';
-		}
-		$stm = $this->bd->prepare($sql);
-		$stm->execute($values);
-		$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
-		return $res[0];
-	}
-
 	public static function clean() {
 		self::$sharedBd = null;
 		self::$sharedPrefix = '';

+ 7 - 5
p/api/greader.php

@@ -364,7 +364,7 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex
 		$count++;	//Shift by one element
 	}
 
-	$entryDAO = new FreshRSS_EntryDAO();
+	$entryDAO = FreshRSS_Factory::createEntryDao();
 	$entries = $entryDAO->listWhere($type, $include_target, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, '', $start_time);
 
 	$items = array();
@@ -458,7 +458,7 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude
 			break;
 	}
 
-	$entryDAO = new FreshRSS_EntryDAO();
+	$entryDAO = FreshRSS_Factory::createEntryDao();
 	$ids = $entryDAO->listIdsWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, '', '', $start_time);
 
 	$itemRefs = array();
@@ -481,7 +481,7 @@ function editTag($e_ids, $a, $r) {
 		$e_ids[$i] = hex2dec(basename($e_id));	//Strip prefix 'tag:google.com,2005:reader/item/'
 	}
 
-	$entryDAO = new FreshRSS_EntryDAO();
+	$entryDAO = FreshRSS_Factory::createEntryDao();
 
 	switch ($a) {
 		case 'user/-/state/com.google/read':
@@ -512,13 +512,15 @@ function editTag($e_ids, $a, $r) {
 
 function markAllAsRead($streamId, $olderThanId) {
 	logMe("markAllAsRead($streamId, $olderThanId)\n");
-	$entryDAO = new FreshRSS_EntryDAO();
+	$entryDAO = FreshRSS_Factory::createEntryDao();
 	if (strpos($streamId, 'feed/') === 0) {
 		$f_id = basename($streamId);
 		$entryDAO->markReadFeed($f_id, $olderThanId);
 	} elseif (strpos($streamId, 'user/-/label/') === 0) {
 		$c_name = basename($streamId);
-		$entryDAO->markReadCatName($c_name, $olderThanId);
+		$categoryDAO = new FreshRSS_CategoryDAO();
+		$cat = $categoryDAO->searchByName($c_name);
+		$entryDAO->markReadCat($cat === null ? -1 : $cat->id(), $olderThanId);
 	} elseif ($streamId === 'user/-/state/com.google/reading-list') {
 		$entryDAO->markReadEntries($olderThanId, false, -1);
 	}