fix(528): pass2 add recurrence_id field
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
-- caldav.calendar_events — add RECURRENCE-ID column for exception instances
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
-- Motivation: AtalayaLabs/OxiCloud#528 — CalDAV clients (Thunderbird, Apple
|
||||
-- Calendar, Gnome Calendar, DAVx⁵) modify a single occurrence of a recurring
|
||||
-- event by PUTting a separate VEVENT that shares the master's UID and adds
|
||||
-- a RECURRENCE-ID identifying which occurrence is overridden (RFC 5545
|
||||
-- §3.8.4.4).
|
||||
--
|
||||
-- Pre-#528 behaviour: modifications either hit a UID collision (silent
|
||||
-- 500 or corrupt state) or overwrote the master. Post-#528 the exception
|
||||
-- override lives as its own row keyed by
|
||||
-- (calendar_id, ical_uid, recurrence_id), with the master identified by
|
||||
-- `recurrence_id IS NULL`.
|
||||
--
|
||||
-- Related but distinct from parser Phase 1 (rewrite of extract_ical_property
|
||||
-- on top of the `ical` crate) — that landed in the same branch to enable
|
||||
-- parsing RECURRENCE-ID at all. This migration is the storage half.
|
||||
--
|
||||
-- No backfill needed — pre-migration events all become masters (NULL). No
|
||||
-- existing exception rows existed because the parser couldn't read them.
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- Column: nullable. NULL = master, non-NULL = exception instance whose
|
||||
-- value pinpoints which occurrence of the recurring master is being
|
||||
-- overridden. TIMESTAMPTZ so both timed (DATE-TIME) and all-day (DATE)
|
||||
-- RECURRENCE-IDs fit — the domain-side `parse_ical_datetime` normalises
|
||||
-- both into `DateTime<Utc>` (all-day → midnight UTC of the target date).
|
||||
ALTER TABLE caldav.calendar_events
|
||||
ADD COLUMN recurrence_id TIMESTAMP WITH TIME ZONE NULL;
|
||||
|
||||
COMMENT ON COLUMN caldav.calendar_events.recurrence_id IS
|
||||
'RFC 5545 §3.8.4.4 RECURRENCE-ID. NULL on the master, non-NULL on '
|
||||
'per-instance exception overrides. Keyed with (calendar_id, ical_uid) '
|
||||
'via the two partial unique indexes below.';
|
||||
|
||||
-- Partial unique index: at most one master row per (calendar_id, ical_uid).
|
||||
--
|
||||
-- Without this a client that re-uses a UID across calendar events (e.g. a
|
||||
-- pre-2026-08 import that didn't dedupe) could produce two masters — the
|
||||
-- lookup by (calendar_id, ical_uid) WHERE recurrence_id IS NULL would then
|
||||
-- be ambiguous and the exception-routing logic would either overwrite the
|
||||
-- wrong master or refuse to insert. Pre-migration duplicates would fail
|
||||
-- this index creation; if that happens, the reconciliation is out of scope
|
||||
-- for this migration (dedup script would go here — but the existing
|
||||
-- codebase generates fresh UIDs on ambiguity so it shouldn't fire in
|
||||
-- practice).
|
||||
CREATE UNIQUE INDEX idx_calendar_events_master_unique
|
||||
ON caldav.calendar_events (calendar_id, ical_uid)
|
||||
WHERE recurrence_id IS NULL;
|
||||
|
||||
-- Partial unique index: at most one exception override per
|
||||
-- (calendar_id, ical_uid, recurrence_id). Prevents two rows both claiming
|
||||
-- to override the same instance of the same master — which would confuse
|
||||
-- the client on next PROPFIND.
|
||||
CREATE UNIQUE INDEX idx_calendar_events_exception_unique
|
||||
ON caldav.calendar_events (calendar_id, ical_uid, recurrence_id)
|
||||
WHERE recurrence_id IS NOT NULL;
|
||||
|
||||
-- Read-path index for the "give me the master + all its exceptions"
|
||||
-- query the PROPFIND handler will run. Covered by the two unique indexes
|
||||
-- above only partially — this covering index reads the full
|
||||
-- (calendar_id, ical_uid) pair in one seek regardless of which side of
|
||||
-- the master/exception split.
|
||||
CREATE INDEX idx_calendar_events_uid_lookup
|
||||
ON caldav.calendar_events (calendar_id, ical_uid);
|
||||
|
||||
COMMIT;
|
||||
Reference in New Issue
Block a user