@@ -238,11 +238,13 @@ public function quote(string $string): string
238238 */
239239 public function beginTransaction (): void
240240 {
241- if ($ this ->transactionDepth !== 0 ) {
242- throw new \LogicException (__METHOD__ . '() call is forbidden inside a transaction() callback ' );
241+ if ($ this ->transactionDepth === 0 ) {
242+ $ this ->logOperation ($ this ->getConnection ()->beginTransaction (...), new SqlLiteral ('BEGIN TRANSACTION ' ));
243+ } else {
244+ $ this ->createSavepoint ($ this ->transactionDepth - 1 );
243245 }
244246
245- $ this ->logOperation ( $ this -> getConnection ()-> beginTransaction (...), new SqlLiteral ( ' BEGIN TRANSACTION ' )) ;
247+ $ this ->transactionDepth ++ ;
246248 }
247249
248250
@@ -252,11 +254,15 @@ public function beginTransaction(): void
252254 */
253255 public function commit (): void
254256 {
255- if ($ this ->transactionDepth !== 0 ) {
256- throw new \LogicException (__METHOD__ . '() call is forbidden inside a transaction() callback ' );
257- }
257+ if ($ this ->transactionDepth === 0 ) {
258+ throw new \LogicException ('No active transaction to commit. ' );
259+
260+ } elseif (--$ this ->transactionDepth === 0 ) {
261+ $ this ->logOperation ($ this ->getConnection ()->commit (...), new SqlLiteral ('COMMIT ' ));
258262
259- $ this ->logOperation ($ this ->getConnection ()->commit (...), new SqlLiteral ('COMMIT ' ));
263+ } else {
264+ $ this ->releaseSavepoint ($ this ->transactionDepth );
265+ }
260266 }
261267
262268
@@ -266,11 +272,15 @@ public function commit(): void
266272 */
267273 public function rollBack (): void
268274 {
269- if ($ this ->transactionDepth !== 0 ) {
270- throw new \LogicException (__METHOD__ . '() call is forbidden inside a transaction() callback ' );
271- }
275+ if ($ this ->transactionDepth === 0 ) {
276+ throw new \LogicException ('No active transaction to roll back. ' );
272277
273- $ this ->logOperation ($ this ->getConnection ()->rollBack (...), new SqlLiteral ('ROLLBACK ' ));
278+ } elseif (--$ this ->transactionDepth === 0 ) {
279+ $ this ->logOperation ($ this ->getConnection ()->rollBack (...), new SqlLiteral ('ROLLBACK ' ));
280+
281+ } else {
282+ $ this ->rollbackToSavepoint ($ this ->transactionDepth );
283+ }
274284 }
275285
276286
@@ -279,28 +289,27 @@ public function rollBack(): void
279289 */
280290 public function transaction (callable $ callback ): mixed
281291 {
282- if ($ this ->transactionDepth === 0 ) {
283- $ this ->beginTransaction ();
284- }
285-
286- $ this ->transactionDepth ++;
292+ $ this ->beginTransaction ();
287293 try {
288294 $ res = $ callback ($ this );
295+ $ this ->commit ();
296+ return $ res ;
289297 } catch (\Throwable $ e ) {
290- $ this ->transactionDepth --;
291- if ($ this ->transactionDepth === 0 ) {
292- $ this ->rollback ();
293- }
294-
298+ $ this ->rollBack ();
295299 throw $ e ;
296300 }
301+ }
297302
298- $ this ->transactionDepth --;
299- if ($ this ->transactionDepth === 0 ) {
300- $ this ->commit ();
301- }
302303
303- return $ res ;
304+ public function createSavepoint (int $ level ): void
305+ {
306+ $ this ->query ('SAVEPOINT LEVEL ' . $ level ); // TODO: to driver, logOperation
307+ }
308+
309+
310+ public function releaseSavepoint (int $ level ): void
311+ {
312+ $ this ->query ('RELEASE SAVEPOINT LEVEL ' . $ level );
304313 }
305314
306315
0 commit comments