diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php index 3465403..a71f4c7 100644 --- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php +++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php @@ -210,7 +210,12 @@ public function deleteMultiple(array $cids) { while (count($cids)); } catch (\Exception $e) { - $this->catchException($e); + // Create the cache table, which will be empty. This fixes cases during + // core install where a cache table is cleared before it is set + // with {cache_block} and {cache_menu}. + if (!$this->ensureBinExists()) { + $this->catchException($e); + } } } @@ -242,7 +247,12 @@ public function deleteAll() { $this->connection->truncate($this->bin)->execute(); } catch (\Exception $e) { - $this->catchException($e); + // Create the cache table, which will be empty. This fixes cases during + // core install where a cache table is cleared before it is set + // with {cache_block} and {cache_menu}. + if (!$this->ensureBinExists()) { + $this->catchException($e); + } } } diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php index 9b9cf97..0176e10 100644 --- a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php @@ -31,6 +31,13 @@ class Connection extends DatabaseConnection { const DATABASE_NOT_FOUND = 7; /** + * Last used cursor number. + * + * @var type int + */ + private $_cursor_ctr; + + /** * Constructs a connection object. */ public function __construct(\PDO $connection, array $connection_options) { @@ -52,6 +59,8 @@ public function __construct(\PDO $connection, array $connection_options) { if (isset($connection_options['init_commands'])) { $this->connection->exec(implode('; ', $connection_options['init_commands'])); } + + $this->_cursor_ctr = 0; } /** @@ -128,11 +137,16 @@ public function query($query, array $args = array(), $options = array()) { $stmt->execute($args, $options); } + if (strpos($stmt->queryString,'SAVEPOINT mimic_innodb_not_released;') !== FALSE) { + $this->connection->prepare("RELEASE SAVEPOINT mimic_innodb_not_released")->execute(); + } + switch ($options['return']) { case Database::RETURN_STATEMENT: $stmt->allowRowCount = FALSE; return $stmt; case Database::RETURN_AFFECTED: + $stmt->allowRowCount = TRUE; return $stmt->rowCount(); case Database::RETURN_INSERT_ID: return $this->connection->lastInsertId($options['sequence_name']); @@ -143,6 +157,10 @@ public function query($query, array $args = array(), $options = array()) { } } catch (\PDOException $e) { + if (preg_match("/SAVEPOINT (mimic_innodb_(released|not_released))/",$stmt->queryString,$matches)) { + $this->connection->prepare("ROLLBACK TO SAVEPOINT " . $matches[1])->execute(); + } + if ($options['throw_exception']) { // Match all SQLSTATE 23xxx errors. if (substr($e->getCode(), -6, -3) == '23') { @@ -170,7 +188,27 @@ public function prepareQuery($query) { // @todo This workaround only affects bytea fields, but the involved field // types involved in the query are unknown, so there is no way to // conditionally execute this for affected queries only. - return parent::prepareQuery(preg_replace('/ ([^ ]+) +(I*LIKE|NOT +I*LIKE) /i', ' ${1}::text ${2} ', $query)); + $query = preg_replace('/ ([^ ]+) +(I*LIKE|NOT +I*LIKE) /i', ' ${1}::text ${2} ', $query); + + // While in transaction context, put a SAVEPOINT around every query that isn't + // itself a SAVEPOINT operation. This means that a failed query can't cause + // the transaction to abort, which mimics the behavior of innodb. + if ($this->inTransaction() && + (stripos($query,'SAVEPOINT ') === FALSE) && + (stripos($query,'RELEASE ') === FALSE)) { + if (preg_match('/^[\s]*SELECT /i', $query)) { + // In the case of SELECT the SAVEPOINT can also be released in the same + // query. Otherwise the RELEASE is a separate query. + $csr = 'csr' . $this->_cursor_ctr++; + $query = 'SAVEPOINT mimic_innodb_released; DECLARE '. $csr .' CURSOR FOR ' . + $query . '; RELEASE SAVEPOINT mimic_innodb_released; FETCH ALL ' . $csr; + } + else { + $query = 'SAVEPOINT mimic_innodb_not_released; ' . $query; + } + } + + return parent::prepareQuery($query); } public function queryRange($query, $from, $count, array $args = array(), array $options = array()) { diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php index 8b4f913..5f33415 100644 --- a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php +++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php @@ -130,7 +130,7 @@ protected function createTableSql($name, $table) { $sql_keys = array(); if (isset($table['primary key']) && is_array($table['primary key'])) { - $sql_keys[] = 'PRIMARY KEY (' . implode(', ', $table['primary key']) . ')'; + $sql_keys[] = 'PRIMARY KEY (' . $this->_createPrimaryKeySql($table['primary key']) . ')'; } if (isset($table['unique keys']) && is_array($table['unique keys'])) { foreach ($table['unique keys'] as $key_name => $key) { @@ -318,6 +318,21 @@ protected function _createKeySql($fields) { return implode(', ', $return); } + protected function _createPrimaryKeySql($fields) { + $return = array(); + foreach ($fields as $field) { + if (is_array($field)) { + // Postgresql does not support key length. It does support fillfactor, + // but that requires a db lookup for each column. + $return[] = '"' . $field[0] . '"'; + } + else { + $return[] = '"' . $field . '"'; + } + } + return implode(', ', $return); + } + function renameTable($table, $new_name) { if (!$this->tableExists($table)) { throw new SchemaObjectDoesNotExistException(t("Cannot rename @table to @table_new: table @table doesn't exist.", array('@table' => $table, '@table_new' => $new_name))); @@ -457,7 +472,7 @@ public function addPrimaryKey($table, $fields) { throw new SchemaObjectExistsException(t("Cannot add primary key to table @table: primary key already exists.", array('@table' => $table))); } - $this->connection->query('ALTER TABLE {' . $table . '} ADD PRIMARY KEY (' . implode(',', $fields) . ')'); + $this->connection->query('ALTER TABLE {' . $table . '} ADD PRIMARY KEY (' . $this->_createPrimaryKeySql($fields) . ')'); } public function dropPrimaryKey($table) { diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Select.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Select.php index bf9f23a..9963969 100644 --- a/core/lib/Drupal/Core/Database/Driver/pgsql/Select.php +++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Select.php @@ -55,6 +55,10 @@ public function orderBy($field, $direction = 'ASC') { // Call parent function to order on this. $return = parent::orderBy($field, $direction); + if ($this->hasTag('pgsql_no_addfield_on_orderby')) { + return $return; + } + // If there is a table alias specified, split it up. if (strpos($field, '.') !== FALSE) { list($table, $table_field) = explode('.', $field); diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Update.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Update.php index 5c93433..9fef61c 100644 --- a/core/lib/Drupal/Core/Database/Driver/pgsql/Update.php +++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Update.php @@ -65,8 +65,7 @@ public function execute() { $options = $this->queryOptions; $options['already_prepared'] = TRUE; - $this->connection->query($stmt, $options); - return $stmt->rowCount(); + return $this->connection->query($stmt, array(), $options); } } diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Query.php b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php index f5c84b4..e7179c4 100644 --- a/core/lib/Drupal/Core/Entity/Query/Sql/Query.php +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php @@ -206,6 +206,7 @@ protected function addSort() { } // Now we know whether this is a simple query or not, actually do the // sorting. + $this->sqlQuery->addTag('pgsql_no_addfield_on_orderby'); foreach ($sort as $key => $sql_alias) { $direction = $this->sort[$key]['direction']; if ($simple_query || isset($this->sqlGroupBy[$sql_alias])) {