Commit b98f2f3a authored by catch's avatar catch
Browse files

fix: #3611653 Upsert fails after upgrade from 11.3 to 11.4

By: john.oltman
By: cilefen
By: mondrake
(cherry picked from commit 220ee9fe)
parent bced52ee
Loading
Loading
Loading
Loading
Loading
+15 −8
Original line number Diff line number Diff line
@@ -23,22 +23,29 @@ public function __toString() {
      return $this->connection->escapeField($field);
    }, $insert_fields);

    $query = $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES ';

    $values = $this->getInsertPlaceholderFragment($this->insertValues, $this->defaultFields);
    $query .= implode(', ', $values);

    // Updating the unique / primary key fields is not necessary.
    $update_fields = $insert_fields;
    foreach ($this->key as $key) {
      unset($insert_fields[$key]);
      unset($update_fields[$key]);
    }

    $query = $comments . 'INSERT ';

    if (empty($update_fields)) {
      $query .= 'IGNORE ';
    }

    $query .= 'INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES ';
    $values = $this->getInsertPlaceholderFragment($this->insertValues, $this->defaultFields);
    $query .= implode(', ', $values);

    if (!empty($update_fields)) {
      $update = [];
    foreach ($insert_fields as $field) {
      foreach ($update_fields as $field) {
        $update[] = "$field = VALUES($field)";
      }

      $query .= ' ON DUPLICATE KEY UPDATE ' . implode(', ', $update);
    }

    return $query;
  }
+8 −1
Original line number Diff line number Diff line
@@ -132,7 +132,14 @@ public function __toString() {
      $update[] = "$field = EXCLUDED.$field";
    }

    $query .= ' ON CONFLICT (' . implode(', ', $keys) . ') DO UPDATE SET ' . implode(', ', $update);
    $query .= ' ON CONFLICT (' . implode(', ', $keys) . ') DO ';

    if (!empty($update)) {
      $query .= 'UPDATE SET ' . implode(', ', $update);
    }
    else {
      $query .= 'NOTHING';
    }

    return $query;
  }
+8 −1
Original line number Diff line number Diff line
@@ -46,7 +46,14 @@ public function __toString() {
      $update[] = "$field = EXCLUDED.$field";
    }

    $query .= ' ON CONFLICT (' . implode(', ', $keys) . ') DO UPDATE SET ' . implode(', ', $update);
    $query .= ' ON CONFLICT (' . implode(', ', $keys) . ') DO ';

    if (!empty($update)) {
      $query .= 'UPDATE SET ' . implode(', ', $update);
    }
    else {
      $query .= 'NOTHING';
    }

    return $query;
  }
+120 −0
Original line number Diff line number Diff line
@@ -4,6 +4,8 @@

namespace Drupal\KernelTests\Core\Database;

// cspell:ignore Zaphod

use Drupal\Core\Database\Database;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use PHPUnit\Framework\Attributes\Group;
@@ -208,6 +210,124 @@ public function testCompositeKeyUpsert(): void {
    $this->assertEquals('Meredith', $person->name, 'Name set correctly.');
  }

  /**
   * Tests a single-value upsert that has no columns to update.
   */
  public function testDegeneratedSingleValueUpsert(): void {
    $this->assertSame('1', $this->connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField());

    // An upsert that only adds a record.
    $this->connection->upsert('test_people')
      ->key('job')
      ->fields(['job'])
      ->values(['Plumber'])
      ->execute();

    $this->assertSame('2', $this->connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField());

    // Do the same upsert, should be a no-op.
    $this->connection->upsert('test_people')
      ->key('job')
      ->fields(['job'])
      ->values(['Plumber'])
      ->execute();

    // No records added.
    $this->assertSame('2', $this->connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField());
  }

  /**
   * Tests a multi-value upsert that has no columns to update.
   */
  public function testDegeneratedMultiValueUpsert(): void {
    $this->assertSame('1', $this->connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField());

    // An upsert that only adds a record.
    $this->connection->upsert('test_people')
      ->key('job')
      ->fields(['job'])
      ->values(['Plumber'])
      ->execute();

    $this->assertSame('2', $this->connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField());

    // Do a multi-value upsert that should add two more records.
    $this->connection->upsert('test_people')
      ->key('job')
      ->fields(['job'])
      ->values(['Plumber'])
      ->values(['Carpenter'])
      ->values(['GalacticHitchhiker'])
      ->execute();

    // Two records added, for a total of four.
    $this->assertSame('4', $this->connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField());
  }

  /**
   * Tests an upsert with a composite key and no columns to update.
   */
  public function testDegeneratedCompositeKeyUpsert(): void {
    $this->installSchema('database_test', ['test_composite_primary']);

    // Insert two new rows.
    $this->connection->insert('test_composite_primary')
      ->fields([
        'name' => 'Kate',
        'age' => 25,
        'job' => 'Volunteer',
      ])
      ->values([
        'name' => 'Bertie',
        'age' => 6,
        'job' => 'Puppet',
      ])
      ->execute();

    $this->assertSame('2', $this->connection->query('SELECT COUNT(*) FROM {test_composite_primary}')->fetchField());

    // Performing an upsert with the same composite key should be a no-op.
    $this->connection->upsert('test_composite_primary')
      ->key(['name', 'age'])
      // Add a new row directly from ::fields().
      ->fields([
        'name' => 'Kate',
        'age' => 25,
      ])
      ->values([
        'name' => 'Bertie',
        'age' => 6,
      ])
      ->execute();

    $this->assertSame('2', $this->connection->query('SELECT COUNT(*) FROM {test_composite_primary}')->fetchField());

    // Different composite keys should insert other rows.
    $this->connection->upsert('test_composite_primary')
      ->key(['name', 'age'])
      // Add a new row directly from ::fields().
      ->fields(['name', 'age'])
      ->values([
        'name' => 'Kate',
        'age' => 25,
      ])
      ->values([
        'name' => 'Arthur',
        'age' => 30,
      ])
      ->values([
        'name' => 'Bertie',
        'age' => 6,
      ])
      ->values([
        'name' => 'Zaphod',
        'age' => 200,
      ])
      ->execute();

    $this->assertSame('4', $this->connection->query('SELECT COUNT(*) FROM {test_composite_primary}')->fetchField());
  }

  /**
   * Tests that we can upsert a null into blob field.
   */