Skip to content

Commit

Permalink
Variable length arithmetic (#25)
Browse files Browse the repository at this point in the history
Co-authored-by: tpetry <[email protected]>
  • Loading branch information
JasperTey and tpetry authored Feb 12, 2024
1 parent e4d1576 commit df0fe4f
Show file tree
Hide file tree
Showing 14 changed files with 150 additions and 33 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,12 @@ use Tpetry\QueryExpressions\Operator\Arithmetic\{
};
use Tpetry\QueryExpressions\Operator\Value\Value;

new Add(string|Expression $value1, string|Expression $value2);
new Divide(string|Expression $value1, string|Expression $value2);
new Modulo(string|Expression $value1, string|Expression $value2);
new Multiply(string|Expression $value1, string|Expression $value2);
new Add(string|Expression $value1, string|Expression $value2, string|Expression ...$values);
new Divide(string|Expression $value1, string|Expression $value2, string|Expression ...$values);
new Modulo(string|Expression $value1, string|Expression $value2, string|Expression ...$values);
new Multiply(string|Expression $value1, string|Expression $value2, string|Expression ...$values);
new Power(string|Expression $value1, string|Expression $value2);
new Subtract(string|Expression $value1, string|Expression $value2);
new Subtract(string|Expression $value1, string|Expression $value2, string|Expression ...$values);

// UPDATE user_quotas SET credits = credits - 15 WHERE id = 1985
$quota->update([
Expand Down
4 changes: 1 addition & 3 deletions src/Operator/Arithmetic/Add.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

namespace Tpetry\QueryExpressions\Operator\Arithmetic;

use Tpetry\QueryExpressions\Operator\OperatorExpression;

class Add extends OperatorExpression
class Add extends ArithmeticExpression
{
protected function operator(): string
{
Expand Down
48 changes: 48 additions & 0 deletions src/Operator/Arithmetic/ArithmeticExpression.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace Tpetry\QueryExpressions\Operator\Arithmetic;

use Illuminate\Contracts\Database\Query\Expression;
use Illuminate\Database\Grammar;
use Tpetry\QueryExpressions\Concerns\StringizeExpression;

/**
* @internal
*/
abstract class ArithmeticExpression implements Expression
{
use StringizeExpression;

/** @var string[]|Expression[] */
private readonly array $values;

public function __construct(
private readonly string|Expression $value1,
private readonly string|Expression $value2,
string|Expression ...$values,
) {
$this->values = $values;
}

public function getValue(Grammar $grammar): string
{
$expression = implode(" {$this->operator()} ", $this->expressions($grammar));

return "({$expression})";
}

/**
* @return array<int, float|int|string>
*/
protected function expressions(Grammar $grammar): array
{
return array_map(
fn (string|Expression $value) => $this->stringize($grammar, $value),
[$this->value1, $this->value2, ...$this->values],
);
}

abstract protected function operator(): string;
}
4 changes: 1 addition & 3 deletions src/Operator/Arithmetic/Divide.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

namespace Tpetry\QueryExpressions\Operator\Arithmetic;

use Tpetry\QueryExpressions\Operator\OperatorExpression;

class Divide extends OperatorExpression
class Divide extends ArithmeticExpression
{
protected function operator(): string
{
Expand Down
4 changes: 1 addition & 3 deletions src/Operator/Arithmetic/Modulo.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

namespace Tpetry\QueryExpressions\Operator\Arithmetic;

use Tpetry\QueryExpressions\Operator\OperatorExpression;

class Modulo extends OperatorExpression
class Modulo extends ArithmeticExpression
{
protected function operator(): string
{
Expand Down
4 changes: 1 addition & 3 deletions src/Operator/Arithmetic/Multiply.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

namespace Tpetry\QueryExpressions\Operator\Arithmetic;

use Tpetry\QueryExpressions\Operator\OperatorExpression;

class Multiply extends OperatorExpression
class Multiply extends ArithmeticExpression
{
protected function operator(): string
{
Expand Down
39 changes: 26 additions & 13 deletions src/Operator/Arithmetic/Power.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,43 @@

namespace Tpetry\QueryExpressions\Operator\Arithmetic;

use Illuminate\Contracts\Database\Query\Expression;
use Illuminate\Database\Grammar;
use Tpetry\QueryExpressions\Concerns\IdentifiesDriver;
use Tpetry\QueryExpressions\Concerns\StringizeExpression;

class Power implements Expression
class Power extends ArithmeticExpression
{
use IdentifiesDriver;
use StringizeExpression;

public function __construct(
private readonly string|Expression $value1,
private readonly string|Expression $value2,
) {
public function getValue(Grammar $grammar): string
{
return match ($this->identify($grammar)) {
'mysql', 'sqlite', 'sqlsrv' => $this->buildPowerFunctionChain($grammar),
'pgsql' => parent::getValue($grammar),
};
}

public function getValue(Grammar $grammar)
protected function buildPowerFunctionChain(Grammar $grammar): string
{
$value1 = $this->stringize($grammar, $this->value1);
$value2 = $this->stringize($grammar, $this->value2);
$expressions = $this->expressions($grammar);

return match ($this->identify($grammar)) {
'mysql', 'sqlite', 'sqlsrv' => "power({$value1}, {$value2})",
'pgsql' => "({$value1} ^ {$value2})",
};
// Build the initial expressions by using the two required parameters of the object.
$value0 = array_shift($expressions);
$value1 = array_shift($expressions);
$expression = "power({$value0}, {$value1})";

// For each remaining value call the power function again with the last result and the new value.
while (count($expressions) > 0) {
$value = array_shift($expressions);
$expression = "power({$expression}, $value)";
}

return $expression;
}

protected function operator(): string
{
return '^';
}
}
4 changes: 1 addition & 3 deletions src/Operator/Arithmetic/Subtract.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

namespace Tpetry\QueryExpressions\Operator\Arithmetic;

use Tpetry\QueryExpressions\Operator\OperatorExpression;

class Subtract extends OperatorExpression
class Subtract extends ArithmeticExpression
{
protected function operator(): string
{
Expand Down
11 changes: 11 additions & 0 deletions tests/Operator/Arithmetic/AddTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,14 @@
->toBePgsql('(0 + "val")')
->toBeSqlite('(0 + "val")')
->toBeSqlsrv('(0 + [val])');

it('can add variadic values')
->expect(new Add(new Expression(0), 'val1', 'val2', new Expression(1)))
->toBeExecutable(function (Blueprint $table) {
$table->integer('val1');
$table->integer('val2');
})
->toBeMysql('(0 + `val1` + `val2` + 1)')
->toBePgsql('(0 + "val1" + "val2" + 1)')
->toBeSqlite('(0 + "val1" + "val2" + 1)')
->toBeSqlsrv('(0 + [val1] + [val2] + 1)');
11 changes: 11 additions & 0 deletions tests/Operator/Arithmetic/DivideTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,14 @@
->toBePgsql('(0 / "val")')
->toBeSqlite('(0 / "val")')
->toBeSqlsrv('(0 / [val])');

it('can divide variadic values')
->expect(new Divide(new Expression(0), 'val1', 'val2', new Expression(1)))
->toBeExecutable(function (Blueprint $table) {
$table->integer('val1');
$table->integer('val2');
})
->toBeMysql('(0 / `val1` / `val2` / 1)')
->toBePgsql('(0 / "val1" / "val2" / 1)')
->toBeSqlite('(0 / "val1" / "val2" / 1)')
->toBeSqlsrv('(0 / [val1] / [val2] / 1)');
11 changes: 11 additions & 0 deletions tests/Operator/Arithmetic/ModuloTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,14 @@
->toBePgsql('(0 % "val")')
->toBeSqlite('(0 % "val")')
->toBeSqlsrv('(0 % [val])');

it('can modulo variadic values')
->expect(new Modulo(new Expression(0), 'val1', 'val2', new Expression(1)))
->toBeExecutable(function (Blueprint $table) {
$table->integer('val1');
$table->integer('val2');
})
->toBeMysql('(0 % `val1` % `val2` % 1)')
->toBePgsql('(0 % "val1" % "val2" % 1)')
->toBeSqlite('(0 % "val1" % "val2" % 1)')
->toBeSqlsrv('(0 % [val1] % [val2] % 1)');
11 changes: 11 additions & 0 deletions tests/Operator/Arithmetic/MultiplyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,14 @@
->toBePgsql('(0 * "val")')
->toBeSqlite('(0 * "val")')
->toBeSqlsrv('(0 * [val])');

it('can multiply variadic values')
->expect(new Multiply(new Expression(0), 'val1', 'val2', new Expression(1)))
->toBeExecutable(function (Blueprint $table) {
$table->integer('val1');
$table->integer('val2');
})
->toBeMysql('(0 * `val1` * `val2` * 1)')
->toBePgsql('(0 * "val1" * "val2" * 1)')
->toBeSqlite('(0 * "val1" * "val2" * 1)')
->toBeSqlsrv('(0 * [val1] * [val2] * 1)');
11 changes: 11 additions & 0 deletions tests/Operator/Arithmetic/PowerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,14 @@
->toBePgsql('(0 ^ "val")')
->toBeSqlite('power(0, "val")')
->toBeSqlsrv('power(0, [val])');

it('can power variadic values')
->expect(new Power(new Expression(0), 'val1', 'val2', new Expression(1)))
->toBeExecutable(function (Blueprint $table) {
$table->integer('val1');
$table->integer('val2');
})
->toBeMysql('power(power(power(0, `val1`), `val2`), 1)')
->toBePgsql('(0 ^ "val1" ^ "val2" ^ 1)')
->toBeSqlite('power(power(power(0, "val1"), "val2"), 1)')
->toBeSqlsrv('power(power(power(0, [val1]), [val2]), 1)');
11 changes: 11 additions & 0 deletions tests/Operator/Arithmetic/SubtractTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,14 @@
->toBePgsql('(0 - "val")')
->toBeSqlite('(0 - "val")')
->toBeSqlsrv('(0 - [val])');

it('can subtract variadic values')
->expect(new Subtract(new Expression(0), 'val1', 'val2', new Expression(1)))
->toBeExecutable(function (Blueprint $table) {
$table->integer('val1');
$table->integer('val2');
})
->toBeMysql('(0 - `val1` - `val2` - 1)')
->toBePgsql('(0 - "val1" - "val2" - 1)')
->toBeSqlite('(0 - "val1" - "val2" - 1)')
->toBeSqlsrv('(0 - [val1] - [val2] - 1)');

0 comments on commit df0fe4f

Please sign in to comment.