diff --git a/code/SQLite3Database.php b/code/SQLite3Database.php index a85a6a3..07942ca 100644 --- a/code/SQLite3Database.php +++ b/code/SQLite3Database.php @@ -65,6 +65,11 @@ class SQLite3Database extends Database */ protected $transactionNesting = 0; + /** + * @var array + */ + protected $transactionSavepoints = []; + /** * List of default pragma values * @@ -471,28 +476,27 @@ class SQLite3Database extends Database public function transactionStart($transaction_mode = false, $session_characteristics = false) { if ($this->transactionDepth()) { - $this->transactionSavepoint($this->getTransactionSavepointName()); + $this->transactionSavepoint('NESTEDTRANSACTION' . $this->transactionDepth()); } else { $this->query('BEGIN'); + $this->transactionDepthIncrease(); } - $this->transactionDepthIncrease(); } public function transactionSavepoint($savepoint) { $this->query("SAVEPOINT \"$savepoint\""); + $this->transactionDepthIncrease($savepoint); } /** - * Fetch the name of the current savepoint - * {@see transactionDepth} should be greater than zero - * or the name will be invalid (because there are none). + * Fetch the name of the most recent savepoint * * @return string */ protected function getTransactionSavepointName() { - return 'NESTEDTRANSACTION' . $this->transactionDepth(); + return end($this->transactionSavepoints); } public function transactionRollback($savepoint = false) @@ -500,6 +504,7 @@ class SQLite3Database extends Database // Named transaction if ($savepoint) { $this->query("ROLLBACK TO $savepoint;"); + $this->transactionDepthDecrease(); return true; } @@ -508,11 +513,11 @@ class SQLite3Database extends Database return false; } - $this->transactionDepthDecrease(); - if ($this->transactionDepth()) { + if ($this->transactionIsNested()) { $this->transactionRollback($this->getTransactionSavepointName()); } else { $this->query('ROLLBACK;'); + $this->transactionDepthDecrease(); } return true; } @@ -528,24 +533,60 @@ class SQLite3Database extends Database if (!$this->transactionDepth()) { return false; } - $this->query('COMMIT;'); - $this->transactionDepthDecrease(); + + if ($this->transactionIsNested()) { + $savepoint = $this->getTransactionSavepointName(); + $this->query('RELEASE ' . $savepoint); + $this->transactionDepthDecrease(); + } else { + $this->query('COMMIT;'); + $this->resetTransactionNesting(); + } + + if ($chain) { + $this->transactionStart(); + } + return true; } /** - * Increase the nested transaction level by one + * Indicate whether or not the current transaction is nested + * Returns false if there are no transactions, or the open + * transaction is the 'outer' transaction, i.e. not nested. + * + * @return bool */ - protected function transactionDepthIncrease() + protected function transactionIsNested() + { + return $this->transactionNesting > 1; + } + + /** + * Increase the nested transaction level by one + * savepoint tracking is optional because BEGIN + * opens a transaction, but is not a named reference + * + * @param string $savepoint + */ + protected function transactionDepthIncrease($savepoint = null) { ++$this->transactionNesting; + if ($savepoint) { + array_push($this->transactionSavepoints, $savepoint); + } } /** * Decrease the nested transaction level by one + * and reduce the savepoint tracking if we are + * nesting, as the last one is no longer valid */ protected function transactionDepthDecrease() { + if ($this->transactionIsNested()) { + array_pop($this->transactionSavepoints); + } --$this->transactionNesting; } @@ -555,6 +596,7 @@ class SQLite3Database extends Database protected function resetTransactionNesting() { $this->transactionNesting = 0; + $this->transactionSavepoints = []; } public function query($sql, $errorLevel = E_USER_ERROR)