Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
fix(query-builder): Don't catch UniqueConstraintViolationException
UniqueConstraintViolationException is no longer throw directly but

instead is now wrapped inside a \OCP\DB\Exception. So check the

exception reason.

Signed-off-by: Carl Schwan <[email protected]>
  • Loading branch information
Carl Schwan authored and Antreesy committed Sep 12, 2025
commit 90271b59d7383534a3c328c1ada7d757613ff8c7
7 changes: 5 additions & 2 deletions lib/private/Authentication/Token/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
*/
namespace OC\Authentication\Token;

use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\Authentication\Exceptions\InvalidTokenException as OcInvalidTokenException;
use OC\Authentication\Exceptions\PasswordlessTokenException;
use OCP\Authentication\Exceptions\ExpiredTokenException;
use OCP\Authentication\Exceptions\InvalidTokenException;
use OCP\Authentication\Exceptions\WipeTokenException;
use OCP\Authentication\Token\IProvider as OCPIProvider;
use OCP\Authentication\Token\IToken as OCPIToken;
use OCP\DB\Exception;

class Manager implements IProvider, OCPIProvider {
/** @var PublicKeyTokenProvider */
Expand Down Expand Up @@ -60,7 +60,10 @@ public function generateToken(string $token,
$remember,
$scope,
);
} catch (UniqueConstraintViolationException $e) {
} catch (Exception $e) {
if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
throw $e;
}
// It's rare, but if two requests of the same session (e.g. env-based SAML)
// try to create the session token they might end up here at the same time
// because we use the session ID as token and the db token is created anew
Expand Down
12 changes: 9 additions & 3 deletions lib/private/Collaboration/Resources/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
*/
namespace OC\Collaboration\Resources;

use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OCP\Collaboration\Resources\CollectionException;
use OCP\Collaboration\Resources\ICollection;
use OCP\Collaboration\Resources\IManager;
use OCP\Collaboration\Resources\IProvider;
use OCP\Collaboration\Resources\IProviderManager;
use OCP\Collaboration\Resources\IResource;
use OCP\Collaboration\Resources\ResourceException;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IUser;
Expand Down Expand Up @@ -351,7 +351,10 @@ public function cacheAccessForResource(IResource $resource, ?IUser $user, bool $
]);
try {
$query->execute();
} catch (UniqueConstraintViolationException $e) {
} catch (Exception $e) {
if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
throw $e;
}
}
}

Expand All @@ -367,7 +370,10 @@ public function cacheAccessForCollection(ICollection $collection, ?IUser $user,
]);
try {
$query->execute();
} catch (UniqueConstraintViolationException $e) {
} catch (Exception $e) {
if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
throw $e;
}
}
}

Expand Down
25 changes: 16 additions & 9 deletions lib/private/Files/Cache/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
*/
namespace OC\Files\Cache;

use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\DB\Exceptions\DbalException;
use OC\DB\QueryBuilder\Sharded\ShardDefinition;
use OC\Files\Search\SearchComparison;
use OC\Files\Search\SearchQuery;
use OC\Files\Storage\Wrapper\Encryption;
use OC\SystemConfig;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Cache\CacheEntryInsertedEvent;
Expand Down Expand Up @@ -247,7 +247,7 @@ public function put($file, array $data) {
* @param array $data
*
* @return int file id
* @throws \RuntimeException
* @throws \RuntimeException|Exception
*/
public function insert($file, array $data) {
// normalize file
Expand Down Expand Up @@ -307,15 +307,19 @@ public function insert($file, array $data) {
$this->eventDispatcher->dispatchTyped($event);
return $fileId;
}
} catch (UniqueConstraintViolationException $e) {
// entry exists already
if ($this->connection->inTransaction()) {
$this->connection->commit();
$this->connection->beginTransaction();
} catch (Exception $e) {
if ($e->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
// entry exists already
if ($this->connection->inTransaction()) {
$this->connection->commit();
$this->connection->beginTransaction();
}
} else {
throw $e;
}
}

// The file was created in the mean time
// The file was created in the meantime
if (($id = $this->getId($file)) > -1) {
$this->update($id, $data);
return $id;
Expand Down Expand Up @@ -375,7 +379,10 @@ public function update($id, array $data) {
}

$query->execute();
} catch (UniqueConstraintViolationException $e) {
} catch (Exception $e) {
if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
throw $e;
}
$query = $this->getQueryBuilder();
$query->update('filecache_extended')
->whereFileId($id)
Expand Down
10 changes: 7 additions & 3 deletions lib/private/Group/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
*/
namespace OC\Group;

use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\User\LazyUser;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Group\Backend\ABackend;
use OCP\Group\Backend\IAddToGroupBackend;
Expand Down Expand Up @@ -75,8 +75,12 @@ public function createGroup(string $name): ?string {
->setValue('gid', $builder->createNamedParameter($gid))
->setValue('displayname', $builder->createNamedParameter($name))
->execute();
} catch (UniqueConstraintViolationException $e) {
return null;
} catch (Exception $e) {
if ($e->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
return null;
} else {
throw $e;
}
}

// Add to cache
Expand Down
32 changes: 19 additions & 13 deletions lib/private/SystemTag/SystemTagManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/
namespace OC\SystemTag;

use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IAppConfig;
Expand Down Expand Up @@ -177,12 +177,15 @@ public function createTag(string $tagName, bool $userVisible, bool $userAssignab

try {
$query->execute();
} catch (UniqueConstraintViolationException $e) {
throw new TagAlreadyExistsException(
'Tag ("' . $truncatedTagName . '", ' . $userVisible . ', ' . $userAssignable . ') already exists',
0,
$e
);
} catch (Exception $e) {
if ($e->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
throw new TagAlreadyExistsException(
'Tag ("' . $truncatedTagName . '", ' . $userVisible . ', ' . $userAssignable . ') already exists',
0,
$e
);
}
throw $e;
}

$tagId = $query->getLastInsertId();
Expand Down Expand Up @@ -262,12 +265,15 @@ public function updateTag(
'Tag does not exist', 0, null, [$tagId]
);
}
} catch (UniqueConstraintViolationException $e) {
throw new TagAlreadyExistsException(
'Tag ("' . $newName . '", ' . $userVisible . ', ' . $userAssignable . ') already exists',
0,
$e
);
} catch (Exception $e) {
if ($e->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
throw new TagAlreadyExistsException(
'Tag ("' . $newName . '", ' . $userVisible . ', ' . $userAssignable . ') already exists',
0,
$e
);
}
throw $e;
}

$this->dispatcher->dispatch(ManagerEvent::EVENT_UPDATE, new ManagerEvent(
Expand Down
7 changes: 5 additions & 2 deletions lib/private/SystemTag/SystemTagObjectMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/
namespace OC\SystemTag;

use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IDBConnection;
Expand Down Expand Up @@ -151,7 +151,10 @@ public function assignTags(string $objId, string $objectType, $tagIds): void {
$query->setParameter('tagid', $tagId);
$query->execute();
$tagsAssigned[] = $tagId;
} catch (UniqueConstraintViolationException $e) {
} catch (Exception $e) {
if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
throw $e;
}
// ignore existing relations
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/public/IDBConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public function lastInsertId(string $table): int;
* @return int number of inserted rows
* @throws Exception used to be the removed dbal exception, since 21.0.0 it's \OCP\DB\Exception
* @since 6.0.0 - parameter $compare was added in 8.1.0, return type changed from boolean in 8.1.0
* @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371
* @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (\OCP\DB\Exception $e) { if ($e->getReason() === \OCP\DB\Exception::REASON_CONSTRAINT_VIOLATION) {} }" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371
*/
public function insertIfNotExist(string $table, array $input, ?array $compare = null);

Expand Down
7 changes: 4 additions & 3 deletions tests/lib/Authentication/Token/ManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@

namespace Test\Authentication\Token;

use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Token\IToken;
use OC\Authentication\Token\Manager;
use OC\Authentication\Token\PublicKeyToken;
use OC\Authentication\Token\PublicKeyTokenProvider;
use OCP\DB\Exception;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;

Expand Down Expand Up @@ -62,8 +62,9 @@ public function testGenerateToken(): void {
}

public function testGenerateConflictingToken(): void {
/** @var MockObject|UniqueConstraintViolationException $exception */
$exception = $this->createMock(UniqueConstraintViolationException::class);
/** @var MockObject|Exception $exception */
$exception = $this->createMock(Exception::class);
$exception->method('getReason')->willReturn(Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION);

$token = new PublicKeyToken();
$token->setUid('uid');
Expand Down