Commit 340ff83b authored by catch's avatar catch
Browse files

Issue #3185399 by alexpott, mondrake, daffie: Use RETURNING to determine last...

Issue #3185399 by alexpott, mondrake, daffie: Use RETURNING to determine last insert id in Drupal\Core\Database\Driver\pgsql\Insert
parent 3c2db2ed
Loading
Loading
Loading
Loading
+21 −26
Changes for core/lib/Drupal/Core/Database/Driver/pgsql/Insert.php: 21 added lines, 26 removed lines.
Original line number Diff line number Diff line
@@ -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 @@ public function execute() {
          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 @@ public function execute() {
      }
    }

    // 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 @@ public function execute() {
    $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 @@ public function execute() {
    // 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 @@ public function __toString() {
    // 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 ';

      $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;
  }