mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-09-13 22:10:11 -04:00
107 lines
3.4 KiB
C#
107 lines
3.4 KiB
C#
using Microsoft.EntityFrameworkCore.Migrations;
|
|
|
|
#nullable disable
|
|
|
|
namespace Cleanuparr.Persistence.Postgres.Migrations.Events
|
|
{
|
|
/// <inheritdoc />
|
|
public partial class NormalizeDownloadHashCasing : Migration
|
|
{
|
|
/// <inheritdoc />
|
|
protected override void Up(MigrationBuilder migrationBuilder)
|
|
{
|
|
// download_id is written lowercase now, so other casings became unreachable.
|
|
// The unique index is case-sensitive, so one torrent can sit in several rows.
|
|
migrationBuilder.DropIndex(
|
|
name: "ix_download_items_download_id",
|
|
schema: "events",
|
|
table: "download_items");
|
|
|
|
// Repoint every loser's strikes onto the survivor before anything is deleted:
|
|
// strikes.download_item_id cascades on delete, so the reverse order would destroy them.
|
|
migrationBuilder.Sql(@"
|
|
WITH ranked AS (
|
|
SELECT
|
|
id,
|
|
lower(download_id) AS normalized,
|
|
ROW_NUMBER() OVER (
|
|
PARTITION BY lower(download_id)
|
|
ORDER BY id
|
|
) AS rank_in_group
|
|
FROM events.download_items
|
|
),
|
|
survivors AS (
|
|
SELECT normalized, id AS survivor_id
|
|
FROM ranked
|
|
WHERE rank_in_group = 1
|
|
),
|
|
losers AS (
|
|
SELECT r.id AS loser_id, s.survivor_id
|
|
FROM ranked r
|
|
JOIN survivors s ON s.normalized = r.normalized
|
|
WHERE r.rank_in_group > 1
|
|
)
|
|
UPDATE events.strikes
|
|
SET download_item_id = losers.survivor_id
|
|
FROM losers
|
|
WHERE strikes.download_item_id = losers.loser_id;
|
|
");
|
|
|
|
// OR the group's status flags onto the survivor, which keeps its own title.
|
|
migrationBuilder.Sql(@"
|
|
WITH grouped AS (
|
|
SELECT
|
|
lower(download_id) AS normalized,
|
|
bool_or(is_marked_for_removal) AS is_marked_for_removal,
|
|
bool_or(is_removed) AS is_removed,
|
|
bool_or(is_returning) AS is_returning
|
|
FROM events.download_items
|
|
GROUP BY lower(download_id)
|
|
)
|
|
UPDATE events.download_items
|
|
SET
|
|
is_marked_for_removal = grouped.is_marked_for_removal,
|
|
is_removed = grouped.is_removed,
|
|
is_returning = grouped.is_returning
|
|
FROM grouped
|
|
WHERE grouped.normalized = lower(download_items.download_id);
|
|
");
|
|
|
|
migrationBuilder.Sql(@"
|
|
WITH ranked AS (
|
|
SELECT
|
|
id,
|
|
ROW_NUMBER() OVER (
|
|
PARTITION BY lower(download_id)
|
|
ORDER BY id
|
|
) AS rank_in_group
|
|
FROM events.download_items
|
|
)
|
|
DELETE FROM events.download_items
|
|
WHERE id IN (SELECT id FROM ranked WHERE rank_in_group > 1);
|
|
");
|
|
|
|
migrationBuilder.Sql("UPDATE events.download_items SET download_id = lower(download_id);");
|
|
|
|
// events.item_hash carries no unique index, so it needs no merge.
|
|
// manual_events.item_hash is left alone: it is normalized in code.
|
|
// Its unique index is filtered, so lowercasing could collide.
|
|
migrationBuilder.Sql("UPDATE events.events SET item_hash = lower(item_hash) WHERE item_hash IS NOT NULL;");
|
|
|
|
migrationBuilder.CreateIndex(
|
|
name: "ix_download_items_download_id",
|
|
schema: "events",
|
|
table: "download_items",
|
|
column: "download_id",
|
|
unique: true);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void Down(MigrationBuilder migrationBuilder)
|
|
{
|
|
// Nothing to undo: the merge cannot be unmerged.
|
|
// The index is identical before and after Up.
|
|
}
|
|
}
|
|
}
|