Browse Source

Accept only working operators for tuple comparison transformation.

ndm2 4 years ago
parent
commit
669b3cdd7c

+ 12 - 5
src/Database/Driver/TupleComparisonTranslatorTrait.php

@@ -20,6 +20,7 @@ use Cake\Database\Expression\IdentifierExpression;
 use Cake\Database\Expression\QueryExpression;
 use Cake\Database\Expression\TupleComparison;
 use Cake\Database\Query;
+use RuntimeException;
 
 /**
  * Provides a translator method for tuple comparisons
@@ -55,17 +56,23 @@ trait TupleComparisonTranslatorTrait
             return;
         }
 
+        $operator = strtoupper($expression->getOperator());
+        if (!in_array($operator, ['IN', '='])) {
+            throw new RuntimeException(
+                sprintf(
+                    'Tuple comparison transform only supports the `IN` and `=` operators, `%s` given.',
+                    $operator
+                )
+            );
+        }
+
         $value = $expression->getValue();
         $true = new QueryExpression('1');
 
         if ($value instanceof Query) {
-            $op = $expression->getOperator();
-            if (strtolower($op) === 'in') {
-                $op = '=';
-            }
             $selected = array_values($value->clause('select'));
             foreach ($fields as $i => $field) {
-                $value->andWhere(["$field $op" => new IdentifierExpression($selected[$i])]);
+                $value->andWhere([$field => new IdentifierExpression($selected[$i])]);
             }
             $value->select($true, true);
             $expression->setField($true);

+ 80 - 0
tests/TestCase/Database/QueryTests/TupleComparisonQueryTest.php

@@ -0,0 +1,80 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         4.3.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+
+namespace Cake\Test\TestCase\Database\QueryTests;
+
+use Cake\Database\Driver\Sqlite;
+use Cake\Database\Driver\Sqlserver;
+use Cake\Database\Expression\TupleComparison;
+use Cake\TestSuite\TestCase;
+use RuntimeException;
+
+/**
+ * Tuple comparison query tests.
+ *
+ * These tests are specifically relevant in the context of Sqlite and
+ * Sqlserver, for which the tuple comparison will be transformed when
+ * composite fields are used.
+ *
+ * @see \Cake\Database\Driver\TupleComparisonTranslatorTrait::_transformTupleComparison()
+ */
+class TupleComparisonQueryTest extends TestCase
+{
+    /**
+     * @inheritDoc
+     */
+    protected $fixtures = [
+        'core.Articles',
+    ];
+
+    public function testTransformWithInvalidOperator(): void
+    {
+        $articles = $this->getTableLocator()->get('Articles');
+
+        $driver = $articles->getConnection()->getDriver();
+        if (
+            $driver instanceof Sqlite ||
+            $driver instanceof Sqlserver
+        ) {
+            $this->expectException(RuntimeException::class);
+            $this->expectExceptionMessage(
+                'Tuple comparison transform only supports the `IN` and `=` operators, `NOT IN` given.'
+            );
+        } else {
+            $this->markTestSkipped('Tuple comparisons are only being transformed for Sqlite and Sqlserver.');
+        }
+
+        $articles
+            ->find()
+            ->select(['Articles.id', 'Articles.author_id'])
+            ->where([
+                new TupleComparison(
+                    ['Articles.id', 'Articles.author_id'],
+                    $articles
+                        ->subquery()
+                        ->select(['ArticlesAlias.id', 'ArticlesAlias.author_id'])
+                        ->from(['ArticlesAlias' => $articles->getTable()])
+                        ->where(['ArticlesAlias.author_id' => 1]),
+                    [],
+                    'NOT IN'
+                ),
+            ])
+            ->orderAsc('Articles.id')
+            ->disableHydration()
+            ->toArray();
+    }
+}