@@ -2179,6 +2179,95 @@ public function testFindFulltextSpecialChars(): void
21792179 $ this ->assertEquals (1 , count ($ documents ));
21802180 }
21812181
2182+ /**
2183+ * Regression: accented characters and non-operator special chars
2184+ * previously caused SQLSTATE[42000] syntax error in FTS BOOLEAN MODE.
2185+ *
2186+ * @see https://appwrite.sentry.io/issues/5628237003
2187+ */
2188+ public function testFindFulltextAccentedAndSpecialChars (): void
2189+ {
2190+ /** @var Database $database */
2191+ $ database = $ this ->getDatabase ();
2192+
2193+ if (!$ database ->getAdapter ()->getSupportForFulltextIndex ()) {
2194+ $ this ->expectNotToPerformAssertions ();
2195+ return ;
2196+ }
2197+
2198+ $ collection = 'full_text_unicode ' ;
2199+ $ database ->createCollection ($ collection , permissions: [
2200+ Permission::create (Role::any ()),
2201+ Permission::update (Role::users ())
2202+ ]);
2203+
2204+ $ this ->assertTrue ($ database ->createAttribute ($ collection , 'nombre ' , Database::VAR_STRING , 128 , true ));
2205+ $ this ->assertTrue ($ database ->createIndex ($ collection , 'nombre-ft ' , Database::INDEX_FULLTEXT , ['nombre ' ]));
2206+
2207+ $ database ->createDocument ($ collection , new Document ([
2208+ '$permissions ' => [Permission::read (Role::any ())],
2209+ 'nombre ' => 'Luis García '
2210+ ]));
2211+
2212+ $ database ->createDocument ($ collection , new Document ([
2213+ '$permissions ' => [Permission::read (Role::any ())],
2214+ 'nombre ' => 'Álvaro Yair Cuéllar '
2215+ ]));
2216+
2217+ $ database ->createDocument ($ collection , new Document ([
2218+ '$permissions ' => [Permission::read (Role::any ())],
2219+ 'nombre ' => 'Fernando naïve über '
2220+ ]));
2221+
2222+ /**
2223+ * Accented characters must not cause FTS parser errors
2224+ */
2225+ $ documents = $ database ->find ($ collection , [
2226+ Query::search ('nombre ' , 'García ' ),
2227+ ]);
2228+ $ this ->assertGreaterThanOrEqual (1 , count ($ documents ));
2229+
2230+ $ documents = $ database ->find ($ collection , [
2231+ Query::search ('nombre ' , 'Álvaro ' ),
2232+ ]);
2233+ $ this ->assertGreaterThanOrEqual (1 , count ($ documents ));
2234+
2235+ $ documents = $ database ->find ($ collection , [
2236+ Query::search ('nombre ' , 'Cuéllar ' ),
2237+ ]);
2238+ $ this ->assertGreaterThanOrEqual (1 , count ($ documents ));
2239+
2240+ /**
2241+ * Non-operator special chars (! . #) were not stripped by old code,
2242+ * producing values like "!!!...###*" that crash MySQL's FTS parser.
2243+ */
2244+ $ documents = $ database ->find ($ collection , [
2245+ Query::search ('nombre ' , '!!!...### ' ),
2246+ ]);
2247+ $ this ->assertEquals (0 , count ($ documents ));
2248+
2249+ $ documents = $ database ->find ($ collection , [
2250+ Query::search ('nombre ' , '$$$%%%^^^ ' ),
2251+ ]);
2252+ $ this ->assertEquals (0 , count ($ documents ));
2253+
2254+ /**
2255+ * FTS operator-only input also must not error
2256+ */
2257+ $ documents = $ database ->find ($ collection , [
2258+ Query::search ('nombre ' , '+-*@<>~ ' ),
2259+ ]);
2260+ $ this ->assertEquals (0 , count ($ documents ));
2261+
2262+ /**
2263+ * Mixed special chars + accented word should still find results
2264+ */
2265+ $ documents = $ database ->find ($ collection , [
2266+ Query::search ('nombre ' , '@García! ' ),
2267+ ]);
2268+ $ this ->assertGreaterThanOrEqual (1 , count ($ documents ));
2269+ }
2270+
21822271 public function testFindMultipleConditions (): void
21832272 {
21842273 /** @var Database $database */
0 commit comments