Skip to content

Commit 0c581a5

Browse files
committed
Implemented Postgre Query Builder Support
1 parent 5fe3cbd commit 0c581a5

File tree

7 files changed

+251
-74
lines changed

7 files changed

+251
-74
lines changed

config/mysqli/config.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
return [
44
'connection' => [
5-
'driver' => 'mysql',
65
'host' => $_ENV['DB_HOST_MYSQL'] ?? $_ENV['DB_HOST'] ?? '127.0.0.1',
76
'port' => (int) ($_ENV['DB_PORT_MYSQL'] ?? $_ENV['DB_PORT'] ?? 3306),
87
'database' => $_ENV['DB_DATABASE_MYSQL'] ?? $_ENV['DB_DATABASE'] ?? 'test',

src/MySQLi/DB.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ private static function initializeIfNeeded(): void
4545
throw new \RuntimeException('MySQLi connection configuration must be an array.');
4646
}
4747

48-
// Validate required connection parameters
4948
$required = ['host', 'database', 'username'];
5049
foreach ($required as $key) {
5150
if (!isset($connectionConfig[$key])) {

src/PostgreSQL/DB.php

Whitespace-only changes.

src/QueryBuilder/MysqliQueryBuilder.php

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -224,40 +224,4 @@ public function delete(): PromiseInterface
224224

225225
return AsyncMySQLi::execute($sql, $this->getCompiledBindings());
226226
}
227-
228-
/**
229-
* Execute a raw SQL query.
230-
*
231-
* @param string $sql The raw SQL query.
232-
* @param array<mixed> $bindings The parameter bindings for the query.
233-
* @return PromiseInterface<array<int, array<string, mixed>>> A promise that resolves to the query results.
234-
*/
235-
public function raw(string $sql, array $bindings = []): PromiseInterface
236-
{
237-
return AsyncMySQLi::query($sql, $bindings);
238-
}
239-
240-
/**
241-
* Execute a raw SQL query and return the first result.
242-
*
243-
* @param string $sql The raw SQL query.
244-
* @param array<mixed> $bindings The parameter bindings for the query.
245-
* @return PromiseInterface<array<string, mixed>|false> A promise that resolves to the first result or false.
246-
*/
247-
public function rawFirst(string $sql, array $bindings = []): PromiseInterface
248-
{
249-
return AsyncMySQLi::fetchOne($sql, $bindings);
250-
}
251-
252-
/**
253-
* Execute a raw SQL query and return a single value.
254-
*
255-
* @param string $sql The raw SQL query.
256-
* @param array<mixed> $bindings The parameter bindings for the query.
257-
* @return PromiseInterface<mixed> A promise that resolves to a single value.
258-
*/
259-
public function rawValue(string $sql, array $bindings = []): PromiseInterface
260-
{
261-
return AsyncMySQLi::fetchValue($sql, $bindings);
262-
}
263227
}

src/QueryBuilder/PDOQueryBuilder.php

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -231,40 +231,4 @@ public function delete(): PromiseInterface
231231

232232
return AsyncPDO::execute($sql, $this->getCompiledBindings());
233233
}
234-
235-
/**
236-
* Execute a raw SQL query.
237-
*
238-
* @param string $sql The raw SQL query.
239-
* @param array<mixed> $bindings The parameter bindings for the query.
240-
* @return PromiseInterface<array<int, array<string, mixed>>> A promise that resolves to the query results.
241-
*/
242-
public function raw(string $sql, array $bindings = []): PromiseInterface
243-
{
244-
return AsyncPDO::query($sql, $bindings);
245-
}
246-
247-
/**
248-
* Execute a raw SQL query and return the first result.
249-
*
250-
* @param string $sql The raw SQL query.
251-
* @param array<mixed> $bindings The parameter bindings for the query.
252-
* @return PromiseInterface<array<string, mixed>|false> A promise that resolves to the first result or false.
253-
*/
254-
public function rawFirst(string $sql, array $bindings = []): PromiseInterface
255-
{
256-
return AsyncPDO::fetchOne($sql, $bindings);
257-
}
258-
259-
/**
260-
* Execute a raw SQL query and return a single value.
261-
*
262-
* @param string $sql The raw SQL query.
263-
* @param array<mixed> $bindings The parameter bindings for the query.
264-
* @return PromiseInterface<mixed> A promise that resolves to a single value.
265-
*/
266-
public function rawValue(string $sql, array $bindings = []): PromiseInterface
267-
{
268-
return AsyncPDO::fetchValue($sql, $bindings);
269-
}
270234
}
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
<?php
2+
3+
namespace Rcalicdan\FiberAsync\QueryBuilder;
4+
5+
use Rcalicdan\FiberAsync\Api\Async;
6+
use Rcalicdan\FiberAsync\Api\AsyncPostgreSQL;
7+
use Rcalicdan\FiberAsync\Api\Promise;
8+
use Rcalicdan\FiberAsync\Promise\Interfaces\PromiseInterface;
9+
use Rcalicdan\FiberAsync\QueryBuilder\Traits\QueryBuilderCoreTrait;
10+
use Rcalicdan\FiberAsync\QueryBuilder\Traits\QueryConditionsTrait;
11+
use Rcalicdan\FiberAsync\QueryBuilder\Traits\QueryJoinTrait;
12+
use Rcalicdan\FiberAsync\QueryBuilder\Traits\QueryGroupingTrait;
13+
use Rcalicdan\FiberAsync\QueryBuilder\Traits\QueryAdvancedConditionsTrait;
14+
use Rcalicdan\FiberAsync\QueryBuilder\Traits\QueryDebugTrait;
15+
use Rcalicdan\FiberAsync\QueryBuilder\Traits\SqlBuilderTrait;
16+
17+
18+
class PostgresQueryBuilder
19+
{
20+
use QueryBuilderCoreTrait,
21+
QueryConditionsTrait,
22+
QueryJoinTrait,
23+
QueryGroupingTrait,
24+
QueryAdvancedConditionsTrait,
25+
SqlBuilderTrait,
26+
QueryDebugTrait;
27+
28+
/**
29+
* Create a new AsyncQueryBuilder instance.
30+
*
31+
* @param string $table The table name to query.
32+
*/
33+
final public function __construct(string $table = '')
34+
{
35+
if ($table !== '') {
36+
$this->table = $table;
37+
}
38+
}
39+
40+
/**
41+
* Execute the query and return all results.
42+
*
43+
* @return PromiseInterface<array<int, array<string, mixed>>> A promise that resolves to the query results.
44+
*/
45+
public function get(): PromiseInterface
46+
{
47+
$sql = $this->buildSelectQuery();
48+
49+
return AsyncPostgreSQL::query($sql, $this->getCompiledBindings());
50+
}
51+
52+
/**
53+
* Get the first result from the query.
54+
*
55+
* @return PromiseInterface<array<string, mixed>|false> A promise that resolves to the first result or false.
56+
*/
57+
public function first(): PromiseInterface
58+
{
59+
// A new instance with a limit is created for this specific query execution
60+
$instanceWithLimit = $this->limit(1);
61+
$sql = $instanceWithLimit->buildSelectQuery();
62+
63+
return AsyncPostgreSQL::fetchOne($sql, $instanceWithLimit->getCompiledBindings());
64+
}
65+
66+
/**
67+
* Find a record by ID.
68+
*
69+
* @param mixed $id The ID value to search for.
70+
* @param string $column The column name to search in.
71+
* @return PromiseInterface<array<string, mixed>|false> A promise that resolves to the found record or false.
72+
*/
73+
public function find(mixed $id, string $column = 'id'): PromiseInterface
74+
{
75+
return $this->where($column, $id)->first();
76+
}
77+
78+
/**
79+
* Find a record by ID or throw an exception if not found.
80+
*
81+
* @param mixed $id The ID value to search for.
82+
* @param string $column The column name to search in.
83+
* @return PromiseInterface<array<string, mixed>> A promise that resolves to the found record.
84+
*
85+
* @throws \RuntimeException When no record is found.
86+
*/
87+
public function findOrFail(mixed $id, string $column = 'id'): PromiseInterface
88+
{
89+
// @phpstan-ignore-next-line
90+
return Async::async(function () use ($id, $column): array {
91+
$result = await($this->find($id, $column));
92+
if ($result === null || $result === false) {
93+
$idString = is_scalar($id) ? (string) $id : 'complex_type';
94+
95+
throw new \RuntimeException("Record not found with {$column} = {$idString}");
96+
}
97+
98+
return $result;
99+
})();
100+
}
101+
102+
/**
103+
* Get a single value from the first result.
104+
*
105+
* @param string $column The column name to retrieve.
106+
* @return PromiseInterface<mixed> A promise that resolves to the column value or null.
107+
*/
108+
public function value(string $column): PromiseInterface
109+
{
110+
// @phpstan-ignore-next-line
111+
return Async::async(function () use ($column): mixed {
112+
$result = await($this->select($column)->first());
113+
114+
return ($result !== false && isset($result[$column])) ? $result[$column] : null;
115+
})();
116+
}
117+
118+
/**
119+
* Count the number of records.
120+
*
121+
* @param string $column The column to count.
122+
* @return PromiseInterface<int> A promise that resolves to the record count.
123+
*/
124+
public function count(string $column = '*'): PromiseInterface
125+
{
126+
$sql = $this->buildCountQuery($column);
127+
/** @var PromiseInterface<int> */
128+
$promise = AsyncPostgreSQL::fetchValue($sql, $this->getCompiledBindings());
129+
130+
return $promise;
131+
}
132+
133+
/**
134+
* Check if any records exist.
135+
*
136+
* @return PromiseInterface<bool> A promise that resolves to true if records exist, false otherwise.
137+
*/
138+
public function exists(): PromiseInterface
139+
{
140+
// @phpstan-ignore-next-line
141+
return Async::async(function (): bool {
142+
$count = await($this->count());
143+
144+
return $count > 0;
145+
})();
146+
}
147+
148+
/**
149+
* Insert a single record.
150+
*
151+
* @param array<string, mixed> $data The data to insert as column => value pairs.
152+
* @return PromiseInterface<int> A promise that resolves to the number of affected rows.
153+
*/
154+
public function insert(array $data): PromiseInterface
155+
{
156+
if ($data === []) {
157+
return Promise::resolve(0);
158+
}
159+
$sql = $this->buildInsertQuery($data);
160+
161+
return AsyncPostgreSQL::execute($sql, array_values($data));
162+
}
163+
164+
/**
165+
* Insert multiple records in a batch operation.
166+
*
167+
* @param array<array<string, mixed>> $data An array of records to insert.
168+
* @return PromiseInterface<int> A promise that resolves to the number of affected rows.
169+
*/
170+
public function insertBatch(array $data): PromiseInterface
171+
{
172+
if ($data === []) {
173+
return Promise::resolve(0);
174+
}
175+
176+
$sql = $this->buildInsertBatchQuery($data);
177+
$bindings = [];
178+
foreach ($data as $row) {
179+
if (is_array($row)) {
180+
$bindings = array_merge($bindings, array_values($row));
181+
}
182+
}
183+
184+
return AsyncPostgreSQL::execute($sql, $bindings);
185+
}
186+
187+
/**
188+
* Create a new record (alias for insert).
189+
*
190+
* @param array<string, mixed> $data The data to insert as column => value pairs.
191+
* @return PromiseInterface<int> A promise that resolves to the number of affected rows.
192+
*/
193+
public function create(array $data): PromiseInterface
194+
{
195+
return $this->insert($data);
196+
}
197+
198+
/**
199+
* Update records matching the query conditions.
200+
*
201+
* @param array<string, mixed> $data The data to update as column => value pairs.
202+
* @return PromiseInterface<int> A promise that resolves to the number of affected rows.
203+
*/
204+
public function update(array $data): PromiseInterface
205+
{
206+
if ($data === []) {
207+
return Promise::resolve(0);
208+
}
209+
$sql = $this->buildUpdateQuery($data);
210+
$whereBindings = $this->getCompiledBindings();
211+
$bindings = array_merge(array_values($data), $whereBindings);
212+
213+
return AsyncPostgreSQL::execute($sql, $bindings);
214+
}
215+
216+
/**
217+
* Delete records matching the query conditions.
218+
*
219+
* @return PromiseInterface<int> A promise that resolves to the number of affected rows.
220+
*/
221+
public function delete(): PromiseInterface
222+
{
223+
$sql = $this->buildDeleteQuery();
224+
225+
return AsyncPostgreSQL::execute($sql, $this->getCompiledBindings());
226+
}
227+
}

test.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
use Rcalicdan\FiberAsync\Benchmark\BenchmarkRunner;
4+
use Rcalicdan\FiberAsync\MySQLi\DB;
5+
use Rcalicdan\FiberAsync\Promise\Promise;
6+
7+
require 'vendor/autoload.php';
8+
9+
for ($i = 1; $i <= 5; $i++) {
10+
echo "Round $i\n";
11+
$start = microtime(true);
12+
$task = run(function () {
13+
$queries = Promise::all([
14+
DB::rawExecute("SELECT SLEEP(1)"),
15+
DB::rawExecute("SELECT SLEEP(2)"),
16+
DB::rawExecute("SELECT SLEEP(3)"),
17+
]);
18+
19+
await($queries);
20+
});
21+
$endTime = microtime(true);
22+
$executionTime = $endTime - $start;
23+
echo "Execution time: " . $executionTime . " seconds\n";
24+
}

0 commit comments

Comments
 (0)