Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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 @@ -12,6 +12,7 @@
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Exception\Timeout as TimeoutException;
use Utopia\Database\Exception\Truncate as TruncateException;
use Utopia\Database\Exception\Unique as UniqueException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;

Expand Down Expand Up @@ -1795,7 +1796,7 @@ protected function processException(PDOException $e): \Exception

// Duplicate row
if ($e->getCode() === '23000' && isset($e->errorInfo[1]) && $e->errorInfo[1] === 1062) {
return new DuplicateException('Document already exists', $e->getCode(), $e);
return new UniqueException('Document already exists', $e->getCode(), $e);
}

// Data is too big for column resize
Expand Down
3 changes: 2 additions & 1 deletion src/Database/Adapter/Postgres.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Utopia\Database\Exception\Timeout as TimeoutException;
use Utopia\Database\Exception\Transaction as TransactionException;
use Utopia\Database\Exception\Truncate as TruncateException;
use Utopia\Database\Exception\Unique as UniqueException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;

Expand Down Expand Up @@ -1924,7 +1925,7 @@ protected function processException(PDOException $e): \Exception

// Duplicate row
if ($e->getCode() === '23505' && isset($e->errorInfo[1]) && $e->errorInfo[1] === 7) {
return new DuplicateException('Document already exists', $e->getCode(), $e);
return new UniqueException('Document already exists', $e->getCode(), $e);
}
Comment on lines 1919 to 1930
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Regex miss silently downgrades _uid duplicate to UniqueException

If preg_match fails to match (e.g. the PDO error message omits the DETAIL line, or the Postgres locale formats it differently), execution falls through to return new UniqueException(...). Before this PR every 23505 returned DuplicateException; now an unmatched primary-key duplicate silently becomes a UniqueException, breaking callers that check $e instanceof DuplicateException && !($e instanceof UniqueException). A safe fallback would return DuplicateException when the regex cannot resolve the column list.


// Data is too big for column resize
Expand Down
22 changes: 9 additions & 13 deletions src/Database/Adapter/SQLite.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
use Utopia\Database\Document;
use Utopia\Database\Exception as DatabaseException;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\NotFound as NotFoundException;
use Utopia\Database\Exception\Timeout as TimeoutException;
use Utopia\Database\Exception\Transaction as TransactionException;
use Utopia\Database\Exception\Unique as UniqueException;
use Utopia\Database\Helpers\ID;

/**
Expand Down Expand Up @@ -619,10 +619,7 @@ public function createDocument(Document $collection, Document $document): Docume
$stmtPermissions->execute();
}
} catch (PDOException $e) {
throw match ($e->getCode()) {
"1062", "23000" => new Duplicate('Duplicated document: ' . $e->getMessage()),
default => $e,
};
throw $this->processException($e);
}


Expand Down Expand Up @@ -841,11 +838,7 @@ public function updateDocument(Document $collection, string $id, Document $docum
$stmtAddPermissions->execute();
}
} catch (PDOException $e) {
throw match ($e->getCode()) {
'1062',
'23000' => new Duplicate('Duplicated document: ' . $e->getMessage()),
default => $e,
};
throw $this->processException($e);
}

return $document;
Expand Down Expand Up @@ -1243,14 +1236,17 @@ public function getKeywords(): array

protected function processException(PDOException $e): \Exception
{
var_dump('processException');
var_dump($e->getCode());
var_dump($e->errorInfo[1]);
Comment thread
fogelito marked this conversation as resolved.
Outdated
// Timeout
if ($e->getCode() === 'HY000' && isset($e->errorInfo[1]) && $e->errorInfo[1] === 3024) {
return new TimeoutException('Query timed out', $e->getCode(), $e);
}

// Duplicate
if ($e->getCode() === 'HY000' && isset($e->errorInfo[1]) && $e->errorInfo[1] === 1) {
return new DuplicateException('Document already exists', $e->getCode(), $e);
// Duplicate row
if ($e->getCode() === '23000' && isset($e->errorInfo[1]) && $e->errorInfo[1] === 19) {
return new UniqueException('Document already exists', $e->getCode(), $e);
}
Comment thread
fogelito marked this conversation as resolved.

return $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
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 UniqueException must extend DuplicateException for the tests to pass

testUniqueIndexDuplicate and testUniqueIndexDuplicateUpdate both assert assertInstanceOf(DuplicateException::class, $e) on the same exception that must also satisfy assertInstanceOf(UniqueException::class, $e). The only way both assertions pass on a single thrown UniqueException is if Unique extends Duplicate. With the current class Unique extends Exception, the assertInstanceOf(DuplicateException::class, $e) check will fail at runtime.

It also preserves backward compatibility: existing catch blocks for DuplicateException will continue to catch unique-constraint violations, which is the expected behaviour the PR description refers to when it says "Public API signatures unchanged."

Suggested change
class Unique extends Exception
class Unique extends Duplicate

with use Utopia\Database\Exception\Duplicate; replacing the base Exception import.

{
}
9 changes: 5 additions & 4 deletions tests/e2e/Adapter/Scopes/DocumentTests.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Utopia\Database\Exception\Limit as LimitException;
use Utopia\Database\Exception\Structure as StructureException;
use Utopia\Database\Exception\Type as TypeException;
use Utopia\Database\Exception\Unique as UniqueException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
Expand Down Expand Up @@ -4729,7 +4730,7 @@ public function testWritePermissionsUpdateFailure(Document $document): Document
*/
public function testUniqueIndexDuplicate(): void
{
$this->expectException(DuplicateException::class);
$this->expectException(UniqueException::class);

/** @var Database $database */
$database = static::getDatabase();
Expand Down Expand Up @@ -4794,7 +4795,7 @@ public function testUniqueIndexDuplicateUpdate(): void
'with-dash' => 'Works4'
]));

$this->expectException(DuplicateException::class);
$this->expectException(UniqueException::class);

$database->updateDocument('movies', $document->getId(), $document->setAttribute('name', 'Frozen'));
}
Expand Down Expand Up @@ -5299,7 +5300,7 @@ public function testExceptionDuplicate(Document $document): void

$document->setAttribute('$id', 'duplicated');
$database->createDocument($document->getCollection(), $document);
$this->expectException(DuplicateException::class);
$this->expectException(UniqueException::class);
$database->createDocument($document->getCollection(), $document);
}

Expand All @@ -5317,7 +5318,7 @@ public function testExceptionCaseInsensitiveDuplicate(Document $document): Docum

$document->setAttribute('$id', 'CaseSensitive');

$this->expectException(DuplicateException::class);
$this->expectException(UniqueException::class);
$database->createDocument($document->getCollection(), $document);

return $document;
Expand Down
5 changes: 3 additions & 2 deletions tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Utopia\Database\Exception\Limit as LimitException;
use Utopia\Database\Exception\Restricted as RestrictedException;
use Utopia\Database\Exception\Structure as StructureException;
use Utopia\Database\Exception\Unique as UniqueException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
Expand Down Expand Up @@ -292,7 +293,7 @@ public function testOneToOneOneWayRelationship(): void
);
$this->fail('Failed to throw duplicate exception');
} catch (Exception $e) {
$this->assertInstanceOf(DuplicateException::class, $e);
$this->assertInstanceOf(UniqueException::class, $e);
}

// Create new document
Expand Down Expand Up @@ -802,7 +803,7 @@ public function testOneToOneTwoWayRelationship(): void
);
$this->fail('Failed to throw exception');
} catch (Exception $e) {
$this->assertInstanceOf(DuplicateException::class, $e);
$this->assertInstanceOf(UniqueException::class, $e);
}

$city1 = $database->getDocument('city', 'city1');
Expand Down