Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
b24e918
chore: add dev container support
barbosa89 Dec 19, 2025
354f80e
feat(SQLite): add SQLite support with connection handling and databas…
barbosa89 Dec 20, 2025
20b8f0a
feat(ParallelQueue): disable processing when no running tasks and que…
barbosa89 Dec 22, 2025
94b908f
feat(devcontainer): add Dockerfile for PHP environment setup
barbosa89 Dec 22, 2025
6c33132
refactor(Database): move common methods to super class and overload m…
barbosa89 Dec 22, 2025
3cb5e85
refactor(Database): implement transaction handling and remove unused …
barbosa89 Dec 23, 2025
ae1d84c
feat: introduce AST for each supported SQL driver
barbosa89 Dec 24, 2025
1306eae
style: php cs
barbosa89 Dec 24, 2025
379a1a8
refactor(Database): improve connection handling and default dialect b…
barbosa89 Dec 24, 2025
b3d5aa1
fix(PostgresInsertCompiler): ensure placeholders are correctly indexe…
barbosa89 Dec 24, 2025
0b42403
feat: add SQLite3 support to composer dependencies
barbosa89 Dec 24, 2025
8885b30
feat: remove DialectCapabilities and add unit tests for MySQL, Postgr…
barbosa89 Dec 25, 2025
9fbfead
refactor(BuildsQuery): remove unused query building methods and const…
barbosa89 Dec 25, 2025
f8f25e0
Refactor database dialect compilers to initialize where compilers
barbosa89 Dec 30, 2025
f90c704
feat: add PostgreSQL/SQLite support for select column query generatio…
barbosa89 Dec 30, 2025
ad47e44
feat: rename distinct operators to not equal across query clauses and…
barbosa89 Dec 30, 2025
0e49e09
tests: delete statement for sqlite and postgres
barbosa89 Dec 30, 2025
3456b2a
feat: add SQLite support for grouped query generation tests
barbosa89 Dec 30, 2025
b239f48
tests: add SQLite and Posgres having clause query generation tests
barbosa89 Dec 30, 2025
1817ddc
feat: implement HasPlaceholders trait and update PostgreSQL compilers…
barbosa89 Dec 30, 2025
e326dbd
style: php cs
barbosa89 Dec 30, 2025
bc8f5d0
fix: update having clause parameter placeholders to use positional sy…
barbosa89 Dec 30, 2025
1adb244
tests: SQLite and Postgre support for join clause query generation
barbosa89 Dec 30, 2025
db67a0e
tests: add SQLite pagination query generation tests
barbosa89 Dec 30, 2025
9f7ba6c
fix: update placeholder conversion to include parameter count in Post…
barbosa89 Dec 31, 2025
2764893
tests: add SQLite support for update statement generation
barbosa89 Dec 31, 2025
35219db
fix: update subquery clause compilation to use correct SQL method
barbosa89 Dec 31, 2025
5e2c8a1
feat: add SQLite support for where clause query generation tests
barbosa89 Dec 31, 2025
988c91b
feat: add tests for date, month, and year query generation in PostgreSQL
barbosa89 Dec 31, 2025
6d6983d
feat: add SQLite support for date, month, and year query generation t…
barbosa89 Dec 31, 2025
3f29f31
feat: implement RETURNING clause support for DELETE statements in Pos…
barbosa89 Dec 31, 2025
5e6affc
style: php cs
barbosa89 Dec 31, 2025
eb3a3c5
feat: add RETURNING clause support for UPDATE statements in PostgreSQ…
barbosa89 Dec 31, 2025
fed5c8f
feat: add error handling for AUTOINCREMENT sequence reset in SQLite t…
barbosa89 Dec 31, 2025
fa1ce87
refactor: rename compilers for each driver
barbosa89 Dec 31, 2025
37c1e29
refactor: rename placeholder constant
barbosa89 Dec 31, 2025
6a790db
feat: replace string placeholders with SQL constant in WHERE clause r…
barbosa89 Dec 31, 2025
ac76184
feat: implement base Dialect class and extend for MySQL, PostgreSQL, …
barbosa89 Dec 31, 2025
82709db
refactor: remove MySQLWhereCompiler class as part of dialect consolid…
barbosa89 Dec 31, 2025
0d9b1f9
feat: implement WhereCompiler abstraction and extend for MySQL, Postg…
barbosa89 Jan 2, 2026
edfc4e6
feat: enhance WhereCompiler with compile method and clause handling f…
barbosa89 Jan 2, 2026
00d4178
refactor: simplify SQLite Where compiler by extending PostgreSQL impl…
barbosa89 Jan 2, 2026
e3cec82
refactor: remove unused WhereClause import and commented pushClause m…
barbosa89 Jan 2, 2026
6386606
feat: add insertOrIgnore, insertFrom, and upsert methods to QueryBuil…
barbosa89 Jan 2, 2026
937f7de
refactor: remove RawWhereClause and its related compilation logic fro…
barbosa89 Jan 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM php:8.2-cli

RUN apt-get update && apt-get install -y \
git \
curl \
wget \
unzip \
zip \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

RUN docker-php-ext-install \
pcntl \
sockets

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

ENV COMPOSER_ALLOW_SUPERUSER=1
ENV COMPOSER_HOME=/composer
23 changes: 23 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "PHP",
"build": {
"dockerfile": "./Dockerfile",
"context": ".."
},
"customizations": {
"vscode": {
"extensions": [
"bmewburn.vscode-intelephense-client",
"xdebug.php-pack",
"devsense.phptools-vscode",
"mehedidracula.php-namespace-resolver",
"devsense.composer-php-vscode",
"phiter.phpstorm-snippets"
]
}
},
"forwardPorts": [
8080
],
"remoteUser": "root"
}
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for more information:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
# https://containers.dev/guide/dependabot

version: 2
updates:
- package-ecosystem: "devcontainers"
directory: "/"
schedule:
interval: weekly
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
"require": {
"php": "^8.2",
"ext-pcntl": "*",
"ext-sockets": "*",
"adbario/php-dot-notation": "^3.1",
"ahjdev/amphp-sqlite3": "dev-main",
"amphp/cache": "^2.0",
"amphp/cluster": "^2.0",
"amphp/file": "^v3.0.0",
Expand Down
40 changes: 25 additions & 15 deletions src/Database/Clause.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,36 @@
namespace Phenix\Database;

use Closure;
use Phenix\Database\Clauses\BasicWhereClause;
use Phenix\Database\Clauses\SubqueryWhereClause;
use Phenix\Database\Clauses\WhereClause;
use Phenix\Database\Concerns\Query\HasWhereClause;
use Phenix\Database\Concerns\Query\PrepareColumns;
use Phenix\Database\Constants\LogicalOperator;
use Phenix\Database\Constants\LogicalConnector;
use Phenix\Database\Constants\Operator;
use Phenix\Database\Constants\SQL;
use Phenix\Database\Contracts\Builder;
use Phenix\Util\Arr;

use function count;
use function is_array;

abstract class Clause extends Grammar implements Builder
{
use HasWhereClause;
use PrepareColumns;

/**
* @var array<int, WhereClause>
*/
protected array $clauses;

protected array $arguments;

protected function resolveWhereMethod(
string $column,
Operator $operator,
Closure|array|string|int $value,
LogicalOperator $logicalConnector = LogicalOperator::AND
LogicalConnector $logicalConnector = LogicalConnector::AND
): void {
if ($value instanceof Closure) {
$this->whereSubquery(
Expand All @@ -46,7 +53,7 @@ protected function whereSubquery(
Operator $comparisonOperator,
string|null $column = null,
Operator|null $operator = null,
LogicalOperator $logicalConnector = LogicalOperator::AND
LogicalConnector $logicalConnector = LogicalConnector::AND
): void {
$builder = new Subquery($this->driver);
$builder->select(['*']);
Expand All @@ -55,9 +62,16 @@ protected function whereSubquery(

[$dml, $arguments] = $builder->toSql();

$value = $operator?->value . $dml;
$connector = count($this->clauses) === 0 ? null : $logicalConnector;

$this->pushClause(array_filter([$column, $comparisonOperator, $value]), $logicalConnector);
$this->clauses[] = new SubqueryWhereClause(
comparisonOperator: $comparisonOperator,
sql: trim($dml, '()'),
params: $arguments,
column: $column,
operator: $operator,
connector: $connector
);

$this->arguments = array_merge($this->arguments, $arguments);
}
Expand All @@ -66,21 +80,17 @@ protected function pushWhereWithArgs(
string $column,
Operator $operator,
array|string|int $value,
LogicalOperator $logicalConnector = LogicalOperator::AND
LogicalConnector $logicalConnector = LogicalConnector::AND
): void {
$placeholders = is_array($value)
? array_fill(0, count($value), SQL::PLACEHOLDER->value)
: SQL::PLACEHOLDER->value;

$this->pushClause([$column, $operator, $placeholders], $logicalConnector);
$this->pushClause(new BasicWhereClause($column, $operator, $value, null, true), $logicalConnector);

$this->arguments = array_merge($this->arguments, (array) $value);
}

protected function pushClause(array $where, LogicalOperator $logicalConnector = LogicalOperator::AND): void
protected function pushClause(WhereClause $where, LogicalConnector $logicalConnector = LogicalConnector::AND): void
{
if (count($this->clauses) > 0) {
array_unshift($where, $logicalConnector);
$where->setConnector($logicalConnector);
}

$this->clauses[] = $where;
Expand All @@ -92,7 +102,7 @@ protected function prepareClauses(array $clauses): array
return array_map(function ($value) {
return match (true) {
$value instanceof Operator => $value->value,
$value instanceof LogicalOperator => $value->value,
$value instanceof LogicalConnector => $value->value,
is_array($value) => '(' . Arr::implodeDeeply($value, ', ') . ')',
default => $value,
};
Expand Down
81 changes: 81 additions & 0 deletions src/Database/Clauses/BasicWhereClause.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

declare(strict_types=1);

namespace Phenix\Database\Clauses;

use Phenix\Database\Constants\LogicalConnector;
use Phenix\Database\Constants\Operator;
use Phenix\Database\Constants\SQL;

use function count;
use function is_array;

class BasicWhereClause extends WhereClause
{
protected string $column;

protected Operator $operator;

protected array|string|int $value;

protected bool $usePlaceholder;

public function __construct(
string $column,
Operator $operator,
array|string|int $value,
LogicalConnector|null $connector = null,
bool $usePlaceholder = false
) {
$this->column = $column;
$this->operator = $operator;
$this->value = $value;
$this->connector = $connector;
$this->usePlaceholder = $usePlaceholder;
}

public function getColumn(): string
{
return $this->column;
}

public function getOperator(): Operator
{
return $this->operator;
}

public function getValue(): array|string|int
{
return $this->value;
}

public function renderValue(): string
{
if ($this->usePlaceholder) {
// In WHERE context with parameterized queries, use placeholder
if (is_array($this->value)) {
return '(' . implode(', ', array_fill(0, count($this->value), SQL::PLACEHOLDER->value)) . ')';
}

return SQL::PLACEHOLDER->value;
}

// In JOIN ON context, render the value directly (typically a column name)
return (string) $this->value;
}

public function getValueCount(): int
{
if (is_array($this->value)) {
return count($this->value);
}

return 1;
}

public function isInOperator(): bool
{
return $this->operator === Operator::IN || $this->operator === Operator::NOT_IN;
}
}
50 changes: 50 additions & 0 deletions src/Database/Clauses/BetweenWhereClause.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Phenix\Database\Clauses;

use Phenix\Database\Constants\LogicalConnector;
use Phenix\Database\Constants\Operator;
use Phenix\Database\Constants\SQL;

class BetweenWhereClause extends WhereClause
{
protected string $column;

protected Operator $operator;

protected array $values;

public function __construct(
string $column,
Operator $operator, // BETWEEN or NOT_BETWEEN
array $values,
LogicalConnector|null $connector = null
) {
$this->column = $column;
$this->operator = $operator;
$this->values = $values;
$this->connector = $connector;
}

public function getColumn(): string
{
return $this->column;
}

public function getOperator(): Operator
{
return $this->operator;
}

public function getValues(): array
{
return $this->values;
}

public function renderValue(): string
{
return SQL::PLACEHOLDER->value . ' AND ' . SQL::PLACEHOLDER->value;
}
}
41 changes: 41 additions & 0 deletions src/Database/Clauses/BooleanWhereClause.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Phenix\Database\Clauses;

use Phenix\Database\Constants\LogicalConnector;
use Phenix\Database\Constants\Operator;

class BooleanWhereClause extends WhereClause
{
protected string $column;

protected Operator $operator;

public function __construct(
string $column,
Operator $operator, // IS_TRUE or IS_FALSE
LogicalConnector|null $connector = null
) {
$this->column = $column;
$this->operator = $operator;
$this->connector = $connector;
}

public function getColumn(): string
{
return $this->column;
}

public function getOperator(): Operator
{
return $this->operator;
}

public function renderValue(): string
{
// Boolean clauses (IS TRUE/IS FALSE) have no value part
return '';
}
}
50 changes: 50 additions & 0 deletions src/Database/Clauses/ColumnWhereClause.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Phenix\Database\Clauses;

use Phenix\Database\Constants\LogicalConnector;
use Phenix\Database\Constants\Operator;

class ColumnWhereClause extends WhereClause
{
protected string $column;

protected Operator $operator;

protected string $compareColumn;

public function __construct(
string $column,
Operator $operator,
string $compareColumn,
LogicalConnector|null $connector = null
) {
$this->column = $column;
$this->operator = $operator;
$this->compareColumn = $compareColumn;
$this->connector = $connector;
}

public function getColumn(): string
{
return $this->column;
}

public function getOperator(): Operator
{
return $this->operator;
}

public function getCompareColumn(): string
{
return $this->compareColumn;
}

public function renderValue(): string
{
// Column comparisons use the column name directly, not a placeholder
return $this->compareColumn;
}
}
Loading