diff --git a/backend/migrations/001_initial.sql b/backend/migrations/001_initial.sql index bc55f12..d099b28 100644 --- a/backend/migrations/001_initial.sql +++ b/backend/migrations/001_initial.sql @@ -12,8 +12,8 @@ CREATE TABLE tutors ( ); CREATE TABLE tutor_courses ( - tutor_id INTEGER REFERENCES tutors(id), - course_id INTEGER REFERENCES courses(id), + tutor_id INTEGER NOT NULL REFERENCES tutors(id), + course_id INTEGER NOT NULL REFERENCES courses(id), PRIMARY KEY (tutor_id, course_id) ); @@ -42,9 +42,9 @@ CREATE TABLE slots ( session_id INTEGER NOT NULL REFERENCES sessions(id), room_id INTEGER REFERENCES rooms(id), tutor_id INTEGER NOT NULL REFERENCES tutors(id), - start_time TEXT NOT NULL, - end_time TEXT NOT NULL, - status TEXT NOT NULL DEFAULT 'closed', + start_time TEXT NOT NULL CHECK (start_time GLOB '??:??'), + end_time TEXT NOT NULL CHECK (end_time GLOB '??:??'), + status TEXT NOT NULL DEFAULT 'closed' CHECK (status IN ('closed','open','locked')), code TEXT UNIQUE ); @@ -67,3 +67,13 @@ CREATE TABLE notes ( updated_at TEXT NOT NULL CHECK (updated_at GLOB '????-??-??T??:??:??*'), UNIQUE(slot_id, student_id, tutor_id) ); + +-- Indexes on high-frequency FK columns (SQLite does not auto-index FKs) +CREATE INDEX idx_students_course ON students(course_id); +CREATE INDEX idx_sessions_course ON sessions(course_id); +CREATE INDEX idx_slots_session ON slots(session_id); +CREATE INDEX idx_slots_tutor ON slots(tutor_id); +CREATE INDEX idx_attendances_slot ON attendances(slot_id); +CREATE INDEX idx_attendances_student ON attendances(student_id); +CREATE INDEX idx_notes_slot ON notes(slot_id); +CREATE INDEX idx_notes_student ON notes(student_id); diff --git a/backend/src/db.rs b/backend/src/db.rs index 61a4931..8923563 100644 --- a/backend/src/db.rs +++ b/backend/src/db.rs @@ -2,7 +2,7 @@ use sqlx::{sqlite::SqlitePoolOptions, SqlitePool}; pub async fn init() -> Result { let url = std::env::var("DATABASE_URL") - .unwrap_or_else(|_| "sqlite:attendance.db".into()); + .unwrap_or_else(|_| "sqlite:/data/attendance.db".into()); let pool = SqlitePoolOptions::new() .after_connect(|conn, _| Box::pin(async move { sqlx::query("PRAGMA foreign_keys = ON") @@ -20,7 +20,7 @@ mod tests { // NOTE: #[sqlx::test] injects its own pool, bypassing after_connect. // We test FK enforcement behaviorally: insert a row with a bad FK and assert it fails. - // after_connect is manually invoked in the test setup below. + // The PRAGMA is manually issued at the top of this test body. #[sqlx::test(migrations = "./migrations")] async fn foreign_keys_enforced(pool: SqlitePool) { // Enable FK for this test connection (mirrors what after_connect does in production)