Commit cf44d388 authored by webchick's avatar webchick

Issue #2181291 by lokapujya, ricardoamaro, mradcliffe, fvideon, Damien...

Issue #2181291 by lokapujya, ricardoamaro, mradcliffe, fvideon, Damien Tournoud, Crell, bzrudi71, steinmb, grom358, jaredsmith: Fixed Prevent a query from aborting the entire transaction in pgsql.
parent 2634b437
......@@ -12,6 +12,7 @@
use Drupal\Core\Database\DatabaseNotFoundException;
use Drupal\Core\Database\StatementInterface;
use Drupal\Core\Database\IntegrityConstraintViolationException;
use Drupal\Core\Database\DatabaseExceptionWrapper;
/**
* @addtogroup database
......@@ -101,7 +102,6 @@ public static function open(array &$connection_options = array()) {
return $pdo;
}
public function query($query, array $args = array(), $options = array()) {
$options += $this->defaultOptions();
......@@ -148,6 +148,9 @@ public function query($query, array $args = array(), $options = array()) {
if (substr($e->getCode(), -6, -3) == '23') {
$e = new IntegrityConstraintViolationException($e->getMessage(), $e->getCode(), $e);
}
else {
$e = new DatabaseExceptionWrapper($e->getMessage(), 0, $e);
}
// Add additional debug information.
if ($query instanceof StatementInterface) {
$e->query_string = $stmt->getQueryString();
......@@ -274,6 +277,52 @@ public function nextId($existing = 0) {
return $id;
}
/**
* Add a new savepoint with an unique name.
*
* The main use for this method is to mimic InnoDB functionality, which
* provides an inherent savepoint before any query in a transaction.
*
* @param $savepoint_name
* A string representing the savepoint name. By default,
* "mimic_implicit_commit" is used.
*
* @see Drupal\Core\Database\Connection::pushTransaction().
*/
public function addSavepoint($savepoint_name = 'mimic_implicit_commit') {
if ($this->inTransaction()) {
$this->pushTransaction($savepoint_name);
}
}
/**
* Release a savepoint by name.
*
* @param $savepoint_name
* A string representing the savepoint name. By default,
* "mimic_implicit_commit" is used.
*
* @see Drupal\Core\Database\Connection::popTransaction().
*/
public function releaseSavepoint($savepoint_name = 'mimic_implicit_commit') {
if (isset($this->transactionLayers[$savepoint_name])) {
$this->popTransaction($savepoint_name);
}
}
/**
* Rollback a savepoint by name if it exists.
*
* @param $savepoint_name
* A string representing the savepoint name. By default,
* "mimic_implicit_commit" is used.
*/
public function rollbackSavepoint($savepoint_name = 'mimic_implicit_commit') {
if (isset($this->transactionLayers[$savepoint_name])) {
$this->rollback($savepoint_name);
}
}
}
/**
......
......@@ -9,4 +9,23 @@
use Drupal\Core\Database\Query\Delete as QueryDelete;
class Delete extends QueryDelete { }
class Delete extends QueryDelete {
/**
* {@inheritdoc}
*/
public function execute() {
$this->connection->addSavepoint();
try {
$result = parent::execute();
}
catch (\Exception $e) {
$this->connection->rollbackSavepoint();
throw $e;
}
$this->connection->releaseSavepoint();
return $result;
}
}
......@@ -60,11 +60,21 @@ public function queryTableInformation($table) {
'sequences' => array(),
);
// Don't use {} around information_schema.columns table.
$result = $this->connection->query("SELECT column_name, data_type, column_default FROM information_schema.columns WHERE table_schema = :schema AND table_name = :table AND (data_type = 'bytea' OR (numeric_precision IS NOT NULL AND column_default LIKE :default))", array(
':schema' => $schema,
':table' => $table_name,
':default' => '%nextval%',
));
$this->connection->addSavepoint();
try {
$result = $this->connection->query("SELECT column_name, data_type, column_default FROM information_schema.columns WHERE table_schema = :schema AND table_name = :table AND (data_type = 'bytea' OR (numeric_precision IS NOT NULL AND column_default LIKE :default))", array(
':schema' => $schema,
':table' => $table_name,
':default' => '%nextval%',
));
}
catch (\Exception $e) {
$this->connection->rollbackSavepoint();
throw $e;
}
$this->connection->releaseSavepoint();
foreach ($result as $column) {
if ($column->data_type == 'bytea') {
$table_information->blob_fields[$column->column_name] = TRUE;
......@@ -102,11 +112,22 @@ public function queryFieldInformation($table, $field) {
$schema = $prefixInfo['schema'];
$table_name = $prefixInfo['table'];
$checks = $this->connection->query("SELECT conname FROM pg_class cl INNER JOIN pg_constraint co ON co.conrelid = cl.oid INNER JOIN pg_attribute attr ON attr.attrelid = cl.oid AND attr.attnum = ANY (co.conkey) INNER JOIN pg_namespace ns ON cl.relnamespace = ns.oid WHERE co.contype = 'c' AND ns.nspname = :schema AND cl.relname = :table AND attr.attname = :column", array(
':schema' => $schema,
':table' => $table_name,
':column' => $field,
));
$this->connection->addSavepoint();
try {
$checks = $this->connection->query("SELECT conname FROM pg_class cl INNER JOIN pg_constraint co ON co.conrelid = cl.oid INNER JOIN pg_attribute attr ON attr.attrelid = cl.oid AND attr.attnum = ANY (co.conkey) INNER JOIN pg_namespace ns ON cl.relnamespace = ns.oid WHERE co.contype = 'c' AND ns.nspname = :schema AND cl.relname = :table AND attr.attname = :column", array(
':schema' => $schema,
':table' => $table_name,
':column' => $field,
));
}
catch (\Exception $e) {
$this->connection->rollbackSavepoint();
throw $e;
}
$this->connection->releaseSavepoint();
$field_information = $checks->fetchCol();
return $field_information;
......
......@@ -108,6 +108,23 @@ public function orderBy($field, $direction = 'ASC') {
$this->addField(NULL, $field);
return $return;
}
/**
* {@inheritdoc}
*/
public function execute() {
$this->connection->addSavepoint();
try {
$result = parent::execute();
}
catch (\Exception $e) {
$this->connection->rollbackSavepoint();
throw $e;
}
$this->connection->releaseSavepoint();
return $result;
}
}
/**
......
......@@ -9,4 +9,22 @@
use Drupal\Core\Database\Query\Truncate as QueryTruncate;
class Truncate extends QueryTruncate { }
class Truncate extends QueryTruncate {
/**
* {@inheritdoc}
*/
public function execute() {
$this->connection->addSavepoint();
try {
$result = parent::execute();
}
catch (\Exception $e) {
$this->connection->rollbackSavepoint();
throw $e;
}
$this->connection->releaseSavepoint();
return $result;
}
}
......@@ -66,7 +66,17 @@ public function execute() {
$options = $this->queryOptions;
$options['already_prepared'] = TRUE;
$options['return'] = Database::RETURN_AFFECTED;
return $this->connection->query($stmt, array(), $options);
$this->connection->addSavepoint();
try {
$result = $this->connection->query($stmt, array(), $options);
$this->connection->releaseSavepoint();
return $result;
}
catch (\Exception $e) {
$this->connection->rollbackSavepoint();
throw $e;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment