Skip to content

Commit 4e2f1ba

Browse files
committed
feat: support connection pooling without default connection
1 parent e701e43 commit 4e2f1ba

1 file changed

Lines changed: 172 additions & 39 deletions

File tree

src/PdoDb.php

Lines changed: 172 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,38 @@
44
namespace tommyknocker\pdodb;
55

66
use Psr\Log\LoggerInterface;
7+
use RuntimeException;
8+
use Throwable;
79
use tommyknocker\pdodb\connection\ConnectionFactory;
810
use tommyknocker\pdodb\connection\ConnectionInterface;
11+
use tommyknocker\pdodb\dialects\DialectInterface;
912
use tommyknocker\pdodb\helpers\RawValue;
1013
use tommyknocker\pdodb\query\QueryBuilder;
11-
use RuntimeException;
12-
use PDOException;
1314

1415
class PdoDb
1516
{
16-
public ?ConnectionInterface $connection {
17+
public DialectInterface $dialect;
18+
19+
public ?ConnectionInterface $connection = null {
1720
get {
18-
if (!$this->connection instanceof ConnectionInterface) {
19-
throw new RuntimeException('Connection not initialized');
21+
if ($this->connection === null) {
22+
throw new RuntimeException(
23+
'Connection not initialized. Use addConnection() to add a connection, then connection() to select it.'
24+
);
2025
}
2126
return $this->connection;
2227
}
2328
}
29+
2430
protected array $connections = [];
25-
protected ?string $connectionName;
31+
2632
public string $prefix;
2733
public string $lastQuery {
2834
get {
2935
return $this->connection->getLastQuery();
3036
}
3137
}
32-
public ?string $lastError {
38+
public string $lastError {
3339
get {
3440
return $this->connection->getLastError();
3541
}
@@ -46,30 +52,34 @@ class PdoDb
4652
}
4753
protected string $lockMethod = 'WRITE';
4854

55+
4956
/**
5057
* Initializes a new PdoDb object.
5158
*
52-
* @param string $driver The database driver to use. Defaults to 'mysql'.
59+
* @param string|null $driver The database driver to use. Pass null to use connection pooling without default connection.
5360
* @param array $config An array of configuration options for the database connection.
5461
* @param array $pdoOptions An array of PDO options to use to connect to the database.
5562
* @param LoggerInterface|null $logger The logger to use to log the queries.
5663
* @see /README.md for details
5764
*/
5865
public function __construct(
59-
string $driver = 'mysql',
66+
?string $driver = null,
6067
array $config = [],
6168
array $pdoOptions = [],
6269
?LoggerInterface $logger = null
6370
) {
6471
$this->prefix = $config['prefix'] ?? '';
6572

66-
$this->addConnection('default', [
67-
'driver' => $driver,
68-
...$config
69-
], $pdoOptions, $logger);
73+
// Only create default connection if driver is provided
74+
if ($driver !== null) {
75+
$this->addConnection('default', [
76+
'driver' => $driver,
77+
...$config
78+
], $pdoOptions, $logger);
7079

71-
// use default connection
72-
$this->connection('default');
80+
// use default connection
81+
$this->connection('default');
82+
}
7383
}
7484

7585
/**
@@ -87,35 +97,35 @@ public function find(): QueryBuilder
8797
/**
8898
* Execute a raw query.
8999
*
90-
* @param string|RawValue $query The raw query to be executed.
100+
* @param string $query The raw query to be executed.
91101
* @param array $params The parameters to be bound to the query.
92102
* @return array The result of the query.
93103
*/
94-
public function rawQuery(string|RawValue $query, array $params = []): array
104+
public function rawQuery(string $query, array $params = []): array
95105
{
96106
return $this->find()->fetchAll($query, $params);
97107
}
98108

99109
/**
100110
* Execute a raw query and return the first row.
101111
*
102-
* @param string|RawValue $query The raw query to be executed.
112+
* @param string $query The raw query to be executed.
103113
* @param array $params The parameters to be bound to the query.
104114
* @return array The first row of the result.
105115
*/
106-
public function rawQueryOne(string|RawValue $query, array $params = []): array
116+
public function rawQueryOne(string $query, array $params = []): array
107117
{
108118
return $this->find()->fetch($query, $params);
109119
}
110120

111121
/**
112122
* Execute a raw query and return the value of the first column of the first row.
113123
*
114-
* @param string|RawValue $query The raw query to be executed.
124+
* @param string $query The raw query to be executed.
115125
* @param array $params The parameters to be bound to the query.
116126
* @return mixed The value of the first column of the first row.
117127
*/
118-
public function rawQueryValue(string|RawValue $query, array $params = []): mixed
128+
public function rawQueryValue(string $query, array $params = []): mixed
119129
{
120130
return $this->find()->fetchColumn($query, $params);
121131
}
@@ -171,7 +181,7 @@ public function lock(string|array $tables): bool
171181
{
172182
$tables = (array)$tables;
173183
$sql = $this->connection->getDialect()->buildLockSql($tables, $this->prefix, $this->lockMethod);
174-
$this->connection->prepare($sql)->execute();
184+
$this->connection->execute($sql);
175185
return $this->executeState !== false;
176186
}
177187

@@ -186,7 +196,7 @@ public function unlock(): bool
186196
if ($sql === '') {
187197
return true;
188198
}
189-
$this->connection->prepare($sql)->execute();
199+
$this->connection->execute($sql);
190200
return $this->executeState !== false;
191201
}
192202

@@ -202,6 +212,104 @@ public function setLockMethod(string $method): self
202212
return $this;
203213
}
204214

215+
/* ---------------- HELPERS ---------------- */
216+
217+
/**
218+
* Returns an array with an increment operation.
219+
*
220+
* @param int|float $num The number to increment by.
221+
* @return array The array with the increment operation.
222+
*/
223+
public function inc(int|float $num = 1): array
224+
{
225+
return ['__op' => 'inc', 'val' => $num];
226+
}
227+
228+
/**
229+
* Returns an array with a decrement operation.
230+
*
231+
* @param int|float $num The number to decrement by.
232+
* @return array The array with the decrement operation.
233+
*/
234+
public function dec(int|float $num = 1): array
235+
{
236+
return ['__op' => 'dec', 'val' => $num];
237+
}
238+
239+
/**
240+
* Returns an array with a not operation.
241+
*
242+
* @param mixed $val The value to negate.
243+
* @return array The array with the not operation.
244+
*/
245+
public function not(mixed $val): array
246+
{
247+
return ['__op' => 'not', 'val' => $val];
248+
}
249+
250+
/**
251+
* Escapes a string for use in a SQL query.
252+
*
253+
* @param string $str The string to escape.
254+
* @return string The escaped string.
255+
*/
256+
public function escape(string $str): string
257+
{
258+
return $this->connection->quote($str);
259+
}
260+
261+
/**
262+
* Disconnects from the database.
263+
*
264+
* @return void
265+
*/
266+
public function disconnect(): void
267+
{
268+
$this->connection = null;
269+
}
270+
271+
/**
272+
* Pings the database.
273+
*
274+
* @return bool True if the ping was successful, false otherwise.
275+
*/
276+
public function ping(): bool
277+
{
278+
try {
279+
$this->connection->query('SELECT 1')->execute();
280+
return true;
281+
} catch (Throwable) {
282+
return false;
283+
}
284+
}
285+
286+
/**
287+
* Checks if a table exists.
288+
*
289+
* @param string $table The table to check.
290+
* @return bool True if the table exists, false otherwise.
291+
*/
292+
public function tableExists(string $table): bool
293+
{
294+
$sql = $this->connection->getDialect()->buildExistsSql($this->prefix . $table);
295+
$res = $this->rawQueryValue($sql);
296+
return !empty($res);
297+
}
298+
299+
300+
/* ---------------- UTILS ---------------- */
301+
302+
303+
/**
304+
* Returns a string with the current date and time.
305+
*
306+
* @param string $diff The time interval to add to the current date and time.
307+
* @return RawValue The current date and time.
308+
*/
309+
public function now(string $diff = ''): RawValue
310+
{
311+
return $this->connection->getDialect()->now($diff);
312+
}
205313

206314
/* ---------------- CONNECTIONS ---------------- */
207315

@@ -223,7 +331,6 @@ public function addConnection(
223331
$params['options'] = $pdoOptions;
224332
$connectionFactory = new ConnectionFactory();
225333
$connection = $connectionFactory->create($params, $logger);
226-
$this->connectionName = $name;
227334
$this->connections[$name] = $connection;
228335
}
229336

@@ -242,35 +349,61 @@ public function connection(string $name): self
242349
return $this;
243350
}
244351

352+
353+
/* ---------------- LOAD DATA/XML ---------------- */
354+
245355
/**
246-
* Disconnects from the database.
356+
* Loads data from a CSV file into a table.
247357
*
248-
* @return void
358+
* @param string $table The table to load data into.
359+
* @param string $filePath The path to the CSV file.
360+
* @param array $options The options to use to load the data.
361+
* @return bool True on success, false on failure.
249362
*/
250-
public function disconnect(): void
363+
public function loadData(string $table, string $filePath, array $options = []): bool
251364
{
252-
$this->connection = null;
253-
unset($this->connections[$this->connectionName]);
254-
$this->connectionName = null;
365+
$this->startTransaction();
366+
try {
367+
$sql = $this->connection->getDialect()->buildLoadDataSql($this->connection->getPdo(),
368+
$this->prefix . $table, $filePath, $options);
369+
$this->connection->execute($sql);
370+
$this->commit();
371+
return $this->executeState !== false;
372+
} catch (Throwable $e) {
373+
$this->rollback();
374+
}
375+
return false;
255376
}
256377

378+
257379
/**
258-
* Pings the database.
380+
* Loads data from an XML file into a table.
259381
*
260-
* @return bool True if the ping was successful, false otherwise.
382+
* @param string $table The table to load data into.
383+
* @param string $filePath The path to the XML file.
384+
* @param string $rowTag The tag that identifies a row.
385+
* @param int|null $linesToIgnore The number of lines to ignore at the beginning of the file.
386+
* @return bool True on success, false on failure.
261387
*/
262-
public function ping(): bool
388+
public function loadXml(string $table, string $filePath, string $rowTag = '<row>', ?int $linesToIgnore = null): bool
263389
{
390+
$this->startTransaction();
264391
try {
265-
$this->connection->query('SELECT 1')->execute();
266-
return true;
267-
} catch (PDOException|RuntimeException) {
268-
return false;
392+
$options = [
393+
'rowTag' => $rowTag,
394+
'linesToIgnore' => $linesToIgnore
395+
];
396+
$sql = $this->connection->getDialect()->buildLoadXML($this->connection->getPdo(), $this->prefix . $table,
397+
$filePath, $options);
398+
$this->connection->execute($sql);
399+
$this->commit();
400+
return $this->executeState !== false;
401+
} catch (Throwable $e) {
402+
$this->rollback();
269403
}
404+
return false;
270405
}
271406

272-
/* ---------------- SQL Introspection Methods ---------------- */
273-
274407
/**
275408
* Describes a table.
276409
*

0 commit comments

Comments
 (0)