diff options
Diffstat (limited to 'includes/database')
-rw-r--r-- | includes/database/database.inc | 51 | ||||
-rw-r--r-- | includes/database/mysql/database.inc | 52 | ||||
-rw-r--r-- | includes/database/select.inc | 2 | ||||
-rw-r--r-- | includes/database/sqlite/database.inc | 14 |
4 files changed, 92 insertions, 27 deletions
diff --git a/includes/database/database.inc b/includes/database/database.inc index 4cc1a33d7..e08f9074f 100644 --- a/includes/database/database.inc +++ b/includes/database/database.inc @@ -273,20 +273,25 @@ abstract class DatabaseConnection extends PDO { protected $schema = NULL; /** - * The default prefix used by this database connection. + * The prefixes used by this database connection. * - * Separated from the other prefixes for performance reasons. + * @var array + */ + protected $prefixes = array(); + + /** + * List of search values for use in prefixTables(). * - * @var string + * @var array */ - protected $defaultPrefix = ''; + protected $prefixSearch = array(); /** - * The non-default prefixes used by this database connection. + * List of replacement values for use in prefixTables(). * * @var array */ - protected $prefixes = array(); + protected $prefixReplace = array(); function __construct($dsn, $username, $password, $driver_options = array()) { // Initialize and prepare the connection prefix. @@ -375,7 +380,7 @@ abstract class DatabaseConnection extends PDO { } /** - * Preprocess the prefixes used by this database connection. + * Set the list of prefixes used by this database connection. * * @param $prefix * The prefixes, in any of the multiple forms documented in @@ -383,14 +388,27 @@ abstract class DatabaseConnection extends PDO { */ protected function setPrefix($prefix) { if (is_array($prefix)) { - $this->defaultPrefix = isset($prefix['default']) ? $prefix['default'] : ''; - unset($prefix['default']); - $this->prefixes = $prefix; + $this->prefixes = $prefix + array('default' => ''); } else { - $this->defaultPrefix = $prefix; - $this->prefixes = array(); + $this->prefixes = array('default' => $prefix); } + + // Set up variables for use in prefixTables(). Replace table-specific + // prefixes first. + $this->prefixSearch = array(); + $this->prefixReplace = array(); + foreach ($this->prefixes as $key => $val) { + if ($key != 'default') { + $this->prefixSearch[] = '{' . $key . '}'; + $this->prefixReplace[] = $val . $key; + } + } + // Then replace remaining tables with the default prefix. + $this->prefixSearch[] = '{'; + $this->prefixReplace[] = $this->prefixes['default']; + $this->prefixSearch[] = '}'; + $this->prefixReplace[] = ''; } /** @@ -408,12 +426,7 @@ abstract class DatabaseConnection extends PDO { * The properly-prefixed string. */ public function prefixTables($sql) { - // Replace specific table prefixes first. - foreach ($this->prefixes as $key => $val) { - $sql = strtr($sql, array('{' . $key . '}' => $val . $key)); - } - // Then replace remaining tables with the default prefix. - return strtr($sql, array('{' => $this->defaultPrefix , '}' => '')); + return str_replace($this->prefixSearch, $this->prefixReplace, $sql); } /** @@ -427,7 +440,7 @@ abstract class DatabaseConnection extends PDO { return $this->prefixes[$table]; } else { - return $this->defaultPrefix; + return $this->prefixes['default']; } } diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc index 262cc6051..bc31feaaf 100644 --- a/includes/database/mysql/database.inc +++ b/includes/database/mysql/database.inc @@ -133,6 +133,58 @@ class DatabaseConnection_mysql extends DatabaseConnection { catch (PDOException $e) { } } + + /** + * Overridden to work around issues to MySQL not supporting transactional DDL. + */ + public function 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. + if (empty($this->transactionLayers)) { + if (!PDO::commit()) { + throw new DatabaseTransactionCommitFailedException(); + } + } + else { + // Attempt to release this savepoint in the standard way. + try { + $this->query('RELEASE SAVEPOINT ' . $name); + } + catch (PDOException $e) { + // However, in MySQL (InnoDB), savepoints are automatically committed + // when tables are altered or created (DDL transactions are not + // supported). This can cause exceptions due to trying to release + // savepoints which no longer exist. + // + // To avoid exceptions when no actual error has occurred, we silently + // succeed for PDOExceptions with SQLSTATE 42000 ("Syntax error or + // access rule violation") and MySQL error code 1305 ("SAVEPOINT does + // not exist"). + if ($e->getCode() == '42000' && $e->errorInfo[1] == '1305') { + // If one SAVEPOINT was released automatically, then all were. + // Therefore, we keep just the topmost transaction. + $this->transactionLayers = array('drupal_transaction'); + } + else { + throw $e; + } + } + break; + } + } + } } diff --git a/includes/database/select.inc b/includes/database/select.inc index 716c2fc3d..53be20adc 100644 --- a/includes/database/select.inc +++ b/includes/database/select.inc @@ -414,7 +414,7 @@ interface SelectQueryInterface extends QueryConditionInterface, QueryAlterableIn * @param $start * The first record from the result set to return. If NULL, removes any * range directives that are set. - * @param $limit + * @param $length * The number of records to return from the result set. * @return SelectQueryInterface * The called object. diff --git a/includes/database/sqlite/database.inc b/includes/database/sqlite/database.inc index cf3b9551f..0fc0b5528 100644 --- a/includes/database/sqlite/database.inc +++ b/includes/database/sqlite/database.inc @@ -71,12 +71,8 @@ class DatabaseConnection_sqlite extends DatabaseConnection { )); // Attach one database for each registered prefix. - $prefixes = &$this->prefixes; - if (!empty($this->defaultPrefix)) { - // Add in the default prefix, which is also attached. - $prefixes[] = &$this->defaultPrefix; - } - foreach ($this->prefixes as $table => &$prefix) { + $prefixes = $this->prefixes; + foreach ($prefixes as $table => &$prefix) { // Empty prefix means query the main database -- no need to attach anything. if (!empty($prefix)) { // Only attach the database once. @@ -90,6 +86,8 @@ class DatabaseConnection_sqlite extends DatabaseConnection { $prefix .= '.'; } } + // Regenerate the prefixes replacement table. + $this->setPrefix($prefixes); // Detect support for SAVEPOINT. $version = $this->query('SELECT sqlite_version()')->fetchField(); @@ -240,7 +238,9 @@ class DatabaseConnection_sqlite extends DatabaseConnection { // Generate a new temporary table name and protect it from prefixing. // SQLite requires that temporary tables to be non-qualified. $tablename = $this->generateTemporaryTableName(); - $this->prefixes[$tablename] = ''; + $prefixes = $this->prefixes; + $prefixes[$tablename] = ''; + $this->setPrefix($prefixes); $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE ' . $tablename . ' AS SELECT', $query), $args, $options); return $tablename; |