diff options
| author | Andrew Dolgov <fox@fakecake.org> | 2025-04-28 04:48:01 +0000 |
|---|---|---|
| committer | Andrew Dolgov <fox@fakecake.org> | 2025-04-28 04:48:01 +0000 |
| commit | 4cb8a84df46d46bc325b6638defbdc4dc34151ed (patch) | |
| tree | 77acdf9b8271568a53e636fc4e84d4bda1404f82 /classes | |
| parent | 0e4b8bd6538f3062d34a3a06ab5531c70042de78 (diff) | |
| parent | f80187e05f12c2fe9c487bff13058b207833b227 (diff) | |
Merge branch 'rip-mysql' into 'master'
initial attempt to remove mysql-related stuff from tt-rss
See merge request tt-rss/tt-rss!120
Diffstat (limited to 'classes')
| -rw-r--r-- | classes/Article.php | 43 | ||||
| -rw-r--r-- | classes/Config.php | 54 | ||||
| -rw-r--r-- | classes/Db.php | 39 | ||||
| -rw-r--r-- | classes/Db_Migrations.php | 9 | ||||
| -rw-r--r-- | classes/FeedItem_Atom.php | 2 | ||||
| -rw-r--r-- | classes/FeedItem_Common.php | 6 | ||||
| -rw-r--r-- | classes/FeedItem_RSS.php | 1 | ||||
| -rw-r--r-- | classes/FeedParser.php | 2 | ||||
| -rw-r--r-- | classes/Feeds.php | 124 | ||||
| -rw-r--r-- | classes/Pref_Feeds.php | 20 | ||||
| -rw-r--r-- | classes/Pref_Prefs.php | 4 | ||||
| -rw-r--r-- | classes/RPC.php | 43 | ||||
| -rw-r--r-- | classes/RSSUtils.php | 85 |
13 files changed, 112 insertions, 320 deletions
diff --git a/classes/Article.php b/classes/Article.php index efb5e4e89..bfff11e6b 100644 --- a/classes/Article.php +++ b/classes/Article.php @@ -85,15 +85,13 @@ class Article extends Handler_Protected { content = ?, content_hash = ? WHERE id = ?"); $sth->execute([$content, $content_hash, $ref_id]); - if (Config::get(Config::DB_TYPE) == "pgsql") { - $sth = $pdo->prepare("UPDATE ttrss_entries - SET tsvector_combined = to_tsvector( :ts_content) - WHERE id = :id"); - $params = [ - ":ts_content" => mb_substr(\Soundasleep\Html2Text::convert($content), 0, 900000), - ":id" => $ref_id]; - $sth->execute($params); - } + $sth = $pdo->prepare("UPDATE ttrss_entries + SET tsvector_combined = to_tsvector( :ts_content) + WHERE id = :id"); + $params = [ + ":ts_content" => mb_substr(\Soundasleep\Html2Text::convert($content), 0, 900000), + ":id" => $ref_id]; + $sth->execute($params); $sth = $pdo->prepare("UPDATE ttrss_user_entries SET published = true, last_published = NOW() WHERE @@ -130,15 +128,15 @@ class Article extends Handler_Protected { if ($row = $sth->fetch()) { $ref_id = $row["id"]; - if (Config::get(Config::DB_TYPE) == "pgsql"){ - $sth = $pdo->prepare("UPDATE ttrss_entries - SET tsvector_combined = to_tsvector( :ts_content) - WHERE id = :id"); - $params = [ - ":ts_content" => mb_substr(\Soundasleep\Html2Text::convert($content), 0, 900000), - ":id" => $ref_id]; - $sth->execute($params); - } + + $sth = $pdo->prepare("UPDATE ttrss_entries + SET tsvector_combined = to_tsvector( :ts_content) + WHERE id = :id"); + $params = [ + ":ts_content" => mb_substr(\Soundasleep\Html2Text::convert($content), 0, 900000), + ":id" => $ref_id]; + $sth->execute($params); + $sth = $pdo->prepare("INSERT INTO ttrss_user_entries (ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache, last_read, note, unread, last_published) @@ -465,16 +463,9 @@ class Article extends Handler_Protected { static function _purge_orphans(): void { - // purge orphaned posts in main content table - - if (Config::get(Config::DB_TYPE) == "mysql") - $limit_qpart = "LIMIT 5000"; - else - $limit_qpart = ""; - $pdo = Db::pdo(); $res = $pdo->query("DELETE FROM ttrss_entries WHERE - NOT EXISTS (SELECT ref_id FROM ttrss_user_entries WHERE ref_id = id) $limit_qpart"); + NOT EXISTS (SELECT ref_id FROM ttrss_user_entries WHERE ref_id = id)"); if (Debug::enabled()) { $rows = $res->rowCount(); diff --git a/classes/Config.php b/classes/Config.php index ca64eb269..c4176b7a8 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -18,13 +18,16 @@ class Config { * * or config.php: * - * putenv('TTRSS_DB_TYPE=pgsql'); + * putenv('TTRSS_DB_HOST=my-patroni.example.com'); * * note lack of quotes and spaces before and after "=". * */ - /** database type: pgsql or mysql */ + /** this is kept for backwards/plugin compatibility, the only supported database is PostgreSQL + * + * @deprecated usages of `Config::get(Config::DB_TYPE)` should be replaced with default (and only) value: `pgsql` or removed + */ const DB_TYPE = "DB_TYPE"; /** database server hostname */ @@ -42,10 +45,6 @@ class Config { /** database server port */ const DB_PORT = "DB_PORT"; - /** connection charset for MySQL. if you have a legacy database and/or experience - * garbage unicode characters with this option, try setting it to a blank string. */ - const MYSQL_CHARSET = "MYSQL_CHARSET"; - /** this is a fallback falue for the CLI SAPI, it should be set to a fully-qualified tt-rss URL */ const SELF_URL_PATH = "SELF_URL_PATH"; @@ -204,7 +203,6 @@ class Config { Config::DB_NAME => [ "", Config::T_STRING ], Config::DB_PASS => [ "", Config::T_STRING ], Config::DB_PORT => [ "5432", Config::T_STRING ], - Config::MYSQL_CHARSET => [ "UTF8", Config::T_STRING ], Config::SELF_URL_PATH => [ "https://example.com/tt-rss", Config::T_STRING ], Config::SINGLE_USER_MODE => [ "", Config::T_BOOL ], Config::SIMPLE_UPDATE_MODE => [ "", Config::T_BOOL ], @@ -481,25 +479,6 @@ class Config { } /* sanity check stuff */ - /** checks for mysql tables not using InnoDB (tt-rss is incompatible with MyISAM) - * @return array<int, array<string, string>> A list of entries identifying tt-rss tables with bad config - */ - private static function check_mysql_tables() { - $pdo = Db::pdo(); - - $sth = $pdo->prepare("SELECT engine, table_name FROM information_schema.tables WHERE - table_schema = ? AND table_name LIKE 'ttrss_%' AND engine != 'InnoDB'"); - $sth->execute([self::get(Config::DB_NAME)]); - - $bad_tables = []; - - while ($line = $sth->fetch()) { - array_push($bad_tables, $line); - } - - return $bad_tables; - } - static function sanity_check(): void { /* @@ -604,29 +583,6 @@ class Config { } } - if (self::get(Config::DB_TYPE) == "mysql") { - $bad_tables = self::check_mysql_tables(); - - if (count($bad_tables) > 0) { - $bad_tables_fmt = []; - - foreach ($bad_tables as $bt) { - array_push($bad_tables_fmt, sprintf("%s (%s)", $bt['table_name'], $bt['engine'])); - } - - $msg = "<p>The following tables use an unsupported MySQL engine: <b>" . - implode(", ", $bad_tables_fmt) . "</b>.</p>"; - - $msg .= "<p>The only supported engine on MySQL is InnoDB. MyISAM lacks functionality to run - tt-rss. - Please backup your data (via OPML) and re-import the schema before continuing.</p> - <p><b>WARNING: importing the schema would mean LOSS OF ALL YOUR DATA.</b></p>"; - - - array_push($errors, $msg); - } - } - if (count($errors) > 0 && php_sapi_name() != "cli") { http_response_code(503); ?> diff --git a/classes/Db.php b/classes/Db.php index 8e0e7047b..872c6b351 100644 --- a/classes/Db.php +++ b/classes/Db.php @@ -9,9 +9,6 @@ class Db { ORM::configure('username', Config::get(Config::DB_USER)); ORM::configure('password', Config::get(Config::DB_PASS)); ORM::configure('return_result_sets', true); - if (Config::get(Config::DB_TYPE) == "mysql" && Config::get(Config::MYSQL_CHARSET)) { - ORM::configure('driver_options', array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . Config::get(Config::MYSQL_CHARSET))); - } } /** @@ -29,13 +26,8 @@ class Db { public static function get_dsn(): string { $db_port = Config::get(Config::DB_PORT) ? ';port=' . Config::get(Config::DB_PORT) : ''; $db_host = Config::get(Config::DB_HOST) ? ';host=' . Config::get(Config::DB_HOST) : ''; - if (Config::get(Config::DB_TYPE) == "mysql" && Config::get(Config::MYSQL_CHARSET)) { - $db_charset = ';charset=' . Config::get(Config::MYSQL_CHARSET); - } else { - $db_charset = ''; - } - return Config::get(Config::DB_TYPE) . ':dbname=' . Config::get(Config::DB_NAME) . $db_host . $db_port . $db_charset; + return 'pgsql:dbname=' . Config::get(Config::DB_NAME) . $db_host . $db_port; } // this really shouldn't be used unless a separate PDO connection is needed @@ -53,20 +45,10 @@ class Db { $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - if (Config::get(Config::DB_TYPE) == "pgsql") { - - $pdo->query("set client_encoding = 'UTF-8'"); - $pdo->query("set datestyle = 'ISO, european'"); - $pdo->query("set TIME ZONE 0"); - $pdo->query("set cpu_tuple_cost = 0.5"); - - } else if (Config::get(Config::DB_TYPE) == "mysql") { - $pdo->query("SET time_zone = '+0:0'"); - - if (Config::get(Config::MYSQL_CHARSET)) { - $pdo->query("SET NAMES " . Config::get(Config::MYSQL_CHARSET)); - } - } + $pdo->query("set client_encoding = 'UTF-8'"); + $pdo->query("set datestyle = 'ISO, european'"); + $pdo->query("set TIME ZONE 0"); + $pdo->query("set cpu_tuple_cost = 0.5"); return $pdo; } @@ -89,16 +71,14 @@ class Db { return self::$instance->pdo; } + /** @deprecated usages should be replaced with `RANDOM()` */ public static function sql_random_function(): string { - if (Config::get(Config::DB_TYPE) == "mysql") { - return "RAND()"; - } return "RANDOM()"; } /** * Helper to build a query part comparing a field against a past datetime (determined by "$now - $some_interval") - * + * * The example below could be read as "last_digest_sent is older than 1 day ago". * ```php * Db::past_comparison_qpart('last_digest_sent', '<', 1, 'day'); @@ -106,7 +86,6 @@ class Db { * * @todo validate value of $unit and fail if invalid (or massage if practical)? * @link https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT - * @link https://dev.mysql.com/doc/refman/9.2/en/expressions.html#temporal-intervals * @param string $field the table field being checked * @param '<'|'>'|'<='|'>='|'=' $operator the comparison operator * @param positive-int $quantity the amount of $unit @@ -114,8 +93,6 @@ class Db { * @return string the query part string */ static function past_comparison_qpart(string $field, string $operator, int $quantity, string $unit): string { - if (Config::get(Config::DB_TYPE) == 'pgsql') - return "$field $operator NOW() - INTERVAL '$quantity $unit' "; - return "$field $operator DATE_SUB(NOW(), INTERVAL $quantity $unit) "; + return "$field $operator NOW() - INTERVAL '$quantity $unit' "; } } diff --git a/classes/Db_Migrations.php b/classes/Db_Migrations.php index 7dcfc24b1..33bc64a32 100644 --- a/classes/Db_Migrations.php +++ b/classes/Db_Migrations.php @@ -23,7 +23,7 @@ class Db_Migrations { } function initialize(string $root_path, string $migrations_table, bool $base_is_latest = true, int $max_version_override = 0): void { - $this->base_path = "$root_path/" . Config::get(Config::DB_TYPE); + $this->base_path = "$root_path/pgsql"; $this->migrations_path = $this->base_path . "/migrations"; $this->migrations_table = $migrations_table; $this->base_is_latest = $base_is_latest; @@ -88,9 +88,7 @@ class Db_Migrations { $lines = $this->get_lines($version); if (count($lines) > 0) { - // mysql doesn't support transactions for DDL statements - if (Config::get(Config::DB_TYPE) != "mysql") - $this->pdo->beginTransaction(); + $this->pdo->beginTransaction(); foreach ($lines as $line) { Debug::log($line, Debug::LOG_EXTENDED); @@ -107,8 +105,7 @@ class Db_Migrations { else $this->set_version($version); - if (Config::get(Config::DB_TYPE) != "mysql") - $this->pdo->commit(); + $this->pdo->commit(); Debug::log("Migration finished, current version: " . $this->get_version(), Debug::LOG_VERBOSE); diff --git a/classes/FeedItem_Atom.php b/classes/FeedItem_Atom.php index 6996705da..044167c9d 100644 --- a/classes/FeedItem_Atom.php +++ b/classes/FeedItem_Atom.php @@ -80,6 +80,7 @@ class FeedItem_Atom extends FeedItem_Common { $elems = $tmpxpath->query("(//*[@href]|//*[@src])"); + /** @var DOMElement $elem */ foreach ($elems as $elem) { if ($elem->hasAttribute("href")) { $elem->setAttribute("href", @@ -210,6 +211,7 @@ class FeedItem_Atom extends FeedItem_Common { return clean($lang); } else { // Fall back to the language declared on the feed, if any. + /** @var DOMElement|DOMNode $child */ foreach ($this->doc->childNodes as $child) { if (method_exists($child, "getAttributeNS")) { return clean($child->getAttributeNS(self::NS_XML, "lang")); diff --git a/classes/FeedItem_Common.php b/classes/FeedItem_Common.php index 5ec958699..241640cc4 100644 --- a/classes/FeedItem_Common.php +++ b/classes/FeedItem_Common.php @@ -91,6 +91,7 @@ abstract class FeedItem_Common extends FeedItem { $enclosures = $this->xpath->query("media:content", $this->elem); + /** @var DOMElement $enclosure */ foreach ($enclosures as $enclosure) { $enc = new FeedEnclosure(); $enc->type = clean($enclosure->getAttribute('type')); @@ -143,6 +144,7 @@ abstract class FeedItem_Common extends FeedItem { $enclosures = $this->xpath->query("media:thumbnail", $this->elem); + /** @var DOMElement $enclosure */ foreach ($enclosures as $enclosure) { $enc = new FeedEnclosure(); $enc->type = 'image/generic'; @@ -193,10 +195,6 @@ abstract class FeedItem_Common extends FeedItem { $cat = preg_replace('/[,\'\"]/', "", $cat); - if (Config::get(Config::DB_TYPE) == "mysql") { - $cat = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $cat); - } - if (mb_strlen($cat) > 250) $cat = mb_substr($cat, 0, 250); diff --git a/classes/FeedItem_RSS.php b/classes/FeedItem_RSS.php index 6e1435310..d1158a45d 100644 --- a/classes/FeedItem_RSS.php +++ b/classes/FeedItem_RSS.php @@ -33,6 +33,7 @@ class FeedItem_RSS extends FeedItem_Common { function get_link(): string { $links = $this->xpath->query("atom:link", $this->elem); + /** @var DOMElement $link */ foreach ($links as $link) { if ($link->hasAttribute("href") && (!$link->hasAttribute("rel") diff --git a/classes/FeedParser.php b/classes/FeedParser.php index fd26226ae..8a117bde4 100644 --- a/classes/FeedParser.php +++ b/classes/FeedParser.php @@ -213,6 +213,7 @@ class FeedParser { case $this::FEED_ATOM: $links = $this->xpath->query("//atom:feed/atom:link"); + /** @var DOMElement $link */ foreach ($links as $link) { if (!$rel || $link->hasAttribute('rel') && $link->getAttribute('rel') == $rel) { array_push($rv, clean(trim($link->getAttribute('href')))); @@ -222,6 +223,7 @@ class FeedParser { case $this::FEED_RSS: $links = $this->xpath->query("//atom:link"); + /** @var DOMElement $link */ foreach ($links as $link) { if (!$rel || $link->hasAttribute('rel') && $link->getAttribute('rel') == $rel) { array_push($rv, clean(trim($link->getAttribute('href')))); diff --git a/classes/Feeds.php b/classes/Feeds.php index 7d818598d..bea3bb0f4 100644 --- a/classes/Feeds.php +++ b/classes/Feeds.php @@ -217,17 +217,6 @@ class Feeds extends Handler_Protected { $id = $line["id"]; - // frontend doesn't expect pdo returning booleans as strings on mysql - if (Config::get(Config::DB_TYPE) == "mysql") { - foreach (["unread", "marked", "published"] as $k) { - if (is_integer($line[$k])) { - $line[$k] = $line[$k] === 1; - } else { - $line[$k] = $line[$k] === "1"; - } - } - } - // normalize archived feed if ($line['feed_id'] === null) { $line['feed_id'] = Feeds::FEED_ARCHIVED; @@ -582,7 +571,7 @@ class Feeds extends Handler_Protected { function search(): void { print json_encode([ - "show_language" => Config::get(Config::DB_TYPE) == "pgsql", + "show_language" => true, "show_syntax_help" => count(PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH)) == 0, "all_languages" => Pref_Feeds::get_ts_languages(), "default_language" => Prefs::get(Prefs::DEFAULT_SEARCH_LANGUAGE, $_SESSION['uid'], $_SESSION['profile'] ?? null) @@ -1407,18 +1396,16 @@ class Feeds extends Handler_Protected { list($search_query_part, $search_words) = self::_search_to_sql($search, $search_language, $owner_uid, $profile); } - if (Config::get(Config::DB_TYPE) == "pgsql") { - $test_sth = $pdo->prepare("select $search_query_part - FROM ttrss_entries, ttrss_user_entries WHERE id = ref_id limit 1"); + $test_sth = $pdo->prepare("select $search_query_part + FROM ttrss_entries, ttrss_user_entries WHERE id = ref_id limit 1"); - try { - $test_sth->execute(); - } catch (PDOException $e) { - // looks like tsquery syntax is invalid - $search_query_part = "false"; + try { + $test_sth->execute(); + } catch (PDOException $e) { + // looks like tsquery syntax is invalid + $search_query_part = "false"; - $query_error_override = T_sprintf("Incorrect search syntax: %s.", implode(" ", $search_words)); - } + $query_error_override = T_sprintf("Incorrect search syntax: %s.", implode(" ", $search_words)); } $search_query_part .= " AND "; @@ -1635,11 +1622,7 @@ class Feeds extends Handler_Protected { $first_id = 0; - if (Config::get(Config::DB_TYPE) == "pgsql") { - $yyiw_qpart = "to_char(date_entered, 'IYYY-IW') AS yyiw"; - } else { - $yyiw_qpart = "date_format(date_entered, '%Y-%u') AS yyiw"; - } + $yyiw_qpart = "to_char(date_entered, 'IYYY-IW') AS yyiw"; if (is_numeric($feed)) { // proper override_order applied above @@ -1679,12 +1662,8 @@ class Feeds extends Handler_Protected { $sanity_interval_qpart = Db::past_comparison_qpart('date_entered', '>=', 1, 'hour') . ' AND '; - if (Config::get(Config::DB_TYPE) == "pgsql") { - $distinct_columns = str_replace("desc", "", strtolower($order_by)); - $distinct_qpart = "DISTINCT ON (id, $distinct_columns)"; - } else { - $distinct_qpart = "DISTINCT"; //fallback - } + $distinct_columns = str_replace("desc", "", strtolower($order_by)); + $distinct_qpart = "DISTINCT ON (id, $distinct_columns)"; // except for Labels category if (Prefs::get(Prefs::HEADLINES_NO_DISTINCT, $owner_uid, $profile) @@ -1785,12 +1764,8 @@ class Feeds extends Handler_Protected { if (Prefs::get(Prefs::HEADLINES_NO_DISTINCT, $owner_uid, $profile)) { $distinct_qpart = ""; } else { - if (Config::get(Config::DB_TYPE) == "pgsql") { - $distinct_columns = str_replace("desc", "", strtolower($order_by)); - $distinct_qpart = "DISTINCT ON (id, $distinct_columns)"; - } else { - $distinct_qpart = "DISTINCT"; //fallback - } + $distinct_columns = str_replace("desc", "", strtolower($order_by)); + $distinct_qpart = "DISTINCT ON (id, $distinct_columns)"; } $query = "SELECT $distinct_qpart @@ -2099,27 +2074,14 @@ class Feeds extends Handler_Protected { else $query_limit = ""; - if (Config::get(Config::DB_TYPE) == "pgsql") { - $sth = $pdo->prepare("DELETE FROM ttrss_user_entries - USING ttrss_entries - WHERE ttrss_entries.id = ref_id AND - marked = false AND - feed_id = ? AND - $query_limit - ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'"); - $sth->execute([$feed_id]); - - } else { - $sth = $pdo->prepare("DELETE FROM ttrss_user_entries - USING ttrss_user_entries, ttrss_entries - WHERE ttrss_entries.id = ref_id AND - marked = false AND - feed_id = ? AND - $query_limit - ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); - $sth->execute([$feed_id]); - - } + $sth = $pdo->prepare("DELETE FROM ttrss_user_entries + USING ttrss_entries + WHERE ttrss_entries.id = ref_id AND + marked = false AND + feed_id = ? AND + $query_limit + ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'"); + $sth->execute([$feed_id]); $rows_deleted = $sth->rowCount(); @@ -2286,16 +2248,12 @@ class Feeds extends Handler_Protected { $k = mb_strtolower($k); - if (Config::get(Config::DB_TYPE) == "pgsql") { - // A hacky way for phrases (e.g. "hello world") to get through PDO quoting. - // Term '"foo bar baz"' becomes '(foo <-> bar <-> baz)' ("<->" meaning "immediately followed by"). - if (preg_match('/\s+/', $k)) - $k = '(' . preg_replace('/\s+/', ' <-> ', $k) . ')'; + // A hacky way for phrases (e.g. "hello world") to get through PDO quoting. + // Term '"foo bar baz"' becomes '(foo <-> bar <-> baz)' ("<->" meaning "immediately followed by"). + if (preg_match('/\s+/', $k)) + $k = '(' . preg_replace('/\s+/', ' <-> ', $k) . ')'; - array_push($search_query_leftover, $not ? "!$k" : $k); - } else { - array_push($search_query_leftover, $not ? "-$k" : $k); - } + array_push($search_query_leftover, $not ? "!$k" : $k); if (!$not) array_push($search_words, $k); } @@ -2304,26 +2262,18 @@ class Feeds extends Handler_Protected { if (count($search_query_leftover) > 0) { - if (Config::get(Config::DB_TYPE) == "pgsql") { - - // if there's no joiners consider this a "simple" search and - // concatenate everything with &, otherwise don't try to mess with tsquery syntax - if (preg_match("/[&|]/", implode(" " , $search_query_leftover))) { - $tsquery = $pdo->quote(implode(" ", $search_query_leftover)); - } else { - $tsquery = $pdo->quote(implode(" & ", $search_query_leftover)); - } - - $search_language = $pdo->quote(mb_strtolower($search_language ?: Prefs::get(Prefs::DEFAULT_SEARCH_LANGUAGE, $owner_uid, $profile))); - - array_push($query_keywords, - "(tsvector_combined @@ to_tsquery($search_language, $tsquery))"); + // if there's no joiners consider this a "simple" search and + // concatenate everything with &, otherwise don't try to mess with tsquery syntax + if (preg_match("/[&|]/", implode(" " , $search_query_leftover))) { + $tsquery = $pdo->quote(implode(" ", $search_query_leftover)); } else { - $ft_query = $pdo->quote(implode(" ", $search_query_leftover)); - - array_push($query_keywords, - "MATCH (ttrss_entries.title, ttrss_entries.content) AGAINST ($ft_query IN BOOLEAN MODE)"); + $tsquery = $pdo->quote(implode(" & ", $search_query_leftover)); } + + $search_language = $pdo->quote(mb_strtolower($search_language ?: Prefs::get(Prefs::DEFAULT_SEARCH_LANGUAGE, $owner_uid, $profile))); + + array_push($query_keywords, + "(tsvector_combined @@ to_tsquery($search_language, $tsquery))"); } if (count($query_keywords) > 0) diff --git a/classes/Pref_Feeds.php b/classes/Pref_Feeds.php index bc059b99f..75ae053bd 100644 --- a/classes/Pref_Feeds.php +++ b/classes/Pref_Feeds.php @@ -15,12 +15,8 @@ class Pref_Feeds extends Handler_Protected { * @return array<int, string> */ public static function get_ts_languages(): array { - if (Config::get(Config::DB_TYPE) == 'pgsql') { - return array_map('ucfirst', - array_column(ORM::for_table('pg_ts_config')->select('cfgname')->find_array(), 'cfgname')); - } - - return []; + return array_map('ucfirst', + array_column(ORM::for_table('pg_ts_config')->select('cfgname')->find_array(), 'cfgname')); } function renameCat(): void { @@ -597,7 +593,7 @@ class Pref_Feeds extends Handler_Protected { "access_level" => $user->access_level ], "lang" => [ - "enabled" => Config::get(Config::DB_TYPE) == "pgsql", + "enabled" => true, "default" => Prefs::get(Prefs::DEFAULT_SEARCH_LANGUAGE, $_SESSION['uid'], $profile), "all" => $this::get_ts_languages(), ] @@ -653,13 +649,11 @@ class Pref_Feeds extends Handler_Protected { </fieldset> <?php } ?> - <?php if (Config::get(Config::DB_TYPE) == "pgsql") { ?> <fieldset> <label><?= __('Language:') ?></label> <?= \Controls\select_tag("feed_language", "", $this::get_ts_languages(), ["disabled"=> 1]) ?> <?= $this->_batch_toggle_checkbox("feed_language") ?> </fieldset> - <?php } ?> </section> <hr/> @@ -1141,12 +1135,6 @@ class Pref_Feeds extends Handler_Protected { function inactiveFeeds(): void { - if (Config::get(Config::DB_TYPE) == "pgsql") { - $interval_qpart = "NOW() - INTERVAL '3 months'"; - } else { - $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)"; - } - $inactive_feeds = ORM::for_table('ttrss_feeds') ->table_alias('f') ->select_many('f.id', 'f.title', 'f.site_url', 'f.feed_url') @@ -1158,7 +1146,7 @@ class Pref_Feeds extends Handler_Protected { "(SELECT MAX(ttrss_entries.updated) FROM ttrss_entries JOIN ttrss_user_entries ON ttrss_entries.id = ttrss_user_entries.ref_id - WHERE ttrss_user_entries.feed_id = f.id) < $interval_qpart") + WHERE ttrss_user_entries.feed_id = f.id) < NOW() - INTERVAL '3 months'") ->group_by('f.title') ->group_by('f.id') ->group_by('f.site_url') diff --git a/classes/Pref_Prefs.php b/classes/Pref_Prefs.php index c96c84100..6942e711c 100644 --- a/classes/Pref_Prefs.php +++ b/classes/Pref_Prefs.php @@ -591,10 +591,6 @@ class Pref_Prefs extends Handler_Protected { continue; } - if ($pref_name == Prefs::DEFAULT_SEARCH_LANGUAGE && Config::get(Config::DB_TYPE) != "pgsql") { - continue; - } - if (isset($prefs_available[$pref_name])) { $item = $prefs_available[$pref_name]; diff --git a/classes/RPC.php b/classes/RPC.php index 82f85fc11..031bef509 100644 --- a/classes/RPC.php +++ b/classes/RPC.php @@ -252,40 +252,23 @@ class RPC extends Handler_Protected { $default_interval = (int) Prefs::get_default(Prefs::DEFAULT_UPDATE_INTERVAL); // Test if the feed need a update (update interval exceded). - if (Config::get(Config::DB_TYPE) == "pgsql") { - $update_limit_qpart = "AND (( - update_interval = 0 - AND (p.value IS NULL OR p.value != '-1') - AND last_updated < NOW() - CAST((COALESCE(p.value, '$default_interval') || ' minutes') AS INTERVAL) - ) OR ( - update_interval > 0 - AND last_updated < NOW() - CAST((update_interval || ' minutes') AS INTERVAL) - ) OR ( - update_interval >= 0 - AND (p.value IS NULL OR p.value != '-1') - AND (last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL) - ))"; - } else { - $update_limit_qpart = "AND (( - update_interval = 0 - AND (p.value IS NULL OR p.value != '-1') - AND last_updated < DATE_SUB(NOW(), INTERVAL CONVERT(COALESCE(p.value, '$default_interval'), SIGNED INTEGER) MINUTE) - ) OR ( - update_interval > 0 - AND last_updated < DATE_SUB(NOW(), INTERVAL update_interval MINUTE) - ) OR ( - update_interval >= 0 - AND (p.value IS NULL OR p.value != '-1') - AND (last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL) - ))"; - } + $update_limit_qpart = "AND (( + update_interval = 0 + AND (p.value IS NULL OR p.value != '-1') + AND last_updated < NOW() - CAST((COALESCE(p.value, '$default_interval') || ' minutes') AS INTERVAL) + ) OR ( + update_interval > 0 + AND last_updated < NOW() - CAST((update_interval || ' minutes') AS INTERVAL) + ) OR ( + update_interval >= 0 + AND (p.value IS NULL OR p.value != '-1') + AND (last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL) + ))"; // Test if feed is currently being updated by another process. $updstart_thresh_qpart = 'AND (last_update_started IS NULL OR ' . Db::past_comparison_qpart('last_update_started', '<', 5, 'minute') . ')'; - $random_qpart = Db::sql_random_function(); - $pdo = Db::pdo(); // we could be invoked from public.php with no active session @@ -305,7 +288,7 @@ class RPC extends Handler_Protected { $owner_check_qpart $update_limit_qpart $updstart_thresh_qpart - ORDER BY $random_qpart LIMIT 30"; + ORDER BY RANDOM() LIMIT 30"; $res = $pdo->query($query); diff --git a/classes/RSSUtils.php b/classes/RSSUtils.php index f6a81d00f..547cc094b 100644 --- a/classes/RSSUtils.php +++ b/classes/RSSUtils.php @@ -35,11 +35,6 @@ class RSSUtils { return sha1(implode(",", $pluginhost->get_plugin_names()) . $tmp); } - // Strips utf8mb4 characters (i.e. emoji) for mysql - static function strip_utf8mb4(string $str): string { - return preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $str); - } - static function cleanup_feed_browser(): void { $pdo = Db::pdo(); $pdo->query("DELETE FROM ttrss_feedbrowser_cache"); @@ -123,33 +118,18 @@ class RSSUtils { $default_interval = (int) Prefs::get_default(Prefs::DEFAULT_UPDATE_INTERVAL); - if (Config::get(Config::DB_TYPE) == "pgsql") { - $update_limit_qpart = "AND (( - update_interval = 0 - AND (p.value IS NULL OR p.value != '-1') - AND last_updated < NOW() - CAST((COALESCE(p.value, '$default_interval') || ' minutes') AS INTERVAL) - ) OR ( - update_interval > 0 - AND last_updated < NOW() - CAST((update_interval || ' minutes') AS INTERVAL) - ) OR ( - update_interval >= 0 - AND (p.value IS NULL OR p.value != '-1') - AND (last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL) - ))"; - } else { - $update_limit_qpart = "AND (( - update_interval = 0 - AND (p.value IS NULL OR p.value != '-1') - AND last_updated < DATE_SUB(NOW(), INTERVAL CONVERT(COALESCE(p.value, '$default_interval'), SIGNED INTEGER) MINUTE) - ) OR ( - update_interval > 0 - AND last_updated < DATE_SUB(NOW(), INTERVAL update_interval MINUTE) - ) OR ( - update_interval >= 0 - AND (p.value IS NULL OR p.value != '-1') - AND (last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL) - ))"; - } + $update_limit_qpart = "AND (( + update_interval = 0 + AND (p.value IS NULL OR p.value != '-1') + AND last_updated < NOW() - CAST((COALESCE(p.value, '$default_interval') || ' minutes') AS INTERVAL) + ) OR ( + update_interval > 0 + AND last_updated < NOW() - CAST((update_interval || ' minutes') AS INTERVAL) + ) OR ( + update_interval >= 0 + AND (p.value IS NULL OR p.value != '-1') + AND (last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL) + ))"; // Test if feed is currently being updated by another process. // TODO: Update RPC::updaterandomfeed_real() to also use 10 minutes? @@ -159,10 +139,7 @@ class RSSUtils { $query_limit = $limit ? sprintf("LIMIT %d", $limit) : ""; // Update the least recently updated feeds first - $query_order = "ORDER BY last_updated"; - - if (Config::get(Config::DB_TYPE) == "pgsql") - $query_order .= " NULLS FIRST"; + $query_order = "ORDER BY last_updated NULLS FIRST"; $query = "SELECT f.feed_url, f.last_updated FROM @@ -856,15 +833,6 @@ class RSSUtils { continue; } - // Yet another episode of "mysql utf8_general_ci is gimped" - if (Config::get(Config::DB_TYPE) == "mysql" && Config::get(Config::MYSQL_CHARSET) != "UTF8MB4") { - foreach ((array)$e as $prop => $val) { - if (is_string($val)) { - $e->$prop = self::strip_utf8mb4($val); - } - } - } - array_push($enclosures, $e); } @@ -935,16 +903,6 @@ class RSSUtils { Debug::log("plugin data: {$entry_plugin_data}", Debug::LOG_VERBOSE); - // Workaround: 4-byte unicode requires utf8mb4 in MySQL. See https://tt-rss.org/forum/viewtopic.php?f=1&t=3377&p=20077#p20077 - if (Config::get(Config::DB_TYPE) == "mysql" && Config::get(Config::MYSQL_CHARSET) != "UTF8MB4") { - foreach ($article as $k => $v) { - // i guess we'll have to take the risk of 4byte unicode labels & tags here - if (is_string($article[$k])) { - $article[$k] = self::strip_utf8mb4($v); - } - } - } - /* Collect article tags here so we could filter by them: */ $matched_rules = []; @@ -1186,14 +1144,9 @@ class RSSUtils { Debug::log("resulting RID: $entry_ref_id, IID: $entry_int_id", Debug::LOG_VERBOSE); - if (Config::get(Config::DB_TYPE) == "pgsql") - $tsvector_qpart = "tsvector_combined = to_tsvector(:ts_lang, :ts_content),"; - else - $tsvector_qpart = ""; - $sth = $pdo->prepare("UPDATE ttrss_entries SET title = :title, - $tsvector_qpart + tsvector_combined = to_tsvector(:ts_lang, :ts_content), content = :content, content_hash = :content_hash, updated = :updated, @@ -1212,12 +1165,10 @@ class RSSUtils { ":plugin_data" => $entry_plugin_data, ":author" => "$entry_author", ":lang" => $entry_language, - ":id" => $ref_id]; - - if (Config::get(Config::DB_TYPE) == "pgsql") { - $params[":ts_lang"] = $feed_language; - $params[":ts_content"] = mb_substr(strip_tags($entry_title) . " " . \Soundasleep\Html2Text::convert($entry_content), 0, 900000); - } + ":id" => $ref_id, + ":ts_lang" => $feed_language, + ":ts_content" => mb_substr(strip_tags($entry_title) . " " . \Soundasleep\Html2Text::convert($entry_content), 0, 900000) + ]; $sth->execute($params); |