diff --git a/src/Lunr/Gravity/Database/MySQL/MySQLCanonicalQuery.php b/src/Lunr/Gravity/Database/MySQL/MySQLCanonicalQuery.php
new file mode 100644
index 0000000000000000000000000000000000000000..9498fa283e66df2a7edc98236b2d7708ee377653
--- /dev/null
+++ b/src/Lunr/Gravity/Database/MySQL/MySQLCanonicalQuery.php
@@ -0,0 +1,441 @@
+<?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;
+        $this->canonical_query = NULL;
+    }
+
+    /**
+     * __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, '"', '"', '"?"', TRUE);
+        $tmp_query = $this->replace_between($tmp_query, '\'', '\'', '\'?\'', TRUE);
+
+        $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
+     * @param boolean $add_to_ignore Input bool to decide if add to ignore list
+     *
+     * @return string $string The provided string replaced
+     */
+    private function replace_between(string $string, string $from, string $to, string $replace, bool $add_to_ignore = FALSE): 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;
+            }
+
+            $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 - strlen($replace));
+
+            if ($add_to_ignore === TRUE)
+            {
+                $this->add_ignore_positions([[$start_pos,$start_pos + strlen($replace) - 1]]);
+            }
+
+            $offset = $start_pos + strlen($replace);
+        }
+
+        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 (is_null($to) || $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): 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 $string, 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;
+    }
+
+}
diff --git a/src/Lunr/Gravity/Database/MySQL/MySQLQueryResult.php b/src/Lunr/Gravity/Database/MySQL/MySQLQueryResult.php
index 16319e94cc26ccb1cf7ff952b89cde0e667a1a2b..edab863ec6576dd2599e1bbed57a649deb608d61 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
@@ -113,9 +119,10 @@ class MySQLQueryResult implements DatabaseQueryResultInterface
             $this->freed   = TRUE;
         }
 
-        $this->result = $result;
-        $this->mysqli = $mysqli;
-        $this->query  = $query;
+        $this->result          = $result;
+        $this->mysqli          = $mysqli;
+        $this->query           = $query;
+        $this->canonical_query = NULL;
 
         if ($async === FALSE)
         {
@@ -142,6 +149,7 @@ class MySQLQueryResult implements DatabaseQueryResultInterface
         unset($this->error_number);
         unset($this->insert_id);
         unset($this->query);
+        unset($this->canonical_query);
     }
 
     /**
@@ -335,6 +343,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 ($this->canonical_query !== NULL)
+        {
+            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..d06475e84d6a2d33a6e49a5792b1d6f74e276fc5
--- /dev/null
+++ b/src/Lunr/Gravity/Database/MySQL/Tests/MySQLCanonicalQueryBaseTest.php
@@ -0,0 +1,260 @@
+<?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 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->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
+     * @param string $expected Modified values
+     *
+     * @dataProvider removeEolBlankSpacesDataProvider
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::remove_eol_blank_spaces
+     */
+    public function testRemoveEolBlankSpaces(string $data, string $expected): void
+    {
+        $method = $this->get_accessible_reflection_method('remove_eol_blank_spaces');
+
+        $result = $method->invokeArgs($this->class, [$data]);
+        $this->assertEquals($expected, $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->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->class, ($data[1] ?? []));
+
+        $method = $this->get_accessible_reflection_method('find_digit');
+
+        $result = $method->invokeArgs($this->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->class, ($data[1] ?? []));
+
+        $method = $this->get_accessible_reflection_method('jump_ignore');
+
+        $result = $method->invokeArgs($this->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->class, $data);
+
+        $this->assertIsBool($result[0]);
+        $this->assertIsInt($result[1]);
+
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * Test that replace_between() returns replaced string correctly.
+     *
+     * @param array  $data     Data values
+     * @param string $expected Modified data values
+     *
+     * @dataProvider replaceBetweenDataProvider
+     * @covers       Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery::replace_between
+     */
+    public function testReplaceBetween(array $data, string $expected): void
+    {
+        $property = $this->get_accessible_reflection_property('ignore_positions');
+        $property->setValue($this->class, ($data[1] ?? []));
+
+        $method = $this->get_accessible_reflection_method('replace_between');
+
+        $result = $method->invokeArgs($this->class, $data[0]);
+
+        $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->class, []);
+
+        $method = $this->get_accessible_reflection_method('add_ignore_positions');
+        $method->invokeArgs($this->class, [[[40,50]]]);
+        $method->invokeArgs($this->class, [[[3,10],[25,30]]]);
+
+        $result = $property->getValue($this->class);
+
+        $this->assertIsArray($result);
+        $this->assertEquals([[3,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->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->class, ($data[1] ?? []));
+
+        $method = $this->get_accessible_reflection_method('replace_numeric');
+
+        $result = $method->invokeArgs($this->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
+    {
+        if (!file_exists($data))
+        {
+            $this->markTestSkipped($data . ' does not exist.');
+            return;
+        }
+
+        if (!file_exists($expected))
+        {
+            $this->markTestSkipped($expected . ' does not exist.');
+            return;
+        }
+
+        $property = $this->get_accessible_reflection_property('query');
+        $input    = file_get_contents($data);
+        $output   = file_get_contents($expected);
+
+        if ($input === FALSE)
+        {
+            $this->markTestSkipped("File \"$data\" could not be read!");
+        }
+
+        if ($output === FALSE)
+        {
+            $this->markTestSkipped("File \"$expected\" could not be read!");
+        }
+
+        $property->setValue($this->class, $input);
+        $value = $this->class->get_canonical_query();
+        $this->assertEquals($output, $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..937bf21110eb5d358e4678d85c2766df65452d77
--- /dev/null
+++ b/src/Lunr/Gravity/Database/MySQL/Tests/MySQLCanonicalQueryTest.php
@@ -0,0 +1,205 @@
+<?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
+{
+
+    /**
+     * TestCase Constructor.
+     */
+    public function setUp(): void
+    {
+         $this->reflection = new ReflectionClass('Lunr\Gravity\Database\MySQL\MySQLCanonicalQuery');
+         $this->class      = new MySQLCanonicalQuery('');
+    }
+
+    /**
+     * TestCase Destructor.
+     */
+    public function tearDown(): void
+    {
+        parent::tearDown();
+    }
+
+    /**
+     * Unit Test Data Provider.
+     *
+     * @return array $data_provider Array of data values.
+     */
+    public function findPositionsDataProvider(): array
+    {
+        $data_provider                               = [];
+        $data_provider['first argument not string']  = [['013456789013456789','23','56'], []];
+        $data_provider['second argument not string'] = [['012346789012346789','23','56'], [[2,17]]];
+        $data_provider['both arguments string']      = [['01234567890123456789','23','56'], [[2,6],[12,16]]];
+        $data_provider['both arguments not string']  = [['01234567890123456789','bb','cc'], []];
+        $data_provider['no second argument']         = [['01234567890123456789','56',''], [[5,6],[15,16]]];
+        $data_provider['no first argument']          = [['01234567890123456789','','56'], []];
+        $data_provider['single no second arg']       = [['01234567890123456789','5',''], [[5,5],[15,15]]];
+
+        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`   ','SELECT * FROM `table`'];
+        $data_provider[] = ["SELECT * \nFROM `table`",'SELECT * FROM `table`'];
+        $data_provider[] = ["SELECT * \r\nFROM `table`",'SELECT * FROM `table`'];
+        $data_provider[] = ["SELECT * \rFROM `table`",'SELECT * FROM `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 replaceBetweenDataProvider(): array
+    {
+        $data_provider                       = [];
+        $data_provider['replace jump'][0]    = [['SELECT * FROM value1="ignore" AND value2="B"', '"', '"', '"?"',TRUE],[[21,28]]];
+        $data_provider['replace jump'][1]    = 'SELECT * FROM value1="ignore" AND value2="?"';
+        $data_provider['replace no jump'][0] = [['SELECT * FROM value1="ignore" AND value2="B"', '"', '"', '"?"',TRUE],NULL];
+        $data_provider['replace no jump'][1] = 'SELECT * FROM value1="?" AND value2="?"';
+
+        return $data_provider;
+    }
+
+    /**
+     * Unit Test Data Provider.
+     *
+     * @return array $data_provider Array of data values.
+     */
+    public function canonicalQueryDataProvider(): array
+    {
+        $path = TEST_STATICS . '/Gravity/Database/MySQL/';
+
+        $data_provider['replace'] = [$path . 'input_replace.sql', $path . 'output_replace.sql'];
+        $data_provider['upserts'] = [$path . 'input_upserts.sql', $path . 'output_upserts.sql'];
+        $data_provider['insert']  = [$path . 'input_insert.sql', $path . 'output_insert.sql'];
+        $data_provider['create']  = [$path . 'input_create.sql', $path . 'output_create.sql'];
+        $data_provider['update']  = [$path . 'input_update.sql', $path . 'output_update.sql'];
+        $data_provider['backup']  = [$path . 'input_backup.sql', $path . 'output_backup.sql'];
+        $data_provider['select']  = [$path . 'input_select.sql', $path . 'output_select.sql'];
+        $data_provider['cte']     = [$path . 'input_cte.sql', $path . 'output_cte.sql'];
+
+        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..517035412d91a3ce59f2a8f045aa6ed9ab49f8af 100644
--- a/src/Lunr/Gravity/Database/MySQL/Tests/MySQLQueryResultBaseTest.php
+++ b/src/Lunr/Gravity/Database/MySQL/Tests/MySQLQueryResultBaseTest.php
@@ -205,6 +205,29 @@ class MySQLQueryResultBaseTest extends MySQLQueryResultTest
         $this->assertSame($this->mysqli, $value);
     }
 
+    /**
+     * Test that canonical_query() returns a string.
+     *
+     * @covers Lunr\Gravity\Database\MySQL\MySQLQueryResult::canonical_query
+     */
+    public function testCanonicalQuery(): void
+    {
+        $class = $this->result_reflection->getProperty('query');
+        $class->setAccessible(TRUE);
+        $class->setValue($this->result, 'SELECT * FROM table1 WHERE value="test"');
+
+        $value = $this->result->canonical_query();
+        $this->assertIsString($value);
+        $this->assertEquals('SELECT * FROM table1 WHERE value="?"', $value);
+
+        $class = $this->result_reflection->getProperty('canonical_query');
+        $class->setAccessible(TRUE);
+        $class->setValue($this->result, 'SELECT * FROM table2 WHERE value=?');
+        $value = $this->result->canonical_query();
+        $this->assertIsString($value);
+        $this->assertEquals('SELECT * FROM table2 WHERE value=?', $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>
diff --git a/tests/statics/Gravity/Database/MySQL/input_backup.sql b/tests/statics/Gravity/Database/MySQL/input_backup.sql
new file mode 100644
index 0000000000000000000000000000000000000000..49748ee7b2084ecf7fd037993c4c428c75c973e6
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/input_backup.sql
@@ -0,0 +1,56 @@
+-- MariaDB dump 10.19  Distrib 10.5.11-MariaDB, for debian-linux-gnu (x86_64)
+--
+-- Host: localhost    Database: table
+-- ------------------------------------------------------
+-- Server version	10.5.11-MariaDB-1:10.5.11+maria~focal
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8mb4 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `statics`
+--
+
+DROP TABLE IF EXISTS `table`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `table` (
+  `param1` varchar(100) NOT NULL,
+  `param2` varchar(20) NOT NULL,
+  `param3` text NOT NULL,
+  PRIMARY KEY (`param1`,`param2`),
+  KEY `param1` (`param1`) USING BTREE,
+  KEY `param2` (`param2`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `statics`
+--
+
+LOCK TABLES `table` WRITE;
+/*!40000 ALTER TABLE `statics` DISABLE KEYS */;
+INSERT INTO `table` VALUES (1,'en-US','There is an update for your flight from $AIRPORT: '),(2,'en-US','Baggage is expected at belt $BELT. '),(3,'en-US','Your flight is boarding now. ');
+/*!40000 ALTER TABLE `statics` ENABLE KEYS */;
+
+UNLOCK TABLES;
+
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2021-07-30 10:01:31
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/input_create.sql b/tests/statics/Gravity/Database/MySQL/input_create.sql
new file mode 100644
index 0000000000000000000000000000000000000000..55c312f2167600fc0443c610eb9aa418dd189f31
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/input_create.sql
@@ -0,0 +1,17 @@
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `table` (
+   `param1` varchar(255) NOT NULL,
+   `param2` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`changedData`)),
+   `param3` int(11) NOT NULL,
+   `param4` bigint(20) NOT NULL,
+   `param5` enum('added','changed','deleted') NOT NULL,
+   `param6` varchar(8) GENERATED ALWAYS AS (json_value(from_base64(`id`),'$.codeShareFlightNumber')) STORED,
+   `param7` tinyint(1) GENERATED ALWAYS AS (if(json_value(from_base64(`id`),'$.arrival') = '1',1,0)) STORED,
+   `param8` date GENERATED ALWAYS AS (json_value(from_base64(`id`),'$.scheduledDate')) STORED,
+   `param9` char(3) GENERATED ALWAYS AS (json_value(from_base64(`id`),'$.departureAirport')) STORED,
+   `param10` char(3) GENERATED ALWAYS AS (json_value(from_base64(`id`),'$.arrivalAirport')) STORED,
+   `param11` char(3) GENERATED ALWAYS AS (json_value(from_base64(`id`),'$.viaAirport')) STORED,
+   PRIMARY KEY (`param1`,`param2`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 `PAGE_COMPRESSED`=1;
+/*!40101 SET character_set_client = @saved_cs_client */;
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/input_cte.sql b/tests/statics/Gravity/Database/MySQL/input_cte.sql
new file mode 100644
index 0000000000000000000000000000000000000000..b873a1e2a8c1114d92baf7ee2f64f14d97651a34
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/input_cte.sql
@@ -0,0 +1,21 @@
+WITH `teste` AS (
+    SELECT
+        `c`.`language` as `lang`,
+        RAND() as `num`
+    FROM
+        `table1` as `c`
+            LEFT JOIN
+        `table2` as `fb` ON `c`.`id`=`fb`.`id`
+    WHERE
+            COALESCE(NULL,1) = 1
+      AND `c`.`id` = 1
+    GROUP BY `lang`
+    ORDER BY `num` DESC
+    LIMIT 100 OFFSET 5
+    )
+SELECT
+    `table1`.*
+FROM
+    `table1`
+        JOIN
+    `teste`;
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/input_insert.sql b/tests/statics/Gravity/Database/MySQL/input_insert.sql
new file mode 100644
index 0000000000000000000000000000000000000000..0f99af422c964e895eccb1f5580836f68d8c6d8f
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/input_insert.sql
@@ -0,0 +1,6 @@
+INSERT INTO `database`.`table` (`param1`, `param2`, `param3`) VALUES
+    (1, 'en-US', 'Lorem ipsum dolor sit amet'),
+    (2, 'en-US', 'consectetur adipiscing elit'),
+    (3, 'en-US', 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'),
+    (4, 'en-US', 'Ut enim ad minim veniam'),
+    (5, 'en-US', 'quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.');
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/input_replace.sql b/tests/statics/Gravity/Database/MySQL/input_replace.sql
new file mode 100644
index 0000000000000000000000000000000000000000..3f5441ab293e5a0400d0594f203461dc61a6160c
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/input_replace.sql
@@ -0,0 +1,3 @@
+REPLACE
+INTO db.table
+VALUES (1, 'Old', '2014-08-20 18:47:00');
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/input_select.sql b/tests/statics/Gravity/Database/MySQL/input_select.sql
new file mode 100644
index 0000000000000000000000000000000000000000..892c63eeccf97ea707667a4a3a4d6d65a22686d8
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/input_select.sql
@@ -0,0 +1,4 @@
+SELECT `c`.`identifier`,`c`.`language`,`c`.`language`,`fb`.`identifier`,COALESCE(NULL,1) as "Teste"
+from `table` as `c`
+         left join `bookmarks` fb on `c`.`identifier` = `fb`.`identifier`
+where `c`.`identifier` = "NOTIFICATION_UPDATE_ARRIVAL";
diff --git a/tests/statics/Gravity/Database/MySQL/input_update.sql b/tests/statics/Gravity/Database/MySQL/input_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..f6f89132738df647ad21ea3b6896644de1f36313
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/input_update.sql
@@ -0,0 +1,3 @@
+UPDATE `database`.`table`
+SET `content` = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit ..', `language` = 'li-LI'
+    WHERE `identifier` = 'LOREM_IPSUM' AND `id` = 2;
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/input_upserts.sql b/tests/statics/Gravity/Database/MySQL/input_upserts.sql
new file mode 100644
index 0000000000000000000000000000000000000000..f87b974961172986d4981e5d7ba4071d4f768ab1
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/input_upserts.sql
@@ -0,0 +1,4 @@
+INSERT INTO `database`.`table` (`identifier`, `language`, `content`)
+VALUES
+    ("LOREM_IPSUM","li-LI","Lorem ipsum dolor sit amet ..")
+        ON DUPLICATE KEY UPDATE `content`="Lorem ipsum dolor sit amet ..";
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/output_backup.sql b/tests/statics/Gravity/Database/MySQL/output_backup.sql
new file mode 100644
index 0000000000000000000000000000000000000000..ce51e7687d6fdbc3e2a6af41e8fad92ac895777a
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/output_backup.sql
@@ -0,0 +1 @@
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8mb4 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; DROP TABLE IF EXISTS `table`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `table` ( `param1` varchar(?) NOT NULL, `param2` varchar(?) NOT NULL, `param3` text NOT NULL, PRIMARY KEY (`param1`,`param2`), KEY `param1` (`param1`) USING BTREE, KEY `param2` (`param2`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; LOCK TABLES `table` WRITE; /*!40000 ALTER TABLE `statics` DISABLE KEYS */; INSERT INTO `table` VALUES (?,'?','?'),(?,'?','?'),(?,'?','?'); /*!40000 ALTER TABLE `statics` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/output_create.sql b/tests/statics/Gravity/Database/MySQL/output_create.sql
new file mode 100644
index 0000000000000000000000000000000000000000..10b3de199f493de3cb6f34c21ecbfc959eba1e54
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/output_create.sql
@@ -0,0 +1 @@
+/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `table` ( `param1` varchar(?) NOT NULL, `param2` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`changedData`)), `param3` int(?) NOT NULL, `param4` bigint(?) NOT NULL, `param5` enum('?','?','?') NOT NULL, `param6` varchar(?) GENERATED ALWAYS AS (json_value(from_base64(`id`),'?')) STORED, `param7` tinyint(?) GENERATED ALWAYS AS (if(json_value(from_base64(`id`),'?') = '?',?,?)) STORED, `param8` date GENERATED ALWAYS AS (json_value(from_base64(`id`),'?')) STORED, `param9` char(?) GENERATED ALWAYS AS (json_value(from_base64(`id`),'?')) STORED, `param10` char(?) GENERATED ALWAYS AS (json_value(from_base64(`id`),'?')) STORED, `param11` char(?) GENERATED ALWAYS AS (json_value(from_base64(`id`),'?')) STORED, PRIMARY KEY (`param1`,`param2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 `PAGE_COMPRESSED`=?; /*!40101 SET character_set_client = @saved_cs_client */;
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/output_cte.sql b/tests/statics/Gravity/Database/MySQL/output_cte.sql
new file mode 100644
index 0000000000000000000000000000000000000000..87bd19d42a484826a1b8dbf5f68e9397cbd7222f
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/output_cte.sql
@@ -0,0 +1 @@
+WITH `teste` AS ( SELECT `c`.`language` as `lang`, RAND() as `num` FROM `table1` as `c` LEFT JOIN `table2` as `fb` ON `c`.`id`=`fb`.`id` WHERE COALESCE(NULL,?) = ? AND `c`.`id` = ? GROUP BY `lang` ORDER BY `num` DESC LIMIT ? OFFSET ? ) SELECT `table1`.* FROM `table1` JOIN `teste`;
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/output_insert.sql b/tests/statics/Gravity/Database/MySQL/output_insert.sql
new file mode 100644
index 0000000000000000000000000000000000000000..f25ec221536eb48069b2acc0662de59c47a3f904
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/output_insert.sql
@@ -0,0 +1 @@
+INSERT INTO `database`.`table` (`param1`, `param2`, `param3`) VALUES (?, '?', '?'), (?, '?', '?'), (?, '?', '?'), (?, '?', '?'), (?, '?', '?');
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/output_replace.sql b/tests/statics/Gravity/Database/MySQL/output_replace.sql
new file mode 100644
index 0000000000000000000000000000000000000000..6666020019b6b3e263b6e88c6d6d130a39373d67
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/output_replace.sql
@@ -0,0 +1 @@
+REPLACE INTO db.table VALUES (?, '?', '?');
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/output_select.sql b/tests/statics/Gravity/Database/MySQL/output_select.sql
new file mode 100644
index 0000000000000000000000000000000000000000..333aa7f130999c85cdfe798d5f3fe59fb81835ff
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/output_select.sql
@@ -0,0 +1 @@
+SELECT `c`.`identifier`,`c`.`language`,`c`.`language`,`fb`.`identifier`,COALESCE(NULL,?) as "?" from `table` as `c` left join `bookmarks` fb on `c`.`identifier` = `fb`.`identifier` where `c`.`identifier` = "?";
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/output_update.sql b/tests/statics/Gravity/Database/MySQL/output_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..f87088ece9ace351d15f2fd5031bdb580effc913
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/output_update.sql
@@ -0,0 +1 @@
+UPDATE `database`.`table` SET `content` = '?', `language` = '?' WHERE `identifier` = '?' AND `id` = ?;
\ No newline at end of file
diff --git a/tests/statics/Gravity/Database/MySQL/output_upserts.sql b/tests/statics/Gravity/Database/MySQL/output_upserts.sql
new file mode 100644
index 0000000000000000000000000000000000000000..94c8703bee15cc736da0edc18eb3a3d5fe9e4b8a
--- /dev/null
+++ b/tests/statics/Gravity/Database/MySQL/output_upserts.sql
@@ -0,0 +1 @@
+INSERT INTO `database`.`table` (`identifier`, `language`, `content`) VALUES ("?","?","?") ON DUPLICATE KEY UPDATE `content`="?";
\ No newline at end of file
