CREATE TABLE IF NOT EXISTS user_ ( id SERIAL PRIMARY KEY, uuid UUID UNIQUE, email TEXT UNIQUE, username TEXT NOT NULL UNIQUE, avatar_url TEXT, password_hash TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS friendship_ ( user_first INT NOT NULL REFERENCES user_(id) ON DELETE CASCADE, user_second INT NOT NULL REFERENCES user_(id) ON DELETE CASCADE, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_first, user_second) ); CREATE TABLE IF NOT EXISTS friend_request_ ( sender INT NOT NULL REFERENCES user_(id) ON DELETE CASCADE, receiver INT NOT NULL REFERENCES user_(id) ON DELETE CASCADE, sent_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (sender, receiver), CHECK (sender <> receiver) ); CREATE TABLE IF NOT EXISTS room_ ( id SERIAL PRIMARY KEY, uuid UUID UNIQUE, owner INT NOT NULL REFERENCES user_(id), name TEXT NOT NULL, global BOOLEAN NOT NULL DEFAULT false ); CREATE TABLE IF NOT EXISTS membership_ ( user_id INT REFERENCES user_(id), room INT REFERENCES room_(id), last_read_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, room) ); CREATE TABLE IF NOT EXISTS room_invite_ ( sender INT NOT NULL REFERENCES user_(id) ON DELETE CASCADE, receiver INT NOT NULL REFERENCES user_(id) ON DELETE CASCADE, room INT NOT NULL, sent_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (sender, receiver), CHECK (sender <> receiver) ); CREATE TABLE IF NOT EXISTS message_ ( id BIGSERIAL PRIMARY KEY, uuid UUID NOT NULL, sender INT REFERENCES user_(id) NOT NULL, room INT REFERENCES room_(id) NOT NULL, message_type VARCHAR(32) NOT NULL, content TEXT NOT NULL, sent_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE ws_token_ ( token TEXT PRIMARY KEY, expires_at TIMESTAMPTZ NOT NULL ); -- ==== INDICES ==== CREATE INDEX idx_message_room_sent_at ON message_ (room, sent_at); CREATE UNIQUE INDEX idx_membership_user_room ON membership_ (user_id, room) INCLUDE (last_read_at); -- Timestamp creation -- CREATE OR REPLACE FUNCTION create_notification_timestamp() -- RETURNS trigger -- AS $$ -- BEGIN -- NEW.sent_at := CURRENT_TIMESTAMP; -- RETURN NEW; -- END; -- $$ LANGUAGE plpgsql; -- -- CREATE OR REPLACE TRIGGER insert_message -- BEFORE INSERT ON message_ -- FOR EACH ROW -- EXECUTE FUNCTION create_notification_timestamp(); -- -- CREATE OR REPLACE TRIGGER insert_room_invite -- BEFORE INSERT ON room_invite_ -- FOR EACH ROW -- EXECUTE FUNCTION create_notification_timestamp(); -- -- CREATE OR REPLACE FUNCTION create_membership_timestamp() -- RETURNS trigger -- AS $$ -- BEGIN -- NEW.last_read_at = CURRENT_TIMESTAMP; -- RETURN NEW; -- END; -- $$ LANGUAGE plpgsql; -- -- CREATE OR REPLACE TRIGGER insert_membership -- BEFORE INSERT ON membership_ -- FOR EACH ROW -- EXECUTE FUNCTION create_membership_timestamp();