[PERF] finalize_db: purge `ir.ui.view` images in a single pass

The purge ran one correlated ``NOT EXISTS`` scan of ``ir_ui_view`` per
candidate attachment (``ILIKE '%web/image/<id>%'`` on ``arch_db::text``),
taking hours on large databases. It now scans ``ir_ui_view`` once with
``regexp_matches``, builds a deduplicated set of referenced ids, and
deletes orphans via hash lookups.

This also fixes substring false positives: ``/web/image/12`` was kept
because it matched ``/web/image/123``. Integer comparison (``ref_id =
a.id``) now requires an exact match.
This commit is contained in:
Stéphan Sainléger
2026-08-13 11:25:51 +02:00
parent 0c096bb5ff
commit e06d750b74

View File

@@ -32,16 +32,24 @@ query_postgres_container "$CLEANUP_SQL" "$DB_NAME"
# ────────────────────────────────────────────────────────────
echo "Purging ir.ui.view images orphaned of their parent view and unreferenced in any arch_db..."
PURGE_VIEW_IMAGES_SQL=$(cat <<'EOF'
DELETE FROM ir_attachment a
WHERE a.res_model = 'ir.ui.view'
AND a.mimetype LIKE 'image/%'
AND a.store_fname IS NOT NULL
AND a.res_id NOT IN (SELECT id FROM ir_ui_view)
AND NOT EXISTS (
SELECT 1 FROM ir_ui_view v
WHERE v.arch_db::text ILIKE '%web/image/' || a.id || '%'
OR v.arch_db::text ILIKE '%web/content/' || a.id || '%'
);
DO $$
DECLARE purged int;
BEGIN
WITH refs AS (
SELECT DISTINCT (m[1])::bigint AS ref_id
FROM ir_ui_view v
CROSS JOIN LATERAL regexp_matches(
COALESCE(v.arch_db::text, ''), '/web/(?:image|content)/(\d+)', 'g') AS m
)
DELETE FROM ir_attachment a
WHERE a.res_model = 'ir.ui.view'
AND a.mimetype LIKE 'image/%'
AND a.store_fname IS NOT NULL
AND a.res_id NOT IN (SELECT id FROM ir_ui_view)
AND NOT EXISTS (SELECT 1 FROM refs r WHERE r.ref_id = a.id);
GET DIAGNOSTICS purged = ROW_COUNT;
RAISE NOTICE 'Purged % orphaned ir.ui.view image attachments', purged;
END $$;
EOF
)
query_postgres_container "$PURGE_VIEW_IMAGES_SQL" "$DB_NAME"