From 2c6aeec429d68deb578deb44fa325c4edf07c266 Mon Sep 17 00:00:00 2001 From: Dmitry Khrysev Date: Wed, 3 Nov 2021 18:47:08 +0200 Subject: [PATCH 1/2] Added uuid type support to CAST; Added CAST to native JSON for MySQL if supported;Updated dev toolchain to run tests --- README.md | 2 +- src/Oro/ORM/Query/AST/Functions/Cast.php | 3 +- .../AST/Platform/Functions/Mysql/Cast.php | 6 +++- .../ORM/AST/Query/Functions/FunctionsTest.php | 13 +++++--- .../Query/Functions/fixtures/mysql/cast.yml | 33 +++++++++++-------- .../fixtures/mysql/timestampdiff.yml | 1 - .../Functions/fixtures/postgresql/cast.yml | 27 +++++++++------ 7 files changed, 54 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 138abad2c4a..bf566a22da3 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Available functions: * `ROUND(value, ?precision)` - Rounds the value to the specified precision (defaults to 0 precision if not specified). * `CEIL(value)` - Returns the value rounded up. * `SIGN(expr)` - Returns the sign of the argument. -* `CAST(expr as type)` - Takes an expression of any type and produces a result value of a specified type. Supported types are: `char`, `string`, `text`, `date`, `datetime`, `time`, `int`, `integer`, `decimal`, `boolean`, `binary`. +* `CAST(expr as type)` - Takes an expression of any type and produces a result value of a specified type. Supported types are: `char`, `string`, `text`, `date`, `datetime`, `time`, `int`, `integer`, `decimal`, `boolean`, `binary`, `uuid`. * `CONCAT_WS` - Concatenate all but the first argument. The first argument is used as the separator string. * `GROUP_CONCAT` - Returns a concatenated string. GROUP_CONCAT full syntax: ``` diff --git a/src/Oro/ORM/Query/AST/Functions/Cast.php b/src/Oro/ORM/Query/AST/Functions/Cast.php index 2a2796bcf56..338b12c9566 100644 --- a/src/Oro/ORM/Query/AST/Functions/Cast.php +++ b/src/Oro/ORM/Query/AST/Functions/Cast.php @@ -25,7 +25,8 @@ class Cast extends AbstractPlatformAwareFunctionNode 'json', 'bool', 'boolean', - 'binary' + 'binary', + 'uuid' ]; public function parse(Parser $parser) diff --git a/src/Oro/ORM/Query/AST/Platform/Functions/Mysql/Cast.php b/src/Oro/ORM/Query/AST/Platform/Functions/Mysql/Cast.php index b15e62f8b38..3e5a065924d 100644 --- a/src/Oro/ORM/Query/AST/Platform/Functions/Mysql/Cast.php +++ b/src/Oro/ORM/Query/AST/Platform/Functions/Mysql/Cast.php @@ -16,11 +16,15 @@ public function getSql(SqlWalker $sqlWalker): string $value = $this->parameters[DqlFunction::PARAMETER_KEY]; $type = $this->parameters[DqlFunction::TYPE_KEY]; + if ($type === 'json' && !$sqlWalker->getConnection()->getDatabasePlatform()->hasNativeJsonType()) { + $type = 'text'; + } + $type = \strtolower($type); $isBoolean = $type === 'bool' || $type === 'boolean'; if ($type === 'char') { $type = 'char(1)'; - } elseif ($type === 'string' || $type === 'text' || $type === 'json') { + } elseif ($type === 'string' || $type === 'text' || $type === 'uuid') { $type = 'char'; } elseif ($type === 'int' || $type === 'integer' || $isBoolean) { $type = 'signed'; diff --git a/tests/Oro/Tests/ORM/AST/Query/Functions/FunctionsTest.php b/tests/Oro/Tests/ORM/AST/Query/Functions/FunctionsTest.php index 6a8366ff75a..35135a5936a 100644 --- a/tests/Oro/Tests/ORM/AST/Query/Functions/FunctionsTest.php +++ b/tests/Oro/Tests/ORM/AST/Query/Functions/FunctionsTest.php @@ -11,6 +11,8 @@ use Oro\Tests\Connection\TestUtil; use Oro\Tests\TestCase; +use PHPUnit\Framework\Constraint\LogicalOr; +use Symfony\Component\Yaml\Yaml; class FunctionsTest extends TestCase { @@ -61,15 +63,18 @@ public function functionsDataProvider(): array \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME ); foreach ($files as $file) { - $fileData = Yaml::parseFile($file); + $fileData = Yaml::parseFile($file->getPathname()); if (!\is_array($fileData)) { throw new \RuntimeException(\sprintf('Could not parse file %s', $file)); } - /** @noinspection SlowArrayOperationsInLoopInspection */ - $data = \array_merge($data, $fileData); + $data[] = $fileData; + } + + if (!$data) { + return []; } - return $data; + return array_merge(...$data); } protected function registerDqlFunction( diff --git a/tests/Oro/Tests/ORM/AST/Query/Functions/fixtures/mysql/cast.yml b/tests/Oro/Tests/ORM/AST/Query/Functions/fixtures/mysql/cast.yml index d1e84d41a4c..bf084f69e70 100644 --- a/tests/Oro/Tests/ORM/AST/Query/Functions/fixtures/mysql/cast.yml +++ b/tests/Oro/Tests/ORM/AST/Query/Functions/fixtures/mysql/cast.yml @@ -23,14 +23,21 @@ #CHAR - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } + dql: "SELECT CAST('56366a57-43fa-4f4e-a0a2-66f87d52b601' as uuid) FROM Oro\\Entities\\Foo f WHERE f.id = 1" + sql: "SELECT CAST('56366a57-43fa-4f4e-a0a2-66f87d52b601' AS char) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" + expectedResult: + - '56366a57-43fa-4f4e-a0a2-66f87d52b601' + +- functions: + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST('12' as char) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST('12' AS char(1)) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: - 1 - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST(f.createdAt as char) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST(t0_.created_at AS char(1)) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: @@ -38,14 +45,14 @@ #STRING - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST(12 as string) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST(12 AS char) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: - '12' - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST(f.createdAt as string) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST(t0_.created_at AS char) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: @@ -53,25 +60,25 @@ #TEXT - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST(12 as text) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST(12 AS char) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: - '12' - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST(f.createdAt as text) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST(t0_.created_at AS char) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: - '2014-01-04 05:06:07' - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST(CONCAT('{\"a\":', f.id, '}') as json) FROM Oro\\Entities\\Foo f WHERE f.id = 1" - sql: "SELECT CAST(CONCAT('{\\\"a\\\":', t0_.id, '}') AS char) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" + sql: "SELECT CAST(CONCAT('{\\\"a\\\":', t0_.id, '}') AS json) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: - - '{"a":1}' + - '{"a": 1}' #DECIMAL - functions: @@ -90,7 +97,7 @@ #DATE - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST('2014-01-02 12:13:14' as date) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST('2014-01-02 12:13:14' AS date) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: @@ -98,7 +105,7 @@ #TIME - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST('2014-01-02 12:13:14' as time) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST('2014-01-02 12:13:14' AS time) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: @@ -106,14 +113,14 @@ #DATETIME - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "datetime" } dql: "SELECT CAST('2014-01-02 12:13:14' as datetime) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST('2014-01-02 12:13:14' AS datetime) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: - '2014-01-02 12:13:14' - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "datetime" } dql: "SELECT CAST(f.createdAt as datetime) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST(t0_.created_at AS datetime) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: diff --git a/tests/Oro/Tests/ORM/AST/Query/Functions/fixtures/mysql/timestampdiff.yml b/tests/Oro/Tests/ORM/AST/Query/Functions/fixtures/mysql/timestampdiff.yml index 13c129749b6..85862a352ce 100644 --- a/tests/Oro/Tests/ORM/AST/Query/Functions/fixtures/mysql/timestampdiff.yml +++ b/tests/Oro/Tests/ORM/AST/Query/Functions/fixtures/mysql/timestampdiff.yml @@ -116,4 +116,3 @@ sql: SELECT TIMESTAMPDIFF(YEAR, '2016-01-01 00:00:01', '2017-01-01 00:00:00') AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1 expectedResult: - 0 - diff --git a/tests/Oro/Tests/ORM/AST/Query/Functions/fixtures/postgresql/cast.yml b/tests/Oro/Tests/ORM/AST/Query/Functions/fixtures/postgresql/cast.yml index 698764403df..122b322a33e 100644 --- a/tests/Oro/Tests/ORM/AST/Query/Functions/fixtures/postgresql/cast.yml +++ b/tests/Oro/Tests/ORM/AST/Query/Functions/fixtures/postgresql/cast.yml @@ -23,14 +23,21 @@ #CHAR - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } + dql: "SELECT CAST('56366a57-43fa-4f4e-a0a2-66f87d52b601' as uuid) FROM Oro\\Entities\\Foo f WHERE f.id = 1" + sql: "SELECT CAST('56366a57-43fa-4f4e-a0a2-66f87d52b601' AS uuid) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" + expectedResult: + - '56366a57-43fa-4f4e-a0a2-66f87d52b601' + +- functions: + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST('12' as char) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST('12' AS char) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: - 1 - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST(f.createdAt as char) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST(t0_.created_at AS char) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: @@ -38,14 +45,14 @@ #STRING - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST(12 as string) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST(12 AS varchar) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: - '12' - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST(f.createdAt as string) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST(t0_.created_at AS varchar) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: @@ -53,14 +60,14 @@ #TEXT - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST(12 as text) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST(12 AS text) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: - '12' - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST(f.createdAt as text) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST(t0_.created_at AS text) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: @@ -92,7 +99,7 @@ #DATE - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST('2014-01-02 12:13:14' as date) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST('2014-01-02 12:13:14' AS date) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: @@ -100,7 +107,7 @@ #TIME - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "string" } dql: "SELECT CAST('2014-01-02 12:13:14' as time) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: "SELECT CAST('2014-01-02 12:13:14' AS time) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1" expectedResult: @@ -108,14 +115,14 @@ #DATETIME - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "datetime" } dql: "SELECT CAST('2014-01-02 12:13:14' as datetime) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: SELECT "timestamp"('2014-01-02 12:13:14') AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1 expectedResult: - '2014-01-02 12:13:14' - functions: - - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "numeric" } + - { name: "cast", className: "Oro\\ORM\\Query\\AST\\Functions\\Cast", type: "datetime" } dql: "SELECT CAST(f.createdAt as datetime) FROM Oro\\Entities\\Foo f WHERE f.id = 1" sql: SELECT "timestamp"(t0_.created_at) AS sclr_0 FROM test_foo t0_ WHERE t0_.id = 1 expectedResult: From 0fe05597803c9a2fa3a870d2daa1beae9b7d6bb0 Mon Sep 17 00:00:00 2001 From: Dmitry Khrysev Date: Wed, 3 Nov 2021 19:17:43 +0200 Subject: [PATCH 2/2] 2.x updated tests --- composer.json | 1 + tests/Oro/Tests/DBAL/Types/PercentTypeTest.php | 1 - tests/Oro/Tests/ORM/AST/Query/Functions/FunctionsTest.php | 6 +----- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index a9925568a7d..5cdb73283fe 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "phpunit/phpunit": "9.*", "doctrine/data-fixtures": "^1.3", "symfony/yaml": "5.*", + "symfony/cache": "5.*", "squizlabs/php_codesniffer": "3.5.*" }, "autoload": { diff --git a/tests/Oro/Tests/DBAL/Types/PercentTypeTest.php b/tests/Oro/Tests/DBAL/Types/PercentTypeTest.php index a49af471769..8959aff25f3 100644 --- a/tests/Oro/Tests/DBAL/Types/PercentTypeTest.php +++ b/tests/Oro/Tests/DBAL/Types/PercentTypeTest.php @@ -5,7 +5,6 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\Types\Type; - use Doctrine\ORM\ORMException; use Oro\DBAL\Types\PercentType; use Oro\Tests\Connection\TestUtil; diff --git a/tests/Oro/Tests/ORM/AST/Query/Functions/FunctionsTest.php b/tests/Oro/Tests/ORM/AST/Query/Functions/FunctionsTest.php index 35135a5936a..6603ec782ea 100644 --- a/tests/Oro/Tests/ORM/AST/Query/Functions/FunctionsTest.php +++ b/tests/Oro/Tests/ORM/AST/Query/Functions/FunctionsTest.php @@ -5,10 +5,6 @@ use Doctrine\ORM\Configuration; use Doctrine\ORM\Query; - -use PHPUnit\Framework\Constraint\LogicalOr; -use Symfony\Component\Yaml\Yaml; - use Oro\Tests\Connection\TestUtil; use Oro\Tests\TestCase; use PHPUnit\Framework\Constraint\LogicalOr; @@ -63,7 +59,7 @@ public function functionsDataProvider(): array \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME ); foreach ($files as $file) { - $fileData = Yaml::parseFile($file->getPathname()); + $fileData = Yaml::parseFile($file); if (!\is_array($fileData)) { throw new \RuntimeException(\sprintf('Could not parse file %s', $file)); }