Skip to content

Commit dc9350f

Browse files
authored
Merge pull request #63984 from nextcloud/backport/62587/stable33
[stable33] fix: don't rely on constraint for filecache_extended "upsert" when in transaction
2 parents beba20d + 4b848d4 commit dc9350f

1 file changed

Lines changed: 67 additions & 43 deletions

File tree

lib/private/Files/Cache/Cache.php

Lines changed: 67 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -311,17 +311,7 @@ public function insert($file, array $data) {
311311
if ($builder->executeStatement()) {
312312
$fileId = $builder->getLastInsertId();
313313

314-
if (count($extensionValues)) {
315-
$query = $this->getQueryBuilder();
316-
$query->insert('filecache_extended');
317-
$query->hintShardKey('storage', $storageId);
318-
319-
$query->setValue('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
320-
foreach ($extensionValues as $column => $value) {
321-
$query->setValue($column, $query->createNamedParameter($value));
322-
}
323-
$query->executeStatement();
324-
}
314+
$this->setExtensionValues($fileId, $extensionValues, true);
325315

326316
$event = new CacheEntryInsertedEvent($this->storage, $file, $fileId, $storageId);
327317
$this->eventDispatcher->dispatch(CacheInsertEvent::class, $event);
@@ -388,48 +378,82 @@ public function update($id, array $data) {
388378
$query->executeStatement();
389379
}
390380

391-
if (count($extensionValues)) {
392-
try {
393-
$query = $this->getQueryBuilder();
394-
$query->insert('filecache_extended');
395-
$query->hintShardKey('storage', $this->getNumericStorageId());
381+
$this->setExtensionValues($id, $extensionValues, false);
396382

397-
$query->setValue('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT));
398-
foreach ($extensionValues as $column => $value) {
399-
$query->setValue($column, $query->createNamedParameter($value));
400-
}
383+
$path = $this->getPathById($id);
384+
// path can still be null if the file doesn't exist
385+
if ($path !== null) {
386+
$event = new CacheEntryUpdatedEvent($this->storage, $path, $id, $this->getNumericStorageId());
387+
$this->eventDispatcher->dispatch(CacheUpdateEvent::class, $event);
388+
$this->eventDispatcher->dispatchTyped($event);
389+
}
390+
}
401391

402-
$query->executeStatement();
392+
private function hasExtensionValues(int $id) {
393+
$query = $this->getQueryBuilder();
394+
$query->select('fileid')
395+
->from('filecache_extended')
396+
->whereFileId($id);
397+
398+
return $query->executeQuery()->fetchOne() !== false;
399+
}
400+
401+
private function setExtensionValues(int $id, array $extensionValues, bool $newFile) {
402+
if (!$extensionValues) {
403+
return;
404+
}
405+
406+
// a failed insert in a transaction aborts the transactions on some platforms, so we can't rely on
407+
// that behavior to to an "upsert"
408+
// for new files, we can safely assume that there won't be a conflict, since the fileid is new
409+
if (!$newFile && $this->connection->inTransaction()) {
410+
if ($this->hasExtensionValues($id)) {
411+
$this->updateExtensionValues($id, $extensionValues);
412+
} else {
413+
$this->insertExtensionValues($id, $extensionValues);
414+
}
415+
} else {
416+
try {
417+
$this->insertExtensionValues($id, $extensionValues);
403418
} catch (Exception $e) {
404419
if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
405420
throw $e;
406421
}
407-
$query = $this->getQueryBuilder();
408-
$query->update('filecache_extended')
409-
->whereFileId($id)
410-
->hintShardKey('storage', $this->getNumericStorageId())
411-
->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
412-
return $query->expr()->orX(
413-
$query->expr()->neq($key, $query->createNamedParameter($value)),
414-
$query->expr()->isNull($key)
415-
);
416-
}, array_keys($extensionValues), array_values($extensionValues))));
417-
418-
foreach ($extensionValues as $key => $value) {
419-
$query->set($key, $query->createNamedParameter($value));
420-
}
421-
422-
$query->executeStatement();
422+
$this->updateExtensionValues($id, $extensionValues);
423423
}
424424
}
425+
}
425426

426-
$path = $this->getPathById($id);
427-
// path can still be null if the file doesn't exist
428-
if ($path !== null) {
429-
$event = new CacheEntryUpdatedEvent($this->storage, $path, $id, $this->getNumericStorageId());
430-
$this->eventDispatcher->dispatch(CacheUpdateEvent::class, $event);
431-
$this->eventDispatcher->dispatchTyped($event);
427+
private function insertExtensionValues(int $id, array $extensionValues) {
428+
$query = $this->getQueryBuilder();
429+
$query->insert('filecache_extended');
430+
$query->hintShardKey('storage', $this->getNumericStorageId());
431+
432+
$query->setValue('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT));
433+
foreach ($extensionValues as $column => $value) {
434+
$query->setValue($column, $query->createNamedParameter($value));
435+
}
436+
437+
$query->executeStatement();
438+
}
439+
440+
private function updateExtensionValues(int $id, array $extensionValues) {
441+
$query = $this->getQueryBuilder();
442+
$query->update('filecache_extended')
443+
->whereFileId($id)
444+
->hintShardKey('storage', $this->getNumericStorageId())
445+
->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
446+
return $query->expr()->orX(
447+
$query->expr()->neq($key, $query->createNamedParameter($value)),
448+
$query->expr()->isNull($key)
449+
);
450+
}, array_keys($extensionValues), array_values($extensionValues))));
451+
452+
foreach ($extensionValues as $key => $value) {
453+
$query->set($key, $query->createNamedParameter($value));
432454
}
455+
456+
$query->executeStatement();
433457
}
434458

435459
/**

0 commit comments

Comments
 (0)