summaryrefslogtreecommitdiff
path: root/app/Models/EntryDAO.php
diff options
context:
space:
mode:
Diffstat (limited to 'app/Models/EntryDAO.php')
-rw-r--r--app/Models/EntryDAO.php210
1 files changed, 134 insertions, 76 deletions
diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php
index f74055835..8f64098e5 100644
--- a/app/Models/EntryDAO.php
+++ b/app/Models/EntryDAO.php
@@ -11,7 +11,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
}
protected function addColumn($name) {
- Minz_Log::debug('FreshRSS_EntryDAO::autoAddColumn: ' . $name);
+ Minz_Log::warning('FreshRSS_EntryDAO::addColumn: ' . $name);
$hasTransaction = false;
try {
$stm = null;
@@ -38,7 +38,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
return $stm && $stm->execute();
}
} catch (Exception $e) {
- Minz_Log::debug('FreshRSS_EntryDAO::autoAddColumn error: ' . $e->getMessage());
+ Minz_Log::error('FreshRSS_EntryDAO::addColumn error: ' . $e->getMessage());
if ($hasTransaction) {
$this->bd->rollBack();
}
@@ -46,9 +46,44 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
return false;
}
- protected function autoAddColumn($errorInfo) {
+ private $triedUpdateToUtf8mb4 = false;
+
+ protected function updateToUtf8mb4() {
+ if ($this->triedUpdateToUtf8mb4) {
+ return false;
+ }
+ $this->triedUpdateToUtf8mb4 = true;
+ $db = FreshRSS_Context::$system_conf->db;
+ if ($db['type'] === 'mysql') {
+ include_once(APP_PATH . '/SQL/install.sql.mysql.php');
+ if (defined('SQL_UPDATE_UTF8MB4')) {
+ Minz_Log::warning('Updating MySQL to UTF8MB4...');
+ $hadTransaction = $this->bd->inTransaction();
+ if ($hadTransaction) {
+ $this->bd->commit();
+ }
+ $ok = false;
+ try {
+ $sql = sprintf(SQL_UPDATE_UTF8MB4, $this->prefix, $db['base']);
+ $stm = $this->bd->prepare($sql);
+ $ok = $stm->execute();
+ } catch (Exception $e) {
+ Minz_Log::error('FreshRSS_EntryDAO::updateToUtf8mb4 error: ' . $e->getMessage());
+ }
+ if ($hadTransaction) {
+ $this->bd->beginTransaction();
+ //NB: Transaction not starting. Why? (tested on PHP 7.0.8-0ubuntu and MySQL 5.7.13-0ubuntu)
+ }
+ return $ok;
+ }
+ }
+ return false;
+ }
+
+ protected function autoUpdateDb($errorInfo) {
if (isset($errorInfo[0])) {
- if ($errorInfo[0] == '42S22') { //ER_BAD_FIELD_ERROR
+ if ($errorInfo[0] === '42S22') { //ER_BAD_FIELD_ERROR
+ //autoAddColumn
foreach (array('lastSeen', 'hash') as $column) {
if (stripos($errorInfo[2], $column) !== false) {
return $this->addColumn($column);
@@ -56,6 +91,11 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
}
}
}
+ if (isset($errorInfo[1])) {
+ if ($errorInfo[1] == '1366') { //ER_TRUNCATED_WRONG_VALUE_FOR_FIELD
+ return $this->updateToUtf8mb4();
+ }
+ }
return false;
}
@@ -94,7 +134,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
return $this->bd->lastInsertId();
} else {
$info = $this->addEntryPrepared == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $this->addEntryPrepared->errorInfo();
- if ($this->autoAddColumn($info)) {
+ if ($this->autoUpdateDb($info)) {
return $this->addEntry($valuesTmp);
} elseif ((int)($info[0] / 1000) !== 23) { //Filter out "SQLSTATE Class code 23: Constraint Violation" because of expected duplicate entries
Minz_Log::error('SQL error addEntry: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
@@ -145,7 +185,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
return $this->bd->lastInsertId();
} else {
$info = $this->updateEntryPrepared == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $this->updateEntryPrepared->errorInfo();
- if ($this->autoAddColumn($info)) {
+ if ($this->autoUpdateDb($info)) {
return $this->updateEntry($valuesTmp);
}
Minz_Log::error('SQL error updateEntry: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
@@ -304,7 +344,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
* @param integer $priorityMin
* @return integer affected rows
*/
- public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0) {
+ public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0, $filter = null, $state = 0) {
if ($idMax == 0) {
$idMax = time() . '000000';
Minz_Log::debug('Calling markReadEntries(0) is deprecated!');
@@ -319,8 +359,11 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
$sql .= ' AND f.priority > ' . intval($priorityMin);
}
$values = array($idMax);
- $stm = $this->bd->prepare($sql);
- if (!($stm && $stm->execute($values))) {
+
+ list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filter, $state);
+
+ $stm = $this->bd->prepare($sql . $search);
+ if (!($stm && $stm->execute(array_merge($values, $searchValues)))) {
$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
Minz_Log::error('SQL error markReadEntries: ' . $info[2]);
return false;
@@ -343,7 +386,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
* @param integer $idMax fail safe article ID
* @return integer affected rows
*/
- public function markReadCat($id, $idMax = 0) {
+ public function markReadCat($id, $idMax = 0, $filter = null, $state = 0) {
if ($idMax == 0) {
$idMax = time() . '000000';
Minz_Log::debug('Calling markReadCat(0) is deprecated!');
@@ -353,8 +396,11 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
. '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))) {
+
+ list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filter, $state);
+
+ $stm = $this->bd->prepare($sql . $search);
+ if (!($stm && $stm->execute(array_merge($values, $searchValues)))) {
$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
Minz_Log::error('SQL error markReadCat: ' . $info[2]);
return false;
@@ -377,19 +423,22 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
* @param integer $idMax fail safe article ID
* @return integer affected rows
*/
- public function markReadFeed($id_feed, $idMax = 0) {
+ public function markReadFeed($id_feed, $idMax = 0, $filter = null, $state = 0) {
if ($idMax == 0) {
$idMax = time() . '000000';
Minz_Log::debug('Calling markReadFeed(0) is deprecated!');
}
$this->bd->beginTransaction();
- $sql = 'UPDATE `' . $this->prefix . 'entry` '
- . 'SET is_read=1 '
- . 'WHERE id_feed=? AND is_read=0 AND id <= ?';
+ $sql = 'UPDATE `' . $this->prefix . 'entry` e '
+ . 'SET e.is_read=1 '
+ . 'WHERE e.id_feed=? AND e.is_read=0 AND e.id <= ?';
$values = array($id_feed, $idMax);
- $stm = $this->bd->prepare($sql);
- if (!($stm && $stm->execute($values))) {
+
+ list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filter, $state);
+
+ $stm = $this->bd->prepare($sql . $search);
+ if (!($stm && $stm->execute(array_merge($values, $searchValues)))) {
$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
Minz_Log::error('SQL error markReadFeed: ' . $info[2]);
$this->bd->rollBack();
@@ -453,52 +502,24 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
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) {
- if (!$state) {
- $state = FreshRSS_Entry::STATE_ALL;
- }
- $where = '';
- $joinFeed = false;
+ protected function sqlListEntriesWhere($alias = '', $filter = null, $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $firstId = '', $date_min = 0) {
+ $search = ' ';
$values = array();
- switch ($type) {
- case 'a':
- $where .= 'f.priority > 0 ';
- $joinFeed = true;
- break;
- case 's': //Deprecated: use $state instead
- $where .= 'e1.is_favorite=1 ';
- break;
- case 'c':
- $where .= 'f.category=? ';
- $values[] = intval($id);
- $joinFeed = true;
- break;
- case 'f':
- $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 . ']!');
- }
-
if ($state & FreshRSS_Entry::STATE_NOT_READ) {
if (!($state & FreshRSS_Entry::STATE_READ)) {
- $where .= 'AND e1.is_read=0 ';
+ $search .= 'AND ' . $alias . 'is_read=0 ';
}
}
elseif ($state & FreshRSS_Entry::STATE_READ) {
- $where .= 'AND e1.is_read=1 ';
+ $search .= 'AND ' . $alias . 'is_read=1 ';
}
if ($state & FreshRSS_Entry::STATE_FAVORITE) {
if (!($state & FreshRSS_Entry::STATE_NOT_FAVORITE)) {
- $where .= 'AND e1.is_favorite=1 ';
+ $search .= 'AND ' . $alias . 'is_favorite=1 ';
}
}
elseif ($state & FreshRSS_Entry::STATE_NOT_FAVORITE) {
- $where .= 'AND e1.is_favorite=0 ';
+ $search .= 'AND ' . $alias . 'is_favorite=0 ';
}
switch ($order) {
@@ -512,76 +533,111 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
$firstId = $order === 'DESC' ? '9000000000'. '000000' : '0'; //MySQL optimization. TODO: check if this is needed again, after the filtering for old articles has been removed in 0.9-dev
}*/
if ($firstId !== '') {
- $where .= 'AND e1.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' ';
+ $search .= 'AND ' . $alias . 'id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' ';
}
if ($date_min > 0) {
- $where .= 'AND e1.id >= ' . $date_min . '000000 ';
+ $search .= 'AND ' . $alias . 'id >= ' . $date_min . '000000 ';
}
- $search = '';
if ($filter) {
if ($filter->getIntitle()) {
- $search .= 'AND e1.title LIKE ? ';
+ $search .= 'AND ' . $alias . 'title LIKE ? ';
$values[] = "%{$filter->getIntitle()}%";
}
if ($filter->getInurl()) {
- $search .= 'AND CONCAT(e1.link, e1.guid) LIKE ? ';
+ $search .= 'AND CONCAT(' . $alias . 'link, ' . $alias . 'guid) LIKE ? ';
$values[] = "%{$filter->getInurl()}%";
}
if ($filter->getAuthor()) {
- $search .= 'AND e1.author LIKE ? ';
+ $search .= 'AND ' . $alias . 'author LIKE ? ';
$values[] = "%{$filter->getAuthor()}%";
}
if ($filter->getMinDate()) {
- $search .= 'AND e1.id >= ? ';
+ $search .= 'AND ' . $alias . 'id >= ? ';
$values[] = "{$filter->getMinDate()}000000";
}
if ($filter->getMaxDate()) {
- $search .= 'AND e1.id <= ? ';
+ $search .= 'AND ' . $alias . 'id <= ? ';
$values[] = "{$filter->getMaxDate()}000000";
}
if ($filter->getMinPubdate()) {
- $search .= 'AND e1.date >= ? ';
+ $search .= 'AND ' . $alias . 'date >= ? ';
$values[] = $filter->getMinPubdate();
}
if ($filter->getMaxPubdate()) {
- $search .= 'AND e1.date <= ? ';
+ $search .= 'AND ' . $alias . 'date <= ? ';
$values[] = $filter->getMaxPubdate();
}
if ($filter->getTags()) {
$tags = $filter->getTags();
foreach ($tags as $tag) {
- $search .= 'AND e1.tags LIKE ? ';
+ $search .= 'AND ' . $alias . 'tags LIKE ? ';
$values[] = "%{$tag}%";
}
}
if ($filter->getSearch()) {
$search_values = $filter->getSearch();
foreach ($search_values as $search_value) {
- $search .= 'AND ' . $this->sqlconcat('e1.title', $this->isCompressed() ? 'UNCOMPRESS(content_bin)' : 'content') . ' LIKE ? ';
+ $search .= 'AND ' . $this->sqlconcat($alias . 'title', $this->isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ? ';
$values[] = "%{$search_value}%";
}
}
}
- return array($values,
- 'SELECT e1.id FROM `' . $this->prefix . 'entry` e1 '
- . ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e1.id_feed=f.id ' : '')
+ return array($values, $search);
+ }
+
+ private function sqlListWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0) {
+ if (!$state) {
+ $state = FreshRSS_Entry::STATE_ALL;
+ }
+ $where = '';
+ $joinFeed = false;
+ $values = array();
+ switch ($type) {
+ case 'a':
+ $where .= 'f.priority > 0 ';
+ $joinFeed = true;
+ break;
+ case 's': //Deprecated: use $state instead
+ $where .= 'e.is_favorite=1 ';
+ break;
+ case 'c':
+ $where .= 'f.category=? ';
+ $values[] = intval($id);
+ $joinFeed = true;
+ break;
+ case 'f':
+ $where .= 'e.id_feed=? ';
+ $values[] = intval($id);
+ break;
+ case 'A':
+ $where .= '1 ';
+ break;
+ default:
+ throw new FreshRSS_EntriesGetter_Exception('Bad type in Entry->listByType: [' . $type . ']!');
+ }
+
+ list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filter, $state, $order, $firstId, $date_min);
+
+ return array(array_merge($values, $searchValues),
+ 'SELECT e.id FROM `' . $this->prefix . 'entry` e '
+ . ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id ' : '')
. 'WHERE ' . $where
. $search
- . 'ORDER BY e1.id ' . $order
+ . 'ORDER BY e.id ' . $order
. ($limit > 0 ? ' LIMIT ' . $limit : '')); //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/
}
public function listWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0) {
list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min);
- $sql = 'SELECT e.id, e.guid, e.title, e.author, '
+ $sql = 'SELECT e0.id, e0.guid, e0.title, e0.author, '
. ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
- . ', e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags '
- . 'FROM `' . $this->prefix . 'entry` e '
+ . ', e0.link, e0.date, e0.is_read, e0.is_favorite, e0.id_feed, e0.tags '
+ . 'FROM `' . $this->prefix . 'entry` e0 '
. 'INNER JOIN ('
. $sql
- . ') e2 ON e2.id=e.id '
- . 'ORDER BY e.id ' . $order;
+ . ') e2 ON e2.id=e0.id '
+ . 'ORDER BY e0.id ' . $order;
$stm = $this->bd->prepare($sql);
$stm->execute($values);
@@ -615,7 +671,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
return $result;
} else {
$info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo();
- if ($this->autoAddColumn($info)) {
+ if ($this->autoUpdateDb($info)) {
return $this->listHashForFeedGuids($id_feed, $guids);
}
Minz_Log::error('SQL error listHashForFeedGuids: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
@@ -636,7 +692,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
return $stm->rowCount();
} else {
$info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo();
- if ($this->autoAddColumn($info)) {
+ if ($this->autoUpdateDb($info)) {
return $this->updateLastSeen($id_feed, $guids);
}
Minz_Log::error('SQL error updateLastSeen: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
@@ -692,7 +748,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
public function optimizeTable() {
$sql = 'OPTIMIZE TABLE `' . $this->prefix . 'entry`'; //MySQL
$stm = $this->bd->prepare($sql);
- $stm->execute();
+ if ($stm) {
+ return $stm->execute();
+ }
}
public function size($all = false) {