Skip to content

Commit 25c4771

Browse files
committed
- Allow true nested transactions via SAVEPOINTs
1 parent 50c7428 commit 25c4771

File tree

2 files changed

+110
-16
lines changed

2 files changed

+110
-16
lines changed

README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,57 @@ $mysql->delete()
126126
->where('t1.field1=? AND t2.field2 > ?', 1, 10)
127127
->run();
128128
```
129+
130+
### True nested transactions
131+
132+
```php
133+
$mysql = new \Kir\MySQL\Databases\MySQL($pdo);
134+
135+
$mysql->delete()->from('test')->run();
136+
137+
$test = function () use ($mysql) {
138+
$name = $mysql->select()
139+
->field('t.name')
140+
->from('t', 'test')
141+
->where('t.id=?', 1)
142+
->fetchValue();
143+
printf("Current name is %s\n", $name);
144+
};
145+
146+
$mysql->insert()
147+
->into('test')
148+
->add('id', 1)
149+
->add('name', 'Peter')
150+
->run();
151+
152+
$test();
153+
154+
$mysql->transaction(function () use ($mysql, $test) {
155+
$mysql->update()
156+
->table('test')
157+
->set('name', 'Paul')
158+
->where('id=?', 1)
159+
->run();
160+
161+
$test();
162+
163+
$mysql->dryRun(function () use ($mysql, $test) {
164+
$mysql->update()
165+
->table('test')
166+
->set('name', 'Bert')
167+
->where('id=?', 1)
168+
->run();
169+
170+
$test();
171+
});
172+
});
173+
174+
$test();
175+
```
176+
177+
```
178+
Current name is Peter
179+
Current name is Paul
180+
Current name is Bert
181+
Current name is Paul
182+
```

src/Databases/MySQL.php

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -265,18 +265,29 @@ public function transactionRollback() {
265265
public function dryRun($callback = null) {
266266
$result = null;
267267
$exception = null;
268-
if($this->pdo->inTransaction()) {
269-
throw new \Exception('Connection is already in a transaction. Dry-run not possible.');
270-
}
271-
$this->transactionStart();
272-
try {
273-
$result = call_user_func($callback, $this);
274-
} catch (\Exception $e) {
275-
$exception = $e;
276-
}
277-
$this->transactionRollback();
278-
if($exception !== null) {
279-
throw $exception;
268+
if(!$this->pdo->inTransaction()) {
269+
$this->transactionStart();
270+
try {
271+
$result = call_user_func($callback, $this);
272+
} catch (\Exception $e) {
273+
$exception = $e;
274+
}
275+
$this->transactionRollback();
276+
if($exception !== null) {
277+
throw $exception;
278+
}
279+
} else {
280+
$uniqueId = $this->genUniqueId();
281+
$this->exec("SAVEPOINT {$uniqueId}");
282+
try {
283+
$result = call_user_func($callback, $this);
284+
} catch (\Exception $e) {
285+
$exception = $e;
286+
}
287+
$this->exec("ROLLBACK TO {$uniqueId}");
288+
if($exception !== null) {
289+
throw $exception;
290+
}
280291
}
281292
return $result;
282293
}
@@ -296,14 +307,26 @@ public function transaction($tries = 1, $callback = null) {
296307
throw new \Exception('$callback must be a callable');
297308
}
298309
$e = null;
299-
for(; $tries--;) {
310+
if(!$this->pdo->inTransaction()) {
311+
for(; $tries--;) {
312+
try {
313+
$this->transactionStart();
314+
$result = call_user_func($callback, $this);
315+
$this->transactionCommit();
316+
return $result;
317+
} catch (\Exception $e) {
318+
$this->transactionRollback();
319+
}
320+
}
321+
} else {
322+
$uniqueId = $this->genUniqueId();
300323
try {
301-
$this->transactionStart();
324+
$this->exec("SAVEPOINT {$uniqueId}");
302325
$result = call_user_func($callback, $this);
303-
$this->transactionCommit();
326+
$this->exec("RELEASE SAVEPOINT {$uniqueId}");
304327
return $result;
305328
} catch (\Exception $e) {
306-
$this->transactionRollback();
329+
$this->exec("ROLLBACK TO {$uniqueId}");
307330
}
308331
}
309332
throw $e;
@@ -355,4 +378,21 @@ private function exceptionHandler($fn) {
355378
$this->exceptionInterpreter->throwMoreConcreteException($e);
356379
}
357380
}
381+
382+
/**
383+
* @return string
384+
*/
385+
private function genUniqueId() {
386+
// Generate a unique id from a former random-uuid-generator
387+
return sprintf('ID%04x%04x%04x%04x%04x%04x%04x%04x',
388+
mt_rand(0, 0xffff),
389+
mt_rand(0, 0xffff),
390+
mt_rand(0, 0xffff),
391+
mt_rand(0, 0x0fff) | 0x4000,
392+
mt_rand(0, 0x3fff) | 0x8000,
393+
mt_rand(0, 0xffff),
394+
mt_rand(0, 0xffff),
395+
mt_rand(0, 0xffff)
396+
);
397+
}
358398
}

0 commit comments

Comments
 (0)