CREATE TABLE IF NOT EXISTS room_categories ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, key TEXT NOT NULL UNIQUE, name TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', sort_order INTEGER NOT NULL DEFAULT 0, is_enabled BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT room_categories_key_not_empty CHECK (length(trim(key)) > 0), CONSTRAINT room_categories_key_format CHECK (key ~ '^[a-z0-9][a-z0-9_-]{0,63}$'), CONSTRAINT room_categories_name_not_empty CHECK (length(trim(name)) > 0), CONSTRAINT room_categories_name_length CHECK (char_length(name) <= 80), CONSTRAINT room_categories_description_length CHECK (char_length(description) <= 300) ); INSERT INTO room_categories (key, name, description, sort_order, is_enabled) VALUES ('general', 'General', 'General rooms', 0, TRUE) ON CONFLICT (key) DO NOTHING; CREATE TABLE IF NOT EXISTS rooms ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, name TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', cover_file_reference_id BIGINT NULL REFERENCES file_references(id) ON DELETE SET NULL, category_id BIGINT NULL REFERENCES room_categories(id) ON DELETE SET NULL, created_by BIGINT NOT NULL REFERENCES users(id) ON DELETE RESTRICT, closed_at TIMESTAMPTZ NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMPTZ NULL, version INTEGER NOT NULL DEFAULT 0, last_activity_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS room_labels ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, key TEXT NOT NULL UNIQUE, name TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', color TEXT NOT NULL DEFAULT '', category_id BIGINT NULL REFERENCES room_categories(id) ON DELETE SET NULL, sort_order INTEGER NOT NULL DEFAULT 0, is_enabled BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT room_labels_key_not_empty CHECK (length(trim(key)) > 0), CONSTRAINT room_labels_key_format CHECK (key ~ '^[a-z0-9][a-z0-9_-]{0,63}$'), CONSTRAINT room_labels_name_not_empty CHECK (length(trim(name)) > 0), CONSTRAINT room_labels_name_length CHECK (char_length(name) <= 80), CONSTRAINT room_labels_description_length CHECK (char_length(description) <= 300), CONSTRAINT room_labels_color_format CHECK (color = '' OR color ~ '^#[0-9A-Fa-f]{6}$') ); CREATE TABLE IF NOT EXISTS room_label_assignments ( room_id BIGINT NOT NULL REFERENCES rooms(id) ON DELETE CASCADE, label_id BIGINT NOT NULL REFERENCES room_labels(id) ON DELETE RESTRICT, assigned_by BIGINT NULL REFERENCES users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (room_id, label_id) ); CREATE INDEX IF NOT EXISTS idx_room_categories_enabled_order ON room_categories(is_enabled, sort_order, id); CREATE INDEX IF NOT EXISTS idx_room_labels_enabled_order ON room_labels(is_enabled, sort_order, id); CREATE INDEX IF NOT EXISTS idx_room_labels_category_order ON room_labels(category_id, sort_order, id); CREATE INDEX IF NOT EXISTS idx_room_label_assignments_label_room ON room_label_assignments(label_id, room_id); CREATE INDEX IF NOT EXISTS idx_rooms_created_by ON rooms(created_by); CREATE INDEX IF NOT EXISTS idx_rooms_category_created_at ON rooms(category_id, created_at DESC) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rooms_category_last_activity ON rooms(category_id, last_activity_at DESC) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rooms_closed_at ON rooms(closed_at) WHERE closed_at IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_rooms_deleted_at ON rooms(deleted_at) WHERE deleted_at IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_rooms_name_trgm ON rooms USING gin(name gin_trgm_ops) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rooms_description_trgm ON rooms USING gin(description gin_trgm_ops) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rooms_last_activity ON rooms(last_activity_at) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rooms_created_at ON rooms(created_at DESC) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rooms_creator_created_at ON rooms(created_by, created_at DESC) WHERE deleted_at IS NULL; CREATE TRIGGER update_room_categories_updated_at BEFORE UPDATE ON room_categories FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); CREATE TRIGGER update_rooms_updated_at BEFORE UPDATE ON rooms FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); CREATE TRIGGER update_room_labels_updated_at BEFORE UPDATE ON room_labels FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();