Skip to content

Commit 60c1330

Browse files
authored
[10.x] HotFix: throw captured UniqueConstraintViolationException if there are no matching records on SELECT retry (#48234)
* HotFix: throw captured `UniqueConstraintViolationException` * Test: Add `testCreateOrFirstNonAttributeFieldViolation` case
1 parent ff96905 commit 60c1330

File tree

4 files changed

+32
-8
lines changed

4 files changed

+32
-8
lines changed

src/Illuminate/Database/Eloquent/Builder.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,8 +581,8 @@ public function createOrFirst(array $attributes = [], array $values = [])
581581
{
582582
try {
583583
return $this->withSavepointIfNeeded(fn () => $this->create(array_merge($attributes, $values)));
584-
} catch (UniqueConstraintViolationException) {
585-
return $this->useWritePdo()->where($attributes)->first();
584+
} catch (UniqueConstraintViolationException $e) {
585+
return $this->useWritePdo()->where($attributes)->first() ?? throw $e;
586586
}
587587
}
588588

src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -648,16 +648,16 @@ public function createOrFirst(array $attributes = [], array $values = [], array
648648
{
649649
try {
650650
return $this->getQuery()->withSavePointIfNeeded(fn () => $this->create(array_merge($attributes, $values), $joining, $touch));
651-
} catch (UniqueConstraintViolationException $exception) {
651+
} catch (UniqueConstraintViolationException $e) {
652652
// ...
653653
}
654654

655655
try {
656-
return tap($this->related->where($attributes)->first(), function ($instance) use ($joining, $touch) {
656+
return tap($this->related->where($attributes)->first() ?? throw $e, function ($instance) use ($joining, $touch) {
657657
$this->getQuery()->withSavepointIfNeeded(fn () => $this->attach($instance, $joining, $touch));
658658
});
659-
} catch (UniqueConstraintViolationException) {
660-
return (clone $this)->useWritePdo()->where($attributes)->first();
659+
} catch (UniqueConstraintViolationException $e) {
660+
return (clone $this)->useWritePdo()->where($attributes)->first() ?? throw $e;
661661
}
662662
}
663663

src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@ public function createOrFirst(array $attributes = [], array $values = [])
253253
{
254254
try {
255255
return $this->getQuery()->withSavepointIfNeeded(fn () => $this->create(array_merge($attributes, $values)));
256-
} catch (UniqueConstraintViolationException) {
257-
return $this->useWritePdo()->where($attributes)->first();
256+
} catch (UniqueConstraintViolationException $e) {
257+
return $this->useWritePdo()->where($attributes)->first() ?? throw $e;
258258
}
259259
}
260260

tests/Database/DatabaseEloquentIntegrationTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Illuminate\Database\Eloquent\SoftDeletes;
1717
use Illuminate\Database\Eloquent\SoftDeletingScope;
1818
use Illuminate\Database\QueryException;
19+
use Illuminate\Database\UniqueConstraintViolationException;
1920
use Illuminate\Pagination\AbstractPaginator as Paginator;
2021
use Illuminate\Pagination\Cursor;
2122
use Illuminate\Pagination\CursorPaginator;
@@ -91,6 +92,8 @@ protected function createSchema()
9192
$this->schema($connection)->create('unique_users', function ($table) {
9293
$table->increments('id');
9394
$table->string('name')->nullable();
95+
// Unique constraint will be applied only for non-null values
96+
$table->string('screen_name')->nullable()->unique();
9497
$table->string('email')->unique();
9598
$table->timestamp('birthday', 6)->nullable();
9699
$table->timestamps();
@@ -552,6 +555,27 @@ public function testCreateOrFirst()
552555
$this->assertSame('Nuno Maduro', $user4->name);
553556
}
554557

558+
public function testCreateOrFirstNonAttributeFieldViolation()
559+
{
560+
// 'email' and 'screen_name' are unique and independent of each other.
561+
EloquentTestUniqueUser::create([
562+
'email' => '[email protected]',
563+
'screen_name' => '@taylorotwell',
564+
]);
565+
566+
$this->expectException(UniqueConstraintViolationException::class);
567+
568+
// Although 'email' is expected to be unique and is passed as $attributes,
569+
// if the 'screen_name' attribute listed in non-unique $values causes a violation,
570+
// a UniqueConstraintViolationException should be thrown.
571+
EloquentTestUniqueUser::createOrFirst(
572+
['email' => '[email protected]'],
573+
[
574+
'screen_name' => '@taylorotwell',
575+
]
576+
);
577+
}
578+
555579
public function testCreateOrFirstWithinTransaction()
556580
{
557581
$user1 = EloquentTestUniqueUser::create(['email' => '[email protected]']);

0 commit comments

Comments
 (0)