aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2016-08-02 22:49:35 +0200
committerGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2016-08-02 22:49:35 +0200
commit7c1b5e322cca0134f57b3a436129985ba9170b9f (patch)
tree48e17355367549337a9bb2ba00237a5dbec2e240 /app
parent4a516aef583f4292293026ebc1d1a3984b75b4e8 (diff)
PostgreSQL draft
https://github.com/FreshRSS/FreshRSS/issues/416 Based on @Damstre work https://github.com/FreshRSS/FreshRSS/pull/1071 Not tested
Diffstat (limited to 'app')
-rw-r--r--app/Models/CategoryDAO.php7
-rw-r--r--app/Models/ConfigurationSetter.php1
-rw-r--r--app/Models/EntryDAO.php2
-rw-r--r--app/Models/EntryDAOPGSQL.php92
-rw-r--r--app/Models/Factory.php42
-rw-r--r--app/Models/FeedDAO.php2
-rw-r--r--app/Models/StatsDAO.php8
-rw-r--r--app/Models/StatsDAOPGSQL.php192
-rw-r--r--app/SQL/install.sql.mysql.php2
-rw-r--r--app/SQL/install.sql.pgsql.php91
-rw-r--r--app/install.php31
11 files changed, 442 insertions, 28 deletions
diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php
index fc431553e..a44edb0f6 100644
--- a/app/Models/CategoryDAO.php
+++ b/app/Models/CategoryDAO.php
@@ -10,7 +10,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
);
if ($stm && $stm->execute($values)) {
- return $this->bd->lastInsertId();
+ return $this->bd->lastInsertId('"' . parent::prefix . 'category_id_seq"');
} else {
$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
Minz_Log::error('SQL error addCategory: ' . $info[2]);
@@ -207,12 +207,13 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
$previousLine = null;
$feedsDao = array();
+ $feedDao = FreshRSS_Factory::createFeedDAO();
foreach ($listDAO as $line) {
if ($previousLine['c_id'] != null && $line['c_id'] !== $previousLine['c_id']) {
// End of the current category, we add it to the $list
$cat = new FreshRSS_Category(
$previousLine['c_name'],
- FreshRSS_FeedDAO::daoToFeed($feedsDao, $previousLine['c_id'])
+ $feedDao->daoToFeed($feedsDao, $previousLine['c_id'])
);
$cat->_id($previousLine['c_id']);
$list[$previousLine['c_id']] = $cat;
@@ -228,7 +229,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
if ($previousLine != null) {
$cat = new FreshRSS_Category(
$previousLine['c_name'],
- FreshRSS_FeedDAO::daoToFeed($feedsDao, $previousLine['c_id'])
+ $feedDao->daoToFeed($feedsDao, $previousLine['c_id'])
);
$cat->_id($previousLine['c_id']);
$list[$previousLine['c_id']] = $cat;
diff --git a/app/Models/ConfigurationSetter.php b/app/Models/ConfigurationSetter.php
index e472b1e7f..988e83356 100644
--- a/app/Models/ConfigurationSetter.php
+++ b/app/Models/ConfigurationSetter.php
@@ -287,6 +287,7 @@ class FreshRSS_ConfigurationSetter {
switch ($value['type']) {
case 'mysql':
+ case 'pgsql':
if (empty($value['host']) ||
empty($value['user']) ||
empty($value['base']) ||
diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php
index c9e6f9742..ba52d3f15 100644
--- a/app/Models/EntryDAO.php
+++ b/app/Models/EntryDAO.php
@@ -3,7 +3,7 @@
class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
public function isCompressed() {
- return parent::$sharedDbType !== 'sqlite';
+ return parent::$sharedDbType === 'mysql';
}
public function hasNativeHex() {
diff --git a/app/Models/EntryDAOPGSQL.php b/app/Models/EntryDAOPGSQL.php
new file mode 100644
index 000000000..95c12ff5d
--- /dev/null
+++ b/app/Models/EntryDAOPGSQL.php
@@ -0,0 +1,92 @@
+<?php
+
+class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAO {
+
+ protected function addColumn($name) {
+ return false;
+ }
+
+ protected function updateCacheUnreads($catId = false, $feedId = false) {
+ return true; //done via triggers
+ }
+
+ /**
+ * Mark all the articles in a feed as read.
+ * There is a fail safe to prevent to mark as read articles that are
+ * loaded during the mark as read action. Then the cache is updated.
+ *
+ * If $idMax equals 0, a deprecated debug message is logged
+ *
+ * @param integer $id_feed feed ID
+ * @param integer $idMax fail safe article ID
+ * @return integer affected rows
+ */
+ public function markReadFeed($id_feed, $idMax = 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=:is_read '
+ . 'WHERE id_feed=:id_feed AND NOT is_read AND id <= :idmax';
+ $values = array($id_feed, $idMax);
+ $stm = $this->bd->prepare($sql);
+ $stm->bindValue(':is_read', true, PDO::PARAM_BOOL);
+ $stm->bindValue(':id_feed', $id_feed);
+ $stm->bindValue(':idmax', $idMax);
+
+ if (!($stm && $stm->execute())) {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::error('SQL error markReadFeed: ' . $info[2]);
+ $this->bd->rollBack();
+ return false;
+ }
+ $affected = $stm->rowCount();
+
+ $this->bd->commit();
+ return $affected;
+ }
+
+ public function listHashForFeedGuids($id_feed, $guids) {
+ if (count($guids) < 1) {
+ return array();
+ }
+ $sql = 'SELECT guid, hash AS hexHash FROM "' . $this->prefix . 'entry" WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)';
+ $stm = $this->bd->prepare($sql);
+ $values = array($id_feed);
+ $values = array_merge($values, $guids);
+ if ($stm && $stm->execute($values)) {
+ $result = array();
+ $rows = $stm->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($rows as $row) {
+ $result[$row['guid']] = $row['hexHash'];
+ }
+ return $result;
+ } else {
+ $info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo();
+ if ($this->autoAddColumn($info)) {
+ return $this->listHashForFeedGuids($id_feed, $guids);
+ }
+ Minz_Log::error('SQL error listHashForFeedGuids: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
+ . ' while querying feed ' . $id_feed);
+ return false;
+ }
+ }
+
+ public function optimizeTable() {
+ return null;
+ }
+
+ public function size($all = true) {
+ $db = FreshRSS_Context::$system_conf->db;
+ $sql = 'SELECT pg_size_pretty(pg_database_size(?))';
+ $values = array($db['base']);
+ $stm = $this->bd->prepare($sql);
+ $stm->execute($values);
+ $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
+ return $res[0];
+ }
+
+}
diff --git a/app/Models/Factory.php b/app/Models/Factory.php
index db09d155d..764987c46 100644
--- a/app/Models/Factory.php
+++ b/app/Models/Factory.php
@@ -4,37 +4,47 @@ class FreshRSS_Factory {
public static function createFeedDao($username = null) {
$conf = Minz_Configuration::get('system');
- if ($conf->db['type'] === 'sqlite') {
- return new FreshRSS_FeedDAOSQLite($username);
- } else {
- return new FreshRSS_FeedDAO($username);
+ switch ($conf->db['type']) {
+ case 'sqlite':
+ return new FreshRSS_FeedDAOSQLite($username);
+ default:
+ return new FreshRSS_FeedDAO($username);
}
}
public static function createEntryDao($username = null) {
$conf = Minz_Configuration::get('system');
- if ($conf->db['type'] === 'sqlite') {
- return new FreshRSS_EntryDAOSQLite($username);
- } else {
- return new FreshRSS_EntryDAO($username);
+ switch ($conf->db['type']) {
+ case 'sqlite':
+ return new FreshRSS_EntryDAOSQLite($username);
+ case 'pgsql':
+ return new FreshRSS_EntryDAOPGSQL($username);
+ default:
+ return new FreshRSS_EntryDAO($username);
}
}
public static function createStatsDAO($username = null) {
$conf = Minz_Configuration::get('system');
- if ($conf->db['type'] === 'sqlite') {
- return new FreshRSS_StatsDAOSQLite($username);
- } else {
- return new FreshRSS_StatsDAO($username);
+ switch ($conf->db['type']) {
+ case 'sqlite':
+ return new FreshRSS_StatsDAOSQLite($username);
+ case 'pgsql':
+ return new FreshRSS_StatsDAOPGSQL($username);
+ default:
+ return new FreshRSS_StatsDAO($username);
}
}
public static function createDatabaseDAO($username = null) {
$conf = Minz_Configuration::get('system');
- if ($conf->db['type'] === 'sqlite') {
- return new FreshRSS_DatabaseDAOSQLite($username);
- } else {
- return new FreshRSS_DatabaseDAO($username);
+ switch ($conf->db['type']) {
+ case 'sqlite':
+ return new FreshRSS_DatabaseDAOSQLite($username);
+ case 'pgsql':
+ return new FreshRSS_DatabaseDAOPGSQL($username);
+ default:
+ return new FreshRSS_DatabaseDAO($username);
}
}
diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php
index 475d39286..f29dac9c0 100644
--- a/app/Models/FeedDAO.php
+++ b/app/Models/FeedDAO.php
@@ -16,7 +16,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
);
if ($stm && $stm->execute($values)) {
- return $this->bd->lastInsertId();
+ return $this->bd->lastInsertId('"' . parent::prefix . 'feed_id_seq"');
} else {
$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
Minz_Log::error('SQL error addFeed: ' . $info[2]);
diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php
index 5ca333396..e18ba4748 100644
--- a/app/Models/StatsDAO.php
+++ b/app/Models/StatsDAO.php
@@ -37,10 +37,10 @@ class FreshRSS_StatsDAO extends Minz_ModelPdo {
$filter .= "AND e.id_feed = {$feed}";
}
$sql = <<<SQL
-SELECT COUNT(1) AS `total`,
-COUNT(1) - SUM(e.is_read) AS `unread`,
-SUM(e.is_read) AS `read`,
-SUM(e.is_favorite) AS `favorite`
+SELECT COUNT(1) AS total,
+COUNT(1) - SUM(e.is_read) AS unread,
+SUM(e.is_read) AS read,
+SUM(e.is_favorite) AS favorite
FROM {$this->prefix}entry AS e
, {$this->prefix}feed AS f
WHERE e.id_feed = f.id
diff --git a/app/Models/StatsDAOPGSQL.php b/app/Models/StatsDAOPGSQL.php
new file mode 100644
index 000000000..0bde72e58
--- /dev/null
+++ b/app/Models/StatsDAOPGSQL.php
@@ -0,0 +1,192 @@
+<?php
+
+class FreshRSS_StatsDAOPGSQL extends FreshRSS_StatsDAO {
+
+ /**
+ * Calculates entry repartition for the selection.
+ * The repartition includes:
+ * - total entries
+ * - read entries
+ * - unread entries
+ * - favorite entries
+ *
+ * @param null|integer $feed feed id
+ * @param boolean $only_main
+ * @return array
+ */
+ public function calculateEntryRepartitionPerFeed($feed = null, $only_main = false) {
+ $filter = '';
+ if ($only_main) {
+ $filter .= 'AND f.priority = 10';
+ }
+ if (!is_null($feed)) {
+ $filter .= "AND e.id_feed = {$feed}";
+ }
+ $sql = <<<SQL
+SELECT COUNT(1) AS total,
+COUNT(1) - SUM(case when e.is_read then 1 else 0 end) AS unread,
+SUM(case when e.is_read then 1 else 0 end) AS read,
+SUM(case when e.is_favorite then 1 else 0 end) AS favorite
+FROM "{$this->prefix}entry" AS e
+, "{$this->prefix}feed" AS f
+WHERE e.id_feed = f.id
+{$filter}
+SQL;
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ $res = $stm->fetchAll(PDO::FETCH_ASSOC);
+
+ return $res[0];
+ }
+
+ /**
+ * Calculates entry count per day on a 30 days period.
+ * Returns the result as a JSON string.
+ *
+ * @return string
+ */
+ public function calculateEntryCount() {
+ $count = $this->initEntryCountArray();
+ $period = self::ENTRY_COUNT_PERIOD;
+
+ // Get stats per day for the last 30 days
+ $sql = <<<SQL
+SELECT to_timestamp(e.date) - NOW() AS day,
+COUNT(1) AS count
+FROM "{$this->prefix}entry" AS e
+WHERE to_timestamp(e.date) BETWEEN NOW() - INTERVAL '{$period} DAYS' AND NOW() - INTERVAL '1 DAY'
+GROUP BY day
+ORDER BY day ASC
+SQL;
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ $res = $stm->fetchAll(PDO::FETCH_ASSOC);
+
+ foreach ($res as $value) {
+ $count[$value['day']] = (int) $value['count'];
+ }
+
+ return $this->convertToSerie($count);
+ }
+
+ /**
+ * Calculates entry average per day on a 30 days period.
+ *
+ * @return integer
+ */
+ public function calculateEntryAverage() {
+ $period = self::ENTRY_COUNT_PERIOD;
+
+ // Get stats per day for the last 30 days
+ $sql = <<<SQL
+SELECT COUNT(1) / {$period} AS average
+FROM "{$this->prefix}entry" AS e
+WHERE to_timestamp(e.date) BETWEEN NOW() - INTERVAL '{$period} DAYS' AND NOW() - INTERVAL '1 DAY'
+SQL;
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ $res = $stm->fetch(PDO::FETCH_NAMED);
+
+ return round($res['average'], 2);
+ }
+
+ /**
+ * Calculates the number of article per hour of the day per feed
+ *
+ * @param integer $feed id
+ * @return string
+ */
+ public function calculateEntryRepartitionPerFeedPerHour($feed = null) {
+ return $this->calculateEntryRepartitionPerFeedPerPeriod('hour', $feed);
+ }
+
+ /**
+ * Calculates the number of article per day of week per feed
+ *
+ * @param integer $feed id
+ * @return string
+ */
+ public function calculateEntryRepartitionPerFeedPerDayOfWeek($feed = null) {
+ return $this->calculateEntryRepartitionPerFeedPerPeriod('day', $feed);
+ }
+
+ /**
+ * Calculates the number of article per month per feed
+ *
+ * @param integer $feed
+ * @return string
+ */
+ public function calculateEntryRepartitionPerFeedPerMonth($feed = null) {
+ return $this->calculateEntryRepartitionPerFeedPerPeriod('month', $feed);
+ }
+
+ /**
+ * Calculates the number of article per period per feed
+ *
+ * @param string $period format string to use for grouping
+ * @param integer $feed id
+ * @return string
+ */
+ protected function calculateEntryRepartitionPerFeedPerPeriod($period, $feed = null) {
+ $restrict = '';
+ if ($feed) {
+ $restrict = "WHERE e.id_feed = {$feed}";
+ }
+ $sql = <<<SQL
+SELECT extract( {$period} from to_timestamp(e.date)) AS period
+, COUNT(1) AS count
+FROM "{$this->prefix}entry" AS e
+{$restrict}
+GROUP BY period
+ORDER BY period ASC
+SQL;
+
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ $res = $stm->fetchAll(PDO::FETCH_NAMED);
+
+ foreach ($res as $value) {
+ $repartition[(int) $value['period']] = (int) $value['count'];
+ }
+
+ return $this->convertToSerie($repartition);
+ }
+
+ /**
+ * Calculates the average number of article per feed
+ *
+ * @param float $period number used to divide the number of day in the period
+ * @param integer $feed id
+ * @return integer
+ */
+ protected function calculateEntryAveragePerFeedPerPeriod($period, $feed = null) {
+ $restrict = '';
+ if ($feed) {
+ $restrict = "WHERE e.id_feed = {$feed}";
+ }
+ $sql = <<<SQL
+SELECT COUNT(1) AS count
+, MIN(date) AS date_min
+, MAX(date) AS date_max
+FROM "{$this->prefix}entry" AS e
+{$restrict}
+SQL;
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ $res = $stm->fetch(PDO::FETCH_NAMED);
+ $date_min = new \DateTime();
+ $date_min->setTimestamp($res['date_min']);
+ $date_max = new \DateTime();
+ $date_max->setTimestamp($res['date_max']);
+ $interval = $date_max->diff($date_min, true);
+ $interval_in_days = $interval->format('%a');
+ if ($interval_in_days <= 0) {
+ // Surely only one article.
+ // We will return count / (period/period) == count.
+ $interval_in_days = $period;
+ }
+
+ return $res['count'] / ($interval_in_days / $period);
+ }
+
+}
diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php
index c78839ef7..92a00aecc 100644
--- a/app/SQL/install.sql.mysql.php
+++ b/app/SQL/install.sql.mysql.php
@@ -1,4 +1,6 @@
<?php
+define('SQL_CREATE_DB', 'CREATE DATABASE IF NOT EXISTS %1$s DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;');
+
define('SQL_CREATE_TABLES', '
CREATE TABLE IF NOT EXISTS `%1$scategory` (
`id` SMALLINT NOT NULL AUTO_INCREMENT, -- v0.7
diff --git a/app/SQL/install.sql.pgsql.php b/app/SQL/install.sql.pgsql.php
new file mode 100644
index 000000000..0f243bdb5
--- /dev/null
+++ b/app/SQL/install.sql.pgsql.php
@@ -0,0 +1,91 @@
+<?php
+define('SQL_CREATE_TABLES', '
+CREATE TABLE IF NOT EXISTS "%1$scategory" (
+ id SERIAL PRIMARY KEY,
+ name varchar(255) UNIQUE NOT NULL
+)
+
+CREATE TABLE IF NOT EXISTS "%1$sfeed" (
+ id SERIAL PRIMARY KEY,
+ url varchar(511) UNIQUE NOT NULL,
+ category SMALLINT DEFAULT 0,
+ name varchar(255) NOT NULL,
+ website varchar(255),
+ description text,
+ lastUpdate int DEFAULT 0,
+ priority smallint NOT NULL DEFAULT 10,
+ pathEntries varchar(511) DEFAULT NULL,
+ httpAuth varchar(511) DEFAULT NULL,
+ error boolean DEFAULT FALSE,
+ keep_history INT NOT NULL DEFAULT -2,
+ ttl INT NOT NULL DEFAULT -2,
+ cache_nbEntries int DEFAULT 0,
+ cache_nbUnreads int DEFAULT 0,
+ FOREIGN KEY (category) REFERENCES "%1$scategory" (id) ON DELETE SET NULL ON UPDATE CASCADE
+)
+CREATE INDEX name_index ON "%1$sfeed" (name)
+CREATE INDEX priority_index ON "%1$sfeed" (priority)
+CREATE INDEX keep_history_index ON "%1$sfeed" (keep_history)
+
+CREATE TABLE IF NOT EXISTS "%1$sentry" (
+ id bigint NOT NULL PRIMARY KEY,
+ guid varchar(760) UNIQUE NOT NULL,
+ title varchar(255) NOT NULL,
+ author varchar(255),
+ content_bin text,
+ link varchar(1023) NOT NULL,
+ date INT,
+ lastSeen INT DEFAULT 0,
+ hash BYTEA,
+ is_read boolean NOT NULL DEFAULT false,
+ is_favorite boolean NOT NULL DEFAULT false,
+ id_feed SMALLINT,
+ tags varchar(1023),
+ FOREIGN KEY (id_feed) REFERENCES "%1$sfeed" (id) ON DELETE CASCADE ON UPDATE CASCADE
+)
+CREATE INDEX is_favorite_index ON "%1$sentry" (is_favorite)
+CREATE INDEX is_read_index ON "%1$sentry" (is_read)
+CREATE INDEX entry_lastSeen_index ON "%1$sentry" (lastSeen)
+
+CREATE OR REPLACE FUNCTION update_unread_feed() RETURNS TRIGGER AS $$
+ BEGIN
+ UPDATE "%1$sfeed"
+ SET cache_nbUnreads=(SELECT COUNT(*) FROM "%1$sentry" WHERE id_feed=OLD.id_feed AND NOT is_read)
+ WHERE id=OLD.id_feed;
+ return NULL;
+ end;
+ $$ LANGUAGE PLPGSQL;
+
+CREATE TRIGGER update_unread_feed
+ AFTER UPDATE OF is_read OR DELETE OR INSERT ON "%1$sentry"
+ FOR EACH ROW
+ EXECUTE PROCEDURE update_unread_feed();
+
+CREATE OR REPLACE FUNCTION reset_feed_seq() RETURNS TRIGGER AS $$
+ BEGIN
+ PERFORM 1 FROM "%1$sfeed";
+ IF NOT FOUND THEN
+ ALTER SEQUENCE IF EXISTS "%1$sfeed_id_seq" RESTART;
+ END IF;
+ return NULL;
+ end;
+ $$ LANGUAGE PLPGSQL;
+
+CREATE TRIGGER reset_feed_seq
+ AFTER DELETE ON "%1$sfeed"
+ FOR EACH STATEMENT
+ EXECUTE PROCEDURE reset_feed_seq();
+
+
+CREATE OR REPLACE RULE check_constraints_on_entry AS ON INSERT TO "%1$sentry" WHERE EXISTS(SELECT 1 FROM "%1$sentry" WHERE guid=NEW.guid) DO INSTEAD NOTHING;
+CREATE OR REPLACE RULE check_constraints_on_feed AS ON INSERT TO "%1$sfeed" WHERE EXISTS(SELECT 1 FROM "%1$sfeed" WHERE url=NEW.url) DO INSTEAD NOTHING;
+CREATE OR REPLACE RULE check_constraints_on_category AS ON INSERT TO "%1$scategory" WHERE EXISTS(SELECT 1 FROM "%1$scategory" WHERE name=NEW.name) DO INSTEAD NOTHING;
+CREATE OR REPLACE RULE check_constraints_on_category as on update to "%1$scategory" WHERE EXISTS(SELECT 1 FROM "%1$scategory" WHERE name=NEW.name) DO INSTEAD NOTHING;
+
+
+INSERT IGNORE INTO "%1$scategory" (id, name) VALUES(1, "%2$s");
+INSERT IGNORE INTO "%1$sfeed" (url, category, name, website, description, ttl) VALUES("http://freshrss.org/feeds/all.atom.xml", 1, "FreshRSS.org", "http://freshrss.org/", "FreshRSS, a free, self-hostable aggregator…", 86400);
+INSERT IGNORE INTO "%1$sfeed" (url, category, name, website, description, ttl) VALUES("https://github.com/FreshRSS/FreshRSS/releases.atom", 1, "FreshRSS @ GitHub", "https://github.com/FreshRSS/FreshRSS/", "FreshRSS releases @ GitHub", 86400);
+');
+
+define('SQL_DROP_TABLES', 'DROP TABLES "%1$sentry", "%1$sfeed", "%1$scategory"');
diff --git a/app/install.php b/app/install.php
index 62695ceb6..ffff9f8e9 100644
--- a/app/install.php
+++ b/app/install.php
@@ -19,8 +19,6 @@ if (isset($_GET['step'])) {
define('STEP', 0);
}
-define('SQL_CREATE_DB', 'CREATE DATABASE IF NOT EXISTS %1$s DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;');
-
if (STEP === 3 && isset($_POST['type'])) {
$_SESSION['bd_type'] = $_POST['type'];
}
@@ -33,6 +31,9 @@ if (isset($_SESSION['bd_type'])) {
case 'sqlite':
include(APP_PATH . '/SQL/install.sql.sqlite.php');
break;
+ case 'pgsql':
+ include(APP_PATH . '/SQL/install.sql.pgsql.php');
+ break;
}
}
@@ -301,7 +302,8 @@ function checkStep1() {
$curl = extension_loaded('curl');
$pdo_mysql = extension_loaded('pdo_mysql');
$pdo_sqlite = extension_loaded('pdo_sqlite');
- $pdo = $pdo_mysql || $pdo_sqlite;
+ $pdo_pgsql = extension_loaded('pdo_pgsql');
+ $pdo = $pdo_mysql || $pdo_sqlite || $pdo_pgsql;
$pcre = extension_loaded('pcre');
$ctype = extension_loaded('ctype');
$dom = class_exists('DOMDocument');
@@ -319,6 +321,7 @@ function checkStep1() {
'curl' => $curl ? 'ok' : 'ko',
'pdo-mysql' => $pdo_mysql ? 'ok' : 'ko',
'pdo-sqlite' => $pdo_sqlite ? 'ok' : 'ko',
+ 'pdo-pgsql' => $pdo_pgsql ? 'ok' : 'ko',
'pdo' => $pdo ? 'ok' : 'ko',
'pcre' => $pcre ? 'ok' : 'ko',
'ctype' => $ctype ? 'ok' : 'ko',
@@ -435,6 +438,22 @@ function checkBD() {
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
);
break;
+ case 'pgsql':
+ $driver_options = array(
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ );
+
+ try { // on ouvre une connexion juste pour créer la base si elle n'existe pas
+ $str = 'pgsql:host=' . $_SESSION['bd_host'] . ';';
+ $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options);
+ $sql = sprintf(SQL_CREATE_DB, $_SESSION['bd_base']);
+ $res = $c->query($sql);
+ } catch (PDOException $e) {
+ }
+
+ // on écrase la précédente connexion en sélectionnant la nouvelle BDD
+ $str = 'pgsql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base'];
+ break;
default:
return false;
}
@@ -708,6 +727,12 @@ function printStep3() {
SQLite
</option>
<?php }?>
+ <?php if (extension_loaded('pdo_pgsql')) {?>
+ <option value="sqlite"
+ <?php echo(isset($_SESSION['bd_type']) && $_SESSION['bd_type'] === 'pgsql') ? 'selected="selected"' : ''; ?>>
+ PostgreSQL
+ </option>
+ <?php }?>
</select>
</div>
</div>