-- 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, sub_course_id, question_set_id, completed_at, updated_at ) SELECT @user_id::BIGINT, CASE WHEN qs.owner_type = 'SUB_COURSE' THEN qs.owner_id WHEN qs.owner_type = 'SUB_MODULE' THEN sm.legacy_sub_course_id ELSE NULL END, qs.id, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP FROM question_sets qs LEFT JOIN sub_modules sm ON qs.owner_type = 'SUB_MODULE' AND qs.owner_id = sm.id WHERE qs.id = @question_set_id::BIGINT ON CONFLICT (user_id, question_set_id) DO UPDATE SET completed_at = EXCLUDED.completed_at, updated_at = EXCLUDED.updated_at;