-- name: GetFirstIncompletePreviousPractice :one WITH target AS ( SELECT id, owner_type, owner_id, COALESCE(display_order, 0) AS display_order FROM question_sets WHERE id = @question_set_id::BIGINT AND set_type = 'PRACTICE' AND status = 'PUBLISHED' ), candidates AS ( SELECT qs.id, qs.title, COALESCE(qs.display_order, 0) AS display_order FROM question_sets qs JOIN target t ON qs.owner_type = t.owner_type AND qs.owner_id = t.owner_id WHERE qs.set_type = 'PRACTICE' AND qs.status = 'PUBLISHED' AND COALESCE(qs.display_order, 0) < t.display_order ) SELECT c.id, c.title, c.display_order FROM candidates c LEFT JOIN user_practice_progress upp ON upp.question_set_id = c.id AND upp.user_id = @user_id::BIGINT AND upp.completed_at IS NOT NULL WHERE upp.id IS NULL ORDER BY c.display_order ASC, c.id ASC LIMIT 1; -- name: MarkPracticeCompleted :execrows INSERT INTO user_practice_progress ( user_id, question_set_id, completed_at, updated_at ) VALUES ( @user_id::BIGINT, @question_set_id::BIGINT, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP ) ON CONFLICT (user_id, question_set_id) DO UPDATE SET completed_at = EXCLUDED.completed_at, updated_at = EXCLUDED.updated_at;