fix(nextcloud): NULL parent_id before deleting Folder rows on SourceRoot remove

Folders have a self-referential parent_id FK with no ON DELETE rule.
A flat DELETE of the whole subtree trips folders_parent_id_fkey because
postgres checks the constraint per-row regardless of insertion / list
order. Hard-removing 'Taco and Muli - 2024 onward' (35-folder subtree)
returned 500 with ForeignKeyViolationError every attempt.

Fix: UPDATE folders SET parent_id = NULL WHERE id IN (folder_ids) before
the DELETE so the chain is broken cleanly. Same pattern used in
prune_missing_photos for the same constraint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claudio
2026-05-10 22:30:34 +02:00
parent 09a00f7419
commit 63dd39d172

View File

@@ -30,7 +30,7 @@ from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel from pydantic import BaseModel
from sqlalchemy import delete, select from sqlalchemy import delete, select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db from app.database import get_db
@@ -332,6 +332,17 @@ async def delete_nextcloud_source_root(
FolderShare.folder_type == 'folder', FolderShare.folder_type == 'folder',
) )
) )
# Folders have a self-referential parent_id FK with no
# ON DELETE rule. A flat DELETE of the whole subtree trips
# `folders_parent_id_fkey` because postgres checks the
# constraint per row, regardless of insertion / list order.
# NULL the parent_id on every folder we're about to delete
# first so the chain breaks cleanly.
await db.execute(
update(Folder)
.where(Folder.id.in_(folder_ids))
.values(parent_id=None)
)
await db.execute(delete(Folder).where(Folder.id.in_(folder_ids))) await db.execute(delete(Folder).where(Folder.id.in_(folder_ids)))
await db.delete(sr) await db.delete(sr)