Issue #3185399 by alexpott, mondrake, daffie: Use RETURNING to determine last insert id in Drupal\Core\Database\Driver\pgsql\Insert

merge-requests/172/head
catch 2020-12-17 20:12:50 +00:00
parent 3c2db2eda0
commit 340ff83bbe
1 changed files with 24 additions and 29 deletions

View File

@ -2,7 +2,6 @@
namespace Drupal\Core\Database\Driver\pgsql;
use Drupal\Core\Database\Database;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\Core\Database\IntegrityConstraintViolationException;
use Drupal\Core\Database\Query\Insert as QueryInsert;
@ -55,11 +54,6 @@ class Insert extends QueryInsert {
if ($serial_key !== FALSE) {
$serial_value = $insert_values[$serial_key];
// Force $last_insert_id to the specified value. This is only done
// if $index is 0.
if ($index == 0) {
$last_insert_id = $serial_value;
}
// Sequences must be greater than or equal to 1.
if ($serial_value === NULL || !$serial_value) {
$serial_value = 1;
@ -86,11 +80,6 @@ class Insert extends QueryInsert {
}
}
// PostgreSQL requires the table name to be specified explicitly when
// requesting the last insert ID. If there are no sequences, then we can't
// get a last insert id.
$sequence_name = $table_information->sequences[0] ?? NULL;
// Create a savepoint so we can rollback a failed query. This is so we can
// mimic MySQL and SQLite transactions which don't fail if a single query
// fails. This is important for tables that are created on demand. For
@ -98,17 +87,8 @@ class Insert extends QueryInsert {
$this->connection->addSavepoint();
try {
$stmt->execute(NULL, $this->queryOptions);
// Only use the returned last_insert_id if it is not already set.
// PostgreSQL requires the table name to be specified explicitly when
// requesting the last insert ID.
if (!isset($last_insert_id)) {
if ($this->queryOptions['return'] === Database::RETURN_INSERT_ID && $sequence_name !== NULL) {
// cspell:ignore currval
$last_insert_id = $this->connection->query("SELECT currval('$sequence_name')")->fetchField();
}
else {
$last_insert_id = NULL;
}
if (isset($table_information->serial_fields[0])) {
$last_insert_id = $stmt->fetchField();
}
$this->connection->releaseSavepoint();
}
@ -131,7 +111,7 @@ class Insert extends QueryInsert {
// Re-initialize the values array so that we can re-use this query.
$this->insertValues = [];
return $last_insert_id;
return $last_insert_id ?? NULL;
}
public function __toString() {
@ -149,14 +129,29 @@ class Insert extends QueryInsert {
// pass it back, as any remaining options are irrelevant.
if (!empty($this->fromQuery)) {
$insert_fields_string = $insert_fields ? ' (' . implode(', ', $insert_fields) . ') ' : ' ';
return $comments . 'INSERT INTO {' . $this->table . '}' . $insert_fields_string . $this->fromQuery;
$query = $comments . 'INSERT INTO {' . $this->table . '}' . $insert_fields_string . $this->fromQuery;
}
else {
$query = $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES ';
$query = $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES ';
$values = $this->getInsertPlaceholderFragment($this->insertValues, $this->defaultFields);
$query .= implode(', ', $values);
$values = $this->getInsertPlaceholderFragment($this->insertValues, $this->defaultFields);
$query .= implode(', ', $values);
}
try {
// Fetch the list of blobs and sequences used on that table.
$table_information = $this->connection->schema()->queryTableInformation($this->table);
if (isset($table_information->serial_fields[0])) {
// Use RETURNING syntax to get the last insert ID in the same INSERT
// query, see https://www.postgresql.org/docs/10/dml-returning.html.
$query .= ' RETURNING ' . $table_information->serial_fields[0];
}
}
catch (DatabaseExceptionWrapper $e) {
// If we fail to get the table information it is probably because the
// table does not exist yet so adding the returning statement is pointless
// because the query will fail. This happens for tables created on demand,
// for example, cache tables.
}
return $query;
}