Skip to content

Commit

Permalink
Merge pull request #38 from sergiosalvatore/add-ceil-floor-support
Browse files Browse the repository at this point in the history
Add CEIL()/CEILING() and FLOOR() SQL Function Support
  • Loading branch information
aaronm67 committed Feb 8, 2023
2 parents 3c6ec7c + a6a8537 commit 21a606f
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 0 deletions.
98 changes: 98 additions & 0 deletions src/Processor/Expression/FunctionEvaluator.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Vimeo\MysqlEngine\Query\Expression\FunctionExpression;
use Vimeo\MysqlEngine\Query\Expression\IntervalOperatorExpression;
use Vimeo\MysqlEngine\Schema\Column;
use Vimeo\MysqlEngine\TokenType;

final class FunctionEvaluator
{
Expand Down Expand Up @@ -91,6 +92,11 @@ public static function evaluate(
return self::sqlDateAdd($conn, $scope, $expr, $row, $result);
case 'ROUND':
return self::sqlRound($conn, $scope, $expr, $row, $result);
case 'CEIL':
case 'CEILING':
return self::sqlCeiling($conn, $scope, $expr, $row, $result);
case 'FLOOR':
return self::sqlFloor($conn, $scope, $expr, $row, $result);
case 'DATEDIFF':
return self::sqlDateDiff($conn, $scope, $expr, $row, $result);
case 'DAY':
Expand Down Expand Up @@ -138,8 +144,34 @@ public static function getColumnSchema(

case 'MOD':
return new Column\IntColumn(false, 10);

case 'AVG':
return new Column\FloatColumn(10, 2);

case 'CEIL':
case 'CEILING':
case 'FLOOR':
// from MySQL docs: https://dev.mysql.com/doc/refman/5.6/en/mathematical-functions.html#function_ceil
// For exact-value numeric arguments, the return value has an exact-value numeric type. For string or
// floating-point arguments, the return value has a floating-point type. But...
//
// mysql> CREATE TEMPORARY TABLE `temp` SELECT FLOOR(1.2);
// Query OK, 1 row affected (0.00 sec)
// Records: 1 Duplicates: 0 Warnings: 0
//
// mysql> describe temp;
// +------------+--------+------+-----+---------+-------+
// | Field | Type | Null | Key | Default | Extra |
// +------------+--------+------+-----+---------+-------+
// | FLOOR(1.2) | bigint | NO | | 0 | NULL |
// +------------+--------+------+-----+---------+-------+
// 1 row in set (0.00 sec)
if ($expr->args[0]->getType() == TokenType::STRING_CONSTANT) {
return new Column\DoubleColumn(10, 2);
}

return new Column\BigInt(false, 10);

case 'IF':
$if = Evaluator::getColumnSchema($expr->args[1], $scope, $columns);
$else = Evaluator::getColumnSchema($expr->args[2], $scope, $columns);
Expand Down Expand Up @@ -1377,6 +1409,72 @@ private static function sqlInetNtoa(
return long2ip((int)$subject);
}

/**
* @param array<string, mixed> $row
* @return float|0
*/
private static function sqlCeiling(
FakePdoInterface $conn,
Scope $scope,
FunctionExpression $expr,
array $row,
QueryResult $result
) {
$args = $expr->args;

if (\count($args) !== 1) {
throw new ProcessorException("MySQL CEILING function must be called with one argument (got " . count($args) . ")");
}

$subject = Evaluator::evaluate($conn, $scope, $args[0], $row, $result);

if (!is_numeric($subject)) {
// CEILING() returns 0 if it does not understand its argument.
return 0;
}

$value = ceil(floatval($subject));

if (!$value) {
return 0;
}

return $value;
}

/**
* @param array<string, mixed> $row
* @return float|0
*/
private static function sqlFloor(
FakePdoInterface $conn,
Scope $scope,
FunctionExpression $expr,
array $row,
QueryResult $result
) {
$args = $expr->args;

if (\count($args) !== 1) {
throw new ProcessorException("MySQL FLOOR function must be called with one argument");
}

$subject = Evaluator::evaluate($conn, $scope, $args[0], $row, $result);

if (!is_numeric($subject)) {
// FLOOR() returns 0 if it does not understand its argument.
return 0;
}

$value = floor(floatval($subject));

if (!$value) {
return 0;
}

return $value;
}

private static function getPhpIntervalFromExpression(
FakePdoInterface $conn,
Scope $scope,
Expand Down
46 changes: 46 additions & 0 deletions tests/EndToEndTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,52 @@ public function testRound()
);
}

public function testCeil()
{
$pdo = self::getPdo('mysql:foo');
$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);

$query = $pdo->prepare('SELECT CEIL(3.1) AS a, CEILING(4.7) AS b, CEIL("abc") AS c, CEIL(5) AS d, CEIL("5.5") AS e');

$query->execute();

$this->assertSame(
[
[
'a' => 4,
'b' => 5,
'c' => 0.0,
'd' => 5,
'e' => 6.0,
],
],
$query->fetchAll(\PDO::FETCH_ASSOC)
);
}

public function testFloor()
{
$pdo = self::getPdo('mysql:foo');
$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);

$query = $pdo->prepare('SELECT FLOOR(3.1) AS a, FLOOR(4.7) AS b, FLOOR("abc") AS c, FLOOR(5) AS d, FLOOR("6.5") AS e');

$query->execute();

$this->assertSame(
[
[
'a' => 3,
'b' => 4,
'c' => 0.0,
'd' => 5,
'e' => 6.0,
],
],
$query->fetchAll(\PDO::FETCH_ASSOC)
);
}

public function testIsInFullSubquery()
{
$pdo = self::getConnectionToFullDB(false);
Expand Down

0 comments on commit 21a606f

Please sign in to comment.