Loading core/modules/mysql/src/Driver/Database/mysql/Upsert.php +15 −8 Original line number Diff line number Diff line Loading @@ -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; } Loading core/modules/pgsql/src/Driver/Database/pgsql/Upsert.php +8 −1 Original line number Diff line number Diff line Loading @@ -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; } Loading core/modules/sqlite/src/Driver/Database/sqlite/Upsert.php +8 −1 Original line number Diff line number Diff line Loading @@ -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; } Loading core/tests/Drupal/KernelTests/Core/Database/UpsertTest.php +120 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading Loading
core/modules/mysql/src/Driver/Database/mysql/Upsert.php +15 −8 Original line number Diff line number Diff line Loading @@ -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; } Loading
core/modules/pgsql/src/Driver/Database/pgsql/Upsert.php +8 −1 Original line number Diff line number Diff line Loading @@ -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; } Loading
core/modules/sqlite/src/Driver/Database/sqlite/Upsert.php +8 −1 Original line number Diff line number Diff line Loading @@ -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; } Loading
core/tests/Drupal/KernelTests/Core/Database/UpsertTest.php +120 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading