Files
pad/internal/store/pgmigrations/054_item_version_seq.sql
T
xarmian 37ec77d110 fix(store): monotonic tie-breaker for same-second item version ordering (BUG-2270)
Adds a per-item monotonic `version_seq` column (dual migrations: SQLite 076 / Postgres 054, backfilled via ROW_NUMBER) so version-history RECONSTRUCTION resolves same-second versions deterministically instead of by the random-UUID PK. Reconstruction paths (shouldCreateItemVersion, ListItemVersions/Resolved, export) order by version_seq; the timeline keyset path (ListItemVersionsBeforeTime) keeps its id-consistent cursor.

Confirming Codex (high effort): found + fixed one keyset-pagination P2 (order/cursor key mismatch). make test-pg green (migration verified against Postgres). Go CI job red only on the pre-existing govulncheck advisory tracked in BUG-2278.

https://claude.ai/code/session_01EZ6yr6pAUFb1uffan912ra
2026-07-21 16:16:15 -04:00

22 lines
1011 B
SQL

-- Add version_seq: a per-item monotonic version counter (BUG-2270).
-- Postgres mirror of migrations/076_item_version_seq.sql. BIGINT because
-- it's a per-item counter that grows unbounded. See the SQLite migration
-- for the full rationale (second-precision created_at ties + random
-- UUIDv4 id = no ordering tie-breaker; rowid is SQLite-only so can't be
-- the cross-dialect fix).
ALTER TABLE item_versions ADD COLUMN IF NOT EXISTS version_seq BIGINT NOT NULL DEFAULT 0;
-- Backfill existing rows deterministically per item. Postgres lacks rowid,
-- so ctid (the physical row locator) is the stable per-partition
-- tie-breaker for same-second rows — the analogue of the SQLite migration's
-- rowid ordering.
UPDATE item_versions AS v
SET version_seq = sub.rn
FROM (
SELECT id, ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY created_at, ctid) AS rn
FROM item_versions
) AS sub
WHERE v.id = sub.id;
CREATE INDEX IF NOT EXISTS idx_item_versions_item_seq ON item_versions(item_id, version_seq);