Files
0pt/sql/015_order_status_check.sql
T
admin c3582838aa
CI (SIAX Cloud) / integration (push) Skipped
CI (SIAX Cloud) / sonar (push) Skipped
masterplan-lock / check (push) Successful in 1m3s
CI (SIAX Cloud) / security (push) Successful in 1m4s
Security Scan (SIAX Cloud) / security (push) Successful in 1m7s
CI (SIAX Cloud) / quality (push) Successful in 1m52s
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (pull_request) Successful in 6s
CI (SIAX Cloud) / security (pull_request) Successful in 18s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 19s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m20s
CI (SIAX Cloud) / integration (pull_request) Successful in 1m58s
feat(orders): kanonisk statusmaskin + guardad DB-constraint (ADR-0002, C3)
2026-09-29 21:15:35 +02:00

65 lines
3.0 KiB
PL/PgSQL

-- 015_order_status_check.sql — Kanonisk orderstatus (ADR-0002, C3 2026-09-29)
--
-- Guardar `orders.status` (fri text sedan sql/001_init.sql line ~92) mot den
-- kanoniska unionen i lib/order-flow.ts (ORDER_STATUSES). Denna fil KÖRS EJ
-- automatiskt av deploy — Simon kör den manuellt mot prod efter harvest.
--
-- KÖRORDNING (DoD §17 backup-städ, §33 migreringsordning, §35 restore-drill):
-- 1. BACKUP FÖRST: pg_dump "$DATABASE_URL" -t orders -Fc -f orders-pre-015.dump
-- (och verifiera att restore-drillen har körts nyligen — §35).
-- 2. HARVEST-PREFLIGHT (läs-only, körs före fas A):
-- SELECT status, COUNT(*) FROM orders GROUP BY status ORDER BY 2 DESC;
-- Alla rader MÅSTE ligga i listan nedan. Finns okända statusar:
-- AVBRYT — mappa dem först (UPDATE ... WHERE status = '<okänd>') i en
-- egen, granskad migrering; kör aldrig fas A med okända rader kvar.
-- 3. FAS A ( TRANSACTIONEN NEDAN — ADD CONSTRAINT ... NOT VALID ):
-- tar bara ett kort ACCESS EXCLUSIVE-lås vid själva katalogändringen,
-- validerar INTE befintliga rader, blockerar inte skrivningar längre tid.
-- 4. FAS B (SEPARAT session/transaktion — VALIDATE CONSTRAINT):
-- BEGIN; SET lock_timeout = '5s';
-- ALTER TABLE orders VALIDATE CONSTRAINT order_status_check;
-- COMMIT;
-- VALIDATE tar SHARE UPDATE EXCLUSIVE (skrivningar tillåtna); med
-- lock_timeout stänger vi ute långa låsköer. Misslyckas fas B: retry.
--
-- Listan är kod-SSOT: håll synkroniserad med ORDER_STATUSES i lib/order-flow.ts.
BEGIN;
-- Preflight-guard: fas A nekar om okända statusar finns (fail-closed).
-- (SELECT ... FOR UPDATE-stil låsning behövs ej — NOT VALID-constrainten
-- läser inga rader; vi vill bara neka körning vid dirt-data.)
DO $preflight$
DECLARE
unknown_rows bigint;
BEGIN
SELECT COUNT(*) INTO unknown_rows
FROM orders
WHERE status NOT IN ('draft', 'quoted', 'in_production', 'review', 'accepted', 'delivered', 'cancelled');
IF unknown_rows > 0 THEN
RAISE EXCEPTION
'order_status_check preflight: % rader med status utanför den kanoniska unionen — kör harvest (SELECT status, COUNT(*) FROM orders GROUP BY status), mappa okända statusar, KÖR EJ fas A', unknown_rows;
END IF;
END
$preflight$;
-- Rapportera (loggar) okända statusar om preflighten ändå passerar tom:
SELECT status, COUNT(*) AS n
FROM orders
WHERE status NOT IN ('draft', 'quoted', 'in_production', 'review', 'accepted', 'delivered', 'cancelled')
GROUP BY status;
-- (0 rader = grönt; >0 rader = ovanstående DO-block har redan avbrutit.)
ALTER TABLE orders
ADD CONSTRAINT order_status_check
CHECK (status IN ('draft', 'quoted', 'in_production', 'review', 'accepted', 'delivered', 'cancelled'))
NOT VALID;
COMMIT;
-- FAS B körs i EGEN session (se körordning steg 4) — kommenterad här för att
-- den ALDRIG ska ligga i samma transaktion som fas A:
-- BEGIN; SET lock_timeout = '5s';
-- ALTER TABLE orders VALIDATE CONSTRAINT order_status_check;
-- COMMIT;