[packages/rt/RT_5] Quote mysql fulltext search values so they match as a phrase
arekm
arekm at pld-linux.org
Mon Aug 3 22:12:50 CEST 2026
commit ca8763b0cc2b0805897ccbab35abcc969a188d05
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date: Mon Aug 3 21:53:08 2026 +0200
Quote mysql fulltext search values so they match as a phrase
Unquoted values matched as an OR of their tokens, so example.com hit
every 'example'.
rt-fts-phrase.patch | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++
rt.spec | 4 ++-
2 files changed, 91 insertions(+), 1 deletion(-)
---
diff --git a/rt.spec b/rt.spec
index 8e59948..60bb8dc 100644
--- a/rt.spec
+++ b/rt.spec
@@ -45,7 +45,7 @@ Summary: Request Tracker
Summary(pl.UTF-8): Request Tracker - system do śledzenia zleceń
Name: rt
Version: 5.0.9
-Release: 1
+Release: 2
License: GPL v2
Group: Applications
# https://bestpractical.com/download-page
@@ -59,6 +59,7 @@ Source5: lighttpd.conf
Patch0: %{name}-layout.patch
Patch1: %{name}-config.patch
Patch2: rt-timeworked.patch
+Patch3: %{name}-fts-phrase.patch
URL: http://www.bestpractical.com/rt/
BuildRequires: autoconf
BuildRequires: automake
@@ -344,6 +345,7 @@ Pliki wspomagające używanie RT z Apache.
%patch -P0 -p1
%patch -P1 -p1
%patch -P2 -p1
+%patch -P3 -p1
mv aclocal.m4 acinclude.m4
diff --git a/rt-fts-phrase.patch b/rt-fts-phrase.patch
new file mode 100644
index 0000000..0c8e1b3
--- /dev/null
+++ b/rt-fts-phrase.patch
@@ -0,0 +1,88 @@
+diff -urN rt-5.0.9.orig/docs/full_text_indexing.pod rt-5.0.9/docs/full_text_indexing.pod
+--- rt-5.0.9.orig/docs/full_text_indexing.pod 2025-10-22 19:11:10.000000000 +0200
++++ rt-5.0.9/docs/full_text_indexing.pod 2026-08-03 21:57:03.978135710 +0200
+@@ -107,12 +107,14 @@
+
+ =head3 Caveats
+
+-Searching is done in "boolean mode." As such, the TicketSQL query
+-C<Content LIKE 'winter 2014'> will return tickets with transactions that
+-contain I<either> word. To find transactions which contain both (but
+-not necessarily adjacent), use C<Content LIKE '+winter +2014'>. To find
+-transactions containing the precise phrase, use C<Content LIKE '"winter
+-2014">.
++Searching is done in "boolean mode." A query that uses no boolean syntax
++of its own is wrapped in double quotes, so the TicketSQL query C<Content
++LIKE 'winter 2014'> returns tickets with transactions containing that
++precise phrase, and C<Content LIKE 'example.com'> matches that domain
++rather than every occurrence of C<example>. To find transactions which
++contain both words, but not necessarily adjacent, use C<Content LIKE
++'+winter +2014'>. Queries that already contain quotes, wildcards, or a
++leading operator are passed to MySQL unchanged.
+
+ See the MySQL documentation, at
+ L<http://dev.mysql.com/doc/refman/5.6/en/fulltext-boolean.html>, for a
+diff -urN rt-5.0.9.orig/lib/RT/Tickets.pm rt-5.0.9/lib/RT/Tickets.pm
+--- rt-5.0.9.orig/lib/RT/Tickets.pm 2025-10-22 19:11:10.000000000 +0200
++++ rt-5.0.9/lib/RT/Tickets.pm 2026-08-03 21:57:03.971985932 +0200
+@@ -1025,11 +1025,20 @@
+ }
+ elsif ( $db_type eq 'mysql' and not $config->{Sphinx}) {
+ my $dbh = $RT::Handle->dbh;
++ # Wrap in double quotes so the value matches as a phrase instead of
++ # an implicit OR of the tokens mysql splits it into; without this
++ # 'example.com' matches every 'example'. Values already using
++ # boolean-mode syntax are left alone: quotes and wildcards
++ # anywhere, and +-<>~( in operator position (start of a word).
++ my $search_value = $value =~ /\W/
++ && $value !~ /["*]/
++ && $value !~ /(?:^|\s)[-+<>~(]/
++ ? qq{"$value"} : $value;
+ $self->Limit(
+ %rest,
+ FUNCTION => "MATCH($alias.Content)",
+ OPERATOR => 'AGAINST',
+- VALUE => "(". $dbh->quote($value) ." IN BOOLEAN MODE)",
++ VALUE => "(". $dbh->quote($search_value) ." IN BOOLEAN MODE)",
+ QUOTEVALUE => 0,
+ );
+ # As with Oracle, above, this forces the LEFT JOINs into
+diff -urN rt-5.0.9.orig/t/fts/indexed_mysql.t rt-5.0.9/t/fts/indexed_mysql.t
+--- rt-5.0.9.orig/t/fts/indexed_mysql.t 2025-10-22 19:11:10.000000000 +0200
++++ rt-5.0.9/t/fts/indexed_mysql.t 2026-08-03 21:57:03.975384297 +0200
+@@ -61,6 +61,35 @@
+ "Content LIKE 'french'" => { first => 0, second => 1, third => 0, fourth => 0 },
+ );
+
++ at tickets = RT::Test->create_tickets(
++ { Queue => $q->id },
++ { Subject => 'phrase', Content => 'winter 2014 report' },
++ { Subject => 'scattered', Content => '2014 summer winter' },
++);
++RT::Test::FTS->sync_index();
++
++run_tests(
++ # multi-word values match as a phrase, not as an OR of the words
++ "Content LIKE 'winter 2014'" => { phrase => 1, scattered => 0 },
++ # values carrying boolean syntax must reach mysql unchanged
++ "Content LIKE '+winter +2014'" => { phrase => 1, scattered => 1 },
++ "Content LIKE '\"winter 2014\" report'" => { phrase => 1, scattered => 0 },
++);
++
++ at tickets = RT::Test->create_tickets(
++ { Queue => $q->id },
++ { Subject => 'dotcom', Content => 'contact example.com expires' },
++ { Subject => 'dotorg', Content => 'contact example.org expires' },
++ { Subject => 'bare', Content => 'just example without a domain' },
++);
++RT::Test::FTS->sync_index();
++
++run_tests(
++ # a value the tokenizer would split must not decay into its tokens
++ "Content LIKE 'example.com'" => { dotcom => 1, dotorg => 0, bare => 0 },
++ "Content LIKE 'example'" => { dotcom => 1, dotorg => 1, bare => 1 },
++);
++
+ @tickets = ();
+
+ done_testing;
================================================================
---- gitweb:
http://git.pld-linux.org/gitweb.cgi/packages/rt.git/commitdiff/ca8763b0cc2b0805897ccbab35abcc969a188d05
More information about the pld-cvs-commit
mailing list