Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Database/Adapter/MariaDB.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Utopia\Database\Exception\Character as CharacterException;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Limit as LimitException;
use Utopia\Database\Exception\Unique as UniqueException;
use Utopia\Database\Exception\NotFound as NotFoundException;
use Utopia\Database\Exception\Operator as OperatorException;
use Utopia\Database\Exception\Query as QueryException;
Expand Down Expand Up @@ -1995,7 +1996,7 @@ protected function processException(PDOException $e): \Exception
return new DuplicateException('Duplicate permissions for document', $e->getCode(), $e);
}
if (!\str_contains($message, '_uid')) {
return new DuplicateException('Document with the requested unique attributes already exists', $e->getCode(), $e);
return new UniqueException('Unique index violation', $e->getCode(), $e);
}
return new DuplicateException('Document already exists', $e->getCode(), $e);
}
Expand Down
11 changes: 8 additions & 3 deletions src/Database/Adapter/Postgres.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Utopia\Database\Exception as DatabaseException;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Limit as LimitException;
use Utopia\Database\Exception\Unique as UniqueException;
use Utopia\Database\Exception\NotFound as NotFoundException;
use Utopia\Database\Exception\Operator as OperatorException;
use Utopia\Database\Exception\Timeout as TimeoutException;
Expand Down Expand Up @@ -2224,9 +2225,13 @@ protected function processException(PDOException $e): \Exception

// Duplicate row
if ($e->getCode() === '23505' && isset($e->errorInfo[1]) && $e->errorInfo[1] === 7) {
$message = $e->getMessage();
if (!\str_contains($message, '_uid')) {
return new DuplicateException('Document with the requested unique attributes already exists', $e->getCode(), $e);
if (preg_match('/Key \(([^)]+)\)=\(.+\) already exists/', $e->getMessage(), $matches)) {
$columns = array_map('trim', explode(',', $matches[1]));
sort($columns);
$target = $this->sharedTables ? ['_tenant', '_uid'] : ['_uid'];
if ($columns !== $target) {
return new UniqueException('Unique index violation', $e->getCode(), $e);
}
}
return new DuplicateException('Document already exists', $e->getCode(), $e);
Comment on lines +2228 to 2236
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Regex no-match silently falls through to wrong exception and message

If the preg_match pattern doesn't match — e.g., when PDO collapses the DETAIL: line, or the locale formats the message differently — the function falls through to return new DuplicateException('Document already exists', ...) for every 23505 error, including genuine non-_uid unique violations. The old code used str_contains($message, '_uid') as a cheap guard, so a non-matching case would still have returned the more descriptive DuplicateException('Document with the requested unique attributes already exists'). After this change, those callers instead receive the wrong exception type and a misleading "Document already exists" message. Adding an explicit fallback UniqueException for the no-match branch (or at minimum a str_contains guard as a secondary heuristic) would protect against this edge case.

}
Expand Down
3 changes: 2 additions & 1 deletion src/Database/Adapter/SQLite.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Utopia\Database\Exception as DatabaseException;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Limit as LimitException;
use Utopia\Database\Exception\Unique as UniqueException;
use Utopia\Database\Exception\NotFound as NotFoundException;
use Utopia\Database\Exception\Operator as OperatorException;
use Utopia\Database\Exception\Timeout as TimeoutException;
Expand Down Expand Up @@ -1994,7 +1995,7 @@ protected function processException(PDOException $e): \Exception
stripos($message, 'duplicate') !== false
) {
if (!\str_contains($message, '_uid')) {
return new DuplicateException('Document with the requested unique attributes already exists', $e->getCode(), $e);
return new UniqueException('Unique index violation', $e->getCode(), $e);
}
return new DuplicateException('Document already exists', $e->getCode(), $e);
}
Expand Down
9 changes: 9 additions & 0 deletions src/Database/Exception/Unique.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Utopia\Database\Exception;

use Utopia\Database\Exception;

class Unique extends Exception
{
}