diff --git a/application/Espo/ORM/Query/Part/Condition.php b/application/Espo/ORM/Query/Part/Condition.php index 39b42a6219..cc618d5e31 100644 --- a/application/Espo/ORM/Query/Part/Condition.php +++ b/application/Espo/ORM/Query/Part/Condition.php @@ -91,10 +91,13 @@ class Condition * Create '=' comparison. * * @param Expression $argument1 An expression. - * @param Expression|string|int|float|bool|null $argument2 A value (if scalar) or expression. + * @param Expression|Select|string|int|float|bool|null $argument2 A scalar, expression or sub-query. */ - public static function equal(Expression $argument1, Expression|string|int|float|bool|null $argument2): Comparison - { + public static function equal( + Expression $argument1, + Expression|Select|string|int|float|bool|null $argument2 + ): Comparison { + return Comparison::equal($argument1, $argument2); } @@ -102,10 +105,13 @@ class Condition * Create '!=' comparison. * * @param Expression $argument1 An expression. - * @param Expression|string|int|float|bool|null $argument2 A value (if scalar) or expression. + * @param Expression|Select|string|int|float|bool|null $argument2 A scalar, expression or sub-query. */ - public static function notEqual(Expression $argument1, Expression|string|int|float|bool|null $argument2): Comparison - { + public static function notEqual( + Expression $argument1, + Expression|Select|string|int|float|bool|null $argument2 + ): Comparison { + return Comparison::notEqual($argument1, $argument2); } @@ -135,10 +141,13 @@ class Condition * Create '>' comparison. * * @param Expression $argument1 An expression. - * @param Expression|string|int|float $argument2 A value (if scalar) or expression. + * @param Expression|Select|string|int|float $argument2 A scalar, expression or sub-query. */ - public static function greater(Expression $argument1, Expression|string|int|float $argument2): Comparison - { + public static function greater( + Expression $argument1, + Expression|Select|string|int|float $argument2 + ): Comparison { + return Comparison::greater($argument1, $argument2); } @@ -146,10 +155,13 @@ class Condition * Create '>=' comparison. * * @param Expression $argument1 An expression. - * @param Expression|string|int|float $argument2 A value (if scalar) or expression. + * @param Expression|Select|string|int|float $argument2 A scalar, expression or sub-query. */ - public static function greaterOrEqual(Expression $argument1, Expression|string|int|float $argument2): Comparison - { + public static function greaterOrEqual( + Expression $argument1, + Expression|Select|string|int|float $argument2 + ): Comparison { + return Comparison::greaterOrEqual($argument1, $argument2); } @@ -157,10 +169,13 @@ class Condition * Create '<' comparison. * * @param Expression $argument1 An expression. - * @param Expression|string|int|float $argument2 A value (if scalar) or expression. + * @param Expression|Select|string|int|float $argument2 A scalar, expression or sub-query. */ - public static function less(Expression $argument1, Expression|string|int|float $argument2): Comparison - { + public static function less( + Expression $argument1, + Expression|Select|string|int|float $argument2 + ): Comparison { + return Comparison::less($argument1, $argument2); } @@ -168,10 +183,13 @@ class Condition * Create '<=' comparison. * * @param Expression $argument1 An expression. - * @param Expression|string|int|float $argument2 A value (if scalar) or expression. + * @param Expression|Select|string|int|float $argument2 A scalar, expression or sub-query. */ - public static function lessOrEqual(Expression $argument1, Expression|string|int|float $argument2): Comparison - { + public static function lessOrEqual( + Expression $argument1, + Expression|Select|string|int|float $argument2 + ): Comparison { + return Comparison::lessOrEqual($argument1, $argument2); } diff --git a/application/Espo/ORM/Query/Part/Where/Comparison.php b/application/Espo/ORM/Query/Part/Where/Comparison.php index 9751d6dd09..564ecebd43 100644 --- a/application/Espo/ORM/Query/Part/Where/Comparison.php +++ b/application/Espo/ORM/Query/Part/Where/Comparison.php @@ -81,11 +81,14 @@ class Comparison implements WhereItem * Create '=' comparison. * * @param Expression $argument1 An expression. - * @param Expression|string|int|float|bool|null $argument2 A value (if scalar) or expression. + * @param Expression|Select|string|int|float|bool|null $argument2 A scalar, expression or sub-query. * @return self */ - public static function equal(Expression $argument1, Expression|string|int|float|bool|null $argument2): self - { + public static function equal( + Expression $argument1, + Expression|Select|string|int|float|bool|null $argument2 + ): self { + return self::createComparison(self::OPERATOR_EQUAL, $argument1, $argument2); } @@ -93,11 +96,14 @@ class Comparison implements WhereItem * Create '!=' comparison. * * @param Expression $argument1 An expression. - * @param Expression|string|int|float|bool|null $argument2 A value (if scalar) or expression. + * @param Expression|Select|string|int|float|bool|null $argument2 A scalar, expression or sub-query. * @return self */ - public static function notEqual(Expression $argument1, Expression|string|int|float|bool|null $argument2): self - { + public static function notEqual( + Expression $argument1, + Expression|Select|string|int|float|bool|null $argument2 + ): self { + return self::createComparison(self::OPERATOR_NOT_EQUAL, $argument1, $argument2); } @@ -129,10 +135,10 @@ class Comparison implements WhereItem * Create '>' comparison. * * @param Expression $argument1 An expression. - * @param Expression|string|int|float $argument2 A value (if scalar) or expression. + * @param Expression|Select|string|int|float $argument2 A scalar, expression or sub-query. * @return self */ - public static function greater(Expression $argument1, Expression|string|int|float $argument2): self + public static function greater(Expression $argument1, Expression|Select|string|int|float $argument2): self { return self::createComparison(self::OPERATOR_GREATER, $argument1, $argument2); } @@ -141,10 +147,10 @@ class Comparison implements WhereItem * Create '>=' comparison. * * @param Expression $argument1 An expression. - * @param Expression|string|int|float $argument2 A value (if scalar) or expression. + * @param Expression|Select|string|int|float $argument2 A scalar, expression or sub-query. * @return self */ - public static function greaterOrEqual(Expression $argument1, Expression|string|int|float $argument2): self + public static function greaterOrEqual(Expression $argument1, Expression|Select|string|int|float $argument2): self { return self::createComparison(self::OPERATOR_GREATER_OR_EQUAL, $argument1, $argument2); } @@ -153,10 +159,10 @@ class Comparison implements WhereItem * Create '<' comparison. * * @param Expression $argument1 An expression. - * @param Expression|string|int|float $argument2 A value (if scalar) or expression. + * @param Expression|Select|string|int|float $argument2 A scalar, expression or sub-query. * @return self */ - public static function less(Expression $argument1, Expression|string|int|float $argument2): self + public static function less(Expression $argument1, Expression|Select|string|int|float $argument2): self { return self::createComparison(self::OPERATOR_LESS, $argument1, $argument2); } @@ -165,10 +171,10 @@ class Comparison implements WhereItem * Create '<=' comparison. * * @param Expression $argument1 An expression. - * @param Expression|string|int|float $argument2 A value (if scalar) or expression. + * @param Expression|Select|string|int|float $argument2 A scalar, expression or sub-query. * @return self */ - public static function lessOrEqual(Expression $argument1, Expression|string|int|float $argument2): self + public static function lessOrEqual(Expression $argument1, Expression|Select|string|int|float $argument2): self { return self::createComparison(self::OPERATOR_LESS_OR_EQUAL, $argument1, $argument2); } @@ -208,7 +214,7 @@ class Comparison implements WhereItem private static function createComparison( string $operator, Expression|string $argument1, - Expression|string|int|float|bool|null $argument2 + Expression|Select|string|int|float|bool|null $argument2 ): self { if (is_string($argument1)) { diff --git a/application/Espo/ORM/QueryComposer/BaseQueryComposer.php b/application/Espo/ORM/QueryComposer/BaseQueryComposer.php index f565252703..dc4f4b1545 100644 --- a/application/Espo/ORM/QueryComposer/BaseQueryComposer.php +++ b/application/Espo/ORM/QueryComposer/BaseQueryComposer.php @@ -2462,6 +2462,7 @@ abstract class BaseQueryComposer implements QueryComposer return $this->quote(false); } + // @todo Operators (s, <=s, =>s) producing 'operator ANY (sub-query)'. if ($operatorOrm === '=s' || $operatorOrm === '!=s') { if ($value instanceof Select) { $subSql = $this->composeSelect($value); @@ -2494,6 +2495,16 @@ abstract class BaseQueryComposer implements QueryComposer return "{$leftPart} {$operator} ({$subSql})"; } + if ($value instanceof Select) { + if ($operatorOrm === '*' || $operatorOrm === '!*') { + throw new RuntimeException("LIKE operator is not compatible with sub-query."); + } + + $subQueryPart = $this->composeSelect($value); + + return "{$leftPart} {$operator} ({$subQueryPart})"; + } + if (is_array($value)) { $valuePartList = $value; diff --git a/tests/unit/Espo/ORM/MysqlQueryComposerTest.php b/tests/unit/Espo/ORM/MysqlQueryComposerTest.php index 5e435cb356..42caf28290 100644 --- a/tests/unit/Espo/ORM/MysqlQueryComposerTest.php +++ b/tests/unit/Espo/ORM/MysqlQueryComposerTest.php @@ -1136,6 +1136,58 @@ class MysqlQueryComposerTest extends \PHPUnit\Framework\TestCase $this->assertEquals($expectedSql, $sql); } + public function testSelectWithSubquery5() + { + $subQuery = SelectBuilder::create() + ->from('Post') + ->select('id') + ->where(['name' => 'test']) + ->build(); + + $sql = $this->query->composeSelect( + SelectBuilder::create() + ->from('Post') + ->select('id') + ->where( + Comparison::equal(Expression::value('1'), $subQuery) + ) + ->build() + ); + + $expectedSql = + "SELECT post.id AS `id` FROM `post` ". + "WHERE '1' = (SELECT post.id AS `id` FROM `post` ". + "WHERE post.name = 'test' AND post.deleted = 0) AND post.deleted = 0"; + + $this->assertEquals($expectedSql, $sql); + } + + public function testSelectWithSubquery6() + { + $subQuery = SelectBuilder::create() + ->from('Post') + ->select('id') + ->where(['name' => 'test']) + ->build(); + + $sql = $this->query->composeSelect( + SelectBuilder::create() + ->from('Post') + ->select('id') + ->where( + Comparison::greaterOrEqual(Expression::value('1'), $subQuery) + ) + ->build() + ); + + $expectedSql = + "SELECT post.id AS `id` FROM `post` ". + "WHERE '1' >= (SELECT post.id AS `id` FROM `post` ". + "WHERE post.name = 'test' AND post.deleted = 0) AND post.deleted = 0"; + + $this->assertEquals($expectedSql, $sql); + } + public function testSelectExists1(): void { $expectedSql =