Skip to content

Commit 645d90d

Browse files
committed
fix(Import): Handle transactions correctly
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
1 parent 0fd69f4 commit 645d90d

2 files changed

Lines changed: 24 additions & 10 deletions

File tree

lib/Db/BookmarkMapper.php

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -913,23 +913,29 @@ public function insert(Entity $entity): Bookmark {
913913
$entityToInsert->setLastPreview(0);
914914
$entityToInsert->setClickcount(0);
915915

916+
// Wrap the insert in a (possibly nested) transaction so that a failed
917+
// statement – e.g. a unique constraint violation when the URL already
918+
// exists – can be rolled back cleanly. When this runs inside a larger
919+
// transaction (e.g. the HTML importer) Nextcloud uses a SAVEPOINT, so
920+
// rolling back discards only the failed INSERT instead of poisoning or
921+
// committing the outer transaction. This keeps the connection usable for
922+
// the insertOrUpdate() fallback below on SQLite, MySQL and Postgres alike.
923+
$this->db->beginTransaction();
916924
try {
917925
parent::insert($entityToInsert);
918-
if (isset($this->userBookmarkCount[$entityToInsert->getUserId()])) {
919-
$this->userBookmarkCount[$entityToInsert->getUserId()] += 1;
920-
}
921-
$this->eventDispatcher->dispatchTyped(new InsertEvent('bookmark', $entityToInsert->getId()));
922-
return $entityToInsert;
926+
$this->db->commit();
923927
} catch (Exception $e) {
928+
$this->db->rollBack();
924929
if ($e->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
925-
if ($this->db->inTransaction()) {
926-
$this->db->commit();
927-
$this->db->beginTransaction();
928-
}
929930
throw new AlreadyExistsError('A bookmark with this URL already exists');
930931
}
931932
throw $e;
932933
}
934+
if (isset($this->userBookmarkCount[$entityToInsert->getUserId()])) {
935+
$this->userBookmarkCount[$entityToInsert->getUserId()] += 1;
936+
}
937+
$this->eventDispatcher->dispatchTyped(new InsertEvent('bookmark', $entityToInsert->getId()));
938+
return $entityToInsert;
933939
}
934940

935941
/**

lib/Service/HtmlImporter.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,16 @@ public function import(string $userId, string $content, ?int $rootFolderId = nul
125125
}
126126
$imported[] = ['type' => 'bookmark', 'id' => $bm->getId(), 'title' => $bookmark['title'], 'url' => $bookmark['href']];
127127
}
128-
} finally {
129128
$this->connection->commit();
129+
} catch (\Throwable $e) {
130+
// Roll back instead of committing a transaction that may contain a
131+
// failed statement – committing such a transaction throws and masks
132+
// the original error.
133+
if ($this->connection->inTransaction()) {
134+
$this->connection->rollBack();
135+
}
136+
$this->hashManager->setInvalidationEnabled(true);
137+
throw $e;
130138
}
131139

132140
$this->hashManager->setInvalidationEnabled(true);

0 commit comments

Comments
 (0)