Skip to content

Commit 9523b2a

Browse files
abnegateclaude
andcommitted
fix: Differentiate duplicate ID vs unique index exceptions, fix Pool transaction pinning
- Mongo, Postgres, SQLite adapters now return distinct messages for duplicate document ID ("Document already exists") vs unique index violations ("Document with the requested unique attributes already exists"), matching MariaDB behavior - Pool adapter now pins a single connection for the entire withTransaction lifecycle, ensuring all delegate calls within a transaction use the same underlying connection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 40080e2 commit 9523b2a

4 files changed

Lines changed: 28 additions & 7 deletions

File tree

src/Database/Adapter/Mongo.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3483,12 +3483,11 @@ protected function processException(\Throwable $e): \Throwable
34833483
}
34843484

34853485
// Duplicate key error
3486-
if ($e->getCode() === 11000) {
3487-
return new DuplicateException('Document already exists', $e->getCode(), $e);
3488-
}
3489-
3490-
// Duplicate key error for unique index
3491-
if ($e->getCode() === 11001) {
3486+
if ($e->getCode() === 11000 || $e->getCode() === 11001) {
3487+
$message = $e->getMessage();
3488+
if (!\str_contains($message, '_uid')) {
3489+
return new DuplicateException('Document with the requested unique attributes already exists', $e->getCode(), $e);
3490+
}
34923491
return new DuplicateException('Document already exists', $e->getCode(), $e);
34933492
}
34943493

src/Database/Adapter/Pool.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ class Pool extends Adapter
1616
*/
1717
protected UtopiaPool $pool;
1818

19+
/**
20+
* When a transaction is active, all delegate calls are routed through
21+
* this pinned adapter to ensure they run on the same connection.
22+
*/
23+
protected ?Adapter $pinnedAdapter = null;
24+
1925
/**
2026
* @param UtopiaPool<covariant Adapter> $pool The pool to use for connections. Must contain instances of Adapter.
2127
*/
@@ -36,6 +42,10 @@ public function __construct(UtopiaPool $pool)
3642
*/
3743
public function delegate(string $method, array $args): mixed
3844
{
45+
if ($this->pinnedAdapter !== null) {
46+
return $this->pinnedAdapter->{$method}(...$args);
47+
}
48+
3949
return $this->pool->use(function (Adapter $adapter) use ($method, $args) {
4050
// Run setters in case config changed since this connection was last used
4151
$adapter->setDatabase($this->getDatabase());
@@ -123,7 +133,12 @@ public function withTransaction(callable $callback): mixed
123133
$adapter->setMetadata($key, $value);
124134
}
125135

126-
return $adapter->withTransaction($callback);
136+
$this->pinnedAdapter = $adapter;
137+
try {
138+
return $adapter->withTransaction($callback);
139+
} finally {
140+
$this->pinnedAdapter = null;
141+
}
127142
});
128143
}
129144

src/Database/Adapter/Postgres.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,6 +2189,10 @@ protected function processException(PDOException $e): \Exception
21892189

21902190
// Duplicate row
21912191
if ($e->getCode() === '23505' && isset($e->errorInfo[1]) && $e->errorInfo[1] === 7) {
2192+
$message = $e->getMessage();
2193+
if (!\str_contains($message, '_uid')) {
2194+
return new DuplicateException('Document with the requested unique attributes already exists', $e->getCode(), $e);
2195+
}
21922196
return new DuplicateException('Document already exists', $e->getCode(), $e);
21932197
}
21942198

src/Database/Adapter/SQLite.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,6 +1328,9 @@ protected function processException(PDOException $e): \Exception
13281328
stripos($message, 'unique') !== false ||
13291329
stripos($message, 'duplicate') !== false
13301330
) {
1331+
if (!\str_contains($message, '_uid')) {
1332+
return new DuplicateException('Document with the requested unique attributes already exists', $e->getCode(), $e);
1333+
}
13311334
return new DuplicateException('Document already exists', $e->getCode(), $e);
13321335
}
13331336
}

0 commit comments

Comments
 (0)