Browse Source

Fix operator not treated case-insensitive in complex field expressions.

refs #15032
ndm2 4 years ago
parent
commit
bd82e10f63
2 changed files with 84 additions and 3 deletions
  1. 6 3
      src/Database/Expression/QueryExpression.php
  2. 78 0
      tests/TestCase/Database/QueryTest.php

+ 6 - 3
src/Database/Expression/QueryExpression.php

@@ -781,21 +781,24 @@ class QueryExpression implements ExpressionInterface, Countable
         $expression = $field;
 
         $spaces = substr_count($field, ' ');
-        // Handle operators with a space in them like `is not` and `not like`
+        // Handle field values that contain multiple spaces, such as
+        // operators with a space in them like `field IS NOT` and
+        // `field NOT LIKE`, or combinations with function expressions
+        // like `CONCAT(first_name, ' ', last_name) IN`.
         if ($spaces > 1) {
             $parts = explode(' ', $field);
             if (preg_match('/(is not|not \w+)$/i', $field)) {
                 $last = array_pop($parts);
                 $second = array_pop($parts);
-                array_push($parts, strtolower("{$second} {$last}"));
+                $parts[] = "{$second} {$last}";
             }
             $operator = array_pop($parts);
             $expression = implode(' ', $parts);
         } elseif ($spaces == 1) {
             $parts = explode(' ', $field, 2);
             [$expression, $operator] = $parts;
-            $operator = strtolower(trim($operator));
         }
+        $operator = strtolower(trim($operator));
         $type = $this->getTypeMap()->type($expression);
 
         $typeMultiple = (is_string($type) && strpos($type, '[]') !== false);

+ 78 - 0
tests/TestCase/Database/QueryTest.php

@@ -5363,4 +5363,82 @@ class QueryTest extends TestCase
         $this->assertSame('First Article', $statement->fetchColumn(0));
         $statement->closeCursor();
     }
+
+    /**
+     * Simple expressions from the point of view of the query expression
+     * object are expressions where the field contains one space at most.
+     */
+    public function testOperatorsInSimpleConditionsAreCaseInsensitive(): void
+    {
+        $query = (new Query($this->connection))
+            ->select('id')
+            ->from('articles')
+            ->where(['id in' => [1, 2, 3]]);
+
+        $this->assertQuotedQuery(
+            'SELECT <id> FROM <articles> WHERE <id> in \(:c0,:c1,:c2\)',
+            $query->sql(),
+            !$this->autoQuote
+        );
+
+        $query = (new Query($this->connection))
+            ->select('id')
+            ->from('articles')
+            ->where(['id IN' => [1, 2, 3]]);
+
+        $this->assertQuotedQuery(
+            'SELECT <id> FROM <articles> WHERE <id> in \(:c0,:c1,:c2\)',
+            $query->sql(),
+            !$this->autoQuote
+        );
+    }
+
+    /**
+     * Complex expressions from the point of view of the query expression
+     * object are expressions where the field contains multiple spaces.
+     */
+    public function testOperatorsInComplexConditionsAreCaseInsensitive(): void
+    {
+        $this->skipIf($this->autoQuote, 'Does not work when autoquoting is enabled.');
+
+        $query = (new Query($this->connection))
+            ->select('id')
+            ->from('profiles')
+            ->where(['CONCAT(first_name, " ", last_name) in' => ['foo bar', 'baz 42']]);
+
+        $this->assertSame(
+            'SELECT id FROM profiles WHERE CONCAT\(first_name, " ", last_name\) in \(:c0,:c1\)',
+            $query->sql()
+        );
+
+        $query = (new Query($this->connection))
+            ->select('id')
+            ->from('profiles')
+            ->where(['CONCAT(first_name, " ", last_name) IN' => ['foo bar', 'baz 42']]);
+
+        $this->assertSame(
+            'SELECT id FROM profiles WHERE CONCAT\(first_name, " ", last_name\) in \(:c0,:c1\)',
+            $query->sql()
+        );
+
+        $query = (new Query($this->connection))
+            ->select('id')
+            ->from('profiles')
+            ->where(['CONCAT(first_name, " ", last_name) not in' => ['foo bar', 'baz 42']]);
+
+        $this->assertSame(
+            'SELECT id FROM profiles WHERE CONCAT\(first_name, " ", last_name\) not in \(:c0,:c1\)',
+            $query->sql()
+        );
+
+        $query = (new Query($this->connection))
+            ->select('id')
+            ->from('profiles')
+            ->where(['CONCAT(first_name, " ", last_name) NOT IN' => ['foo bar', 'baz 42']]);
+
+        $this->assertSame(
+            'SELECT id FROM profiles WHERE CONCAT\(first_name, " ", last_name\) not in \(:c0,:c1\)',
+            $query->sql()
+        );
+    }
 }