diff options
author | Dries Buytaert <dries@buytaert.net> | 2010-03-28 06:54:13 +0000 |
---|---|---|
committer | Dries Buytaert <dries@buytaert.net> | 2010-03-28 06:54:13 +0000 |
commit | 3520ea515364a528d06fc8ad477a103ff168a1f7 (patch) | |
tree | aa74f72aa1d0573e663660ca357cbd1227c1f888 /includes/database/sqlite/database.inc | |
parent | d864fc6363b03662cb904d6117b4e110f16d0158 (diff) | |
download | brdo-3520ea515364a528d06fc8ad477a103ff168a1f7.tar.gz brdo-3520ea515364a528d06fc8ad477a103ff168a1f7.tar.bz2 |
- Patch #754192 by andypost, Damien Tournoud: critical bug: fixed transaction support for old sqlite versions.
Diffstat (limited to 'includes/database/sqlite/database.inc')
-rw-r--r-- | includes/database/sqlite/database.inc | 131 |
1 files changed, 130 insertions, 1 deletions
diff --git a/includes/database/sqlite/database.inc b/includes/database/sqlite/database.inc index f3cb77a70..da3ca3525 100644 --- a/includes/database/sqlite/database.inc +++ b/includes/database/sqlite/database.inc @@ -18,6 +18,23 @@ include_once DRUPAL_ROOT . '/includes/database/prefetch.inc'; */ class DatabaseConnection_sqlite extends DatabaseConnection { + /** + * Whether this database connection supports savepoints. + * + * Version of sqlite lower then 3.6.8 can't use savepoints. + * See http://www.sqlite.org/releaselog/3_6_8.html + * + * @var bool + */ + protected $savepointSupport = FALSE; + + /** + * Whether or not the active transaction (if any) will be rolled back. + * + * @var boolean + */ + protected $willRollback; + public function __construct(array $connection_options = array()) { // We don't need a specific PDOStatement class here, we simulate it below. $this->statementClass = NULL; @@ -34,6 +51,10 @@ class DatabaseConnection_sqlite extends DatabaseConnection { $this->exec('PRAGMA encoding="UTF-8"'); + // Detect support for SAVEPOINT. + $version = $this->query('SELECT sqlite_version()')->fetchField(); + $this->savepointSupport = (version_compare($version, '3.6.8') >= 0); + // Create functions needed by SQLite. $this->sqliteCreateFunction('if', array($this, 'sqlFunctionIf')); $this->sqliteCreateFunction('greatest', array($this, 'sqlFunctionGreatest')); @@ -188,6 +209,114 @@ class DatabaseConnection_sqlite extends DatabaseConnection { // because it gets out of scope. return (int) $this->query('SELECT value FROM {sequences}')->fetchField(); } + + public function rollback($savepoint_name = 'drupal_transaction', $type = NULL, $message = NULL, $variables = array(), $severity = NULL, $link = NULL) { + if ($this->savepointSupport) { + return parent::rollBack($savepoint_name, $type, $message, $variables, $severity, $link); + } + + if (!$this->inTransaction()) { + throw new DatabaseTransactionNoActiveException(); + } + // A previous rollback to an earlier savepoint may mean that the savepoint + // in question has already been rolled back. + if (!in_array($savepoint_name, $this->transactionLayers)) { + return; + } + + // Set the severity to the configured default if not specified. + if (!isset($severity)) { + $logging = Database::getLoggingCallback(); + if (is_array($logging)) { + $severity = $logging['default_severity']; + } + } + + // Record in an array to send to the log after transaction rollback. + // Messages written directly to a log (with a database back-end) will roll + // back during the following transaction rollback. This is an array because + // rollback could be requested multiple times during a transaction, and all + // such errors ought to be logged. + if (isset($message)) { + $this->rollbackLogs[] = array( + 'type' => $type, + 'message' => $message, + 'variables' => $variables, + 'severity' => $severity, + 'link' => $link, + ); + } + + // We need to find the point we're rolling back to, all other savepoints + // before are no longer needed. + while ($savepoint = array_pop($this->transactionLayers)) { + if ($savepoint == $savepoint_name) { + // Mark whole stack of transactions as needed roll back. + $this->willRollback = TRUE; + // If it is the last the transaction in the stack, then it is not a + // savepoint, it is the transaction itself so we will need to roll back + // the transaction rather than a savepoint. + if (empty($this->transactionLayers)) { + break; + } + return; + } + } + if ($this->supportsTransactions()) { + PDO::rollBack(); + } + $this->logRollback(); + } + + public function pushTransaction($name) { + if ($this->savepointSupport) { + return parent::pushTransaction($name); + } + if (!$this->supportsTransactions()) { + return; + } + if (isset($this->transactionLayers[$name])) { + throw new DatabaseTransactionNameNonUniqueException($name . " is already in use."); + } + if (!$this->inTransaction()) { + PDO::beginTransaction(); + } + $this->transactionLayers[$name] = $name; + } + + public function popTransaction($name) { + if ($this->savepointSupport) { + return parent::popTransaction($name); + } + if (!$this->supportsTransactions()) { + return; + } + if (!$this->inTransaction()) { + throw new DatabaseTransactionNoActiveException(); + } + + // Commit everything since SAVEPOINT $name. + while($savepoint = array_pop($this->transactionLayers)) { + if ($savepoint != $name) continue; + + // If there are no more layers left then we should commit or rollback. + if (empty($this->transactionLayers)) { + // If there was any rollback() we should roll back whole transaction. + if ($this->willRollback) { + $this->willRollback = FALSE; + PDO::rollBack(); + $this->logRollback(); + } + elseif (!PDO::commit()) { + throw new DatabaseTransactionCommitFailedException(); + } + } + else { + break; + } + } + } + } /** @@ -243,7 +372,7 @@ class DatabaseStatement_sqlite extends DatabaseStatementPrefetch implements Iter // in the automatic cast. $value = sprintf('%F', $value); } - + // We will remove this placeholder from the query as PDO throws an // exception if the number of placeholders in the query and the // arguments does not match. |