44namespace tommyknocker \pdodb ;
55
66use Psr \Log \LoggerInterface ;
7+ use RuntimeException ;
8+ use Throwable ;
79use tommyknocker \pdodb \connection \ConnectionFactory ;
810use tommyknocker \pdodb \connection \ConnectionInterface ;
11+ use tommyknocker \pdodb \dialects \DialectInterface ;
912use tommyknocker \pdodb \helpers \RawValue ;
1013use tommyknocker \pdodb \query \QueryBuilder ;
11- use RuntimeException ;
12- use PDOException ;
1314
1415class 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