diff --git a/src/Lunr/Gravity/Database/MySQL/MySQLCanonicalQuery.php b/src/Lunr/Gravity/Database/MySQL/MySQLCanonicalQuery.php
new file mode 100644
index 0000000000000000000000000000000000000000..f41ab31df3840bade50ec5743fd77438c325803c
--- /dev/null
+++ b/src/Lunr/Gravity/Database/MySQL/MySQLCanonicalQuery.php
@@ -0,0 +1,453 @@
+<?php
+/**
+ * MySQL canonical query class.
+ *
+ * @package    Lunr\Gravity\Database\MySQL
+ * @author     David Mendes <d.mendes@m2mobi.com>
+ * @copyright  2012-2018, M2Mobi BV, Amsterdam, The Netherlands
+ * @license    http://lunr.nl/LICENSE MIT License
+ */
+
+namespace Lunr\Gravity\Database\MySQL;
+
+/**
+ * This class returns the Canonicalized query.
+ */
+class MySQLCanonicalQuery
+{
+    /**
+     * Query to canonicalize
+     * @var $query
+     */
+    private $query;
+
+    /**
+     * Canonicalized query.
+     * @var $canonical_query
+     */
+    private $canonical_query;
+
+    /**
+     * List of index ranges to ignore
+     * @var array
+     */
+    private $ignore_positions;
+
+    /**
+     * Constructor
+     *
+     * @param string $query Executed query
+     */
+    public function __construct(string $query)
+    {
+        $this->query = $query;
+        $this->canonicalize_query();
+    }
+
+    /**
+     * Get the executed query.
+     *
+     * @return string $query The executed query
+     */
+    public function query()
+    {
+        return $this->query;
+    }
+
+    /**
+     * Get the executed query canonicalize .
+     *
+     * @return string $canonicalized_query The executed query canonicalized
+     */
+    public function canonical_query()
+    {
+        return $this->canonical_query;
+    }
+
+    /**
+     * Get the executed query canonicalize .
+     *
+     * @return string $canonicalize_query The executed query canonicalized
+     */
+    public function canonicalize_query()
+    {
+        $this->ignore_positions = [];
+
+        $tmp_query = $this->query();
+        $tmp_query = $this->replace_between ($tmp_query, '#', PHP_EOL, '');
+        $tmp_query = $this->replace_between ($tmp_query, '--', PHP_EOL, '');
+
+        $this->add_ignore_positions($this->find_positions($tmp_query, '/*M', '*/'));
+        $this->add_ignore_positions($this->find_positions($tmp_query, '/*!', '*/'));
+        $this->add_ignore_positions($this->find_positions($tmp_query, '`', '`'));
+        $this->add_ignore_positions($this->find_positions($tmp_query, '\\'));
+
+        $tmp_query = $this->replace_between ($tmp_query, '/*', '*/', '');
+        $tmp_query = $this->replace_between ($tmp_query, '"', '"', '?');
+        $tmp_query = $this->replace_between ($tmp_query, '\'', '\'', '?');
+
+        $tmp_query = $this->replace_numeric($tmp_query, '?');
+
+        $this->canonical_query = $this->remove_eol_blank_spaces($tmp_query);
+
+        return $this->canonical_query;
+    }
+
+    /**
+     * Replaces text in between two strings
+     *
+     * @param string $string  Input string to replace
+     * @param string $from    Input string to start replacing
+     * @param string $to      Input string to end replacing
+     * @param string $replace Input string to replace with
+     * @return string returns replaced $string
+     */
+    private function replace_between (string $string, string $from, string $to, string $replace): string
+    {
+        $offset = 0;
+
+        while ($offset < strlen($string))
+        {
+            $start_pos = strpos($string, $from, $offset);
+
+            if ($start_pos === FALSE)
+            {
+                break;
+            }
+
+            $offset = $this->jump_ignore($start_pos);
+
+            if ($offset > $start_pos)
+            {
+                continue;
+            }
+
+            $end_pos = strpos($string, $to, ($start_pos + 1));
+
+            if ($end_pos === FALSE)
+            {
+                $end_pos == strlen($string ) - 1 - $start_pos;
+            }
+
+            $len_to_remove = ($end_pos + strlen($to)) - $start_pos;
+
+            $string = substr_replace($string, $replace, $start_pos, $len_to_remove);
+
+            $this->ignore_positions = $this->update_positions($this->ignore_positions, $start_pos, $len_to_remove);
+
+            $offset = $start_pos + 1;
+        }
+
+        return $string;
+    }
+
+    /**
+     * Search for all of the indexes ranges in a string between two strings
+     * if $to is empty searches only for $from position
+     *
+     * @param string      $string Input string to search
+     * @param string      $from   Input string to search the start of range
+     * @param string|null $to     Input string to search the end of range
+     * @return array returns all the ranges between $to and $from,
+     *               if can't find $to returns empty,
+     *               if can't find $from returns the end of string,
+     *               if $to is empty returns all $from positions
+     */
+    private function find_positions(string $string, string $from, string $to = NULL): array
+    {
+        if (empty($from))
+        {
+            return [];
+        }
+
+        $positions = [];
+        $offset    = 0;
+        $end       = strlen($string);
+        while ($offset < $end)
+        {
+            $position = [NULL,NULL];
+            $position = [strpos($string, $from, $offset),NULL];
+            if ($position[0] === FALSE)
+            {
+                break;
+            }
+
+            if (empty($to))
+            {
+                $position[1] = $position[0] + strlen($from) - 1;
+                $offset      = $position[1] + 1;
+                $positions[] = $position;
+                continue;
+            }
+
+            $position[1] = strpos($string, $to, ($position[0] + 1));
+
+            if ($position[1] === FALSE)
+            {
+                $position[1] = strlen($string) - 1;
+            }
+            else
+            {
+                $position[1] = $position[1] + strlen($to) - 1;
+            }
+
+            $offset      = $position[1] + 1;
+            $positions[] = $position;
+        }
+
+        return $positions;
+    }
+
+    /**
+     * Removes blank space and end of line characters from string
+     *
+     * @param string $string Input string remove
+     * @return string returns the $string without blank spaces and end of line characters
+     */
+    private function remove_eol_blank_spaces($string)
+    {
+        return trim(preg_replace('/\s+/', ' ', $string));
+    }
+
+    /**
+     * Update all index ranges if after start index
+     *
+     * @param array $positions Input array with ranges of indexes
+     * @return void
+     */
+    private function add_ignore_positions(array $positions): void
+    {
+        $this->ignore_positions = array_merge($this->ignore_positions, $positions);
+        asort($this->ignore_positions);
+        $this->ignore_positions = array_values($this->ignore_positions);
+    }
+
+    /**
+     * Update all index ranges if after start index
+     *
+     * @param array   $positions Input array with ranges of indexes
+     * @param integer $start     Input integer to start the update
+     * @param integer $offset    Input integer with the amount to offset positions
+     * @return array returns $positions updated
+     */
+    private function update_positions(array $positions, int $start, int $offset): array
+    {
+        foreach ($positions as $k => $position)
+        {
+            $positions[$k][0] = ($position[0] < $start) ? $position[0] : $position[0] - $offset;
+            $positions[$k][1] = ($position[1] < $start) ? $position[1] : $position[1] - $offset;
+        }
+
+        return $positions;
+    }
+
+    /**
+     * Get the index position of the next digit from offset
+     *
+     * @param string  $string Input string where to search for the next index position
+     * @param integer $offset Input integer where to start to search
+     * @return integer|boolean return Index of the next digit, FALSE if not found
+     */
+    private function find_digit(string $string, int $offset)
+    {
+        $end = strlen($string);
+        for ($i = $offset; $i < $end; ++$i)
+        {
+            $jump = $this->jump_ignore($i);
+            if ($jump > $i)
+            {
+                $i = $jump;
+                continue;
+            }
+
+            if ($this->is_digit($string[$i]) && ($i == 0 || (!$this->is_alnum(substr($string, $i - 1, 1)) && substr($string, $i - 1, 1) != '_')))
+            {
+                return $i;
+            }
+        }
+
+        return FALSE;
+    }
+
+    /**
+     * Replaces numeric value (Integer, Decimal, Hexadecimal, Exponential) with string
+     *
+     * @param string $string  Input string to replace
+     * @param string $replace Input string to replace with
+     * @return string returns string with numeric values replaced
+     */
+    private function replace_numeric($string, $replace): string
+    {
+        //find digit
+        $end = strlen($string);
+        for ($i = 0; $i < $end; ++$i)
+        {
+            $pos = $this->find_digit($string, $i);
+            if ($pos === FALSE)
+            {
+                break;
+            }
+
+            $number_end = $this->is_numeric_value($string, $pos);
+
+            $i = $number_end[1];
+
+            if ($number_end[0] === FALSE)
+            {
+                continue;
+            }
+
+            if ($this->is_negative_number($string, $pos))
+            {
+                $pos--;
+            }
+
+            $replace_size           = strlen($replace);
+            $to_replace_length      = $number_end[1] - $pos + 1;
+            $string                 = substr_replace($string, $replace, $pos, $to_replace_length);
+            $this->ignore_positions = $this->update_positions($this->ignore_positions, $pos, $to_replace_length - $replace_size);
+
+            $i = $pos + $replace_size;
+        }
+
+        return $string;
+    }
+
+    /**
+     * Check if number is a negation
+     *
+     * @param string  $string Input string to check
+     * @param integer $i      Start index of the number position
+     * @return boolean returns TRUE if finds the negation character and is not a subtraction, FALSE otherwise
+     */
+    private function is_negative_number(string $string, int $i): bool
+    {
+        $is_negative_number = FALSE;
+        if ($i > 0 && $string[$i - 1] == '-')
+        {
+            $i--;
+            // Possibly a negative number
+            $is_negative_number = TRUE;
+            for ($j = $i - 1; $j >= 0; $j--)
+            {
+                if (!ctype_space($string[$j]))
+                {
+                    /** If we find a previously converted value, we know that it
+                     * is not a negation but a subtraction. */
+                    $is_negative_number = ($string[$j] != '?');
+                    break;
+                }
+            }
+        }
+
+        return $is_negative_number;
+    }
+
+    /**
+     * Checks if the string provided is a blank space
+     *
+     * @param string $val Input string to check
+     * @return boolean returns TRUE if $val is a decimal digit, FALSE otherwise
+     */
+    private function is_digit(string $val): bool
+    {
+        return ctype_digit($val);
+    }
+
+    /**
+     * Checks if the provided argument is alphanumeric
+     *
+     * @param string $val Input string to check
+     * @return boolean returns TRUE if $val is alphanumeric, FALSE otherwise
+     */
+    private function is_alnum(string $val): bool
+    {
+        return ctype_alnum($val);
+    }
+
+    /**
+     * Check if digit is a number and get the position of last number digit
+     *
+     * @param string  $string Input string to check
+     * @param integer $i      Start position of digit index
+     * @return array $return the last index of number and is number result
+     */
+    private function is_numeric_value(string $string, int $i): array
+    {
+        $allow_hex      = FALSE;
+        $is_hexadecimal = (($string[$i] == '0'));
+
+        $i++; //first number we already know is numeric
+        $end       = strlen($string);
+        $is_number = TRUE;
+
+        while ($i < $end)
+        {
+            if (!($this->is_digit($string[$i]) || ($allow_hex && ctype_xdigit($string[$i]))))
+            {
+                if ($is_hexadecimal == TRUE && strtolower($string[$i]) == 'x')
+                {
+                    $is_hexadecimal = FALSE;
+                    $allow_hex      = TRUE;
+                }
+                elseif (strtolower($string[$i]) == 'e')
+                {
+                    $next = $i + 1;
+                    // Possible scientific notation number
+                    if ($next == $end || (!$this->is_digit($string[$next]) && $string[$next] != '-'))
+                    {
+                        $i         = ++$next;
+                        $is_number = FALSE;
+                        break;
+                    }
+
+                    // Skip over the minus if we have one
+                    if ($string[$next] == '-')
+                    {
+                        $i++;
+                    }
+                }
+                elseif ($string[$i] == '.')
+                {
+                    $next = $i + 1;
+                    // Possible decimal number
+                    if ($next != $end && !$this->is_digit($string[$next]))
+                    {
+                        /** The fractional part of a decimal is optional in MariaDB. */
+                        break;
+                    }
+                }
+                else
+                {
+                    // If we have a non-text character, we treat it as a number
+                    $is_number = !ctype_alpha($string[$i]);
+                    break;
+                }
+            }
+
+            $i++;
+        }
+
+        return [$is_number,--$i];
+    }
+
+    /**
+     * Checks if index is in a position to ignore, and returns next position
+     *
+     * @param integer $index Input integer to check if is between ranges
+     * @return integer returns the position after the range to ignore, if not in range returns the provided index
+     */
+    private function jump_ignore(int $index): int
+    {
+        foreach ($this->ignore_positions as $position)
+        {
+            if ($position[0] <= $index && $position[1] >= $index)
+            {
+                $index = $position[1] + 1;
+            }
+        }
+
+        return $index;
+    }
+
+}
diff --git a/src/Lunr/Gravity/Database/MySQL/MySQLQueryResult.php b/src/Lunr/Gravity/Database/MySQL/MySQLQueryResult.php
index 16319e94cc26ccb1cf7ff952b89cde0e667a1a2b..6e42faf776c4aee88745e444934df48f0b565ada 100644
--- a/src/Lunr/Gravity/Database/MySQL/MySQLQueryResult.php
+++ b/src/Lunr/Gravity/Database/MySQL/MySQLQueryResult.php
@@ -38,6 +38,12 @@ class MySQLQueryResult implements DatabaseQueryResultInterface
      */
     protected $query;
 
+    /**
+     * The canonicalized query string that was executed.
+     * @var String
+     */
+    protected $canonical_query;
+
     /**
      * Return value from mysqli->query().
      * @var mixed
@@ -142,6 +148,7 @@ class MySQLQueryResult implements DatabaseQueryResultInterface
         unset($this->error_number);
         unset($this->insert_id);
         unset($this->query);
+        unset($this->canonical_query);
     }
 
     /**
@@ -335,6 +342,24 @@ class MySQLQueryResult implements DatabaseQueryResultInterface
         return isset($line[$column]) ? $line[$column] : NULL;
     }
 
+    /**
+     * Get the executed query canonicalize .
+     *
+     * @return string $canonicalized_query The executed query canonicalized
+     */
+    public function canonical_query(): string
+    {
+        if (isset($this->canonical_query))
+        {
+            return $this->canonical_query;
+        }
+
+        $canonical_query       = new MySQLCanonicalQuery($this->query());
+        $this->canonical_query = $canonical_query->canonical_query();
+
+        return $this->canonical_query;
+    }
+
 }
 
 ?>
diff --git a/src/Lunr/Gravity/Database/MySQL/Tests/MySQLCanonicalQueryTest.php b/src/Lunr/Gravity/Database/MySQL/Tests/MySQLCanonicalQueryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..6da612e90f31298da966432c352b3d411efcaa5b
--- /dev/null
+++ b/src/Lunr/Gravity/Database/MySQL/Tests/MySQLCanonicalQueryTest.php
@@ -0,0 +1,451 @@
+<?php
+/**
+ * This file contains the MySQLConnectionEscapeTest class.
+ *
+ * @package    Lunr\Gravity\Database\MySQL
+ * @author     Heinz Wiesinger <heinz@m2mobi.com>
+ * @copyright  2012-2018, M2Mobi BV, Amsterdam, The Netherlands
+ * @license    http://lunr.nl/LICENSE MIT License
+ */
+
+namespace Lunr\Gravity\Database\MySQL\Tests;
+
+use Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery;
+use Lunr\Gravity\Database\MySQL\MySQLQueryResult;
+use Lunr\Halo\LunrBaseTest;
+use ReflectionClass;
+
+/**
+ * This class contains unit tests for MySQLCanonicalQuery.
+ *
+ * @covers Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery
+ */
+class MySQLCanonicalQueryTest extends LunrBaseTest
+{
+    /**
+     * Reflection instance of the MySQLCanonicalQuery class.
+     * @var ReflectionClass
+     */
+    protected $reflection_class;
+
+    /**
+     * Instance of the MySQLCanonicalQuery class.
+     * @var MySQLCanonicalQuery
+     */
+    protected $test_class;
+
+    /**
+     * TestCase Constructor.
+     */
+    public function setUp(): void
+    {
+         $this->reflection_class = new ReflectionClass('Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery');
+         $this->test_class       = new MySQLCanonicalQuery('');
+    }
+
+    /**
+     * TestCase Destructor.
+     */
+    public function tearDown(): void
+    {
+        unset($this->reflection_class);
+        unset($this->test_class);
+    }
+
+    /**
+     * Test that query() returns the given string.
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::query
+     */
+    public function testQuery(): void
+    {
+        $property = $this->reflection_class->getProperty('query');
+        $property->setAccessible(TRUE);
+        $property->setValue($this->test_class, 'SELECT * FROM table1 WHERE value="A"');
+
+        $value = $this->test_class->query();
+        $this->assertIsString($value);
+        $this->assertEquals('SELECT * FROM table1 WHERE value="A"', $value);
+    }
+
+    /**
+     * Test that canonical_query() returns the canonical query.
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::canonical_query
+     */
+    public function testCanonicalQuery(): void
+    {
+        $property = $this->reflection_class->getProperty('canonical_query');
+        $property->setAccessible(TRUE);
+        $property->setValue($this->test_class, 'SELECT * FROM table1 WHERE value=?');
+
+        $value = $this->test_class->canonical_query();
+        $this->assertIsString($value);
+        $this->assertEquals('SELECT * FROM table1 WHERE value=?', $value);
+    }
+
+    /**
+     * Test that replace_between() returns replaced string with correctly.
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::replace_between
+     */
+    public function testReplaceBetween(): void
+    {
+        $property = $this->reflection_class->getProperty('ignore_positions');
+        $property->setAccessible(TRUE);
+        $property->setValue($this->test_class, [[21,28]]);
+
+        $method = $this->reflection_class->getMethod('replace_between');
+        $method->setAccessible(TRUE);
+
+        //test first argument not in string
+        $result = $method->invokeArgs($this->test_class, ['SELECT * FROM value1="ignore" AND value2="B"', '"', '"', '?']);
+        $this->assertIsString($result);
+        $this->assertEquals('SELECT * FROM value1="ignore" AND value2=?', $result);
+    }
+
+    /**
+     * Test that find_positions() returns the correct positions.
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::find_positions
+     */
+    public function testFindPositions(): void
+    {
+        $method = $this->reflection_class->getMethod('find_positions');
+        $method->setAccessible(TRUE);
+
+        //test first argument not in string
+        $result = $method->invokeArgs($this->test_class, ['013456789013456789','23','56']);
+        $this->assertIsArray($result);
+        $this->assertEquals([], $result);
+
+        //test second argument not in string
+        $result = $method->invokeArgs($this->test_class, ['012346789012346789','23','56']);
+        $this->assertIsArray($result);
+        $this->assertEquals([[2,17]], $result);
+
+        //test both arguments in string
+        $result = $method->invokeArgs($this->test_class, ['01234567890123456789','23','56']);
+        $this->assertIsArray($result);
+        $this->assertEquals([[2,6],[12,16]], $result);
+
+        //test both arguments not in string
+        $result = $method->invokeArgs($this->test_class, ['01234567890123456789','bb','cc']);
+        $this->assertIsArray($result);
+        $this->assertEquals([], $result);
+
+        //test both arguments not in string
+        $result = $method->invokeArgs($this->test_class, ['01234567890123456789','56','']);
+        $this->assertIsArray($result);
+        $this->assertEquals([[5,6],[15,16]], $result);
+
+        //test both arguments not in string
+        $result = $method->invokeArgs($this->test_class, ['01234567890123456789','5','']);
+        $this->assertIsArray($result);
+        $this->assertEquals([[5,5],[15,15]], $result);
+
+        //test both arguments not in string
+        $result = $method->invokeArgs($this->test_class, ['01234567890123456789','','5']);
+        $this->assertIsArray($result);
+        $this->assertEquals([], $result);
+    }
+
+    /**
+     * Test that remove_eol_blank_spaces() returns string without double blank spaces and end of line characters.
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::remove_eol_blank_spaces
+     */
+    public function testRemoveEolBlankSpaces(): void
+    {
+        $method = $this->reflection_class->getMethod('remove_eol_blank_spaces');
+        $method->setAccessible(TRUE);
+
+        $string_d_blank = 'SELECT     * FROM `table`';
+        $string_eol     = 'SELECT *
+                           FROM `table`';
+        $string_eoln    = "SELECT * \nFROM `table`";
+        $string_eolrn   = "SELECT * \r\nFROM `table`";
+        $string_eolr    = "SELECT     * \rFROM `table`";
+
+        $result = $method->invokeArgs($this->test_class, [$string_d_blank]);
+        $this->assertIsString($result);
+        $this->assertEquals('SELECT * FROM `table`', $result);
+
+        $result = $method->invokeArgs($this->test_class, [$string_eol]);
+        $this->assertIsString($result);
+        $this->assertEquals('SELECT * FROM `table`', $result);
+
+        $result = $method->invokeArgs($this->test_class, [$string_eoln]);
+        $this->assertIsString($result);
+        $this->assertEquals('SELECT * FROM `table`', $result);
+
+        $result = $method->invokeArgs($this->test_class, [$string_eolrn]);
+        $this->assertIsString($result);
+        $this->assertEquals('SELECT * FROM `table`', $result);
+
+        $result = $method->invokeArgs($this->test_class, [$string_eolr]);
+        $this->assertIsString($result);
+        $this->assertEquals('SELECT * FROM `table`', $result);
+    }
+
+    /**
+     * Test that escape_string() properly escapes the given string.
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::update_positions
+     */
+    public function testUpdatePositions(): void
+    {
+        $method = $this->reflection_class->getMethod('update_positions');
+        $method->setAccessible(TRUE);
+
+        $result = $method->invokeArgs($this->test_class, [[[3,10],[25,30],[40,50]],15,5]);
+
+        $this->assertIsArray($result);
+        $this->assertEquals([[3,10],[20,25],[35,45]], $result);
+    }
+
+    //__construct
+
+    /**
+     * Test that find_digit() finds next digit if exists
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::find_digit
+     */
+    public function testFindDigit(): void
+    {
+        $property = $this->reflection_class->getProperty('ignore_positions');
+        $property->setAccessible(TRUE);
+        $property->setValue($this->test_class, [[29,36],[50,57]]);
+
+        $method = $this->reflection_class->getMethod('find_digit');
+        $method->setAccessible(TRUE);
+
+        $result = $method->invokeArgs($this->test_class, ['SELECT * FROM `table1` WHERE `value1`="teste" AND `value2`=12', 22]);
+
+        $this->assertIsInt($result);
+        $this->assertEquals(59, $result);
+
+        $result = $method->invokeArgs($this->test_class, ['SELECT * FROM `table1` WHERE `value1`="teste" AND `value2`="A"', 22]);
+
+        $this->assertIsBool($result);
+        $this->assertEquals(FALSE, $result);
+    }
+
+    /**
+     * Test that jump_ignore() returns the index after positions to ignore or the same index case is not between ranges
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::jump_ignore
+     */
+    public function testJumpIgnore(): void
+    {
+        $property = $this->reflection_class->getProperty('ignore_positions');
+        $property->setAccessible(TRUE);
+        $property->setValue($this->test_class, [[0,10],[10,100],[102,110]]);
+
+        $method = $this->reflection_class->getMethod('jump_ignore');
+        $method->setAccessible(TRUE);
+
+        $result = $method->invokeArgs($this->test_class, [0]);
+
+        $this->assertIsInt($result);
+        $this->assertEquals(101, $result);
+    }
+
+    /**
+     * Test that is_digit() returns true if provided value is a digit, otherwise returns false
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::is_digit
+     */
+    public function testIsDigit(): void
+    {
+        $method = $this->reflection_class->getMethod('is_digit');
+        $method->setAccessible(TRUE);
+
+        $result = $method->invokeArgs($this->test_class, ['1']);
+
+        $this->assertIsBool($result);
+        $this->assertEquals(TRUE, $result);
+
+        $result = $method->invokeArgs($this->test_class, ['.']);
+
+        $this->assertIsBool($result);
+        $this->assertEquals(FALSE, $result);
+    }
+
+    /**
+     * Test that is_alnum() returns true if provided value is a alphanumeric character, otherwise returns false
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::is_alnum
+     */
+    public function testIsAlnum(): void
+    {
+        $method = $this->reflection_class->getMethod('is_alnum');
+        $method->setAccessible(TRUE);
+
+        $result = $method->invokeArgs($this->test_class, ['1Z']);
+
+        $this->assertIsBool($result);
+        $this->assertEquals(TRUE, $result);
+
+        $result = $method->invokeArgs($this->test_class, ['.']);
+
+        $this->assertIsBool($result);
+        $this->assertEquals(FALSE, $result);
+    }
+
+    /**
+     * Test that is_numeric_value() returns true and the end position of the numeric value, and false if its not numeric
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::is_numeric_value
+     */
+    public function testIsNumericValue(): void
+    {
+        $method = $this->reflection_class->getMethod('is_numeric_value');
+        $method->setAccessible(TRUE);
+
+        $result = $method->invokeArgs($this->test_class, ['value=234567 AND',6]);
+
+        $this->assertIsArray($result);
+        $this->assertIsBool($result[0]);
+        $this->assertIsInt($result[1]);
+
+        $this->assertEquals([TRUE,11], $result);
+
+        $result = $method->invokeArgs($this->test_class, ['value=0x47 AND',6]);
+
+        $this->assertIsArray($result);
+        $this->assertIsBool($result[0]);
+        $this->assertIsInt($result[1]);
+
+        $this->assertEquals([TRUE,9], $result);
+
+        $result = $method->invokeArgs($this->test_class, ['value=1.245 AND',6]);
+
+        $this->assertIsArray($result);
+        $this->assertIsBool($result[0]);
+        $this->assertIsInt($result[1]);
+
+        $this->assertEquals([TRUE,10], $result);
+
+        $result = $method->invokeArgs($this->test_class, ['value=3.82384E-11 AND',6]);
+
+        $this->assertIsArray($result);
+        $this->assertIsBool($result[0]);
+        $this->assertIsInt($result[1]);
+
+        $this->assertEquals([TRUE,16], $result);
+
+        $result = $method->invokeArgs($this->test_class, ['value=3.82384E+11 AND',6]);//check plus char
+
+        $this->assertIsArray($result);
+        $this->assertIsBool($result[0]);
+        $this->assertIsInt($result[1]);
+
+        $this->assertEquals([FALSE,14], $result);
+    }
+
+    /**
+     * Test that add_ignore_positions() returns true and the end position of the numeric value, and false if its not numeric
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::add_ignore_positions
+     */
+    public function testAddIgnorePositions(): void
+    {
+        $method = $this->reflection_class->getMethod('add_ignore_positions');
+        $method->setAccessible(TRUE);
+
+        $method->invokeArgs($this->test_class, [[[40,50]]]);
+        $method->invokeArgs($this->test_class, [[[3,10],[25,30]]]);
+
+        $property = $this->reflection_class->getProperty('ignore_positions');
+        $property->setAccessible(TRUE);
+        $result = $property->getValue($this->test_class);
+
+        $this->assertIsArray($result);
+        $this->assertEquals([[3,10],[25,30],[40,50]], $result);
+    }
+
+    /**
+     * Test that is_negative_number()
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::is_negative_number
+     */
+    public function testIsNegativeNumber(): void
+    {
+        $method = $this->reflection_class->getMethod('is_negative_number');
+        $method->setAccessible(TRUE);
+
+        $result = $method->invokeArgs($this->test_class, ['value=123',6]);
+        $this->assertIsBool($result);
+        $this->assertEquals(FALSE, $result);
+
+        $result = $method->invokeArgs($this->test_class, ['value=-123',7]);
+        $this->assertIsBool($result);
+        $this->assertEquals(TRUE, $result);
+
+        $result = $method->invokeArgs($this->test_class, ['value=?-123',8]);
+        $this->assertIsBool($result);
+        $this->assertEquals(FALSE, $result);
+    }
+
+    /**
+     * Test that replace_numeric() returns the query string without numeric values and does not remove numeric non values,
+     * numeric values includes numeric, decimal, exponential, hexadecimal, negative
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::replace_numeric
+     */
+    public function testReplaceNumeric(): void
+    {
+        $method = $this->reflection_class->getMethod('replace_numeric');
+        $method->setAccessible(TRUE);
+
+        $result = $method->invokeArgs($this->test_class, ['value=-12 and','?']);
+        $this->assertIsString($result);
+        $this->assertEquals('value=? and', $result);
+
+        $result = $method->invokeArgs($this->test_class, ['value=0x24 and','?']);
+        $this->assertIsString($result);
+        $this->assertEquals('value=? and', $result);
+
+        $result = $method->invokeArgs($this->test_class, ['value=1.24 and','?']);
+        $this->assertIsString($result);
+        $this->assertEquals('value=? and', $result);
+
+        $result = $method->invokeArgs($this->test_class, ['value=-1.24 and','?']);
+        $this->assertIsString($result);
+        $this->assertEquals('value=? and', $result);
+
+        $result = $method->invokeArgs($this->test_class, ['value=3.8E-11 and','?']);
+        $this->assertIsString($result);
+        $this->assertEquals('value=? and', $result);
+
+        $result = $method->invokeArgs($this->test_class, ['value=2-1 and','?']);
+        $this->assertIsString($result);
+        $this->assertEquals('value=?-? and', $result);
+
+        $property = $this->reflection_class->getProperty('ignore_positions');
+        $property->setAccessible(TRUE);
+        $property->setValue($this->test_class, [[32,41]]);
+
+        $result = $method->invokeArgs($this->test_class, ['value1=123456 and value2=123456 /*! 123 */','?']);
+        $this->assertIsString($result);
+        $this->assertEquals('value1=? and value2=? /*! 123 */', $result);
+    }
+
+    /**
+     * Test that canonicalize_query() returns the canonical query.
+     *
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::canonicalize_query
+     */
+    public function testCanonicalizeQuery(): void
+    {
+        $property = $this->reflection_class->getProperty('query');
+        $property->setAccessible(TRUE);
+
+        $property->setValue($this->test_class, 'SELECT * FROM table1 WHERE value1="teste" and value2=-12');
+        $value = $this->test_class->canonicalize_query();
+        $this->assertIsString($value);
+        $this->assertEquals('SELECT * FROM table1 WHERE value1=? and value2=?', $value);
+    }
+
+}
diff --git a/src/Lunr/Gravity/Database/MySQL/Tests/MySQLQueryResultBaseTest.php b/src/Lunr/Gravity/Database/MySQL/Tests/MySQLQueryResultBaseTest.php
index 58652170e73bb71819568489a13f6a7ef16da759..3a28b975ec10616ba82c4b290f109ddbe841cb08 100644
--- a/src/Lunr/Gravity/Database/MySQL/Tests/MySQLQueryResultBaseTest.php
+++ b/src/Lunr/Gravity/Database/MySQL/Tests/MySQLQueryResultBaseTest.php
@@ -205,6 +205,37 @@ class MySQLQueryResultBaseTest extends MySQLQueryResultTest
         $this->assertSame($this->mysqli, $value);
     }
 
+    /**
+     * Test that canonicalized_query() returns a string.
+     *
+     * @covers Lunr\Gravity\Database\MySQL\MySQLQueryResult::canonical_query
+     */
+    public function testCanonicalQueryReturnsString(): void
+    {
+        $class = $this->result_reflection->getProperty('canonical_query');
+        $class->setAccessible(TRUE);
+        $class->setValue($this->result, 'SELECT * FROM table1 WHERE value=?');
+
+        $value = $this->result->canonical_query();
+        $this->assertIsString($value);
+        $this->assertEquals('SELECT * FROM table1 WHERE value=?', $value);
+    }
+
+    /**
+     * Test that canonicalized_query() returns a string.
+     *
+     * @covers Lunr\Gravity\Database\MySQL\MySQLQueryResult::canonical_query
+     */
+    public function testCanonicalQueryEmptyReturnsString(): void
+    {
+        $class = $this->result_reflection->getProperty('query');
+        $class->setAccessible(TRUE);
+        $class->setValue($this->result, 'SELECT * FROM table2 WHERE value1="teste"');
+        $value = $this->result->canonical_query();
+        $this->assertIsString($value);
+        $this->assertEquals('SELECT * FROM table2 WHERE value1=?', $value);
+    }
+
 }
 
 ?>
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
index 5b6ebc7c724fe07eb344f72fdd01c6a4c29b8a51..8bb4eeaef5a74d2f2ff51a74938cd5cfa57ba8d4 100644
--- a/tests/phpunit.xml
+++ b/tests/phpunit.xml
@@ -145,6 +145,7 @@
             <file>../src/Lunr/Gravity/Database/MySQL/Tests/MySQLSimpleDMLQueryBuilderWriteTest.php</file>
             <file>../src/Lunr/Gravity/Database/MySQL/Tests/MySQLSimpleDMLQueryBuilderFluidInterfaceTest.php</file>
             <file>../src/Lunr/Gravity/Database/MySQL/Tests/MySQLSimpleDMLQueryBuilderUsingTest.php</file>
+            <file>../src/Lunr/Gravity/Database/MySQL/Tests/MySQLCanonicalQueryTest.php</file>
             <file>../src/Lunr/Gravity/Database/Tests/DatabaseConnectionBaseTest.php</file>
             <file>../src/Lunr/Gravity/Database/MySQL/Tests/MySQLConnectionBaseTest.php</file>
             <file>../src/Lunr/Gravity/Database/MySQL/Tests/MySQLConnectionSetTest.php</file>
