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 68e9c98..e8d52a4 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,6 +137,10 @@ 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: return $stmt; @@ -142,6 +155,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') { @@ -169,7 +186,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/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/Entity/Query/Sql/Query.php b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php index 615e8b7..22fe382 100644 --- a/core/lib/Drupal/Core/Entity/Query/Sql/Query.php +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php @@ -210,6 +210,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])) {