Skip to content

Commit a09f0f7

Browse files
committed
feature: first version of postgresql client
1 parent 9c52f63 commit a09f0f7

File tree

196 files changed

+7329
-795
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

196 files changed

+7329
-795
lines changed
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\PostgreSql\Client;
6+
7+
use Flow\PostgreSql\Client\Exception\{QueryException, TransactionException};
8+
use Flow\PostgreSql\QueryBuilder\SqlQuery;
9+
10+
interface Client
11+
{
12+
/**
13+
* Begin a transaction.
14+
* Supports nesting via SAVEPOINTs.
15+
*
16+
* @throws TransactionException
17+
*/
18+
public function beginTransaction() : void;
19+
20+
/**
21+
* Close the connection.
22+
*/
23+
public function close() : void;
24+
25+
/**
26+
* Commit the current transaction.
27+
* If nested, releases the savepoint.
28+
*
29+
* @throws TransactionException
30+
*/
31+
public function commit() : void;
32+
33+
/**
34+
* Get a cursor for lazy iteration over large result sets.
35+
* Memory efficient - rows are fetched one at a time.
36+
* Use cursor->map() to map rows to objects.
37+
*
38+
* @param SqlQuery|string $sql SQL query or query builder with $1, $2, ... placeholders
39+
* @param array<int, mixed> $parameters Positional parameters
40+
*
41+
* @throws QueryException
42+
*/
43+
public function cursor(SqlQuery|string $sql, array $parameters = []) : Cursor;
44+
45+
/**
46+
* Execute a statement that modifies data (INSERT, UPDATE, DELETE).
47+
* Returns the number of affected rows.
48+
*
49+
* @param SqlQuery|string $sql SQL statement or query builder with $1, $2, ... placeholders
50+
* @param array<int, mixed> $parameters Positional parameters
51+
*
52+
* @throws QueryException
53+
*/
54+
public function execute(SqlQuery|string $sql, array $parameters = []) : int;
55+
56+
/**
57+
* Fetch the first row from query result.
58+
* Returns null if no rows found.
59+
*
60+
* @param SqlQuery|string $sql SQL query or query builder with $1, $2, ... placeholders
61+
* @param array<int, mixed> $parameters Positional parameters
62+
*
63+
* @throws QueryException
64+
*
65+
* @return null|array<string, mixed>
66+
*/
67+
public function fetch(SqlQuery|string $sql, array $parameters = []) : ?array;
68+
69+
/**
70+
* Fetch all rows from query result.
71+
*
72+
* @param SqlQuery|string $sql SQL query or query builder with $1, $2, ... placeholders
73+
* @param array<int, mixed> $parameters Positional parameters
74+
*
75+
* @throws QueryException
76+
*
77+
* @return array<int, array<string, mixed>>
78+
*/
79+
public function fetchAll(SqlQuery|string $sql, array $parameters = []) : array;
80+
81+
/**
82+
* Fetch all rows and map to objects.
83+
*
84+
* @template T of object
85+
*
86+
* @param class-string<T> $class Target class for mapping
87+
* @param SqlQuery|string $sql SQL query or query builder with $1, $2, ... placeholders
88+
* @param array<int, mixed> $parameters Positional parameters
89+
* @param null|RowMapper $mapper Override default mapper for this call
90+
*
91+
* @throws QueryException
92+
*
93+
* @return array<int, T>
94+
*/
95+
public function fetchAllInto(
96+
string $class,
97+
SqlQuery|string $sql,
98+
array $parameters = [],
99+
?RowMapper $mapper = null,
100+
) : array;
101+
102+
/**
103+
* Fetch the first row and map to object.
104+
* Returns null if no rows found.
105+
*
106+
* @template T of object
107+
*
108+
* @param class-string<T> $class Target class for mapping
109+
* @param SqlQuery|string $sql SQL query or query builder with $1, $2, ... placeholders
110+
* @param array<int, mixed> $parameters Positional parameters
111+
* @param null|RowMapper $mapper Override default mapper for this call
112+
*
113+
* @throws QueryException
114+
*
115+
* @return null|T
116+
*/
117+
public function fetchInto(
118+
string $class,
119+
SqlQuery|string $sql,
120+
array $parameters = [],
121+
?RowMapper $mapper = null,
122+
) : ?object;
123+
124+
/**
125+
* Fetch exactly one row. Throws if result has 0 or more than 1 row.
126+
* Use when you expect precisely one result (e.g., SELECT by primary key).
127+
*
128+
* @param SqlQuery|string $sql SQL query or query builder with $1, $2, ... placeholders
129+
* @param array<int, mixed> $parameters Positional parameters
130+
*
131+
* @throws QueryException When row count is not exactly 1
132+
*
133+
* @return array<string, mixed>
134+
*/
135+
public function fetchOne(SqlQuery|string $sql, array $parameters = []) : array;
136+
137+
/**
138+
* Fetch exactly one row and map to object.
139+
* Throws if result has 0 or more than 1 row.
140+
*
141+
* @template T of object
142+
*
143+
* @param class-string<T> $class Target class for mapping
144+
* @param SqlQuery|string $sql SQL query or query builder with $1, $2, ... placeholders
145+
* @param array<int, mixed> $parameters Positional parameters
146+
* @param null|RowMapper $mapper Override default mapper for this call
147+
*
148+
* @throws QueryException When row count is not exactly 1
149+
*
150+
* @return T
151+
*/
152+
public function fetchOneInto(
153+
string $class,
154+
SqlQuery|string $sql,
155+
array $parameters = [],
156+
?RowMapper $mapper = null,
157+
) : object;
158+
159+
/**
160+
* Fetch a single scalar value from the first column of first row.
161+
* Ideal for COUNT(*), MAX(), MIN(), etc.
162+
*
163+
* @param SqlQuery|string $sql SQL query or query builder with $1, $2, ... placeholders
164+
* @param array<int, mixed> $parameters Positional parameters
165+
*
166+
* @throws QueryException
167+
*/
168+
public function fetchScalar(SqlQuery|string $sql, array $parameters = []) : mixed;
169+
170+
/**
171+
* Get the current transaction nesting level.
172+
* 0 = no active transaction, 1 = top-level, 2+ = nested.
173+
*/
174+
public function getTransactionNestingLevel() : int;
175+
176+
/**
177+
* Check if auto-commit mode is enabled.
178+
* When enabled (default), each query is executed in its own transaction.
179+
* When disabled, a transaction is implicitly started and remains open until
180+
* commit() or rollBack() is called.
181+
*/
182+
public function isAutoCommit() : bool;
183+
184+
/**
185+
* Check if the connection is alive.
186+
*/
187+
public function isConnected() : bool;
188+
189+
/**
190+
* Get the last inserted ID from a sequence.
191+
* PostgreSQL requires a sequence name - there's no concept of "last insert ID" without it.
192+
*
193+
* Preferred alternative: Use RETURNING clause in INSERT statements:
194+
* $row = $client->fetch('INSERT INTO users (name) VALUES ($1) RETURNING id', ['John']);
195+
*
196+
* @param string $sequenceName The name of the sequence (e.g., 'users_id_seq')
197+
*
198+
* @throws QueryException
199+
*/
200+
public function lastInsertId(string $sequenceName) : int|string;
201+
202+
/**
203+
* Roll back the current transaction.
204+
* If nested, rolls back to the savepoint.
205+
*
206+
* @throws TransactionException
207+
*/
208+
public function rollBack() : void;
209+
210+
/**
211+
* Set auto-commit mode.
212+
* When enabled (default), each query is executed in its own transaction.
213+
* When disabled, a transaction is implicitly started and remains open until
214+
* commit() or rollBack() is called.
215+
*
216+
* @throws TransactionException
217+
*/
218+
public function setAutoCommit(bool $autoCommit) : void;
219+
220+
/**
221+
* Execute a callback within a transaction.
222+
* Auto-commits on success, auto-rollbacks on exception.
223+
* Supports nesting via SAVEPOINTs.
224+
*
225+
* @template T
226+
*
227+
* @param callable(Client): T $callback
228+
*
229+
* @throws TransactionException
230+
*
231+
* @return T
232+
*/
233+
public function transaction(callable $callback) : mixed;
234+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\PostgreSql\Client;
6+
7+
final readonly class ConnectionParameters
8+
{
9+
private function __construct(
10+
public string $connectionString,
11+
) {
12+
}
13+
14+
/**
15+
* Create from individual parameters.
16+
*
17+
* @param array<string, string> $options Additional connection options (e.g., sslmode, connect_timeout)
18+
*/
19+
public static function fromParams(
20+
string $database,
21+
string $host = 'localhost',
22+
int $port = 5432,
23+
?string $user = null,
24+
?string $password = null,
25+
array $options = [],
26+
) : self {
27+
$parts = [
28+
\sprintf('host=%s', $host),
29+
\sprintf('port=%d', $port),
30+
\sprintf('dbname=%s', $database),
31+
];
32+
33+
if ($user !== null) {
34+
$parts[] = \sprintf('user=%s', $user);
35+
}
36+
37+
if ($password !== null) {
38+
$parts[] = \sprintf('password=%s', $password);
39+
}
40+
41+
foreach ($options as $key => $value) {
42+
$parts[] = \sprintf('%s=%s', $key, $value);
43+
}
44+
45+
return new self(\implode(' ', $parts));
46+
}
47+
48+
/**
49+
* Create from a PostgreSQL connection string.
50+
*/
51+
public static function fromString(string $connectionString) : self
52+
{
53+
return new self($connectionString);
54+
}
55+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\PostgreSql\Client;
6+
7+
/**
8+
* Cursor for lazy iteration over large result sets.
9+
* Use Client::cursor() to obtain a cursor.
10+
*
11+
* Supports both raw array iteration and object mapping via RowMapper.
12+
*
13+
* @extends \IteratorAggregate<int, array<string, mixed>>
14+
*/
15+
interface Cursor extends \Countable, \IteratorAggregate
16+
{
17+
/**
18+
* Get the number of rows in the result set.
19+
*/
20+
public function count() : int;
21+
22+
/**
23+
* Free the cursor resources.
24+
* Called automatically when iteration completes.
25+
*/
26+
public function free() : void;
27+
28+
/**
29+
* Iterate all remaining rows lazily as arrays.
30+
* Memory efficient - one row at a time.
31+
*
32+
* @return \Generator<int, array<string, mixed>>
33+
*/
34+
public function iterate() : \Generator;
35+
36+
/**
37+
* Iterate all remaining rows, mapping each to an object.
38+
* Memory efficient - one object at a time.
39+
*
40+
* Uses the client's default mapper unless overridden.
41+
*
42+
* @template T of object
43+
*
44+
* @param class-string<T> $class Target class for mapping
45+
* @param null|RowMapper $mapper Override default mapper for this iteration
46+
*
47+
* @return \Generator<int, T>
48+
*/
49+
public function map(string $class, ?RowMapper $mapper = null) : \Generator;
50+
51+
/**
52+
* Fetch the next row. Returns null when exhausted.
53+
*
54+
* @return null|array<string, mixed>
55+
*/
56+
public function next() : ?array;
57+
}

0 commit comments

Comments
 (0)