16

The following query results in an error due to the @ (at symbol). The query will work fine when it is removed. I tried escaping the @ character, but no luck.

SELECT * FROM clients WHERE MATCH (form) AGAINST ('test@test.com' IN BOOLEAN MODE);

The error produced is:

#1064 - syntax error, unexpected '@', expecting $end

Note that I am testing these queries in the phpMyAdmin SQL console area, so it's not a matter of an escape error with my other programming.

MySQL server is version 5.6.17.

Any ideas? Thanks.

Marc B
  • 356,200
  • 43
  • 426
  • 500
witoto
  • 163
  • 1
  • 5
  • there's no reason for this to happen, unless you had a `'` somewhere already where `....'test` closes that string. – Marc B Aug 01 '14 at 20:34
  • @Mark B The error continues happen exactly as shown. Note that the following two queries work fine, but are not suitable for my goals: SELECT * FROM clients WHERE form LIKE '%test@test.com%' SELECT * FROM clients WHERE MATCH (form) AGAINST ('"test@test.com"' IN BOOLEAN MODE); – witoto Aug 01 '14 at 20:41
  • i run the query and have no error – catalinetu Aug 01 '14 at 20:41
  • I should add that the database is on Amazon RDS. – witoto Aug 01 '14 at 20:42
  • doesn't matter where you run it. as pasted above, there's nothing wrong with the syntax of the query. something ELSE is causing this. – Marc B Aug 01 '14 at 20:44
  • @MarcB - yes, like Amazon RDS causing the issue or maybe it's a limitation of InnoDB's rather new implementation of fulltext search or how I have my tables set up? – witoto Aug 01 '14 at 20:46
  • 1
    I doubt it's mysql or innodb. it's probably phpmyadmin stuffing a `'` into the mix in the background somewhere. like I said, the sql you've posted above is syntactically perfect. it will NOT screw up on any else's system if they had the proper tables/fields on their system. – Marc B Aug 01 '14 at 20:49
  • @MarcB - I just tested the same query via PHP and the error still occurs. Does not appear to by phpMyAdmin, then. Fatal error: syntax error, unexpected '@', expecting $end in XXX on line XXX Do you think this could be the result of a MySQL server version issue? See my post on Amazon RDS yesterday, yet to be answered: https://forums.aws.amazon.com/thread.jspa?threadID=157598&tstart=0 – witoto Aug 01 '14 at 21:00
  • 1
    I fixed this same issue, (not a syntax error either - only occurs when string has '@' in it) and i fixed by changing to NATURAL LANGUAGE MODE instead of BOOLEAN MODE. – xd6_ Aug 29 '14 at 20:18

3 Answers3

13

This is connected to INNODB FULLTEXT indexes.

It is introduced as a combination of:

  1. InnoDB full-text search does not support the use of multiple operators on a single search word

  2. @distance This operator works on InnoDB tables only. It tests whether two or more words all start within a specified distance from each other, measured in words.

http://dev.mysql.com/doc/refman/5.6/en/fulltext-boolean.html

# Running a test search for MATCH('+test{$ascii}test' IN BOOLEAN MODE) on all ASCII chars returns errors on:
40 (
41 )
64 @

MYSQL seems to be treating these symbols as wordbreaks and I have found no way to escape and include these in the actual query so my solution is the split on the symbol and include them as a group e.g. “test@bar” == (+test +bar)

# As a further test, running a search for MATCH('+{$ascii}' IN BOOLEAN MODE) returns errors for:
40 (
41 )
42 *
43 +
45 -
60 <
62 >
64 @
126 ~

Which is as expected from the MYSQL docs as the special BOOLEAN modifier characters

# As a testcase (Requires MYSQL 5.6+): 
CREATE TABLE `fulltext_innodb` (
`id` int(11) NOT NULL AUTO_INCREMENT,
 `text` text COLLATE utf8_unicode_ci,
 PRIMARY KEY (`id`),
 FULLTEXT KEY `text` (`text`)
) ENGINE=InnoDB
INSERT INTO `fulltext_innodb` (`id`, `text`) VALUES (1, 'test@bar');

SELECT * FROM `fulltext_innodb` WHERE MATCH (`text`) AGAINST( '+test@bar’ IN BOOLEAN MODE )

#1064 - syntax error, unexpected '@', expecting $end
Community
  • 1
  • 1
chris
  • 3,019
  • 23
  • 21
5

Not a direct answer, but if anybody is looking for a PHP code to handle tokenizing of user-input search string for Full Text Searching, can use the following code:

/**
 * Method to take an input string and tokenize it into an array of words for Full Text Searching (FTS).
 *
 * This method is used when an input string can be made up of multiple words (let's say, separated by space characters),
 * and we need to use different Boolean operators on each of the words. The tokenizing process is similar to extraction
 * of words by FTS parser in MySQL. The operators used for matching in Boolean condition are removed from the input $phrase.
 * These characters as of latest version of MySQL (8+) are: +-><()~*:""&|@  (@ is specific for InnoDB)
 * We can also execute the following query to get updated list: show variables like 'ft_boolean_syntax';
 * Afterwards, the modified string is split into individual words considering either space, comma, and, period (.) characters.
 * Details at: https://dev.mysql.com/doc/refman/8.0/en/fulltext-natural-language.html
 *
 * @param string $phrase Input statement/phrase consisting of words
 * @return array Tokenized words
 * @author Madhur, 2019
 */
function tokenizeStringIntoFTSWords(string $phrase) : array {
    $phrase_mod = trim(preg_replace('/[><()~*:"&|@+-]/', ' ', trim($phrase)));
    $words_arr = preg_split('/[\s,.]/', $phrase_mod, null, PREG_SPLIT_NO_EMPTY);

    // filter out the fulltext stop words and words whose length is less than 3.
    $fts_words = array();
    $fulltext_stop_words = array(
        'about','are','com','for','from','how','that','this','was','what',
        'when','where','who','will','with','und','the','www'
    );
    foreach($words_arr as $word) {
        // By default MySQL FULLTEXT index does not store words whose length is less than 3.
        // Check innodb_ft_min_token_size Ref: https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_ft_min_token_size
        // So we need to ignore words whose length is less than 3.
        if(strlen($word) < 3) continue;

        // Ignore the fulltext stop words, whose length is greater than 3 or equal to 3.
        // Ref: https://dev.mysql.com/doc/refman/8.0/en/fulltext-stopwords.html
        if (in_array($word, $fulltext_stop_words)) continue;

        $fts_words[] = $word;
    }

    return $fts_words;
}

Above code will handle Stopwords, minimum word length limit, and Boolean mode operators as well. So, for instance, if user inputs: Search@bar with in Javascript, it will return an array of (Search, bar, Javascript). Afterwards, a Full text query can be written using this array.

Madhur Bhaiya
  • 28,155
  • 10
  • 49
  • 57
0

It seems that there is no way to replace the "@" term with any other character. Removing the "@" and adding the string after it to the search is the best workaround that i found so far. That means that

$mail = 'test@test.com';
$mail = str_replace("@", " +", $mail); //replace the @ that causes the problem
$query = "SELECT * FROM clients WHERE MATCH (form) AGAINST ('$mail' IN BOOLEAN MODE)'; //query with replaced mail address

should bring the needed result.

Another way would be to handle it like in this post that contains a similar problem with another good workaround.