Procházet zdrojové kódy

Fix SQL errors breaking regex search on MySQL / MariaDB (#9036)

On MySQL and MariaDB (the compressed content_bin code path,
isCompressed() === true), three of the four regex-search constructions
in FreshRSS_EntryDAO::sqlBooleanSearch() generated invalid SQL, so the
query failed and the search silently returned zero articles:

* intext:/regex/ appended a stray closing parenthesis after
  sqlRegex(...) (MySQL error 1248);
* -intext:/regex/ had the same stray parenthesis;
* -/regex/ emitted ' ANT NOT ' instead of ' AND NOT '
  (MySQL error 1064).

sqlRegex() already returns a self-contained, balanced expression
(REGEXP_LIKE(expr, ?, 'flags') or expr REGEXP ?), so no extra
parenthesis is needed, matching the uncompressed (SQLite / PostgreSQL)
branches.

The existing MySQL / MariaDB unit tests for intext:/regex/ had
captured the buggy unbalanced SQL as their expected strings; they are
corrected, and new cases are added for -intext:/regex/ and -/regex/
on both flavours.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
TowyTowy před 1 týdnem
rodič
revize
8c5bd58808
2 změnil soubory, kde provedl 25 přidání a 5 odebrání
  1. 3 3
      app/Models/EntryDAO.php
  2. 22 2
      tests/app/Models/SearchTest.php

+ 3 - 3
app/Models/EntryDAO.php

@@ -1226,7 +1226,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 			if ($filter->getIntextRegex() !== null) {
 				if (static::isCompressed()) {	// MySQL-only
 					foreach ($filter->getIntextRegex() as $content) {
-						$sub_search .= 'AND ' . static::sqlRegex("UNCOMPRESS({$alias}content_bin)", $content, $values) . ') ';
+						$sub_search .= 'AND ' . static::sqlRegex("UNCOMPRESS({$alias}content_bin)", $content, $values) . ' ';
 					}
 				} else {
 					foreach ($filter->getIntextRegex() as $content) {
@@ -1295,7 +1295,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 			if ($filter->getNotIntextRegex() !== null) {
 				if (static::isCompressed()) {	// MySQL-only
 					foreach ($filter->getNotIntextRegex() as $content) {
-						$sub_search .= 'AND NOT ' . static::sqlRegex("UNCOMPRESS({$alias}content_bin)", $content, $values) . ') ';
+						$sub_search .= 'AND NOT ' . static::sqlRegex("UNCOMPRESS({$alias}content_bin)", $content, $values) . ' ';
 					}
 				} else {
 					foreach ($filter->getNotIntextRegex() as $content) {
@@ -1365,7 +1365,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 				foreach ($filter->getNotSearchRegex() as $search_value) {
 					if (static::isCompressed()) {	// MySQL-only
 						$sub_search .= 'AND NOT ' . static::sqlRegex($alias . 'title', $search_value, $values) .
-							' ANT NOT ' . static::sqlRegex("UNCOMPRESS({$alias}content_bin)", $search_value, $values) . ' ';
+							' AND NOT ' . static::sqlRegex("UNCOMPRESS({$alias}content_bin)", $search_value, $values) . ' ';
 					} else {
 						$sub_search .= 'AND NOT ' . static::sqlRegex($alias . 'title', $search_value, $values) .
 							' AND NOT ' . static::sqlRegex($alias . 'content', $search_value, $values) . ' ';

+ 22 - 2
tests/app/Models/SearchTest.php

@@ -1007,9 +1007,19 @@ final class SearchTest extends \PHPUnit\Framework\TestCase {
 			],
 			[
 				'intext:/^ab$/m',
-				'(UNCOMPRESS(e.content_bin) REGEXP ?))',
+				'(UNCOMPRESS(e.content_bin) REGEXP ?)',
 				['(?-i)(?m)^ab$']
 			],
+			[
+				'-intext:/^ab$/m',
+				'(NOT UNCOMPRESS(e.content_bin) REGEXP ?)',
+				['(?-i)(?m)^ab$']
+			],
+			[
+				'-/^ab$/',
+				'(NOT e.title REGEXP ? AND NOT UNCOMPRESS(e.content_bin) REGEXP ?)',
+				['(?-i)^ab$', '(?-i)^ab$']
+			],
 		];
 	}
 
@@ -1045,9 +1055,19 @@ final class SearchTest extends \PHPUnit\Framework\TestCase {
 			],
 			[
 				'intext:/^ab$/m',
-				"(REGEXP_LIKE(UNCOMPRESS(e.content_bin),?,'mc')))",
+				"(REGEXP_LIKE(UNCOMPRESS(e.content_bin),?,'mc'))",
 				['^ab$']
 			],
+			[
+				'-intext:/^ab$/m',
+				"(NOT REGEXP_LIKE(UNCOMPRESS(e.content_bin),?,'mc'))",
+				['^ab$']
+			],
+			[
+				'-/^ab$/',
+				"(NOT REGEXP_LIKE(e.title,?,'c') AND NOT REGEXP_LIKE(UNCOMPRESS(e.content_bin),?,'c'))",
+				['^ab$', '^ab$']
+			],
 		];
 	}