diff --git a/backend/src/main/resources/db/migration/V13__add_user_guest_check_constraint.sql b/backend/src/main/resources/db/migration/V13__add_user_guest_check_constraint.sql new file mode 100644 index 0000000..ad7e8bb --- /dev/null +++ b/backend/src/main/resources/db/migration/V13__add_user_guest_check_constraint.sql @@ -0,0 +1,17 @@ +-- Enforce the Order entity invariant at the database level: exactly one of +-- (user_id, guest_token) must be set — never both, never neither. +-- +-- The Order Javadoc states "Either userId or guestToken is set; never both, +-- never neither", but previously only the @PrePersist lifecycle callback +-- enforced this in Java. A stray INSERT (admin tooling, manual SQL) could +-- violate it silently. +-- +-- The CHECK expression (user_id IS NULL) <> (guest_token IS NULL) evaluates: +-- TRUE when exactly one column is NULL (the other is set) — allowed +-- FALSE when both are NULL or both are set — rejected +-- +-- Standard SQL CHECK constraint, supported by both H2 (tests/dev) and +-- PostgreSQL (prod). +ALTER TABLE orders + ADD CONSTRAINT chk_user_or_guest + CHECK ((user_id IS NULL) <> (guest_token IS NULL));