diff --git a/src/Processor/Expression/FunctionEvaluator.php b/src/Processor/Expression/FunctionEvaluator.php index 60c81b6d..c6db5e8f 100644 --- a/src/Processor/Expression/FunctionEvaluator.php +++ b/src/Processor/Expression/FunctionEvaluator.php @@ -353,9 +353,13 @@ private static function sqlCount( } /** - * @param array $columns + * @param FakePdoInterface $conn + * @param Scope $scope + * @param FunctionExpression $expr + * @param QueryResult $result * - * @return ?numeric + * @return float|int|mixed|string|null + * @throws ProcessorException */ private static function sqlSum( FakePdoInterface $conn, @@ -368,6 +372,10 @@ private static function sqlSum( $sum = 0; if (!$result->rows) { + if ($expr instanceof FunctionExpression) { + return self::evaluate($conn, $scope, $expr, [], $result); + } + return null; } @@ -1575,6 +1583,10 @@ private static function sqlConvertTz( throw new \InvalidArgumentException("CONVERT_TZ() requires exactly 3 arguments"); } + if ($args[0] instanceof ColumnExpression && empty($row)) { + return null; + } + /** @var string|null $dtValue */ $dtValue = Evaluator::evaluate($conn, $scope, $args[0], $row, $result); /** @var string|null $fromTzValue */ diff --git a/tests/EndToEndTest.php b/tests/EndToEndTest.php index bd6118d1..f59be4f0 100644 --- a/tests/EndToEndTest.php +++ b/tests/EndToEndTest.php @@ -1516,4 +1516,44 @@ public function leastWithExceptionProvider(): iterable yield ['Should fail with single argument' => [1]]; yield ['Should fail without any arguments' => []]; } + + public function testNestedFunctions() + { + $pdo = self::getConnectionToFullDB(); + + $query = $pdo->prepare(" + SELECT + SUM( + TIMESTAMPDIFF( + SECOND, + CONVERT_TZ('2025-12-31 22:59:59', 'Europe/Kyiv', 'Europe/Kyiv'), + CONVERT_TZ('2025-12-31 23:59:59', 'Europe/Kyiv', 'Europe/Kyiv') + ) + ) + "); + $query->execute(); + + $this->assertSame(3600, (int)$query->fetchColumn()); + } + + public function testNestedFunctionsFromDB() + { + $pdo = self::getConnectionToFullDB(); + $count = $pdo->query("SELECT COUNT(*) FROM video_game_characters")->fetchColumn(); + + $query = $pdo->prepare(" + SELECT SUM( + TIMESTAMPDIFF( + SECOND, + CONVERT_TZ(`created_on`, 'Europe/Kyiv', 'Europe/Kyiv'), + CONVERT_TZ(`created_on` + INTERVAL 1 SECOND, 'Europe/Kyiv', 'Europe/Kyiv') + ) + ) + FROM `video_game_characters` + "); + + $query->execute(); + + $this->assertSame((int)$count, (int)$query->fetchColumn()); + } }