diff --git a/src/Lunr/Gravity/Database/MySQL/MySQLCanonicalQuery.php b/src/Lunr/Gravity/Database/MySQL/MySQLCanonicalQuery.php
new file mode 100644
index 0000000000000000000000000000000000000000..ac392cc101962477fb93505f1d79d183a8be00dc
--- /dev/null
+++ b/src/Lunr/Gravity/Database/MySQL/MySQLCanonicalQuery.php
@@ -0,0 +1,426 @@
+<?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 string
+     */
+    private $query;
+
+    /**
+     * Canonicalized query.
+     * @var string
+     */
+    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;
+    }
+
+    /**
+     * __toString.
+     *
+     * @return string $returns The executed query canonicalized
+     */
+    public function __toString(): string
+    {
+        return $this->get_canonical_query();
+    }
+
+    /**
+     * Get the executed query canonicalized.
+     *
+     * @return string $canonical_query The executed query canonicalized
+     */
+    public function get_canonical_query(): string
+    {
+        if ($this->canonical_query !== NULL)
+        {
+            return $this->canonical_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 $string The provided string replaced
+     */
+    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 $positions All 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 = [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): string
+    {
+        return trim(preg_replace('/\s+/', ' ', $string));
+    }
+
+    /**
+     * Add a range of indexes to $ignore_positions
+     *
+     * @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 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|null return Index of the next digit, NULL if not found
+     */
+    private function find_digit(string $string, int $offset): ?int
+    {
+        $end = strlen($string);
+        for ($i = $offset; $i < $end; ++$i)
+        {
+            $jump = $this->jump_ignore($i);
+            if ($jump > $i)
+            {
+                $i = $jump;
+                continue;
+            }
+
+            if (ctype_digit($string[$i]) && ($i == 0 || (!ctype_alnum(substr($string, $i - 1, 1)) && substr($string, $i - 1, 1) != '_')))
+            {
+                return $i;
+            }
+        }
+
+        return NULL;
+    }
+
+    /**
+     * 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
+    {
+        $end = strlen($string);
+        for ($i = 0; $i < $end; ++$i)
+        {
+            $pos = $this->find_digit($string, $i);
+            if (!isset($pos))
+            {
+                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;
+    }
+
+    /**
+     * 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 (!(ctype_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 || (!ctype_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 && !ctype_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;
+    }
+
+}
+
+$teste = new MySQLCanonicalQuery('select "teste" value="teste" ');
+echo $teste->get_canonical_query();
diff --git a/src/Lunr/Gravity/Database/MySQL/MySQLQueryResult.php b/src/Lunr/Gravity/Database/MySQL/MySQLQueryResult.php
index 16319e94cc26ccb1cf7ff952b89cde0e667a1a2b..b4f7349297b87c47cf5615ecc1a1ba967eceb7e5 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,23 @@ class MySQLQueryResult implements DatabaseQueryResultInterface
         return isset($line[$column]) ? $line[$column] : NULL;
     }
 
+    /**
+     * Get the executed query canonicalized.
+     *
+     * @return string $canonicalized_query The executed query canonicalized
+     */
+    public function canonical_query(): string
+    {
+        if (isset($this->canonical_query))
+        {
+            return $this->canonical_query;
+        }
+
+        $this->canonical_query = new MySQLCanonicalQuery($this->query());
+
+        return $this->canonical_query;
+    }
+
 }
 
 ?>
diff --git a/src/Lunr/Gravity/Database/MySQL/Tests/MySQLCanonicalQueryBaseTest.php b/src/Lunr/Gravity/Database/MySQL/Tests/MySQLCanonicalQueryBaseTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..967a651f5bcd259e9e0cfbc782e853a98f0ce85a
--- /dev/null
+++ b/src/Lunr/Gravity/Database/MySQL/Tests/MySQLCanonicalQueryBaseTest.php
@@ -0,0 +1,232 @@
+<?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;
+
+/**
+ * This class contains unit tests for MySQLCanonicalQuery.
+ *
+ * @covers Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery
+ */
+class MySQLCanonicalQueryBaseTest extends MySQLCanonicalQueryTest
+{
+
+    /**
+     * Test that replace_between() returns replaced string correctly.
+     *
+     * @covers Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::replace_between
+     */
+    public function testReplaceBetween(): void
+    {
+        $property = $this->get_accessible_reflection_property('ignore_positions');
+        $property->setValue($this->test_class, [[21,28]]);
+
+        $method = $this->get_accessible_reflection_method('replace_between');
+
+        //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.
+     *
+     * @param array $data     Data values
+     * @param array $expected Modified data values
+     *
+     * @dataProvider findPositionsDataProvider
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::find_positions
+     */
+    public function testFindPositions(array $data, array $expected): void
+    {
+        $method = $this->get_accessible_reflection_method('find_positions');
+
+        $result = $method->invokeArgs($this->test_class, $data );
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * Test that remove_eol_blank_spaces() returns string without double blank spaces and end of line characters.
+     *
+     * @param string $data Data values
+     *
+     * @dataProvider removeEolBlankSpacesDataProvider
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::remove_eol_blank_spaces
+     */
+    public function testRemoveEolBlankSpaces(string $data): void
+    {
+        $method = $this->get_accessible_reflection_method('remove_eol_blank_spaces');
+
+        $result = $method->invokeArgs($this->test_class, [$data]);
+        $this->assertEquals('SELECT * FROM `table`', $result);
+    }
+
+    /**
+     * Test that escape_string() properly escapes the given string.
+     *
+     * @param array $data     Data values
+     * @param array $expected Modified values
+     *
+     * @dataProvider updatePositionsDataProvider
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::update_positions
+     */
+    public function testUpdatePositions(array $data, array $expected): void
+    {
+        $method = $this->get_accessible_reflection_method('update_positions');
+
+        $result = $method->invokeArgs($this->test_class, $data);
+
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * Test that find_digit() finds next digit if exists
+     *
+     * @param array $data     Data values
+     * @param mixed $expected Modified values
+     *
+     * @dataProvider findDigitDataProvider
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::find_digit
+     */
+    public function testFindDigit(array $data, $expected): void
+    {
+        $property = $this->get_accessible_reflection_property('ignore_positions');
+        $property->setValue($this->test_class, ($data[1] ?? []));
+
+        $method = $this->get_accessible_reflection_method('find_digit');
+
+        $result = $method->invokeArgs($this->test_class, $data[0]);
+
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * Test that jump_ignore() returns the index after positions to ignore or the same index case is not between ranges
+     *
+     * @param array   $data     Data values
+     * @param integer $expected Modified values
+     *
+     * @dataProvider jumpIgnoreDataProvider
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::jump_ignore
+     */
+    public function testJumpIgnore(array $data, int $expected): void
+    {
+        $property = $this->get_accessible_reflection_property('ignore_positions');
+        $property->setValue($this->test_class, ($data[1] ?? []));
+
+        $method = $this->get_accessible_reflection_method('jump_ignore');
+
+        $result = $method->invokeArgs($this->test_class, [$data[0]]);
+
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * Test that is_numeric_value() returns true and the end position of the numeric value, and false if its not numeric
+     *
+     * @param array $data     Data values
+     * @param array $expected Modified data values
+     *
+     * @dataProvider isNumericValueDataProvider
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::is_numeric_value
+     */
+    public function testIsNumericValue(array $data, array $expected): void
+    {
+        $method = $this->get_accessible_reflection_method('is_numeric_value');
+
+        $result = $method->invokeArgs($this->test_class, $data);
+
+        $this->assertIsBool($result[0]);
+        $this->assertIsInt($result[1]);
+
+        $this->assertEquals($expected, $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
+    {
+        $property = $this->get_accessible_reflection_property('ignore_positions');
+        $property->setValue($this->test_class, []);
+
+        $method = $this->get_accessible_reflection_method('add_ignore_positions');
+        $method->invokeArgs($this->test_class, [[[40,50]]]);
+        $method->invokeArgs($this->test_class, [[[3,10],[25,30]]]);
+
+        $result = $property->getValue($this->test_class);
+
+        $this->assertIsArray($result);
+        $this->assertEquals([[4,10],[25,30],[40,50]], $result);
+    }
+
+    /**
+     * Test that is_negative_number()
+     *
+     * @param array   $data     Data values
+     * @param boolean $expected Modified data values
+     *
+     * @dataProvider isNegativeNumberDataProvider
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::is_negative_number
+     */
+    public function testIsNegativeNumber(array $data, bool $expected): void
+    {
+        $method = $this->get_accessible_reflection_method('is_negative_number');
+
+        $result = $method->invokeArgs($this->test_class, $data);
+        $this->assertIsBool($result);
+        $this->assertEquals($expected, $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
+     *
+     * @param array  $data     Data values
+     * @param string $expected Modified data values
+     *
+     * @dataProvider replaceNumericDataProvider
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::replace_numeric
+     */
+    public function testReplaceNumeric(array $data, string $expected): void
+    {
+        $property = $this->get_accessible_reflection_property('ignore_positions');
+        $property->setValue($this->test_class, ($data[1] ?? []));
+
+        $method = $this->get_accessible_reflection_method('replace_numeric');
+
+        $result = $method->invokeArgs($this->test_class, $data[0]);
+
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * Test that get_canonical_query() returns the canonical query.
+     *
+     * @param string $data     Data values
+     * @param string $expected Modified data values
+     *
+     * @dataProvider canonicalQueryDataProvider
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::get_canonical_query
+     */
+    public function testCanonicalQuery(string $data, string $expected): void
+    {
+        $property = $this->get_accessible_reflection_property('query');
+
+        $property->setValue($this->test_class, $data);
+        $value = $this->test_class->get_canonical_query();
+        $this->assertIsString($value);
+        $this->assertEquals($expected, $value);
+    }
+
+}
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..ce46bbf5a4f23d03375d5ebf84bb415c5781b9e1
--- /dev/null
+++ b/src/Lunr/Gravity/Database/MySQL/Tests/MySQLCanonicalQueryTest.php
@@ -0,0 +1,189 @@
+<?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\Halo\LunrBaseTest;
+use ReflectionClass;
+
+/**
+ * This class contains unit tests for MySQLCanonicalQuery.
+ *
+ * @covers Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery
+ */
+class MySQLCanonicalQueryTest extends LunrBaseTest
+{
+    /**
+     * Instance of the MySQLCanonicalQuery class.
+     * @var MySQLCanonicalQuery
+     */
+    protected $test_class;
+    /**
+     * @var ReflectionClass
+     */
+
+    /**
+     * TestCase Constructor.
+     */
+    public function setUp(): void
+    {
+         $this->reflection = new ReflectionClass('Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery');
+         $this->test_class = new MySQLCanonicalQuery('');
+    }
+
+    /**
+     * TestCase Destructor.
+     */
+    public function tearDown(): void
+    {
+        unset($this->reflection);
+        unset($this->test_class);
+    }
+
+    /**
+     * Unit Test Data Provider.
+     *
+     * @return array $data_provider Array of data values.
+     */
+    public function findPositionsDataProvider(): array
+    {
+        $data_provider   = [];
+        $data_provider[] = [['013456789013456789','23','56'], []];                //test that first argument is not in string
+        $data_provider[] = [['012346789012346789','23','56'], [[2,17]]];          //test that second argument is not in string
+        $data_provider[] = [['01234567890123456789','23','56'], [[2,6],[12,16]]]; //test that both arguments are in the string
+        $data_provider[] = [['01234567890123456789','bb','cc'], []];              //test that both arguments are not in the string
+        $data_provider[] = [['01234567890123456789','56',''], [[5,6],[15,16]]];   //test without second argument
+        $data_provider[] = [['01234567890123456789','5',''], [[5,5],[15,15]]];    //test that first arg is single char and without second arg
+        $data_provider[] = [['01234567890123456789','','56'], []];                //test without first argument
+
+        return $data_provider;
+    }
+
+    /**
+     * Unit Test Data Provider.
+     *
+     * @return array $data_provider Array of data values.
+     */
+    public function removeEolBlankSpacesDataProvider(): array
+    {
+        $data_provider   = [];
+        $data_provider[] = ['  SELECT     *   FROM `table`   '];
+        $data_provider[] = ["SELECT * \nFROM `table`"];
+        $data_provider[] = ["SELECT * \r\nFROM `table`"];
+        $data_provider[] = ["SELECT * \rFROM `table`"];
+
+        return $data_provider;
+    }
+
+    /**
+     * Unit Test Data Provider.
+     *
+     * @return array $data_provider Array of data values.
+     */
+    public function isNumericValueDataProvider(): array
+    {
+        $data_provider   = [];
+        $data_provider[] = [['value=234567 AND',6],[TRUE,11]];
+        $data_provider[] = [['value=0x47 AND',6],[TRUE,9]];
+        $data_provider[] = [['value=1.245 AND',6],[TRUE,10]];
+        $data_provider[] = [['value=3.82384E-11 AND',6],[TRUE,16]];
+
+        return $data_provider;
+    }
+
+    /**
+     * Unit Test Data Provider.
+     *
+     * @return array $data_provider Array of data values.
+     */
+    public function updatePositionsDataProvider(): array
+    {
+        $data_provider   = [];
+        $data_provider[] = [[[[3,10],[25,30],[40,50]],15,5],[[3,10],[20,25],[35,45]]];
+
+        return $data_provider;
+    }
+
+    /**
+     * Unit Test Data Provider.
+     *
+     * @return array $data_provider Array of data values.
+     */
+    public function findDigitDataProvider(): array
+    {
+        $data_provider   = [];
+        $data_provider[] = [[['SELECT * FROM `table1` WHERE `value1`="teste" AND `value2`=12', 22],[[29,36],[50,57]]],59];
+        $data_provider[] = [[['SELECT * FROM `table1` WHERE `value1`="teste" AND `value2`="A"', 22],[[29,36],[50,57]]],NULL];
+
+        return $data_provider;
+    }
+
+    /**
+     * Unit Test Data Provider.
+     *
+     * @return array $data_provider Array of data values.
+     */
+    public function jumpIgnoreDataProvider(): array
+    {
+        $data_provider   = [];
+        $data_provider[] = [[0,[[0,10],[10,100],[102,110]]], 101];
+
+        return $data_provider;
+    }
+
+    /**
+     * Unit Test Data Provider.
+     *
+     * @return array $data_provider Array of data values.
+     */
+    public function isNegativeNumberDataProvider(): array
+    {
+        $data_provider   = [];
+        $data_provider[] = [['value=123',6], FALSE];
+        $data_provider[] = [['value=-123',7], TRUE];
+        $data_provider[] = [['value=?-123',8], FALSE];
+
+        return $data_provider;
+    }
+
+    /**
+     * Unit Test Data Provider.
+     *
+     * @return array $data_provider Array of data values.
+     */
+    public function replaceNumericDataProvider(): array
+    {
+        $data_provider   = [];
+        $data_provider[] = [[['value=-12 and','?'],NULL],'value=? and'];
+        $data_provider[] = [[['value=0x24 and','?'],NULL],'value=? and'];
+        $data_provider[] = [[['value=-12 and','?'],NULL],'value=? and'];
+        $data_provider[] = [[['value=1.24 and','?'],NULL],'value=? and'];
+        $data_provider[] = [[['value=-1.24 and','?'],NULL],'value=? and'];
+        $data_provider[] = [[['value=3.8E-11 and','?'],NULL],'value=? and'];
+        $data_provider[] = [[['value1=123456 and value2=123456 /*! 123 */','?'],[[32,41]]],'value1=? and value2=? /*! 123 */'];
+
+        return $data_provider;
+    }
+
+    /**
+     * Unit Test Data Provider.
+     *
+     * @return array $data_provider Array of data values.
+     */
+    public function canonicalQueryDataProvider(): array
+    {
+        $data_provider   = [];
+        $data_provider[] = ['SELECT * FROM table1 WHERE value1="teste" and value2="teste"','SELECT * FROM table1 WHERE value1=? and value2=?'];
+
+        return $data_provider;
+    }
+
+}
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>
